diff --git a/flepimop/gempyor_pkg/src/gempyor/inference.py b/flepimop/gempyor_pkg/src/gempyor/inference.py index fc8b6c66d..5111868f8 100644 --- a/flepimop/gempyor_pkg/src/gempyor/inference.py +++ b/flepimop/gempyor_pkg/src/gempyor/inference.py @@ -430,7 +430,7 @@ def get_all_sim_arguments(self): def get_logloss(self, proposal): if not self.inferpar.check_in_bound(proposal=proposal): if not self.silent: - print("`llik` is -inf (out of bound proposal)") + print("`llik` is -inf (out of bound proposal).") return -np.inf, -np.inf, -np.inf snpi_df_mod, hnpi_df_mod = self.inferpar.inject_proposal( diff --git a/flepimop/gempyor_pkg/src/gempyor/model_info.py b/flepimop/gempyor_pkg/src/gempyor/model_info.py index e1ee32a9d..e6b38e08a 100644 --- a/flepimop/gempyor_pkg/src/gempyor/model_info.py +++ b/flepimop/gempyor_pkg/src/gempyor/model_info.py @@ -20,7 +20,7 @@ def __init__(self, config: confuse.ConfigView): self.tf = config["end_date"].as_date() if self.tf <= self.ti: raise ValueError( - "tf (time to finish) is less than or equal to ti (time to start)" + f"Final time ('{self.tf}') is less than or equal to initial time ('{self.ti}')." ) self.n_days = (self.tf - self.ti).days + 1 self.dates = pd.date_range(start=self.ti, end=self.tf, freq="D") @@ -77,9 +77,7 @@ def __init__( # Auto-detect old config if config["interventions"].exists(): raise ValueError( - """This config has an intervention section, and has been written for a previous version of flepiMoP/COVIDScenarioPipeline \ - Please use flepiMoP Version 1.1 (Commit SHA: 0c30c23937dd496d33c2b9fa7c6edb198ad80dac) to run this config. \ - (use git checkout v1.1 inside the flepiMoP directory)""" + "This config has an intervention section, which is only compatible with a previous version (v1.1) of flepiMoP. " ) # 1. Create a setup name that contains every scenario. @@ -105,7 +103,7 @@ def __init__( subpop_config = config["subpop_setup"] if "data_path" in config: raise ValueError( - "The config has a data_path section. This is no longer supported." + "The config has a `data_path` section. This is no longer supported." ) self.path_prefix = pathlib.Path(path_prefix) @@ -170,19 +168,19 @@ def __init__( self.seir_modifiers_library = config["seir_modifiers"][ "modifiers" ].get() - raise ValueError( - "Not implemented yet" + raise NotImplementedError( + "This feature has not been implemented yet." ) # TODO create a Stacked from all elif self.seir_modifiers_scenario is not None: raise ValueError( - "An seir modifiers scenario was provided to ModelInfo but no 'seir_modifiers' sections in config" + "A `seir_modifiers_scenario` argument was provided to `ModelInfo` but there is no `seir_modifiers` section in the config." ) else: - logging.info("Running ModelInfo with seir but without SEIR Modifiers") + logging.info("Running `ModelInfo` with seir but without SEIR Modifiers") elif self.seir_modifiers_scenario is not None: raise ValueError( - "A seir modifiers scenario was provided to ModelInfo but no 'seir:' sections in config" + "A `seir_modifiers_scenario` argument was provided to `ModelInfo` but there is no `seir` section in the config." ) else: logging.critical("Running ModelInfo without SEIR") @@ -203,28 +201,28 @@ def __init__( self.outcome_modifiers_library = config["outcome_modifiers"][ "modifiers" ].get() - raise ValueError( - "Not implemented yet" + raise NotImplementedError( + "This feature has not been implemented yet." ) # TODO create a Stacked from all ## NEED TO IMPLEMENT THIS -- CURRENTLY CANNOT USE outcome modifiers elif self.outcome_modifiers_scenario is not None: if config["outcome_modifiers"].exists(): raise ValueError( - "An outcome modifiers scenario was provided to ModelInfo but no 'outcome_modifiers' sections in config" + "A `outcome_modifiers_scenario` argument was provided to `ModelInfo` but there is no `outcome_modifiers` section in the config." ) else: self.outcome_modifiers_scenario = None else: logging.info( - "Running ModelInfo with outcomes but without Outcomes Modifiers" + "Running `ModelInfo` with outcomes but without Outcomes Modifiers" ) elif self.outcome_modifiers_scenario is not None: raise ValueError( - "An outcome modifiers scenario was provided to ModelInfo but no 'outcomes:' sections in config" + "A `outcome_modifiers_scenario` argument was provided to `ModelInfo` but there is no `outcomes` section in the config." ) else: - logging.info("Running ModelInfo without Outcomes") + logging.info("Running `ModelInfo` without outcomes.") # 6. Inputs and outputs if in_run_id is None: diff --git a/flepimop/gempyor_pkg/src/gempyor/outcomes.py b/flepimop/gempyor_pkg/src/gempyor/outcomes.py index 94b9239bc..16d35fdc4 100644 --- a/flepimop/gempyor_pkg/src/gempyor/outcomes.py +++ b/flepimop/gempyor_pkg/src/gempyor/outcomes.py @@ -140,7 +140,7 @@ def read_parameters_from_config(modinf: model_info.ModelInfo): branching_data = pa.parquet.read_table(branching_file).to_pandas() if "relative_probability" not in list(branching_data["quantity"]): raise ValueError( - f"No 'relative_probability' quantity in {branching_file}, therefor making it useless" + f"There is no `relative_probability` quantity in '{branching_file}'." ) print( @@ -162,7 +162,7 @@ def read_parameters_from_config(modinf: model_info.ModelInfo): modinf.subpop_struct.subpop_names ): raise ValueError( - f"Places in seir input files does not correspond to subpops in outcome probability file {branching_file}" + f"SEIR input files do not have subpops that match those in outcome probability file '{branching_file}'." ) parameters = {} @@ -178,7 +178,8 @@ def read_parameters_from_config(modinf: model_info.ModelInfo): else: raise ValueError( - f"unsure how to read outcome {new_comp}: not a str, nor an incidence or prevalence: {src_name}" + f"Expected a `str` or `dict` containing `incidence` or `prevalence`. " + f"Instead given '{src_name}' for outcome '{new_comp}'." ) parameters[new_comp]["probability"] = outcomes_config[new_comp][ @@ -289,7 +290,7 @@ def read_parameters_from_config(modinf: model_info.ModelInfo): parameters[new_comp] = {} parameters[new_comp]["sum"] = outcomes_config[new_comp]["sum"].get() else: - raise ValueError(f"No 'source' or 'sum' specified for comp {new_comp}") + raise ValueError(f"No `source` or `sum` specified for comp '{new_comp}'.") return parameters @@ -392,7 +393,7 @@ def compute_all_multioutcomes( ) else: raise ValueError( - f"Unknown type for seir simulation provided, got f{type(seir_sim)}" + f"Unknown type provided for seir simulation, received '{type(seir_sim)}'." ) # we don't keep source in this cases else: # already defined outcomes @@ -400,7 +401,7 @@ def compute_all_multioutcomes( source_array = all_data[source_name] else: raise ValueError( - f"ERROR with outcome {new_comp}: the specified source {source_name} is not a dictionnary (for seir outcome) nor an existing pre-identified outcomes." + f"Issue with outcome '{new_comp}'; the specified source '{source_name}' is neither a dictionnary (for seir outcome) nor an existing pre-identified outcome." ) if (loaded_values is not None) and ( @@ -586,7 +587,7 @@ def filter_seir_df(diffI, dates, subpops, filters, outcome_name) -> np.ndarray: vtype = "prevalence" else: raise ValueError( - f"Cannot distinguish the source of outcome {outcome_name}: it is not another previously defined outcome and there is no 'incidence:' or 'prevalence:'." + f"Cannot discern the source of outcome '{outcome_name}'; it is not a previously defined outcome and there is no `incidence` or `prevalence`." ) diffI = diffI[diffI["mc_value_type"] == vtype] @@ -615,7 +616,7 @@ def filter_seir_xr(diffI, dates, subpops, filters, outcome_name) -> np.ndarray: vtype = "prevalence" else: raise ValueError( - f"Cannot distinguish the source of outcome {outcome_name}: it is not another previously defined outcome and there is no 'incidence:' or 'prevalence:'." + f"Cannot discern the source of outcome '{outcome_name}'; it is not a previously defined outcome and there is no `incidence` or `prevalence`." ) # Filter the data filters = filters[vtype] @@ -665,7 +666,7 @@ def multishiftee(arr, shifts, stoch_delay_flag=True): result = np.zeros_like(arr) if stoch_delay_flag: - raise ValueError("NOT SUPPORTED YET") + raise NotImplementedError("`stoch_delay_flag` not supported yet.") # for i, row in reversed(enumerate(np.rows(arr))): # for j,elem in reversed(enumerate(row)): ## This function takes in : @@ -691,7 +692,7 @@ def multishift(arr, shifts, stoch_delay_flag=True): result = np.zeros_like(arr) if stoch_delay_flag: - raise ValueError("NOT SUPPORTED YET") + raise NotImplementedError("`stoch_delay_flag` not supported yet.") # for i, row in reversed(enumerate(np.rows(arr))): # for j,elem in reversed(enumerate(row)): ## This function takes in : diff --git a/flepimop/gempyor_pkg/src/gempyor/seeding.py b/flepimop/gempyor_pkg/src/gempyor/seeding.py index af091131a..bbfeaa07f 100644 --- a/flepimop/gempyor_pkg/src/gempyor/seeding.py +++ b/flepimop/gempyor_pkg/src/gempyor/seeding.py @@ -17,9 +17,7 @@ def _DataFrame2NumbaDict(df, amounts, modinf) -> nb.typed.Dict: if not df["date"].is_monotonic_increasing: - raise ValueError( - "_DataFrame2NumbaDict got an unsorted dataframe, exposing itself to non-sense" - ) + raise ValueError("The `df` given is not sorted by the 'date' column.") cmp_grp_names = [ col for col in modinf.compartments.compartments.columns if col != "name" @@ -111,7 +109,7 @@ def get_from_config(self, sim_id: int, modinf) -> nb.typed.Dict: dupes = seeding[seeding.duplicated(["subpop", "date"])].index + 1 if not dupes.empty: raise ValueError( - f"Repeated subpop-date in rows {dupes.tolist()} of seeding::lambda_file." + f"There are repeating subpop-date in rows '{dupes.tolist()}' of `seeding::lambda_file`." ) elif method == "FolderDraw": seeding = pd.read_csv( @@ -136,7 +134,7 @@ def get_from_config(self, sim_id: int, modinf) -> nb.typed.Dict: seeding = pd.DataFrame(columns=["date", "subpop"]) return _DataFrame2NumbaDict(df=seeding, amounts=[], modinf=modinf) else: - raise NotImplementedError(f"unknown seeding method [got: {method}]") + raise ValueError(f"Unknown seeding method given, '{method}'.") # Sorting by date is very important here for the seeding format necessary !!!! # print(seeding.shape) @@ -160,8 +158,6 @@ def get_from_config(self, sim_id: int, modinf) -> nb.typed.Dict: ) elif method == "FolderDraw" or method == "FromFile": amounts = seeding["amount"] - else: - raise ValueError(f"Unknown seeding method: {method}") return _DataFrame2NumbaDict(df=seeding, amounts=amounts, modinf=modinf) diff --git a/flepimop/gempyor_pkg/src/gempyor/seir.py b/flepimop/gempyor_pkg/src/gempyor/seir.py index 5b76860ab..2492a3825 100644 --- a/flepimop/gempyor_pkg/src/gempyor/seir.py +++ b/flepimop/gempyor_pkg/src/gempyor/seir.py @@ -34,7 +34,9 @@ def build_step_source_arg( if integration_method == "rk4": integration_method = "rk4.jit" if integration_method not in ["rk4.jit", "legacy"]: - raise ValueError(f"Unknown integration method {integration_method}.") + raise ValueError( + f"Unknown integration method given, '{integration_method}'." + ) if "dt" in modinf.seir_config["integration"].keys(): dt = float( eval(str(modinf.seir_config["integration"]["dt"].get())) @@ -151,8 +153,7 @@ def steps_SEIR( elif integration_method == "rk4.jit": if modinf.stoch_traj_flag == True: raise ValueError( - f"with method {integration_method}, only deterministic " - f"integration is possible (got stoch_straj_flag={modinf.stoch_traj_flag}" + f"'{integration_method}' integration method only supports deterministic integration, but `stoch_straj_flag` is '{modinf.stoch_traj_flag}'." ) seir_sim = steps_rk4.rk4_integration(**fnct_args, silent=True) else: @@ -167,8 +168,7 @@ def steps_SEIR( ]: if modinf.stoch_traj_flag == True: raise ValueError( - f"with method {integration_method}, only deterministic " - f"integration is possible (got stoch_straj_flag={modinf.stoch_traj_flag}" + f"'{integration_method}' integration method only supports deterministic integration, but `stoch_straj_flag` is '{modinf.stoch_traj_flag}'." ) seir_sim = steps_experimental.ode_integration( **fnct_args, integration_method=integration_method @@ -190,7 +190,7 @@ def steps_SEIR( elif integration_method == "rk4_aot": seir_sim = steps_experimental.rk4_aot(**fnct_args) else: - raise ValueError(f"Unknow integration scheme, got {integration_method}") + raise ValueError(f"Unknown integration method given, '{integration_method}'.") # We return an xarray instead of a ndarray now compartment_coords = {} diff --git a/flepimop/gempyor_pkg/src/gempyor/statistics.py b/flepimop/gempyor_pkg/src/gempyor/statistics.py index ea10e7aa1..180dbe80e 100644 --- a/flepimop/gempyor_pkg/src/gempyor/statistics.py +++ b/flepimop/gempyor_pkg/src/gempyor/statistics.py @@ -74,7 +74,10 @@ def __init__(self, name: str, statistic_config: confuse.ConfigView) -> None: reg_name = reg_config["name"].get() reg_func = getattr(self, f"_{reg_name}_regularize", None) if reg_func is None: - raise ValueError(f"Unsupported regularization: {reg_name}") + raise ValueError( + f"Unsupported regularization [received: '{reg_name}']. " + f"Currently only `forecast` and `allsubpop` are supported." + ) self.regularizations.append((reg_func, reg_config.get())) self.resample = False @@ -253,7 +256,10 @@ def llik(self, model_data: xr.DataArray, gt_data: xr.DataArray) -> xr.DataArray: "absolute_error": lambda x, y: -np.log(np.nansum(np.abs(x - y))), } if self.dist not in dist_map: - raise ValueError(f"Invalid distribution specified: {self.dist}") + raise ValueError( + f"Invalid distribution specified: '{self.dist}'. " + f"Valid distributions: '{dist_map.keys()}'." + ) if self.dist in ["pois", "nbinom"]: model_data = model_data.astype(int) gt_data = gt_data.astype(int) @@ -295,11 +301,9 @@ def compute_logloss( if not model_data.shape == gt_data.shape: raise ValueError( - ( - f"{self.name} Statistic error: data and groundtruth do not have " - f"the same shape: model_data.shape={model_data.shape} != " - f"gt_data.shape={gt_data.shape}" - ) + f"`model_data` and `gt_data` do not have " + f"the same shape: `model_data.shape` = '{model_data.shape}' != " + f"`gt_data.shape` = '{gt_data.shape}'." ) regularization = 0.0 diff --git a/flepimop/gempyor_pkg/src/gempyor/steps_source.py b/flepimop/gempyor_pkg/src/gempyor/steps_source.py index ea6f7ad21..7da7c393d 100644 --- a/flepimop/gempyor_pkg/src/gempyor/steps_source.py +++ b/flepimop/gempyor_pkg/src/gempyor/steps_source.py @@ -278,8 +278,11 @@ def steps_SEIR_nb( print(" ", states_current[comp].max()) if (states_current.min() < 0) or (states_current.max() > 10**10): - print((states_current.min() < 0), (states_current.max() > 10**10)) - raise ValueError(f"Overflow error. Too small ?. Too large ?") + raise ValueError( + f"State values are outside the valid range. " + f"Minimum value: '{states_current.min()}', Maximum value: '{states_current.max()}'. " + f"Valid range is between 0 and {10**10}." + ) return states, states_daily_incid diff --git a/flepimop/gempyor_pkg/src/gempyor/utils.py b/flepimop/gempyor_pkg/src/gempyor/utils.py index a240be71f..2a848d5b9 100644 --- a/flepimop/gempyor_pkg/src/gempyor/utils.py +++ b/flepimop/gempyor_pkg/src/gempyor/utils.py @@ -58,7 +58,7 @@ def write_df( elif path.suffix == ".parquet": return df.to_parquet(path, index=False, engine="pyarrow") raise NotImplementedError( - f"Invalid extension {path.suffix[1:]}. Must be 'csv' or 'parquet'." + f"Invalid extension provided: '.{path.suffix[1:]}'. Supported extensions are `.csv` or `.parquet`." ) @@ -95,7 +95,7 @@ def read_df( elif path.suffix == ".parquet": return pd.read_parquet(path, engine="pyarrow") raise NotImplementedError( - f"Invalid extension {path.suffix[1:]}. Must be 'csv' or 'parquet'." + f"Invalid extension provided: '.{path.suffix[1:]}'. Supported extensions are `.csv` or `.parquet`." ) @@ -114,7 +114,7 @@ def command_safe_run( As a tuple; the return code, the standard output, and standard error from running the command. Raises: - Exception: If fail_on_fail=True and the command fails, an exception will be thrown. + RuntimeError: If fail_on_fail=True and the command fails, an error will be thrown. """ import subprocess import shlex # using shlex to split the command because it's not obvious https://docs.python.org/3/library/subprocess.html#subprocess.Popen @@ -124,8 +124,8 @@ def command_safe_run( ) (stdout, stderr) = sr.communicate() if sr.returncode != 0: - print(f"{command_name} failed failed with returncode {sr.returncode}") - print(f"{command_name}: {command}") + print(f"'{command_name}' failed failed with returncode '{sr.returncode}'") + print(f"'{command_name}': '{command}'") print("{command_name} command failed with stdout and stderr:") print("{command_name} stdout >>>>>>") @@ -136,7 +136,7 @@ def command_safe_run( print(stderr.decode()) print("{command_name} stderr <<<<<<") if fail_on_fail: - raise Exception(f"{command_name} command failed") + raise RuntimeError(f"The '{command_name}' command failed.") return sr.returncode, stdout, stderr @@ -362,7 +362,7 @@ def as_evaled_expression(self): except TypeError as e: raise ValueError(e) from e else: - raise ValueError(f"expected numeric or string expression [got: {value}]") + raise ValueError(f"Expected numeric or string expression [received: '{value}'].") def get_truncated_normal( @@ -484,7 +484,7 @@ def random_distribution_sampler( elif distribution == "binomial": p = kwargs.get("p") if not (0 < p < 1): - raise ValueError(f"p value {p} is out of range [0,1]") + raise ValueError(f"Invalid `p-value`: '{p}' is out of range [0,1].") return functools.partial(np.random.binomial, kwargs.get("n"), p) elif distribution == "truncnorm": # Truncated normal with mean, sd on interval [a, b] @@ -497,7 +497,7 @@ def random_distribution_sampler( elif distribution == "lognorm": # Lognormal distribution with meanlog, sdlog return get_log_normal(kwargs.get("meanlog"), kwargs.get("sdlog")).rvs - raise NotImplementedError(f"unknown distribution [got: {distribution}]") + raise NotImplementedError(f"Unknown distribution [received: '{distribution}'].") @add_method(confuse.ConfigView) @@ -556,7 +556,7 @@ def as_random_distribution(self): elif dist == "binomial": p = self["p"].as_evaled_expression() if (p < 0) or (p > 1): - raise ValueError(f"""p value { p } is out of range [0,1]""") + raise ValueError(f"Invalid `p-value`: '{p}' is out of range [0,1].") # if (self["p"] < 0) or (self["p"] > 1): # raise ValueError(f"""p value { self["p"] } is out of range [0,1]""") return functools.partial( @@ -578,7 +578,7 @@ def as_random_distribution(self): sdlog=self["sdlog"].as_evaled_expression(), ).rvs else: - raise NotImplementedError(f"unknown distribution [got: {dist}]") + raise NotImplementedError(f"Unknown distribution [received: '{dist}'].") else: # we allow a fixed value specified directly: return functools.partial( @@ -716,13 +716,13 @@ def bash(command: str) -> str: f"shutil.disk_usage: {total_bytes/ 1000000} Mb total, {used_bytes / 1000000} Mb used, {free_bytes / 1000000} Mb free..." ) print("------------") - print(f"df -hT: {bash('df -hT')}") + print(f"df -hT: '{bash('df -hT')}'") print("------------") - print(f"df -i: {bash('df -i')}") + print(f"df -i: '{bash('df -i')}'") print("------------") - print(f"free -h: {bash('free -h')}") + print(f"free -h: '{bash('free -h')}'") print("------------") - print(f"lsblk: {bash('lsblk')}") + print(f"lsblk: '{bash('lsblk')}'") print("END AWS DIAGNOSIS ================================") @@ -1004,8 +1004,8 @@ def download_file_from_s3(name_map: dict[str, str]) -> None: except ModuleNotFoundError: raise ModuleNotFoundError( ( - "No module named 'boto3', which is required for " - "gempyor.utils.download_file_from_s3. Please install the aws target." + "No module named `boto3` found, which is required for " + "`gempyor.utils.download_file_from_s3`. Please install the aws target." ) ) s3 = boto3.client("s3") @@ -1020,10 +1020,9 @@ def download_file_from_s3(name_map: dict[str, str]) -> None: object = s3_uri[len(bucket) + 6 :] s3.download_file(bucket, object, name_map[s3_uri]) else: - raise ValueError(f"Invalid S3 URI format {s3_uri}") + raise ValueError(f"Invalid S3 URI format [received: '{s3_uri}'].") except ClientError as e: - print(f"An error occurred: {e}") - print("Could not download file from s3") + raise Exception(f"'{e}': could not download filefrom S3.") def move_file_at_local(name_map: dict[str, str]) -> None: diff --git a/flepimop/gempyor_pkg/tests/parameters/test_parameters_class.py b/flepimop/gempyor_pkg/tests/parameters/test_parameters_class.py index f82cf2a27..9801a6193 100644 --- a/flepimop/gempyor_pkg/tests/parameters/test_parameters_class.py +++ b/flepimop/gempyor_pkg/tests/parameters/test_parameters_class.py @@ -283,7 +283,7 @@ def test_timeseries_parameter_has_insufficient_dates_value_error( ValueError, match=( f"Issue loading file '{tmp_file}' for parameter 'sigma': " - f"Provided file dates span '{timeseries_start_date}( 00\:00\:00)?' to '{timeseries_end_date}( 00\:00\:00)?', " + f"Provided file dates span '{timeseries_start_date}( 00:00:00)?' to '{timeseries_end_date}( 00:00:00)?', " f"but the config dates span '{mock_inputs.ti}' to '{mock_inputs.tf}'.$" ), ): diff --git a/flepimop/gempyor_pkg/tests/seir/test_model_info.py b/flepimop/gempyor_pkg/tests/seir/test_model_info.py index 3fbf21a75..b241ba263 100644 --- a/flepimop/gempyor_pkg/tests/seir/test_model_info.py +++ b/flepimop/gempyor_pkg/tests/seir/test_model_info.py @@ -4,6 +4,7 @@ import pandas as pd import pytest import confuse +import re from gempyor.model_info import ModelInfo, subpopulation_structure @@ -44,7 +45,10 @@ def test_ModelInfo_init_tf_is_ahead_of_ti_fail(self): config.read(user=False) config.set_file(f"{DATA_DIR}/config_test.yml") config["start_date"] = "2022-01-02" - with pytest.raises(ValueError, match=r"tf.*is less than or equal to ti.*"): + with pytest.raises( + ValueError, + match=re.escape(rf"Final time ('{config['end_date']}') is less than or equal to initial time ('{config['start_date']}')."), + ): s = ModelInfo( config=config, seir_modifiers_scenario=None, diff --git a/flepimop/gempyor_pkg/tests/seir/test_seir.py b/flepimop/gempyor_pkg/tests/seir/test_seir.py index 4f8bda7b3..02c6e1cd8 100644 --- a/flepimop/gempyor_pkg/tests/seir/test_seir.py +++ b/flepimop/gempyor_pkg/tests/seir/test_seir.py @@ -129,7 +129,9 @@ def test_constant_population_legacy_integration(): def test_constant_population_rk4jit_integration_fail(): - with pytest.raises(ValueError, match=r".*with.*method.*integration.*"): + with pytest.raises( + ValueError, match=r"'rk4.jit' integration method only supports deterministic integration.*" + ): config.set_file(f"{DATA_DIR}/config.yml") first_sim_index = 1 diff --git a/flepimop/gempyor_pkg/tests/statistics/test_statistic_class.py b/flepimop/gempyor_pkg/tests/statistics/test_statistic_class.py index 4843986c1..bc2007270 100644 --- a/flepimop/gempyor_pkg/tests/statistics/test_statistic_class.py +++ b/flepimop/gempyor_pkg/tests/statistics/test_statistic_class.py @@ -8,6 +8,7 @@ import pytest import scipy import xarray as xr +import re from gempyor.statistics import Statistic from gempyor.testing import create_confuse_configview_from_dict @@ -248,9 +249,7 @@ def test_unsupported_regularizations_value_error( ] if reg_name not in ["forecast", "allsubpop"] ) - with pytest.raises( - ValueError, match=rf"^Unsupported regularization\: {unsupported_name}$" - ): + with pytest.raises(ValueError, match=rf"^Unsupported regularization \[received: 'invalid'\]"): mock_inputs.create_statistic_instance() @pytest.mark.parametrize("factory", all_valid_factories) @@ -522,10 +521,10 @@ def test_compute_logloss_data_misshape_value_error( model_rows, model_cols = mock_inputs.model_data[mock_inputs.config["sim_var"]].shape gt_rows, gt_cols = mock_inputs.gt_data[mock_inputs.config["data_var"]].shape - expected_match = ( - rf"^{mock_inputs.name} Statistic error\: data and groundtruth do not have " - rf"the same shape\: model\_data\.shape\=\({model_rows}\, {model_cols}\) " - rf"\!\= gt\_data\.shape\=\({gt_rows}\, {gt_cols}\)$" + expected_match = re.escape( + rf"`model_data` and `gt_data` do not have the same shape: " + rf"`model_data.shape` = '{mock_inputs.model_data[mock_inputs.config['sim_var']].shape}' " + rf"!= `gt_data.shape` = '{mock_inputs.gt_data[mock_inputs.config['data_var']].shape}'." ) with pytest.raises(ValueError, match=expected_match): statistic.compute_logloss(mock_inputs.model_data, mock_inputs.gt_data) diff --git a/flepimop/gempyor_pkg/tests/utils/test_random_distribution_sampler.py b/flepimop/gempyor_pkg/tests/utils/test_random_distribution_sampler.py index 4ca9c0ac7..0dcd110fb 100644 --- a/flepimop/gempyor_pkg/tests/utils/test_random_distribution_sampler.py +++ b/flepimop/gempyor_pkg/tests/utils/test_random_distribution_sampler.py @@ -4,6 +4,7 @@ import numpy as np import pytest +import re from gempyor.testing import partials_are_similar from gempyor.utils import random_distribution_sampler @@ -14,7 +15,7 @@ class TestRandomDistributionSampler: def test_not_implemented_error_exception(self, distribution: str) -> None: with pytest.raises( NotImplementedError, - match=rf"^unknown distribution \[got\: {distribution}\]$", + match=re.escape(rf"Unknown distribution [received: '{distribution}']."), ): random_distribution_sampler(distribution) @@ -22,7 +23,7 @@ def test_not_implemented_error_exception(self, distribution: str) -> None: def test_binomial_p_value_error(self, p: float) -> None: with pytest.raises( ValueError, - match=rf"^p value {p} is out of range \[0\,1\]$", + match=re.escape(rf"Invalid `p-value`: '{p}' is out of range [0,1]."), ): random_distribution_sampler("binomial", n=100, p=p) diff --git a/flepimop/gempyor_pkg/tests/utils/test_read_df.py b/flepimop/gempyor_pkg/tests/utils/test_read_df.py index 48f05dbd5..b591aecd9 100644 --- a/flepimop/gempyor_pkg/tests/utils/test_read_df.py +++ b/flepimop/gempyor_pkg/tests/utils/test_read_df.py @@ -38,13 +38,13 @@ def test_raises_not_implemented_error(self) -> None: """ with pytest.raises( expected_exception=NotImplementedError, - match="Invalid extension txt. Must be 'csv' or 'parquet'.", + match=r"Invalid extension provided: '.txt'. Supported extensions are `.csv` or `.parquet`.", ) as _: with NamedTemporaryFile(suffix=".txt") as temp_file: read_df(fname=temp_file.name) with pytest.raises( expected_exception=NotImplementedError, - match="Invalid extension txt. Must be 'csv' or 'parquet'.", + match=r"Invalid extension provided: '.txt'. Supported extensions are `.csv` or `.parquet`.", ) as _: with NamedTemporaryFile(suffix=".txt") as temp_file: fname = temp_file.name[:-4] diff --git a/flepimop/gempyor_pkg/tests/utils/test_utils.py b/flepimop/gempyor_pkg/tests/utils/test_utils.py index 87e7c4fbb..7b6606166 100644 --- a/flepimop/gempyor_pkg/tests/utils/test_utils.py +++ b/flepimop/gempyor_pkg/tests/utils/test_utils.py @@ -37,7 +37,8 @@ def test_read_df_and_write_success(fname, extension): ("fname", "extension"), [("mobility", "csv"), ("usa-geoid-params-output", "parquet")] ) def test_read_df_and_write_fail(fname, extension): - with pytest.raises(NotImplementedError, match=r".*Invalid.*extension.*Must.*"): + with pytest.raises( + NotImplementedError, match=r"Invalid extension provided: '.'. Supported extensions are `.csv` or `.parquet`."): os.chdir(tmp_path) os.makedirs("data", exist_ok=True) os.chdir("data") @@ -56,7 +57,7 @@ def test_read_df_and_write_fail(fname, extension): @pytest.mark.parametrize(("fname", "extension"), [("mobility", "")]) def test_read_df_fail(fname, extension): - with pytest.raises(NotImplementedError, match=r".*Invalid.*extension.*"): + with pytest.raises(NotImplementedError, match=r"Invalid extension provided: '.'. Supported extensions are `.csv` or `.parquet`."): os.chdir(tmp_path) utils.read_df(fname=f"{DATA_DIR}/" + fname, extension=extension) diff --git a/flepimop/gempyor_pkg/tests/utils/test_write_df.py b/flepimop/gempyor_pkg/tests/utils/test_write_df.py index b13e0b948..e63afa2e5 100644 --- a/flepimop/gempyor_pkg/tests/utils/test_write_df.py +++ b/flepimop/gempyor_pkg/tests/utils/test_write_df.py @@ -30,13 +30,13 @@ def test_raises_not_implemented_error(self) -> None: """ with pytest.raises( expected_exception=NotImplementedError, - match="Invalid extension txt. Must be 'csv' or 'parquet'.", + match=r"Invalid extension provided: '.txt'. Supported extensions are `.csv` or `.parquet`.", ) as _: with NamedTemporaryFile(suffix=".txt") as temp_file: write_df(fname=temp_file.name, df=self.sample_df) with pytest.raises( expected_exception=NotImplementedError, - match="Invalid extension txt. Must be 'csv' or 'parquet'.", + match=r"Invalid extension provided: '.txt'. Supported extensions are `.csv` or `.parquet`.", ) as _: with NamedTemporaryFile(suffix=".txt") as temp_file: fname = temp_file.name[:-4]