From 08e8a10ab57702efcac517365a28b020352ecc7b Mon Sep 17 00:00:00 2001 From: Mauricio Villegas <5780272+mauvilsa@users.noreply.github.com> Date: Fri, 24 Oct 2025 06:02:05 +0200 Subject: [PATCH] Path.__call__ is deprecated and will be removed in v5.0.0 --- CHANGELOG.rst | 6 +++++ jsonargparse/_deprecated.py | 9 +++++++ jsonargparse/_paths.py | 10 +------- jsonargparse_tests/test_deprecated.py | 34 +++++++++++++++++++-------- jsonargparse_tests/test_paths.py | 21 +++++++---------- 5 files changed, 49 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 42d0cf87..c79c0733 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -36,6 +36,12 @@ Changed - Non-parsing actions now have a common base class to ease identification (`#793 `__). +Deprecated +^^^^^^^^^^ +- ``Path.__call__`` is deprecated and will be removed in v5.0.0. Use the + ``absolute`` or ``relative`` properties instead (`#794 + `__). + v4.42.0 (2025-10-14) -------------------- diff --git a/jsonargparse/_deprecated.py b/jsonargparse/_deprecated.py index dc1475e2..77be394d 100644 --- a/jsonargparse/_deprecated.py +++ b/jsonargparse/_deprecated.py @@ -439,6 +439,11 @@ def path_skip_check_deprecation(stacklevel=2): -> ``absolute``, ``cwd`` no name change, ``skip_check`` will be removed. """ +path_call_message = """ + Calling Path objects is deprecated and will be removed in v5.0.0. Use the + ``absolute`` or ``relative`` properties instead. +""" + class PathDeprecations: """Deprecated methods for Path.""" @@ -493,6 +498,10 @@ def skip_check(self, skip_check): deprecation_warning("Path attr set", path_immutable_attrs_message) self._skip_check = skip_check + def __call__(self, absolute: bool = True) -> str: + deprecation_warning("Path.__call__", path_call_message) + return self._absolute if absolute else self._relative + class DebugException(Exception): pass diff --git a/jsonargparse/_paths.py b/jsonargparse/_paths.py index 77e37c22..527b44d0 100644 --- a/jsonargparse/_paths.py +++ b/jsonargparse/_paths.py @@ -85,7 +85,7 @@ class PathError(TypeError): class Path(PathDeprecations): - """Stores a (possibly relative) path and the corresponding absolute path. + """Base class for Path types. Stores a (possibly relative) path and the corresponding absolute path. The absolute path can be obtained without having to remember the working directory (or parent remote path) from when the object was created. @@ -283,14 +283,6 @@ def __eq__(self, other: Any) -> bool: return str(self) == other return False - def __call__(self, absolute: bool = True) -> str: - """Returns the path as a string. - - Args: - absolute: If false returns the original path given, otherwise the corresponding absolute path. - """ - return self._absolute if absolute else self._relative - def get_content(self, mode: str = "r") -> str: """Returns the contents of the file or the remote path.""" if self._std_io: diff --git a/jsonargparse_tests/test_deprecated.py b/jsonargparse_tests/test_deprecated.py index d2ec1cfd..4b7a1b92 100644 --- a/jsonargparse_tests/test_deprecated.py +++ b/jsonargparse_tests/test_deprecated.py @@ -57,6 +57,7 @@ ) from jsonargparse_tests.test_dataclasses import DataClassA from jsonargparse_tests.test_jsonnet import example_2_jsonnet +from jsonargparse_tests.test_paths import paths # noqa: F401 @pytest.fixture(autouse=True) @@ -457,29 +458,29 @@ def test_ActionPath(tmp_cwd): parser.add_argument("--files", nargs="+", action=ActionPath(mode="fr")) cfg = parser.parse_args(["--cfg", abs_yaml_file]) - assert str(tmp_cwd) == os.path.realpath(cfg.dir(absolute=True)) - assert abs_yaml_file == os.path.realpath(cfg.cfg[0](absolute=False)) - assert abs_yaml_file == os.path.realpath(cfg.cfg[0](absolute=True)) - assert rel_yaml_file == cfg.file(absolute=False) - assert abs_yaml_file == os.path.realpath(cfg.file(absolute=True)) + assert str(tmp_cwd) == os.path.realpath(cfg.dir.absolute) + assert abs_yaml_file == os.path.realpath(cfg.cfg[0].relative) + assert abs_yaml_file == os.path.realpath(cfg.cfg[0].absolute) + assert rel_yaml_file == cfg.file.relative + assert abs_yaml_file == os.path.realpath(cfg.file.absolute) pytest.raises(ArgumentError, lambda: parser.parse_args(["--cfg", abs_yaml_file + "~"])) cfg = parser.parse_args(["--cfg", "file: " + abs_yaml_file + "\ndir: " + str(tmp_cwd) + "\n"]) - assert str(tmp_cwd) == os.path.realpath(cfg.dir(absolute=True)) + assert str(tmp_cwd) == os.path.realpath(cfg.dir.absolute) assert cfg.cfg[0] is None - assert abs_yaml_file == os.path.realpath(cfg.file(absolute=True)) + assert abs_yaml_file == os.path.realpath(cfg.file.absolute) pytest.raises(ArgumentError, lambda: parser.parse_args(["--cfg", '{"k":"v"}'])) cfg = parser.parse_args(["--file", abs_yaml_file, "--dir", str(tmp_cwd)]) - assert str(tmp_cwd) == os.path.realpath(cfg.dir(absolute=True)) - assert abs_yaml_file == os.path.realpath(cfg.file(absolute=True)) + assert str(tmp_cwd) == os.path.realpath(cfg.dir.absolute) + assert abs_yaml_file == os.path.realpath(cfg.file.absolute) pytest.raises(ArgumentError, lambda: parser.parse_args(["--dir", abs_yaml_file])) pytest.raises(ArgumentError, lambda: parser.parse_args(["--file", str(tmp_cwd)])) cfg = parser.parse_args(["--files", abs_yaml_file, abs_yaml_file]) assert isinstance(cfg.files, list) assert 2 == len(cfg.files) - assert abs_yaml_file == os.path.realpath(cfg.files[-1](absolute=True)) + assert abs_yaml_file == os.path.realpath(cfg.files[-1].absolute) pytest.raises(TypeError, lambda: parser.add_argument("--op1", action=ActionPath)) pytest.raises( @@ -545,6 +546,19 @@ def test_Path_attr_set(tmp_cwd): assert "Path objects are not meant to be mutable" in str(w[-1].message) +def test_path_call(paths): # noqa: F811 + path = Path(paths.file_rw, "frw") + with catch_warnings(record=True) as w: + assert path(False) == str(paths.file_rw) + assert_deprecation_warn( + w, + message="Calling Path objects is deprecated", + code="assert path(False) == ", + ) + assert path(True) == str(paths.tmp_path / paths.file_rw) + assert path() == str(paths.tmp_path / paths.file_rw) + + def test_ActionPathList(tmp_cwd): tmpdir = os.path.join(tmp_cwd, "subdir") os.mkdir(tmpdir) diff --git a/jsonargparse_tests/test_paths.py b/jsonargparse_tests/test_paths.py index 29e1f235..bfbe000e 100644 --- a/jsonargparse_tests/test_paths.py +++ b/jsonargparse_tests/test_paths.py @@ -115,7 +115,7 @@ def test_path_cwd(paths): def test_path_empty_mode(paths): path = Path("does_not_exist", "") - assert path() == str(paths.tmp_path / "does_not_exist") + assert path.absolute == str(paths.tmp_path / "does_not_exist") def test_path_pathlike(paths): @@ -198,9 +198,6 @@ def test_path_invalid_modes(paths): def test_path_class_hidden_methods(paths): path = Path(paths.file_rw, "frw") - assert path(False) == str(paths.file_rw) - assert path(True) == str(paths.tmp_path / paths.file_rw) - assert path() == str(paths.tmp_path / paths.file_rw) assert str(path) == str(paths.file_rw) assert path.__repr__().startswith("Path_frw(") @@ -212,8 +209,8 @@ def test_path_tilde_home(paths): path = Path(os.path.join("~", paths.file_rw), "frw") assert str(home) == "~" assert str(path) == os.path.join("~", paths.file_rw) - assert home() == str(paths.tmp_path) - assert path() == os.path.join(paths.tmp_path, paths.file_rw) + assert home.absolute == str(paths.tmp_path) + assert path.absolute == os.path.join(paths.tmp_path, paths.file_rw) def test_std_input_path(): @@ -396,7 +393,7 @@ def test_path_relative_path_context_url(): with path1.relative_path_context() as dir: assert "http://example.com/nested/path" == dir path2 = Path("../file2.txt", mode="u") - assert path2() == "http://example.com/nested/file2.txt" + assert path2.absolute == "http://example.com/nested/file2.txt" @skip_if_fsspec_unavailable @@ -421,18 +418,18 @@ def test_relative_path_context_fsspec(tmp_cwd, subtests): path1 = Path("../file1.txt", mode="fsr") assert "one" == path1.get_content() assert str(path1) == "../file1.txt" - assert path1() == "memory://one/two/file1.txt" + assert path1.absolute == "memory://one/two/file1.txt" assert path1._url_data is not None with subtests.test("nested fsspec dir"): with path1.relative_path_context() as dir2: assert "memory://one/two" == dir2 path2 = Path("four/five/six/../file2.txt", mode="fsc") - assert path2() == "memory://one/two/four/five/file2.txt" + assert path2.absolute == "memory://one/two/four/five/file2.txt" with subtests.test("non-fsspec path"): path3 = Path("file3.txt", mode="fc") - assert path3() == str(tmp_cwd / "file3.txt") + assert path3.absolute == str(tmp_cwd / "file3.txt") with subtests.test("current path dir unset"): assert _current_path_dir.get() is None @@ -444,13 +441,13 @@ def test_relative_path_context_fsspec(tmp_cwd, subtests): def test_path_fr(file_r): path = Path_fr(file_r) assert path == file_r - assert path() == os.path.realpath(file_r) + assert path.absolute == os.path.realpath(file_r) pytest.raises(TypeError, lambda: Path_fr("does_not_exist")) def test_path_fc_with_kwargs(tmpdir): path = Path_fc("some-file.txt", cwd=tmpdir) - assert path() == os.path.join(tmpdir, "some-file.txt") + assert path.absolute == os.path.join(tmpdir, "some-file.txt") def test_path_fr_already_registered():