if mode == "none":
return default
- msg = (
- f"Invalid {ctx} {value!r}: supported values are {', '.join(valid_options)}"
- )
+ msg = enum_error_message(ctx, value, valid_options)
if mode == "raise":
raise ValueError(msg)
logger.warning(f"{msg}, using {default!r}")
split_builder = self._make_timeseries_split_datasets
case _:
raise ValueError(
- f"Invalid data_split_parameters.method value {method!r}: "
- f"supported values are "
- f"{', '.join(QuickAdapterRegressorV3._DATA_SPLIT_METHODS)}"
+ enum_error_message(
+ "data_split_parameters.method",
+ method,
+ QuickAdapterRegressorV3._DATA_SPLIT_METHODS,
+ )
)
def split_fn(
) -> None:
if namespace not in {_OPTUNA_NAMESPACES.label}:
raise ValueError(
- f"Invalid namespace value {namespace!r}: "
- f"supported values are {_OPTUNA_NAMESPACES.label}"
+ enum_error_message("namespace", namespace, (_OPTUNA_NAMESPACES.label,))
)
if not callable(callback):
raise ValueError(
pred_label_minima = pred_label[pred_label < -eps]
else:
raise ValueError(
- f"Invalid selection_method value {selection_method!r}: "
- f"supported values are {', '.join(EXTREMA_SELECTION_METHODS)}"
+ enum_error_message(
+ "selection_method", selection_method, EXTREMA_SELECTION_METHODS
+ )
)
return pred_label_minima, pred_label_maxima
threshold_func = getattr(skimage.filters, f"threshold_{method}")
except AttributeError:
raise ValueError(
- f"Invalid skimage threshold method value {method!r}: "
- f"supported values are {', '.join(SKIMAGE_THRESHOLD_METHODS)}"
+ enum_error_message(
+ "skimage threshold method", method, SKIMAGE_THRESHOLD_METHODS
+ )
)
min_func = QuickAdapterRegressorV3.apply_skimage_threshold
)
else:
raise ValueError(
- f"Invalid trial_selection_method value {trial_selection_method!r}: "
- f"supported values are {', '.join(QuickAdapterRegressorV3._DISTANCE_METHODS)}"
+ enum_error_message(
+ "trial_selection_method",
+ trial_selection_method,
+ QuickAdapterRegressorV3._DISTANCE_METHODS,
+ )
)
min_score_position = np.nanargmin(scores)
)
else:
raise ValueError(
- f"Invalid selection_method value {selection_method!r}: "
- f"supported values are {', '.join(QuickAdapterRegressorV3._DISTANCE_METHODS)}"
+ enum_error_message(
+ "selection_method",
+ selection_method,
+ QuickAdapterRegressorV3._DISTANCE_METHODS,
+ )
)
ordered_cluster_indices = np.argsort(cluster_center_scores)
else:
raise ValueError(
- f"Invalid cluster_method value {cluster_method!r}: "
- f"supported values are {', '.join(QuickAdapterRegressorV3._CLUSTER_METHODS)}"
+ enum_error_message(
+ "cluster_method",
+ cluster_method,
+ QuickAdapterRegressorV3._CLUSTER_METHODS,
+ )
)
@staticmethod
return np.nanmax(neighbor_distances, axis=1)
else:
raise ValueError(
- f"Invalid aggregation value {aggregation!r}: "
- f"supported values are {', '.join(QuickAdapterRegressorV3._DENSITY_AGGREGATIONS)}"
+ enum_error_message(
+ "aggregation",
+ aggregation,
+ QuickAdapterRegressorV3._DENSITY_AGGREGATIONS,
+ )
)
@staticmethod
)
raise ValueError(
- f"Invalid label_method value {selection_method!r}: "
- f"supported values are {', '.join(QuickAdapterRegressorV3._SELECTION_METHODS)}"
+ enum_error_message(
+ "label_method",
+ selection_method,
+ QuickAdapterRegressorV3._SELECTION_METHODS,
+ )
)
def _get_multi_objective_study_best_trial(
) -> Optional[optuna.trial.FrozenTrial]:
if namespace not in {_OPTUNA_NAMESPACES.label}:
raise ValueError(
- f"Invalid namespace value {namespace!r}: "
- f"supported values are {_OPTUNA_NAMESPACES.label}"
+ enum_error_message("namespace", namespace, (_OPTUNA_NAMESPACES.label,))
)
n_objectives = len(study.directions)
if n_objectives < 2:
)
else:
raise ValueError(
- f"Invalid optuna storage_backend value {storage_backend!r}: "
- f"supported values are {', '.join(QuickAdapterRegressorV3._OPTUNA_STORAGE_BACKENDS)}"
+ enum_error_message(
+ "optuna storage_backend",
+ storage_backend,
+ QuickAdapterRegressorV3._OPTUNA_STORAGE_BACKENDS,
+ )
)
return storage
match sampler:
case None:
raise ValueError(
- f"Invalid optuna sampler value {sampler!r}: "
- f"supported values are {', '.join(QuickAdapterRegressorV3._OPTUNA_SAMPLERS)}"
+ enum_error_message(
+ "optuna sampler",
+ sampler,
+ QuickAdapterRegressorV3._OPTUNA_SAMPLERS,
+ )
)
case QuickAdapterRegressorV3._OPTUNA_SAMPLERS.tpe:
return optuna.samplers.TPESampler(
)
else:
raise ValueError(
- f"Invalid namespace value {namespace!r}: "
- f"supported values are {', '.join(_OPTUNA_NAMESPACES)}"
+ enum_error_message("namespace", namespace, _OPTUNA_NAMESPACES)
)
@staticmethod
samplers, sampler = self.optuna_samplers_by_namespace(namespace)
if sampler not in samplers:
raise ValueError(
- f"Invalid optuna {namespace} sampler value {sampler!r}: "
- f"supported values are {', '.join(samplers)}"
+ enum_error_message(
+ f"optuna {namespace} sampler", sampler, tuple(samplers)
+ )
)
try:
--- /dev/null
+"""Canonical enum validation error message. Dependency-free (stdlib only).
+
+Owns the ``Invalid <ctx> value <value>: supported values are <options>`` form;
+messages that deviate from it (custom prefix/infix/suffix) are built inline.
+"""
+
+from collections.abc import Sequence
+from typing import Any
+
+
+def enum_error_message(ctx: str, value: Any, options: Sequence[str]) -> str:
+ return f"Invalid {ctx} value {value!r}: supported values are {', '.join(options)}"
RobustScaler,
StandardScaler,
)
+from EnumErrors import enum_error_message
logger = logging.getLogger(__name__)
) -> NDArray[np.floating]:
scaler_attr = family.registry.get(method)
if scaler_attr is None:
- raise ValueError(
- f"Invalid {family.kind} value {method!r}: "
- f"supported values are {', '.join(family.type_names)}"
- )
+ raise ValueError(enum_error_message(family.kind, method, family.type_names))
scaler = getattr(state, scaler_attr, None)
if scaler is None:
raise RuntimeError(f"{scaler_attr} not fitted")
return
raise ValueError(
- f"Invalid standardization value {method!r}: "
- f"supported values are {', '.join(STANDARDIZATION_TYPES)}"
+ enum_error_message("standardization", method, STANDARDIZATION_TYPES)
)
def _fit_normalization(
return
raise ValueError(
- f"Invalid normalization value {method!r}: "
- f"supported values are {', '.join(NORMALIZATION_TYPES)}"
+ enum_error_message("normalization", method, NORMALIZATION_TYPES)
)
def _fit_column(
compute_label_weight_known_at_lookahead,
compute_label_weights,
ensure_datetime_series,
+ enum_error_message,
ewo,
format_dict,
format_number,
)
if trade_price_target_method_fn is None:
raise ValueError(
- f"Invalid trade_price_target_method value {self.trade_price_target_method!r}: "
- f"supported values are {', '.join(TRADE_PRICE_TARGETS)}"
+ enum_error_message(
+ "trade_price_target_method",
+ self.trade_price_target_method,
+ TRADE_PRICE_TARGETS,
+ )
)
return trade_price_target_method_fn()
)
else:
raise ValueError(
- f"Invalid interpolation_direction value {interpolation_direction!r}: "
- f"supported values are {', '.join(QuickAdapterV3._INTERPOLATION_DIRECTIONS)}"
+ enum_error_message(
+ "interpolation_direction",
+ interpolation_direction,
+ QuickAdapterV3._INTERPOLATION_DIRECTIONS,
+ )
)
candle_deviation = (
candle_label_natr_value / 100.0
candle_threshold = base_price * (1 - current_deviation)
else:
raise ValueError(
- f"Invalid side value {side!r}: supported values are {', '.join(QuickAdapterV3._TRADE_DIRECTIONS)}"
+ enum_error_message("side", side, QuickAdapterV3._TRADE_DIRECTIONS)
)
self._candle_threshold_cache[cache_key] = candle_threshold
return self._candle_threshold_cache[cache_key]
return False
else:
raise ValueError(
- f"Invalid trading_mode value {trading_mode!r}: "
- f"supported values are {', '.join(QuickAdapterV3._TRADING_MODES)}"
+ enum_error_message(
+ "trading_mode", trading_mode, QuickAdapterV3._TRADING_MODES
+ )
)
@cached_property
import scipy as sp
import talib.abstract as ta
from freqtrade.misc import pair_to_filename
+from EnumErrors import enum_error_message
from LabelTransformer import (
COMBINED_AGGREGATIONS,
COMBINED_METRICS,
return as_dict(value)
-def enum_error_message(ctx: str, value: Any, options: Sequence[str]) -> str:
- return f"Invalid {ctx} value {value!r}: supported values are {', '.join(options)}"
-
-
ValidateParamsFn = Callable[[dict[str, Any], Logger, str], dict[str, Any]]
CrossFieldValidatorFn = Callable[[dict[str, Any], str], None]
elif win_type == SMOOTHING_KERNELS[3]: # "triang"
coeffs = sp.signal.windows.triang(M=window, sym=True)
else:
- raise ValueError(
- f"Invalid window type value {win_type!r}: "
- f"supported values are {', '.join(SMOOTHING_KERNELS)}"
- )
+ raise ValueError(enum_error_message("window type", win_type, SMOOTHING_KERNELS))
normalized_coeffs = coeffs / np.sum(coeffs)
normalized_coeffs.setflags(write=False)
return normalized_coeffs
return np.full(M, float(sigma_candles), dtype=float)
if bandwidth != FILL_BANDWIDTHS[1]: # "knn"
raise ValueError(
- f"Invalid fill_bandwidth value {bandwidth!r}: "
- f"supported values are {', '.join(FILL_BANDWIDTHS)}"
+ enum_error_message("fill_bandwidth", bandwidth, FILL_BANDWIDTHS)
)
d_k = _compute_pivot_kth_neighbor_distances(pivot_indices, neighbors)
out=fill_weights,
)
else:
- raise ValueError(
- f"Invalid fill_method value {fill_method!r}: "
- f"supported values are {', '.join(FILL_METHODS)}"
- )
+ raise ValueError(enum_error_message("fill_method", fill_method, FILL_METHODS))
return _scatter_weights(
n_values=n_values,
if dist_name not in dist_map:
raise ValueError(
- f"Invalid dist_name {dist_name!r}: supported values are {', '.join(dist_map.keys())}"
+ enum_error_message("dist_name", dist_name, tuple(dist_map.keys()))
)
return dist_map[dist_name]
fitted_iterations = int(model.tree_count_)
initial_iterations = 0
else:
- raise ValueError(
- f"Invalid regressor value {regressor!r}: "
- f"supported values are {', '.join(REGRESSORS)}"
- )
+ raise ValueError(enum_error_message("regressor", regressor, REGRESSORS))
spec = _REGRESSOR_SPEC_BY_NAME[regressor]
# The sole caller refits the cold-started selection model
spec = _REGRESSOR_SPEC_BY_NAME.get(regressor)
if spec is None:
- raise ValueError(
- f"Invalid regressor value {regressor!r}: "
- f"supported values are {', '.join(REGRESSORS)}"
- )
+ raise ValueError(enum_error_message("regressor", regressor, REGRESSORS))
model_training_parameters.setdefault(spec.seed_param, 1)
if trial is not None and vary_model_seed_by_trial:
model_training_parameters[spec.seed_param] = (