omdev 0.0.0.dev344__py3-none-any.whl → 0.0.0.dev346__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.

Potentially problematic release.


This version of omdev might be problematic. Click here for more details.

omdev/.manifests.json CHANGED
@@ -408,6 +408,21 @@
408
408
  }
409
409
  }
410
410
  },
411
+ {
412
+ "module": ".tools.jsonview.__main__",
413
+ "attr": "_CLI_MODULE",
414
+ "file": "omdev/tools/jsonview/__main__.py",
415
+ "line": 4,
416
+ "value": {
417
+ "$.cli.types.CliModule": {
418
+ "cmd_name": [
419
+ "jsonview",
420
+ "jv"
421
+ ],
422
+ "mod_name": "omdev.tools.jsonview.__main__"
423
+ }
424
+ }
425
+ },
411
426
  {
412
427
  "module": ".tools.linehisto",
413
428
  "attr": "_CLI_MODULE",
@@ -509,6 +524,21 @@
509
524
  }
510
525
  }
511
526
  },
527
+ {
528
+ "module": ".tools.shell",
529
+ "attr": "_CLI_MODULE",
530
+ "file": "omdev/tools/shell.py",
531
+ "line": 110,
532
+ "value": {
533
+ "$.cli.types.CliModule": {
534
+ "cmd_name": [
535
+ "shell",
536
+ "sh"
537
+ ],
538
+ "mod_name": "omdev.tools.shell"
539
+ }
540
+ }
541
+ },
512
542
  {
513
543
  "module": ".tools.sqlrepl",
514
544
  "attr": "_CLI_MODULE",
File without changes
@@ -0,0 +1,11 @@
1
+ from ...cli import CliModule
2
+
3
+
4
+ # @omlish-manifest
5
+ _CLI_MODULE = CliModule(['jsonview', 'jv'], __name__)
6
+
7
+
8
+ if __name__ == '__main__':
9
+ from .cli import _main
10
+
11
+ _main()
@@ -0,0 +1,142 @@
1
+ import argparse
2
+ import http.server
3
+ import json
4
+ import os
5
+ import socketserver
6
+ import sys
7
+ import threading
8
+ import webbrowser
9
+
10
+ from omlish import lang
11
+
12
+
13
+ ##
14
+
15
+
16
+ HTML_TEMPLATE = """
17
+ <!DOCTYPE html>
18
+ <html lang="en">
19
+
20
+ <head>
21
+ <meta charset="UTF-8">
22
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
23
+ <title>JSON Viewer</title>
24
+ <link
25
+ href="https://cdn.jsdelivr.net/npm/jsoneditor/dist/jsoneditor.min.css"
26
+ rel="stylesheet"
27
+ type="text/css"
28
+ >
29
+ <style>
30
+ {css_src}
31
+ </style>
32
+ </head>
33
+
34
+ <body>
35
+ <div class="input-area">
36
+ <label for="jmespath-input">JMESPath:</label>
37
+ <input
38
+ type="text"
39
+ id="jmespath-input"
40
+ list="jmespath-history"
41
+ placeholder="Enter JMESPath expression..."
42
+ autocomplete="off"
43
+ >
44
+ <datalist id="jmespath-history"></datalist>
45
+ <span id="error-message"></span>
46
+ </div>
47
+ <div id="jsoneditor"></div>
48
+
49
+ <script src="https://cdn.jsdelivr.net/npm/jsoneditor@10.2.0/dist/jsoneditor.min.js"></script>
50
+ <script src="https://cdn.jsdelivr.net/npm/jmespath@0.16.0/jmespath.min.js"></script>
51
+ <script>
52
+ const originalJsonData = {json_data};
53
+ </script>
54
+ <script>
55
+ {js_src}
56
+ </script>
57
+ </body>
58
+
59
+ </html>
60
+ """
61
+
62
+
63
+ def view_json(filepath: str, port: int) -> None:
64
+ if not os.path.exists(filepath):
65
+ print(f"Error: File not found at '{filepath}'", file=sys.stderr)
66
+ return
67
+
68
+ try:
69
+ with open(filepath, encoding='utf-8') as f:
70
+ raw_content = f.read()
71
+ except json.JSONDecodeError as e:
72
+ print(f'Error: Invalid JSON file. {e}', file=sys.stderr)
73
+ return
74
+
75
+ if filepath.endswith('.jsonl'):
76
+ json_content = [json.loads(sl) for l in raw_content.splitlines() if (sl := l.strip())]
77
+ elif filepath.endswith('.json5'):
78
+ from omlish.formats import json5
79
+ json_content = json5.loads(raw_content)
80
+ else:
81
+ json_content = json.loads(raw_content)
82
+
83
+ # Use compact dumps for embedding in JS, it's more efficient
84
+ json_string = json.dumps(json_content)
85
+
86
+ resources = lang.get_relative_resources('resources', globals=globals())
87
+ css_src = resources['jsonview.css'].read_text()
88
+ js_src = resources['jsonview.js'].read_text()
89
+
90
+ class JsonViewerHttpRequestHandler(http.server.SimpleHTTPRequestHandler):
91
+ def do_GET(self) -> None: # noqa
92
+ if self.path == '/':
93
+ self.send_response(200)
94
+ self.send_header('Content-type', 'text/html')
95
+ self.end_headers()
96
+ html_content = HTML_TEMPLATE.format(
97
+ css_src=css_src,
98
+ js_src=js_src,
99
+ json_data=json_string,
100
+ )
101
+ self.wfile.write(html_content.encode('utf-8'))
102
+ else:
103
+ super().do_GET()
104
+
105
+ handler_cls = JsonViewerHttpRequestHandler
106
+ with socketserver.TCPServer(('127.0.0.1', port), handler_cls) as httpd:
107
+ url = f'http://127.0.0.1:{port}'
108
+ print(f'Serving JSON file: {os.path.abspath(filepath)}', file=sys.stderr)
109
+ print(f'Viewer running at: {url}', file=sys.stderr)
110
+ print('Press Ctrl+C to stop the server.', file=sys.stderr)
111
+
112
+ threading.Timer(1, lambda: webbrowser.open_new(url)).start()
113
+
114
+ try:
115
+ httpd.serve_forever()
116
+ except KeyboardInterrupt:
117
+ print('\nShutting down server.', file=sys.stderr)
118
+ httpd.shutdown()
119
+
120
+
121
+ def _main() -> None:
122
+ parser = argparse.ArgumentParser(
123
+ description='Launch a web-based JSON viewer with JMESPath transformations.',
124
+ formatter_class=argparse.RawTextHelpFormatter,
125
+ )
126
+ parser.add_argument(
127
+ 'filepath',
128
+ help='The path to the JSON file you want to view.',
129
+ )
130
+ parser.add_argument(
131
+ '-p', '--port',
132
+ type=int,
133
+ default=(default_port := 8999),
134
+ help=f'The port to run the web server on. Defaults to {default_port}.',
135
+ )
136
+ args = parser.parse_args()
137
+
138
+ view_json(args.filepath, args.port)
139
+
140
+
141
+ if __name__ == '__main__':
142
+ _main()
File without changes
@@ -0,0 +1,32 @@
1
+ html, body {
2
+ height: 100%;
3
+ margin: 0;
4
+ padding: 0;
5
+ overflow: hidden;
6
+ display: flex;
7
+ flex-direction: column;
8
+ font-family: sans-serif;
9
+ }
10
+ .input-area {
11
+ padding: 10px;
12
+ display: flex;
13
+ align-items: center;
14
+ gap: 10px;
15
+ border-bottom: 1px solid #ccc;
16
+ flex-shrink: 0;
17
+ }
18
+ #jmespath-input {
19
+ flex-grow: 1;
20
+ padding: 8px;
21
+ border: 1px solid #ccc;
22
+ border-radius: 4px;
23
+ font-size: 14px;
24
+ }
25
+ #error-message {
26
+ color: red;
27
+ font-size: 14px;
28
+ white-space: nowrap;
29
+ }
30
+ #jsoneditor {
31
+ flex-grow: 1;
32
+ }
@@ -0,0 +1,67 @@
1
+ const container = document.getElementById('jsoneditor');
2
+ const jmespathInput = document.getElementById('jmespath-input');
3
+ const errorMessage = document.getElementById('error-message');
4
+ const historyDataList = document.getElementById('jmespath-history');
5
+
6
+ let jmespathHistory = [];
7
+
8
+ const options = {
9
+ mode: 'tree',
10
+ modes: ['code', 'form', 'text', 'tree', 'view', 'preview'],
11
+ onError: function (err) {
12
+ alert(err.toString());
13
+ }
14
+ };
15
+
16
+ const editor = new JSONEditor(container, options);
17
+ editor.set(originalJsonData);
18
+ editor.expandAll();
19
+
20
+ let debounceTimer;
21
+
22
+ function updateHistory(expression) {
23
+ if (!expression || jmespathHistory.includes(expression)) {
24
+ return;
25
+ }
26
+
27
+ jmespathHistory.unshift(expression);
28
+
29
+ historyDataList.innerHTML = '';
30
+ jmespathHistory.forEach(item => {
31
+ const option = document.createElement('option');
32
+ option.value = item;
33
+ historyDataList.appendChild(option);
34
+ });
35
+ }
36
+
37
+ function updateView() {
38
+ const expression = jmespathInput.value.trim();
39
+ errorMessage.textContent = '';
40
+
41
+ if (expression === '') {
42
+ editor.set(originalJsonData);
43
+ editor.expandAll();
44
+ return;
45
+ }
46
+
47
+ try {
48
+ const result = jmespath.search(originalJsonData, expression);
49
+ editor.set(result);
50
+ editor.expandAll();
51
+ updateHistory(expression);
52
+ } catch (err) {
53
+ errorMessage.textContent = 'Invalid JMESPath expression';
54
+ }
55
+ }
56
+
57
+ jmespathInput.addEventListener('input', () => {
58
+ clearTimeout(debounceTimer);
59
+ debounceTimer = setTimeout(updateView, 50);
60
+ });
61
+
62
+ jmespathInput.addEventListener('keydown', (event) => {
63
+ if (event.key === 'Enter') {
64
+ clearTimeout(debounceTimer);
65
+ updateView();
66
+ }
67
+ });
omdev/tools/shell.py ADDED
@@ -0,0 +1,119 @@
1
+ import shlex
2
+ import typing as ta
3
+
4
+ from omlish import check
5
+ from omlish.argparse import all as ap
6
+ from omlish.formats import json
7
+
8
+ from ..cli.types import CliModule
9
+
10
+
11
+ ##
12
+
13
+
14
+ DEFAULT_DELIMITER = ' '
15
+
16
+
17
+ def _print_list(
18
+ lst: ta.Iterable[str],
19
+ *,
20
+ delimiter: str | None = None,
21
+ ) -> None:
22
+ check.not_isinstance(lst, str)
23
+
24
+ if delimiter is None:
25
+ delimiter = DEFAULT_DELIMITER
26
+
27
+ for e in lst:
28
+ check.not_in(delimiter, e)
29
+
30
+ print(delimiter.join(lst))
31
+
32
+
33
+ ##
34
+
35
+
36
+ class ShellCli(ap.Cli):
37
+ @ap.cmd(
38
+ ap.arg('strs', nargs='*'),
39
+ ap.arg('--delimiter', '-d'),
40
+ )
41
+ def quote(self) -> None:
42
+ _print_list(
43
+ [shlex.quote(e) for e in self.args.strs],
44
+ delimiter=self.args.delimiter,
45
+ )
46
+
47
+ #
48
+
49
+ _PREFIX_OR_INTERLEAVE_ARGS: ta.ClassVar[ta.Sequence[ta.Any]] = [
50
+ ap.arg('--quote', '-q', action='store_true'),
51
+ ap.arg('--delimiter', '-d'),
52
+ ]
53
+
54
+ def _prefix_or_interleave(
55
+ self,
56
+ mode: ta.Literal['prefix', 'interleave'],
57
+ separator: str,
58
+ items: ta.Iterable[str],
59
+ ) -> None:
60
+ lst = []
61
+
62
+ for i, e in enumerate(check.not_isinstance(items, str)):
63
+ if i or mode != 'interleave':
64
+ lst.append(separator)
65
+
66
+ if self.args.quote:
67
+ e = shlex.quote(e)
68
+
69
+ lst.append(e)
70
+
71
+ _print_list(
72
+ lst,
73
+ delimiter=self.args.delimiter,
74
+ )
75
+
76
+ @ap.cmd(
77
+ ap.arg('prefix'),
78
+ ap.arg('items', nargs='*'),
79
+ *_PREFIX_OR_INTERLEAVE_ARGS,
80
+ )
81
+ def prefix(self) -> None:
82
+ self._prefix_or_interleave(
83
+ 'prefix',
84
+ self.args.prefix,
85
+ self.args.items,
86
+ )
87
+
88
+ @ap.cmd(
89
+ ap.arg('separator'),
90
+ ap.arg('items', nargs='*'),
91
+ *_PREFIX_OR_INTERLEAVE_ARGS,
92
+ )
93
+ def interleave(self) -> None:
94
+ self._prefix_or_interleave(
95
+ 'interleave',
96
+ self.args.separator,
97
+ self.args.items,
98
+ )
99
+
100
+ #
101
+
102
+ @ap.cmd(accepts_unknown=True)
103
+ def argv(self) -> None:
104
+ print(json.dumps_pretty(self.unknown_args))
105
+
106
+
107
+ ##
108
+
109
+
110
+ # @omlish-manifest
111
+ _CLI_MODULE = CliModule(['shell', 'sh'], __name__)
112
+
113
+
114
+ def _main() -> None:
115
+ ShellCli().cli_run_and_exit()
116
+
117
+
118
+ if __name__ == '__main__':
119
+ _main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omdev
3
- Version: 0.0.0.dev344
3
+ Version: 0.0.0.dev346
4
4
  Summary: omdev
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -12,7 +12,7 @@ Classifier: Operating System :: OS Independent
12
12
  Classifier: Operating System :: POSIX
13
13
  Requires-Python: >=3.12
14
14
  License-File: LICENSE
15
- Requires-Dist: omlish==0.0.0.dev344
15
+ Requires-Dist: omlish==0.0.0.dev346
16
16
  Provides-Extra: all
17
17
  Requires-Dist: black~=25.1; extra == "all"
18
18
  Requires-Dist: pycparser~=2.22; extra == "all"
@@ -1,4 +1,4 @@
1
- omdev/.manifests.json,sha256=O4r1UUZGQl-G3MCacKgdjWG7H0ybieqdxw9AuTGaAQM,11257
1
+ omdev/.manifests.json,sha256=L7A85Vj8U60KXQwEEr2wWeUSv2sXTgiTHE10hIp8Uxk,11870
2
2
  omdev/__about__.py,sha256=2_6pQyjxqAspvwBSJUWQgdBBcGaDO3zX8yETLaspUQE,1202
3
3
  omdev/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  omdev/cmake.py,sha256=9rfSvFHPmKDj9ngvfDB2vK8O-xO_ZwUm7hMKLWA-yOw,4578
@@ -274,6 +274,7 @@ omdev/tools/pip.py,sha256=eBD41hp-V3thGfhUBM3Erxl4CSG-5LG6Szo1sA76P2k,3459
274
274
  omdev/tools/prof.py,sha256=hQakAsViJD4gLJpLLZnTkOqmTDAwM48Nx5q-O_aFlYM,1467
275
275
  omdev/tools/qr.py,sha256=tm68lPwEAkEwIL2sUKPKBYfwwPtjVWG1DBZwur8_jY8,1737
276
276
  omdev/tools/shadow.py,sha256=4E2ilxa16liIvQxvgU37ITkOMrP6ufShRQfeW7wwtRc,1697
277
+ omdev/tools/shell.py,sha256=5hF_8DCtB3XrzJSfmQDsr7X3Fi9KRV4M70q9qp0KREA,2341
277
278
  omdev/tools/sqlrepl.py,sha256=wAjrfXNrRV63-NJCC2HlGQnFh7lUH0bHMnOjYotQqFs,5753
278
279
  omdev/tools/antlr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
279
280
  omdev/tools/antlr/__main__.py,sha256=7db8YIfCp4GoU-lW7jJJDiLQ6dDrd25M7gUv1AHGj4w,170
@@ -293,12 +294,18 @@ omdev/tools/json/io.py,sha256=sfj2hJS9Hy3aUR8a_lLzOrYcmL9fSKyvOHiofdUASsI,1427
293
294
  omdev/tools/json/parsing.py,sha256=csoTuG2C2AJB7PgG4TKt2XqdiBAQXtpXRWBxoMkk14g,2103
294
295
  omdev/tools/json/processing.py,sha256=iFm5VqaxJ97WHaun2ed7NEjMxhFeJqf28bLNfoDJft0,1209
295
296
  omdev/tools/json/rendering.py,sha256=3HhdlKSetS6iK1tjF2aILzsl8Mb3D8wW92vYwGpRdVA,2244
297
+ omdev/tools/jsonview/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
298
+ omdev/tools/jsonview/__main__.py,sha256=BF-MVWpPJJeQYUqJA48G395kxw0lEJnV-hRLV_F9G2A,173
299
+ omdev/tools/jsonview/cli.py,sha256=XU-knzWnIAeFL_g6PmE9FLdrXFSuFpOTNXC3KivH1zY,4008
300
+ omdev/tools/jsonview/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
301
+ omdev/tools/jsonview/resources/jsonview.css,sha256=3pWHBd-NHONkhJzrHBxnlHtDq916Q6BWFJbnTuwJ32U,555
302
+ omdev/tools/jsonview/resources/jsonview.js,sha256=faDvXDOXKvEvjOuIlz4D3F2ReQXb_buXDU27hKuuqAw,1648
296
303
  omdev/tools/pawk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
297
304
  omdev/tools/pawk/__main__.py,sha256=VCqeRVnqT1RPEoIrqHFSu4PXVMg4YEgF4qCQm90-eRI,66
298
305
  omdev/tools/pawk/pawk.py,sha256=zsEkfQX0jF5bn712uqPAyBSdJt2dno1LH2oeSMNfXQI,11424
299
- omdev-0.0.0.dev344.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
300
- omdev-0.0.0.dev344.dist-info/METADATA,sha256=EJ8fgtaVB26Z4o3H1CGpoK8met3ip-LwKvsuNEozsBM,1674
301
- omdev-0.0.0.dev344.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
302
- omdev-0.0.0.dev344.dist-info/entry_points.txt,sha256=dHLXFmq5D9B8qUyhRtFqTGWGxlbx3t5ejedjrnXNYLU,33
303
- omdev-0.0.0.dev344.dist-info/top_level.txt,sha256=1nr7j30fEWgLYHW3lGR9pkdHkb7knv1U1ES1XRNVQ6k,6
304
- omdev-0.0.0.dev344.dist-info/RECORD,,
306
+ omdev-0.0.0.dev346.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
307
+ omdev-0.0.0.dev346.dist-info/METADATA,sha256=vP1PiHBd2-wIaE6dpVF49FPkbt8J0Fr3JGagbVukrZs,1674
308
+ omdev-0.0.0.dev346.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
309
+ omdev-0.0.0.dev346.dist-info/entry_points.txt,sha256=dHLXFmq5D9B8qUyhRtFqTGWGxlbx3t5ejedjrnXNYLU,33
310
+ omdev-0.0.0.dev346.dist-info/top_level.txt,sha256=1nr7j30fEWgLYHW3lGR9pkdHkb7knv1U1ES1XRNVQ6k,6
311
+ omdev-0.0.0.dev346.dist-info/RECORD,,