ominfra 0.0.0.dev431__py3-none-any.whl → 0.0.0.dev433__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.
@@ -5467,6 +5467,16 @@ class LoggingContext(Abstract):
5467
5467
  raise TypeError(f'LoggingContextInfo absent: {ty}')
5468
5468
  return info
5469
5469
 
5470
+
5471
+ @ta.final
5472
+ class SimpleLoggingContext(LoggingContext):
5473
+ def __init__(self, *infos: LoggingContextInfo) -> None:
5474
+ self._infos: ta.Dict[ta.Type[LoggingContextInfo], LoggingContextInfo] = {type(i): i for i in infos}
5475
+
5476
+ def get_info(self, ty: ta.Type[LoggingContextInfoT]) -> ta.Optional[LoggingContextInfoT]:
5477
+ return self._infos.get(ty)
5478
+
5479
+
5470
5480
  ##
5471
5481
 
5472
5482
 
@@ -5517,16 +5527,17 @@ class CaptureLoggingContextImpl(CaptureLoggingContext):
5517
5527
  stack_offset: int = 0,
5518
5528
  stack_info: bool = False,
5519
5529
  ) -> None:
5520
- # TODO: Name, Msg, Extra
5521
-
5522
5530
  if time_ns is None:
5523
5531
  time_ns = time.time_ns()
5524
5532
 
5533
+ # Done early to not trample on sys.exc_info()
5534
+ exc = LoggingContextInfos.Exc.build(exc_info)
5535
+
5525
5536
  self._infos: ta.Dict[ta.Type[LoggingContextInfo], LoggingContextInfo] = {}
5526
5537
  self._set_info(
5527
5538
  LoggingContextInfos.Level.build(level),
5539
+ exc,
5528
5540
  LoggingContextInfos.Time.build(time_ns),
5529
- LoggingContextInfos.Exc.build(exc_info),
5530
5541
  )
5531
5542
 
5532
5543
  if caller is not CaptureLoggingContextImpl.NOT_SET:
@@ -5668,10 +5679,14 @@ def _locking_logging_module_lock() -> ta.Iterator[None]:
5668
5679
  def configure_standard_logging(
5669
5680
  level: ta.Union[int, str] = logging.INFO,
5670
5681
  *,
5671
- json: bool = False,
5672
5682
  target: ta.Optional[logging.Logger] = None,
5683
+
5673
5684
  force: bool = False,
5685
+
5674
5686
  handler_factory: ta.Optional[ta.Callable[[], logging.Handler]] = None,
5687
+
5688
+ formatter: ta.Optional[logging.Formatter] = None, # noqa
5689
+ json: bool = False,
5675
5690
  ) -> ta.Optional[StandardConfiguredLoggingHandler]:
5676
5691
  with _locking_logging_module_lock():
5677
5692
  if target is None:
@@ -5692,11 +5707,11 @@ def configure_standard_logging(
5692
5707
 
5693
5708
  #
5694
5709
 
5695
- formatter: logging.Formatter
5696
- if json:
5697
- formatter = JsonLoggingFormatter()
5698
- else:
5699
- formatter = StandardLoggingFormatter(StandardLoggingFormatter.build_log_format(STANDARD_LOG_FORMAT_PARTS))
5710
+ if formatter is None:
5711
+ if json:
5712
+ formatter = JsonLoggingFormatter()
5713
+ else:
5714
+ formatter = StandardLoggingFormatter(StandardLoggingFormatter.build_log_format(STANDARD_LOG_FORMAT_PARTS)) # noqa
5700
5715
  handler.setFormatter(formatter)
5701
5716
 
5702
5717
  #
@@ -6157,22 +6172,6 @@ class LoggingContextInfoRecordAdapters:
6157
6172
  args=rec.args,
6158
6173
  )
6159
6174
 
6160
- # FIXME: handled specially - all unknown attrs on LogRecord
6161
- # class Extra(Adapter[LoggingContextInfos.Extra]):
6162
- # _record_attrs: ta.ClassVar[ta.Mapping[str, ta.Union[ta.Any, ta.Tuple[ta.Any, ta.Any]]]] = dict()
6163
- #
6164
- # def info_to_record(self, info: ta.Optional[LoggingContextInfos.Extra]) -> ta.Mapping[str, ta.Any]:
6165
- # # FIXME:
6166
- # # if extra is not None:
6167
- # # for key in extra:
6168
- # # if (key in ["message", "asctime"]) or (key in rv.__dict__):
6169
- # # raise KeyError("Attempt to overwrite %r in LogRecord" % key)
6170
- # # rv.__dict__[key] = extra[key]
6171
- # return dict()
6172
- #
6173
- # def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Extra]:
6174
- # return None
6175
-
6176
6175
  class Time(RequiredAdapter[LoggingContextInfos.Time]):
6177
6176
  info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Time]] = LoggingContextInfos.Time
6178
6177
 
@@ -6278,8 +6277,23 @@ class LoggingContextInfoRecordAdapters:
6278
6277
 
6279
6278
  def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Caller]:
6280
6279
  # FIXME: piecemeal?
6281
- # FIXME: strip _STACK_INFO_PREFIX
6282
- raise NotImplementedError
6280
+ if (
6281
+ rec.pathname != self._UNKNOWN_PATH_NAME and
6282
+ rec.lineno != 0 and
6283
+ rec.funcName != self._UNKNOWN_FUNC_NAME
6284
+ ):
6285
+ if (sinfo := rec.stack_info) is not None and sinfo.startswith(self._STACK_INFO_PREFIX):
6286
+ sinfo = sinfo[len(self._STACK_INFO_PREFIX):]
6287
+ return LoggingContextInfos.Caller(
6288
+ file_path=rec.pathname,
6289
+
6290
+ line_no=rec.lineno,
6291
+ func_name=rec.funcName,
6292
+
6293
+ stack_info=sinfo,
6294
+ )
6295
+
6296
+ return None
6283
6297
 
6284
6298
  class SourceFile(Adapter[LoggingContextInfos.SourceFile]):
6285
6299
  info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.SourceFile]] = LoggingContextInfos.SourceFile
@@ -6315,9 +6329,9 @@ class LoggingContextInfoRecordAdapters:
6315
6329
  )
6316
6330
 
6317
6331
  def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.SourceFile]:
6318
- if not (
6319
- rec.module is None or
6320
- rec.module == self._UNKNOWN_MODULE
6332
+ if (
6333
+ rec.module is not None and
6334
+ rec.module != self._UNKNOWN_MODULE
6321
6335
  ):
6322
6336
  return LoggingContextInfos.SourceFile(
6323
6337
  file_name=rec.filename,
@@ -6470,16 +6484,11 @@ _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS: ta.Mapping[ta.Type[LoggingContextInfo], L
6470
6484
  ##
6471
6485
 
6472
6486
 
6473
- KNOWN_STD_LOGGING_RECORD_ATTR_SET: ta.FrozenSet[str] = frozenset(
6474
- a for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS.values() for a in ad.record_attrs
6475
- )
6476
-
6477
-
6478
6487
  # Formatter:
6479
6488
  # - https://github.com/python/cpython/blob/39b2f82717a69dde7212bc39b673b0f55c99e6a3/Lib/logging/__init__.py#L514 (3.8)
6480
6489
  # - https://github.com/python/cpython/blob/f070f54c5f4a42c7c61d1d5d3b8f3b7203b4a0fb/Lib/logging/__init__.py#L554 (~3.14) # noqa
6481
6490
  #
6482
- KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS: ta.Dict[str, ta.Any] = dict(
6491
+ _KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS: ta.Dict[str, ta.Any] = dict(
6483
6492
  # The logged message, computed as msg % args. Set to `record.getMessage()`.
6484
6493
  message=str,
6485
6494
 
@@ -6493,7 +6502,15 @@ KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS: ta.Dict[str, ta.Any] = dict(
6493
6502
  exc_text=ta.Optional[str],
6494
6503
  )
6495
6504
 
6496
- KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTR_SET: ta.FrozenSet[str] = frozenset(KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS)
6505
+
6506
+ ##
6507
+
6508
+
6509
+ _KNOWN_STD_LOGGING_RECORD_ATTR_SET: ta.FrozenSet[str] = frozenset(
6510
+ a for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS.values() for a in ad.record_attrs
6511
+ )
6512
+
6513
+ _KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTR_SET: ta.FrozenSet[str] = frozenset(_KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS)
6497
6514
 
6498
6515
 
6499
6516
  class UnknownStdLoggingRecordAttrsWarning(LoggingSetupWarning):
@@ -6503,13 +6520,13 @@ class UnknownStdLoggingRecordAttrsWarning(LoggingSetupWarning):
6503
6520
  def _check_std_logging_record_attrs() -> None:
6504
6521
  if (
6505
6522
  len([a for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS.values() for a in ad.record_attrs]) !=
6506
- len(KNOWN_STD_LOGGING_RECORD_ATTR_SET)
6523
+ len(_KNOWN_STD_LOGGING_RECORD_ATTR_SET)
6507
6524
  ):
6508
6525
  raise RuntimeError('Duplicate LoggingContextInfoRecordAdapter record attrs')
6509
6526
 
6510
6527
  rec_dct = dict(logging.makeLogRecord({}).__dict__)
6511
6528
 
6512
- if (unk_rec_fields := frozenset(rec_dct) - KNOWN_STD_LOGGING_RECORD_ATTR_SET):
6529
+ if (unk_rec_fields := frozenset(rec_dct) - _KNOWN_STD_LOGGING_RECORD_ATTR_SET):
6513
6530
  import warnings # noqa
6514
6531
 
6515
6532
  warnings.warn(
@@ -6535,11 +6552,60 @@ class LoggingContextLogRecord(logging.LogRecord):
6535
6552
  # - exc_info: LoggingExcInfoTuple | None
6536
6553
  # - func: str | None = None -> funcName
6537
6554
  # - sinfo: str | None = None -> stack_info
6555
+ #
6538
6556
 
6539
6557
  def __init__(self, *, _logging_context: LoggingContext) -> None: # noqa
6558
+ self.__dict__.update(_logging_context=_logging_context)
6559
+
6540
6560
  for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS_:
6541
6561
  self.__dict__.update(ad.context_to_record(_logging_context))
6542
6562
 
6563
+ _logging_context: LoggingContext
6564
+
6565
+ # FIXME: track extra
6566
+ # def __setattr__(self, key, value):
6567
+ # super().__setattr__(key, value)
6568
+
6569
+
6570
+ ##
6571
+
6572
+
6573
+ @ta.final
6574
+ class LogRecordLoggingContext(LoggingContext):
6575
+ def __init__(self, rec: logging.LogRecord) -> None:
6576
+ if isinstance(rec, LoggingContextLogRecord):
6577
+ raise TypeError(rec)
6578
+
6579
+ self._rec = rec
6580
+
6581
+ infos: ta.List[LoggingContextInfo] = [
6582
+ info
6583
+ for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS_
6584
+ if (info := ad.record_to_info(rec)) is not None
6585
+ ]
6586
+
6587
+ # FIXME:
6588
+ # if extra is not None:
6589
+ # for key in extra:
6590
+ # if (key in ["message", "asctime"]) or (key in rv.__dict__):
6591
+ # raise KeyError("Attempt to overwrite %r in LogRecord" % key)
6592
+ # rv.__dict__[key] = extra[key]
6593
+
6594
+ if (extra := {
6595
+ a: v
6596
+ for a, v in rec.__dict__.items()
6597
+ if a not in _KNOWN_STD_LOGGING_RECORD_ATTR_SET
6598
+ }):
6599
+ infos.append(LoggingContextInfos.Extra(extra))
6600
+
6601
+ self._infos: ta.Dict[ta.Type[LoggingContextInfo], LoggingContextInfo] = {
6602
+ type(info): info
6603
+ for info in infos
6604
+ }
6605
+
6606
+ def get_info(self, ty: ta.Type[LoggingContextInfoT]) -> ta.Optional[LoggingContextInfoT]:
6607
+ return self._infos.get(ty)
6608
+
6543
6609
 
6544
6610
  ########################################
6545
6611
  # ../../../../../omlish/logs/std/loggers.py
ominfra/scripts/manage.py CHANGED
@@ -10105,6 +10105,16 @@ class LoggingContext(Abstract):
10105
10105
  raise TypeError(f'LoggingContextInfo absent: {ty}')
10106
10106
  return info
10107
10107
 
10108
+
10109
+ @ta.final
10110
+ class SimpleLoggingContext(LoggingContext):
10111
+ def __init__(self, *infos: LoggingContextInfo) -> None:
10112
+ self._infos: ta.Dict[ta.Type[LoggingContextInfo], LoggingContextInfo] = {type(i): i for i in infos}
10113
+
10114
+ def get_info(self, ty: ta.Type[LoggingContextInfoT]) -> ta.Optional[LoggingContextInfoT]:
10115
+ return self._infos.get(ty)
10116
+
10117
+
10108
10118
  ##
10109
10119
 
10110
10120
 
@@ -10155,16 +10165,17 @@ class CaptureLoggingContextImpl(CaptureLoggingContext):
10155
10165
  stack_offset: int = 0,
10156
10166
  stack_info: bool = False,
10157
10167
  ) -> None:
10158
- # TODO: Name, Msg, Extra
10159
-
10160
10168
  if time_ns is None:
10161
10169
  time_ns = time.time_ns()
10162
10170
 
10171
+ # Done early to not trample on sys.exc_info()
10172
+ exc = LoggingContextInfos.Exc.build(exc_info)
10173
+
10163
10174
  self._infos: ta.Dict[ta.Type[LoggingContextInfo], LoggingContextInfo] = {}
10164
10175
  self._set_info(
10165
10176
  LoggingContextInfos.Level.build(level),
10177
+ exc,
10166
10178
  LoggingContextInfos.Time.build(time_ns),
10167
- LoggingContextInfos.Exc.build(exc_info),
10168
10179
  )
10169
10180
 
10170
10181
  if caller is not CaptureLoggingContextImpl.NOT_SET:
@@ -10306,10 +10317,14 @@ def _locking_logging_module_lock() -> ta.Iterator[None]:
10306
10317
  def configure_standard_logging(
10307
10318
  level: ta.Union[int, str] = logging.INFO,
10308
10319
  *,
10309
- json: bool = False,
10310
10320
  target: ta.Optional[logging.Logger] = None,
10321
+
10311
10322
  force: bool = False,
10323
+
10312
10324
  handler_factory: ta.Optional[ta.Callable[[], logging.Handler]] = None,
10325
+
10326
+ formatter: ta.Optional[logging.Formatter] = None, # noqa
10327
+ json: bool = False,
10313
10328
  ) -> ta.Optional[StandardConfiguredLoggingHandler]:
10314
10329
  with _locking_logging_module_lock():
10315
10330
  if target is None:
@@ -10330,11 +10345,11 @@ def configure_standard_logging(
10330
10345
 
10331
10346
  #
10332
10347
 
10333
- formatter: logging.Formatter
10334
- if json:
10335
- formatter = JsonLoggingFormatter()
10336
- else:
10337
- formatter = StandardLoggingFormatter(StandardLoggingFormatter.build_log_format(STANDARD_LOG_FORMAT_PARTS))
10348
+ if formatter is None:
10349
+ if json:
10350
+ formatter = JsonLoggingFormatter()
10351
+ else:
10352
+ formatter = StandardLoggingFormatter(StandardLoggingFormatter.build_log_format(STANDARD_LOG_FORMAT_PARTS)) # noqa
10338
10353
  handler.setFormatter(formatter)
10339
10354
 
10340
10355
  #
@@ -11394,22 +11409,6 @@ class LoggingContextInfoRecordAdapters:
11394
11409
  args=rec.args,
11395
11410
  )
11396
11411
 
11397
- # FIXME: handled specially - all unknown attrs on LogRecord
11398
- # class Extra(Adapter[LoggingContextInfos.Extra]):
11399
- # _record_attrs: ta.ClassVar[ta.Mapping[str, ta.Union[ta.Any, ta.Tuple[ta.Any, ta.Any]]]] = dict()
11400
- #
11401
- # def info_to_record(self, info: ta.Optional[LoggingContextInfos.Extra]) -> ta.Mapping[str, ta.Any]:
11402
- # # FIXME:
11403
- # # if extra is not None:
11404
- # # for key in extra:
11405
- # # if (key in ["message", "asctime"]) or (key in rv.__dict__):
11406
- # # raise KeyError("Attempt to overwrite %r in LogRecord" % key)
11407
- # # rv.__dict__[key] = extra[key]
11408
- # return dict()
11409
- #
11410
- # def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Extra]:
11411
- # return None
11412
-
11413
11412
  class Time(RequiredAdapter[LoggingContextInfos.Time]):
11414
11413
  info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Time]] = LoggingContextInfos.Time
11415
11414
 
@@ -11515,8 +11514,23 @@ class LoggingContextInfoRecordAdapters:
11515
11514
 
11516
11515
  def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Caller]:
11517
11516
  # FIXME: piecemeal?
11518
- # FIXME: strip _STACK_INFO_PREFIX
11519
- raise NotImplementedError
11517
+ if (
11518
+ rec.pathname != self._UNKNOWN_PATH_NAME and
11519
+ rec.lineno != 0 and
11520
+ rec.funcName != self._UNKNOWN_FUNC_NAME
11521
+ ):
11522
+ if (sinfo := rec.stack_info) is not None and sinfo.startswith(self._STACK_INFO_PREFIX):
11523
+ sinfo = sinfo[len(self._STACK_INFO_PREFIX):]
11524
+ return LoggingContextInfos.Caller(
11525
+ file_path=rec.pathname,
11526
+
11527
+ line_no=rec.lineno,
11528
+ func_name=rec.funcName,
11529
+
11530
+ stack_info=sinfo,
11531
+ )
11532
+
11533
+ return None
11520
11534
 
11521
11535
  class SourceFile(Adapter[LoggingContextInfos.SourceFile]):
11522
11536
  info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.SourceFile]] = LoggingContextInfos.SourceFile
@@ -11552,9 +11566,9 @@ class LoggingContextInfoRecordAdapters:
11552
11566
  )
11553
11567
 
11554
11568
  def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.SourceFile]:
11555
- if not (
11556
- rec.module is None or
11557
- rec.module == self._UNKNOWN_MODULE
11569
+ if (
11570
+ rec.module is not None and
11571
+ rec.module != self._UNKNOWN_MODULE
11558
11572
  ):
11559
11573
  return LoggingContextInfos.SourceFile(
11560
11574
  file_name=rec.filename,
@@ -11707,16 +11721,11 @@ _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS: ta.Mapping[ta.Type[LoggingContextInfo], L
11707
11721
  ##
11708
11722
 
11709
11723
 
11710
- KNOWN_STD_LOGGING_RECORD_ATTR_SET: ta.FrozenSet[str] = frozenset(
11711
- a for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS.values() for a in ad.record_attrs
11712
- )
11713
-
11714
-
11715
11724
  # Formatter:
11716
11725
  # - https://github.com/python/cpython/blob/39b2f82717a69dde7212bc39b673b0f55c99e6a3/Lib/logging/__init__.py#L514 (3.8)
11717
11726
  # - https://github.com/python/cpython/blob/f070f54c5f4a42c7c61d1d5d3b8f3b7203b4a0fb/Lib/logging/__init__.py#L554 (~3.14) # noqa
11718
11727
  #
11719
- KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS: ta.Dict[str, ta.Any] = dict(
11728
+ _KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS: ta.Dict[str, ta.Any] = dict(
11720
11729
  # The logged message, computed as msg % args. Set to `record.getMessage()`.
11721
11730
  message=str,
11722
11731
 
@@ -11730,7 +11739,15 @@ KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS: ta.Dict[str, ta.Any] = dict(
11730
11739
  exc_text=ta.Optional[str],
11731
11740
  )
11732
11741
 
11733
- KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTR_SET: ta.FrozenSet[str] = frozenset(KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS)
11742
+
11743
+ ##
11744
+
11745
+
11746
+ _KNOWN_STD_LOGGING_RECORD_ATTR_SET: ta.FrozenSet[str] = frozenset(
11747
+ a for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS.values() for a in ad.record_attrs
11748
+ )
11749
+
11750
+ _KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTR_SET: ta.FrozenSet[str] = frozenset(_KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS)
11734
11751
 
11735
11752
 
11736
11753
  class UnknownStdLoggingRecordAttrsWarning(LoggingSetupWarning):
@@ -11740,13 +11757,13 @@ class UnknownStdLoggingRecordAttrsWarning(LoggingSetupWarning):
11740
11757
  def _check_std_logging_record_attrs() -> None:
11741
11758
  if (
11742
11759
  len([a for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS.values() for a in ad.record_attrs]) !=
11743
- len(KNOWN_STD_LOGGING_RECORD_ATTR_SET)
11760
+ len(_KNOWN_STD_LOGGING_RECORD_ATTR_SET)
11744
11761
  ):
11745
11762
  raise RuntimeError('Duplicate LoggingContextInfoRecordAdapter record attrs')
11746
11763
 
11747
11764
  rec_dct = dict(logging.makeLogRecord({}).__dict__)
11748
11765
 
11749
- if (unk_rec_fields := frozenset(rec_dct) - KNOWN_STD_LOGGING_RECORD_ATTR_SET):
11766
+ if (unk_rec_fields := frozenset(rec_dct) - _KNOWN_STD_LOGGING_RECORD_ATTR_SET):
11750
11767
  import warnings # noqa
11751
11768
 
11752
11769
  warnings.warn(
@@ -11772,11 +11789,60 @@ class LoggingContextLogRecord(logging.LogRecord):
11772
11789
  # - exc_info: LoggingExcInfoTuple | None
11773
11790
  # - func: str | None = None -> funcName
11774
11791
  # - sinfo: str | None = None -> stack_info
11792
+ #
11775
11793
 
11776
11794
  def __init__(self, *, _logging_context: LoggingContext) -> None: # noqa
11795
+ self.__dict__.update(_logging_context=_logging_context)
11796
+
11777
11797
  for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS_:
11778
11798
  self.__dict__.update(ad.context_to_record(_logging_context))
11779
11799
 
11800
+ _logging_context: LoggingContext
11801
+
11802
+ # FIXME: track extra
11803
+ # def __setattr__(self, key, value):
11804
+ # super().__setattr__(key, value)
11805
+
11806
+
11807
+ ##
11808
+
11809
+
11810
+ @ta.final
11811
+ class LogRecordLoggingContext(LoggingContext):
11812
+ def __init__(self, rec: logging.LogRecord) -> None:
11813
+ if isinstance(rec, LoggingContextLogRecord):
11814
+ raise TypeError(rec)
11815
+
11816
+ self._rec = rec
11817
+
11818
+ infos: ta.List[LoggingContextInfo] = [
11819
+ info
11820
+ for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS_
11821
+ if (info := ad.record_to_info(rec)) is not None
11822
+ ]
11823
+
11824
+ # FIXME:
11825
+ # if extra is not None:
11826
+ # for key in extra:
11827
+ # if (key in ["message", "asctime"]) or (key in rv.__dict__):
11828
+ # raise KeyError("Attempt to overwrite %r in LogRecord" % key)
11829
+ # rv.__dict__[key] = extra[key]
11830
+
11831
+ if (extra := {
11832
+ a: v
11833
+ for a, v in rec.__dict__.items()
11834
+ if a not in _KNOWN_STD_LOGGING_RECORD_ATTR_SET
11835
+ }):
11836
+ infos.append(LoggingContextInfos.Extra(extra))
11837
+
11838
+ self._infos: ta.Dict[ta.Type[LoggingContextInfo], LoggingContextInfo] = {
11839
+ type(info): info
11840
+ for info in infos
11841
+ }
11842
+
11843
+ def get_info(self, ty: ta.Type[LoggingContextInfoT]) -> ta.Optional[LoggingContextInfoT]:
11844
+ return self._infos.get(ty)
11845
+
11780
11846
 
11781
11847
  ########################################
11782
11848
  # ../../../omlish/subprocesses/base.py
@@ -8591,6 +8591,16 @@ class LoggingContext(Abstract):
8591
8591
  raise TypeError(f'LoggingContextInfo absent: {ty}')
8592
8592
  return info
8593
8593
 
8594
+
8595
+ @ta.final
8596
+ class SimpleLoggingContext(LoggingContext):
8597
+ def __init__(self, *infos: LoggingContextInfo) -> None:
8598
+ self._infos: ta.Dict[ta.Type[LoggingContextInfo], LoggingContextInfo] = {type(i): i for i in infos}
8599
+
8600
+ def get_info(self, ty: ta.Type[LoggingContextInfoT]) -> ta.Optional[LoggingContextInfoT]:
8601
+ return self._infos.get(ty)
8602
+
8603
+
8594
8604
  ##
8595
8605
 
8596
8606
 
@@ -8641,16 +8651,17 @@ class CaptureLoggingContextImpl(CaptureLoggingContext):
8641
8651
  stack_offset: int = 0,
8642
8652
  stack_info: bool = False,
8643
8653
  ) -> None:
8644
- # TODO: Name, Msg, Extra
8645
-
8646
8654
  if time_ns is None:
8647
8655
  time_ns = time.time_ns()
8648
8656
 
8657
+ # Done early to not trample on sys.exc_info()
8658
+ exc = LoggingContextInfos.Exc.build(exc_info)
8659
+
8649
8660
  self._infos: ta.Dict[ta.Type[LoggingContextInfo], LoggingContextInfo] = {}
8650
8661
  self._set_info(
8651
8662
  LoggingContextInfos.Level.build(level),
8663
+ exc,
8652
8664
  LoggingContextInfos.Time.build(time_ns),
8653
- LoggingContextInfos.Exc.build(exc_info),
8654
8665
  )
8655
8666
 
8656
8667
  if caller is not CaptureLoggingContextImpl.NOT_SET:
@@ -8792,10 +8803,14 @@ def _locking_logging_module_lock() -> ta.Iterator[None]:
8792
8803
  def configure_standard_logging(
8793
8804
  level: ta.Union[int, str] = logging.INFO,
8794
8805
  *,
8795
- json: bool = False,
8796
8806
  target: ta.Optional[logging.Logger] = None,
8807
+
8797
8808
  force: bool = False,
8809
+
8798
8810
  handler_factory: ta.Optional[ta.Callable[[], logging.Handler]] = None,
8811
+
8812
+ formatter: ta.Optional[logging.Formatter] = None, # noqa
8813
+ json: bool = False,
8799
8814
  ) -> ta.Optional[StandardConfiguredLoggingHandler]:
8800
8815
  with _locking_logging_module_lock():
8801
8816
  if target is None:
@@ -8816,11 +8831,11 @@ def configure_standard_logging(
8816
8831
 
8817
8832
  #
8818
8833
 
8819
- formatter: logging.Formatter
8820
- if json:
8821
- formatter = JsonLoggingFormatter()
8822
- else:
8823
- formatter = StandardLoggingFormatter(StandardLoggingFormatter.build_log_format(STANDARD_LOG_FORMAT_PARTS))
8834
+ if formatter is None:
8835
+ if json:
8836
+ formatter = JsonLoggingFormatter()
8837
+ else:
8838
+ formatter = StandardLoggingFormatter(StandardLoggingFormatter.build_log_format(STANDARD_LOG_FORMAT_PARTS)) # noqa
8824
8839
  handler.setFormatter(formatter)
8825
8840
 
8826
8841
  #
@@ -10002,22 +10017,6 @@ class LoggingContextInfoRecordAdapters:
10002
10017
  args=rec.args,
10003
10018
  )
10004
10019
 
10005
- # FIXME: handled specially - all unknown attrs on LogRecord
10006
- # class Extra(Adapter[LoggingContextInfos.Extra]):
10007
- # _record_attrs: ta.ClassVar[ta.Mapping[str, ta.Union[ta.Any, ta.Tuple[ta.Any, ta.Any]]]] = dict()
10008
- #
10009
- # def info_to_record(self, info: ta.Optional[LoggingContextInfos.Extra]) -> ta.Mapping[str, ta.Any]:
10010
- # # FIXME:
10011
- # # if extra is not None:
10012
- # # for key in extra:
10013
- # # if (key in ["message", "asctime"]) or (key in rv.__dict__):
10014
- # # raise KeyError("Attempt to overwrite %r in LogRecord" % key)
10015
- # # rv.__dict__[key] = extra[key]
10016
- # return dict()
10017
- #
10018
- # def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Extra]:
10019
- # return None
10020
-
10021
10020
  class Time(RequiredAdapter[LoggingContextInfos.Time]):
10022
10021
  info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.Time]] = LoggingContextInfos.Time
10023
10022
 
@@ -10123,8 +10122,23 @@ class LoggingContextInfoRecordAdapters:
10123
10122
 
10124
10123
  def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.Caller]:
10125
10124
  # FIXME: piecemeal?
10126
- # FIXME: strip _STACK_INFO_PREFIX
10127
- raise NotImplementedError
10125
+ if (
10126
+ rec.pathname != self._UNKNOWN_PATH_NAME and
10127
+ rec.lineno != 0 and
10128
+ rec.funcName != self._UNKNOWN_FUNC_NAME
10129
+ ):
10130
+ if (sinfo := rec.stack_info) is not None and sinfo.startswith(self._STACK_INFO_PREFIX):
10131
+ sinfo = sinfo[len(self._STACK_INFO_PREFIX):]
10132
+ return LoggingContextInfos.Caller(
10133
+ file_path=rec.pathname,
10134
+
10135
+ line_no=rec.lineno,
10136
+ func_name=rec.funcName,
10137
+
10138
+ stack_info=sinfo,
10139
+ )
10140
+
10141
+ return None
10128
10142
 
10129
10143
  class SourceFile(Adapter[LoggingContextInfos.SourceFile]):
10130
10144
  info_cls: ta.ClassVar[ta.Type[LoggingContextInfos.SourceFile]] = LoggingContextInfos.SourceFile
@@ -10160,9 +10174,9 @@ class LoggingContextInfoRecordAdapters:
10160
10174
  )
10161
10175
 
10162
10176
  def record_to_info(self, rec: logging.LogRecord) -> ta.Optional[LoggingContextInfos.SourceFile]:
10163
- if not (
10164
- rec.module is None or
10165
- rec.module == self._UNKNOWN_MODULE
10177
+ if (
10178
+ rec.module is not None and
10179
+ rec.module != self._UNKNOWN_MODULE
10166
10180
  ):
10167
10181
  return LoggingContextInfos.SourceFile(
10168
10182
  file_name=rec.filename,
@@ -10315,16 +10329,11 @@ _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS: ta.Mapping[ta.Type[LoggingContextInfo], L
10315
10329
  ##
10316
10330
 
10317
10331
 
10318
- KNOWN_STD_LOGGING_RECORD_ATTR_SET: ta.FrozenSet[str] = frozenset(
10319
- a for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS.values() for a in ad.record_attrs
10320
- )
10321
-
10322
-
10323
10332
  # Formatter:
10324
10333
  # - https://github.com/python/cpython/blob/39b2f82717a69dde7212bc39b673b0f55c99e6a3/Lib/logging/__init__.py#L514 (3.8)
10325
10334
  # - https://github.com/python/cpython/blob/f070f54c5f4a42c7c61d1d5d3b8f3b7203b4a0fb/Lib/logging/__init__.py#L554 (~3.14) # noqa
10326
10335
  #
10327
- KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS: ta.Dict[str, ta.Any] = dict(
10336
+ _KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS: ta.Dict[str, ta.Any] = dict(
10328
10337
  # The logged message, computed as msg % args. Set to `record.getMessage()`.
10329
10338
  message=str,
10330
10339
 
@@ -10338,7 +10347,15 @@ KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS: ta.Dict[str, ta.Any] = dict(
10338
10347
  exc_text=ta.Optional[str],
10339
10348
  )
10340
10349
 
10341
- KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTR_SET: ta.FrozenSet[str] = frozenset(KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS)
10350
+
10351
+ ##
10352
+
10353
+
10354
+ _KNOWN_STD_LOGGING_RECORD_ATTR_SET: ta.FrozenSet[str] = frozenset(
10355
+ a for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS.values() for a in ad.record_attrs
10356
+ )
10357
+
10358
+ _KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTR_SET: ta.FrozenSet[str] = frozenset(_KNOWN_STD_LOGGING_FORMATTER_RECORD_ATTRS)
10342
10359
 
10343
10360
 
10344
10361
  class UnknownStdLoggingRecordAttrsWarning(LoggingSetupWarning):
@@ -10348,13 +10365,13 @@ class UnknownStdLoggingRecordAttrsWarning(LoggingSetupWarning):
10348
10365
  def _check_std_logging_record_attrs() -> None:
10349
10366
  if (
10350
10367
  len([a for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS.values() for a in ad.record_attrs]) !=
10351
- len(KNOWN_STD_LOGGING_RECORD_ATTR_SET)
10368
+ len(_KNOWN_STD_LOGGING_RECORD_ATTR_SET)
10352
10369
  ):
10353
10370
  raise RuntimeError('Duplicate LoggingContextInfoRecordAdapter record attrs')
10354
10371
 
10355
10372
  rec_dct = dict(logging.makeLogRecord({}).__dict__)
10356
10373
 
10357
- if (unk_rec_fields := frozenset(rec_dct) - KNOWN_STD_LOGGING_RECORD_ATTR_SET):
10374
+ if (unk_rec_fields := frozenset(rec_dct) - _KNOWN_STD_LOGGING_RECORD_ATTR_SET):
10358
10375
  import warnings # noqa
10359
10376
 
10360
10377
  warnings.warn(
@@ -10380,11 +10397,60 @@ class LoggingContextLogRecord(logging.LogRecord):
10380
10397
  # - exc_info: LoggingExcInfoTuple | None
10381
10398
  # - func: str | None = None -> funcName
10382
10399
  # - sinfo: str | None = None -> stack_info
10400
+ #
10383
10401
 
10384
10402
  def __init__(self, *, _logging_context: LoggingContext) -> None: # noqa
10403
+ self.__dict__.update(_logging_context=_logging_context)
10404
+
10385
10405
  for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS_:
10386
10406
  self.__dict__.update(ad.context_to_record(_logging_context))
10387
10407
 
10408
+ _logging_context: LoggingContext
10409
+
10410
+ # FIXME: track extra
10411
+ # def __setattr__(self, key, value):
10412
+ # super().__setattr__(key, value)
10413
+
10414
+
10415
+ ##
10416
+
10417
+
10418
+ @ta.final
10419
+ class LogRecordLoggingContext(LoggingContext):
10420
+ def __init__(self, rec: logging.LogRecord) -> None:
10421
+ if isinstance(rec, LoggingContextLogRecord):
10422
+ raise TypeError(rec)
10423
+
10424
+ self._rec = rec
10425
+
10426
+ infos: ta.List[LoggingContextInfo] = [
10427
+ info
10428
+ for ad in _LOGGING_CONTEXT_INFO_RECORD_ADAPTERS_
10429
+ if (info := ad.record_to_info(rec)) is not None
10430
+ ]
10431
+
10432
+ # FIXME:
10433
+ # if extra is not None:
10434
+ # for key in extra:
10435
+ # if (key in ["message", "asctime"]) or (key in rv.__dict__):
10436
+ # raise KeyError("Attempt to overwrite %r in LogRecord" % key)
10437
+ # rv.__dict__[key] = extra[key]
10438
+
10439
+ if (extra := {
10440
+ a: v
10441
+ for a, v in rec.__dict__.items()
10442
+ if a not in _KNOWN_STD_LOGGING_RECORD_ATTR_SET
10443
+ }):
10444
+ infos.append(LoggingContextInfos.Extra(extra))
10445
+
10446
+ self._infos: ta.Dict[ta.Type[LoggingContextInfo], LoggingContextInfo] = {
10447
+ type(info): info
10448
+ for info in infos
10449
+ }
10450
+
10451
+ def get_info(self, ty: ta.Type[LoggingContextInfoT]) -> ta.Optional[LoggingContextInfoT]:
10452
+ return self._infos.get(ty)
10453
+
10388
10454
 
10389
10455
  ########################################
10390
10456
  # ../dispatchers.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ominfra
3
- Version: 0.0.0.dev431
3
+ Version: 0.0.0.dev433
4
4
  Summary: ominfra
5
5
  Author: wrmsr
6
6
  License-Expression: BSD-3-Clause
@@ -14,8 +14,8 @@ Classifier: Programming Language :: Python :: 3.13
14
14
  Requires-Python: >=3.13
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
- Requires-Dist: omdev==0.0.0.dev431
18
- Requires-Dist: omlish==0.0.0.dev431
17
+ Requires-Dist: omdev==0.0.0.dev433
18
+ Requires-Dist: omlish==0.0.0.dev433
19
19
  Provides-Extra: all
20
20
  Requires-Dist: paramiko~=4.0; extra == "all"
21
21
  Requires-Dist: asyncssh~=2.21; extra == "all"
@@ -112,9 +112,9 @@ ominfra/manage/targets/connection.py,sha256=mOHCsDVG-DZBhl3Mb7TTr1vhPb0gxDOOMW1x
112
112
  ominfra/manage/targets/inject.py,sha256=3M4wBkxtvymq_yhiotHlTN8iydELMjVCndyp9Bq-4eo,1572
113
113
  ominfra/manage/targets/targets.py,sha256=LjSQrDsHEjEQMDHcxtNKmLjy0YGLXJRGPFdUjazzFIM,1918
114
114
  ominfra/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
115
- ominfra/scripts/journald2aws.py,sha256=vk8CdqFkHxr-ehrZ-PPp5GQ05bdbwS_ul8d8bcOGznw,240702
116
- ominfra/scripts/manage.py,sha256=zgVcSuduHT48pCoqJvkYYhB81yOe839h5KxPBfnWVoA,464423
117
- ominfra/scripts/supervisor.py,sha256=5W9BDeAi5M8KwnNMqcgfn9H7UlHJGuuYiWDVp3p2CE0,374162
115
+ ominfra/scripts/journald2aws.py,sha256=djP-ywraucrb3UTm0oCk1WjugORbV_tcvJPXSZQAhdE,242341
116
+ ominfra/scripts/manage.py,sha256=HZU2z0BC32nokLzqi8CDeQi7xcY_SDnbNRS4lPP5Y58,466062
117
+ ominfra/scripts/supervisor.py,sha256=GM8cRAi6hkOx3-RQRiN-DrBYNsZba0vOmraaYL0OWIk,375801
118
118
  ominfra/supervisor/LICENSE.txt,sha256=ZrHY15PVR98y26Yg6iQfa-SXnUaYTDhrUsPVcEO5OKM,1874
119
119
  ominfra/supervisor/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
120
120
  ominfra/supervisor/__main__.py,sha256=I0yFw-C08OOiZ3BF6lF1Oiv789EQXu-_j6whDhQUTEA,66
@@ -156,9 +156,9 @@ ominfra/tailscale/api.py,sha256=XASv9C_CWI-u-yX5jVzhJrkJhlwQRkYQWQQG1uJwAd8,1375
156
156
  ominfra/tailscale/cli.py,sha256=zRV7-tKB7kBah1oTVZlol-vwx1FBlnfzYAPGkeU5jX4,3543
157
157
  ominfra/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
158
158
  ominfra/tools/listresources.py,sha256=ePmo7cUAiBZARkM_3K4GKYZXxV73An_aioS1m-AAJis,6181
159
- ominfra-0.0.0.dev431.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
160
- ominfra-0.0.0.dev431.dist-info/METADATA,sha256=3i5UVpLScKololuVLWCYbIDvRF_8d6zGi0QKHb_FdYY,2377
161
- ominfra-0.0.0.dev431.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
162
- ominfra-0.0.0.dev431.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
163
- ominfra-0.0.0.dev431.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
164
- ominfra-0.0.0.dev431.dist-info/RECORD,,
159
+ ominfra-0.0.0.dev433.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
160
+ ominfra-0.0.0.dev433.dist-info/METADATA,sha256=QDFpqnzcVehom36HXSyoF0m4Qb4GzikrHIUSAOmbJqM,2377
161
+ ominfra-0.0.0.dev433.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
162
+ ominfra-0.0.0.dev433.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
163
+ ominfra-0.0.0.dev433.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
164
+ ominfra-0.0.0.dev433.dist-info/RECORD,,