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.
Files changed (210) hide show
  1. omlish/.manifests.json +265 -7
  2. omlish/__about__.py +5 -3
  3. omlish/antlr/_runtime/__init__.py +0 -22
  4. omlish/antlr/_runtime/_all.py +24 -0
  5. omlish/antlr/_runtime/atn/ParserATNSimulator.py +1 -1
  6. omlish/antlr/_runtime/dfa/DFASerializer.py +1 -1
  7. omlish/antlr/_runtime/error/DiagnosticErrorListener.py +2 -1
  8. omlish/antlr/_runtime/xpath/XPath.py +7 -1
  9. omlish/antlr/_runtime/xpath/XPathLexer.py +1 -1
  10. omlish/antlr/delimit.py +106 -0
  11. omlish/antlr/dot.py +31 -0
  12. omlish/antlr/errors.py +11 -0
  13. omlish/antlr/input.py +96 -0
  14. omlish/antlr/parsing.py +19 -0
  15. omlish/antlr/runtime.py +102 -0
  16. omlish/antlr/utils.py +38 -0
  17. omlish/argparse/all.py +45 -0
  18. omlish/{argparse.py → argparse/cli.py} +112 -107
  19. omlish/asyncs/__init__.py +0 -35
  20. omlish/asyncs/all.py +35 -0
  21. omlish/asyncs/asyncio/all.py +7 -0
  22. omlish/asyncs/asyncio/channels.py +40 -0
  23. omlish/asyncs/asyncio/streams.py +45 -0
  24. omlish/asyncs/asyncio/subprocesses.py +238 -0
  25. omlish/asyncs/asyncio/timeouts.py +16 -0
  26. omlish/asyncs/bluelet/LICENSE +6 -0
  27. omlish/asyncs/bluelet/all.py +67 -0
  28. omlish/asyncs/bluelet/api.py +23 -0
  29. omlish/asyncs/bluelet/core.py +178 -0
  30. omlish/asyncs/bluelet/events.py +78 -0
  31. omlish/asyncs/bluelet/files.py +80 -0
  32. omlish/asyncs/bluelet/runner.py +416 -0
  33. omlish/asyncs/bluelet/sockets.py +214 -0
  34. omlish/bootstrap/sys.py +3 -3
  35. omlish/cached.py +2 -2
  36. omlish/check.py +49 -460
  37. omlish/codecs/__init__.py +72 -0
  38. omlish/codecs/base.py +106 -0
  39. omlish/codecs/bytes.py +119 -0
  40. omlish/codecs/chain.py +23 -0
  41. omlish/codecs/funcs.py +39 -0
  42. omlish/codecs/registry.py +139 -0
  43. omlish/codecs/standard.py +4 -0
  44. omlish/codecs/text.py +217 -0
  45. omlish/collections/cache/impl.py +50 -57
  46. omlish/collections/coerce.py +1 -0
  47. omlish/collections/mappings.py +1 -1
  48. omlish/configs/flattening.py +1 -1
  49. omlish/defs.py +1 -1
  50. omlish/diag/_pycharm/runhack.py +8 -2
  51. omlish/diag/procfs.py +8 -8
  52. omlish/docker/__init__.py +0 -36
  53. omlish/docker/all.py +31 -0
  54. omlish/docker/consts.py +4 -0
  55. omlish/{lite/docker.py → docker/detect.py} +18 -0
  56. omlish/docker/{helpers.py → timebomb.py} +0 -21
  57. omlish/formats/cbor.py +31 -0
  58. omlish/formats/cloudpickle.py +31 -0
  59. omlish/formats/codecs.py +93 -0
  60. omlish/formats/json/codecs.py +29 -0
  61. omlish/formats/json/delimted.py +4 -0
  62. omlish/formats/json/stream/errors.py +2 -0
  63. omlish/formats/json/stream/lex.py +12 -6
  64. omlish/formats/json/stream/parse.py +38 -22
  65. omlish/formats/json5.py +31 -0
  66. omlish/formats/pickle.py +31 -0
  67. omlish/formats/repr.py +25 -0
  68. omlish/formats/toml.py +17 -0
  69. omlish/formats/yaml.py +25 -0
  70. omlish/funcs/__init__.py +0 -0
  71. omlish/{genmachine.py → funcs/genmachine.py} +5 -4
  72. omlish/{matchfns.py → funcs/match.py} +1 -1
  73. omlish/funcs/pairs.py +215 -0
  74. omlish/http/__init__.py +0 -48
  75. omlish/http/all.py +48 -0
  76. omlish/http/coro/__init__.py +0 -0
  77. omlish/{lite/fdio/corohttp.py → http/coro/fdio.py} +21 -19
  78. omlish/{lite/http/coroserver.py → http/coro/server.py} +20 -21
  79. omlish/{lite/http → http}/handlers.py +3 -2
  80. omlish/{lite/http → http}/parsing.py +1 -0
  81. omlish/http/sessions.py +1 -1
  82. omlish/{lite/http → http}/versions.py +1 -0
  83. omlish/inject/managed.py +2 -2
  84. omlish/io/__init__.py +0 -3
  85. omlish/{lite/io.py → io/buffers.py} +8 -9
  86. omlish/io/compress/__init__.py +9 -0
  87. omlish/io/compress/abc.py +104 -0
  88. omlish/io/compress/adapters.py +148 -0
  89. omlish/io/compress/base.py +24 -0
  90. omlish/io/compress/brotli.py +47 -0
  91. omlish/io/compress/bz2.py +61 -0
  92. omlish/io/compress/codecs.py +78 -0
  93. omlish/io/compress/gzip.py +350 -0
  94. omlish/io/compress/lz4.py +91 -0
  95. omlish/io/compress/lzma.py +81 -0
  96. omlish/io/compress/snappy.py +34 -0
  97. omlish/io/compress/zlib.py +74 -0
  98. omlish/io/compress/zstd.py +44 -0
  99. omlish/io/fdio/__init__.py +1 -0
  100. omlish/{lite → io}/fdio/handlers.py +5 -5
  101. omlish/{lite → io}/fdio/kqueue.py +8 -8
  102. omlish/{lite → io}/fdio/manager.py +7 -7
  103. omlish/{lite → io}/fdio/pollers.py +13 -13
  104. omlish/io/generators/__init__.py +56 -0
  105. omlish/io/generators/consts.py +1 -0
  106. omlish/io/generators/direct.py +13 -0
  107. omlish/io/generators/readers.py +189 -0
  108. omlish/io/generators/stepped.py +191 -0
  109. omlish/io/pyio.py +5 -2
  110. omlish/iterators/__init__.py +24 -0
  111. omlish/iterators/iterators.py +132 -0
  112. omlish/iterators/recipes.py +18 -0
  113. omlish/iterators/tools.py +96 -0
  114. omlish/iterators/unique.py +67 -0
  115. omlish/lang/__init__.py +13 -1
  116. omlish/lang/functions.py +11 -2
  117. omlish/lang/generators.py +243 -0
  118. omlish/lang/iterables.py +46 -49
  119. omlish/lang/maybes.py +4 -4
  120. omlish/lite/cached.py +39 -6
  121. omlish/lite/check.py +438 -75
  122. omlish/lite/contextmanagers.py +17 -4
  123. omlish/lite/dataclasses.py +42 -0
  124. omlish/lite/inject.py +28 -45
  125. omlish/lite/logs.py +0 -270
  126. omlish/lite/marshal.py +309 -144
  127. omlish/lite/pycharm.py +47 -0
  128. omlish/lite/reflect.py +33 -0
  129. omlish/lite/resources.py +8 -0
  130. omlish/lite/runtime.py +4 -4
  131. omlish/lite/shlex.py +12 -0
  132. omlish/lite/socketserver.py +2 -2
  133. omlish/lite/strings.py +31 -0
  134. omlish/logs/__init__.py +0 -32
  135. omlish/logs/{_abc.py → abc.py} +0 -1
  136. omlish/logs/all.py +37 -0
  137. omlish/logs/{formatters.py → color.py} +1 -2
  138. omlish/logs/configs.py +7 -38
  139. omlish/logs/filters.py +10 -0
  140. omlish/logs/handlers.py +4 -1
  141. omlish/logs/json.py +56 -0
  142. omlish/logs/proxy.py +99 -0
  143. omlish/logs/standard.py +128 -0
  144. omlish/logs/utils.py +2 -2
  145. omlish/manifests/__init__.py +2 -0
  146. omlish/manifests/load.py +209 -0
  147. omlish/manifests/types.py +17 -0
  148. omlish/marshal/base.py +1 -1
  149. omlish/marshal/factories.py +1 -1
  150. omlish/marshal/forbidden.py +1 -1
  151. omlish/marshal/iterables.py +1 -1
  152. omlish/marshal/literals.py +50 -0
  153. omlish/marshal/mappings.py +1 -1
  154. omlish/marshal/maybes.py +1 -1
  155. omlish/marshal/standard.py +5 -1
  156. omlish/marshal/unions.py +1 -1
  157. omlish/os/__init__.py +0 -0
  158. omlish/os/atomics.py +205 -0
  159. omlish/os/deathsig.py +23 -0
  160. omlish/{os.py → os/files.py} +0 -9
  161. omlish/{lite → os}/journald.py +2 -1
  162. omlish/os/linux.py +484 -0
  163. omlish/os/paths.py +36 -0
  164. omlish/{lite → os}/pidfile.py +1 -0
  165. omlish/os/sizes.py +9 -0
  166. omlish/reflect/__init__.py +3 -0
  167. omlish/reflect/subst.py +2 -1
  168. omlish/reflect/types.py +126 -44
  169. omlish/secrets/pwhash.py +1 -1
  170. omlish/secrets/subprocesses.py +3 -1
  171. omlish/specs/jsonrpc/marshal.py +1 -1
  172. omlish/specs/openapi/marshal.py +1 -1
  173. omlish/sql/alchemy/asyncs.py +1 -1
  174. omlish/sql/queries/__init__.py +9 -1
  175. omlish/sql/queries/building.py +3 -0
  176. omlish/sql/queries/exprs.py +10 -27
  177. omlish/sql/queries/idents.py +48 -10
  178. omlish/sql/queries/names.py +80 -13
  179. omlish/sql/queries/params.py +64 -0
  180. omlish/sql/queries/rendering.py +1 -1
  181. omlish/subprocesses.py +340 -0
  182. omlish/term.py +29 -14
  183. omlish/testing/pytest/marks.py +2 -2
  184. omlish/testing/pytest/plugins/asyncs.py +6 -1
  185. omlish/testing/pytest/plugins/logging.py +1 -1
  186. omlish/testing/pytest/plugins/switches.py +1 -1
  187. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/METADATA +7 -5
  188. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/RECORD +200 -117
  189. omlish/fnpairs.py +0 -496
  190. omlish/formats/json/cli/__main__.py +0 -11
  191. omlish/formats/json/cli/cli.py +0 -298
  192. omlish/formats/json/cli/formats.py +0 -71
  193. omlish/formats/json/cli/io.py +0 -74
  194. omlish/formats/json/cli/parsing.py +0 -82
  195. omlish/formats/json/cli/processing.py +0 -48
  196. omlish/formats/json/cli/rendering.py +0 -92
  197. omlish/iterators.py +0 -300
  198. omlish/lite/subprocesses.py +0 -130
  199. /omlish/{formats/json/cli → argparse}/__init__.py +0 -0
  200. /omlish/{lite/fdio → asyncs/asyncio}/__init__.py +0 -0
  201. /omlish/asyncs/{asyncio.py → asyncio/asyncio.py} +0 -0
  202. /omlish/{lite/http → asyncs/bluelet}/__init__.py +0 -0
  203. /omlish/collections/{_abc.py → abc.py} +0 -0
  204. /omlish/{fnpipes.py → funcs/pipes.py} +0 -0
  205. /omlish/io/{_abc.py → abc.py} +0 -0
  206. /omlish/sql/{_abc.py → abc.py} +0 -0
  207. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/LICENSE +0 -0
  208. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/WHEEL +0 -0
  209. {omlish-0.0.0.dev133.dist-info → omlish-0.0.0.dev177.dist-info}/entry_points.txt +0 -0
  210. {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__()))
@@ -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
- REQUIRED_PYTHON_VERSION = (3, 8)
12
+ LITE_REQUIRED_PYTHON_VERSION = (3, 8)
13
13
 
14
14
 
15
- def check_runtime_version() -> None:
16
- if sys.version_info < REQUIRED_PYTHON_VERSION:
17
- raise OSError(f'Requires python {REQUIRED_PYTHON_VERSION}, got {sys.version_info} from {sys.executable}') # noqa
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
@@ -0,0 +1,12 @@
1
+ import shlex
2
+
3
+
4
+ def shlex_needs_quote(s: str) -> bool:
5
+ return bool(s) and len(list(shlex.shlex(s))) > 1
6
+
7
+
8
+ def shlex_maybe_quote(s: str) -> str:
9
+ if shlex_needs_quote(s):
10
+ return shlex.quote(s)
11
+ else:
12
+ return s
@@ -3,7 +3,7 @@ import socket
3
3
  import socketserver
4
4
  import typing as ta
5
5
 
6
- from omlish.lite.check import check_not_none
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 = check_not_none(self.socket_handler_factory)(
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
- )
@@ -1,7 +1,6 @@
1
1
  # ruff: noqa: A002
2
2
  # ruff: noqa: N802
3
3
  # ruff: noqa: N815
4
-
5
4
  import types
6
5
  import typing as ta
7
6
 
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 ..lite.logs import StandardLogFormatter
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
@@ -0,0 +1,10 @@
1
+ # ruff: noqa: UP006 UP007
2
+ # @omlish-lite
3
+ import logging
4
+ import threading
5
+
6
+
7
+ class TidLogFilter(logging.Filter):
8
+ def filter(self, record):
9
+ record.tid = threading.get_native_id()
10
+ return True
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: list[logging.LogRecord] = []
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)
@@ -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
- log = logging.getLogger(__name__)
5
+ _log = logging.getLogger(__name__)
6
6
 
7
7
 
8
- def error_logging(log=log): # noqa
8
+ def error_logging(log=_log): # noqa
9
9
  def outer(fn):
10
10
  @functools.wraps(fn)
11
11
  def inner(*args, **kwargs):
@@ -0,0 +1,2 @@
1
+ # @omlish-lite
2
+ from .types import Manifest # noqa