Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ Deprecated
deprecated and will be removed in v5.0.0. Instead run ``.keys()`` and then
sort or get the parent and leaf separately. (`#900
<https://github.com/omni-us/jsonargparse/pull/900>`__).
- ``Path.get_content`` is deprecated and will be removed in v5.0.0. Instead use
``Path.read_text`` for text and ``Path.open`` for binary content (`#906
<https://github.com/omni-us/jsonargparse/pull/906>`__).


v4.48.0 (2026-04-10)
Expand Down
8 changes: 4 additions & 4 deletions DOCUMENTATION.rst
Original file line number Diff line number Diff line change
Expand Up @@ -651,8 +651,8 @@ actual path, thus for the previous example:
'/.../app/data/info.db'

The content of a file referenced by a :class:`.Path` instance can be read using
the :py:meth:`.Path.get_content` method. For the previous example, this would be
``info_db = cfg.databases.info.get_content()``.
the :py:meth:`.Path.read_text` method. For the previous example, this would be
``info_db = cfg.databases.info.read_text()``.

An argument with a path type can be given ``nargs='+'`` to parse multiple paths.
Thus, from command line you could do ``--files file1 file2``, separated by
Expand Down Expand Up @@ -721,7 +721,7 @@ Parsing URLs
------------

The :func:`.path_type` function also supports URLs which after parsing, the
:py:meth:`.Path.get_content` method can be used to perform a GET request to the
:py:meth:`.Path.read_text` method can be used to perform a GET request to the
corresponding URL and retrieve its content. For this to work the *requests*
Python package is required. Alternatively, :func:`.path_type` can also be used
for `fsspec <https://filesystem-spec.readthedocs.io>`__ supported file systems.
Expand All @@ -735,7 +735,7 @@ either a readable file or URL, the type would be created as ``Path_fur =
path_type('fur')``. If the value appears to be a URL, a HEAD request would be
triggered to check if it is accessible. To get the content of the parsed path,
without needing to care if it is a local file or a URL, the
:py:meth:`.Path.get_content` method can be used.
:py:meth:`.Path.read_text` method can be used.

If you import ``from jsonargparse import set_parsing_settings`` and then run
``set_parsing_settings(config_read_mode_urls_enabled=True)`` or
Expand Down
6 changes: 3 additions & 3 deletions jsonargparse/_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,7 @@ def parse_path(
"""
fpath = Path(cfg_path, mode=_get_config_read_mode())
with change_to_path_dir(fpath):
cfg_str = fpath.get_content()
cfg_str = fpath.read_text()
parsed_cfg = self.parse_string(
cfg_str=cfg_str,
cfg_path=os.path.basename(cfg_path),
Expand Down Expand Up @@ -950,7 +950,7 @@ def save_paths(cfg):
val_path = Path(os.path.basename(val.absolute), mode="fc")
check_overwrite(val_path)
with open(val_path.absolute, "w") as f:
f.write(val.get_content())
f.write(val.read_text())
cfg[key] = type(val)(str(val_path))

with change_to_path_dir(path_fc), parser_context(parent_parser=self):
Expand Down Expand Up @@ -1024,7 +1024,7 @@ def get_defaults(self, skip_validation: bool = False, **kwargs) -> Namespace:

default_config_files = self._get_default_config_files()
for default_config_file in default_config_files:
default_config_file_content = default_config_file.get_content()
default_config_file_content = default_config_file.read_text()
if not default_config_file_content.strip():
continue
with change_to_path_dir(default_config_file), parser_context(parent_parser=self, parsing_defaults=True):
Expand Down
31 changes: 31 additions & 0 deletions jsonargparse/_deprecated.py
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,12 @@
``absolute`` or ``relative`` properties instead.
"""

path_get_content_message = """
``Path.get_content`` was deprecated in v4.49.0 and will be removed in
v5.0.0. Instead use ``Path.read_text`` for text and ``Path.open`` for binary
data.
"""


class PathDeprecations:
"""Deprecated methods for Path."""
Expand Down Expand Up @@ -524,6 +530,31 @@
def __call__(self, absolute: bool = True) -> str:
return self._absolute if absolute else self._relative

def get_content(self, mode: str = "r"):
deprecation_warning("Path.get_content", path_get_content_message)

Check failure on line 534 in jsonargparse/_deprecated.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "Path.get_content" 3 times.

See more on https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&issues=AZ3ensqFHX0QXjOwqcNj&open=AZ3ensqFHX0QXjOwqcNj&pullRequest=906
if self._std_io: # type: ignore[attr-defined]
from ._paths import _read_cached_stdin

return _read_cached_stdin()
elif self._is_url: # type: ignore[attr-defined]
from ._optionals import import_requests

assert mode == "r"
requests = import_requests("Path.get_content")
response = requests.get(self._absolute)
response.raise_for_status()
return response.text
elif self._is_fsspec: # type: ignore[attr-defined]
from ._optionals import import_fsspec

fsspec = import_fsspec("Path.get_content")
with fsspec.open(self._absolute, mode) as handle:
with handle as input_file:
return input_file.read()
else:
with open(self._absolute, mode) as input_file:
return input_file.read()


@deprecated("""
usage_and_exit_error_handler was deprecated in v4.20.0 and will be removed
Expand Down
2 changes: 1 addition & 1 deletion jsonargparse/_from_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def _parse_class_kwargs_from_config(cls: Type[T], config: Union[str, PathLike, d
from .typing import Path

cfg_path = Path(config, mode=_get_config_read_mode())
cfg_str = cfg_path.get_content()
cfg_str = cfg_path.read_text()
with parser_context(load_value_mode=parser.parser_mode):
try:
config = load_value(cfg_str, path=str(config))
Expand Down
2 changes: 1 addition & 1 deletion jsonargparse/_jsonnet.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ def parse(
pass
else:
fname = jsonnet(absolute=False) if isinstance(jsonnet, Path) else jsonnet
snippet = fpath.get_content()
snippet = fpath.read_text()
try:
with parser_context(load_value_mode="yaml" if pyyaml_available else "json"):
values = load_value(_jsonnet.evaluate_snippet(fname, snippet, ext_vars=ext_vars, ext_codes=ext_codes))
Expand Down
15 changes: 7 additions & 8 deletions jsonargparse/_paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,23 +285,22 @@ def __eq__(self, other: Any) -> bool:
return str(self) == other
return False

def get_content(self, mode: str = "r") -> str:
"""Returns the contents of the file or the remote path."""
def read_text(self) -> str:
"""Returns the text contents of the file or the remote path."""
if self._std_io:
return _read_cached_stdin()
elif self._is_url:
assert mode == "r"
requests = import_requests("Path.get_content")
requests = import_requests("Path.read_text")
response = requests.get(self._absolute)
response.raise_for_status()
return response.text
elif self._is_fsspec:
fsspec = import_fsspec("Path.get_content")
with fsspec.open(self._absolute, mode) as handle:
fsspec = import_fsspec("Path.read_text")
with fsspec.open(self._absolute, "r") as handle:
with handle as input_file:
return input_file.read()
else:
with open(self._absolute, mode) as input_file:
with open(self._absolute) as input_file:
return input_file.read()

@contextmanager
Expand All @@ -313,7 +312,7 @@ def open(self, mode: str = "r") -> Iterator[IO]:
elif "w" in mode:
yield sys.stdout
elif self._is_url:
yield StringIO(self.get_content())
yield StringIO(self.read_text())
elif self._is_fsspec:
fsspec = import_fsspec("Path.open")
with fsspec.open(self._absolute, mode) as handle:
Expand Down
2 changes: 1 addition & 1 deletion jsonargparse/_typehints.py
Original file line number Diff line number Diff line change
Expand Up @@ -948,7 +948,7 @@ def adapt_typehints(
from ._optionals import _get_config_read_mode

list_path = Path(val, mode=_get_config_read_mode())
val = list_path.get_content().splitlines()
val = list_path.read_text().splitlines()
if isinstance(val, NestedArg) and subtypehints is not None:
val = (prev_val[:-1] if isinstance(prev_val, list) else []) + [val]
elif isinstance(val, Iterable) and not isinstance(val, (list, str)) and type(val) not in mapping_origin_types:
Expand Down
2 changes: 1 addition & 1 deletion jsonargparse/_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def parse_value_or_config(
pass
else:
with cfg_path.relative_path_context():
value = load_value(cfg_path.get_content(), simple_types=simple_types)
value = load_value(cfg_path.read_text(), simple_types=simple_types)
if type(value) is str and value.strip() != "":
parsed_val = load_value(value, simple_types=simple_types)
if type(parsed_val) is not str:
Expand Down
2 changes: 1 addition & 1 deletion jsonargparse_tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -878,7 +878,7 @@ def test_save_fsspec(example_parser):
cfg = example_parser.parse_args(["--nums.val1=5"])
example_parser.save(cfg, "memory://config.yaml", multifile=False)
path = path_type("sr")("memory://config.yaml")
assert cfg == example_parser.parse_string(path.get_content())
assert cfg == example_parser.parse_string(path.read_text())

with pytest.raises(NotImplementedError) as ctx:
example_parser.save(cfg, "memory://config.yaml", multifile=True)
Expand Down
63 changes: 63 additions & 0 deletions jsonargparse_tests/test_deprecated.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,11 @@
from jsonargparse_tests.conftest import (
get_parser_help,
is_posix,
responses_activate,
skip_if_docstring_parser_unavailable,
skip_if_fsspec_unavailable,
skip_if_requests_unavailable,
skip_if_responses_unavailable,
)
from jsonargparse_tests.test_dataclasses import DataClassA
from jsonargparse_tests.test_jsonnet import example_2_jsonnet
Expand Down Expand Up @@ -705,6 +707,67 @@ def test_path_call(paths): # noqa: F811
assert path() == str(paths.tmp_path / paths.file_rw)


def test_file_path_get_content(paths): # noqa: F811
path = Path(paths.file_r, "fr")
with catch_warnings(record=True) as w:
content = path.get_content()
assert_deprecation_warn(
w,
message="``Path.get_content`` was deprecated",
code="content = path.get_content()",
)
assert "file contents" == content


def test_std_input_path_get_content():
input_text_to_test = "a text here\n"
with patch("sys.stdin", StringIO(input_text_to_test)), catch_warnings(record=True) as w:
path = Path("-", mode="fr")
assert input_text_to_test == path.get_content()
assert_deprecation_warn(
w,
message="``Path.get_content`` was deprecated",
code="input_text_to_test == path.get_content()",
)


@skip_if_responses_unavailable
@responses_activate
def test_path_url_200():
import responses

existing = "http://example.com/existing-url"
existing_body = "url contents"
responses.add(responses.GET, existing, status=200, body=existing_body)
responses.add(responses.HEAD, existing, status=200)
path = Path(existing, mode="ur")
with catch_warnings(record=True) as w:
assert existing_body == path.get_content()
assert_deprecation_warn(
w,
message="``Path.get_content`` was deprecated",
code="existing_body == path.get_content()",
)


@skip_if_fsspec_unavailable
def test_path_fsspec_memory():
import fsspec

file_content = "content in memory"
memfile = "memfile.txt"
path = Path(f"memory://{memfile}", mode="sw")
with fsspec.open(path, "w") as f:
f.write(file_content)
with catch_warnings(record=True) as w:
assert file_content == path.get_content()
assert_deprecation_warn(
w,
message="``Path.get_content`` was deprecated",
code="file_content == path.get_content()",
)


def test_ActionPathList(tmp_cwd):
tmpdir = os.path.join(tmp_cwd, "subdir")
os.mkdir(tmpdir)
Expand Down
22 changes: 11 additions & 11 deletions jsonargparse_tests/test_paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,10 @@ def test_path_dir_access_mode(paths):
pytest.raises(TypeError, lambda: Path(paths.file_r, "dr"))


def test_path_get_content(paths):
assert "file contents" == Path(paths.file_r, "fr").get_content()
assert "file contents" == Path(f"file://{paths.tmp_path}/{paths.file_r}", "fr").get_content()
assert "file contents" == Path(f"file://{paths.tmp_path}/{paths.file_r}", "ur").get_content()
def test_path_read_text(paths):
assert "file contents" == Path(paths.file_r, "fr").read_text()
assert "file contents" == Path(f"file://{paths.tmp_path}/{paths.file_r}", "fr").read_text()
assert "file contents" == Path(f"file://{paths.tmp_path}/{paths.file_r}", "ur").read_text()


@skip_if_running_as_root
Expand Down Expand Up @@ -227,13 +227,13 @@ def test_path_tilde_home(paths):
assert path.absolute == os.path.join(paths.tmp_path, paths.file_rw)


def test_std_input_path_get_content():
def test_std_input_path_read_text():
input_text_to_test = "a text here\n"

with patch("sys.stdin", StringIO(input_text_to_test)):
path = Path("-", mode="fr")
assert path == "-"
assert input_text_to_test == path.get_content("r")
assert input_text_to_test == path.read_text()


def test_std_input_path_open():
Expand Down Expand Up @@ -302,7 +302,7 @@ def test_path_url_200():
responses.add(responses.GET, existing, status=200, body=existing_body)
responses.add(responses.HEAD, existing, status=200)
path = Path(existing, mode="ur")
assert existing_body == path.get_content()
assert existing_body == path.read_text()


@skip_if_responses_unavailable
Expand Down Expand Up @@ -337,7 +337,7 @@ def test_path_fsspec_zipfile(tmp_cwd):
zip2_path.chmod(0)

path = Path(f"zip://{existing}::file://{zip1_path}", mode="sr")
assert existing_body == path.get_content()
assert existing_body == path.read_text()

with pytest.raises(TypeError) as ctx:
Path(f"zip://{nonexisting}::file://{zip1_path}", mode="sr")
Expand All @@ -356,7 +356,7 @@ def test_path_fsspec_memory():
path = Path(f"memory://{memfile}", mode="sw")
with fsspec.open(path, "w") as f:
f.write(file_content)
assert file_content == path.get_content()
assert file_content == path.read_text()


def test_path_fsspec_invalid_mode():
Expand Down Expand Up @@ -431,11 +431,11 @@ def test_relative_path_context_fsspec(tmp_cwd, subtests):

with subtests.test("absolute local path"):
path0 = Path(local_path, mode="fr")
assert "zero" == path0.get_content()
assert "zero" == path0.read_text()

with subtests.test("relative fsspec path"):
path1 = Path("../file1.txt", mode="fsr")
assert "one" == path1.get_content()
assert "one" == path1.read_text()
assert str(path1) == "../file1.txt"
assert path1.absolute == "memory://one/two/file1.txt"
assert path1._url_data is not None
Expand Down
Loading