From c4c255d4a9e1ca1232de0304a9bfe9303a4b333e Mon Sep 17 00:00:00 2001 From: Jammy2211 Date: Thu, 7 May 2026 08:42:23 +0100 Subject: [PATCH] refactor: replace os.path with pathlib Drop legacy `from os import path` and `os.path.X` usages in favour of `pathlib.Path`. Internal-only refactor; no public API changes. Refs PyAutoLabs/PyAutoFit#1257. Co-Authored-By: Claude Opus 4.7 (1M context) --- autofit/aggregator/aggregator.py | 7 +++---- autofit/aggregator/search_output.py | 3 +-- autofit/database/__init__.py | 3 ++- autofit/mapper/model_mapper.py | 4 ++-- autofit/non_linear/grid/grid_search/__init__.py | 8 ++------ autofit/non_linear/paths/abstract.py | 7 +++---- autofit/non_linear/paths/directory.py | 11 +++++------ .../non_linear/search/mcmc/blackjax/nuts/search.py | 3 ++- autofit/non_linear/search/mcmc/emcee/search.py | 3 ++- .../search/nest/dynesty/search/abstract.py | 3 ++- autofit/non_linear/search/nest/nautilus/search.py | 3 ++- autofit/tools/util.py | 10 +++++----- docs/conf.py | 6 +++--- test_autofit/aggregator/conftest.py | 3 +-- test_autofit/conftest.py | 9 ++++----- test_autofit/database/paths/test_paths.py | 9 ++------- test_autofit/database/paths/test_switch.py | 4 +--- test_autofit/non_linear/paths/test_paths.py | 3 +-- test_autofit/non_linear/samples/test_samples.py | 3 ++- test_autofit/non_linear/test_parallel.py | 4 ++-- test_autofit/text/files/text/model.results | 2 ++ test_autofit/text/test_formatter.py | 10 +++++----- test_autofit/text/test_samples_text.py | 4 ++-- test_autofit/text/test_text_util.py | 6 +++--- test_autofit/tools/test_path_util.py | 12 ++++++------ test_autofit/tools/test_paths.py | 7 +++---- 26 files changed, 68 insertions(+), 79 deletions(-) create mode 100644 test_autofit/text/files/text/model.results diff --git a/autofit/aggregator/aggregator.py b/autofit/aggregator/aggregator.py index e6a74e632..38dca8bcb 100755 --- a/autofit/aggregator/aggregator.py +++ b/autofit/aggregator/aggregator.py @@ -15,7 +15,6 @@ import os import zipfile from collections import defaultdict -from os import path from pathlib import Path from shutil import rmtree from typing import List, Union, Iterator, Optional @@ -84,8 +83,8 @@ def unzip_directory(directory: str): for filename in filenames: if filename.endswith(".zip"): try: - with zipfile.ZipFile(path.join(root, filename), "r") as f: - f.extractall(path.join(root, filename[:-4])) + with zipfile.ZipFile(Path(root) / filename, "r") as f: + f.extractall(Path(root) / filename[:-4]) except zipfile.BadZipFile: raise zipfile.BadZipFile( f"File is not a zip file: \n " f"{root} \n" f"{filename}" @@ -213,7 +212,7 @@ def remove_unzipped(self): Removes the unzipped output directory for each phase. """ for phase in self.search_outputs: - split_path = path.split(phase.directory)[0] + split_path = Path(phase.directory).parent rmtree(split_path, ignore_errors=True) diff --git a/autofit/aggregator/search_output.py b/autofit/aggregator/search_output.py index d4484dbdf..b1a4d0f93 100644 --- a/autofit/aggregator/search_output.py +++ b/autofit/aggregator/search_output.py @@ -3,7 +3,6 @@ import logging import pickle from abc import ABC -from os import path from pathlib import Path from typing import Generator, Tuple, Optional, List, cast, Type @@ -372,7 +371,7 @@ def header(self) -> str: """ phase = self.phase or "" dataset_name = self.dataset_name or "" - return path.join(phase, dataset_name) + return str(Path(phase) / dataset_name) if dataset_name else phase @property def search(self): diff --git a/autofit/database/__init__.py b/autofit/database/__init__.py index c2e409514..a54c3beb0 100644 --- a/autofit/database/__init__.py +++ b/autofit/database/__init__.py @@ -1,4 +1,5 @@ import os +from pathlib import Path from .sqlalchemy_ import sa @@ -66,7 +67,7 @@ def open_database( exist_ok=True ) - exists = os.path.exists(filename) + exists = Path(filename).exists() string = f'sqlite:///{filename}' kwargs = dict( diff --git a/autofit/mapper/model_mapper.py b/autofit/mapper/model_mapper.py index a35a06c5a..93f9f82e8 100644 --- a/autofit/mapper/model_mapper.py +++ b/autofit/mapper/model_mapper.py @@ -1,8 +1,8 @@ -import os +from pathlib import Path from autofit.mapper.prior_model.collection import Collection -path = os.path.dirname(os.path.realpath(__file__)) +path = Path(__file__).resolve().parent class ModelMapper(Collection): diff --git a/autofit/non_linear/grid/grid_search/__init__.py b/autofit/non_linear/grid/grid_search/__init__.py index 5c42d201f..b28567410 100644 --- a/autofit/non_linear/grid/grid_search/__init__.py +++ b/autofit/non_linear/grid/grid_search/__init__.py @@ -1,7 +1,7 @@ import copy import logging import os -from os import path +from pathlib import Path from typing import List, Tuple, Union, Type, Optional, Dict from autoconf.dictable import to_dict @@ -312,11 +312,7 @@ def job_for_analysis_grid_priors_and_values( ) ) - name_path = path.join( - self.paths.name, - self.paths.identifier, - "_".join(labels), - ) + name_path = str(Path(self.paths.name) / self.paths.identifier / "_".join(labels)) search_instance = self.search_instance(name_path=name_path) search_instance.paths.model = model diff --git a/autofit/non_linear/paths/abstract.py b/autofit/non_linear/paths/abstract.py index 4d0f26e37..edccecb4c 100644 --- a/autofit/non_linear/paths/abstract.py +++ b/autofit/non_linear/paths/abstract.py @@ -5,7 +5,6 @@ import zipfile from abc import ABC, abstractmethod from configparser import NoSectionError -from os import path from pathlib import Path from typing import Optional @@ -224,7 +223,7 @@ def image_path(self) -> Path: The path to the image folder. """ - if not os.path.exists(self.output_path / f"image{self.image_path_suffix}"): + if not (self.output_path / f"image{self.image_path_suffix}").exists(): os.makedirs(self.output_path / f"image{self.image_path_suffix}") return self.output_path / f"image{self.image_path_suffix}" @@ -257,7 +256,7 @@ def output_path(self) -> Path: if self.is_identifier_in_paths: strings.append(self.identifier) - return Path(path.join("", *strings)) + return Path(*strings) if strings else Path("") @property def _files_path(self) -> Path: @@ -296,7 +295,7 @@ def restore(self): Copy files from the ``.zip`` file to the samples folder. """ - if path.exists(self._zip_path): + if Path(self._zip_path).exists(): shutil.rmtree(self.output_path, ignore_errors=True) try: diff --git a/autofit/non_linear/paths/directory.py b/autofit/non_linear/paths/directory.py index 9e0818ebf..d00edb5ad 100644 --- a/autofit/non_linear/paths/directory.py +++ b/autofit/non_linear/paths/directory.py @@ -4,7 +4,6 @@ import json import numpy as np import os -from os import path from pathlib import Path from typing import Optional, Union, cast, Type import logging @@ -175,14 +174,14 @@ def is_object(self, name: str) -> bool: """ Is there a file pickles/{name}.pickle? """ - return os.path.exists(self._path_for_pickle(name)) + return self._path_for_pickle(name).exists() @property def is_complete(self) -> bool: """ Has the search been completed? """ - return path.exists(self._has_completed_path) + return self._has_completed_path.exists() def save_search_internal(self, obj): """ @@ -214,7 +213,7 @@ def load_search_internal(self): import emcee backend_filename = self.search_internal_path / "search_internal.hdf" - if os.path.isfile(backend_filename): + if backend_filename.is_file(): return emcee.backends.HDFBackend(filename=str(backend_filename)) except ImportError: pass @@ -400,7 +399,7 @@ def is_grid_search(self) -> bool: """ Is this a grid search which comprises a number of child searches? """ - return os.path.exists(self._grid_search_path) + return self._grid_search_path.exists() def create_child( self, @@ -533,7 +532,7 @@ def _make_path(self) -> str: The path terminates with the identifier, unless the identifier has already been added to the path. """ - path_ = Path(path.join(conf.instance.output_path, self.path_prefix, self.name)) + path_ = Path(conf.instance.output_path) / self.path_prefix / self.name if self.is_identifier_in_paths: path_ = path_ / self.identifier return path_ diff --git a/autofit/non_linear/search/mcmc/blackjax/nuts/search.py b/autofit/non_linear/search/mcmc/blackjax/nuts/search.py index 32cb37c7c..6bf9e9e57 100644 --- a/autofit/non_linear/search/mcmc/blackjax/nuts/search.py +++ b/autofit/non_linear/search/mcmc/blackjax/nuts/search.py @@ -1,6 +1,7 @@ import logging import os import pickle +from pathlib import Path from typing import Optional import numpy as np @@ -349,7 +350,7 @@ def backend_filename(self): @property def backend(self) -> dict: """Load the pickled search-internal dict written by ``_fit``.""" - if not os.path.isfile(self.backend_filename): + if not Path(self.backend_filename).is_file(): raise FileNotFoundError( f"search_internal.pickle does not exist at " f"{self.paths.search_internal_path}" diff --git a/autofit/non_linear/search/mcmc/emcee/search.py b/autofit/non_linear/search/mcmc/emcee/search.py index 29140bf5b..fc8118881 100644 --- a/autofit/non_linear/search/mcmc/emcee/search.py +++ b/autofit/non_linear/search/mcmc/emcee/search.py @@ -1,5 +1,6 @@ import logging import os +from pathlib import Path from typing import Dict, Optional import numpy as np @@ -371,7 +372,7 @@ def backend(self) -> "emcee.backends.HDFBackend": """ import emcee - if os.path.isfile(self.backend_filename): + if Path(self.backend_filename).is_file(): return emcee.backends.HDFBackend(filename=str(self.backend_filename)) else: raise FileNotFoundError( diff --git a/autofit/non_linear/search/nest/dynesty/search/abstract.py b/autofit/non_linear/search/nest/dynesty/search/abstract.py index bc53febed..4b1f3cb0e 100644 --- a/autofit/non_linear/search/nest/dynesty/search/abstract.py +++ b/autofit/non_linear/search/nest/dynesty/search/abstract.py @@ -1,6 +1,7 @@ import logging import os from abc import ABC +from pathlib import Path from typing import Dict, Optional, Tuple, Union import numpy as np @@ -185,7 +186,7 @@ def _fit( ) if not isinstance(self.paths, NullPaths): - checkpoint_exists = os.path.exists(self.checkpoint_file) + checkpoint_exists = Path(self.checkpoint_file).exists() else: checkpoint_exists = False diff --git a/autofit/non_linear/search/nest/nautilus/search.py b/autofit/non_linear/search/nest/nautilus/search.py index 6039ed49a..f5bec9730 100644 --- a/autofit/non_linear/search/nest/nautilus/search.py +++ b/autofit/non_linear/search/nest/nautilus/search.py @@ -2,6 +2,7 @@ import logging import os import sys +from pathlib import Path from typing import Dict, Optional, Tuple from autofit.database.sqlalchemy_ import sa @@ -169,7 +170,7 @@ def _fit(self, model: AbstractPriorModel, analysis): """ if not isinstance(self.paths, NullPaths): - checkpoint_exists = os.path.exists(self.checkpoint_file) + checkpoint_exists = Path(self.checkpoint_file).exists() else: checkpoint_exists = False diff --git a/autofit/tools/util.py b/autofit/tools/util.py index 34286e549..44991bb87 100644 --- a/autofit/tools/util.py +++ b/autofit/tools/util.py @@ -46,8 +46,8 @@ def zip_directory(source_directory, output=None): for root, dirs, files in os.walk(source_directory): for file in files: f.write( - os.path.join(root, file), - os.path.join(root[len(str(source_directory)) :], file), + Path(root) / file, + Path(root[len(str(source_directory)):]) / file, ) @@ -94,12 +94,12 @@ def numpy_array_to_json( numpy_array_to_json(array_2d=array_2d, file_path='/path/to/file/filename.json', overwrite=True) """ - file_dir = os.path.split(file_path)[0] + file_dir = Path(file_path).parent - if not os.path.exists(file_dir): + if not file_dir.exists(): os.makedirs(file_dir) - if overwrite and os.path.exists(file_path): + if overwrite and Path(file_path).exists(): os.remove(file_path) with open(file_path, "w+") as f: diff --git a/docs/conf.py b/docs/conf.py index 5fd60ed53..c26a291bf 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -9,13 +9,13 @@ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. +# documentation root, use Path(...).resolve() to make it absolute, like shown here. # -import os import sys +from pathlib import Path -sys.path.insert(0, os.path.abspath(".")) +sys.path.insert(0, str(Path(".").resolve())) import autofit diff --git a/test_autofit/aggregator/conftest.py b/test_autofit/aggregator/conftest.py index 0dbc3258d..a2deb2e51 100644 --- a/test_autofit/aggregator/conftest.py +++ b/test_autofit/aggregator/conftest.py @@ -1,4 +1,3 @@ -from os import path from pathlib import Path import pytest @@ -28,7 +27,7 @@ def make_directory(): @pytest.fixture(name="aggregator_directory") def make_aggregator_directory(directory): - directory = path.dirname(path.realpath(__file__)) + directory = Path(__file__).resolve().parent return directory.parent / "tools" / "files" / "aggregator" diff --git a/test_autofit/conftest.py b/test_autofit/conftest.py index 2eeb884ae..8c972f553 100644 --- a/test_autofit/conftest.py +++ b/test_autofit/conftest.py @@ -2,7 +2,6 @@ import os import shutil import sys -from os import path from pathlib import Path from unittest.mock import MagicMock @@ -79,7 +78,7 @@ def __init__(self): self.paths = [] def __call__(self, path, *args, **kwargs): - self.paths.append(path) + self.paths.append(str(path)) @pytest.fixture(name="plot_patch") @@ -105,14 +104,14 @@ def remove_logs(): for d, _, files in os.walk(directory): for file in files: if file.endswith(".log"): - os.remove(path.join(d, file)) + os.remove(Path(d) / file) @pytest.fixture(autouse=True) def set_config_path(): conf.instance.push( - new_path=path.join(directory, "config"), - output_path=path.join(directory, "output"), + new_path=str(directory / "config"), + output_path=str(directory / "output"), ) diff --git a/test_autofit/database/paths/test_paths.py b/test_autofit/database/paths/test_paths.py index 4d0f042ad..83851e72d 100644 --- a/test_autofit/database/paths/test_paths.py +++ b/test_autofit/database/paths/test_paths.py @@ -1,5 +1,4 @@ import logging -import os from pathlib import Path import pytest @@ -29,9 +28,7 @@ def query_fit(session, paths): ) def test_create(): m.open_database("test.sqlite") - assert os.path.exists( - output_path - ) + assert Path(output_path).exists() @output_path_for_test( @@ -53,9 +50,7 @@ def test_create_postgres(): ) except Exception as e: logging.exception(e) - assert not os.path.exists( - output_path - ) + assert not Path(output_path).exists() def test_incomplete(paths): diff --git a/test_autofit/database/paths/test_switch.py b/test_autofit/database/paths/test_switch.py index 56e52f189..04a251e88 100644 --- a/test_autofit/database/paths/test_switch.py +++ b/test_autofit/database/paths/test_switch.py @@ -69,6 +69,4 @@ def test_is_database_paths(search): # Analysis() # ) # -# assert not os.path.exists( -# output_path -# ) +# assert not Path(output_path).exists() diff --git a/test_autofit/non_linear/paths/test_paths.py b/test_autofit/non_linear/paths/test_paths.py index acc791e31..e36cf5ed4 100644 --- a/test_autofit/non_linear/paths/test_paths.py +++ b/test_autofit/non_linear/paths/test_paths.py @@ -1,4 +1,3 @@ -import os import pickle from pathlib import Path @@ -61,7 +60,7 @@ def test_identifier_file(model): paths.search = af.DynestyStatic() paths.save_all({}, {}) - assert os.path.exists(output_path / paths.identifier / ".identifier") + assert (output_path / paths.identifier / ".identifier").exists() def test_serialize(model): diff --git a/test_autofit/non_linear/samples/test_samples.py b/test_autofit/non_linear/samples/test_samples.py index 428715e96..00d2b4f08 100644 --- a/test_autofit/non_linear/samples/test_samples.py +++ b/test_autofit/non_linear/samples/test_samples.py @@ -1,4 +1,5 @@ import os +from pathlib import Path import pytest import autofit as af @@ -55,7 +56,7 @@ def test__table__write_table(): filename = "samples.csv" samples_x5.write_table(filename=filename) - assert os.path.exists(filename) + assert Path(filename).exists() os.remove(filename) diff --git a/test_autofit/non_linear/test_parallel.py b/test_autofit/non_linear/test_parallel.py index 466cd8acd..ff1e654b0 100644 --- a/test_autofit/non_linear/test_parallel.py +++ b/test_autofit/non_linear/test_parallel.py @@ -1,7 +1,7 @@ import autofit as af from autofit.non_linear.parallel.sneaky import SneakyProcess -from os import path +from pathlib import Path class MockSneakyProcess(SneakyProcess): def __init__( @@ -17,7 +17,7 @@ def __init__( def test__test_mode_parallel_profile_outputs_prof_files(): paths = af.DirectoryPaths( - path_prefix=path.join("non_linear", "parallel"), + path_prefix=str(Path("non_linear") / "parallel"), ) process = MockSneakyProcess(paths=paths) diff --git a/test_autofit/text/files/text/model.results b/test_autofit/text/files/text/model.results new file mode 100644 index 000000000..70e8a1fb0 --- /dev/null +++ b/test_autofit/text/files/text/model.results @@ -0,0 +1,2 @@ +hi +hello \ No newline at end of file diff --git a/test_autofit/text/test_formatter.py b/test_autofit/text/test_formatter.py index 93b57c97e..f913384ff 100644 --- a/test_autofit/text/test_formatter.py +++ b/test_autofit/text/test_formatter.py @@ -1,10 +1,10 @@ import os import shutil -from os import path +from pathlib import Path from autofit.text import formatter as frm -text_path = path.join("{}".format(path.dirname(path.realpath(__file__))), "files", "text") +text_path = Path(__file__).resolve().parent / "files" / "text" def test__value_result_string(): @@ -56,17 +56,17 @@ def test__parameter_result_latex(): def test__output_list_of_strings_to_file(): - if path.exists(text_path): + if text_path.exists(): shutil.rmtree(text_path) os.mkdir(text_path) results = ["hi\n", "hello"] frm.output_list_of_strings_to_file( - file=text_path + "model.results", list_of_strings=results + file=text_path / "model.results", list_of_strings=results ) - file = open(text_path + "model.results", "r") + file = open(text_path / "model.results", "r") assert file.readlines() == ["hi\n", "hello"] diff --git a/test_autofit/text/test_samples_text.py b/test_autofit/text/test_samples_text.py index 31d5e1598..12eb148cb 100644 --- a/test_autofit/text/test_samples_text.py +++ b/test_autofit/text/test_samples_text.py @@ -1,4 +1,4 @@ -from os import path +from pathlib import Path import pytest @@ -8,7 +8,7 @@ from autofit import SamplesStored from autofit.text import samples_text -text_path = path.join("{}".format(path.dirname(path.realpath(__file__))), "files", "samples") +text_path = Path(__file__).resolve().parent / "files" / "samples" @pytest.fixture(name="model") diff --git a/test_autofit/text/test_text_util.py b/test_autofit/text/test_text_util.py index 532c5f51e..3999152da 100644 --- a/test_autofit/text/test_text_util.py +++ b/test_autofit/text/test_text_util.py @@ -1,11 +1,11 @@ -from os import path +from pathlib import Path import pytest import autofit as af from autofit.text import text_util -text_path = path.join("{}".format(path.dirname(path.realpath(__file__))), "files", "samples") +text_path = Path(__file__).resolve().parent / "files" / "samples" @pytest.fixture(name="model") @@ -40,7 +40,7 @@ def test__results_to_file(samples): assert "Maximum Log Likelihood 1.00000000\n" in result_info def test__search_summary_to_file(model): - file_search_summary = path.join(text_path, "search.summary") + file_search_summary = text_path / "search.summary" parameters = [[1.0, 2.0], [1.2, 2.2]] diff --git a/test_autofit/tools/test_path_util.py b/test_autofit/tools/test_path_util.py index b60c05795..f97a7365d 100644 --- a/test_autofit/tools/test_path_util.py +++ b/test_autofit/tools/test_path_util.py @@ -1,23 +1,23 @@ import os -from os import path +from pathlib import Path import numpy as np import autofit as af -test_path = path.join("{}".format(path.dirname(path.realpath(__file__))), "files", "path") +test_path = Path(__file__).resolve().parent / "files" / "path" class TestJson: def test__numpy_array_to_json__output_and_load(self): - if path.exists(test_path + "array_out.json"): - os.remove(test_path + "array_out.json") + if (test_path / "array_out.json").exists(): + os.remove(test_path / "array_out.json") arr = np.array([10.0, 30.0, 40.0, 92.0, 19.0, 20.0]) - af.util.numpy_array_to_json(arr, file_path=test_path + "array_out.json") + af.util.numpy_array_to_json(arr, file_path=test_path / "array_out.json") array_load = af.util.numpy_array_from_json( - file_path=test_path + "array_out.json" + file_path=test_path / "array_out.json" ) assert (arr == array_load).all() diff --git a/test_autofit/tools/test_paths.py b/test_autofit/tools/test_paths.py index d001c1e27..7cbdf7f0f 100644 --- a/test_autofit/tools/test_paths.py +++ b/test_autofit/tools/test_paths.py @@ -1,11 +1,10 @@ import os import shutil -from os import path +from pathlib import Path import pytest import autofit as af -from pathlib import Path directory = Path(__file__).parent @@ -32,7 +31,7 @@ def test_restore(paths): paths.zip_remove() paths.restore() - assert path.exists(paths.output_path) - assert not path.exists(paths._zip_path) + assert paths.output_path.exists() + assert not Path(paths._zip_path).exists() shutil.rmtree(paths.output_path)