omlish 0.0.0.dev133__py3-none-any.whl → 0.0.0.dev177__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 +265 -7
- omlish/__about__.py +5 -3
- omlish/antlr/_runtime/__init__.py +0 -22
- omlish/antlr/_runtime/_all.py +24 -0
- omlish/antlr/_runtime/atn/ParserATNSimulator.py +1 -1
- omlish/antlr/_runtime/dfa/DFASerializer.py +1 -1
- omlish/antlr/_runtime/error/DiagnosticErrorListener.py +2 -1
- omlish/antlr/_runtime/xpath/XPath.py +7 -1
- omlish/antlr/_runtime/xpath/XPathLexer.py +1 -1
- omlish/antlr/delimit.py +106 -0
- omlish/antlr/dot.py +31 -0
- omlish/antlr/errors.py +11 -0
- omlish/antlr/input.py +96 -0
- omlish/antlr/parsing.py +19 -0
- omlish/antlr/runtime.py +102 -0
- omlish/antlr/utils.py +38 -0
- omlish/argparse/all.py +45 -0
- omlish/{argparse.py → argparse/cli.py} +112 -107
- omlish/asyncs/__init__.py +0 -35
- omlish/asyncs/all.py +35 -0
- omlish/asyncs/asyncio/all.py +7 -0
- omlish/asyncs/asyncio/channels.py +40 -0
- omlish/asyncs/asyncio/streams.py +45 -0
- omlish/asyncs/asyncio/subprocesses.py +238 -0
- omlish/asyncs/asyncio/timeouts.py +16 -0
- omlish/asyncs/bluelet/LICENSE +6 -0
- omlish/asyncs/bluelet/all.py +67 -0
- omlish/asyncs/bluelet/api.py +23 -0
- omlish/asyncs/bluelet/core.py +178 -0
- omlish/asyncs/bluelet/events.py +78 -0
- omlish/asyncs/bluelet/files.py +80 -0
- omlish/asyncs/bluelet/runner.py +416 -0
- omlish/asyncs/bluelet/sockets.py +214 -0
- omlish/bootstrap/sys.py +3 -3
- omlish/cached.py +2 -2
- omlish/check.py +49 -460
- omlish/codecs/__init__.py +72 -0
- omlish/codecs/base.py +106 -0
- omlish/codecs/bytes.py +119 -0
- omlish/codecs/chain.py +23 -0
- omlish/codecs/funcs.py +39 -0
- omlish/codecs/registry.py +139 -0
- omlish/codecs/standard.py +4 -0
- omlish/codecs/text.py +217 -0
- omlish/collections/cache/impl.py +50 -57
- omlish/collections/coerce.py +1 -0
- omlish/collections/mappings.py +1 -1
- omlish/configs/flattening.py +1 -1
- omlish/defs.py +1 -1
- omlish/diag/_pycharm/runhack.py +8 -2
- omlish/diag/procfs.py +8 -8
- omlish/docker/__init__.py +0 -36
- omlish/docker/all.py +31 -0
- omlish/docker/consts.py +4 -0
- omlish/{lite/docker.py → docker/detect.py} +18 -0
- omlish/docker/{helpers.py → timebomb.py} +0 -21
- omlish/formats/cbor.py +31 -0
- omlish/formats/cloudpickle.py +31 -0
- omlish/formats/codecs.py +93 -0
- omlish/formats/json/codecs.py +29 -0
- omlish/formats/json/delimted.py +4 -0
- omlish/formats/json/stream/errors.py +2 -0
- omlish/formats/json/stream/lex.py +12 -6
- omlish/formats/json/stream/parse.py +38 -22
- omlish/formats/json5.py +31 -0
- omlish/formats/pickle.py +31 -0
- omlish/formats/repr.py +25 -0
- omlish/formats/toml.py +17 -0
- omlish/formats/yaml.py +25 -0
- omlish/funcs/__init__.py +0 -0
- omlish/{genmachine.py → funcs/genmachine.py} +5 -4
- omlish/{matchfns.py → funcs/match.py} +1 -1
- omlish/funcs/pairs.py +215 -0
- omlish/http/__init__.py +0 -48
- omlish/http/all.py +48 -0
- omlish/http/coro/__init__.py +0 -0
- omlish/{lite/fdio/corohttp.py → http/coro/fdio.py} +21 -19
- omlish/{lite/http/coroserver.py → http/coro/server.py} +20 -21
- omlish/{lite/http → http}/handlers.py +3 -2
- omlish/{lite/http → http}/parsing.py +1 -0
- omlish/http/sessions.py +1 -1
- omlish/{lite/http → http}/versions.py +1 -0
- omlish/inject/managed.py +2 -2
- omlish/io/__init__.py +0 -3
- omlish/{lite/io.py → io/buffers.py} +8 -9
- omlish/io/compress/__init__.py +9 -0
- omlish/io/compress/abc.py +104 -0
- omlish/io/compress/adapters.py +148 -0
- omlish/io/compress/base.py +24 -0
- omlish/io/compress/brotli.py +47 -0
- omlish/io/compress/bz2.py +61 -0
- omlish/io/compress/codecs.py +78 -0
- omlish/io/compress/gzip.py +350 -0
- omlish/io/compress/lz4.py +91 -0
- omlish/io/compress/lzma.py +81 -0
- omlish/io/compress/snappy.py +34 -0
- omlish/io/compress/zlib.py +74 -0
- omlish/io/compress/zstd.py +44 -0
- omlish/io/fdio/__init__.py +1 -0
- omlish/{lite → io}/fdio/handlers.py +5 -5
- omlish/{lite → io}/fdio/kqueue.py +8 -8
- omlish/{lite → io}/fdio/manager.py +7 -7
- omlish/{lite → io}/fdio/pollers.py +13 -13
- omlish/io/generators/__init__.py +56 -0
- omlish/io/generators/consts.py +1 -0
- omlish/io/generators/direct.py +13 -0
- omlish/io/generators/readers.py +189 -0
- omlish/io/generators/stepped.py +191 -0
- omlish/io/pyio.py +5 -2
- omlish/iterators/__init__.py +24 -0
- omlish/iterators/iterators.py +132 -0
- omlish/iterators/recipes.py +18 -0
- omlish/iterators/tools.py +96 -0
- omlish/iterators/unique.py +67 -0
- omlish/lang/__init__.py +13 -1
- omlish/lang/functions.py +11 -2
- omlish/lang/generators.py +243 -0
- omlish/lang/iterables.py +46 -49
- omlish/lang/maybes.py +4 -4
- omlish/lite/cached.py +39 -6
- omlish/lite/check.py +438 -75
- omlish/lite/contextmanagers.py +17 -4
- omlish/lite/dataclasses.py +42 -0
- omlish/lite/inject.py +28 -45
- omlish/lite/logs.py +0 -270
- omlish/lite/marshal.py +309 -144
- omlish/lite/pycharm.py +47 -0
- omlish/lite/reflect.py +33 -0
- omlish/lite/resources.py +8 -0
- omlish/lite/runtime.py +4 -4
- omlish/lite/shlex.py +12 -0
- omlish/lite/socketserver.py +2 -2
- omlish/lite/strings.py +31 -0
- omlish/logs/__init__.py +0 -32
- omlish/logs/{_abc.py → abc.py} +0 -1
- omlish/logs/all.py +37 -0
- omlish/logs/{formatters.py → color.py} +1 -2
- omlish/logs/configs.py +7 -38
- omlish/logs/filters.py +10 -0
- omlish/logs/handlers.py +4 -1
- omlish/logs/json.py +56 -0
- omlish/logs/proxy.py +99 -0
- omlish/logs/standard.py +128 -0
- omlish/logs/utils.py +2 -2
- omlish/manifests/__init__.py +2 -0
- omlish/manifests/load.py +209 -0
- omlish/manifests/types.py +17 -0
- omlish/marshal/base.py +1 -1
- omlish/marshal/factories.py +1 -1
- omlish/marshal/forbidden.py +1 -1
- omlish/marshal/iterables.py +1 -1
- omlish/marshal/literals.py +50 -0
- omlish/marshal/mappings.py +1 -1
- omlish/marshal/maybes.py +1 -1
- omlish/marshal/standard.py +5 -1
- omlish/marshal/unions.py +1 -1
- omlish/os/__init__.py +0 -0
- omlish/os/atomics.py +205 -0
- omlish/os/deathsig.py +23 -0
- omlish/{os.py → os/files.py} +0 -9
- omlish/{lite → os}/journald.py +2 -1
- omlish/os/linux.py +484 -0
- omlish/os/paths.py +36 -0
- omlish/{lite → os}/pidfile.py +1 -0
- omlish/os/sizes.py +9 -0
- omlish/reflect/__init__.py +3 -0
- omlish/reflect/subst.py +2 -1
- omlish/reflect/types.py +126 -44
- omlish/secrets/pwhash.py +1 -1
- omlish/secrets/subprocesses.py +3 -1
- omlish/specs/jsonrpc/marshal.py +1 -1
- omlish/specs/openapi/marshal.py +1 -1
- omlish/sql/alchemy/asyncs.py +1 -1
- omlish/sql/queries/__init__.py +9 -1
- omlish/sql/queries/building.py +3 -0
- omlish/sql/queries/exprs.py +10 -27
- omlish/sql/queries/idents.py +48 -10
- omlish/sql/queries/names.py +80 -13
- omlish/sql/queries/params.py +64 -0
- omlish/sql/queries/rendering.py +1 -1
- omlish/subprocesses.py +340 -0
- omlish/term.py +29 -14
- omlish/testing/pytest/marks.py +2 -2
- omlish/testing/pytest/plugins/asyncs.py +6 -1
- omlish/testing/pytest/plugins/logging.py +1 -1
- omlish/testing/pytest/plugins/switches.py +1 -1
- {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/METADATA +7 -5
- {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/RECORD +200 -117
- omlish/fnpairs.py +0 -496
- omlish/formats/json/cli/__main__.py +0 -11
- omlish/formats/json/cli/cli.py +0 -298
- omlish/formats/json/cli/formats.py +0 -71
- omlish/formats/json/cli/io.py +0 -74
- omlish/formats/json/cli/parsing.py +0 -82
- omlish/formats/json/cli/processing.py +0 -48
- omlish/formats/json/cli/rendering.py +0 -92
- omlish/iterators.py +0 -300
- omlish/lite/subprocesses.py +0 -130
- /omlish/{formats/json/cli → argparse}/__init__.py +0 -0
- /omlish/{lite/fdio → asyncs/asyncio}/__init__.py +0 -0
- /omlish/asyncs/{asyncio.py → asyncio/asyncio.py} +0 -0
- /omlish/{lite/http → asyncs/bluelet}/__init__.py +0 -0
- /omlish/collections/{_abc.py → abc.py} +0 -0
- /omlish/{fnpipes.py → funcs/pipes.py} +0 -0
- /omlish/io/{_abc.py → abc.py} +0 -0
- /omlish/sql/{_abc.py → abc.py} +0 -0
- {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/top_level.txt +0 -0
omlish/antlr/runtime.py
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
# ruff: noqa: I001
|
2
|
+
# flake8: noqa: F401
|
3
|
+
|
4
|
+
from ._runtime.BufferedTokenStream import ( # type: ignore
|
5
|
+
TokenStream,
|
6
|
+
)
|
7
|
+
|
8
|
+
from ._runtime.CommonTokenStream import ( # type: ignore
|
9
|
+
CommonTokenStream,
|
10
|
+
)
|
11
|
+
|
12
|
+
from ._runtime.FileStream import ( # type: ignore
|
13
|
+
FileStream,
|
14
|
+
)
|
15
|
+
|
16
|
+
from ._runtime.InputStream import ( # type: ignore
|
17
|
+
InputStream,
|
18
|
+
)
|
19
|
+
|
20
|
+
from ._runtime.Lexer import ( # type: ignore
|
21
|
+
Lexer,
|
22
|
+
)
|
23
|
+
|
24
|
+
from ._runtime.Parser import ( # type: ignore
|
25
|
+
Parser,
|
26
|
+
)
|
27
|
+
|
28
|
+
from ._runtime.ParserRuleContext import ( # type: ignore
|
29
|
+
ParserRuleContext,
|
30
|
+
RuleContext,
|
31
|
+
)
|
32
|
+
|
33
|
+
from ._runtime.PredictionContext import ( # type: ignore
|
34
|
+
PredictionContextCache,
|
35
|
+
)
|
36
|
+
|
37
|
+
from ._runtime.StdinStream import ( # type: ignore
|
38
|
+
StdinStream,
|
39
|
+
)
|
40
|
+
|
41
|
+
from ._runtime.Token import ( # type: ignore
|
42
|
+
Token,
|
43
|
+
)
|
44
|
+
|
45
|
+
from ._runtime.Utils import ( # type: ignore
|
46
|
+
str_list,
|
47
|
+
)
|
48
|
+
|
49
|
+
from ._runtime.atn.ATN import ( # type: ignore
|
50
|
+
ATN,
|
51
|
+
)
|
52
|
+
|
53
|
+
from ._runtime.atn.ATNDeserializer import ( # type: ignore
|
54
|
+
ATNDeserializer,
|
55
|
+
)
|
56
|
+
|
57
|
+
from ._runtime.atn.LexerATNSimulator import ( # type: ignore
|
58
|
+
LexerATNSimulator,
|
59
|
+
)
|
60
|
+
|
61
|
+
from ._runtime.atn.ParserATNSimulator import ( # type: ignore
|
62
|
+
ParserATNSimulator,
|
63
|
+
)
|
64
|
+
|
65
|
+
from ._runtime.atn.PredictionMode import ( # type: ignore
|
66
|
+
PredictionMode,
|
67
|
+
)
|
68
|
+
|
69
|
+
from ._runtime.dfa.DFA import ( # type: ignore
|
70
|
+
DFA,
|
71
|
+
)
|
72
|
+
|
73
|
+
from ._runtime.error.DiagnosticErrorListener import ( # type: ignore
|
74
|
+
DiagnosticErrorListener,
|
75
|
+
)
|
76
|
+
|
77
|
+
from ._runtime.error.ErrorListener import ( # type: ignore
|
78
|
+
ErrorListener,
|
79
|
+
)
|
80
|
+
|
81
|
+
from ._runtime.error.ErrorStrategy import ( # type: ignore
|
82
|
+
BailErrorStrategy,
|
83
|
+
)
|
84
|
+
|
85
|
+
from ._runtime.error.Errors import ( # type: ignore
|
86
|
+
LexerNoViableAltException,
|
87
|
+
)
|
88
|
+
|
89
|
+
from ._runtime.error.Errors import ( # type: ignore
|
90
|
+
IllegalStateException,
|
91
|
+
NoViableAltException,
|
92
|
+
RecognitionException,
|
93
|
+
)
|
94
|
+
|
95
|
+
from ._runtime.tree.Tree import ( # type: ignore
|
96
|
+
ErrorNode,
|
97
|
+
ParseTreeListener,
|
98
|
+
ParseTreeVisitor,
|
99
|
+
ParseTreeWalker,
|
100
|
+
RuleNode,
|
101
|
+
TerminalNode,
|
102
|
+
)
|
omlish/antlr/utils.py
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# ruff: noqa: N802 N803
|
2
|
+
import io
|
3
|
+
import typing as ta
|
4
|
+
|
5
|
+
from . import runtime as antlr4
|
6
|
+
|
7
|
+
|
8
|
+
##
|
9
|
+
|
10
|
+
|
11
|
+
def pformat(
|
12
|
+
node: ta.Any,
|
13
|
+
*,
|
14
|
+
buf: ta.IO | None = None,
|
15
|
+
indent: str = '',
|
16
|
+
child_indent: str = ' ',
|
17
|
+
) -> ta.IO:
|
18
|
+
if buf is None:
|
19
|
+
buf = io.StringIO()
|
20
|
+
buf.write(indent)
|
21
|
+
buf.write(node.__class__.__name__)
|
22
|
+
if hasattr(node, 'start') and hasattr(node, 'stop'):
|
23
|
+
buf.write(f' ({node.start} -> {node.stop})')
|
24
|
+
buf.write('\n')
|
25
|
+
for child in getattr(node, 'children', []) or []:
|
26
|
+
pformat(child, buf=buf, indent=indent + child_indent, child_indent=child_indent)
|
27
|
+
return buf
|
28
|
+
|
29
|
+
|
30
|
+
def yield_contexts(
|
31
|
+
root: antlr4.ParserRuleContext,
|
32
|
+
) -> ta.Iterator[antlr4.ParserRuleContext]:
|
33
|
+
q = [root]
|
34
|
+
while q:
|
35
|
+
c = q.pop()
|
36
|
+
yield c
|
37
|
+
if not isinstance(c, antlr4.TerminalNode) and c.children:
|
38
|
+
q.extend(c.children)
|
omlish/argparse/all.py
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# ruff: noqa: I001
|
2
|
+
import argparse
|
3
|
+
|
4
|
+
from .cli import ( # noqa
|
5
|
+
ArgparseArg as Arg,
|
6
|
+
argparse_arg as arg,
|
7
|
+
|
8
|
+
ArgparseCommandFn as CommandFn,
|
9
|
+
ArgparseCommand as Command,
|
10
|
+
argparse_command as command,
|
11
|
+
|
12
|
+
ArgparseCli as Cli,
|
13
|
+
)
|
14
|
+
|
15
|
+
|
16
|
+
##
|
17
|
+
|
18
|
+
|
19
|
+
SUPPRESS = argparse.SUPPRESS
|
20
|
+
|
21
|
+
OPTIONAL = argparse.OPTIONAL
|
22
|
+
ZERO_OR_MORE = argparse.ZERO_OR_MORE
|
23
|
+
ONE_OR_MORE = argparse.ONE_OR_MORE
|
24
|
+
PARSER = argparse.PARSER
|
25
|
+
REMAINDER = argparse.REMAINDER
|
26
|
+
|
27
|
+
HelpFormatter = argparse.HelpFormatter
|
28
|
+
RawDescriptionHelpFormatter = argparse.RawDescriptionHelpFormatter
|
29
|
+
RawTextHelpFormatter = argparse.RawTextHelpFormatter
|
30
|
+
ArgumentDefaultsHelpFormatter = argparse.ArgumentDefaultsHelpFormatter
|
31
|
+
|
32
|
+
MetavarTypeHelpFormatter = argparse.MetavarTypeHelpFormatter
|
33
|
+
|
34
|
+
ArgumentError = argparse.ArgumentError
|
35
|
+
ArgumentTypeError = argparse.ArgumentTypeError
|
36
|
+
|
37
|
+
Action = argparse.Action
|
38
|
+
BooleanOptionalAction = argparse.BooleanOptionalAction
|
39
|
+
SubParsersAction = argparse._SubParsersAction # noqa
|
40
|
+
|
41
|
+
FileType = argparse.FileType
|
42
|
+
|
43
|
+
Namespace = argparse.Namespace
|
44
|
+
|
45
|
+
ArgumentParser = argparse.ArgumentParser
|
@@ -1,7 +1,11 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
# @omlish-lite
|
1
3
|
"""
|
2
4
|
TODO:
|
3
5
|
- default command
|
4
6
|
- auto match all underscores to hyphens
|
7
|
+
- pre-run, post-run hooks
|
8
|
+
- exitstack?
|
5
9
|
"""
|
6
10
|
import argparse
|
7
11
|
import dataclasses as dc
|
@@ -9,53 +13,25 @@ import functools
|
|
9
13
|
import sys
|
10
14
|
import typing as ta
|
11
15
|
|
12
|
-
from . import
|
13
|
-
from . import
|
16
|
+
from ..lite.check import check
|
17
|
+
from ..lite.reflect import get_optional_alias_arg
|
18
|
+
from ..lite.reflect import is_optional_alias
|
14
19
|
|
15
20
|
|
16
21
|
T = ta.TypeVar('T')
|
17
22
|
|
18
23
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
SUPPRESS = argparse.SUPPRESS
|
23
|
-
|
24
|
-
OPTIONAL = argparse.OPTIONAL
|
25
|
-
ZERO_OR_MORE = argparse.ZERO_OR_MORE
|
26
|
-
ONE_OR_MORE = argparse.ONE_OR_MORE
|
27
|
-
PARSER = argparse.PARSER
|
28
|
-
REMAINDER = argparse.REMAINDER
|
29
|
-
|
30
|
-
HelpFormatter = argparse.HelpFormatter
|
31
|
-
RawDescriptionHelpFormatter = argparse.RawDescriptionHelpFormatter
|
32
|
-
RawTextHelpFormatter = argparse.RawTextHelpFormatter
|
33
|
-
ArgumentDefaultsHelpFormatter = argparse.ArgumentDefaultsHelpFormatter
|
34
|
-
|
35
|
-
MetavarTypeHelpFormatter = argparse.MetavarTypeHelpFormatter
|
36
|
-
|
37
|
-
ArgumentError = argparse.ArgumentError
|
38
|
-
ArgumentTypeError = argparse.ArgumentTypeError
|
39
|
-
|
40
|
-
Action = argparse.Action
|
41
|
-
BooleanOptionalAction = argparse.BooleanOptionalAction
|
42
|
-
SubParsersAction = argparse._SubParsersAction # noqa
|
43
|
-
|
44
|
-
FileType = argparse.FileType
|
45
|
-
|
46
|
-
Namespace = argparse.Namespace
|
47
|
-
|
48
|
-
ArgumentParser = argparse.ArgumentParser
|
24
|
+
ArgparseCommandFn = ta.Callable[[], ta.Optional[int]] # ta.TypeAlias
|
49
25
|
|
50
26
|
|
51
27
|
##
|
52
28
|
|
53
29
|
|
54
30
|
@dc.dataclass(eq=False)
|
55
|
-
class
|
31
|
+
class ArgparseArg:
|
56
32
|
args: ta.Sequence[ta.Any]
|
57
33
|
kwargs: ta.Mapping[str, ta.Any]
|
58
|
-
dest: str
|
34
|
+
dest: ta.Optional[str] = None
|
59
35
|
|
60
36
|
def __get__(self, instance, owner=None):
|
61
37
|
if instance is None:
|
@@ -63,26 +39,23 @@ class Arg:
|
|
63
39
|
return getattr(instance.args, self.dest) # type: ignore
|
64
40
|
|
65
41
|
|
66
|
-
def
|
67
|
-
return
|
42
|
+
def argparse_arg(*args, **kwargs) -> ArgparseArg:
|
43
|
+
return ArgparseArg(args, kwargs)
|
68
44
|
|
69
45
|
|
70
46
|
#
|
71
47
|
|
72
48
|
|
73
|
-
CommandFn = ta.Callable[[], int | None]
|
74
|
-
|
75
|
-
|
76
49
|
@dc.dataclass(eq=False)
|
77
|
-
class
|
50
|
+
class ArgparseCommand:
|
78
51
|
name: str
|
79
|
-
fn:
|
80
|
-
args: ta.Sequence[
|
52
|
+
fn: ArgparseCommandFn
|
53
|
+
args: ta.Sequence[ArgparseArg] = () # noqa
|
81
54
|
|
82
|
-
_: dc.KW_ONLY
|
55
|
+
# _: dc.KW_ONLY
|
83
56
|
|
84
|
-
aliases: ta.Sequence[str]
|
85
|
-
parent: ta.Optional['
|
57
|
+
aliases: ta.Optional[ta.Sequence[str]] = None
|
58
|
+
parent: ta.Optional['ArgparseCommand'] = None
|
86
59
|
accepts_unknown: bool = False
|
87
60
|
|
88
61
|
def __post_init__(self) -> None:
|
@@ -95,9 +68,9 @@ class Command:
|
|
95
68
|
for a in self.aliases or []:
|
96
69
|
check_name(a)
|
97
70
|
|
98
|
-
check.callable(self.fn)
|
99
|
-
check.arg(all(isinstance(a,
|
100
|
-
check.isinstance(self.parent, (
|
71
|
+
check.arg(callable(self.fn))
|
72
|
+
check.arg(all(isinstance(a, ArgparseArg) for a in self.args))
|
73
|
+
check.isinstance(self.parent, (ArgparseCommand, type(None)))
|
101
74
|
check.isinstance(self.accepts_unknown, bool)
|
102
75
|
|
103
76
|
functools.update_wrapper(self, self.fn)
|
@@ -107,25 +80,25 @@ class Command:
|
|
107
80
|
return self
|
108
81
|
return dc.replace(self, fn=self.fn.__get__(instance, owner)) # noqa
|
109
82
|
|
110
|
-
def __call__(self, *args, **kwargs) -> int
|
83
|
+
def __call__(self, *args, **kwargs) -> ta.Optional[int]:
|
111
84
|
return self.fn(*args, **kwargs)
|
112
85
|
|
113
86
|
|
114
|
-
def
|
115
|
-
*args:
|
116
|
-
name: str
|
117
|
-
aliases: ta.Iterable[str]
|
118
|
-
parent:
|
87
|
+
def argparse_command(
|
88
|
+
*args: ArgparseArg,
|
89
|
+
name: ta.Optional[str] = None,
|
90
|
+
aliases: ta.Optional[ta.Iterable[str]] = None,
|
91
|
+
parent: ta.Optional[ArgparseCommand] = None,
|
119
92
|
accepts_unknown: bool = False,
|
120
|
-
) -> ta.Any: # ta.Callable[[
|
93
|
+
) -> ta.Any: # ta.Callable[[ArgparseCommandFn], ArgparseCommand]: # FIXME
|
121
94
|
for arg in args:
|
122
|
-
check.isinstance(arg,
|
123
|
-
check.isinstance(name, (str, None))
|
124
|
-
check.isinstance(parent, (
|
95
|
+
check.isinstance(arg, ArgparseArg)
|
96
|
+
check.isinstance(name, (str, type(None)))
|
97
|
+
check.isinstance(parent, (ArgparseCommand, type(None)))
|
125
98
|
check.not_isinstance(aliases, str)
|
126
99
|
|
127
100
|
def inner(fn):
|
128
|
-
return
|
101
|
+
return ArgparseCommand(
|
129
102
|
(name if name is not None else fn.__name__).replace('_', '-'),
|
130
103
|
fn,
|
131
104
|
args,
|
@@ -140,7 +113,7 @@ def command(
|
|
140
113
|
##
|
141
114
|
|
142
115
|
|
143
|
-
def
|
116
|
+
def _get_argparse_arg_ann_kwargs(ann: ta.Any) -> ta.Mapping[str, ta.Any]:
|
144
117
|
if ann is str:
|
145
118
|
return {}
|
146
119
|
elif ann is int:
|
@@ -149,56 +122,71 @@ def get_arg_ann_kwargs(ann: ta.Any) -> ta.Mapping[str, ta.Any]:
|
|
149
122
|
return {'action': 'store_true'}
|
150
123
|
elif ann is list:
|
151
124
|
return {'action': 'append'}
|
125
|
+
elif is_optional_alias(ann):
|
126
|
+
return _get_argparse_arg_ann_kwargs(get_optional_alias_arg(ann))
|
152
127
|
else:
|
153
128
|
raise TypeError(ann)
|
154
129
|
|
155
130
|
|
156
|
-
class
|
157
|
-
|
131
|
+
class _ArgparseCliAnnotationBox:
|
158
132
|
def __init__(self, annotations: ta.Mapping[str, ta.Any]) -> None:
|
159
133
|
super().__init__()
|
160
134
|
self.__annotations__ = annotations # type: ignore
|
161
135
|
|
162
136
|
|
163
|
-
class
|
137
|
+
class ArgparseCli:
|
138
|
+
def __init__(self, argv: ta.Optional[ta.Sequence[str]] = None) -> None:
|
139
|
+
super().__init__()
|
140
|
+
|
141
|
+
self._argv = argv if argv is not None else sys.argv[1:]
|
164
142
|
|
165
|
-
|
166
|
-
if not bases:
|
167
|
-
return super().__new__(mcls, name, tuple(bases), dict(namespace))
|
143
|
+
self._args, self._unknown_args = self.get_parser().parse_known_args(self._argv)
|
168
144
|
|
169
|
-
|
170
|
-
namespace = dict(namespace)
|
145
|
+
#
|
171
146
|
|
147
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
148
|
+
super().__init_subclass__(**kwargs)
|
149
|
+
|
150
|
+
ns = cls.__dict__
|
172
151
|
objs = {}
|
173
|
-
mro =
|
174
|
-
for bns in [bcls.__dict__ for bcls in reversed(mro)] + [
|
152
|
+
mro = cls.__mro__[::-1]
|
153
|
+
for bns in [bcls.__dict__ for bcls in reversed(mro)] + [ns]:
|
175
154
|
bseen = set() # type: ignore
|
176
155
|
for k, v in bns.items():
|
177
|
-
if isinstance(v, (
|
156
|
+
if isinstance(v, (ArgparseCommand, ArgparseArg)):
|
178
157
|
check.not_in(v, bseen)
|
179
158
|
bseen.add(v)
|
180
159
|
objs[k] = v
|
181
160
|
elif k in objs:
|
182
161
|
del [k]
|
183
162
|
|
184
|
-
|
163
|
+
#
|
164
|
+
|
165
|
+
anns = ta.get_type_hints(_ArgparseCliAnnotationBox({
|
185
166
|
**{k: v for bcls in reversed(mro) for k, v in getattr(bcls, '__annotations__', {}).items()},
|
186
|
-
**
|
187
|
-
}), globalns=
|
167
|
+
**ns.get('__annotations__', {}),
|
168
|
+
}), globalns=ns.get('__globals__', {}))
|
169
|
+
|
170
|
+
#
|
188
171
|
|
189
|
-
if '
|
190
|
-
parser = check.isinstance(
|
172
|
+
if '_parser' in ns:
|
173
|
+
parser = check.isinstance(ns['_parser'], argparse.ArgumentParser)
|
191
174
|
else:
|
192
|
-
parser = ArgumentParser()
|
193
|
-
|
175
|
+
parser = argparse.ArgumentParser()
|
176
|
+
setattr(cls, '_parser', parser)
|
177
|
+
|
178
|
+
#
|
194
179
|
|
195
180
|
subparsers = parser.add_subparsers()
|
181
|
+
|
196
182
|
for att, obj in objs.items():
|
197
|
-
if isinstance(obj,
|
183
|
+
if isinstance(obj, ArgparseCommand):
|
198
184
|
if obj.parent is not None:
|
199
185
|
raise NotImplementedError
|
186
|
+
|
200
187
|
for cn in [obj.name, *(obj.aliases or [])]:
|
201
|
-
|
188
|
+
subparser = subparsers.add_parser(cn)
|
189
|
+
|
202
190
|
for arg in (obj.args or []):
|
203
191
|
if (
|
204
192
|
len(arg.args) == 1 and
|
@@ -206,45 +194,38 @@ class _CliMeta(type):
|
|
206
194
|
not (n := check.isinstance(arg.args[0], str)).startswith('-') and
|
207
195
|
'metavar' not in arg.kwargs
|
208
196
|
):
|
209
|
-
|
197
|
+
subparser.add_argument(
|
210
198
|
n.replace('-', '_'),
|
211
199
|
**arg.kwargs,
|
212
200
|
metavar=n,
|
213
201
|
)
|
214
202
|
else:
|
215
|
-
|
216
|
-
|
203
|
+
subparser.add_argument(*arg.args, **arg.kwargs)
|
204
|
+
|
205
|
+
subparser.set_defaults(_cmd=obj)
|
217
206
|
|
218
|
-
elif isinstance(obj,
|
207
|
+
elif isinstance(obj, ArgparseArg):
|
219
208
|
if att in anns:
|
220
|
-
|
221
|
-
obj.kwargs = {**
|
209
|
+
ann_kwargs = _get_argparse_arg_ann_kwargs(anns[att])
|
210
|
+
obj.kwargs = {**ann_kwargs, **obj.kwargs}
|
211
|
+
|
222
212
|
if not obj.dest:
|
223
213
|
if 'dest' in obj.kwargs:
|
224
214
|
obj.dest = obj.kwargs['dest']
|
225
215
|
else:
|
226
216
|
obj.dest = obj.kwargs['dest'] = att # type: ignore
|
217
|
+
|
227
218
|
parser.add_argument(*obj.args, **obj.kwargs)
|
228
219
|
|
229
220
|
else:
|
230
221
|
raise TypeError(obj)
|
231
222
|
|
232
|
-
|
233
|
-
|
223
|
+
#
|
234
224
|
|
235
|
-
|
236
|
-
|
237
|
-
def __init__(self, argv: ta.Sequence[str] | None = None) -> None:
|
238
|
-
super().__init__()
|
239
|
-
|
240
|
-
self._argv = argv if argv is not None else sys.argv[1:]
|
241
|
-
|
242
|
-
self._args, self._unknown_args = self.get_parser().parse_known_args(self._argv)
|
243
|
-
|
244
|
-
_parser: ta.ClassVar[ArgumentParser]
|
225
|
+
_parser: ta.ClassVar[argparse.ArgumentParser]
|
245
226
|
|
246
227
|
@classmethod
|
247
|
-
def get_parser(cls) -> ArgumentParser:
|
228
|
+
def get_parser(cls) -> argparse.ArgumentParser:
|
248
229
|
return cls._parser
|
249
230
|
|
250
231
|
@property
|
@@ -252,17 +233,19 @@ class Cli(metaclass=_CliMeta):
|
|
252
233
|
return self._argv
|
253
234
|
|
254
235
|
@property
|
255
|
-
def args(self) -> Namespace:
|
236
|
+
def args(self) -> argparse.Namespace:
|
256
237
|
return self._args
|
257
238
|
|
258
239
|
@property
|
259
240
|
def unknown_args(self) -> ta.Sequence[str]:
|
260
241
|
return self._unknown_args
|
261
242
|
|
262
|
-
|
263
|
-
|
243
|
+
#
|
244
|
+
|
245
|
+
def _bind_cli_cmd(self, cmd: ArgparseCommand) -> ta.Callable:
|
246
|
+
return cmd.__get__(self, type(self))
|
264
247
|
|
265
|
-
def
|
248
|
+
def prepare_cli_run(self) -> ta.Optional[ta.Callable]:
|
266
249
|
cmd = getattr(self.args, '_cmd', None)
|
267
250
|
|
268
251
|
if self._unknown_args and not (cmd is not None and cmd.accepts_unknown):
|
@@ -270,13 +253,35 @@ class Cli(metaclass=_CliMeta):
|
|
270
253
|
if (parser := self.get_parser()).exit_on_error: # type: ignore
|
271
254
|
parser.error(msg)
|
272
255
|
else:
|
273
|
-
raise ArgumentError(None, msg)
|
256
|
+
raise argparse.ArgumentError(None, msg)
|
274
257
|
|
275
258
|
if cmd is None:
|
276
259
|
self.get_parser().print_help()
|
260
|
+
return None
|
261
|
+
|
262
|
+
return self._bind_cli_cmd(cmd)
|
263
|
+
|
264
|
+
#
|
265
|
+
|
266
|
+
def cli_run(self) -> ta.Optional[int]:
|
267
|
+
if (fn := self.prepare_cli_run()) is None:
|
277
268
|
return 0
|
278
269
|
|
279
|
-
return
|
270
|
+
return fn()
|
271
|
+
|
272
|
+
def cli_run_and_exit(self) -> ta.NoReturn:
|
273
|
+
sys.exit(rc if isinstance(rc := self.cli_run(), int) else 0)
|
274
|
+
|
275
|
+
def __call__(self, *, exit: bool = False) -> ta.Optional[int]: # noqa
|
276
|
+
if exit:
|
277
|
+
return self.cli_run_and_exit()
|
278
|
+
else:
|
279
|
+
return self.cli_run()
|
280
|
+
|
281
|
+
#
|
282
|
+
|
283
|
+
async def async_cli_run(self) -> ta.Optional[int]:
|
284
|
+
if (fn := self.prepare_cli_run()) is None:
|
285
|
+
return 0
|
280
286
|
|
281
|
-
|
282
|
-
sys.exit(rc if isinstance(rc := self(), int) else 0)
|
287
|
+
return await fn()
|
omlish/asyncs/__init__.py
CHANGED
@@ -1,35 +0,0 @@
|
|
1
|
-
from .asyncs import ( # noqa
|
2
|
-
SyncableIterable,
|
3
|
-
async_list,
|
4
|
-
sync_await,
|
5
|
-
sync_list,
|
6
|
-
syncable_iterable,
|
7
|
-
)
|
8
|
-
|
9
|
-
from .bridge import ( # noqa
|
10
|
-
a_to_s,
|
11
|
-
is_in_bridge,
|
12
|
-
s_to_a,
|
13
|
-
s_to_a_await,
|
14
|
-
trivial_a_to_s,
|
15
|
-
trivial_s_to_a,
|
16
|
-
)
|
17
|
-
|
18
|
-
from .flavors import ( # noqa
|
19
|
-
ContextManagerAdapter,
|
20
|
-
Flavor,
|
21
|
-
adapt,
|
22
|
-
adapt_context,
|
23
|
-
from_anyio,
|
24
|
-
from_anyio_context,
|
25
|
-
from_asyncio,
|
26
|
-
from_asyncio_context,
|
27
|
-
from_trio,
|
28
|
-
from_trio_context,
|
29
|
-
get_flavor,
|
30
|
-
mark_anyio,
|
31
|
-
mark_asyncio,
|
32
|
-
mark_flavor,
|
33
|
-
mark_trio,
|
34
|
-
with_adapter_loop,
|
35
|
-
)
|
omlish/asyncs/all.py
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
from .asyncs import ( # noqa
|
2
|
+
SyncableIterable,
|
3
|
+
async_list,
|
4
|
+
sync_await,
|
5
|
+
sync_list,
|
6
|
+
syncable_iterable,
|
7
|
+
)
|
8
|
+
|
9
|
+
from .bridge import ( # noqa
|
10
|
+
a_to_s,
|
11
|
+
is_in_bridge,
|
12
|
+
s_to_a,
|
13
|
+
s_to_a_await,
|
14
|
+
trivial_a_to_s,
|
15
|
+
trivial_s_to_a,
|
16
|
+
)
|
17
|
+
|
18
|
+
from .flavors import ( # noqa
|
19
|
+
ContextManagerAdapter,
|
20
|
+
Flavor,
|
21
|
+
adapt,
|
22
|
+
adapt_context,
|
23
|
+
from_anyio,
|
24
|
+
from_anyio_context,
|
25
|
+
from_asyncio,
|
26
|
+
from_asyncio_context,
|
27
|
+
from_trio,
|
28
|
+
from_trio_context,
|
29
|
+
get_flavor,
|
30
|
+
mark_anyio,
|
31
|
+
mark_asyncio,
|
32
|
+
mark_flavor,
|
33
|
+
mark_trio,
|
34
|
+
with_adapter_loop,
|
35
|
+
)
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
# @omlish-lite
|
3
|
+
import asyncio
|
4
|
+
import typing as ta
|
5
|
+
|
6
|
+
|
7
|
+
class AsyncioBytesChannelTransport(asyncio.Transport):
|
8
|
+
def __init__(self, reader: asyncio.StreamReader) -> None:
|
9
|
+
super().__init__()
|
10
|
+
|
11
|
+
self.reader = reader
|
12
|
+
self.closed: asyncio.Future = asyncio.Future()
|
13
|
+
|
14
|
+
# @ta.override
|
15
|
+
def write(self, data: bytes) -> None:
|
16
|
+
self.reader.feed_data(data)
|
17
|
+
|
18
|
+
# @ta.override
|
19
|
+
def close(self) -> None:
|
20
|
+
self.reader.feed_eof()
|
21
|
+
if not self.closed.done():
|
22
|
+
self.closed.set_result(True)
|
23
|
+
|
24
|
+
# @ta.override
|
25
|
+
def is_closing(self) -> bool:
|
26
|
+
return self.closed.done()
|
27
|
+
|
28
|
+
|
29
|
+
def asyncio_create_bytes_channel(
|
30
|
+
loop: ta.Any = None,
|
31
|
+
) -> ta.Tuple[asyncio.StreamReader, asyncio.StreamWriter]:
|
32
|
+
if loop is None:
|
33
|
+
loop = asyncio.get_running_loop()
|
34
|
+
|
35
|
+
reader = asyncio.StreamReader()
|
36
|
+
protocol = asyncio.StreamReaderProtocol(reader)
|
37
|
+
transport = AsyncioBytesChannelTransport(reader)
|
38
|
+
writer = asyncio.StreamWriter(transport, protocol, reader, loop)
|
39
|
+
|
40
|
+
return reader, writer
|