json-sorted 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,9 @@
1
+ from json_sorted.core.main import main
2
+ from json_sorted.core.run import run
3
+ from json_sorted.enum.Instruction import Instruction
4
+ from json_sorted.enum.Selector import Selector
5
+
6
+ __all__ = ["Instruction", "Selector", "main", "run"]
7
+
8
+ if __name__ == "__main__":
9
+ main()
@@ -0,0 +1,4 @@
1
+ from json_sorted import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
File without changes
@@ -0,0 +1,67 @@
1
+ import argparse
2
+ import logging
3
+ import sys
4
+ from collections.abc import Iterable
5
+ from typing import Any, Optional
6
+
7
+ import setdoc
8
+
9
+ from ..enum.Instruction import Instruction
10
+ from ..enum.Selector import Selector
11
+ from .run import run
12
+
13
+ __all__ = ["main"]
14
+
15
+
16
+ @setdoc.basic
17
+ def main(args: Optional[Iterable[str]] = None, /) -> None:
18
+ kwargs: dict[str, Any]
19
+ parser: argparse.ArgumentParser
20
+ parser = argparse.ArgumentParser(fromfile_prefix_chars="@")
21
+ parser.add_argument(
22
+ "filepatterns",
23
+ nargs="*",
24
+ default=[],
25
+ )
26
+ parser.add_argument(
27
+ "--all-indices",
28
+ action="append_const",
29
+ const=Selector.ALL_INDICES,
30
+ dest="instructions",
31
+ )
32
+ parser.add_argument(
33
+ "--all-keys",
34
+ action="append_const",
35
+ const=Selector.ALL_KEYS,
36
+ dest="instructions",
37
+ )
38
+ parser.add_argument(
39
+ "--key",
40
+ action="append",
41
+ dest="instructions",
42
+ )
43
+ parser.add_argument(
44
+ "--index",
45
+ action="append",
46
+ dest="instructions",
47
+ type=int,
48
+ )
49
+ parser.add_argument(
50
+ "--sort",
51
+ action="append_const",
52
+ const=Instruction.SORT,
53
+ dest="instructions",
54
+ )
55
+ parser.add_argument(
56
+ "--sort-reverse",
57
+ action="append_const",
58
+ const=Instruction.SORT_REVERSE,
59
+ dest="instructions",
60
+ )
61
+ parser.set_defaults(instructions=[])
62
+ kwargs = vars(parser.parse_args(args))
63
+ try:
64
+ run(*kwargs.pop("filepatterns"), **kwargs)
65
+ except Exception:
66
+ logging.exception("json_sort failed!")
67
+ sys.exit(1)
@@ -0,0 +1,173 @@
1
+ import glob
2
+ import json
3
+ import os
4
+ from collections.abc import Iterable
5
+ from typing import Any, cast
6
+
7
+ import setdoc
8
+
9
+ from json_sorted.enum.Instruction import Instruction
10
+ from json_sorted.enum.Selector import Selector
11
+
12
+ __all__ = ["run"]
13
+
14
+
15
+ def get_absfiles(filepatterns: Iterable[str]) -> list[str]:
16
+ absfile: str
17
+ absfiles: list[str]
18
+ file: str
19
+ pattern: str
20
+ absfiles = list()
21
+ for pattern in filepatterns:
22
+ for file in glob.iglob(pattern, recursive=True):
23
+ absfile = os.path.abspath(file)
24
+ if absfile in absfiles:
25
+ continue
26
+ if os.path.isfile(absfile):
27
+ absfiles.append(absfile)
28
+ return absfiles
29
+
30
+
31
+ def parse_instructions(
32
+ instructions: Iterable[Instruction | Selector | int | str] = (),
33
+ ) -> list[tuple[Instruction, list[Selector | int | str]]]:
34
+ ans: list[tuple[Instruction, list[Selector | int | str]]]
35
+ x: Instruction | Selector | int | str
36
+ ans = list()
37
+ for x in instructions:
38
+ if isinstance(x, Instruction):
39
+ ans.append((x, list()))
40
+ elif len(ans):
41
+ ans[-1][1].append(x)
42
+ return ans
43
+
44
+
45
+ @setdoc.basic
46
+ def run(
47
+ *filepatterns: str,
48
+ instructions: Iterable[Instruction | Selector | int | str] = (),
49
+ ) -> None:
50
+ absfiles: list[str]
51
+ parsed: list[tuple[Instruction, list[Selector | int | str]]]
52
+ parsed = parse_instructions(instructions)
53
+ absfiles = get_absfiles(filepatterns)
54
+ run_instructions_on_files(absfiles=absfiles, parsed=parsed)
55
+
56
+
57
+ def run_instruction_on_data(
58
+ *,
59
+ instruction: Instruction,
60
+ keys: list[Selector | int | str],
61
+ data: dict[str, Any],
62
+ ) -> dict[str, Any]:
63
+ return cast(
64
+ dict[str, Any],
65
+ run_instruction_along_keys(
66
+ data,
67
+ instruction=instruction,
68
+ keys=list(keys),
69
+ ),
70
+ )
71
+
72
+
73
+ def run_instruction_along_keys(
74
+ data: Any,
75
+ *,
76
+ instruction: Instruction,
77
+ keys: list[Selector | int | str],
78
+ ) -> Any:
79
+ head: Selector | int | str
80
+ rest: list[Selector | int | str]
81
+ if not len(keys):
82
+ return run_instruction_on_value(data, reverse=instruction.value)
83
+ head = keys[0]
84
+ rest = keys[1:]
85
+ if head is Selector.ALL_KEYS:
86
+ return run_instruction_on_all_keys(
87
+ data,
88
+ instruction=instruction,
89
+ keys=rest,
90
+ )
91
+ if head is Selector.ALL_INDICES:
92
+ return run_instruction_on_all_indices(
93
+ data,
94
+ instruction=instruction,
95
+ keys=rest,
96
+ )
97
+ data[head] = run_instruction_along_keys(
98
+ data[head],
99
+ instruction=instruction,
100
+ keys=rest,
101
+ )
102
+ return data
103
+
104
+
105
+ def run_instruction_on_all_keys(
106
+ data: Any,
107
+ *,
108
+ instruction: Instruction,
109
+ keys: list[Selector | int | str],
110
+ ) -> Any:
111
+ key: Any
112
+ if isinstance(data, dict):
113
+ for key in list(data.keys()):
114
+ data[key] = run_instruction_along_keys(
115
+ data[key],
116
+ instruction=instruction,
117
+ keys=keys,
118
+ )
119
+ return data
120
+ raise TypeError(
121
+ f"Value {data!r} of type {type(data).__name__} has no keys to expand!"
122
+ )
123
+
124
+
125
+ def run_instruction_on_all_indices(
126
+ data: Any,
127
+ *,
128
+ instruction: Instruction,
129
+ keys: list[Selector | int | str],
130
+ ) -> Any:
131
+ index: int
132
+ if isinstance(data, list):
133
+ for index in range(len(data)):
134
+ data[index] = run_instruction_along_keys(
135
+ data[index],
136
+ instruction=instruction,
137
+ keys=keys,
138
+ )
139
+ return data
140
+ raise TypeError(
141
+ f"Value {data!r} of type {type(data).__name__} has no indices to expand!"
142
+ )
143
+
144
+
145
+ def run_instruction_on_value(data: Any, *, reverse: bool) -> Any:
146
+ if isinstance(data, dict):
147
+ return dict(sorted(data.items(), reverse=reverse))
148
+ if isinstance(data, list):
149
+ return list(sorted(data, reverse=reverse))
150
+ raise TypeError(
151
+ f"Value {data!r} of type {type(data).__name__} cannot be sorted!"
152
+ )
153
+
154
+
155
+ def run_instructions_on_files(
156
+ *,
157
+ absfiles: list[str],
158
+ parsed: list[tuple[Instruction, list[Selector | int | str]]],
159
+ ) -> None:
160
+ absfile: str
161
+ data: Any
162
+ for absfile in absfiles:
163
+ with open(absfile, "r", encoding="utf-8") as stream:
164
+ data = json.load(stream)
165
+ for instruction, keys in parsed:
166
+ data = run_instruction_on_data(
167
+ data=data,
168
+ instruction=instruction,
169
+ keys=keys,
170
+ )
171
+ with open(absfile, "w", encoding="utf-8") as stream:
172
+ json.dump(data, stream, indent=4)
173
+ stream.write("\n")
@@ -0,0 +1,8 @@
1
+ import enum
2
+
3
+ __all__ = ["Instruction"]
4
+
5
+
6
+ class Instruction(enum.Enum):
7
+ SORT = False
8
+ SORT_REVERSE = True
@@ -0,0 +1,8 @@
1
+ import enum
2
+
3
+ __all__ = ["Selector"]
4
+
5
+
6
+ class Selector(enum.Enum):
7
+ ALL_KEYS = enum.auto()
8
+ ALL_INDICES = enum.auto()
json_sorted/py.typed ADDED
File without changes
@@ -0,0 +1,29 @@
1
+ Metadata-Version: 2.4
2
+ Name: json_sorted
3
+ Version: 1.0.0
4
+ Summary: This project sorts JSON data.
5
+ Author-email: Johannes <johannes.programming@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Download, https://pypi.org/project/json_sorted/#files
8
+ Project-URL: Index, https://pypi.org/project/json_sorted/
9
+ Project-URL: Source, https://github.com/johannes-programming/json_sorted/
10
+ Project-URL: Website, https://json-sorted.johannes-programming.online/
11
+ Classifier: Development Status :: 2 - Pre-Alpha
12
+ Classifier: Natural Language :: English
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Requires-Python: >=3.11
18
+ Description-Content-Type: text/x-rst
19
+ License-File: LICENSE.txt
20
+ Requires-Dist: setdoc<3,>=1.3.14
21
+ Dynamic: license-file
22
+
23
+ ===========
24
+ json_sorted
25
+ ===========
26
+
27
+ Each minor version has its own documentation.
28
+ These docs can be found as rst-files in the ``docs/`` directory of this project.
29
+ They can also be viewed on the website `https://json-sorted.johannes-programming.online/ <https://json-sorted.johannes-programming.online/>`_.
@@ -0,0 +1,13 @@
1
+ json_sorted/__init__.py,sha256=HR9OhTwybUGbx1IHEjsGASowYSvg9xSK7wW4Jiy67ZQ,269
2
+ json_sorted/__main__.py,sha256=XfsEL112wTYLQ52AvxiVT-d3_zAOrgwoBNIvLlbbmag,68
3
+ json_sorted/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ json_sorted/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ json_sorted/core/main.py,sha256=6okwYlsIvpi86-SPQgBJR75Tk3QIFDkHcKQJQhSvShA,1598
6
+ json_sorted/core/run.py,sha256=w-OIoQ9auQaToKJw6Cy0oCHRcxncT8n0IZVuYhPDtH8,4643
7
+ json_sorted/enum/Instruction.py,sha256=8Txp7qZmM1UIdg9s3tjv2qtZ6xg-NgCDxkFIsQUudpg,112
8
+ json_sorted/enum/Selector.py,sha256=Ap9CVuiAtHTVM5ZCiW_0XK__ygd1Ct7TVA3WEugENT4,122
9
+ json_sorted-1.0.0.dist-info/licenses/LICENSE.txt,sha256=gw3yPLaDFF_WChv7zU7oXjd1QJNufDmFG4J9F9fTkBQ,1075
10
+ json_sorted-1.0.0.dist-info/METADATA,sha256=2hk5GXhJaXcw8rO7lGMtYjm6X32W-QfzbrzfeIN_V1E,1176
11
+ json_sorted-1.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
12
+ json_sorted-1.0.0.dist-info/top_level.txt,sha256=Q7dXH0iXbDvlsJg51HodycBsepZOXZamB9llBT36kJc,12
13
+ json_sorted-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Johannes
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ json_sorted