omlish 0.0.0.dev132__py3-none-any.whl → 0.0.0.dev177__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- omlish/.manifests.json +265 -7
- omlish/__about__.py +7 -5
- 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.dev132.dist-info → omlish-0.0.0.dev177.dist-info}/METADATA +13 -11
- {omlish-0.0.0.dev132.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.dev132.dist-info → omlish-0.0.0.dev177.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev132.dist-info → omlish-0.0.0.dev177.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev132.dist-info → omlish-0.0.0.dev177.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev132.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):
|