omlish 0.0.0.dev423__py3-none-any.whl → 0.0.0.dev425__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 (96) hide show
  1. omlish/__about__.py +3 -3
  2. omlish/dataclasses/impl/configs.py +2 -1
  3. omlish/formats/json/stream/lexing.py +8 -0
  4. omlish/formats/json/stream/utils.py +3 -0
  5. omlish/formats/json5/streams.py +22 -0
  6. omlish/lang/__init__.py +3 -1
  7. omlish/lang/asyncs.py +12 -0
  8. omlish/lang/functions.py +0 -11
  9. omlish/lang/lazyglobals.py +27 -5
  10. omlish/lang/maysync.py +2 -2
  11. omlish/lifecycles/contextmanagers.py +1 -2
  12. omlish/lifecycles/controller.py +1 -2
  13. omlish/lite/asyncs.py +15 -0
  14. omlish/lite/timing.py +2 -2
  15. omlish/logs/all.py +23 -34
  16. omlish/logs/base.py +248 -0
  17. omlish/logs/callers.py +21 -15
  18. omlish/logs/infos.py +105 -0
  19. omlish/logs/levels.py +64 -0
  20. omlish/logs/protocols.py +31 -0
  21. omlish/logs/standard.py +12 -11
  22. omlish/logs/std/adapters.py +41 -0
  23. omlish/logs/std/configs.py +29 -0
  24. omlish/logs/{filters.py → std/filters.py} +1 -1
  25. omlish/logs/{handlers.py → std/handlers.py} +1 -1
  26. omlish/logs/{json.py → std/json.py} +2 -2
  27. omlish/logs/{proxy.py → std/proxy.py} +3 -3
  28. omlish/logs/std/records.py +286 -0
  29. omlish/logs/times.py +89 -0
  30. omlish/logs/typed/bindings.py +24 -0
  31. omlish/logs/utils.py +60 -4
  32. omlish/logs/warnings.py +8 -0
  33. omlish/manifests/loading.py +1 -1
  34. omlish/os/journald.py +3 -3
  35. omlish/testing/pytest/plugins/skips.py +0 -4
  36. {omlish-0.0.0.dev423.dist-info → omlish-0.0.0.dev425.dist-info}/METADATA +1 -1
  37. {omlish-0.0.0.dev423.dist-info → omlish-0.0.0.dev425.dist-info}/RECORD +44 -86
  38. omlish/defs.py +0 -216
  39. omlish/dispatch/_dispatch2.py +0 -69
  40. omlish/dispatch/_dispatch3.py +0 -108
  41. omlish/dynamic.py +0 -219
  42. omlish/formats/json/Json.g4 +0 -77
  43. omlish/formats/json/_antlr/JsonLexer.py +0 -109
  44. omlish/formats/json/_antlr/JsonListener.py +0 -61
  45. omlish/formats/json/_antlr/JsonParser.py +0 -457
  46. omlish/formats/json/_antlr/JsonVisitor.py +0 -42
  47. omlish/io/trampoline.py +0 -289
  48. omlish/logs/abc.py +0 -319
  49. omlish/logs/color.py +0 -27
  50. omlish/logs/configs.py +0 -29
  51. omlish/logs/protocol.py +0 -218
  52. omlish/logs/timing.py +0 -58
  53. omlish/specs/irc/__init__.py +0 -0
  54. omlish/specs/irc/messages/__init__.py +0 -0
  55. omlish/specs/irc/messages/base.py +0 -49
  56. omlish/specs/irc/messages/formats.py +0 -92
  57. omlish/specs/irc/messages/messages.py +0 -774
  58. omlish/specs/irc/messages/parsing.py +0 -98
  59. omlish/specs/irc/numerics/__init__.py +0 -0
  60. omlish/specs/irc/numerics/formats.py +0 -97
  61. omlish/specs/irc/numerics/numerics.py +0 -865
  62. omlish/specs/irc/numerics/types.py +0 -59
  63. omlish/specs/irc/protocol/LICENSE +0 -11
  64. omlish/specs/irc/protocol/__init__.py +0 -61
  65. omlish/specs/irc/protocol/consts.py +0 -6
  66. omlish/specs/irc/protocol/errors.py +0 -30
  67. omlish/specs/irc/protocol/message.py +0 -21
  68. omlish/specs/irc/protocol/nuh.py +0 -55
  69. omlish/specs/irc/protocol/parsing.py +0 -158
  70. omlish/specs/irc/protocol/rendering.py +0 -153
  71. omlish/specs/irc/protocol/tags.py +0 -102
  72. omlish/specs/irc/protocol/utils.py +0 -30
  73. omlish/specs/proto/Protobuf3.g4 +0 -396
  74. omlish/specs/proto/__init__.py +0 -0
  75. omlish/specs/proto/_antlr/Protobuf3Lexer.py +0 -340
  76. omlish/specs/proto/_antlr/Protobuf3Listener.py +0 -448
  77. omlish/specs/proto/_antlr/Protobuf3Parser.py +0 -3909
  78. omlish/specs/proto/_antlr/Protobuf3Visitor.py +0 -257
  79. omlish/specs/proto/_antlr/__init__.py +0 -0
  80. omlish/specs/proto/nodes.py +0 -54
  81. omlish/specs/proto/parsing.py +0 -97
  82. omlish/sql/parsing/Minisql.g4 +0 -292
  83. omlish/sql/parsing/__init__.py +0 -0
  84. omlish/sql/parsing/_antlr/MinisqlLexer.py +0 -322
  85. omlish/sql/parsing/_antlr/MinisqlListener.py +0 -511
  86. omlish/sql/parsing/_antlr/MinisqlParser.py +0 -3763
  87. omlish/sql/parsing/_antlr/MinisqlVisitor.py +0 -292
  88. omlish/sql/parsing/_antlr/__init__.py +0 -0
  89. omlish/sql/parsing/parsing.py +0 -119
  90. /omlish/{.manifests.json → .omlish-manifests.json} +0 -0
  91. /omlish/{formats/json/_antlr → logs/std}/__init__.py +0 -0
  92. /omlish/logs/{noisy.py → std/noisy.py} +0 -0
  93. {omlish-0.0.0.dev423.dist-info → omlish-0.0.0.dev425.dist-info}/WHEEL +0 -0
  94. {omlish-0.0.0.dev423.dist-info → omlish-0.0.0.dev425.dist-info}/entry_points.txt +0 -0
  95. {omlish-0.0.0.dev423.dist-info → omlish-0.0.0.dev425.dist-info}/licenses/LICENSE +0 -0
  96. {omlish-0.0.0.dev423.dist-info → omlish-0.0.0.dev425.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,286 @@
1
+ # ruff: noqa: UP006 UP007 UP045
2
+ # @omlish-lite
3
+ import collections.abc
4
+ import logging
5
+ import sys
6
+ import typing as ta
7
+
8
+ from ..base import LoggingContext
9
+ from ..base import LoggingExcInfoTuple
10
+ from ..warnings import LoggingSetupWarning
11
+
12
+
13
+ ##
14
+
15
+
16
+ # Ref:
17
+ # - https://docs.python.org/3/library/logging.html#logrecord-attributes
18
+ #
19
+ # LogRecord:
20
+ # - https://github.com/python/cpython/blob/39b2f82717a69dde7212bc39b673b0f55c99e6a3/Lib/logging/__init__.py#L276 (3.8)
21
+ # - https://github.com/python/cpython/blob/f070f54c5f4a42c7c61d1d5d3b8f3b7203b4a0fb/Lib/logging/__init__.py#L286 (~3.14) # noqa
22
+ #
23
+ # LogRecord.__init__ args:
24
+ # - name: str
25
+ # - level: int
26
+ # - pathname: str - Confusingly referred to as `fn` before the LogRecord ctor. May be empty or "(unknown file)".
27
+ # - lineno: int - May be 0.
28
+ # - msg: str
29
+ # - args: tuple | dict | 1-tuple[dict]
30
+ # - exc_info: LoggingExcInfoTuple | None
31
+ # - func: str | None = None -> funcName
32
+ # - sinfo: str | None = None -> stack_info
33
+ #
34
+ KNOWN_STD_LOGGING_RECORD_ATTRS: ta.Dict[str, ta.Any] = dict(
35
+ # Name of the logger used to log the call. Unmodified by ctor.
36
+ name=str,
37
+
38
+ # The format string passed in the original logging call. Merged with args to produce message, or an arbitrary object
39
+ # (see Using arbitrary objects as messages). Unmodified by ctor.
40
+ msg=str,
41
+
42
+ # The tuple of arguments merged into msg to produce message, or a dict whose values are used for the merge (when
43
+ # there is only one argument, and it is a dictionary). Ctor will transform a 1-tuple containing a Mapping into just
44
+ # the mapping, but is otherwise unmodified.
45
+ args=ta.Union[tuple, dict],
46
+
47
+ #
48
+
49
+ # Text logging level for the message ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'). Set to
50
+ # `getLevelName(level)`.
51
+ levelname=str,
52
+
53
+ # Numeric logging level for the message (DEBUG, INFO, WARNING, ERROR, CRITICAL). Unmodified by ctor.
54
+ levelno=int,
55
+
56
+ #
57
+
58
+ # Full pathname of the source file where the logging call was issued (if available). Unmodified by ctor. May default
59
+ # to "(unknown file)" by Logger.findCaller / Logger._log.
60
+ pathname=str,
61
+
62
+ # Filename portion of pathname. Set to `os.path.basename(pathname)` if successful, otherwise defaults to pathname.
63
+ filename=str,
64
+
65
+ # Module (name portion of filename). Set to `os.path.splitext(filename)[0]`, otherwise defaults to
66
+ # "Unknown module".
67
+ module=str,
68
+
69
+ #
70
+
71
+ # Exception tuple (à la sys.exc_info) or, if no exception has occurred, None. Unmodified by ctor.
72
+ exc_info=ta.Optional[LoggingExcInfoTuple],
73
+
74
+ # Used to cache the traceback text. Simply set to None by ctor, later set by Formatter.format.
75
+ exc_text=ta.Optional[str],
76
+
77
+ #
78
+
79
+ # Stack frame information (where available) from the bottom of the stack in the current thread, up to and including
80
+ # the stack frame of the logging call which resulted in the creation of this record. Set by ctor to `sinfo` arg,
81
+ # unmodified. Mostly set, if requested, by `Logger.findCaller`, to `traceback.print_stack(f)`, but prepended with
82
+ # the literal "Stack (most recent call last):\n", and stripped of exactly one trailing `\n` if present.
83
+ stack_info=ta.Optional[str],
84
+
85
+ # Source line number where the logging call was issued (if available). Unmodified by ctor. May default to 0 by
86
+ # Logger.findCaller / Logger._log.
87
+ lineno=int,
88
+
89
+ # Name of function containing the logging call. Set by ctor to `func` arg, unmodified. May default to
90
+ # "(unknown function)" by Logger.findCaller / Logger._log.
91
+ funcName=str,
92
+
93
+ #
94
+
95
+ # Time when the LogRecord was created. Set to `time.time_ns() / 1e9` for >=3.13.0b1, otherwise simply `time.time()`.
96
+ #
97
+ # See:
98
+ # - https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
99
+ # - https://github.com/python/cpython/commit/1500a23f33f5a6d052ff1ef6383d9839928b8ff1
100
+ #
101
+ created=float,
102
+
103
+ # Millisecond portion of the time when the LogRecord was created.
104
+ msecs=float,
105
+
106
+ # Time in milliseconds when the LogRecord was created, relative to the time the logging module was loaded.
107
+ relativeCreated=float,
108
+
109
+ #
110
+
111
+ # Thread ID if available, and `logging.logThreads` is truthy.
112
+ thread=ta.Optional[int],
113
+
114
+ # Thread name if available, and `logging.logThreads` is truthy.
115
+ threadName=ta.Optional[str],
116
+
117
+ #
118
+
119
+ # Process name if available. Set to None if `logging.logMultiprocessing` is not truthy. Otherwise, set to
120
+ # 'MainProcess', then `sys.modules.get('multiprocessing').current_process().name` if that works, otherwise remains
121
+ # as 'MainProcess'.
122
+ #
123
+ # As noted by stdlib:
124
+ #
125
+ # Errors may occur if multiprocessing has not finished loading yet - e.g. if a custom import hook causes
126
+ # third-party code to run when multiprocessing calls import. See issue 8200 for an example
127
+ #
128
+ processName=ta.Optional[str],
129
+
130
+ # Process ID if available - that is, if `hasattr(os, 'getpid')` - and `logging.logProcesses` is truthy, otherwise
131
+ # None.
132
+ process=ta.Optional[int],
133
+
134
+ #
135
+
136
+ # Absent <3.12, otherwise asyncio.Task name if available, and `logging.logAsyncioTasks` is truthy. Set to
137
+ # `sys.modules.get('asyncio').current_task().get_name()`, otherwise None.
138
+ taskName=ta.Optional[str],
139
+ )
140
+
141
+ KNOWN_STD_LOGGING_RECORD_ATTR_SET: ta.FrozenSet[str] = frozenset(KNOWN_STD_LOGGING_RECORD_ATTRS)
142
+
143
+
144
+ # Formatter:
145
+ # - https://github.com/python/cpython/blob/39b2f82717a69dde7212bc39b673b0f55c99e6a3/Lib/logging/__init__.py#L514 (3.8)
146
+ # - https://github.com/python/cpython/blob/f070f54c5f4a42c7c61d1d5d3b8f3b7203b4a0fb/Lib/logging/__init__.py#L554 (~3.14) # noqa
147
+ #
148
+ KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS: ta.Dict[str, ta.Any] = dict(
149
+ # The logged message, computed as msg % args. Set to `record.getMessage()`.
150
+ message=str,
151
+
152
+ # Human-readable time when the LogRecord was created. By default this is of the form '2003-07-08 16:49:45,896' (the
153
+ # numbers after the comma are millisecond portion of the time). Set to `self.formatTime(record, self.datefmt)` if
154
+ # `self.usesTime()`, otherwise unset.
155
+ asctime=str,
156
+
157
+ # Used to cache the traceback text. If unset (falsey) on the record and `exc_info` is truthy, set to
158
+ # `self.formatException(record.exc_info)` - otherwise unmodified.
159
+ exc_text=ta.Optional[str],
160
+ )
161
+
162
+ KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTR_SET: ta.FrozenSet[str] = frozenset(KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS)
163
+
164
+
165
+ ##
166
+
167
+
168
+ class UnknownStdLoggingRecordAttrsWarning(LoggingSetupWarning):
169
+ pass
170
+
171
+
172
+ def _check_std_logging_record_attrs() -> None:
173
+ rec_dct = dict(logging.makeLogRecord({}).__dict__)
174
+
175
+ if (unk_rec_fields := frozenset(rec_dct) - KNOWN_STD_LOGGING_RECORD_ATTR_SET):
176
+ import warnings # noqa
177
+
178
+ warnings.warn(
179
+ f'Unknown log record attrs detected: {sorted(unk_rec_fields)!r}',
180
+ UnknownStdLoggingRecordAttrsWarning,
181
+ )
182
+
183
+
184
+ _check_std_logging_record_attrs()
185
+
186
+
187
+ ##
188
+
189
+
190
+ class LoggingContextLogRecord(logging.LogRecord):
191
+ _SHOULD_ADD_TASK_NAME: ta.ClassVar[bool] = sys.version_info >= (3, 12)
192
+
193
+ _UNKNOWN_PATH_NAME: ta.ClassVar[str] = '(unknown file)'
194
+ _UNKNOWN_FUNC_NAME: ta.ClassVar[str] = '(unknown function)'
195
+ _UNKNOWN_MODULE: ta.ClassVar[str] = 'Unknown module'
196
+
197
+ _STACK_INFO_PREFIX: ta.ClassVar[str] = 'Stack (most recent call last):\n'
198
+
199
+ def __init__( # noqa
200
+ self,
201
+ # name,
202
+ # level,
203
+ # pathname,
204
+ # lineno,
205
+ # msg,
206
+ # args,
207
+ # exc_info,
208
+ # func=None,
209
+ # sinfo=None,
210
+ # **kwargs,
211
+ *,
212
+ name: str,
213
+ msg: str,
214
+ args: ta.Union[tuple, dict],
215
+
216
+ _logging_context: LoggingContext,
217
+ ) -> None:
218
+ ctx = _logging_context
219
+
220
+ self.name: str = name
221
+
222
+ self.msg: str = msg
223
+
224
+ # https://github.com/python/cpython/blob/e709361fc87d0d9ab9c58033a0a7f2fef0ad43d2/Lib/logging/__init__.py#L307
225
+ if args and len(args) == 1 and isinstance(args[0], collections.abc.Mapping) and args[0]:
226
+ args = args[0] # type: ignore[assignment]
227
+ self.args: ta.Union[tuple, dict] = args
228
+
229
+ self.levelname: str = logging.getLevelName(ctx.level)
230
+ self.levelno: int = ctx.level
231
+
232
+ if (caller := ctx.caller()) is not None:
233
+ self.pathname: str = caller.file_path
234
+ else:
235
+ self.pathname = self._UNKNOWN_PATH_NAME
236
+
237
+ if (src_file := ctx.source_file()) is not None:
238
+ self.filename: str = src_file.file_name
239
+ self.module: str = src_file.module
240
+ else:
241
+ self.filename = self.pathname
242
+ self.module = self._UNKNOWN_MODULE
243
+
244
+ self.exc_info: ta.Optional[LoggingExcInfoTuple] = ctx.exc_info_tuple
245
+ self.exc_text: ta.Optional[str] = None
246
+
247
+ # If ctx.build_caller() was never called, we simply don't have a stack trace.
248
+ if caller is not None:
249
+ if (sinfo := caller.stack_info) is not None:
250
+ self.stack_info: ta.Optional[str] = '\n'.join([
251
+ self._STACK_INFO_PREFIX,
252
+ sinfo[1:] if sinfo.endswith('\n') else sinfo,
253
+ ])
254
+ else:
255
+ self.stack_info = None
256
+
257
+ self.lineno: int = caller.line_no
258
+ self.funcName: str = caller.name
259
+
260
+ else:
261
+ self.stack_info = None
262
+
263
+ self.lineno = 0
264
+ self.funcName = self._UNKNOWN_FUNC_NAME
265
+
266
+ times = ctx.times
267
+ self.created: float = times.created
268
+ self.msecs: float = times.msecs
269
+ self.relativeCreated: float = times.relative_created
270
+
271
+ thread = ctx.thread
272
+ self.thread: ta.Optional[int] = thread.ident
273
+ self.threadName: ta.Optional[str] = thread.name
274
+
275
+ process = ctx.process
276
+ self.process: ta.Optional[int] = process.pid
277
+
278
+ if (mp := ctx.multiprocessing) is not None:
279
+ self.processName: ta.Optional[str] = mp.process_name
280
+ else:
281
+ self.processName = None
282
+
283
+ if (at := ctx.asyncio_task) is not None:
284
+ self.taskName: ta.Optional[str] = at.name
285
+ else:
286
+ self.taskName = None
omlish/logs/times.py ADDED
@@ -0,0 +1,89 @@
1
+ # ruff: noqa: UP045
2
+ # @omlish-lite
3
+ import logging
4
+ import time
5
+ import typing as ta
6
+
7
+ from .warnings import LoggingSetupWarning
8
+
9
+
10
+ ##
11
+
12
+
13
+ class LoggingTimeFields(ta.NamedTuple):
14
+ """Maps directly to stdlib `logging.LogRecord` fields, and must be kept in sync with it."""
15
+
16
+ created: float
17
+ msecs: float
18
+ relative_created: float
19
+
20
+ @classmethod
21
+ def get_std_start_time_ns(cls) -> int:
22
+ x: ta.Any = logging._startTime # type: ignore[attr-defined] # noqa
23
+
24
+ # Before 3.13.0b1 this will be `time.time()`, a float of seconds. After that, it will be `time.time_ns()`, an
25
+ # int.
26
+ #
27
+ # See:
28
+ # - https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
29
+ #
30
+ if isinstance(x, float):
31
+ return int(x * 1e9)
32
+ else:
33
+ return x
34
+
35
+ @classmethod
36
+ def build(
37
+ cls,
38
+ time_ns: int,
39
+ *,
40
+ start_time_ns: ta.Optional[int] = None,
41
+ ) -> 'LoggingTimeFields':
42
+ # https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
43
+ created = time_ns / 1e9 # ns to float seconds
44
+
45
+ # Get the number of whole milliseconds (0-999) in the fractional part of seconds.
46
+ # Eg: 1_677_903_920_999_998_503 ns --> 999_998_503 ns--> 999 ms
47
+ # Convert to float by adding 0.0 for historical reasons. See gh-89047
48
+ msecs = (time_ns % 1_000_000_000) // 1_000_000 + 0.0
49
+
50
+ # https://github.com/python/cpython/commit/1500a23f33f5a6d052ff1ef6383d9839928b8ff1
51
+ if msecs == 999.0 and int(created) != time_ns // 1_000_000_000:
52
+ # ns -> sec conversion can round up, e.g:
53
+ # 1_677_903_920_999_999_900 ns --> 1_677_903_921.0 sec
54
+ msecs = 0.0
55
+
56
+ if start_time_ns is None:
57
+ start_time_ns = cls.get_std_start_time_ns()
58
+ relative_created = (time_ns - start_time_ns) / 1e6
59
+
60
+ return cls(
61
+ created,
62
+ msecs,
63
+ relative_created,
64
+ )
65
+
66
+
67
+ ##
68
+
69
+
70
+ class UnexpectedLoggingStartTimeWarning(LoggingSetupWarning):
71
+ pass
72
+
73
+
74
+ def _check_logging_start_time() -> None:
75
+ x = LoggingTimeFields.get_std_start_time_ns()
76
+ ns = time.time_ns()
77
+
78
+ if x > ns:
79
+ import warnings # noqa
80
+
81
+ warnings.warn(
82
+ f'Unexpected logging start time detected: '
83
+ f'get_std_start_time_ns={x}, '
84
+ f'time.time_ns()={ns}',
85
+ UnexpectedLoggingStartTimeWarning,
86
+ )
87
+
88
+
89
+ _check_logging_start_time()
@@ -448,6 +448,30 @@ _AS_TYPED_LOGGER_BINDINGS_FIELD_VALUE_DIRECT_TYPES: ta.Tuple[type, ...] = (
448
448
  )
449
449
 
450
450
 
451
+ # @ta.final
452
+ # class TypedLoggerBindingsBuilder:
453
+ # def __init__(
454
+ # self,
455
+ # *,
456
+ # add_default_keys: bool = False,
457
+ # default_key_filter: ta.Optional[ta.Callable[[str], bool]] = None,
458
+ #
459
+ # add_default_values: bool = False,
460
+ # default_value_filter: ta.Optional[ta.Callable[[ta.Type[DefaultTypedLoggerValue]], bool]] = None,
461
+ #
462
+ # value_wrapper: ta.Optional[TypedLoggerValueWrapperFn] = None,
463
+ # ) -> None:
464
+ # self._add_default_keys = add_default_keys
465
+ # self._default_key_filter = default_key_filter
466
+ #
467
+ # self._add_default_values = add_default_values
468
+ # self._default_value_filter = default_value_filter
469
+ #
470
+ # self._value_wrapper = value_wrapper
471
+ #
472
+ # self._lst: ta.List[TypedLoggerBindingItem] = []
473
+
474
+
451
475
  def as_typed_logger_bindings(
452
476
  *objs: CanTypedLoggerBinding,
453
477
 
omlish/logs/utils.py CHANGED
@@ -1,14 +1,17 @@
1
+ # ruff: noqa: UP006 UP007 UP045
2
+ # @omlish-lite
1
3
  import functools
2
4
  import logging
5
+ import time
6
+ import typing as ta
3
7
 
4
-
5
- ##
8
+ from .protocols import LoggerLike
6
9
 
7
10
 
8
- _log = logging.getLogger(__name__)
11
+ ##
9
12
 
10
13
 
11
- def error_logging(log=_log): # noqa
14
+ def error_logging(log): # noqa
12
15
  def outer(fn):
13
16
  @functools.wraps(fn)
14
17
  def inner(*args, **kwargs):
@@ -21,3 +24,56 @@ def error_logging(log=_log): # noqa
21
24
  return inner
22
25
 
23
26
  return outer
27
+
28
+
29
+ ##
30
+
31
+
32
+ class LogTimingContext:
33
+ DEFAULT_LOG: ta.ClassVar[ta.Optional[LoggerLike]] = None
34
+
35
+ class _NOT_SPECIFIED: # noqa
36
+ def __new__(cls, *args, **kwargs): # noqa
37
+ raise TypeError
38
+
39
+ def __init__(
40
+ self,
41
+ description: str,
42
+ *,
43
+ log: ta.Union[LoggerLike, ta.Type[_NOT_SPECIFIED], None] = _NOT_SPECIFIED, # noqa
44
+ level: int = logging.DEBUG,
45
+ ) -> None:
46
+ super().__init__()
47
+
48
+ self._description = description
49
+ if log is self._NOT_SPECIFIED:
50
+ log = self.DEFAULT_LOG # noqa
51
+ self._log: ta.Optional[LoggerLike] = log # type: ignore
52
+ self._level = level
53
+
54
+ def set_description(self, description: str) -> 'LogTimingContext':
55
+ self._description = description
56
+ return self
57
+
58
+ _begin_time: float
59
+ _end_time: float
60
+
61
+ def __enter__(self) -> 'LogTimingContext':
62
+ self._begin_time = time.time()
63
+
64
+ if self._log is not None:
65
+ self._log.log(self._level, f'Begin : {self._description}') # noqa
66
+
67
+ return self
68
+
69
+ def __exit__(self, exc_type, exc_val, exc_tb):
70
+ self._end_time = time.time()
71
+
72
+ if self._log is not None:
73
+ self._log.log(
74
+ self._level,
75
+ f'End : {self._description} - {self._end_time - self._begin_time:0.2f} s elapsed',
76
+ )
77
+
78
+
79
+ log_timing_context = LogTimingContext
@@ -0,0 +1,8 @@
1
+ # @omlish-lite
2
+
3
+
4
+ ##
5
+
6
+
7
+ class LoggingSetupWarning(Warning):
8
+ pass
@@ -432,7 +432,7 @@ class ManifestLoader:
432
432
  return None
433
433
  return t.read_text('utf-8')
434
434
 
435
- MANIFESTS_FILE_NAME: ta.ClassVar[str] = '.manifests.json'
435
+ MANIFESTS_FILE_NAME: ta.ClassVar[str] = '.omlish-manifests.json'
436
436
 
437
437
  @classmethod
438
438
  def _read_package_raw_manifests(cls, package_name: str) -> ta.Optional[ta.Sequence[Manifest]]:
omlish/os/journald.py CHANGED
@@ -79,7 +79,7 @@ SD_LOG_LEVEL_MAP: ta.Mapping[int, int] = {
79
79
  }
80
80
 
81
81
 
82
- class JournaldLogHandler(logging.Handler):
82
+ class JournaldLoggingHandler(logging.Handler):
83
83
  """
84
84
  TODO:
85
85
  - fallback handler for when this barfs
@@ -148,7 +148,7 @@ class JournaldLogHandler(logging.Handler):
148
148
  self.handleError(record)
149
149
 
150
150
 
151
- def journald_log_handler_factory(
151
+ def journald_logging_handler_factory(
152
152
  *,
153
153
  no_tty_check: bool = False,
154
154
  no_fallback: bool = False,
@@ -158,6 +158,6 @@ def journald_log_handler_factory(
158
158
  (no_tty_check or not sys.stderr.isatty()) and
159
159
  (no_fallback or sd_try_libsystemd() is not None)
160
160
  ):
161
- return JournaldLogHandler()
161
+ return JournaldLoggingHandler()
162
162
 
163
163
  return logging.StreamHandler()
@@ -1,7 +1,3 @@
1
- """
2
- TODO:
3
- - go-style tags: +slow,-ci, ...
4
- """
5
1
  from _pytest.main import resolve_collection_argument # noqa
6
2
  import pytest
7
3
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omlish
3
- Version: 0.0.0.dev423
3
+ Version: 0.0.0.dev425
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License-Expression: BSD-3-Clause