omlish 0.0.0.dev55__py3-none-any.whl → 0.0.0.dev56__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.
omlish/.manifests.json CHANGED
@@ -35,6 +35,18 @@
35
35
  }
36
36
  }
37
37
  },
38
+ {
39
+ "module": ".formats.json.__main__",
40
+ "attr": "_CLI_MODULE",
41
+ "file": "omlish/formats/json/__main__.py",
42
+ "line": 1,
43
+ "value": {
44
+ "$omdev.cli.types.CliModule": {
45
+ "cmd_name": "json",
46
+ "mod_name": "omlish.formats.json.__main__"
47
+ }
48
+ }
49
+ },
38
50
  {
39
51
  "module": ".specs.jmespath.__main__",
40
52
  "attr": "_CLI_MODULE",
omlish/__about__.py CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev55'
2
- __revision__ = '6185da5f1914b76d0daffc87fd41272d70bf3806'
1
+ __version__ = '0.0.0.dev56'
2
+ __revision__ = '1d081aeb233ec7ae534199fc0a6c063d4c79fcf7'
3
3
 
4
4
 
5
5
  #
@@ -0,0 +1,19 @@
1
+ from .json import ( # noqa
2
+ COMPACT_KWARGS,
3
+ COMPACT_SEPARATORS,
4
+ PRETTY_INDENT,
5
+ PRETTY_KWARGS,
6
+ detect_encoding,
7
+ dump,
8
+ dump_compact,
9
+ dump_pretty,
10
+ dumps,
11
+ dumps_compact,
12
+ dumps_pretty,
13
+ load,
14
+ loads,
15
+ )
16
+
17
+ from .render import ( # noqa
18
+ JsonRenderer,
19
+ )
@@ -0,0 +1,11 @@
1
+ # @omlish-manifest
2
+ _CLI_MODULE = {'$omdev.cli.types.CliModule': {
3
+ 'cmd_name': 'json',
4
+ 'mod_name': __name__,
5
+ }}
6
+
7
+
8
+ if __name__ == '__main__':
9
+ from .cli import _main
10
+
11
+ _main()
File without changes
@@ -0,0 +1,72 @@
1
+ """
2
+ def loads(obj: str | bytes | bytearray | memoryview) -> ta.Any | oj.JSONDEcodeError
3
+ def dumps(obj: ta.Any, **DumpOpts) -> bytes
4
+ """
5
+ import dataclasses as dc
6
+ import typing as ta
7
+
8
+ from .... import lang
9
+
10
+
11
+ if ta.TYPE_CHECKING:
12
+ import orjson as oj
13
+ else:
14
+ oj = lang.proxy_import('orjson')
15
+
16
+
17
+ @dc.dataclass(frozen=True, kw_only=True)
18
+ class Options:
19
+ append_newline: bool = False # append \n to the output
20
+
21
+ indent_2: bool = False # pretty-print output with an indent of two spaces.
22
+
23
+ naive_utc: bool = False # serialize datetime.datetime objects without a tzinfo as UTC
24
+
25
+ non_str_keys: bool = False # serialize dict keys of type other than str
26
+
27
+ omit_microseconds: bool = False # do not serialize the microsecond field on datetime.datetime and datetime.time instances # noqa
28
+
29
+ passthrough_dataclass: bool = False # passthrough dataclasses.dataclass instances to default
30
+ passthrough_datetime: bool = False # passthrough datetime.datetime, datetime.date, and datetime.time instances to default # noqa
31
+ passthrough_subclass: bool = False # passthrough subclasses of builtin types to default
32
+
33
+ serialize_numpy: bool = False # serialize numpy.ndarray instances
34
+
35
+ sort_keys: bool = False # serialize dict keys in sorted order - the default is to serialize in an unspecified order
36
+
37
+ strict_integer: bool = False # enforce 53-bit limit on integers
38
+
39
+ utc_z: bool = False # serialize a UTC timezone on datetime.datetime instances as Z instead of +00:00
40
+
41
+ ##
42
+
43
+ def __int__(self) -> int:
44
+ return (
45
+ (oj.OPT_APPEND_NEWLINE if self.append_newline else 0) |
46
+
47
+ (oj.OPT_INDENT_2 if self.indent_2 else 0) |
48
+
49
+ (oj.OPT_NAIVE_UTC if self.naive_utc else 0) |
50
+
51
+ (oj.OPT_NON_STR_KEYS if self.non_str_keys else 0) |
52
+
53
+ (oj.OPT_OMIT_MICROSECONDS if self.omit_microseconds else 0) |
54
+
55
+ (oj.OPT_PASSTHROUGH_DATACLASS if self.passthrough_dataclass else 0) |
56
+ (oj.OPT_PASSTHROUGH_DATETIME if self.passthrough_datetime else 0) |
57
+ (oj.OPT_PASSTHROUGH_SUBCLASS if self.passthrough_subclass else 0) |
58
+
59
+ (oj.OPT_SERIALIZE_NUMPY if self.serialize_numpy else 0) |
60
+
61
+ (oj.OPT_SORT_KEYS if self.sort_keys else 0) |
62
+
63
+ (oj.OPT_STRICT_INTEGER if self.strict_integer else 0) |
64
+
65
+ (oj.OPT_UTC_Z if self.utc_z else 0)
66
+ )
67
+
68
+
69
+ @dc.dataclass(frozen=True, kw_only=True)
70
+ class DumpOpts:
71
+ default: ta.Callable[[ta.Any], ta.Any] | None = None
72
+ option: Options = Options()
@@ -0,0 +1,37 @@
1
+ """
2
+ load(fp: File, **LoadOpts) -> ta.Any | json.JSONDecodeError
3
+ loads(s: str | bytes | bytearray, **LoadOpts) -> ta.Any | json.JSONDecodeError
4
+ dump(obj: ta.Any, fp: File, **DumpOpts) -> None
5
+ dumps(obj: ta.Any, **DumpOpts) -> str
6
+ """
7
+ import dataclasses as dc
8
+ import json
9
+ import typing as ta
10
+
11
+
12
+ @dc.dataclass(frozen=True, kw_only=True)
13
+ class DumpOpts:
14
+ cls: type[json.JSONEncoder] | None = None
15
+ skipkeys: bool = False # dict keys that are not basic types will be skipped instead of raising a TypeError
16
+ ensure_ascii: bool = True # escape non-ASCII characters in JSON strings
17
+ check_circular: bool = True # if False a circular reference will result in an RecursionError
18
+ allow_nan: bool = True # use JS equivalents for out-of-range float values, otherwise raise ValueError
19
+ indent: int | None = None # item indent - 0 will insert only newlines, None is most compact
20
+ separators: tuple[str, str] | None = None # (item_separator, key_separator) - default (', ', ': ') if indent is None else (',', ': ') # noqa
21
+ default: ta.Callable[[ta.Any], ta.Any] | None = None # should return a serializable version of obj or raise TypeError # noqa
22
+ sort_keys: bool = False
23
+
24
+
25
+ @dc.dataclass(frozen=True, kw_only=True)
26
+ class LoadOpts:
27
+ cls: type[json.JSONDecoder] | None = None
28
+
29
+ parse_float: ta.Callable[[str], ta.Any] | None = None # by default this is equivalent to float(num_str)
30
+ parse_int: ta.Callable[[str], ta.Any] | None = None # by default this is equivalent to int(num_str)
31
+ parse_constant: ta.Callable[[str], ta.Any] | None = None # # called with one of the following strings: -Infinity, Infinity, NaN # noqa
32
+
33
+ # called with the result of any object literal decoded
34
+ object_hook: ta.Callable[[dict], ta.Any] | None = None
35
+
36
+ # called with the result of any object literal decoded with an ordered list of pairs, by default dict # noqa
37
+ object_pairs_hook: ta.Callable[[list[tuple[str, ta.Any]]], ta.Any] | None = None
@@ -0,0 +1,25 @@
1
+ """
2
+ load(fp: File) -> ta.Any | uj.JSONDecodeError
3
+ loads(s: str | bytes | bytearray) -> ta.Any | uj.JSONDecodeError
4
+ dump(obj: ta.Any, fp: File, **DumpOpts) -> None
5
+ dumps(obj: ta.Any, **DumpOpts) -> None
6
+ """
7
+ import dataclasses as dc
8
+ import typing as ta
9
+
10
+
11
+ @dc.dataclass(frozen=True, kw_only=True)
12
+ class DumpOpts:
13
+ """
14
+ https://github.com/ultrajson/ultrajson/blob/2e4aba339b0ab08f893590aaa989d12846afc5d6/python/objToJSON.c#L662
15
+ """
16
+
17
+ ensure_ascii: bool = True # limits output to ASCII and escapes all extended characters above 127.
18
+ encode_html_chars: bool = False # enables special encoding of "unsafe" HTML characters into safer Unicode sequences
19
+ escape_forward_slashes: bool = True # whether forward slashes (/) are escaped
20
+ indent: int = 0
21
+ sort_keys: bool = False
22
+ allow_nan: bool = True
23
+ reject_bytes: bool = True
24
+ default: ta.Callable[[ta.Any], ta.Any] | None = None # should return a serializable version of obj or raise TypeError # noqa
25
+ separators: tuple[str, str] | None = None
@@ -0,0 +1,72 @@
1
+ import argparse
2
+ import contextlib
3
+ import json
4
+ import sys
5
+ import typing as ta
6
+
7
+ from ... import term
8
+ from .render import JsonRenderer
9
+
10
+
11
+ def term_color(o: ta.Any, state: JsonRenderer.State) -> tuple[str, str]:
12
+ if state is JsonRenderer.State.KEY:
13
+ return term.SGR(term.SGRs.FG.BRIGHT_BLUE), term.SGR(term.SGRs.RESET)
14
+ elif isinstance(o, str):
15
+ return term.SGR(term.SGRs.FG.GREEN), term.SGR(term.SGRs.RESET)
16
+ else:
17
+ return '', ''
18
+
19
+
20
+ def _main() -> None:
21
+ parser = argparse.ArgumentParser()
22
+ parser.add_argument('file', nargs='?')
23
+ parser.add_argument('-z', '--compact', action='store_true')
24
+ parser.add_argument('-p', '--pretty', action='store_true')
25
+ parser.add_argument('-i', '--indent')
26
+ parser.add_argument('-s', '--sort-keys', action='store_true')
27
+ parser.add_argument('-c', '--color', action='store_true')
28
+ args = parser.parse_args()
29
+
30
+ separators = None
31
+ if args.compact:
32
+ separators = (',', ':')
33
+
34
+ indent = None
35
+ if args.pretty:
36
+ indent = 2
37
+ if args.indent:
38
+ try:
39
+ indent = int(args.indent)
40
+ except ValueError:
41
+ indent = args.indent
42
+
43
+ with contextlib.ExitStack() as es:
44
+ if args.file is None:
45
+ in_file = sys.stdin
46
+ else:
47
+ in_file = es.enter_context(open(args.file))
48
+
49
+ data = json.load(in_file)
50
+
51
+ kw: dict[str, ta.Any] = dict(
52
+ indent=indent,
53
+ separators=separators,
54
+ )
55
+
56
+ if args.color:
57
+ JsonRenderer(
58
+ sys.stdout,
59
+ **kw,
60
+ style=term_color,
61
+ ).render(data)
62
+ print()
63
+
64
+ else:
65
+ print(json.dumps(
66
+ data,
67
+ **kw,
68
+ ))
69
+
70
+
71
+ if __name__ == '__main__':
72
+ _main()
@@ -7,17 +7,6 @@ import functools
7
7
  import json as _json
8
8
  import typing as ta
9
9
 
10
- from .. import lang
11
-
12
-
13
- if ta.TYPE_CHECKING:
14
- import orjson as _orjson
15
- import ujson as _ujson
16
-
17
- else:
18
- _orjson = lang.proxy_import('orjson')
19
- _ujson = lang.proxy_import('ujson')
20
-
21
10
 
22
11
  ##
23
12
 
@@ -0,0 +1,114 @@
1
+ import enum
2
+ import io
3
+ import json
4
+ import typing as ta
5
+
6
+
7
+ class JsonRenderer:
8
+ class State(enum.Enum):
9
+ VALUE = enum.auto()
10
+ KEY = enum.auto()
11
+
12
+ def __init__(
13
+ self,
14
+ out: ta.TextIO,
15
+ *,
16
+ indent: int | str | None = None,
17
+ separators: tuple[str, str] | None = None,
18
+ sort_keys: bool = False,
19
+ style: ta.Callable[[ta.Any, State], tuple[str, str]] | None = None,
20
+ ) -> None:
21
+ super().__init__()
22
+
23
+ self._out = out
24
+ if isinstance(indent, (str, int)):
25
+ self._indent = (' ' * indent) if isinstance(indent, int) else indent
26
+ self._endl = '\n'
27
+ if separators is None:
28
+ separators = (',', ': ')
29
+ elif indent is None:
30
+ self._indent = self._endl = ''
31
+ if separators is None:
32
+ separators = (', ', ': ')
33
+ else:
34
+ raise TypeError(indent)
35
+ self._comma, self._colon = separators
36
+ self._sort_keys = sort_keys
37
+ self._style = style
38
+
39
+ self._level = 0
40
+
41
+ _literals: ta.ClassVar[ta.Mapping[ta.Any, str]] = {
42
+ True: 'true',
43
+ False: 'false',
44
+ None: 'null',
45
+ }
46
+
47
+ def _write(self, s: str) -> None:
48
+ if s:
49
+ self._out.write(s)
50
+
51
+ def _write_indent(self) -> None:
52
+ if self._indent:
53
+ self._write(self._endl)
54
+ if self._level:
55
+ self._write(self._indent * self._level)
56
+
57
+ def _render(self, o: ta.Any, state: State = State.VALUE) -> None:
58
+ if self._style is not None:
59
+ pre, post = self._style(o, state)
60
+ self._write(pre)
61
+ else:
62
+ post = None
63
+
64
+ if o is None or isinstance(o, bool):
65
+ self._write(self._literals[o])
66
+
67
+ elif isinstance(o, (str, int, float)):
68
+ self._write(json.dumps(o))
69
+
70
+ elif isinstance(o, ta.Mapping):
71
+ self._write('{')
72
+ self._level += 1
73
+ items = list(o.items())
74
+ if self._sort_keys:
75
+ items.sort(key=lambda t: t[0])
76
+ for i, (k, v) in enumerate(items):
77
+ if i:
78
+ self._write(self._comma)
79
+ self._write_indent()
80
+ self._render(k, JsonRenderer.State.KEY)
81
+ self._write(self._colon)
82
+ self._render(v)
83
+ self._level -= 1
84
+ if o:
85
+ self._write_indent()
86
+ self._write('}')
87
+
88
+ elif isinstance(o, ta.Sequence):
89
+ self._write('[')
90
+ self._level += 1
91
+ for i, e in enumerate(o):
92
+ if i:
93
+ self._write(self._comma)
94
+ self._write_indent()
95
+ self._render(e)
96
+ self._level -= 1
97
+ if o:
98
+ self._write_indent()
99
+ self._write(']')
100
+
101
+ else:
102
+ raise TypeError(o)
103
+
104
+ if post:
105
+ self._write(post)
106
+
107
+ def render(self, o: ta.Any) -> None:
108
+ self._render(o)
109
+
110
+ @classmethod
111
+ def render_str(cls, o: ta.Any, **kwargs: ta.Any) -> str:
112
+ out = io.StringIO()
113
+ cls(out, **kwargs).render(o)
114
+ return out.getvalue()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omlish
3
- Version: 0.0.0.dev55
3
+ Version: 0.0.0.dev56
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -1,5 +1,5 @@
1
- omlish/.manifests.json,sha256=Xj-NnBpowZX7faAyhZ2j7g4fPFFw9Da7ITTC8AojNlM,1135
2
- omlish/__about__.py,sha256=86pwA2MtS7JWsIagoYPxrhXgpb3BzVvGI5w-JyrXhZM,3420
1
+ omlish/.manifests.json,sha256=wTGXwNmvtaKsDWtTwwj3Uib5Inaj8ZBn0MB69bE80X4,1419
2
+ omlish/__about__.py,sha256=OmcoVJ5WHgdOGJt6I58kb1scFk6M-rJM3RyqDolJYdE,3420
3
3
  omlish/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  omlish/argparse.py,sha256=Vr70_85EVLJLgEkRtwOr264tMRtqtlN7ncFfXRUk5aM,6914
5
5
  omlish/c3.py,sha256=4vogWgwPb8TbNS2KkZxpoWbwjj7MuHG2lQG-hdtkvjI,8062
@@ -116,9 +116,17 @@ omlish/docker/hub.py,sha256=YcDYOi6t1FA2Sp0RVrmZ9cBXbzFWQ8wTps3wOskA-K0,1955
116
116
  omlish/docker/manifests.py,sha256=LR4FpOGNUT3bZQ-gTjB6r_-1C3YiG30QvevZjrsVUQM,7068
117
117
  omlish/formats/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
118
118
  omlish/formats/dotenv.py,sha256=UjZl3gac-0U24sDjCCGMcCqO1UCWG2Zs8PZ4JdAg2YE,17348
119
- omlish/formats/json.py,sha256=61XG6rveb3SSXmYrKvUmRdaVDyMD6C-7yVqXBBMu8t8,1017
120
119
  omlish/formats/props.py,sha256=diYjZDsG1s50ImJhkpeinMwjr8952nIVI-0gYhBIvCY,18897
121
120
  omlish/formats/yaml.py,sha256=R3NTkjomsIfjsUNmSf_bOaCUIID3JTyHJHsliQDSYQo,6688
121
+ omlish/formats/json/__init__.py,sha256=moSR67Qkju2eYb_qVDtaivepe44mxAnYuC8OCSbtETg,298
122
+ omlish/formats/json/__main__.py,sha256=1wxxKZVkj_u7HCcewwMIbGuZj_Wph95yrUbm474Op9M,188
123
+ omlish/formats/json/cli.py,sha256=4zNG69FUQ58F0sxZ-va0ysmthzuVAEFzYpDUoHB-itA,1734
124
+ omlish/formats/json/json.py,sha256=y8d8WWgzGZDTjzYc_xe9v4T0foXHI-UP7gjCwnHzUIA,828
125
+ omlish/formats/json/render.py,sha256=6edhSrxXWW3nzRfokp5qaldT0_YAj-HVEan_rErf-vo,3208
126
+ omlish/formats/json/backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
127
+ omlish/formats/json/backends/orjson.py,sha256=GYZx0zgpxwkJbFh4EJLGa6VMoEK-Q6mf5tQp8aXqTDc,2526
128
+ omlish/formats/json/backends/std.py,sha256=00NdUFT9GeWL1EWbgKhWLboDBIuDxr7EiizPZXbRWrc,1973
129
+ omlish/formats/json/backends/ujson.py,sha256=m5-hlEQCMLhat3Hg_8QTyfMH-rSsQGJYdWRWoTWkfhM,1029
122
130
  omlish/graphs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
123
131
  omlish/graphs/dags.py,sha256=JpTGxt5rsK7hy5EUy9rNUlIeDStT9ri86m8xEKiHQLE,3063
124
132
  omlish/graphs/domination.py,sha256=45iTyn7mZWPJ1ANrqD96aPXqzEeyFpybMvvcVxo9XvQ,7592
@@ -339,9 +347,9 @@ omlish/text/delimit.py,sha256=ubPXcXQmtbOVrUsNh5gH1mDq5H-n1y2R4cPL5_DQf68,4928
339
347
  omlish/text/glyphsplit.py,sha256=Ug-dPRO7x-OrNNr8g1y6DotSZ2KH0S-VcOmUobwa4B0,3296
340
348
  omlish/text/indent.py,sha256=6Jj6TFY9unaPa4xPzrnZemJ-fHsV53IamP93XGjSUHs,1274
341
349
  omlish/text/parts.py,sha256=7vPF1aTZdvLVYJ4EwBZVzRSy8XB3YqPd7JwEnNGGAOo,6495
342
- omlish-0.0.0.dev55.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
343
- omlish-0.0.0.dev55.dist-info/METADATA,sha256=KETPIaUeC-qvI4B-ZMyZKQC6pNl-9-GHQdoNBC5Yc4w,4167
344
- omlish-0.0.0.dev55.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
345
- omlish-0.0.0.dev55.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
346
- omlish-0.0.0.dev55.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
347
- omlish-0.0.0.dev55.dist-info/RECORD,,
350
+ omlish-0.0.0.dev56.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
351
+ omlish-0.0.0.dev56.dist-info/METADATA,sha256=LAf0N5afnrlk6A12ObzkTXJ8pDcdKwjpADduKtBz6f4,4167
352
+ omlish-0.0.0.dev56.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
353
+ omlish-0.0.0.dev56.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
354
+ omlish-0.0.0.dev56.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
355
+ omlish-0.0.0.dev56.dist-info/RECORD,,