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
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import dataclasses as dc
|
|
2
|
+
import os
|
|
3
|
+
import typing as ta
|
|
4
|
+
|
|
5
|
+
from omlish.text.filecache import TextFileCache
|
|
6
|
+
|
|
7
|
+
from .base import Precheck
|
|
8
|
+
from .base import PrecheckContext
|
|
9
|
+
from .caches import DirWalkCache
|
|
10
|
+
from .caches import HeadersCache
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
##
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BlankLinesPrecheck(Precheck['BlankLinesPrecheck.Config']):
|
|
17
|
+
@dc.dataclass(frozen=True)
|
|
18
|
+
class Config(Precheck.Config):
|
|
19
|
+
DEFAULT_FILE_EXTENSIONS: ta.ClassVar[ta.AbstractSet[str]] = frozenset([
|
|
20
|
+
'py',
|
|
21
|
+
|
|
22
|
+
'c',
|
|
23
|
+
'cc',
|
|
24
|
+
'cu',
|
|
25
|
+
'h',
|
|
26
|
+
'hh',
|
|
27
|
+
])
|
|
28
|
+
|
|
29
|
+
file_extensions: ta.AbstractSet[str] = DEFAULT_FILE_EXTENSIONS
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
context: PrecheckContext,
|
|
34
|
+
config: Config = Config(),
|
|
35
|
+
*,
|
|
36
|
+
dir_walk_cache: DirWalkCache,
|
|
37
|
+
text_file_cache: TextFileCache,
|
|
38
|
+
headers_cache: HeadersCache,
|
|
39
|
+
) -> None:
|
|
40
|
+
super().__init__(config)
|
|
41
|
+
|
|
42
|
+
self._context = context
|
|
43
|
+
|
|
44
|
+
self._dir_walk_cache = dir_walk_cache
|
|
45
|
+
self._text_file_cache = text_file_cache
|
|
46
|
+
self._headers_cache = headers_cache
|
|
47
|
+
|
|
48
|
+
async def _run_file(self, file: str) -> ta.AsyncGenerator[Precheck.Violation]:
|
|
49
|
+
src = self._text_file_cache.get_entry(file).text()
|
|
50
|
+
|
|
51
|
+
if src and not src.splitlines()[0]:
|
|
52
|
+
yield Precheck.Violation(self, f'source file {file} starts with blank line')
|
|
53
|
+
|
|
54
|
+
async def run(self) -> ta.AsyncGenerator[Precheck.Violation]:
|
|
55
|
+
files = [
|
|
56
|
+
os.path.join(e.root, f)
|
|
57
|
+
for src_root in self._context.src_roots
|
|
58
|
+
for e in self._dir_walk_cache.list_dir(src_root)
|
|
59
|
+
for f in e.files
|
|
60
|
+
if '.' in f
|
|
61
|
+
and any(f.endswith('.' + ext) for ext in self._config.file_extensions)
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
for file in sorted(files):
|
|
65
|
+
async for v in self._run_file(file):
|
|
66
|
+
yield v
|
omdev/precheck/caches.py
CHANGED
omdev/precheck/imports.py
CHANGED
|
@@ -3,6 +3,8 @@ import dataclasses as dc
|
|
|
3
3
|
import os.path
|
|
4
4
|
import typing as ta
|
|
5
5
|
|
|
6
|
+
from omlish.text.filecache import TextFileCache
|
|
7
|
+
|
|
6
8
|
from .base import Precheck
|
|
7
9
|
from .base import PrecheckContext
|
|
8
10
|
from .caches import AstCache
|
|
@@ -26,6 +28,7 @@ class RootRelativeImportPrecheck(Precheck['RootRelativeImportPrecheck.Config']):
|
|
|
26
28
|
dir_walk_cache: DirWalkCache,
|
|
27
29
|
headers_cache: HeadersCache,
|
|
28
30
|
ast_cache: AstCache,
|
|
31
|
+
text_file_cache: TextFileCache,
|
|
29
32
|
) -> None:
|
|
30
33
|
super().__init__(config)
|
|
31
34
|
|
|
@@ -34,6 +37,7 @@ class RootRelativeImportPrecheck(Precheck['RootRelativeImportPrecheck.Config']):
|
|
|
34
37
|
self._dir_walk_cache = dir_walk_cache
|
|
35
38
|
self._headers_cache = headers_cache
|
|
36
39
|
self._ast_cache = ast_cache
|
|
40
|
+
self._text_file_cache = text_file_cache
|
|
37
41
|
|
|
38
42
|
async def _run_py_file(self, py_file: str, src_root: str) -> ta.AsyncGenerator[Precheck.Violation]:
|
|
39
43
|
if isinstance(header_lines := self._headers_cache.get_file_headers(py_file), Exception):
|
|
@@ -41,11 +45,20 @@ class RootRelativeImportPrecheck(Precheck['RootRelativeImportPrecheck.Config']):
|
|
|
41
45
|
if any(hl.src.strip() == '# ruff: noqa' for hl in header_lines):
|
|
42
46
|
return
|
|
43
47
|
|
|
48
|
+
py_file_lines = self._text_file_cache.get_entry(py_file).lines()
|
|
49
|
+
|
|
44
50
|
py_file_ast = self._ast_cache.get_file_ast(py_file)
|
|
45
51
|
if not isinstance(py_file_ast, ast.Module):
|
|
46
52
|
return
|
|
47
53
|
|
|
48
|
-
for cur_node in py_file_ast
|
|
54
|
+
for cur_node in ast.walk(py_file_ast):
|
|
55
|
+
if not isinstance(cur_node, (ast.Import, ast.ImportFrom)):
|
|
56
|
+
continue
|
|
57
|
+
|
|
58
|
+
# FIXME: lame lol
|
|
59
|
+
if py_file_lines[cur_node.lineno - 1].strip().endswith('# noqa'):
|
|
60
|
+
continue
|
|
61
|
+
|
|
49
62
|
if isinstance(cur_node, ast.Import):
|
|
50
63
|
imp_alias: ast.alias
|
|
51
64
|
for imp_alias in cur_node.names:
|
omdev/precheck/lite.py
CHANGED
|
@@ -2,13 +2,13 @@ import asyncio
|
|
|
2
2
|
import dataclasses as dc
|
|
3
3
|
import glob
|
|
4
4
|
import inspect
|
|
5
|
-
import logging
|
|
6
5
|
import os.path
|
|
7
6
|
import subprocess
|
|
8
7
|
import textwrap
|
|
9
8
|
import typing as ta
|
|
10
9
|
|
|
11
10
|
from omlish import cached
|
|
11
|
+
from omlish.logs import all as logs
|
|
12
12
|
from omlish.subprocesses.wrap import subprocess_maybe_shell_wrap_exec
|
|
13
13
|
|
|
14
14
|
from .. import magic
|
|
@@ -16,7 +16,7 @@ from .base import Precheck
|
|
|
16
16
|
from .base import PrecheckContext
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
log =
|
|
19
|
+
log = logs.get_module_logger(globals())
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
##
|
omdev/precheck/main.py
CHANGED
|
@@ -20,9 +20,7 @@ TODO:
|
|
|
20
20
|
"""
|
|
21
21
|
import argparse
|
|
22
22
|
import asyncio
|
|
23
|
-
import logging
|
|
24
23
|
import os.path
|
|
25
|
-
import sys
|
|
26
24
|
import typing as ta
|
|
27
25
|
|
|
28
26
|
from omlish import inject as inj
|
|
@@ -30,6 +28,7 @@ from omlish.logs import all as logs
|
|
|
30
28
|
|
|
31
29
|
from .base import Precheck
|
|
32
30
|
from .base import PrecheckContext
|
|
31
|
+
from .blanklines import BlankLinesPrecheck
|
|
33
32
|
from .caches import AstCache
|
|
34
33
|
from .caches import DirWalkCache
|
|
35
34
|
from .caches import HeadersCache
|
|
@@ -43,7 +42,7 @@ from .scripts import ScriptDepsPrecheck
|
|
|
43
42
|
from .unicode import UnicodePrecheck
|
|
44
43
|
|
|
45
44
|
|
|
46
|
-
log =
|
|
45
|
+
log = logs.get_module_logger(globals())
|
|
47
46
|
|
|
48
47
|
|
|
49
48
|
##
|
|
@@ -88,6 +87,7 @@ def _check_cmd(args) -> None:
|
|
|
88
87
|
)
|
|
89
88
|
|
|
90
89
|
pc_cfgs: list[Precheck.Config] = [
|
|
90
|
+
BlankLinesPrecheck.Config(),
|
|
91
91
|
GitBlacklistPrecheck.Config(),
|
|
92
92
|
LitePython8Precheck.Config(),
|
|
93
93
|
ManifestsPrecheck.Config(),
|
|
@@ -125,7 +125,7 @@ def _check_cmd(args) -> None:
|
|
|
125
125
|
|
|
126
126
|
if vs:
|
|
127
127
|
print(f'{len(vs)} violations found')
|
|
128
|
-
|
|
128
|
+
raise SystemExit(1)
|
|
129
129
|
|
|
130
130
|
|
|
131
131
|
##
|
|
@@ -133,12 +133,12 @@ def _check_cmd(args) -> None:
|
|
|
133
133
|
|
|
134
134
|
def _build_parser() -> argparse.ArgumentParser:
|
|
135
135
|
parser = argparse.ArgumentParser()
|
|
136
|
+
parser.add_argument('-v', '--verbose', action='store_true')
|
|
136
137
|
|
|
137
138
|
subparsers = parser.add_subparsers()
|
|
138
139
|
|
|
139
140
|
parser_check = subparsers.add_parser('check')
|
|
140
141
|
parser_check.add_argument('roots', nargs='+')
|
|
141
|
-
parser_check.add_argument('-v', '--verbose', action='store_true')
|
|
142
142
|
parser_check.set_defaults(func=_check_cmd)
|
|
143
143
|
|
|
144
144
|
return parser
|
omdev/precheck/unicode.py
CHANGED
|
@@ -29,6 +29,18 @@ class UnicodePrecheck(Precheck['UnicodePrecheck.Config']):
|
|
|
29
29
|
|
|
30
30
|
permitted_categories: ta.AbstractSet[str] = DEFAULT_PERMITTED_CATEGORIES
|
|
31
31
|
|
|
32
|
+
DEFAULT_FILE_EXTENSIONS: ta.ClassVar[ta.AbstractSet[str]] = frozenset([
|
|
33
|
+
'py',
|
|
34
|
+
|
|
35
|
+
'c',
|
|
36
|
+
'cc',
|
|
37
|
+
'cu',
|
|
38
|
+
'h',
|
|
39
|
+
'hh',
|
|
40
|
+
])
|
|
41
|
+
|
|
42
|
+
file_extensions: ta.AbstractSet[str] = DEFAULT_FILE_EXTENSIONS
|
|
43
|
+
|
|
32
44
|
def __init__(
|
|
33
45
|
self,
|
|
34
46
|
context: PrecheckContext,
|
|
@@ -46,19 +58,15 @@ class UnicodePrecheck(Precheck['UnicodePrecheck.Config']):
|
|
|
46
58
|
self._text_file_cache = text_file_cache
|
|
47
59
|
self._headers_cache = headers_cache
|
|
48
60
|
|
|
49
|
-
async def
|
|
50
|
-
if
|
|
51
|
-
|
|
52
|
-
if any(hl.src.strip() == '# @omlish-precheck-allow-any-unicode' for hl in header_lines):
|
|
53
|
-
return
|
|
54
|
-
|
|
55
|
-
src = self._text_file_cache.get_entry(py_file).text()
|
|
61
|
+
async def _run_file(self, file: str, src: str | None = None) -> ta.AsyncGenerator[Precheck.Violation]:
|
|
62
|
+
if src is None:
|
|
63
|
+
src = self._text_file_cache.get_entry(file).text()
|
|
56
64
|
|
|
57
65
|
illegal_chars = {
|
|
58
66
|
ch
|
|
59
67
|
for ch in src
|
|
60
|
-
if ord(ch) > 255
|
|
61
|
-
unicodedata.category(ch) not in self._config.permitted_categories
|
|
68
|
+
if ord(ch) > 255
|
|
69
|
+
and unicodedata.category(ch) not in self._config.permitted_categories
|
|
62
70
|
}
|
|
63
71
|
|
|
64
72
|
if illegal_chars:
|
|
@@ -66,16 +74,32 @@ class UnicodePrecheck(Precheck['UnicodePrecheck.Config']):
|
|
|
66
74
|
f'({ch!r}, {unicodedata.category(ch)})'
|
|
67
75
|
for ch in sorted(illegal_chars)
|
|
68
76
|
]
|
|
69
|
-
yield Precheck.Violation(self, f'source file {
|
|
77
|
+
yield Precheck.Violation(self, f'source file {file} has illegal unicode characters: {", ".join(sl)}')
|
|
78
|
+
|
|
79
|
+
async def _run_py_file(self, py_file: str) -> ta.AsyncGenerator[Precheck.Violation]:
|
|
80
|
+
if isinstance(header_lines := self._headers_cache.get_file_headers(py_file), Exception):
|
|
81
|
+
return
|
|
82
|
+
if any(hl.src.strip() == '# @omlish-precheck-allow-any-unicode' for hl in header_lines):
|
|
83
|
+
return
|
|
84
|
+
|
|
85
|
+
async for v in self._run_file(py_file):
|
|
86
|
+
yield v
|
|
70
87
|
|
|
71
88
|
async def run(self) -> ta.AsyncGenerator[Precheck.Violation]:
|
|
72
|
-
|
|
89
|
+
files = [
|
|
73
90
|
os.path.join(e.root, f)
|
|
74
91
|
for src_root in self._context.src_roots
|
|
75
92
|
for e in self._dir_walk_cache.list_dir(src_root)
|
|
76
93
|
for f in e.files
|
|
77
|
-
if
|
|
94
|
+
if '.' in f
|
|
95
|
+
and any(f.endswith('.' + ext) for ext in self._config.file_extensions)
|
|
78
96
|
]
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
97
|
+
|
|
98
|
+
for file in sorted(files):
|
|
99
|
+
if file.endswith('.py'):
|
|
100
|
+
async for v in self._run_py_file(file):
|
|
101
|
+
yield v
|
|
102
|
+
|
|
103
|
+
else:
|
|
104
|
+
async for v in self._run_file(file):
|
|
105
|
+
yield v
|
|
File without changes
|
omdev/py/asts/parents.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
|
|
3
|
+
from omlish import check
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
##
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class _ParentsNodeVisitor(ast.NodeVisitor):
|
|
10
|
+
def __init__(self) -> None:
|
|
11
|
+
super().__init__()
|
|
12
|
+
|
|
13
|
+
self.parents: dict[ast.AST, ast.AST | None] = {}
|
|
14
|
+
|
|
15
|
+
parent: ast.AST | None = None
|
|
16
|
+
|
|
17
|
+
def generic_visit(self, node: ast.AST) -> None:
|
|
18
|
+
check.not_in(node, self.parents)
|
|
19
|
+
prev_parent = self.parents[node] = self.parent
|
|
20
|
+
self.parent = node
|
|
21
|
+
super().generic_visit(node)
|
|
22
|
+
self.parent = prev_parent
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_node_parents(node: ast.AST) -> dict[ast.AST, ast.AST | None]:
|
|
26
|
+
visitor = _ParentsNodeVisitor()
|
|
27
|
+
visitor.visit(node)
|
|
28
|
+
return visitor.parents
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Quite, quite lame, but sufficient for immediate needs. Accurate static symbol resolution is in development but can't
|
|
3
|
+
block other work.
|
|
4
|
+
"""
|
|
5
|
+
import ast
|
|
6
|
+
import dataclasses as dc
|
|
7
|
+
import typing as ta
|
|
8
|
+
|
|
9
|
+
from omlish import check
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
##
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dc.dataclass(frozen=True, kw_only=True)
|
|
16
|
+
class TopLevelImport:
|
|
17
|
+
spec: str
|
|
18
|
+
name: str
|
|
19
|
+
node: ast.Import | ast.ImportFrom
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dc.dataclass(frozen=True, kw_only=True)
|
|
23
|
+
class TopLevelCall:
|
|
24
|
+
node: ast.Call
|
|
25
|
+
imp: TopLevelImport
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dc.dataclass(frozen=True, kw_only=True)
|
|
29
|
+
class TopLevelFindings:
|
|
30
|
+
imports: ta.Mapping[str, TopLevelImport]
|
|
31
|
+
calls: ta.Sequence[TopLevelCall]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
##
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class _TopLevelModuleVisitor(ast.NodeVisitor):
|
|
38
|
+
def __init__(self, module_name: str) -> None:
|
|
39
|
+
super().__init__()
|
|
40
|
+
|
|
41
|
+
self.module_name = module_name
|
|
42
|
+
self.module_name_parts = module_name.split('.')
|
|
43
|
+
|
|
44
|
+
self.imports: dict[str, TopLevelImport] = {}
|
|
45
|
+
self.calls: list[TopLevelCall] = []
|
|
46
|
+
|
|
47
|
+
#
|
|
48
|
+
|
|
49
|
+
def visit_Import(self, node: ast.Import) -> None:
|
|
50
|
+
for alias in node.names:
|
|
51
|
+
name = alias.asname if alias.asname else alias.name
|
|
52
|
+
self.imports[name] = TopLevelImport(
|
|
53
|
+
spec=alias.name,
|
|
54
|
+
name=name,
|
|
55
|
+
node=node,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
self.generic_visit(node)
|
|
59
|
+
|
|
60
|
+
def visit_ImportFrom(self, node: ast.ImportFrom) -> None:
|
|
61
|
+
if node.level:
|
|
62
|
+
check.state(node.level < len(self.module_name_parts))
|
|
63
|
+
module = '.'.join([
|
|
64
|
+
*self.module_name_parts[:-node.level],
|
|
65
|
+
*([node.module] if node.module else []),
|
|
66
|
+
])
|
|
67
|
+
else:
|
|
68
|
+
module = check.not_none(node.module)
|
|
69
|
+
|
|
70
|
+
for alias in node.names:
|
|
71
|
+
name = alias.asname if alias.asname else alias.name
|
|
72
|
+
self.imports[name] = TopLevelImport(
|
|
73
|
+
spec='.'.join([module, alias.name]),
|
|
74
|
+
name=name,
|
|
75
|
+
node=node,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
self.generic_visit(node)
|
|
79
|
+
|
|
80
|
+
#
|
|
81
|
+
|
|
82
|
+
def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef) -> None:
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
def visit_ClassDef(self, node: ast.ClassDef) -> None:
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
def visit_FunctionDef(self, node: ast.FunctionDef) -> None:
|
|
89
|
+
pass
|
|
90
|
+
|
|
91
|
+
#
|
|
92
|
+
|
|
93
|
+
def handle_call(self, node: ast.Call) -> None:
|
|
94
|
+
if not (
|
|
95
|
+
isinstance(attr := node.func, ast.Attribute) and
|
|
96
|
+
isinstance(attr.ctx, ast.Load) and
|
|
97
|
+
isinstance(name := attr.value, ast.Name) and
|
|
98
|
+
isinstance(name.ctx, ast.Load)
|
|
99
|
+
):
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
if (imp := self.imports.get(name.id)) is None:
|
|
103
|
+
return
|
|
104
|
+
|
|
105
|
+
self.calls.append(TopLevelCall(
|
|
106
|
+
node=node,
|
|
107
|
+
imp=imp,
|
|
108
|
+
))
|
|
109
|
+
|
|
110
|
+
def visit_Call(self, node: ast.Call) -> None:
|
|
111
|
+
self.handle_call(node)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def analyze_module_top_level(
|
|
115
|
+
module: ast.Module,
|
|
116
|
+
module_name: str,
|
|
117
|
+
) -> TopLevelFindings:
|
|
118
|
+
visitor = _TopLevelModuleVisitor(module_name)
|
|
119
|
+
visitor.visit(module)
|
|
120
|
+
return TopLevelFindings(
|
|
121
|
+
imports=visitor.imports,
|
|
122
|
+
calls=visitor.calls,
|
|
123
|
+
)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
|
|
3
|
+
from omlish import check
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
##
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class StackNodeVisitor(ast.NodeVisitor):
|
|
10
|
+
def __init__(self) -> None:
|
|
11
|
+
super().__init__()
|
|
12
|
+
|
|
13
|
+
self.node_stack: list[ast.AST] = []
|
|
14
|
+
|
|
15
|
+
def generic_visit(self, node: ast.AST) -> None:
|
|
16
|
+
self.node_stack.append(node)
|
|
17
|
+
super().generic_visit(node)
|
|
18
|
+
check.is_(self.node_stack.pop(), node)
|
omdev/py/attrdocs.py
CHANGED
|
@@ -12,7 +12,7 @@ import typing as ta
|
|
|
12
12
|
from omlish import check
|
|
13
13
|
from omlish import lang
|
|
14
14
|
|
|
15
|
-
from
|
|
15
|
+
from .tokens import all as tks
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
##
|
|
@@ -24,12 +24,11 @@ class AttrDoc:
|
|
|
24
24
|
trailing_comment: str | None = None
|
|
25
25
|
preceding_comment: str | None = None
|
|
26
26
|
|
|
27
|
-
__repr__ = lang.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
)
|
|
27
|
+
__repr__ = lang.attr_ops(lambda o: (
|
|
28
|
+
o.docstring,
|
|
29
|
+
o.trailing_comment,
|
|
30
|
+
o.preceding_comment,
|
|
31
|
+
), repr_filter=bool).repr
|
|
33
32
|
|
|
34
33
|
|
|
35
34
|
_EMPTY_ATTR_DOC = AttrDoc()
|
omdev/py/bracepy.py
CHANGED
|
@@ -101,18 +101,26 @@ _CLI_MODULE = {'!.cli.types.CliModule': {
|
|
|
101
101
|
if __name__ == '__main__':
|
|
102
102
|
def _main(argv=None) -> None:
|
|
103
103
|
import argparse
|
|
104
|
+
import sys
|
|
104
105
|
|
|
105
106
|
parser = argparse.ArgumentParser()
|
|
106
107
|
parser.add_argument('-x', '--exec', action='store_true')
|
|
107
|
-
parser.add_argument('
|
|
108
|
+
parser.add_argument('-i', '--indent', type=int)
|
|
109
|
+
parser.add_argument('code', nargs='?')
|
|
108
110
|
|
|
109
111
|
args = parser.parse_args(argv)
|
|
110
112
|
|
|
111
|
-
|
|
113
|
+
if (code := args.code) is None:
|
|
114
|
+
code = sys.stdin.read()
|
|
115
|
+
|
|
116
|
+
out = translate_brace_python(
|
|
117
|
+
code,
|
|
118
|
+
**(dict(indent_width=args.indent) if args.indent is not None else {}),
|
|
119
|
+
)
|
|
112
120
|
|
|
113
121
|
if args.exec:
|
|
114
|
-
exec(
|
|
122
|
+
exec(out)
|
|
115
123
|
else:
|
|
116
|
-
print(
|
|
124
|
+
print(out)
|
|
117
125
|
|
|
118
126
|
_main()
|
omdev/py/docstrings/numpydoc.py
CHANGED
|
@@ -74,7 +74,7 @@ class Section:
|
|
|
74
74
|
dashes = '-' * len(self.title)
|
|
75
75
|
return rf'^({self.title})\s*?\n{dashes}\s*$'
|
|
76
76
|
|
|
77
|
-
def parse(self, text: str) -> ta.
|
|
77
|
+
def parse(self, text: str) -> ta.Iterator[DocstringMeta]:
|
|
78
78
|
"""
|
|
79
79
|
Parse ``DocstringMeta`` objects from the body of this section.
|
|
80
80
|
|
|
@@ -100,7 +100,7 @@ class _KVSection(Section, abc.ABC):
|
|
|
100
100
|
def _parse_item(self, key: str, value: str) -> DocstringMeta:
|
|
101
101
|
raise NotImplementedError
|
|
102
102
|
|
|
103
|
-
def parse(self, text: str) -> ta.
|
|
103
|
+
def parse(self, text: str) -> ta.Iterator[DocstringMeta]:
|
|
104
104
|
for match, next_match in _pairwise(KV_PAT.finditer(text)):
|
|
105
105
|
start = match.end()
|
|
106
106
|
end = next_match.start() if next_match is not None else None
|
|
@@ -227,7 +227,7 @@ class YieldsSection(ReturnsSection):
|
|
|
227
227
|
class DeprecationSection(_SphinxSection):
|
|
228
228
|
"""Parser for numpydoc "deprecation warning" sections."""
|
|
229
229
|
|
|
230
|
-
def parse(self, text: str) -> ta.
|
|
230
|
+
def parse(self, text: str) -> ta.Iterator[DocstringDeprecated]:
|
|
231
231
|
version, desc, *_ = [*text.split(sep='\n', maxsplit=1), None, None]
|
|
232
232
|
|
|
233
233
|
if desc is not None:
|
|
@@ -254,7 +254,7 @@ class ExamplesSection(Section):
|
|
|
254
254
|
[ 6586976, 22740995]])
|
|
255
255
|
"""
|
|
256
256
|
|
|
257
|
-
def parse(self, text: str) -> ta.
|
|
257
|
+
def parse(self, text: str) -> ta.Iterator[DocstringMeta]:
|
|
258
258
|
"""
|
|
259
259
|
Parse ``DocstringExample`` objects from the body of this section.
|
|
260
260
|
|
omdev/py/reprs.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
def textwrap_repr(text: str, width: int = 120, quote_char: str = '"') -> list[str]:
|
|
2
|
+
escaped_quote = '\\' + quote_char
|
|
3
|
+
content_width = width - 2
|
|
4
|
+
|
|
5
|
+
lines: list[str] = []
|
|
6
|
+
current_line: list[str] = []
|
|
7
|
+
current_length = 0
|
|
8
|
+
|
|
9
|
+
for char in text:
|
|
10
|
+
if char == quote_char:
|
|
11
|
+
safe_char = escaped_quote
|
|
12
|
+
elif char == '\\':
|
|
13
|
+
safe_char = '\\\\'
|
|
14
|
+
elif not char.isprintable():
|
|
15
|
+
safe_char = repr(char)[1:-1]
|
|
16
|
+
if quote_char in safe_char:
|
|
17
|
+
safe_char = safe_char.replace(quote_char, escaped_quote)
|
|
18
|
+
else:
|
|
19
|
+
safe_char = char
|
|
20
|
+
|
|
21
|
+
if current_length + len(safe_char) > content_width:
|
|
22
|
+
lines.append(f"{quote_char}{''.join(current_line)}{quote_char}")
|
|
23
|
+
current_line = []
|
|
24
|
+
current_length = 0
|
|
25
|
+
|
|
26
|
+
current_line.append(safe_char)
|
|
27
|
+
current_length += len(safe_char)
|
|
28
|
+
|
|
29
|
+
if current_line:
|
|
30
|
+
lines.append(f"{quote_char}{''.join(current_line)}{quote_char}")
|
|
31
|
+
|
|
32
|
+
return lines
|
omdev/py/scripts/execstat.py
CHANGED
|
@@ -17,13 +17,14 @@ import typing as ta
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def _run(
|
|
20
|
+
report: 'ta.Callable[[dict], None]',
|
|
20
21
|
src: str,
|
|
21
22
|
*,
|
|
22
23
|
setup: 'ta.Optional[str]' = None,
|
|
23
24
|
time: bool = False,
|
|
24
25
|
rss: bool = False,
|
|
25
26
|
modules: bool = False,
|
|
26
|
-
) ->
|
|
27
|
+
) -> None:
|
|
27
28
|
if rss:
|
|
28
29
|
import resource # noqa
|
|
29
30
|
|
|
@@ -61,26 +62,28 @@ def _run(
|
|
|
61
62
|
|
|
62
63
|
#
|
|
63
64
|
|
|
64
|
-
|
|
65
|
+
try:
|
|
66
|
+
exec(code, globals(), ns)
|
|
65
67
|
|
|
66
|
-
|
|
68
|
+
finally:
|
|
69
|
+
#
|
|
67
70
|
|
|
68
|
-
|
|
69
|
-
|
|
71
|
+
if time:
|
|
72
|
+
end_time = get_time()
|
|
70
73
|
|
|
71
|
-
|
|
72
|
-
|
|
74
|
+
if rss:
|
|
75
|
+
end_rss = get_rss()
|
|
73
76
|
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
if modules:
|
|
78
|
+
end_modules = get_modules()
|
|
76
79
|
|
|
77
|
-
|
|
80
|
+
#
|
|
78
81
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
report({
|
|
83
|
+
**({'time': (end_time - start_time)} if time else {}), # noqa
|
|
84
|
+
**({'rss': (end_rss - start_rss)} if rss else {}), # noqa
|
|
85
|
+
**({'modules': [m for m in end_modules if m not in start_modules]} if modules else {}), # noqa
|
|
86
|
+
})
|
|
84
87
|
|
|
85
88
|
|
|
86
89
|
#
|
|
@@ -145,13 +148,10 @@ def _main() -> None:
|
|
|
145
148
|
|
|
146
149
|
payload = '\n'.join([
|
|
147
150
|
inspect.getsource(_run),
|
|
148
|
-
f'
|
|
149
|
-
f'import json',
|
|
150
|
-
f'with open({out_file!r}, "w") as f:', # noqa
|
|
151
|
-
f' f.write(json.dumps(dct))',
|
|
151
|
+
f'with open({out_file!r}, "w") as f: _run(lambda dct: f.write(__import__("json").dumps(dct)), **{run_kw!r})', # noqa
|
|
152
152
|
])
|
|
153
153
|
|
|
154
|
-
subprocess.
|
|
154
|
+
subprocess.call([exe, '-c', payload])
|
|
155
155
|
|
|
156
156
|
with open(out_file) as f:
|
|
157
157
|
result = json.load(f)
|
|
@@ -185,14 +185,19 @@ def _main() -> None:
|
|
|
185
185
|
'rss_s': f'{rss:_}',
|
|
186
186
|
})
|
|
187
187
|
|
|
188
|
-
if args.modules:
|
|
188
|
+
if args.modules or args.modules_ordered:
|
|
189
|
+
mods = results[0]['modules']
|
|
189
190
|
out.update({
|
|
190
|
-
'
|
|
191
|
-
})
|
|
192
|
-
if args.modules_ordered:
|
|
193
|
-
out.update({
|
|
194
|
-
'modules_ordered': results[0]['modules'],
|
|
191
|
+
'num_modules': len(mods),
|
|
195
192
|
})
|
|
193
|
+
if args.modules:
|
|
194
|
+
out.update({
|
|
195
|
+
'modules': sorted(mods),
|
|
196
|
+
})
|
|
197
|
+
if args.modules_ordered:
|
|
198
|
+
out.update({
|
|
199
|
+
'modules_ordered': mods,
|
|
200
|
+
})
|
|
196
201
|
|
|
197
202
|
print(json.dumps(out, indent=2))
|
|
198
203
|
|