From fc8bd8b6249510e47a4cd27707d7c799fe9e13b4 Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Wed, 27 Nov 2024 15:51:19 +0100 Subject: [PATCH] Add --max-indent-leaves and its alias -L --- docs/source/changelog.rst | 1 + docs/source/cli/jsonyx-diff.rst | 4 ++ docs/source/cli/jsonyx-format.rst | 4 ++ docs/source/cli/jsonyx-patch.rst | 4 ++ docs/source/get-started.rst | 7 +-- src/jsonyx/__init__.py | 6 +-- src/jsonyx/__main__.py | 9 +++- src/jsonyx/_encoder.py | 2 +- src/jsonyx/test/test_dumps.py | 89 ++++++++++++++----------------- 9 files changed, 69 insertions(+), 57 deletions(-) diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 6ea7004..f79dc79 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -16,6 +16,7 @@ jsonyx 2.0.0 (unreleased) :func:`jsonyx.write` - Added ``python -m jsonyx diff`` and ``python -m jsonyx patch`` - Added ``--indent-leaves`` and its alias ``-l`` to ``python -m jsonyx format`` +- Added ``--max-indent-level`` and its alias ``-L`` to ``python -m jsonyx format`` - Added ``--unquoted-keys`` and its alias ``-q`` to ``python -m jsonyx format`` - Added ``--version`` and its alias ``-v`` to ``python -m jsonyx`` - Added :data:`jsonyx.allow.UNQUOTED_KEYS` diff --git a/docs/source/cli/jsonyx-diff.rst b/docs/source/cli/jsonyx-diff.rst index 3dee09b..aadef0a 100644 --- a/docs/source/cli/jsonyx-diff.rst +++ b/docs/source/cli/jsonyx-diff.rst @@ -61,6 +61,10 @@ Options Indent leaf objects and arrays. +.. option:: -L, --max-indent-level + + The level up to which to indent. + .. option:: -q, --unquoted-keys Don't quote keys which are identifiers. diff --git a/docs/source/cli/jsonyx-format.rst b/docs/source/cli/jsonyx-format.rst index cd30d48..a80e9c3 100644 --- a/docs/source/cli/jsonyx-format.rst +++ b/docs/source/cli/jsonyx-format.rst @@ -57,6 +57,10 @@ Options Indent leaf objects and arrays. +.. option:: -L, --max-indent-level + + The level up to which to indent. + .. option:: -q, --unquoted-keys Don't quote keys which are identifiers. diff --git a/docs/source/cli/jsonyx-patch.rst b/docs/source/cli/jsonyx-patch.rst index 6d220b3..769c35f 100644 --- a/docs/source/cli/jsonyx-patch.rst +++ b/docs/source/cli/jsonyx-patch.rst @@ -61,6 +61,10 @@ Options Indent leaf objects and arrays. +.. option:: -L, --max-indent-level + + The level up to which to indent. + .. option:: -q, --unquoted-keys Don't quote keys which are identifiers. diff --git a/docs/source/get-started.rst b/docs/source/get-started.rst index 40f6ced..d4b09ed 100644 --- a/docs/source/get-started.rst +++ b/docs/source/get-started.rst @@ -132,15 +132,16 @@ Pretty printing .. versionchanged:: 2.0 Added ``indent_leaves`` and ``max_indent_level``. >>> import jsonyx as json ->>> json.dump({"foo": [1, 2, 3], "bar": {"a": 1, "b": 2, "c": 3}}, indent=4) +>>> obj = {"foo": [1, 2, 3], "bar": {"a": 1, "b": 2, "c": 3}} +>>> json.dump(obj, indent=4, indent_leaves=False) { "foo": [1, 2, 3], "bar": {"a": 1, "b": 2, "c": 3} } .. tip:: Use ``ensure_ascii=True`` to escape non-ASCII characters, - ``indent_leaves=True`` to indent everything, ``max_indent_level=1`` to - indent up to level 1, and ``sort_keys=True`` to sort the keys of objects. + ``max_indent_level=1`` to indent up to level 1, and ``sort_keys=True`` + to sort the keys of objects. .. seealso:: The built-in :mod:`pprint` module for pretty-printing arbitrary Python data structures. diff --git a/src/jsonyx/__init__.py b/src/jsonyx/__init__.py index 2b60f93..90797fe 100644 --- a/src/jsonyx/__init__.py +++ b/src/jsonyx/__init__.py @@ -226,7 +226,7 @@ def write( end: str = "\n", ensure_ascii: bool = False, indent: int | str | None = None, - indent_leaves: bool = False, + indent_leaves: bool = True, mapping_types: type | tuple[type, ...] = (), max_indent_level: int | None = None, quoted_keys: bool = True, @@ -308,7 +308,7 @@ def dump( end: str = "\n", ensure_ascii: bool = False, indent: int | str | None = None, - indent_leaves: bool = False, + indent_leaves: bool = True, mapping_types: type | tuple[type, ...] = (), max_indent_level: int | None = None, quoted_keys: bool = True, @@ -388,7 +388,7 @@ def dumps( end: str = "\n", ensure_ascii: bool = False, indent: int | str | None = None, - indent_leaves: bool = False, + indent_leaves: bool = True, mapping_types: type | tuple[type, ...] = (), max_indent_level: int | None = None, quoted_keys: bool = True, diff --git a/src/jsonyx/__main__.py b/src/jsonyx/__main__.py index 03899f5..572a1db 100644 --- a/src/jsonyx/__main__.py +++ b/src/jsonyx/__main__.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # Copyright (C) 2024 Nice Zombies """A command line utility to manipulate JSON files.""" -# TODO(Nice Zombies): add --max-indent-level from __future__ import annotations __all__: list[str] = ["main"] @@ -28,6 +27,7 @@ class _Namespace: indent: int | str | None indent_leaves: bool input_filename: str | None + max_indent_level: int | None quoted_keys: bool nonstrict: bool output_filename: str | None @@ -92,6 +92,12 @@ def _configure(parser: ArgumentParser) -> None: action="store_true", help="indent leaf objects and arrays", ) + parent_parser.add_argument( + "-L", + "--max-indent-level", + type=int, + help="the level up to which to indent", + ) parent_parser.add_argument( "-s", "--sort-keys", @@ -198,6 +204,7 @@ def _run(args: _Namespace) -> None: ensure_ascii=args.ensure_ascii, indent=args.indent, indent_leaves=args.indent_leaves, + max_indent_level=args.max_indent_level, quoted_keys=args.quoted_keys, separators=(",", ":") if args.compact else (", ", ": "), sort_keys=args.sort_keys, diff --git a/src/jsonyx/_encoder.py b/src/jsonyx/_encoder.py index 256df3d..a4b0ca5 100644 --- a/src/jsonyx/_encoder.py +++ b/src/jsonyx/_encoder.py @@ -330,7 +330,7 @@ def __init__( end: str = "\n", ensure_ascii: bool = False, indent: int | str | None = None, - indent_leaves: bool = False, + indent_leaves: bool = True, mapping_types: type | tuple[type, ...] = (), max_indent_level: int | None = None, quoted_keys: bool = True, diff --git a/src/jsonyx/test/test_dumps.py b/src/jsonyx/test/test_dumps.py index 5ee3cd9..954dd70 100644 --- a/src/jsonyx/test/test_dumps.py +++ b/src/jsonyx/test/test_dumps.py @@ -206,17 +206,6 @@ def test_list(json: ModuleType, obj: list[object], expected: str) -> None: assert json.dumps(obj, end="") == expected -@pytest.mark.parametrize(("obj", "expected"), [ - ([1, 2, 3], "[1, 2, 3]"), - ([[1, 2, 3]], "[\n [1, 2, 3]\n]"), -]) -def test_list_indent( - json: ModuleType, obj: list[object], expected: str, -) -> None: - """Test list indent.""" - assert json.dumps(obj, end="", indent=1) == expected - - @pytest.mark.parametrize(("indent", "expected"), [ # Integer (0, ""), @@ -225,19 +214,29 @@ def test_list_indent( # String ("\t", "\t"), ]) -def test_list_indent_leaves( +def test_list_indent( json: ModuleType, indent: int | str, expected: str, ) -> None: - """Test list indent with indent_leaves.""" - s: str = json.dumps([1, 2, 3], end="", indent=indent, indent_leaves=True) + """Test list indent.""" + s: str = json.dumps([1, 2, 3], end="", indent=indent) assert s == f"[\n{expected}1,\n{expected}2,\n{expected}3\n]" +@pytest.mark.parametrize(("obj", "expected"), [ + ([1, 2, 3], "[1, 2, 3]"), + ([[1, 2, 3]], "[\n [1, 2, 3]\n]"), +]) +def test_list_indent_leaves( + json: ModuleType, obj: list[object], expected: str, +) -> None: + """Test list indent without indent_leaves.""" + assert json.dumps(obj, end="", indent=1, indent_leaves=False) == expected + + def test_list_max_indent_level(json: ModuleType) -> None: - """Test list indent with max_indent_level and indent_leaves.""" - assert json.dumps( - [[1, 2, 3]], end="", indent=1, indent_leaves=True, max_indent_level=1, - ) == "[\n [1, 2, 3]\n]" + """Test list indent with max_indent_level.""" + s: str = json.dumps([[1, 2, 3]], end="", indent=1, max_indent_level=1) + assert s == "[\n [1, 2, 3]\n]" def test_list_recursion(json: ModuleType) -> None: @@ -388,17 +387,6 @@ def test_sort_keys(json: ModuleType) -> None: assert s == '{"a": 1, "b": 2, "c": 3}' -@pytest.mark.parametrize(("obj", "expected"), [ - ({"a": 1, "b": 2, "c": 3}, '{"a": 1, "b": 2, "c": 3}'), - ({"": {"a": 1, "b": 2, "c": 3}}, '{\n "": {"a": 1, "b": 2, "c": 3}\n}'), -]) -def test_dict_indent( - json: ModuleType, obj: dict[str, object], expected: str, -) -> None: - """Test dict indent.""" - assert json.dumps(obj, end="", indent=1) == expected - - @pytest.mark.parametrize(("indent", "expected"), [ # Integer (0, ""), @@ -407,23 +395,33 @@ def test_dict_indent( # String ("\t", "\t"), ]) -def test_dict_indent_leaves( +def test_dict_indent( json: ModuleType, indent: int | str, expected: str, ) -> None: - """Test dict indent with indent_leaves.""" + """Test dict indent.""" obj: dict[str, object] = {"a": 1, "b": 2, "c": 3} - s: str = json.dumps(obj, end="", indent=indent, indent_leaves=True) + s: str = json.dumps(obj, end="", indent=indent) assert s == ( f'{{\n{expected}"a": 1,\n{expected}"b": 2,\n{expected}"c": 3\n}}' ) +@pytest.mark.parametrize(("obj", "expected"), [ + ({"a": 1, "b": 2, "c": 3}, '{"a": 1, "b": 2, "c": 3}'), + ({"": {"a": 1, "b": 2, "c": 3}}, '{\n "": {"a": 1, "b": 2, "c": 3}\n}'), +]) +def test_dict_indent_leaves( + json: ModuleType, obj: dict[str, object], expected: str, +) -> None: + """Test dict indent without indent_leaves.""" + assert json.dumps(obj, end="", indent=1, indent_leaves=False) == expected + + def test_dict_max_indent_level(json: ModuleType) -> None: - """Test dict indent with max_indent_level and indent_leaves.""" + """Test dict indent with max_indent_level.""" obj: dict[str, object] = {"": {"a": 1, "b": 2, "c": 3}} - assert json.dumps( - obj, end="", indent=1, indent_leaves=True, max_indent_level=1, - ) == '{\n "": {"a": 1, "b": 2, "c": 3}\n}' + s: str = json.dumps(obj, end="", indent=1, max_indent_level=1) + assert s == '{\n "": {"a": 1, "b": 2, "c": 3}\n}' def test_dict_recursion(json: ModuleType) -> None: @@ -478,20 +476,15 @@ def test_no_commas( ({"a": 1, "b": 2, "c": 3}, '{\n "a": 1\n "b": 2\n "c": 3\n}'), ]) @pytest.mark.parametrize("trailing_comma", [True, False]) -def test_no_commas_indent_leaves( +def test_no_commas_indent( json: ModuleType, obj: dict[str, object] | list[object], expected: str, trailing_comma: bool, # noqa: FBT001 ) -> None: - """Test no commas with indent and indent_leaves.""" + """Test no commas with indent.""" assert json.dumps( - obj, - commas=False, - end="", - indent=1, - indent_leaves=True, - trailing_comma=trailing_comma, + obj, commas=False, end="", indent=1, trailing_comma=trailing_comma, ) == expected @@ -531,10 +524,8 @@ def test_trailing_comma( ([1, 2, 3], "[\n 1,\n 2,\n 3,\n]"), ({"a": 1, "b": 2, "c": 3}, '{\n "a": 1,\n "b": 2,\n "c": 3,\n}'), ]) -def test_trailing_comma_indent_leaves( +def test_trailing_comma_indent( json: ModuleType, obj: dict[str, object] | list[object], expected: str, ) -> None: - """Test trailing_comma with indent and indent_leaves.""" - assert json.dumps( - obj, end="", indent=1, indent_leaves=True, trailing_comma=True, - ) == expected + """Test trailing_comma with indent.""" + assert json.dumps(obj, end="", indent=1, trailing_comma=True) == expected