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.
Files changed (210) hide show
  1. omlish/.manifests.json +265 -7
  2. omlish/__about__.py +7 -5
  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.dev132.dist-info → omlish-0.0.0.dev177.dist-info}/METADATA +13 -11
  188. {omlish-0.0.0.dev132.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.dev132.dist-info → omlish-0.0.0.dev177.dist-info}/LICENSE +0 -0
  208. {omlish-0.0.0.dev132.dist-info → omlish-0.0.0.dev177.dist-info}/WHEEL +0 -0
  209. {omlish-0.0.0.dev132.dist-info → omlish-0.0.0.dev177.dist-info}/entry_points.txt +0 -0
  210. {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__()))
@@ -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