From 7f05bc5f69e4428dbfb631a558a85ea314d6f672 Mon Sep 17 00:00:00 2001 From: RalfG Date: Sat, 1 Aug 2026 12:58:29 +0200 Subject: [PATCH] fix: force utf-8 encoding on all text file reads/writes Several read_text()/open() calls relied on the platform's default encoding (locale-dependent), which breaks on ASCII-locale systems (e.g. bioconda's Linux test env, UnicodeDecodeError on texts.toml) and mis-encodes on non-UTF-8 Windows locales (e.g. French cp1252). --- ms2rescore/__main__.py | 2 +- ms2rescore/config_parser.py | 8 ++++++-- ms2rescore/core.py | 4 ++-- ms2rescore/report/data.py | 2 +- ms2rescore/report/generate.py | 4 +++- ms2rescore/report/utils.py | 2 +- 6 files changed, 14 insertions(+), 8 deletions(-) diff --git a/ms2rescore/__main__.py b/ms2rescore/__main__.py index c3d42dc..f2426b1 100644 --- a/ms2rescore/__main__.py +++ b/ms2rescore/__main__.py @@ -218,7 +218,7 @@ def main(tims=False): json.loads( importlib.resources.files(package_data) .joinpath("config_default_tims.json") - .read_text() + .read_text(encoding="utf-8") ) ) if cli_args.config_file: diff --git a/ms2rescore/config_parser.py b/ms2rescore/config_parser.py index c1c57be..817b223 100644 --- a/ms2rescore/config_parser.py +++ b/ms2rescore/config_parser.py @@ -130,10 +130,14 @@ def parse_configurations(configurations: list[dict | str | Path | Namespace]) -> # Initialize CascadeConfig with validation schema and defaults config_schema = json.loads( - importlib.resources.files(package_data).joinpath("config_schema.json").read_text() + importlib.resources.files(package_data) + .joinpath("config_schema.json") + .read_text(encoding="utf-8") ) config_default = json.loads( - importlib.resources.files(package_data).joinpath("config_default.json").read_text() + importlib.resources.files(package_data) + .joinpath("config_default.json") + .read_text(encoding="utf-8") ) cascade_conf = CascadeConfig( validation_schema=config_schema, diff --git a/ms2rescore/core.py b/ms2rescore/core.py index f36a3eb..fb53cab 100644 --- a/ms2rescore/core.py +++ b/ms2rescore/core.py @@ -36,7 +36,7 @@ def rescore(configuration: dict, psm_list: PSMList | None = None) -> None: ] # if no intermediate, takes full name # Write full configuration including defaults to file - with open(output_file_root + ".full-config.json", "w") as f: + with open(output_file_root + ".full-config.json", "w", encoding="utf-8") as f: json.dump(configuration, f, indent=4) logger.debug("Using %i of %i available CPUs.", int(config["processes"]), int(cpu_count())) @@ -273,7 +273,7 @@ def rescore(configuration: dict, psm_list: PSMList | None = None) -> None: def _write_feature_names(feature_names, output_file_root): """Write feature names to file.""" - with open(output_file_root + ".feature_names.tsv", "w") as f: + with open(output_file_root + ".feature_names.tsv", "w", encoding="utf-8") as f: f.write("feature_generator\tfeature_name\n") for fgen, fgen_features in feature_names.items(): f.writelines(f"{fgen}\t{feature}\n" for feature in fgen_features) diff --git a/ms2rescore/report/data.py b/ms2rescore/report/data.py index 21ea81e..dee871a 100644 --- a/ms2rescore/report/data.py +++ b/ms2rescore/report/data.py @@ -184,7 +184,7 @@ def _read_config(path: Path) -> dict: logger.info("No configuration file found. Proceeding without it.") return {"ms2rescore": {}} try: - return json.loads(path.read_text()) + return json.loads(path.read_text(encoding="utf-8")) except (json.JSONDecodeError, OSError): logger.warning("Could not read configuration file. Proceeding without it.") return {"ms2rescore": {}} diff --git a/ms2rescore/report/generate.py b/ms2rescore/report/generate.py index 92b74c8..88ee06b 100644 --- a/ms2rescore/report/generate.py +++ b/ms2rescore/report/generate.py @@ -32,7 +32,9 @@ # charts (DeepLC, IM2Deep, MS²PIP) reuse the same color as the feature-generator overview charts. FEATURE_GENERATOR_COLORS = charts.FEATURE_GENERATOR_COLORS -TEXTS = tomllib.loads(importlib.resources.files(templates).joinpath("texts.toml").read_text()) +TEXTS = tomllib.loads( + importlib.resources.files(templates).joinpath("texts.toml").read_text(encoding="utf-8") +) def generate_report( diff --git a/ms2rescore/report/utils.py b/ms2rescore/report/utils.py index bfcffe1..a555149 100644 --- a/ms2rescore/report/utils.py +++ b/ms2rescore/report/utils.py @@ -28,7 +28,7 @@ def read_feature_names(feature_names_path: Path | None) -> dict: return feature_names try: - with open(feature_names_path) as f: + with open(feature_names_path, encoding="utf-8") as f: reader = DictReader(f, delimiter="\t") for line in reader: feature_names[line["feature_generator"]].append(line["feature_name"])