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/lite/reflect.py
CHANGED
@@ -7,6 +7,9 @@ import typing as ta
|
|
7
7
|
T = ta.TypeVar('T')
|
8
8
|
|
9
9
|
|
10
|
+
##
|
11
|
+
|
12
|
+
|
10
13
|
_GENERIC_ALIAS_TYPES = (
|
11
14
|
ta._GenericAlias, # type: ignore # noqa
|
12
15
|
*([ta._SpecialGenericAlias] if hasattr(ta, '_SpecialGenericAlias') else []), # noqa
|
@@ -24,6 +27,9 @@ is_union_alias = functools.partial(is_generic_alias, origin=ta.Union)
|
|
24
27
|
is_callable_alias = functools.partial(is_generic_alias, origin=ta.Callable)
|
25
28
|
|
26
29
|
|
30
|
+
##
|
31
|
+
|
32
|
+
|
27
33
|
def is_optional_alias(spec: ta.Any) -> bool:
|
28
34
|
return (
|
29
35
|
isinstance(spec, _GENERIC_ALIAS_TYPES) and # noqa
|
@@ -38,6 +44,9 @@ def get_optional_alias_arg(spec: ta.Any) -> ta.Any:
|
|
38
44
|
return it
|
39
45
|
|
40
46
|
|
47
|
+
##
|
48
|
+
|
49
|
+
|
41
50
|
def is_new_type(spec: ta.Any) -> bool:
|
42
51
|
if isinstance(ta.NewType, type):
|
43
52
|
return isinstance(spec, ta.NewType)
|
@@ -46,6 +55,30 @@ def is_new_type(spec: ta.Any) -> bool:
|
|
46
55
|
return isinstance(spec, types.FunctionType) and spec.__code__ is ta.NewType.__code__.co_consts[1] # type: ignore # noqa
|
47
56
|
|
48
57
|
|
58
|
+
def get_new_type_supertype(spec: ta.Any) -> ta.Any:
|
59
|
+
return spec.__supertype__
|
60
|
+
|
61
|
+
|
62
|
+
##
|
63
|
+
|
64
|
+
|
65
|
+
def is_literal_type(spec: ta.Any) -> bool:
|
66
|
+
if hasattr(ta, '_LiteralGenericAlias'):
|
67
|
+
return isinstance(spec, ta._LiteralGenericAlias) # noqa
|
68
|
+
else:
|
69
|
+
return (
|
70
|
+
isinstance(spec, ta._GenericAlias) and # type: ignore # noqa
|
71
|
+
spec.__origin__ is ta.Literal
|
72
|
+
)
|
73
|
+
|
74
|
+
|
75
|
+
def get_literal_type_args(spec: ta.Any) -> ta.Iterable[ta.Any]:
|
76
|
+
return spec.__args__
|
77
|
+
|
78
|
+
|
79
|
+
##
|
80
|
+
|
81
|
+
|
49
82
|
def deep_subclasses(cls: ta.Type[T]) -> ta.Iterator[ta.Type[T]]:
|
50
83
|
seen = set()
|
51
84
|
todo = list(reversed(cls.__subclasses__()))
|
omlish/lite/resources.py
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
def read_package_resource_binary(package: str, resource: str) -> bytes:
|
2
|
+
import importlib.resources
|
3
|
+
return importlib.resources.read_binary(package, resource)
|
4
|
+
|
5
|
+
|
6
|
+
def read_package_resource_text(package: str, resource: str) -> str:
|
7
|
+
import importlib.resources
|
8
|
+
return importlib.resources.read_text(package, resource)
|
omlish/lite/runtime.py
CHANGED
@@ -9,9 +9,9 @@ def is_debugger_attached() -> bool:
|
|
9
9
|
return any(frame[1].endswith('pydevd.py') for frame in inspect.stack())
|
10
10
|
|
11
11
|
|
12
|
-
|
12
|
+
LITE_REQUIRED_PYTHON_VERSION = (3, 8)
|
13
13
|
|
14
14
|
|
15
|
-
def
|
16
|
-
if sys.version_info <
|
17
|
-
raise OSError(f'Requires python {
|
15
|
+
def check_lite_runtime_version() -> None:
|
16
|
+
if sys.version_info < LITE_REQUIRED_PYTHON_VERSION:
|
17
|
+
raise OSError(f'Requires python {LITE_REQUIRED_PYTHON_VERSION}, got {sys.version_info} from {sys.executable}') # noqa
|
omlish/lite/shlex.py
ADDED
omlish/lite/socketserver.py
CHANGED
@@ -3,7 +3,7 @@ import socket
|
|
3
3
|
import socketserver
|
4
4
|
import typing as ta
|
5
5
|
|
6
|
-
from omlish.lite.check import
|
6
|
+
from omlish.lite.check import check
|
7
7
|
|
8
8
|
from .socket import SocketAddress
|
9
9
|
from .socket import SocketHandlerFactory
|
@@ -58,7 +58,7 @@ class SocketHandlerSocketServerStreamRequestHandler( # type: ignore[misc]
|
|
58
58
|
)
|
59
59
|
|
60
60
|
def handle(self) -> None:
|
61
|
-
target =
|
61
|
+
target = check.not_none(self.socket_handler_factory)(
|
62
62
|
self.client_address,
|
63
63
|
self.rfile, # type: ignore[arg-type]
|
64
64
|
self.wfile, # type: ignore[arg-type]
|
omlish/lite/strings.py
CHANGED
@@ -21,6 +21,37 @@ def snake_case(name: str) -> str:
|
|
21
21
|
##
|
22
22
|
|
23
23
|
|
24
|
+
def strip_with_newline(s: str) -> str:
|
25
|
+
if not s:
|
26
|
+
return ''
|
27
|
+
return s.strip() + '\n'
|
28
|
+
|
29
|
+
|
30
|
+
@ta.overload
|
31
|
+
def split_keep_delimiter(s: str, d: str) -> str:
|
32
|
+
...
|
33
|
+
|
34
|
+
|
35
|
+
@ta.overload
|
36
|
+
def split_keep_delimiter(s: bytes, d: bytes) -> bytes:
|
37
|
+
...
|
38
|
+
|
39
|
+
|
40
|
+
def split_keep_delimiter(s, d):
|
41
|
+
ps = []
|
42
|
+
i = 0
|
43
|
+
while i < len(s):
|
44
|
+
if (n := s.find(d, i)) < i:
|
45
|
+
ps.append(s[i:])
|
46
|
+
break
|
47
|
+
ps.append(s[i:n + 1])
|
48
|
+
i = n + 1
|
49
|
+
return ps
|
50
|
+
|
51
|
+
|
52
|
+
##
|
53
|
+
|
54
|
+
|
24
55
|
def is_dunder(name: str) -> bool:
|
25
56
|
return (
|
26
57
|
name[:2] == name[-2:] == '__' and
|
omlish/logs/__init__.py
CHANGED
@@ -1,32 +0,0 @@
|
|
1
|
-
from .configs import ( # noqa
|
2
|
-
configure_standard_logging,
|
3
|
-
)
|
4
|
-
|
5
|
-
from .formatters import ( # noqa
|
6
|
-
ColorLogFormatter,
|
7
|
-
)
|
8
|
-
|
9
|
-
from .handlers import ( # noqa
|
10
|
-
ListHandler,
|
11
|
-
)
|
12
|
-
|
13
|
-
from .utils import ( # noqa
|
14
|
-
error_logging,
|
15
|
-
)
|
16
|
-
|
17
|
-
|
18
|
-
##
|
19
|
-
|
20
|
-
|
21
|
-
from ..lite.logs import ( # noqa
|
22
|
-
TidLogFilter,
|
23
|
-
JsonLogFormatter,
|
24
|
-
|
25
|
-
STANDARD_LOG_FORMAT_PARTS,
|
26
|
-
StandardLogFormatter,
|
27
|
-
|
28
|
-
ProxyLogFilterer,
|
29
|
-
ProxyLogHandler,
|
30
|
-
|
31
|
-
StandardLogHandler,
|
32
|
-
)
|
omlish/logs/{_abc.py → abc.py}
RENAMED
omlish/logs/all.py
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
from .color import ( # noqa
|
2
|
+
ColorLogFormatter,
|
3
|
+
)
|
4
|
+
|
5
|
+
from .filters import ( # noqa
|
6
|
+
TidLogFilter,
|
7
|
+
)
|
8
|
+
|
9
|
+
from .handlers import ( # noqa
|
10
|
+
ListHandler,
|
11
|
+
)
|
12
|
+
|
13
|
+
from .json import ( # noqa
|
14
|
+
JsonLogFormatter,
|
15
|
+
)
|
16
|
+
|
17
|
+
from .noisy import ( # noqa
|
18
|
+
silence_noisy_loggers,
|
19
|
+
)
|
20
|
+
|
21
|
+
from .proxy import ( # noqa
|
22
|
+
ProxyLogFilterer,
|
23
|
+
ProxyLogHandler,
|
24
|
+
)
|
25
|
+
|
26
|
+
from .standard import ( # noqa
|
27
|
+
STANDARD_LOG_FORMAT_PARTS,
|
28
|
+
StandardLogFormatter,
|
29
|
+
|
30
|
+
StandardConfiguredLogHandler,
|
31
|
+
|
32
|
+
configure_standard_logging,
|
33
|
+
)
|
34
|
+
|
35
|
+
from .utils import ( # noqa
|
36
|
+
error_logging,
|
37
|
+
)
|
@@ -3,11 +3,10 @@ import logging
|
|
3
3
|
import typing as ta
|
4
4
|
|
5
5
|
from .. import term
|
6
|
-
from
|
6
|
+
from .standard import StandardLogFormatter
|
7
7
|
|
8
8
|
|
9
9
|
class ColorLogFormatter(StandardLogFormatter):
|
10
|
-
|
11
10
|
LEVEL_COLORS: ta.Mapping[int, term.SGRs.FG] = {
|
12
11
|
logging.WARNING: term.SGRs.FG.BRIGHT_YELLOW,
|
13
12
|
logging.ERROR: term.SGRs.FG.BRIGHT_RED,
|
omlish/logs/configs.py
CHANGED
@@ -1,19 +1,17 @@
|
|
1
|
+
"""
|
2
|
+
https://docs.python.org/3/howto/logging.html#configuring-logging
|
3
|
+
"""
|
1
4
|
import dataclasses as dc
|
2
|
-
import logging
|
3
5
|
import typing as ta
|
4
6
|
|
5
|
-
from ..lite.logs import StandardLogHandler
|
6
|
-
from ..lite.logs import configure_standard_logging as configure_lite_standard_logging
|
7
|
-
from .noisy import silence_noisy_loggers
|
8
|
-
|
9
7
|
|
10
8
|
##
|
11
9
|
|
12
10
|
|
13
|
-
FilterConfig = dict[str, ta.Any]
|
14
|
-
FormatterConfig = dict[str, ta.Any]
|
15
|
-
HandlerConfig = dict[str, ta.Any]
|
16
|
-
LoggerConfig = dict[str, ta.Any]
|
11
|
+
FilterConfig: ta.TypeAlias = dict[str, ta.Any]
|
12
|
+
FormatterConfig: ta.TypeAlias = dict[str, ta.Any]
|
13
|
+
HandlerConfig: ta.TypeAlias = dict[str, ta.Any]
|
14
|
+
LoggerConfig: ta.TypeAlias = dict[str, ta.Any]
|
17
15
|
|
18
16
|
|
19
17
|
@dc.dataclass()
|
@@ -26,32 +24,3 @@ class DictConfig:
|
|
26
24
|
handlers: dict[str, HandlerConfig] = dc.field(default_factory=dict)
|
27
25
|
loggers: dict[str, LoggerConfig] = dc.field(default_factory=dict)
|
28
26
|
root: LoggerConfig | None = None
|
29
|
-
|
30
|
-
|
31
|
-
##
|
32
|
-
|
33
|
-
|
34
|
-
def configure_standard_logging(
|
35
|
-
level: ta.Any = None,
|
36
|
-
*,
|
37
|
-
json: bool = False,
|
38
|
-
target: logging.Logger | None = None,
|
39
|
-
force: bool = False,
|
40
|
-
) -> StandardLogHandler | None:
|
41
|
-
handler = configure_lite_standard_logging(
|
42
|
-
level,
|
43
|
-
json=json,
|
44
|
-
target=target,
|
45
|
-
force=force,
|
46
|
-
)
|
47
|
-
|
48
|
-
if handler is None:
|
49
|
-
return None
|
50
|
-
|
51
|
-
#
|
52
|
-
|
53
|
-
silence_noisy_loggers()
|
54
|
-
|
55
|
-
#
|
56
|
-
|
57
|
-
return handler
|
omlish/logs/filters.py
ADDED
omlish/logs/handlers.py
CHANGED
@@ -1,10 +1,13 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
# @omlish-lite
|
1
3
|
import logging
|
4
|
+
import typing as ta
|
2
5
|
|
3
6
|
|
4
7
|
class ListHandler(logging.Handler):
|
5
8
|
def __init__(self) -> None:
|
6
9
|
super().__init__()
|
7
|
-
self.records:
|
10
|
+
self.records: ta.List[logging.LogRecord] = []
|
8
11
|
|
9
12
|
def emit(self, record: logging.LogRecord) -> None:
|
10
13
|
self.records.append(record)
|
omlish/logs/json.py
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
# @omlish-lite
|
3
|
+
"""
|
4
|
+
TODO:
|
5
|
+
- translate json keys
|
6
|
+
"""
|
7
|
+
import logging
|
8
|
+
import typing as ta
|
9
|
+
|
10
|
+
from ..lite.json import json_dumps_compact
|
11
|
+
|
12
|
+
|
13
|
+
class JsonLogFormatter(logging.Formatter):
|
14
|
+
KEYS: ta.Mapping[str, bool] = {
|
15
|
+
'name': False,
|
16
|
+
'msg': False,
|
17
|
+
'args': False,
|
18
|
+
'levelname': False,
|
19
|
+
'levelno': False,
|
20
|
+
'pathname': False,
|
21
|
+
'filename': False,
|
22
|
+
'module': False,
|
23
|
+
'exc_info': True,
|
24
|
+
'exc_text': True,
|
25
|
+
'stack_info': True,
|
26
|
+
'lineno': False,
|
27
|
+
'funcName': False,
|
28
|
+
'created': False,
|
29
|
+
'msecs': False,
|
30
|
+
'relativeCreated': False,
|
31
|
+
'thread': False,
|
32
|
+
'threadName': False,
|
33
|
+
'processName': False,
|
34
|
+
'process': False,
|
35
|
+
}
|
36
|
+
|
37
|
+
def __init__(
|
38
|
+
self,
|
39
|
+
*args: ta.Any,
|
40
|
+
json_dumps: ta.Optional[ta.Callable[[ta.Any], str]] = None,
|
41
|
+
**kwargs: ta.Any,
|
42
|
+
) -> None:
|
43
|
+
super().__init__(*args, **kwargs)
|
44
|
+
|
45
|
+
if json_dumps is None:
|
46
|
+
json_dumps = json_dumps_compact
|
47
|
+
self._json_dumps = json_dumps
|
48
|
+
|
49
|
+
def format(self, record: logging.LogRecord) -> str:
|
50
|
+
dct = {
|
51
|
+
k: v
|
52
|
+
for k, o in self.KEYS.items()
|
53
|
+
for v in [getattr(record, k)]
|
54
|
+
if not (o and v is None)
|
55
|
+
}
|
56
|
+
return self._json_dumps(dct)
|
omlish/logs/proxy.py
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
# ruff: noqa: N802 UP006 UP007
|
2
|
+
# @omlish-lite
|
3
|
+
import logging
|
4
|
+
|
5
|
+
|
6
|
+
class ProxyLogFilterer(logging.Filterer):
|
7
|
+
def __init__(self, underlying: logging.Filterer) -> None: # noqa
|
8
|
+
self._underlying = underlying
|
9
|
+
|
10
|
+
@property
|
11
|
+
def underlying(self) -> logging.Filterer:
|
12
|
+
return self._underlying
|
13
|
+
|
14
|
+
@property
|
15
|
+
def filters(self):
|
16
|
+
return self._underlying.filters
|
17
|
+
|
18
|
+
@filters.setter
|
19
|
+
def filters(self, filters):
|
20
|
+
self._underlying.filters = filters
|
21
|
+
|
22
|
+
def addFilter(self, filter): # noqa
|
23
|
+
self._underlying.addFilter(filter)
|
24
|
+
|
25
|
+
def removeFilter(self, filter): # noqa
|
26
|
+
self._underlying.removeFilter(filter)
|
27
|
+
|
28
|
+
def filter(self, record):
|
29
|
+
return self._underlying.filter(record)
|
30
|
+
|
31
|
+
|
32
|
+
class ProxyLogHandler(ProxyLogFilterer, logging.Handler):
|
33
|
+
def __init__(self, underlying: logging.Handler) -> None: # noqa
|
34
|
+
ProxyLogFilterer.__init__(self, underlying)
|
35
|
+
|
36
|
+
_underlying: logging.Handler
|
37
|
+
|
38
|
+
@property
|
39
|
+
def underlying(self) -> logging.Handler:
|
40
|
+
return self._underlying
|
41
|
+
|
42
|
+
def get_name(self):
|
43
|
+
return self._underlying.get_name()
|
44
|
+
|
45
|
+
def set_name(self, name):
|
46
|
+
self._underlying.set_name(name)
|
47
|
+
|
48
|
+
@property
|
49
|
+
def name(self):
|
50
|
+
return self._underlying.name
|
51
|
+
|
52
|
+
@property
|
53
|
+
def level(self):
|
54
|
+
return self._underlying.level
|
55
|
+
|
56
|
+
@level.setter
|
57
|
+
def level(self, level):
|
58
|
+
self._underlying.level = level
|
59
|
+
|
60
|
+
@property
|
61
|
+
def formatter(self):
|
62
|
+
return self._underlying.formatter
|
63
|
+
|
64
|
+
@formatter.setter
|
65
|
+
def formatter(self, formatter):
|
66
|
+
self._underlying.formatter = formatter
|
67
|
+
|
68
|
+
def createLock(self):
|
69
|
+
self._underlying.createLock()
|
70
|
+
|
71
|
+
def acquire(self):
|
72
|
+
self._underlying.acquire()
|
73
|
+
|
74
|
+
def release(self):
|
75
|
+
self._underlying.release()
|
76
|
+
|
77
|
+
def setLevel(self, level):
|
78
|
+
self._underlying.setLevel(level)
|
79
|
+
|
80
|
+
def format(self, record):
|
81
|
+
return self._underlying.format(record)
|
82
|
+
|
83
|
+
def emit(self, record):
|
84
|
+
self._underlying.emit(record)
|
85
|
+
|
86
|
+
def handle(self, record):
|
87
|
+
return self._underlying.handle(record)
|
88
|
+
|
89
|
+
def setFormatter(self, fmt):
|
90
|
+
self._underlying.setFormatter(fmt)
|
91
|
+
|
92
|
+
def flush(self):
|
93
|
+
self._underlying.flush()
|
94
|
+
|
95
|
+
def close(self):
|
96
|
+
self._underlying.close()
|
97
|
+
|
98
|
+
def handleError(self, record):
|
99
|
+
self._underlying.handleError(record)
|
omlish/logs/standard.py
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
# ruff: noqa: N802 UP006 UP007
|
2
|
+
# @omlish-lite
|
3
|
+
"""
|
4
|
+
TODO:
|
5
|
+
- structured
|
6
|
+
- prefixed
|
7
|
+
- debug
|
8
|
+
- optional noisy? noisy will never be lite - some kinda configure_standard callback mechanism?
|
9
|
+
"""
|
10
|
+
import contextlib
|
11
|
+
import datetime
|
12
|
+
import logging
|
13
|
+
import typing as ta
|
14
|
+
|
15
|
+
from .filters import TidLogFilter
|
16
|
+
from .json import JsonLogFormatter
|
17
|
+
from .proxy import ProxyLogHandler
|
18
|
+
|
19
|
+
|
20
|
+
##
|
21
|
+
|
22
|
+
|
23
|
+
STANDARD_LOG_FORMAT_PARTS = [
|
24
|
+
('asctime', '%(asctime)-15s'),
|
25
|
+
('process', 'pid=%(process)-6s'),
|
26
|
+
('thread', 'tid=%(thread)x'),
|
27
|
+
('levelname', '%(levelname)s'),
|
28
|
+
('name', '%(name)s'),
|
29
|
+
('separator', '::'),
|
30
|
+
('message', '%(message)s'),
|
31
|
+
]
|
32
|
+
|
33
|
+
|
34
|
+
class StandardLogFormatter(logging.Formatter):
|
35
|
+
@staticmethod
|
36
|
+
def build_log_format(parts: ta.Iterable[ta.Tuple[str, str]]) -> str:
|
37
|
+
return ' '.join(v for k, v in parts)
|
38
|
+
|
39
|
+
converter = datetime.datetime.fromtimestamp # type: ignore
|
40
|
+
|
41
|
+
def formatTime(self, record, datefmt=None):
|
42
|
+
ct = self.converter(record.created) # type: ignore
|
43
|
+
if datefmt:
|
44
|
+
return ct.strftime(datefmt) # noqa
|
45
|
+
else:
|
46
|
+
t = ct.strftime('%Y-%m-%d %H:%M:%S')
|
47
|
+
return '%s.%03d' % (t, record.msecs) # noqa
|
48
|
+
|
49
|
+
|
50
|
+
##
|
51
|
+
|
52
|
+
|
53
|
+
class StandardConfiguredLogHandler(ProxyLogHandler):
|
54
|
+
def __init_subclass__(cls, **kwargs):
|
55
|
+
raise TypeError('This class serves only as a marker and should not be subclassed.')
|
56
|
+
|
57
|
+
|
58
|
+
##
|
59
|
+
|
60
|
+
|
61
|
+
@contextlib.contextmanager
|
62
|
+
def _locking_logging_module_lock() -> ta.Iterator[None]:
|
63
|
+
if hasattr(logging, '_acquireLock'):
|
64
|
+
logging._acquireLock() # noqa
|
65
|
+
try:
|
66
|
+
yield
|
67
|
+
finally:
|
68
|
+
logging._releaseLock() # type: ignore # noqa
|
69
|
+
|
70
|
+
elif hasattr(logging, '_lock'):
|
71
|
+
# https://github.com/python/cpython/commit/74723e11109a320e628898817ab449b3dad9ee96
|
72
|
+
with logging._lock: # noqa
|
73
|
+
yield
|
74
|
+
|
75
|
+
else:
|
76
|
+
raise Exception("Can't find lock in logging module")
|
77
|
+
|
78
|
+
|
79
|
+
def configure_standard_logging(
|
80
|
+
level: ta.Union[int, str] = logging.INFO,
|
81
|
+
*,
|
82
|
+
json: bool = False,
|
83
|
+
target: ta.Optional[logging.Logger] = None,
|
84
|
+
force: bool = False,
|
85
|
+
handler_factory: ta.Optional[ta.Callable[[], logging.Handler]] = None,
|
86
|
+
) -> ta.Optional[StandardConfiguredLogHandler]:
|
87
|
+
with _locking_logging_module_lock():
|
88
|
+
if target is None:
|
89
|
+
target = logging.root
|
90
|
+
|
91
|
+
#
|
92
|
+
|
93
|
+
if not force:
|
94
|
+
if any(isinstance(h, StandardConfiguredLogHandler) for h in list(target.handlers)):
|
95
|
+
return None
|
96
|
+
|
97
|
+
#
|
98
|
+
|
99
|
+
if handler_factory is not None:
|
100
|
+
handler = handler_factory()
|
101
|
+
else:
|
102
|
+
handler = logging.StreamHandler()
|
103
|
+
|
104
|
+
#
|
105
|
+
|
106
|
+
formatter: logging.Formatter
|
107
|
+
if json:
|
108
|
+
formatter = JsonLogFormatter()
|
109
|
+
else:
|
110
|
+
formatter = StandardLogFormatter(StandardLogFormatter.build_log_format(STANDARD_LOG_FORMAT_PARTS))
|
111
|
+
handler.setFormatter(formatter)
|
112
|
+
|
113
|
+
#
|
114
|
+
|
115
|
+
handler.addFilter(TidLogFilter())
|
116
|
+
|
117
|
+
#
|
118
|
+
|
119
|
+
target.addHandler(handler)
|
120
|
+
|
121
|
+
#
|
122
|
+
|
123
|
+
if level is not None:
|
124
|
+
target.setLevel(level)
|
125
|
+
|
126
|
+
#
|
127
|
+
|
128
|
+
return StandardConfiguredLogHandler(handler)
|
omlish/logs/utils.py
CHANGED
@@ -2,10 +2,10 @@ import functools
|
|
2
2
|
import logging
|
3
3
|
|
4
4
|
|
5
|
-
|
5
|
+
_log = logging.getLogger(__name__)
|
6
6
|
|
7
7
|
|
8
|
-
def error_logging(log=
|
8
|
+
def error_logging(log=_log): # noqa
|
9
9
|
def outer(fn):
|
10
10
|
@functools.wraps(fn)
|
11
11
|
def inner(*args, **kwargs):
|