omdev 0.0.0.dev416__py3-none-any.whl → 0.0.0.dev500__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 → .omlish-manifests.json} +23 -47
- omdev/README.md +51 -0
- omdev/__about__.py +12 -8
- omdev/amalg/cli/main.py +1 -2
- omdev/amalg/gen/gen.py +49 -6
- omdev/amalg/gen/imports.py +1 -1
- omdev/amalg/gen/manifests.py +1 -1
- omdev/amalg/gen/resources.py +1 -1
- omdev/amalg/gen/srcfiles.py +26 -3
- omdev/amalg/gen/strip.py +1 -1
- omdev/amalg/gen/types.py +1 -1
- omdev/amalg/gen/typing.py +1 -1
- omdev/amalg/info.py +32 -0
- omdev/cache/compute/storage.py +3 -1
- omdev/cache/data/actions.py +1 -1
- omdev/cache/data/cache.py +2 -2
- omdev/cache/data/specs.py +1 -1
- omdev/cexts/_boilerplate.cc +2 -3
- omdev/cexts/_distutils/build_ext.py +5 -2
- omdev/cexts/_distutils/compilers/ccompiler.py +5 -2
- omdev/cexts/_distutils/compilers/options.py +3 -0
- omdev/cexts/_distutils/compilers/unixccompiler.py +6 -2
- omdev/cexts/_distutils/dir_util.py +6 -2
- omdev/cexts/_distutils/errors.py +3 -0
- omdev/cexts/_distutils/extension.py +3 -0
- omdev/cexts/_distutils/file_util.py +6 -2
- omdev/cexts/_distutils/modified.py +3 -0
- omdev/cexts/_distutils/spawn.py +6 -2
- omdev/cexts/_distutils/sysconfig.py +3 -0
- omdev/cexts/_distutils/util.py +6 -2
- omdev/cexts/_distutils/version.py +3 -0
- omdev/cexts/cmake.py +5 -3
- omdev/cexts/scan.py +1 -2
- omdev/ci/cache.py +7 -3
- omdev/ci/cli.py +6 -4
- omdev/ci/docker/buildcaching.py +3 -1
- omdev/ci/docker/cache.py +2 -1
- omdev/ci/docker/cacheserved/cache.py +4 -1
- omdev/ci/docker/cacheserved/manifests.py +2 -2
- omdev/ci/docker/dataserver.py +2 -2
- omdev/ci/docker/imagepulling.py +2 -1
- omdev/ci/docker/packing.py +1 -1
- omdev/ci/docker/repositories.py +2 -1
- omdev/ci/github/api/clients.py +8 -4
- omdev/ci/github/api/v1/client.py +4 -1
- omdev/ci/github/api/v2/api.py +2 -0
- omdev/ci/github/api/v2/azure.py +4 -1
- omdev/ci/github/api/v2/client.py +4 -1
- omdev/cli/clicli.py +37 -7
- omdev/clipboard/clipboard.py +1 -1
- omdev/cmake.py +2 -1
- omdev/cmdlog/cli.py +1 -2
- omdev/dataclasses/_dumping.py +1960 -0
- omdev/dataclasses/_template.py +22 -0
- omdev/dataclasses/cli.py +7 -2
- omdev/dataclasses/codegen.py +342 -62
- omdev/dataclasses/dumping.py +200 -0
- omdev/dataserver/handlers.py +3 -2
- omdev/dataserver/targets.py +2 -2
- omdev/imgur.py +2 -2
- omdev/interp/cli.py +1 -1
- omdev/interp/inspect.py +2 -1
- omdev/interp/providers/base.py +3 -2
- omdev/interp/providers/standalone.py +4 -1
- omdev/interp/providers/system.py +2 -2
- omdev/interp/pyenv/install.py +2 -1
- omdev/interp/pyenv/provider.py +2 -2
- omdev/interp/types.py +3 -2
- omdev/interp/uv/provider.py +40 -2
- omdev/interp/uv/uv.py +2 -2
- omdev/interp/venvs.py +3 -2
- omdev/irc/messages/base.py +50 -0
- omdev/irc/messages/formats.py +92 -0
- omdev/irc/messages/messages.py +775 -0
- omdev/irc/messages/parsing.py +99 -0
- omdev/irc/numerics/formats.py +97 -0
- omdev/irc/numerics/numerics.py +865 -0
- omdev/irc/numerics/types.py +59 -0
- omdev/irc/protocol/LICENSE +11 -0
- omdev/irc/protocol/__init__.py +61 -0
- omdev/irc/protocol/consts.py +6 -0
- omdev/irc/protocol/errors.py +30 -0
- omdev/irc/protocol/message.py +21 -0
- omdev/irc/protocol/nuh.py +55 -0
- omdev/irc/protocol/parsing.py +158 -0
- omdev/irc/protocol/rendering.py +153 -0
- omdev/irc/protocol/tags.py +102 -0
- omdev/irc/protocol/utils.py +30 -0
- omdev/manifests/_dumping.py +529 -136
- omdev/manifests/building.py +6 -3
- omdev/manifests/main.py +1 -1
- omdev/markdown/__init__.py +0 -0
- omdev/markdown/incparse.py +116 -0
- omdev/markdown/tokens.py +51 -0
- omdev/oci/data.py +2 -2
- omdev/oci/datarefs.py +2 -2
- omdev/oci/media.py +2 -2
- omdev/oci/repositories.py +3 -2
- omdev/packaging/marshal.py +9 -9
- omdev/packaging/requires.py +6 -6
- omdev/packaging/revisions.py +5 -2
- omdev/packaging/specifiers.py +41 -42
- omdev/packaging/versions.py +10 -10
- omdev/packaging/wheelfile.py +4 -2
- omdev/precheck/blanklines.py +66 -0
- omdev/precheck/caches.py +1 -1
- omdev/precheck/imports.py +14 -1
- omdev/precheck/lite.py +2 -2
- omdev/precheck/main.py +5 -5
- omdev/precheck/unicode.py +39 -15
- omdev/py/asts/__init__.py +0 -0
- omdev/py/asts/parents.py +28 -0
- omdev/py/asts/toplevel.py +123 -0
- omdev/py/asts/visitors.py +18 -0
- omdev/py/attrdocs.py +6 -7
- omdev/py/bracepy.py +12 -4
- omdev/py/docstrings/numpydoc.py +4 -4
- omdev/py/reprs.py +32 -0
- omdev/py/scripts/execstat.py +31 -26
- omdev/py/srcheaders.py +1 -1
- omdev/py/tokens/__init__.py +0 -0
- omdev/{tokens → py/tokens}/utils.py +2 -1
- omdev/py/tools/importscan.py +2 -2
- omdev/py/tools/mkrelimp.py +3 -4
- omdev/py/tools/pipdepup.py +686 -0
- omdev/pyproject/cli.py +1 -1
- omdev/pyproject/pkg.py +197 -48
- omdev/pyproject/reqs.py +36 -10
- omdev/pyproject/tools/__init__.py +0 -0
- omdev/pyproject/tools/aboutdeps.py +60 -0
- omdev/pyproject/venvs.py +12 -2
- omdev/rs/__init__.py +0 -0
- omdev/scripts/ci.py +9551 -6982
- omdev/scripts/interp.py +1323 -892
- omdev/scripts/lib/__init__.py +0 -0
- omdev/scripts/lib/inject.py +2086 -0
- omdev/scripts/lib/logs.py +2175 -0
- omdev/scripts/lib/marshal.py +1731 -0
- omdev/scripts/pyproject.py +4979 -1874
- omdev/tools/docker.py +19 -7
- omdev/tools/git/cli.py +56 -16
- omdev/tools/git/messages.py +2 -2
- omdev/tools/json/cli.py +6 -6
- omdev/tools/json/formats.py +2 -0
- omdev/tools/json/parsing.py +5 -5
- omdev/tools/json/processing.py +6 -3
- omdev/tools/json/rendering.py +2 -2
- omdev/tools/jsonview/cli.py +49 -65
- omdev/tools/jsonview/resources/jsonview.html.j2 +43 -0
- omdev/tools/pawk/README.md +195 -0
- omdev/tools/pawk/pawk.py +2 -2
- omdev/tools/pip.py +8 -0
- omdev/tui/__init__.py +0 -0
- omdev/tui/apps/__init__.py +0 -0
- omdev/tui/apps/edit/__init__.py +0 -0
- omdev/tui/apps/edit/main.py +167 -0
- omdev/tui/apps/irc/__init__.py +0 -0
- omdev/tui/apps/irc/__main__.py +4 -0
- omdev/tui/apps/irc/app.py +286 -0
- omdev/tui/apps/irc/client.py +187 -0
- omdev/tui/apps/irc/commands.py +175 -0
- omdev/tui/apps/irc/main.py +26 -0
- omdev/tui/apps/markdown/__init__.py +0 -0
- omdev/tui/apps/markdown/__main__.py +11 -0
- omdev/{ptk → tui/apps}/markdown/cli.py +5 -7
- omdev/tui/rich/__init__.py +46 -0
- omdev/tui/rich/console2.py +20 -0
- omdev/tui/rich/markdown2.py +186 -0
- omdev/tui/textual/__init__.py +265 -0
- omdev/tui/textual/app2.py +16 -0
- omdev/tui/textual/autocomplete/LICENSE +21 -0
- omdev/tui/textual/autocomplete/__init__.py +33 -0
- omdev/tui/textual/autocomplete/matching.py +226 -0
- omdev/tui/textual/autocomplete/paths.py +202 -0
- omdev/tui/textual/autocomplete/widget.py +612 -0
- omdev/tui/textual/debug/__init__.py +10 -0
- omdev/tui/textual/debug/dominfo.py +151 -0
- omdev/tui/textual/debug/screen.py +24 -0
- omdev/tui/textual/devtools.py +187 -0
- omdev/tui/textual/drivers2.py +55 -0
- omdev/tui/textual/logging2.py +20 -0
- omdev/tui/textual/types.py +45 -0
- {omdev-0.0.0.dev416.dist-info → omdev-0.0.0.dev500.dist-info}/METADATA +18 -12
- omdev-0.0.0.dev500.dist-info/RECORD +386 -0
- omdev/ptk/__init__.py +0 -103
- omdev/ptk/apps/ncdu.py +0 -167
- omdev/ptk/confirm.py +0 -60
- omdev/ptk/markdown/LICENSE +0 -22
- omdev/ptk/markdown/__init__.py +0 -10
- omdev/ptk/markdown/__main__.py +0 -11
- omdev/ptk/markdown/border.py +0 -94
- omdev/ptk/markdown/markdown.py +0 -390
- omdev/ptk/markdown/parser.py +0 -42
- omdev/ptk/markdown/styles.py +0 -29
- omdev/ptk/markdown/tags.py +0 -299
- omdev/ptk/markdown/utils.py +0 -366
- omdev/pyproject/cexts.py +0 -110
- omdev/tools/antlr/__main__.py +0 -11
- omdev/tools/antlr/cli.py +0 -62
- omdev/tools/antlr/consts.py +0 -7
- omdev/tools/antlr/gen.py +0 -188
- omdev-0.0.0.dev416.dist-info/RECORD +0 -332
- /omdev/{ptk/apps → irc}/__init__.py +0 -0
- /omdev/{tokens → irc/messages}/__init__.py +0 -0
- /omdev/{tools/antlr → irc/numerics}/__init__.py +0 -0
- /omdev/{tokens → py/tokens}/all.py +0 -0
- /omdev/{tokens → py/tokens}/tokenizert.py +0 -0
- {omdev-0.0.0.dev416.dist-info → omdev-0.0.0.dev500.dist-info}/WHEEL +0 -0
- {omdev-0.0.0.dev416.dist-info → omdev-0.0.0.dev500.dist-info}/entry_points.txt +0 -0
- {omdev-0.0.0.dev416.dist-info → omdev-0.0.0.dev500.dist-info}/licenses/LICENSE +0 -0
- {omdev-0.0.0.dev416.dist-info → omdev-0.0.0.dev500.dist-info}/top_level.txt +0 -0
omdev/tools/docker.py
CHANGED
|
@@ -13,12 +13,14 @@ import re
|
|
|
13
13
|
import shutil
|
|
14
14
|
import subprocess
|
|
15
15
|
import sys
|
|
16
|
+
import threading
|
|
16
17
|
import typing as ta
|
|
17
18
|
|
|
18
19
|
from omlish import check
|
|
19
20
|
from omlish import lang
|
|
20
21
|
from omlish import marshal as msh
|
|
21
22
|
from omlish.argparse import all as ap
|
|
23
|
+
from omlish.concurrent import all as conc
|
|
22
24
|
from omlish.docker import all as dck
|
|
23
25
|
from omlish.docker.ns1 import build_docker_ns1_run_cmd
|
|
24
26
|
from omlish.formats import json
|
|
@@ -204,6 +206,7 @@ class Cli(ap.Cli):
|
|
|
204
206
|
|
|
205
207
|
@ap.cmd(
|
|
206
208
|
ap.arg('-f', '--file'),
|
|
209
|
+
ap.arg('-p', '--parallelism', type=int, default=4),
|
|
207
210
|
)
|
|
208
211
|
def compose_image_updates(self) -> None:
|
|
209
212
|
if self.args.file:
|
|
@@ -215,23 +218,32 @@ class Cli(ap.Cli):
|
|
|
215
218
|
yml_src = f.read()
|
|
216
219
|
|
|
217
220
|
cfg_dct = yaml.safe_load(yml_src)
|
|
218
|
-
for svc_name, svc_dct in cfg_dct.get('services', {}).items():
|
|
219
|
-
if not (img := svc_dct.get('image')):
|
|
220
|
-
continue
|
|
221
221
|
|
|
222
|
+
print_lock = threading.Lock()
|
|
223
|
+
|
|
224
|
+
def do_svc(svc_name: str, img: str) -> None: # noqa
|
|
222
225
|
repo, _, base = img.partition(':')
|
|
223
226
|
try:
|
|
224
227
|
if (info := dck.get_hub_repo_info(repo)) is None:
|
|
225
|
-
|
|
228
|
+
return
|
|
226
229
|
|
|
227
230
|
lt = dck.select_latest_tag(info.tags, base=base)
|
|
228
231
|
if f'{repo}:{lt}' == img:
|
|
229
|
-
|
|
232
|
+
return
|
|
230
233
|
|
|
231
|
-
|
|
234
|
+
with print_lock:
|
|
235
|
+
print(f'{svc_name}: {lt}')
|
|
232
236
|
|
|
233
237
|
except Exception as e: # noqa
|
|
234
|
-
|
|
238
|
+
with print_lock:
|
|
239
|
+
print(f'Error checking docker update for {svc_name}: {e!r}', file=sys.stderr)
|
|
240
|
+
|
|
241
|
+
with conc.new_executor(self.args.parallelism) as exe:
|
|
242
|
+
conc.wait_futures([
|
|
243
|
+
exe.submit(do_svc, svc_name, img)
|
|
244
|
+
for svc_name, svc_dct in cfg_dct.get('services', {}).items()
|
|
245
|
+
if (img := svc_dct.get('image'))
|
|
246
|
+
])
|
|
235
247
|
|
|
236
248
|
#
|
|
237
249
|
|
omdev/tools/git/cli.py
CHANGED
|
@@ -21,7 +21,7 @@ TODO:
|
|
|
21
21
|
fatal: Need to specify how to reconcile divergent branches.
|
|
22
22
|
"""
|
|
23
23
|
import dataclasses as dc
|
|
24
|
-
import
|
|
24
|
+
import glob
|
|
25
25
|
import os
|
|
26
26
|
import shutil
|
|
27
27
|
import tempfile
|
|
@@ -58,7 +58,7 @@ else:
|
|
|
58
58
|
msh = lang.proxy_import('omlish.marshal')
|
|
59
59
|
|
|
60
60
|
|
|
61
|
-
log =
|
|
61
|
+
log = logs.get_module_logger(globals())
|
|
62
62
|
|
|
63
63
|
|
|
64
64
|
##
|
|
@@ -90,7 +90,12 @@ def get_first_commit_of_day(rev: str) -> str | None:
|
|
|
90
90
|
class Cli(ap.Cli):
|
|
91
91
|
@dc.dataclass(frozen=True, kw_only=True)
|
|
92
92
|
class Config:
|
|
93
|
-
|
|
93
|
+
@dc.dataclass(frozen=True, kw_only=True)
|
|
94
|
+
class MessageGenerator:
|
|
95
|
+
name: str
|
|
96
|
+
config: ta.Mapping[str, ta.Any] | None = None
|
|
97
|
+
|
|
98
|
+
message_generator: str | MessageGenerator | None = None
|
|
94
99
|
|
|
95
100
|
_config_file_path_arg: ta.Optional[str] = ap.arg_('-c', '--config-file-path', nargs='?')
|
|
96
101
|
|
|
@@ -280,6 +285,35 @@ class Cli(ap.Cli):
|
|
|
280
285
|
|
|
281
286
|
# Lazy helpers
|
|
282
287
|
|
|
288
|
+
def _make_git_message_generator(
|
|
289
|
+
self,
|
|
290
|
+
*,
|
|
291
|
+
name: str | None = None,
|
|
292
|
+
cwd: str | None = None,
|
|
293
|
+
) -> GitMessageGenerator:
|
|
294
|
+
cls: type[GitMessageGenerator] = TimestampGitMessageGenerator
|
|
295
|
+
kw: dict[str, ta.Any] = {}
|
|
296
|
+
|
|
297
|
+
if name is None:
|
|
298
|
+
if cwd is None:
|
|
299
|
+
cwd = os.getcwd()
|
|
300
|
+
|
|
301
|
+
cfg = self.load_config(cwd).message_generator
|
|
302
|
+
if cfg is None:
|
|
303
|
+
pass
|
|
304
|
+
elif isinstance(cfg, str):
|
|
305
|
+
name = cfg
|
|
306
|
+
elif isinstance(cfg, Cli.Config.MessageGenerator):
|
|
307
|
+
name = cfg.name
|
|
308
|
+
kw.update(cfg.config or {})
|
|
309
|
+
else:
|
|
310
|
+
raise TypeError(cfg)
|
|
311
|
+
|
|
312
|
+
if name is not None:
|
|
313
|
+
cls = load_message_generator_manifests_map()[name].load_cls()
|
|
314
|
+
|
|
315
|
+
return cls(**kw)
|
|
316
|
+
|
|
283
317
|
@ap.cmd(
|
|
284
318
|
ap.arg('-g', '--message-generator', nargs='?'),
|
|
285
319
|
ap.arg('dir', nargs='*'),
|
|
@@ -292,12 +326,10 @@ class Cli(ap.Cli):
|
|
|
292
326
|
if not (st.has_staged or st.has_dirty):
|
|
293
327
|
return
|
|
294
328
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
mg_cls = load_message_generator_manifests_map()[mg_name].load_cls()
|
|
300
|
-
mg = mg_cls()
|
|
329
|
+
mg = self._make_git_message_generator(
|
|
330
|
+
name=self.args.message_generator,
|
|
331
|
+
cwd=cwd,
|
|
332
|
+
)
|
|
301
333
|
|
|
302
334
|
mgr = mg.generate_commit_message(GitMessageGenerator.GenerateCommitMessageArgs(
|
|
303
335
|
cwd=cwd,
|
|
@@ -316,6 +348,7 @@ class Cli(ap.Cli):
|
|
|
316
348
|
ap.arg('-g', '--message-generator', nargs='?'),
|
|
317
349
|
ap.arg('--dry-run', action='store_true'),
|
|
318
350
|
ap.arg('-y', '--no-confirmation', action='store_true'),
|
|
351
|
+
ap.arg('-r', '--repository'),
|
|
319
352
|
ap.arg('dir', nargs='*'),
|
|
320
353
|
aliases=['acp'],
|
|
321
354
|
)
|
|
@@ -337,12 +370,10 @@ class Cli(ap.Cli):
|
|
|
337
370
|
msg = self.args.message
|
|
338
371
|
|
|
339
372
|
else:
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
mg_cls = load_message_generator_manifests_map()[mg_name].load_cls()
|
|
345
|
-
mg = mg_cls()
|
|
373
|
+
mg = self._make_git_message_generator(
|
|
374
|
+
name=self.args.message_generator,
|
|
375
|
+
cwd=cwd,
|
|
376
|
+
)
|
|
346
377
|
|
|
347
378
|
mgr = mg.generate_commit_message(GitMessageGenerator.GenerateCommitMessageArgs(
|
|
348
379
|
cwd=cwd,
|
|
@@ -355,7 +386,7 @@ class Cli(ap.Cli):
|
|
|
355
386
|
|
|
356
387
|
check_call('git', 'commit', '-m', msg)
|
|
357
388
|
|
|
358
|
-
check_call('git', 'push')
|
|
389
|
+
check_call('git', 'push', *([self.args.repository] if self.args.repository is not None else []))
|
|
359
390
|
|
|
360
391
|
if not self.args.dir:
|
|
361
392
|
run(None)
|
|
@@ -452,6 +483,7 @@ class Cli(ap.Cli):
|
|
|
452
483
|
BUILTIN_COMMIT_MESSAGES: ta.Mapping[str, str] = {
|
|
453
484
|
'tableflip': '(╯°□°)╯︵ ┻━┻',
|
|
454
485
|
'tableunflip': '┬─┬ノ(º _ ºノ)',
|
|
486
|
+
'shrug': r'¯\_(ツ)_/¯',
|
|
455
487
|
}
|
|
456
488
|
|
|
457
489
|
@ap.cmd(
|
|
@@ -501,10 +533,18 @@ class Cli(ap.Cli):
|
|
|
501
533
|
repo_dir = os.path.join(tmp_dir, repo_dir_name)
|
|
502
534
|
check.state(os.path.isdir(repo_dir))
|
|
503
535
|
|
|
536
|
+
#
|
|
537
|
+
|
|
504
538
|
git_dir = os.path.join(repo_dir, '.git')
|
|
505
539
|
check.state(os.path.isdir(git_dir))
|
|
506
540
|
shutil.rmtree(git_dir)
|
|
507
541
|
|
|
542
|
+
for f in glob.glob(os.path.join(repo_dir, '**/.gitattributes'), recursive=True):
|
|
543
|
+
if os.path.isfile(f):
|
|
544
|
+
os.unlink(f)
|
|
545
|
+
|
|
546
|
+
#
|
|
547
|
+
|
|
508
548
|
shutil.move(repo_dir, cwd)
|
|
509
549
|
|
|
510
550
|
out_dir = repo_dir_name
|
omdev/tools/git/messages.py
CHANGED
|
@@ -16,7 +16,7 @@ from . import consts
|
|
|
16
16
|
##
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
class GitMessageGenerator(
|
|
19
|
+
class GitMessageGenerator(lang.Abstract):
|
|
20
20
|
@dc.dataclass(frozen=True, kw_only=True)
|
|
21
21
|
class GenerateCommitMessageArgs:
|
|
22
22
|
cwd: str | None = None
|
|
@@ -44,7 +44,7 @@ class GitMessageGeneratorManifest(NameAliasesManifest, ModAttrManifest):
|
|
|
44
44
|
return check.issubclass(self.resolve(), GitMessageGenerator)
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
class StaticGitMessageGeneratorManifest(StaticModAttrManifest, GitMessageGeneratorManifest,
|
|
47
|
+
class StaticGitMessageGeneratorManifest(StaticModAttrManifest, GitMessageGeneratorManifest, lang.Abstract):
|
|
48
48
|
pass
|
|
49
49
|
|
|
50
50
|
|
omdev/tools/json/cli.py
CHANGED
|
@@ -195,7 +195,7 @@ def _main() -> None:
|
|
|
195
195
|
else:
|
|
196
196
|
in_file = es.enter_context(open(args.file, 'rb'))
|
|
197
197
|
|
|
198
|
-
def yield_input() -> ta.
|
|
198
|
+
def yield_input() -> ta.Iterator[bytes]:
|
|
199
199
|
fd = check.isinstance(in_file.fileno(), int)
|
|
200
200
|
|
|
201
201
|
while True:
|
|
@@ -240,7 +240,7 @@ def _main() -> None:
|
|
|
240
240
|
def flush_output(
|
|
241
241
|
fn: ta.Callable[[T], ta.Iterable[U]],
|
|
242
242
|
i: T,
|
|
243
|
-
) -> ta.
|
|
243
|
+
) -> ta.Iterator[U]:
|
|
244
244
|
n = 0
|
|
245
245
|
for o in fn(i):
|
|
246
246
|
yield o
|
|
@@ -259,7 +259,7 @@ def _main() -> None:
|
|
|
259
259
|
def append_newlines(
|
|
260
260
|
fn: ta.Callable[[T], ta.Iterable[str]],
|
|
261
261
|
i: T,
|
|
262
|
-
) -> ta.
|
|
262
|
+
) -> ta.Iterator[str]:
|
|
263
263
|
yield from fn(i)
|
|
264
264
|
yield '\n'
|
|
265
265
|
|
|
@@ -267,15 +267,15 @@ def _main() -> None:
|
|
|
267
267
|
pipeline = fp.bind(append_newlines, pipeline) # Any -> [str]
|
|
268
268
|
pipeline = fp.bind(lang.flatmap, pipeline) # [Any] -> [str]
|
|
269
269
|
pipeline = fp.pipe(fp.bind(lang.flatmap, processor.process), pipeline) # [Any] -> [str]
|
|
270
|
-
pipeline = fp.pipe(fp.bind(lang.flatmap, builder.build), pipeline) # [
|
|
270
|
+
pipeline = fp.pipe(fp.bind(lang.flatmap, builder.build), pipeline) # [Event] -> [str] # noqa
|
|
271
271
|
pipeline = fp.pipe(parser.parse, pipeline) # bytes -> [str]
|
|
272
272
|
|
|
273
273
|
else:
|
|
274
274
|
renderer = StreamRenderer(cfg.rendering)
|
|
275
275
|
trailing_newline = True
|
|
276
276
|
|
|
277
|
-
pipeline = renderer.render #
|
|
278
|
-
pipeline = fp.bind(lang.flatmap, pipeline) # [
|
|
277
|
+
pipeline = renderer.render # Event -> [str]
|
|
278
|
+
pipeline = fp.bind(lang.flatmap, pipeline) # [Event] -> [str]
|
|
279
279
|
pipeline = fp.pipe(parser.parse, pipeline) # bytes -> [str]
|
|
280
280
|
|
|
281
281
|
pipeline = fp.bind(flush_output, pipeline) # bytes -> [str]
|
omdev/tools/json/formats.py
CHANGED
omdev/tools/json/parsing.py
CHANGED
|
@@ -6,8 +6,8 @@ from omlish import check
|
|
|
6
6
|
from omlish import lang
|
|
7
7
|
from omlish.formats.json.stream.building import JsonValueBuilder
|
|
8
8
|
from omlish.formats.json.stream.lexing import JsonStreamLexer
|
|
9
|
+
from omlish.formats.json.stream.parsing import Event
|
|
9
10
|
from omlish.formats.json.stream.parsing import JsonStreamParser
|
|
10
|
-
from omlish.formats.json.stream.parsing import JsonStreamParserEvent
|
|
11
11
|
from omlish.io.buffers import DelimitingBuffer
|
|
12
12
|
|
|
13
13
|
from .formats import Format
|
|
@@ -22,7 +22,7 @@ class EagerParser:
|
|
|
22
22
|
|
|
23
23
|
self._fmt = fmt
|
|
24
24
|
|
|
25
|
-
def parse(self, f: ta.TextIO) -> ta.
|
|
25
|
+
def parse(self, f: ta.TextIO) -> ta.Iterator[ta.Any]:
|
|
26
26
|
return self._fmt.load(f)
|
|
27
27
|
|
|
28
28
|
|
|
@@ -42,7 +42,7 @@ class DelimitingParser:
|
|
|
42
42
|
|
|
43
43
|
self._db = DelimitingBuffer(delimiters)
|
|
44
44
|
|
|
45
|
-
def parse(self, b: bytes) -> ta.
|
|
45
|
+
def parse(self, b: bytes) -> ta.Iterator[ta.Any]:
|
|
46
46
|
for chunk in self._db.feed(b):
|
|
47
47
|
s = check.isinstance(chunk, bytes).decode('utf-8')
|
|
48
48
|
v = self._fmt.load(io.StringIO(s))
|
|
@@ -58,7 +58,7 @@ class StreamBuilder(lang.ExitStacked):
|
|
|
58
58
|
def _enter_contexts(self) -> None:
|
|
59
59
|
self._builder = self._enter_context(JsonValueBuilder())
|
|
60
60
|
|
|
61
|
-
def build(self, e:
|
|
61
|
+
def build(self, e: Event) -> ta.Iterator[ta.Any]:
|
|
62
62
|
yield from check.not_none(self._builder)(e)
|
|
63
63
|
|
|
64
64
|
|
|
@@ -72,7 +72,7 @@ class StreamParser(lang.ExitStacked):
|
|
|
72
72
|
self._lex = self._enter_context(JsonStreamLexer())
|
|
73
73
|
self._parse = self._enter_context(JsonStreamParser())
|
|
74
74
|
|
|
75
|
-
def parse(self, b: bytes) -> ta.
|
|
75
|
+
def parse(self, b: bytes) -> ta.Iterator[Event]:
|
|
76
76
|
for s in self._decoder.decode(b, not b):
|
|
77
77
|
for c in s:
|
|
78
78
|
for t in self._lex(c):
|
omdev/tools/json/processing.py
CHANGED
|
@@ -53,11 +53,14 @@ class Processor:
|
|
|
53
53
|
|
|
54
54
|
def _marshal(self, v: ta.Any) -> ta.Any:
|
|
55
55
|
return msh.MarshalContext(
|
|
56
|
-
msh.
|
|
57
|
-
|
|
56
|
+
configs=msh.global_config_registry(),
|
|
57
|
+
marshal_factory_context=msh.MarshalFactoryContext(
|
|
58
|
+
configs=msh.global_config_registry(),
|
|
59
|
+
marshaler_factory=self._marshaler_factory(),
|
|
60
|
+
),
|
|
58
61
|
).marshal(v)
|
|
59
62
|
|
|
60
|
-
def process(self, v: ta.Any) -> ta.
|
|
63
|
+
def process(self, v: ta.Any) -> ta.Iterator[ta.Any]:
|
|
61
64
|
if self._jmespath_expr is not None:
|
|
62
65
|
v = self._jmespath_expr.search(v)
|
|
63
66
|
|
omdev/tools/json/rendering.py
CHANGED
|
@@ -4,7 +4,7 @@ import typing as ta
|
|
|
4
4
|
|
|
5
5
|
from omlish import lang
|
|
6
6
|
from omlish.formats.json.rendering import JsonRenderer
|
|
7
|
-
from omlish.formats.json.stream.parsing import
|
|
7
|
+
from omlish.formats.json.stream.parsing import Event
|
|
8
8
|
from omlish.formats.json.stream.rendering import StreamJsonRenderer
|
|
9
9
|
from omlish.formats.json5.rendering import Json5Renderer
|
|
10
10
|
from omlish.term import codes as tc
|
|
@@ -101,5 +101,5 @@ class StreamRenderer(Renderer):
|
|
|
101
101
|
**self._kw,
|
|
102
102
|
)
|
|
103
103
|
|
|
104
|
-
def render(self, e:
|
|
104
|
+
def render(self, e: Event) -> ta.Iterator[str]:
|
|
105
105
|
return self._renderer.render((e,))
|
omdev/tools/jsonview/cli.py
CHANGED
|
@@ -3,7 +3,7 @@ TODO:
|
|
|
3
3
|
- read from stdin
|
|
4
4
|
- evaluate jmespath on server using extended engine
|
|
5
5
|
- integrate with json tool
|
|
6
|
-
- use omlish server
|
|
6
|
+
- use omlish server
|
|
7
7
|
- vendor deps, serve local
|
|
8
8
|
- update to https://github.com/josdejong/svelte-jsoneditor
|
|
9
9
|
"""
|
|
@@ -14,62 +14,33 @@ import os
|
|
|
14
14
|
import socketserver
|
|
15
15
|
import sys
|
|
16
16
|
import threading
|
|
17
|
+
import typing as ta
|
|
17
18
|
import webbrowser
|
|
18
19
|
|
|
20
|
+
from omlish import check
|
|
19
21
|
from omlish import lang
|
|
22
|
+
from omlish.sockets.ports import get_available_port
|
|
23
|
+
from omlish.text import minja
|
|
20
24
|
|
|
21
25
|
|
|
22
26
|
##
|
|
23
27
|
|
|
24
28
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
<meta charset="UTF-8">
|
|
31
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
32
|
-
<title>JSON Viewer</title>
|
|
33
|
-
<link
|
|
34
|
-
href="https://cdn.jsdelivr.net/npm/jsoneditor/dist/jsoneditor.min.css"
|
|
35
|
-
rel="stylesheet"
|
|
36
|
-
type="text/css"
|
|
37
|
-
>
|
|
38
|
-
<style>
|
|
39
|
-
{css_src}
|
|
40
|
-
</style>
|
|
41
|
-
</head>
|
|
42
|
-
|
|
43
|
-
<body>
|
|
44
|
-
<div class="input-area">
|
|
45
|
-
<label for="jmespath-input">JMESPath:</label>
|
|
46
|
-
<input
|
|
47
|
-
type="text"
|
|
48
|
-
id="jmespath-input"
|
|
49
|
-
list="jmespath-history"
|
|
50
|
-
placeholder="Enter JMESPath expression..."
|
|
51
|
-
autocomplete="off"
|
|
52
|
-
>
|
|
53
|
-
<datalist id="jmespath-history"></datalist>
|
|
54
|
-
<span id="error-message"></span>
|
|
55
|
-
</div>
|
|
56
|
-
<div id="jsoneditor"></div>
|
|
57
|
-
|
|
58
|
-
<script src="https://cdn.jsdelivr.net/npm/jsoneditor@10.2.0/dist/jsoneditor.min.js"></script>
|
|
59
|
-
<script src="https://cdn.jsdelivr.net/npm/jmespath@0.16.0/jmespath.min.js"></script>
|
|
60
|
-
<script>
|
|
61
|
-
const originalJsonData = {json_data};
|
|
62
|
-
</script>
|
|
63
|
-
<script>
|
|
64
|
-
{js_src}
|
|
65
|
-
</script>
|
|
66
|
-
</body>
|
|
67
|
-
|
|
68
|
-
</html>
|
|
69
|
-
"""
|
|
29
|
+
@lang.cached_function
|
|
30
|
+
def html_template() -> minja.MinjaTemplate:
|
|
31
|
+
src = lang.get_relative_resources('resources', globals=globals())['jsonview.html.j2'].read_text()
|
|
32
|
+
return minja.compile_minja_template(src, ['ctx'])
|
|
33
|
+
|
|
70
34
|
|
|
35
|
+
def view_json(
|
|
36
|
+
filepath: str,
|
|
37
|
+
port: int | None,
|
|
38
|
+
*,
|
|
39
|
+
mode: ta.Literal['jsonl', 'json5', 'json', None] = None,
|
|
40
|
+
) -> None:
|
|
41
|
+
if filepath == '-':
|
|
42
|
+
filepath = '/dev/stdin'
|
|
71
43
|
|
|
72
|
-
def view_json(filepath: str, port: int) -> None:
|
|
73
44
|
if not os.path.exists(filepath):
|
|
74
45
|
print(f"Error: File not found at '{filepath}'", file=sys.stderr)
|
|
75
46
|
return
|
|
@@ -81,13 +52,21 @@ def view_json(filepath: str, port: int) -> None:
|
|
|
81
52
|
print(f'Error: Invalid JSON file. {e}', file=sys.stderr)
|
|
82
53
|
return
|
|
83
54
|
|
|
84
|
-
if
|
|
55
|
+
if mode is None:
|
|
56
|
+
if filepath.endswith('.jsonl'):
|
|
57
|
+
mode = 'json'
|
|
58
|
+
elif filepath.endswith('.json5'):
|
|
59
|
+
mode = 'json5'
|
|
60
|
+
|
|
61
|
+
if mode == 'jsonl':
|
|
85
62
|
json_content = [json.loads(sl) for l in raw_content.splitlines() if (sl := l.strip())]
|
|
86
|
-
elif
|
|
63
|
+
elif mode == 'json5':
|
|
87
64
|
from omlish.formats import json5
|
|
88
65
|
json_content = json5.loads(raw_content)
|
|
89
|
-
|
|
66
|
+
elif mode in ('json', None):
|
|
90
67
|
json_content = json.loads(raw_content)
|
|
68
|
+
else:
|
|
69
|
+
raise ValueError(mode)
|
|
91
70
|
|
|
92
71
|
# Use compact dumps for embedding in JS, it's more efficient
|
|
93
72
|
json_string = json.dumps(json_content)
|
|
@@ -102,15 +81,18 @@ def view_json(filepath: str, port: int) -> None:
|
|
|
102
81
|
self.send_response(200)
|
|
103
82
|
self.send_header('Content-type', 'text/html')
|
|
104
83
|
self.end_headers()
|
|
105
|
-
html_content =
|
|
106
|
-
css_src=css_src,
|
|
107
|
-
js_src=js_src,
|
|
84
|
+
html_content = html_template()(ctx=dict(
|
|
85
|
+
css_src=css_src.strip(),
|
|
86
|
+
js_src=js_src.strip(),
|
|
108
87
|
json_data=json_string,
|
|
109
|
-
)
|
|
88
|
+
))
|
|
110
89
|
self.wfile.write(html_content.encode('utf-8'))
|
|
111
90
|
else:
|
|
112
91
|
super().do_GET()
|
|
113
92
|
|
|
93
|
+
if port is None:
|
|
94
|
+
port = get_available_port()
|
|
95
|
+
|
|
114
96
|
handler_cls = JsonViewerHttpRequestHandler
|
|
115
97
|
with socketserver.TCPServer(('127.0.0.1', port), handler_cls) as httpd:
|
|
116
98
|
url = f'http://127.0.0.1:{port}'
|
|
@@ -132,19 +114,21 @@ def _main() -> None:
|
|
|
132
114
|
description='Launch a web-based JSON viewer with JMESPath transformations.',
|
|
133
115
|
formatter_class=argparse.RawTextHelpFormatter,
|
|
134
116
|
)
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
)
|
|
139
|
-
parser.add_argument(
|
|
140
|
-
|
|
141
|
-
type=int,
|
|
142
|
-
default=(default_port := 8999),
|
|
143
|
-
help=f'The port to run the web server on. Defaults to {default_port}.',
|
|
144
|
-
)
|
|
117
|
+
|
|
118
|
+
parser.add_argument('filepath')
|
|
119
|
+
parser.add_argument('-p', '--port', type=int)
|
|
120
|
+
parser.add_argument('-l', '--lines', action='store_true')
|
|
121
|
+
parser.add_argument('-5', '--five', action='store_true')
|
|
122
|
+
|
|
145
123
|
args = parser.parse_args()
|
|
146
124
|
|
|
147
|
-
|
|
125
|
+
check.state(not (args.lines and args.five))
|
|
126
|
+
|
|
127
|
+
view_json(
|
|
128
|
+
args.filepath,
|
|
129
|
+
args.port,
|
|
130
|
+
mode='jsonl' if args.lines else 'json5' if args.five else None,
|
|
131
|
+
)
|
|
148
132
|
|
|
149
133
|
|
|
150
134
|
if __name__ == '__main__':
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title>JSON Viewer</title>
|
|
8
|
+
<link
|
|
9
|
+
href="https://cdn.jsdelivr.net/npm/jsoneditor/dist/jsoneditor.min.css"
|
|
10
|
+
rel="stylesheet"
|
|
11
|
+
type="text/css"
|
|
12
|
+
>
|
|
13
|
+
<style>
|
|
14
|
+
{{ ctx['css_src'] }}
|
|
15
|
+
</style>
|
|
16
|
+
</head>
|
|
17
|
+
|
|
18
|
+
<body>
|
|
19
|
+
<div class="input-area">
|
|
20
|
+
<label for="jmespath-input">JMESPath:</label>
|
|
21
|
+
<input
|
|
22
|
+
type="text"
|
|
23
|
+
id="jmespath-input"
|
|
24
|
+
list="jmespath-history"
|
|
25
|
+
placeholder="Enter JMESPath expression..."
|
|
26
|
+
autocomplete="off"
|
|
27
|
+
>
|
|
28
|
+
<datalist id="jmespath-history"></datalist>
|
|
29
|
+
<span id="error-message"></span>
|
|
30
|
+
</div>
|
|
31
|
+
<div id="jsoneditor"></div>
|
|
32
|
+
|
|
33
|
+
<script src="https://cdn.jsdelivr.net/npm/jsoneditor@10.2.0/dist/jsoneditor.min.js"></script>
|
|
34
|
+
<script src="https://cdn.jsdelivr.net/npm/jmespath@0.16.0/jmespath.min.js"></script>
|
|
35
|
+
<script>
|
|
36
|
+
const originalJsonData = {{ ctx['json_data'] }};
|
|
37
|
+
</script>
|
|
38
|
+
<script>
|
|
39
|
+
{{ ctx['js_src'] }}
|
|
40
|
+
</script>
|
|
41
|
+
</body>
|
|
42
|
+
|
|
43
|
+
</html>
|