omdev 0.0.0.dev440__py3-none-any.whl → 0.0.0.dev495__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.

Potentially problematic release.


This version of omdev might be problematic. Click here for more details.

Files changed (148) hide show
  1. omdev/.omlish-manifests.json +18 -30
  2. omdev/README.md +51 -0
  3. omdev/__about__.py +11 -7
  4. omdev/amalg/gen/gen.py +49 -6
  5. omdev/amalg/gen/imports.py +1 -1
  6. omdev/amalg/gen/manifests.py +1 -1
  7. omdev/amalg/gen/resources.py +1 -1
  8. omdev/amalg/gen/srcfiles.py +13 -3
  9. omdev/amalg/gen/strip.py +1 -1
  10. omdev/amalg/gen/types.py +1 -1
  11. omdev/amalg/gen/typing.py +1 -1
  12. omdev/amalg/info.py +32 -0
  13. omdev/cache/data/actions.py +1 -1
  14. omdev/cache/data/specs.py +1 -1
  15. omdev/cexts/_boilerplate.cc +2 -3
  16. omdev/cexts/cmake.py +4 -1
  17. omdev/ci/cli.py +2 -3
  18. omdev/cli/clicli.py +37 -7
  19. omdev/cmdlog/cli.py +1 -2
  20. omdev/dataclasses/_dumping.py +1960 -0
  21. omdev/dataclasses/_template.py +22 -0
  22. omdev/dataclasses/cli.py +7 -2
  23. omdev/dataclasses/codegen.py +340 -60
  24. omdev/dataclasses/dumping.py +200 -0
  25. omdev/interp/cli.py +1 -1
  26. omdev/interp/types.py +3 -2
  27. omdev/interp/uv/provider.py +37 -0
  28. omdev/interp/venvs.py +1 -0
  29. omdev/irc/messages/base.py +50 -0
  30. omdev/irc/messages/formats.py +92 -0
  31. omdev/irc/messages/messages.py +775 -0
  32. omdev/irc/messages/parsing.py +99 -0
  33. omdev/irc/numerics/__init__.py +0 -0
  34. omdev/irc/numerics/formats.py +97 -0
  35. omdev/irc/numerics/numerics.py +865 -0
  36. omdev/irc/numerics/types.py +59 -0
  37. omdev/irc/protocol/LICENSE +11 -0
  38. omdev/irc/protocol/__init__.py +61 -0
  39. omdev/irc/protocol/consts.py +6 -0
  40. omdev/irc/protocol/errors.py +30 -0
  41. omdev/irc/protocol/message.py +21 -0
  42. omdev/irc/protocol/nuh.py +55 -0
  43. omdev/irc/protocol/parsing.py +158 -0
  44. omdev/irc/protocol/rendering.py +153 -0
  45. omdev/irc/protocol/tags.py +102 -0
  46. omdev/irc/protocol/utils.py +30 -0
  47. omdev/manifests/_dumping.py +125 -25
  48. omdev/manifests/main.py +1 -1
  49. omdev/markdown/__init__.py +0 -0
  50. omdev/markdown/incparse.py +116 -0
  51. omdev/markdown/tokens.py +51 -0
  52. omdev/packaging/marshal.py +8 -8
  53. omdev/packaging/requires.py +6 -6
  54. omdev/packaging/revisions.py +1 -1
  55. omdev/packaging/specifiers.py +2 -1
  56. omdev/packaging/versions.py +4 -4
  57. omdev/packaging/wheelfile.py +2 -0
  58. omdev/precheck/blanklines.py +66 -0
  59. omdev/precheck/caches.py +1 -1
  60. omdev/precheck/imports.py +14 -1
  61. omdev/precheck/main.py +4 -3
  62. omdev/precheck/unicode.py +39 -15
  63. omdev/py/asts/__init__.py +0 -0
  64. omdev/py/asts/parents.py +28 -0
  65. omdev/py/asts/toplevel.py +123 -0
  66. omdev/py/asts/visitors.py +18 -0
  67. omdev/py/attrdocs.py +1 -1
  68. omdev/py/bracepy.py +12 -4
  69. omdev/py/reprs.py +32 -0
  70. omdev/py/srcheaders.py +1 -1
  71. omdev/py/tokens/__init__.py +0 -0
  72. omdev/py/tools/mkrelimp.py +1 -1
  73. omdev/py/tools/pipdepup.py +686 -0
  74. omdev/pyproject/cli.py +1 -1
  75. omdev/pyproject/pkg.py +190 -45
  76. omdev/pyproject/reqs.py +31 -9
  77. omdev/pyproject/tools/__init__.py +0 -0
  78. omdev/pyproject/tools/aboutdeps.py +60 -0
  79. omdev/pyproject/venvs.py +8 -1
  80. omdev/rs/__init__.py +0 -0
  81. omdev/scripts/ci.py +752 -98
  82. omdev/scripts/interp.py +232 -39
  83. omdev/scripts/lib/inject.py +74 -27
  84. omdev/scripts/lib/logs.py +187 -43
  85. omdev/scripts/lib/marshal.py +67 -25
  86. omdev/scripts/pyproject.py +1369 -143
  87. omdev/tools/git/cli.py +10 -0
  88. omdev/tools/json/formats.py +2 -0
  89. omdev/tools/json/processing.py +5 -2
  90. omdev/tools/jsonview/cli.py +49 -65
  91. omdev/tools/jsonview/resources/jsonview.html.j2 +43 -0
  92. omdev/tools/pawk/README.md +195 -0
  93. omdev/tools/pawk/pawk.py +2 -2
  94. omdev/tools/pip.py +8 -0
  95. omdev/tui/__init__.py +0 -0
  96. omdev/tui/apps/__init__.py +0 -0
  97. omdev/tui/apps/edit/__init__.py +0 -0
  98. omdev/tui/apps/edit/main.py +167 -0
  99. omdev/tui/apps/irc/__init__.py +0 -0
  100. omdev/tui/apps/irc/__main__.py +4 -0
  101. omdev/tui/apps/irc/app.py +286 -0
  102. omdev/tui/apps/irc/client.py +187 -0
  103. omdev/tui/apps/irc/commands.py +175 -0
  104. omdev/tui/apps/irc/main.py +26 -0
  105. omdev/tui/apps/markdown/__init__.py +0 -0
  106. omdev/tui/apps/markdown/__main__.py +11 -0
  107. omdev/{ptk → tui/apps}/markdown/cli.py +5 -7
  108. omdev/tui/rich/__init__.py +46 -0
  109. omdev/tui/rich/console2.py +20 -0
  110. omdev/tui/rich/markdown2.py +186 -0
  111. omdev/tui/textual/__init__.py +265 -0
  112. omdev/tui/textual/app2.py +16 -0
  113. omdev/tui/textual/autocomplete/LICENSE +21 -0
  114. omdev/tui/textual/autocomplete/__init__.py +33 -0
  115. omdev/tui/textual/autocomplete/matching.py +226 -0
  116. omdev/tui/textual/autocomplete/paths.py +202 -0
  117. omdev/tui/textual/autocomplete/widget.py +612 -0
  118. omdev/tui/textual/debug/__init__.py +10 -0
  119. omdev/tui/textual/debug/dominfo.py +151 -0
  120. omdev/tui/textual/debug/screen.py +24 -0
  121. omdev/tui/textual/devtools.py +187 -0
  122. omdev/tui/textual/drivers2.py +55 -0
  123. omdev/tui/textual/logging2.py +20 -0
  124. omdev/tui/textual/types.py +45 -0
  125. {omdev-0.0.0.dev440.dist-info → omdev-0.0.0.dev495.dist-info}/METADATA +15 -9
  126. {omdev-0.0.0.dev440.dist-info → omdev-0.0.0.dev495.dist-info}/RECORD +135 -80
  127. omdev/ptk/__init__.py +0 -103
  128. omdev/ptk/apps/ncdu.py +0 -167
  129. omdev/ptk/confirm.py +0 -60
  130. omdev/ptk/markdown/LICENSE +0 -22
  131. omdev/ptk/markdown/__init__.py +0 -10
  132. omdev/ptk/markdown/__main__.py +0 -11
  133. omdev/ptk/markdown/border.py +0 -94
  134. omdev/ptk/markdown/markdown.py +0 -390
  135. omdev/ptk/markdown/parser.py +0 -42
  136. omdev/ptk/markdown/styles.py +0 -29
  137. omdev/ptk/markdown/tags.py +0 -299
  138. omdev/ptk/markdown/utils.py +0 -366
  139. omdev/pyproject/cexts.py +0 -110
  140. /omdev/{ptk/apps → irc}/__init__.py +0 -0
  141. /omdev/{tokens → irc/messages}/__init__.py +0 -0
  142. /omdev/{tokens → py/tokens}/all.py +0 -0
  143. /omdev/{tokens → py/tokens}/tokenizert.py +0 -0
  144. /omdev/{tokens → py/tokens}/utils.py +0 -0
  145. {omdev-0.0.0.dev440.dist-info → omdev-0.0.0.dev495.dist-info}/WHEEL +0 -0
  146. {omdev-0.0.0.dev440.dist-info → omdev-0.0.0.dev495.dist-info}/entry_points.txt +0 -0
  147. {omdev-0.0.0.dev440.dist-info → omdev-0.0.0.dev495.dist-info}/licenses/LICENSE +0 -0
  148. {omdev-0.0.0.dev440.dist-info → omdev-0.0.0.dev495.dist-info}/top_level.txt +0 -0
omdev/scripts/lib/logs.py CHANGED
@@ -30,9 +30,33 @@ if sys.version_info < (3, 8):
30
30
  raise OSError(f'Requires python (3, 8), got {sys.version_info} from {sys.executable}') # noqa
31
31
 
32
32
 
33
+ def __omlish_amalg__(): # noqa
34
+ return dict(
35
+ src_files=[
36
+ dict(path='../lite/abstract.py', sha1='a2fc3f3697fa8de5247761e9d554e70176f37aac'),
37
+ dict(path='../lite/json.py', sha1='57eeddc4d23a17931e00284ffa5cb6e3ce089486'),
38
+ dict(path='levels.py', sha1='91405563d082a5eba874da82aac89d83ce7b6152'),
39
+ dict(path='std/filters.py', sha1='f36aab646d84d31e295b33aaaaa6f8b67ff38b3d'),
40
+ dict(path='std/proxy.py', sha1='3e7301a2aa351127f9c85f61b2f85dcc3f15aafb'),
41
+ dict(path='warnings.py', sha1='c4eb694b24773351107fcc058f3620f1dbfb6799'),
42
+ dict(path='infos.py', sha1='4dd104bd468a8c438601dd0bbda619b47d2f1620'),
43
+ dict(path='std/json.py', sha1='2a75553131e4d5331bb0cedde42aa183f403fc3b'),
44
+ dict(path='contexts.py', sha1='1000a6d5ddfb642865ca532e34b1d50759781cf0'),
45
+ dict(path='std/standard.py', sha1='5c97c1b9f7ead58d6127d047b873398f708f288d'),
46
+ dict(path='base.py', sha1='8d06faee05fead6b1dd98c9035a5b042af4aebb1'),
47
+ dict(path='std/records.py', sha1='8bbf6ef9eccb3a012c6ca416ddf3969450fd8fc9'),
48
+ dict(path='std/loggers.py', sha1='a569179445d6a8a942b5dcfad1d1f77702868803'),
49
+ dict(path='_amalg.py', sha1='ae5189de25ab155651a5b2f21dd0baf6eb4f3916'),
50
+ ],
51
+ )
52
+
53
+
33
54
  ########################################
34
55
 
35
56
 
57
+ # ../lite/abstract.py
58
+ T = ta.TypeVar('T')
59
+
36
60
  # levels.py
37
61
  LogLevel = int # ta.TypeAlias
38
62
 
@@ -46,9 +70,6 @@ LoggingContextInfo = ta.Any # ta.TypeAlias
46
70
  # contexts.py
47
71
  LoggingContextInfoT = ta.TypeVar('LoggingContextInfoT', bound=LoggingContextInfo)
48
72
 
49
- # base.py
50
- T = ta.TypeVar('T')
51
-
52
73
 
53
74
  ########################################
54
75
  # ../../lite/abstract.py
@@ -65,25 +86,49 @@ def is_abstract_method(obj: ta.Any) -> bool:
65
86
  return bool(getattr(obj, _IS_ABSTRACT_METHOD_ATTR, False))
66
87
 
67
88
 
68
- def update_abstracts(cls, *, force=False):
89
+ def compute_abstract_methods(cls: type) -> ta.FrozenSet[str]:
90
+ # ~> https://github.com/python/cpython/blob/f3476c6507381ca860eec0989f53647b13517423/Modules/_abc.c#L358
91
+
92
+ # Stage 1: direct abstract methods
93
+
94
+ abstracts = {
95
+ a
96
+ # Get items as a list to avoid mutation issues during iteration
97
+ for a, v in list(cls.__dict__.items())
98
+ if is_abstract_method(v)
99
+ }
100
+
101
+ # Stage 2: inherited abstract methods
102
+
103
+ for base in cls.__bases__:
104
+ # Get __abstractmethods__ from base if it exists
105
+ if (base_abstracts := getattr(base, _ABSTRACT_METHODS_ATTR, None)) is None:
106
+ continue
107
+
108
+ # Iterate over abstract methods in base
109
+ for key in base_abstracts:
110
+ # Check if this class has an attribute with this name
111
+ try:
112
+ value = getattr(cls, key)
113
+ except AttributeError:
114
+ # Attribute not found in this class, skip
115
+ continue
116
+
117
+ # Check if it's still abstract
118
+ if is_abstract_method(value):
119
+ abstracts.add(key)
120
+
121
+ return frozenset(abstracts)
122
+
123
+
124
+ def update_abstracts(cls: ta.Type[T], *, force: bool = False) -> ta.Type[T]:
69
125
  if not force and not hasattr(cls, _ABSTRACT_METHODS_ATTR):
70
126
  # Per stdlib: We check for __abstractmethods__ here because cls might by a C implementation or a python
71
127
  # implementation (especially during testing), and we want to handle both cases.
72
128
  return cls
73
129
 
74
- abstracts: ta.Set[str] = set()
75
-
76
- for scls in cls.__bases__:
77
- for name in getattr(scls, _ABSTRACT_METHODS_ATTR, ()):
78
- value = getattr(cls, name, None)
79
- if getattr(value, _IS_ABSTRACT_METHOD_ATTR, False):
80
- abstracts.add(name)
81
-
82
- for name, value in cls.__dict__.items():
83
- if getattr(value, _IS_ABSTRACT_METHOD_ATTR, False):
84
- abstracts.add(name)
85
-
86
- setattr(cls, _ABSTRACT_METHODS_ATTR, frozenset(abstracts))
130
+ abstracts = compute_abstract_methods(cls)
131
+ setattr(cls, _ABSTRACT_METHODS_ATTR, abstracts)
87
132
  return cls
88
133
 
89
134
 
@@ -137,23 +182,26 @@ class Abstract:
137
182
  super().__init_subclass__(**kwargs)
138
183
 
139
184
  if not (Abstract in cls.__bases__ or abc.ABC in cls.__bases__):
140
- ams = {a: cls for a, o in cls.__dict__.items() if is_abstract_method(o)}
141
-
142
- seen = set(cls.__dict__)
143
- for b in cls.__bases__:
144
- ams.update({a: b for a in set(getattr(b, _ABSTRACT_METHODS_ATTR, [])) - seen}) # noqa
145
- seen.update(dir(b))
185
+ if ams := compute_abstract_methods(cls):
186
+ amd = {
187
+ a: mcls
188
+ for mcls in cls.__mro__[::-1]
189
+ for a in ams
190
+ if a in mcls.__dict__
191
+ }
146
192
 
147
- if ams:
148
193
  raise AbstractTypeError(
149
194
  f'Cannot subclass abstract class {cls.__name__} with abstract methods: ' +
150
195
  ', '.join(sorted([
151
196
  '.'.join([
152
- *([m] if (m := getattr(c, '__module__')) else []),
153
- getattr(c, '__qualname__', getattr(c, '__name__')),
197
+ *([
198
+ *([m] if (m := getattr(c, '__module__')) else []),
199
+ getattr(c, '__qualname__', getattr(c, '__name__')),
200
+ ] if c is not None else '?'),
154
201
  a,
155
202
  ])
156
- for a, c in ams.items()
203
+ for a in ams
204
+ for c in [amd.get(a)]
157
205
  ])),
158
206
  )
159
207
 
@@ -954,6 +1002,9 @@ class CaptureLoggingContextImpl(CaptureLoggingContext):
954
1002
  self._infos[type(info)] = info
955
1003
  return self
956
1004
 
1005
+ def get_infos(self) -> ta.Mapping[ta.Type[LoggingContextInfo], LoggingContextInfo]:
1006
+ return self._infos
1007
+
957
1008
  def get_info(self, ty: ta.Type[LoggingContextInfoT]) -> ta.Optional[LoggingContextInfoT]:
958
1009
  return self._infos.get(ty)
959
1010
 
@@ -976,7 +1027,7 @@ class CaptureLoggingContextImpl(CaptureLoggingContext):
976
1027
  _stack_offset: int
977
1028
  _stack_info: bool
978
1029
 
979
- def inc_stack_offset(self, ofs: int = 1) -> 'CaptureLoggingContext':
1030
+ def inc_stack_offset(self, ofs: int = 1) -> 'CaptureLoggingContextImpl':
980
1031
  if hasattr(self, '_stack_offset'):
981
1032
  self._stack_offset += ofs
982
1033
  return self
@@ -1008,10 +1059,9 @@ class CaptureLoggingContextImpl(CaptureLoggingContext):
1008
1059
 
1009
1060
 
1010
1061
  ########################################
1011
- # ../standard.py
1062
+ # ../std/standard.py
1012
1063
  """
1013
1064
  TODO:
1014
- - !! move to std !!
1015
1065
  - structured
1016
1066
  - prefixed
1017
1067
  - debug
@@ -1161,6 +1211,11 @@ class AnyLogger(Abstract, ta.Generic[T]):
1161
1211
 
1162
1212
  ##
1163
1213
 
1214
+ # This will be 1 for [Sync]Logger and 0 for AsyncLogger - in sync loggers these methods remain present on the stack,
1215
+ # in async loggers they return a coroutine to be awaited and thus aren't actually present when said coroutine is
1216
+ # awaited.
1217
+ _level_proxy_method_stack_offset: int
1218
+
1164
1219
  @ta.overload
1165
1220
  def log(self, level: LogLevel, msg: str, *args: ta.Any, **kwargs: ta.Any) -> T:
1166
1221
  ...
@@ -1175,7 +1230,14 @@ class AnyLogger(Abstract, ta.Generic[T]):
1175
1230
 
1176
1231
  @ta.final
1177
1232
  def log(self, level: LogLevel, *args, **kwargs):
1178
- return self._log(CaptureLoggingContextImpl(level, stack_offset=1), *args, **kwargs)
1233
+ return self._log(
1234
+ CaptureLoggingContextImpl(
1235
+ level,
1236
+ stack_offset=self._level_proxy_method_stack_offset,
1237
+ ),
1238
+ *args,
1239
+ **kwargs,
1240
+ )
1179
1241
 
1180
1242
  #
1181
1243
 
@@ -1193,7 +1255,14 @@ class AnyLogger(Abstract, ta.Generic[T]):
1193
1255
 
1194
1256
  @ta.final
1195
1257
  def debug(self, *args, **kwargs):
1196
- return self._log(CaptureLoggingContextImpl(NamedLogLevel.DEBUG, stack_offset=1), *args, **kwargs)
1258
+ return self._log(
1259
+ CaptureLoggingContextImpl(
1260
+ NamedLogLevel.DEBUG,
1261
+ stack_offset=self._level_proxy_method_stack_offset,
1262
+ ),
1263
+ *args,
1264
+ **kwargs,
1265
+ )
1197
1266
 
1198
1267
  #
1199
1268
 
@@ -1211,7 +1280,14 @@ class AnyLogger(Abstract, ta.Generic[T]):
1211
1280
 
1212
1281
  @ta.final
1213
1282
  def info(self, *args, **kwargs):
1214
- return self._log(CaptureLoggingContextImpl(NamedLogLevel.INFO, stack_offset=1), *args, **kwargs)
1283
+ return self._log(
1284
+ CaptureLoggingContextImpl(
1285
+ NamedLogLevel.INFO,
1286
+ stack_offset=self._level_proxy_method_stack_offset,
1287
+ ),
1288
+ *args,
1289
+ **kwargs,
1290
+ )
1215
1291
 
1216
1292
  #
1217
1293
 
@@ -1229,7 +1305,14 @@ class AnyLogger(Abstract, ta.Generic[T]):
1229
1305
 
1230
1306
  @ta.final
1231
1307
  def warning(self, *args, **kwargs):
1232
- return self._log(CaptureLoggingContextImpl(NamedLogLevel.WARNING, stack_offset=1), *args, **kwargs)
1308
+ return self._log(
1309
+ CaptureLoggingContextImpl(
1310
+ NamedLogLevel.WARNING,
1311
+ stack_offset=self._level_proxy_method_stack_offset,
1312
+ ),
1313
+ *args,
1314
+ **kwargs,
1315
+ )
1233
1316
 
1234
1317
  #
1235
1318
 
@@ -1247,7 +1330,14 @@ class AnyLogger(Abstract, ta.Generic[T]):
1247
1330
 
1248
1331
  @ta.final
1249
1332
  def error(self, *args, **kwargs):
1250
- return self._log(CaptureLoggingContextImpl(NamedLogLevel.ERROR, stack_offset=1), *args, **kwargs)
1333
+ return self._log(
1334
+ CaptureLoggingContextImpl(
1335
+ NamedLogLevel.ERROR,
1336
+ stack_offset=self._level_proxy_method_stack_offset,
1337
+ ),
1338
+ *args,
1339
+ **kwargs,
1340
+ )
1251
1341
 
1252
1342
  #
1253
1343
 
@@ -1265,7 +1355,15 @@ class AnyLogger(Abstract, ta.Generic[T]):
1265
1355
 
1266
1356
  @ta.final
1267
1357
  def exception(self, *args, exc_info: LoggingExcInfoArg = True, **kwargs):
1268
- return self._log(CaptureLoggingContextImpl(NamedLogLevel.ERROR, exc_info=exc_info, stack_offset=1), *args, **kwargs) # noqa
1358
+ return self._log(
1359
+ CaptureLoggingContextImpl(
1360
+ NamedLogLevel.ERROR,
1361
+ exc_info=exc_info,
1362
+ stack_offset=self._level_proxy_method_stack_offset,
1363
+ ),
1364
+ *args,
1365
+ **kwargs,
1366
+ )
1269
1367
 
1270
1368
  #
1271
1369
 
@@ -1283,24 +1381,53 @@ class AnyLogger(Abstract, ta.Generic[T]):
1283
1381
 
1284
1382
  @ta.final
1285
1383
  def critical(self, *args, **kwargs):
1286
- return self._log(CaptureLoggingContextImpl(NamedLogLevel.CRITICAL, stack_offset=1), *args, **kwargs)
1384
+ return self._log(
1385
+ CaptureLoggingContextImpl(
1386
+ NamedLogLevel.CRITICAL,
1387
+ stack_offset=self._level_proxy_method_stack_offset,
1388
+ ),
1389
+ *args,
1390
+ **kwargs,
1391
+ )
1287
1392
 
1288
1393
  ##
1289
1394
 
1290
1395
  @abc.abstractmethod
1291
- def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> T: # noqa
1396
+ def _log(
1397
+ self,
1398
+ ctx: CaptureLoggingContext,
1399
+ msg: ta.Union[str, tuple, LoggingMsgFn],
1400
+ *args: ta.Any,
1401
+ **kwargs: ta.Any,
1402
+ ) -> T:
1292
1403
  raise NotImplementedError
1293
1404
 
1294
1405
 
1295
1406
  class Logger(AnyLogger[None], Abstract):
1407
+ _level_proxy_method_stack_offset: int = 1
1408
+
1296
1409
  @abc.abstractmethod
1297
- def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> None: # noqa
1410
+ def _log(
1411
+ self,
1412
+ ctx: CaptureLoggingContext,
1413
+ msg: ta.Union[str, tuple, LoggingMsgFn],
1414
+ *args: ta.Any,
1415
+ **kwargs: ta.Any,
1416
+ ) -> None:
1298
1417
  raise NotImplementedError
1299
1418
 
1300
1419
 
1301
1420
  class AsyncLogger(AnyLogger[ta.Awaitable[None]], Abstract):
1421
+ _level_proxy_method_stack_offset: int = 0
1422
+
1302
1423
  @abc.abstractmethod
1303
- def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> ta.Awaitable[None]: # noqa
1424
+ def _log(
1425
+ self,
1426
+ ctx: CaptureLoggingContext,
1427
+ msg: ta.Union[str, tuple, LoggingMsgFn],
1428
+ *args: ta.Any,
1429
+ **kwargs: ta.Any,
1430
+ ) -> ta.Awaitable[None]:
1304
1431
  raise NotImplementedError
1305
1432
 
1306
1433
 
@@ -1315,13 +1442,25 @@ class AnyNopLogger(AnyLogger[T], Abstract):
1315
1442
 
1316
1443
  @ta.final
1317
1444
  class NopLogger(AnyNopLogger[None], Logger):
1318
- def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> None: # noqa
1445
+ def _log(
1446
+ self,
1447
+ ctx: CaptureLoggingContext,
1448
+ msg: ta.Union[str, tuple, LoggingMsgFn],
1449
+ *args: ta.Any,
1450
+ **kwargs: ta.Any,
1451
+ ) -> None:
1319
1452
  pass
1320
1453
 
1321
1454
 
1322
1455
  @ta.final
1323
1456
  class AsyncNopLogger(AnyNopLogger[ta.Awaitable[None]], AsyncLogger):
1324
- async def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any, **kwargs: ta.Any) -> None: # noqa
1457
+ async def _log(
1458
+ self,
1459
+ ctx: CaptureLoggingContext,
1460
+ msg: ta.Union[str, tuple, LoggingMsgFn],
1461
+ *args: ta.Any,
1462
+ **kwargs: ta.Any,
1463
+ ) -> None:
1325
1464
  pass
1326
1465
 
1327
1466
 
@@ -2006,7 +2145,12 @@ class StdLogger(Logger):
2006
2145
  def get_effective_level(self) -> LogLevel:
2007
2146
  return self._std.getEffectiveLevel()
2008
2147
 
2009
- def _log(self, ctx: CaptureLoggingContext, msg: ta.Union[str, tuple, LoggingMsgFn], *args: ta.Any) -> None:
2148
+ def _log(
2149
+ self,
2150
+ ctx: CaptureLoggingContext,
2151
+ msg: ta.Union[str, tuple, LoggingMsgFn],
2152
+ *args: ta.Any,
2153
+ ) -> None:
2010
2154
  if not self.is_enabled_for(ctx.must_get_info(LoggingContextInfos.Level).level):
2011
2155
  return
2012
2156
 
@@ -37,11 +37,26 @@ if sys.version_info < (3, 8):
37
37
  raise OSError(f'Requires python (3, 8), got {sys.version_info} from {sys.executable}') # noqa
38
38
 
39
39
 
40
+ def __omlish_amalg__(): # noqa
41
+ return dict(
42
+ src_files=[
43
+ dict(path='abstract.py', sha1='a2fc3f3697fa8de5247761e9d554e70176f37aac'),
44
+ dict(path='check.py', sha1='bb6b6b63333699b84462951a854d99ae83195b94'),
45
+ dict(path='objects.py', sha1='9566bbf3530fd71fcc56321485216b592fae21e9'),
46
+ dict(path='reflect.py', sha1='c4fec44bf144e9d93293c996af06f6c65fc5e63d'),
47
+ dict(path='strings.py', sha1='89831ecbc34ad80e118a865eceb390ed399dc4d6'),
48
+ dict(path='marshal.py', sha1='96348f5f2a26dc27d842d33cc3927e9da163436b'),
49
+ ],
50
+ )
51
+
52
+
40
53
  ########################################
41
54
 
42
55
 
43
- # check.py
56
+ # abstract.py
44
57
  T = ta.TypeVar('T')
58
+
59
+ # check.py
45
60
  SizedT = ta.TypeVar('SizedT', bound=ta.Sized)
46
61
  CheckMessage = ta.Union[str, ta.Callable[..., ta.Optional[str]], None] # ta.TypeAlias
47
62
  CheckLateConfigureFn = ta.Callable[['Checks'], None] # ta.TypeAlias
@@ -65,25 +80,49 @@ def is_abstract_method(obj: ta.Any) -> bool:
65
80
  return bool(getattr(obj, _IS_ABSTRACT_METHOD_ATTR, False))
66
81
 
67
82
 
68
- def update_abstracts(cls, *, force=False):
83
+ def compute_abstract_methods(cls: type) -> ta.FrozenSet[str]:
84
+ # ~> https://github.com/python/cpython/blob/f3476c6507381ca860eec0989f53647b13517423/Modules/_abc.c#L358
85
+
86
+ # Stage 1: direct abstract methods
87
+
88
+ abstracts = {
89
+ a
90
+ # Get items as a list to avoid mutation issues during iteration
91
+ for a, v in list(cls.__dict__.items())
92
+ if is_abstract_method(v)
93
+ }
94
+
95
+ # Stage 2: inherited abstract methods
96
+
97
+ for base in cls.__bases__:
98
+ # Get __abstractmethods__ from base if it exists
99
+ if (base_abstracts := getattr(base, _ABSTRACT_METHODS_ATTR, None)) is None:
100
+ continue
101
+
102
+ # Iterate over abstract methods in base
103
+ for key in base_abstracts:
104
+ # Check if this class has an attribute with this name
105
+ try:
106
+ value = getattr(cls, key)
107
+ except AttributeError:
108
+ # Attribute not found in this class, skip
109
+ continue
110
+
111
+ # Check if it's still abstract
112
+ if is_abstract_method(value):
113
+ abstracts.add(key)
114
+
115
+ return frozenset(abstracts)
116
+
117
+
118
+ def update_abstracts(cls: ta.Type[T], *, force: bool = False) -> ta.Type[T]:
69
119
  if not force and not hasattr(cls, _ABSTRACT_METHODS_ATTR):
70
120
  # Per stdlib: We check for __abstractmethods__ here because cls might by a C implementation or a python
71
121
  # implementation (especially during testing), and we want to handle both cases.
72
122
  return cls
73
123
 
74
- abstracts: ta.Set[str] = set()
75
-
76
- for scls in cls.__bases__:
77
- for name in getattr(scls, _ABSTRACT_METHODS_ATTR, ()):
78
- value = getattr(cls, name, None)
79
- if getattr(value, _IS_ABSTRACT_METHOD_ATTR, False):
80
- abstracts.add(name)
81
-
82
- for name, value in cls.__dict__.items():
83
- if getattr(value, _IS_ABSTRACT_METHOD_ATTR, False):
84
- abstracts.add(name)
85
-
86
- setattr(cls, _ABSTRACT_METHODS_ATTR, frozenset(abstracts))
124
+ abstracts = compute_abstract_methods(cls)
125
+ setattr(cls, _ABSTRACT_METHODS_ATTR, abstracts)
87
126
  return cls
88
127
 
89
128
 
@@ -137,23 +176,26 @@ class Abstract:
137
176
  super().__init_subclass__(**kwargs)
138
177
 
139
178
  if not (Abstract in cls.__bases__ or abc.ABC in cls.__bases__):
140
- ams = {a: cls for a, o in cls.__dict__.items() if is_abstract_method(o)}
141
-
142
- seen = set(cls.__dict__)
143
- for b in cls.__bases__:
144
- ams.update({a: b for a in set(getattr(b, _ABSTRACT_METHODS_ATTR, [])) - seen}) # noqa
145
- seen.update(dir(b))
179
+ if ams := compute_abstract_methods(cls):
180
+ amd = {
181
+ a: mcls
182
+ for mcls in cls.__mro__[::-1]
183
+ for a in ams
184
+ if a in mcls.__dict__
185
+ }
146
186
 
147
- if ams:
148
187
  raise AbstractTypeError(
149
188
  f'Cannot subclass abstract class {cls.__name__} with abstract methods: ' +
150
189
  ', '.join(sorted([
151
190
  '.'.join([
152
- *([m] if (m := getattr(c, '__module__')) else []),
153
- getattr(c, '__qualname__', getattr(c, '__name__')),
191
+ *([
192
+ *([m] if (m := getattr(c, '__module__')) else []),
193
+ getattr(c, '__qualname__', getattr(c, '__name__')),
194
+ ] if c is not None else '?'),
154
195
  a,
155
196
  ])
156
- for a, c in ams.items()
197
+ for a in ams
198
+ for c in [amd.get(a)]
157
199
  ])),
158
200
  )
159
201