ominfra 0.0.0.dev438__py3-none-any.whl → 0.0.0.dev483__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 ominfra might be problematic. Click here for more details.

@@ -54,6 +54,57 @@ if sys.version_info < (3, 8):
54
54
  raise OSError(f'Requires python (3, 8), got {sys.version_info} from {sys.executable}') # noqa
55
55
 
56
56
 
57
+ def __omlish_amalg__(): # noqa
58
+ return dict(
59
+ src_files=[
60
+ dict(path='../../../../omlish/configs/types.py', sha1='f7a5584cd6eccb77d18d729796072a162e9a8790'),
61
+ dict(path='../../../../omlish/formats/ini/sections.py', sha1='731c92cce82e183d1d4bdc23fc781fad62187394'),
62
+ dict(path='../../../../omlish/formats/toml/parser.py', sha1='73dac82289350ab951c4bcdbfe61167fa221f26f'),
63
+ dict(path='../../../../omlish/formats/toml/writer.py', sha1='6ea41d7e724bb1dcf6bd84b88993ff4e8798e021'),
64
+ dict(path='../../../../omlish/io/readers.py', sha1='4b19ab4a87f2fa2a6f6c3cad7e1f3892b7cbd3a4'),
65
+ dict(path='../../../../omlish/lite/abstract.py', sha1='a2fc3f3697fa8de5247761e9d554e70176f37aac'),
66
+ dict(path='../../../../omlish/lite/attrops.py', sha1='c1ebfb8573d766d34593c452a2377208d02726dc'),
67
+ dict(path='../../../../omlish/lite/cached.py', sha1='0c33cf961ac8f0727284303c7a30c5ea98f714f2'),
68
+ dict(path='../../../../omlish/lite/check.py', sha1='bb6b6b63333699b84462951a854d99ae83195b94'),
69
+ dict(path='../../../../omlish/lite/contextmanagers.py', sha1='993f5ed96d3410f739a20363f55670d5e5267fa3'),
70
+ dict(path='../../../../omlish/lite/json.py', sha1='57eeddc4d23a17931e00284ffa5cb6e3ce089486'),
71
+ dict(path='../../../../omlish/lite/objects.py', sha1='9566bbf3530fd71fcc56321485216b592fae21e9'),
72
+ dict(path='../../../../omlish/lite/reflect.py', sha1='c4fec44bf144e9d93293c996af06f6c65fc5e63d'),
73
+ dict(path='../../../../omlish/lite/strings.py', sha1='89831ecbc34ad80e118a865eceb390ed399dc4d6'),
74
+ dict(path='../../../../omlish/logs/levels.py', sha1='91405563d082a5eba874da82aac89d83ce7b6152'),
75
+ dict(path='../../../../omlish/logs/std/filters.py', sha1='f36aab646d84d31e295b33aaaaa6f8b67ff38b3d'),
76
+ dict(path='../../../../omlish/logs/std/proxy.py', sha1='3e7301a2aa351127f9c85f61b2f85dcc3f15aafb'),
77
+ dict(path='../../../../omlish/logs/warnings.py', sha1='c4eb694b24773351107fcc058f3620f1dbfb6799'),
78
+ dict(path='../../../../omlish/os/pidfiles/pidfile.py', sha1='7c3c6d4674855bfc4f1d7fd77b8fa40f36581535'),
79
+ dict(path='../../../../omlish/subprocesses/utils.py', sha1='2210d90ab1bfc75642aa2f4caad662368900aa1c'),
80
+ dict(path='../auth.py', sha1='b1ac1a5e03d4e9e38957a54e346943c6dcc964a1'),
81
+ dict(path='../dataclasses.py', sha1='8e950d7815904588fed284889392cbb0b1002605'),
82
+ dict(path='../../../../omlish/configs/formats.py', sha1='9bc4f953b4b8700f6f109e6f49e2d70f8e48ce7c'),
83
+ dict(path='../../../../omlish/io/buffers.py', sha1='45a5f79c6d71f02ab82082a48d63ebbd10959031'),
84
+ dict(path='../../../../omlish/lite/marshal.py', sha1='96348f5f2a26dc27d842d33cc3927e9da163436b'),
85
+ dict(path='../../../../omlish/lite/runtime.py', sha1='2e752a27ae2bf89b1bb79b4a2da522a3ec360c70'),
86
+ dict(path='../../../../omlish/logs/infos.py', sha1='4dd104bd468a8c438601dd0bbda619b47d2f1620'),
87
+ dict(path='../../../../omlish/logs/std/json.py', sha1='2a75553131e4d5331bb0cedde42aa183f403fc3b'),
88
+ dict(path='../logs.py', sha1='5a4fad522508bdc1b790f1d5234a87f319c9da2d'),
89
+ dict(path='../../../../omlish/lite/configs.py', sha1='c8602e0e197ef1133e7e8e248935ac745bfd46cb'),
90
+ dict(path='../../../../omlish/logs/contexts.py', sha1='7456964ade9ac66460e9ade4e242dbdc24b39501'),
91
+ dict(path='../../../../omlish/logs/standard.py', sha1='818b674f7d15012f25b79f52f6e8e7368b633038'),
92
+ dict(path='../../../../omlish/subprocesses/wrap.py', sha1='8a9b7d2255481fae15c05f5624b0cdc0766f4b3f'),
93
+ dict(path='../../../../omlish/logs/base.py', sha1='a376460b11b9dc0555fd4ead5437af62c2109a4b'),
94
+ dict(path='../../../../omlish/logs/std/records.py', sha1='8bbf6ef9eccb3a012c6ca416ddf3969450fd8fc9'),
95
+ dict(path='../../../../omlish/logs/std/loggers.py', sha1='daa35bdc4adea5006e442688017f0de3392579b7'),
96
+ dict(path='../../../../omlish/logs/modules.py', sha1='99e73cde6872fd5eda6af3dbf0fc9322bdeb641a'),
97
+ dict(path='cursor.py', sha1='00f1c62e16e4c85b20658eaf33c0bedf22c9e18f'),
98
+ dict(path='../../../journald/messages.py', sha1='6f2d2eeedb71723b1c6631ad2e634b473b297696'),
99
+ dict(path='../../../threadworkers.py', sha1='e3413436070b66faeb3e6974dc9a75cd8a949ad7'),
100
+ dict(path='poster.py', sha1='275770a4e60ea5777053c9044e37d71397c3ed20'),
101
+ dict(path='../../../journald/tailer.py', sha1='1c37a6bbde32e8556d26b94b439f14869731ac01'),
102
+ dict(path='driver.py', sha1='a9353a417fc4e57f29e7f04038c3bf4668f36dbd'),
103
+ dict(path='main.py', sha1='e051dbef37e026f887b6084cfaa811ccfa543ee7'),
104
+ ],
105
+ )
106
+
107
+
57
108
  ########################################
58
109
 
59
110
 
@@ -68,7 +119,7 @@ TomlParseFloat = ta.Callable[[str], ta.Any] # ta.TypeAlias
68
119
  TomlKey = ta.Tuple[str, ...] # ta.TypeAlias
69
120
  TomlPos = int # ta.TypeAlias
70
121
 
71
- # ../../../../omlish/lite/attrops.py
122
+ # ../../../../omlish/lite/abstract.py
72
123
  T = ta.TypeVar('T')
73
124
 
74
125
  # ../../../../omlish/lite/cached.py
@@ -1200,6 +1251,36 @@ class TomlWriter:
1200
1251
  return out.getvalue()
1201
1252
 
1202
1253
 
1254
+ ########################################
1255
+ # ../../../../../omlish/io/readers.py
1256
+
1257
+
1258
+ ##
1259
+
1260
+
1261
+ class RawBytesReader(ta.Protocol):
1262
+ def read1(self, n: int = -1, /) -> bytes: ...
1263
+
1264
+
1265
+ class BufferedBytesReader(RawBytesReader, ta.Protocol):
1266
+ def read(self, n: int = -1, /) -> bytes: ...
1267
+
1268
+ def readall(self) -> bytes: ...
1269
+
1270
+
1271
+ #
1272
+
1273
+
1274
+ class AsyncRawBytesReader(ta.Protocol):
1275
+ def read1(self, n: int = -1, /) -> ta.Awaitable[bytes]: ...
1276
+
1277
+
1278
+ class AsyncBufferedBytesReader(AsyncRawBytesReader, ta.Protocol):
1279
+ def read(self, n: int = -1, /) -> ta.Awaitable[bytes]: ...
1280
+
1281
+ def readall(self) -> ta.Awaitable[bytes]: ...
1282
+
1283
+
1203
1284
  ########################################
1204
1285
  # ../../../../../omlish/lite/abstract.py
1205
1286
 
@@ -1215,25 +1296,49 @@ def is_abstract_method(obj: ta.Any) -> bool:
1215
1296
  return bool(getattr(obj, _IS_ABSTRACT_METHOD_ATTR, False))
1216
1297
 
1217
1298
 
1218
- def update_abstracts(cls, *, force=False):
1299
+ def compute_abstract_methods(cls: type) -> ta.FrozenSet[str]:
1300
+ # ~> https://github.com/python/cpython/blob/f3476c6507381ca860eec0989f53647b13517423/Modules/_abc.c#L358
1301
+
1302
+ # Stage 1: direct abstract methods
1303
+
1304
+ abstracts = {
1305
+ a
1306
+ # Get items as a list to avoid mutation issues during iteration
1307
+ for a, v in list(cls.__dict__.items())
1308
+ if is_abstract_method(v)
1309
+ }
1310
+
1311
+ # Stage 2: inherited abstract methods
1312
+
1313
+ for base in cls.__bases__:
1314
+ # Get __abstractmethods__ from base if it exists
1315
+ if (base_abstracts := getattr(base, _ABSTRACT_METHODS_ATTR, None)) is None:
1316
+ continue
1317
+
1318
+ # Iterate over abstract methods in base
1319
+ for key in base_abstracts:
1320
+ # Check if this class has an attribute with this name
1321
+ try:
1322
+ value = getattr(cls, key)
1323
+ except AttributeError:
1324
+ # Attribute not found in this class, skip
1325
+ continue
1326
+
1327
+ # Check if it's still abstract
1328
+ if is_abstract_method(value):
1329
+ abstracts.add(key)
1330
+
1331
+ return frozenset(abstracts)
1332
+
1333
+
1334
+ def update_abstracts(cls: ta.Type[T], *, force: bool = False) -> ta.Type[T]:
1219
1335
  if not force and not hasattr(cls, _ABSTRACT_METHODS_ATTR):
1220
1336
  # Per stdlib: We check for __abstractmethods__ here because cls might by a C implementation or a python
1221
1337
  # implementation (especially during testing), and we want to handle both cases.
1222
1338
  return cls
1223
1339
 
1224
- abstracts: ta.Set[str] = set()
1225
-
1226
- for scls in cls.__bases__:
1227
- for name in getattr(scls, _ABSTRACT_METHODS_ATTR, ()):
1228
- value = getattr(cls, name, None)
1229
- if getattr(value, _IS_ABSTRACT_METHOD_ATTR, False):
1230
- abstracts.add(name)
1231
-
1232
- for name, value in cls.__dict__.items():
1233
- if getattr(value, _IS_ABSTRACT_METHOD_ATTR, False):
1234
- abstracts.add(name)
1235
-
1236
- setattr(cls, _ABSTRACT_METHODS_ATTR, frozenset(abstracts))
1340
+ abstracts = compute_abstract_methods(cls)
1341
+ setattr(cls, _ABSTRACT_METHODS_ATTR, abstracts)
1237
1342
  return cls
1238
1343
 
1239
1344
 
@@ -1287,23 +1392,26 @@ class Abstract:
1287
1392
  super().__init_subclass__(**kwargs)
1288
1393
 
1289
1394
  if not (Abstract in cls.__bases__ or abc.ABC in cls.__bases__):
1290
- ams = {a: cls for a, o in cls.__dict__.items() if is_abstract_method(o)}
1291
-
1292
- seen = set(cls.__dict__)
1293
- for b in cls.__bases__:
1294
- ams.update({a: b for a in set(getattr(b, _ABSTRACT_METHODS_ATTR, [])) - seen}) # noqa
1295
- seen.update(dir(b))
1395
+ if ams := compute_abstract_methods(cls):
1396
+ amd = {
1397
+ a: mcls
1398
+ for mcls in cls.__mro__[::-1]
1399
+ for a in ams
1400
+ if a in mcls.__dict__
1401
+ }
1296
1402
 
1297
- if ams:
1298
1403
  raise AbstractTypeError(
1299
1404
  f'Cannot subclass abstract class {cls.__name__} with abstract methods: ' +
1300
1405
  ', '.join(sorted([
1301
1406
  '.'.join([
1302
- *([m] if (m := getattr(c, '__module__')) else []),
1303
- getattr(c, '__qualname__', getattr(c, '__name__')),
1407
+ *([
1408
+ *([m] if (m := getattr(c, '__module__')) else []),
1409
+ getattr(c, '__qualname__', getattr(c, '__name__')),
1410
+ ] if c is not None else '?'),
1304
1411
  a,
1305
1412
  ])
1306
- for a, c in ams.items()
1413
+ for a in ams
1414
+ for c in [amd.get(a)]
1307
1415
  ])),
1308
1416
  )
1309
1417
 
@@ -1328,6 +1436,8 @@ TODO:
1328
1436
  - per-attr repr transform / filter
1329
1437
  - __ne__ ? cases where it still matters
1330
1438
  - ordering ?
1439
+ - repr_filter: ta.Union[ta.Callable[[ta.Any], ta.Optional[str]], ta.Literal['not_none', 'truthy']]] ?
1440
+ - unify repr/repr_fn/repr_filter
1331
1441
  """
1332
1442
 
1333
1443
 
@@ -1345,6 +1455,8 @@ class AttrOps(ta.Generic[T]):
1345
1455
  display: ta.Optional[str] = None,
1346
1456
 
1347
1457
  repr: bool = True, # noqa
1458
+ repr_fn: ta.Optional[ta.Callable[[ta.Any], ta.Optional[str]]] = None,
1459
+
1348
1460
  hash: bool = True, # noqa
1349
1461
  eq: bool = True,
1350
1462
  ) -> None:
@@ -1359,6 +1471,8 @@ class AttrOps(ta.Generic[T]):
1359
1471
  self._display = display
1360
1472
 
1361
1473
  self._repr = repr
1474
+ self._repr_fn = repr_fn
1475
+
1362
1476
  self._hash = hash
1363
1477
  self._eq = eq
1364
1478
 
@@ -1366,21 +1480,30 @@ class AttrOps(ta.Generic[T]):
1366
1480
  def of(
1367
1481
  cls,
1368
1482
  o: ta.Union[
1369
- str,
1370
- ta.Tuple[str, str],
1371
1483
  'AttrOps.Attr',
1484
+ str,
1485
+ ta.Tuple[str, ta.Union[str, ta.Mapping[str, ta.Any]]],
1486
+ ta.Mapping[str, ta.Any],
1372
1487
  ],
1373
1488
  ) -> 'AttrOps.Attr':
1374
1489
  if isinstance(o, AttrOps.Attr):
1375
1490
  return o
1376
1491
  elif isinstance(o, str):
1377
1492
  return cls(o)
1493
+ elif isinstance(o, tuple):
1494
+ name, x = o
1495
+ kw: ta.Mapping[str, ta.Any]
1496
+ if isinstance(x, str):
1497
+ kw = dict(display=x)
1498
+ elif isinstance(x, ta.Mapping):
1499
+ kw = x
1500
+ else:
1501
+ raise TypeError(x)
1502
+ return cls(name, **kw)
1503
+ elif isinstance(o, ta.Mapping):
1504
+ return cls(**o)
1378
1505
  else:
1379
- name, disp = o
1380
- return cls(
1381
- name,
1382
- display=disp,
1383
- )
1506
+ raise TypeError(o)
1384
1507
 
1385
1508
  @property
1386
1509
  def name(self) -> str:
@@ -1398,19 +1521,34 @@ class AttrOps(ta.Generic[T]):
1398
1521
  def eq(self) -> bool:
1399
1522
  return self._eq
1400
1523
 
1524
+ @staticmethod
1525
+ def opt_repr(o: ta.Any) -> ta.Optional[str]:
1526
+ return repr(o) if o is not None else None
1527
+
1528
+ @staticmethod
1529
+ def truthy_repr(o: ta.Any) -> ta.Optional[str]:
1530
+ return repr(o) if o else None
1531
+
1532
+ #
1533
+
1401
1534
  @ta.overload
1402
1535
  def __init__(
1403
1536
  self,
1404
1537
  *attrs: ta.Sequence[ta.Union[
1405
1538
  str,
1406
- ta.Tuple[str, str],
1539
+ ta.Tuple[str, ta.Union[str, ta.Mapping[str, ta.Any]]],
1540
+ ta.Mapping[str, ta.Any],
1407
1541
  Attr,
1408
1542
  ]],
1543
+
1409
1544
  with_module: bool = False,
1410
1545
  use_qualname: bool = False,
1411
1546
  with_id: bool = False,
1547
+ terse: bool = False,
1412
1548
  repr_filter: ta.Optional[ta.Callable[[ta.Any], bool]] = None,
1413
1549
  recursive: bool = False,
1550
+
1551
+ cache_hash: ta.Union[bool, str] = False,
1414
1552
  subtypes_eq: bool = False,
1415
1553
  ) -> None:
1416
1554
  ...
@@ -1420,16 +1558,20 @@ class AttrOps(ta.Generic[T]):
1420
1558
  self,
1421
1559
  attrs_fn: ta.Callable[[T], ta.Tuple[ta.Union[
1422
1560
  ta.Any,
1423
- ta.Tuple[str, ta.Any],
1561
+ ta.Tuple[ta.Any, ta.Union[str, ta.Mapping[str, ta.Any]]],
1424
1562
  Attr,
1425
1563
  ], ...]],
1426
1564
  /,
1427
1565
  *,
1566
+
1428
1567
  with_module: bool = False,
1429
1568
  use_qualname: bool = False,
1430
1569
  with_id: bool = False,
1570
+ terse: bool = False,
1431
1571
  repr_filter: ta.Optional[ta.Callable[[ta.Any], bool]] = None,
1432
1572
  recursive: bool = False,
1573
+
1574
+ cache_hash: ta.Union[bool, str] = False,
1433
1575
  subtypes_eq: bool = False,
1434
1576
  ) -> None:
1435
1577
  ...
@@ -1437,11 +1579,15 @@ class AttrOps(ta.Generic[T]):
1437
1579
  def __init__(
1438
1580
  self,
1439
1581
  *args,
1582
+
1440
1583
  with_module=False,
1441
1584
  use_qualname=False,
1442
1585
  with_id=False,
1586
+ terse=False,
1443
1587
  repr_filter=None,
1444
1588
  recursive=False,
1589
+
1590
+ cache_hash=False,
1445
1591
  subtypes_eq=False,
1446
1592
  ) -> None:
1447
1593
  if args and len(args) == 1 and callable(args[0]):
@@ -1452,8 +1598,11 @@ class AttrOps(ta.Generic[T]):
1452
1598
  self._with_module: bool = with_module
1453
1599
  self._use_qualname: bool = use_qualname
1454
1600
  self._with_id: bool = with_id
1601
+ self._terse: bool = terse
1455
1602
  self._repr_filter: ta.Optional[ta.Callable[[ta.Any], bool]] = repr_filter
1456
1603
  self._recursive: bool = recursive
1604
+
1605
+ self._cache_hash: ta.Union[bool, str] = cache_hash
1457
1606
  self._subtypes_eq: bool = subtypes_eq
1458
1607
 
1459
1608
  @property
@@ -1488,20 +1637,27 @@ class AttrOps(ta.Generic[T]):
1488
1637
 
1489
1638
  attrs: ta.List[AttrOps.Attr] = []
1490
1639
  for o in raw:
1491
- if isinstance(o, AttrOps.Attr):
1492
- attrs.append(o)
1640
+ if isinstance(o, (AttrOps.Attr, ta.Mapping)):
1641
+ attrs.append(AttrOps.Attr.of(o))
1493
1642
  continue
1494
1643
 
1644
+ kw: ta.Mapping[str, ta.Any]
1495
1645
  if isinstance(o, tuple):
1496
- disp, cap, = o
1646
+ cap, x = o
1647
+ if isinstance(x, str):
1648
+ kw = dict(display=x)
1649
+ elif isinstance(x, ta.Mapping):
1650
+ kw = x
1651
+ else:
1652
+ raise TypeError(x)
1497
1653
  else:
1498
- disp, cap = None, o
1654
+ cap, kw = o, {}
1499
1655
 
1500
1656
  path = tuple(rec(cap))
1501
1657
 
1502
1658
  attrs.append(AttrOps.Attr(
1503
1659
  '.'.join(path),
1504
- display=disp,
1660
+ **kw,
1505
1661
  ))
1506
1662
 
1507
1663
  return attrs
@@ -1518,19 +1674,27 @@ class AttrOps(ta.Generic[T]):
1518
1674
  pass
1519
1675
 
1520
1676
  def _repr(o: T) -> str:
1521
- vs = ', '.join(
1522
- f'{a._display}={v!r}' # noqa
1523
- for a in self._attrs
1524
- if a._repr # noqa
1525
- for v in [getattr(o, a._name)] # noqa
1526
- if self._repr_filter is None or self._repr_filter(v)
1527
- )
1677
+ vs: ta.List[str] = []
1678
+ for a in self._attrs:
1679
+ if not a._repr: # noqa
1680
+ continue
1681
+ v = getattr(o, a._name) # noqa
1682
+ if self._repr_filter is not None and not self._repr_filter(v):
1683
+ continue
1684
+ if (rfn := a._repr_fn) is None: # noqa
1685
+ rfn = repr
1686
+ if (vr := rfn(v)) is None:
1687
+ continue
1688
+ if self._terse:
1689
+ vs.append(vr)
1690
+ else:
1691
+ vs.append(f'{a._display}={vr}') # noqa
1528
1692
 
1529
1693
  return (
1530
1694
  f'{o.__class__.__module__ + "." if self._with_module else ""}'
1531
1695
  f'{o.__class__.__qualname__ if self._use_qualname else o.__class__.__name__}'
1532
1696
  f'{("@" + hex(id(o))[2:]) if self._with_id else ""}' # noqa
1533
- f'({vs})'
1697
+ f'({", ".join(vs)})'
1534
1698
  )
1535
1699
 
1536
1700
  if self._recursive:
@@ -1555,6 +1719,8 @@ class AttrOps(ta.Generic[T]):
1555
1719
 
1556
1720
  #
1557
1721
 
1722
+ _DEFAULT_CACHED_HASH_ATTR: ta.ClassVar[str] = '__cached_hash__'
1723
+
1558
1724
  _hash: ta.Callable[[T], int]
1559
1725
 
1560
1726
  @property
@@ -1564,13 +1730,33 @@ class AttrOps(ta.Generic[T]):
1564
1730
  except AttributeError:
1565
1731
  pass
1566
1732
 
1567
- def _hash(o: T) -> int:
1733
+ def _calc_hash(o: T) -> int:
1568
1734
  return hash(tuple(
1569
1735
  getattr(o, a._name) # noqa
1570
1736
  for a in self._attrs
1571
1737
  if a._hash # noqa
1572
1738
  ))
1573
1739
 
1740
+ if (ch := self._cache_hash) is not False:
1741
+ if ch is True:
1742
+ cha = self._DEFAULT_CACHED_HASH_ATTR
1743
+ elif isinstance(ch, str):
1744
+ cha = ch
1745
+ else:
1746
+ raise TypeError(ch)
1747
+
1748
+ def _cached_hash(o: T) -> int:
1749
+ try:
1750
+ return object.__getattribute__(o, cha)
1751
+ except AttributeError:
1752
+ object.__setattr__(o, cha, h := _calc_hash(o))
1753
+ return h
1754
+
1755
+ _hash = _cached_hash
1756
+
1757
+ else:
1758
+ _hash = _calc_hash
1759
+
1574
1760
  self._hash = _hash
1575
1761
  return _hash
1576
1762
 
@@ -1711,6 +1897,62 @@ def async_cached_nullary(fn): # ta.Callable[..., T]) -> ta.Callable[..., T]:
1711
1897
  return _AsyncCachedNullary(fn)
1712
1898
 
1713
1899
 
1900
+ ##
1901
+
1902
+
1903
+ cached_property = functools.cached_property
1904
+
1905
+
1906
+ class _cached_property: # noqa
1907
+ """Backported to pick up https://github.com/python/cpython/commit/056dfc71dce15f81887f0bd6da09d6099d71f979 ."""
1908
+
1909
+ def __init__(self, func):
1910
+ self.func = func
1911
+ self.attrname = None # noqa
1912
+ self.__doc__ = func.__doc__
1913
+ self.__module__ = func.__module__
1914
+
1915
+ _NOT_FOUND = object()
1916
+
1917
+ def __set_name__(self, owner, name):
1918
+ if self.attrname is None:
1919
+ self.attrname = name # noqa
1920
+ elif name != self.attrname:
1921
+ raise TypeError(
1922
+ f'Cannot assign the same cached_property to two different names ({self.attrname!r} and {name!r}).',
1923
+ )
1924
+
1925
+ def __get__(self, instance, owner=None):
1926
+ if instance is None:
1927
+ return self
1928
+ if self.attrname is None:
1929
+ raise TypeError('Cannot use cached_property instance without calling __set_name__ on it.')
1930
+
1931
+ try:
1932
+ cache = instance.__dict__
1933
+ except AttributeError: # not all objects have __dict__ (e.g. class defines slots)
1934
+ raise TypeError(
1935
+ f"No '__dict__' attribute on {type(instance).__name__!r} instance to cache {self.attrname!r} property.",
1936
+ ) from None
1937
+
1938
+ val = cache.get(self.attrname, self._NOT_FOUND)
1939
+
1940
+ if val is self._NOT_FOUND:
1941
+ val = self.func(instance)
1942
+ try:
1943
+ cache[self.attrname] = val
1944
+ except TypeError:
1945
+ raise TypeError(
1946
+ f"The '__dict__' attribute on {type(instance).__name__!r} instance does not support item "
1947
+ f"assignment for caching {self.attrname!r} property.",
1948
+ ) from None
1949
+
1950
+ return val
1951
+
1952
+
1953
+ globals()['cached_property'] = _cached_property
1954
+
1955
+
1714
1956
  ########################################
1715
1957
  # ../../../../../omlish/lite/check.py
1716
1958
  """
@@ -2250,7 +2492,7 @@ class ExitStacked:
2250
2492
  es.__enter__()
2251
2493
  try:
2252
2494
  self._enter_contexts()
2253
- except Exception: # noqa
2495
+ except BaseException: # noqa
2254
2496
  es.__exit__(*sys.exc_info())
2255
2497
  raise
2256
2498
  return self
@@ -2261,7 +2503,7 @@ class ExitStacked:
2261
2503
  return None
2262
2504
  try:
2263
2505
  self._exit_contexts()
2264
- except Exception: # noqa
2506
+ except BaseException: # noqa
2265
2507
  es.__exit__(*sys.exc_info())
2266
2508
  raise
2267
2509
  return es.__exit__(exc_type, exc_val, exc_tb)
@@ -2309,7 +2551,7 @@ class AsyncExitStacked:
2309
2551
  await es.__aenter__()
2310
2552
  try:
2311
2553
  await self._async_enter_contexts()
2312
- except Exception: # noqa
2554
+ except BaseException: # noqa
2313
2555
  await es.__aexit__(*sys.exc_info())
2314
2556
  raise
2315
2557
  return self
@@ -2320,7 +2562,7 @@ class AsyncExitStacked:
2320
2562
  return None
2321
2563
  try:
2322
2564
  await self._async_exit_contexts()
2323
- except Exception: # noqa
2565
+ except BaseException: # noqa
2324
2566
  await es.__aexit__(*sys.exc_info())
2325
2567
  raise
2326
2568
  return await es.__aexit__(exc_type, exc_val, exc_tb)
@@ -3747,6 +3989,10 @@ DEFAULT_CONFIG_RENDERER = SwitchedConfigRenderer(DEFAULT_CONFIG_RENDERERS)
3747
3989
 
3748
3990
  ########################################
3749
3991
  # ../../../../../omlish/io/buffers.py
3992
+ """
3993
+ TODO:
3994
+ - overhaul and just coro-ify pyio?
3995
+ """
3750
3996
 
3751
3997
 
3752
3998
  ##
@@ -3925,6 +4171,9 @@ class ReadableListBuffer:
3925
4171
 
3926
4172
  self._lst: list[bytes] = []
3927
4173
 
4174
+ def __bool__(self) -> ta.NoReturn:
4175
+ raise TypeError("Use 'buf is not None' or 'len(buf)'.")
4176
+
3928
4177
  def __len__(self) -> int:
3929
4178
  return sum(map(len, self._lst))
3930
4179
 
@@ -3950,6 +4199,9 @@ class ReadableListBuffer:
3950
4199
 
3951
4200
  def read(self, n: ta.Optional[int] = None) -> ta.Optional[bytes]:
3952
4201
  if n is None:
4202
+ if not self._lst:
4203
+ return b''
4204
+
3953
4205
  o = b''.join(self._lst)
3954
4206
  self._lst = []
3955
4207
  return o
@@ -3988,6 +4240,110 @@ class ReadableListBuffer:
3988
4240
  r = self.read_until_(delim)
3989
4241
  return r if isinstance(r, bytes) else None
3990
4242
 
4243
+ #
4244
+
4245
+ DEFAULT_BUFFERED_READER_CHUNK_SIZE: ta.ClassVar[int] = -1
4246
+
4247
+ @ta.final
4248
+ class _BufferedBytesReader(BufferedBytesReader):
4249
+ def __init__(
4250
+ self,
4251
+ raw: RawBytesReader,
4252
+ buf: 'ReadableListBuffer',
4253
+ *,
4254
+ chunk_size: ta.Optional[int] = None,
4255
+ ) -> None:
4256
+ self._raw = raw
4257
+ self._buf = buf
4258
+ self._chunk_size = chunk_size or ReadableListBuffer.DEFAULT_BUFFERED_READER_CHUNK_SIZE
4259
+
4260
+ def read1(self, n: int = -1, /) -> bytes:
4261
+ if n < 0:
4262
+ n = self._chunk_size
4263
+ if not n:
4264
+ return b''
4265
+ if 0 < n <= len(self._buf):
4266
+ return self._buf.read(n) or b''
4267
+ return self._raw.read1(n)
4268
+
4269
+ def read(self, /, n: int = -1) -> bytes:
4270
+ if n < 0:
4271
+ return self.readall()
4272
+ while len(self._buf) < n:
4273
+ if not (b := self._raw.read1(n)):
4274
+ break
4275
+ self._buf.feed(b)
4276
+ return self._buf.read(n) or b''
4277
+
4278
+ def readall(self) -> bytes:
4279
+ buf = io.BytesIO()
4280
+ buf.write(self._buf.read() or b'')
4281
+ while (b := self._raw.read1(self._chunk_size)):
4282
+ buf.write(b)
4283
+ return buf.getvalue()
4284
+
4285
+ def new_buffered_reader(
4286
+ self,
4287
+ raw: RawBytesReader,
4288
+ *,
4289
+ chunk_size: ta.Optional[int] = None,
4290
+ ) -> BufferedBytesReader:
4291
+ return self._BufferedBytesReader(
4292
+ raw,
4293
+ self,
4294
+ chunk_size=chunk_size,
4295
+ )
4296
+
4297
+ @ta.final
4298
+ class _AsyncBufferedBytesReader(AsyncBufferedBytesReader):
4299
+ def __init__(
4300
+ self,
4301
+ raw: AsyncRawBytesReader,
4302
+ buf: 'ReadableListBuffer',
4303
+ *,
4304
+ chunk_size: ta.Optional[int] = None,
4305
+ ) -> None:
4306
+ self._raw = raw
4307
+ self._buf = buf
4308
+ self._chunk_size = chunk_size or ReadableListBuffer.DEFAULT_BUFFERED_READER_CHUNK_SIZE
4309
+
4310
+ async def read1(self, n: int = -1, /) -> bytes:
4311
+ if n < 0:
4312
+ n = self._chunk_size
4313
+ if not n:
4314
+ return b''
4315
+ if 0 < n <= len(self._buf):
4316
+ return self._buf.read(n) or b''
4317
+ return await self._raw.read1(n)
4318
+
4319
+ async def read(self, /, n: int = -1) -> bytes:
4320
+ if n < 0:
4321
+ return await self.readall()
4322
+ while len(self._buf) < n:
4323
+ if not (b := await self._raw.read1(n)):
4324
+ break
4325
+ self._buf.feed(b)
4326
+ return self._buf.read(n) or b''
4327
+
4328
+ async def readall(self) -> bytes:
4329
+ buf = io.BytesIO()
4330
+ buf.write(self._buf.read() or b'')
4331
+ while b := await self._raw.read1(self._chunk_size):
4332
+ buf.write(b)
4333
+ return buf.getvalue()
4334
+
4335
+ def new_async_buffered_reader(
4336
+ self,
4337
+ raw: AsyncRawBytesReader,
4338
+ *,
4339
+ chunk_size: ta.Optional[int] = None,
4340
+ ) -> AsyncBufferedBytesReader:
4341
+ return self._AsyncBufferedBytesReader(
4342
+ raw,
4343
+ self,
4344
+ chunk_size=chunk_size,
4345
+ )
4346
+
3991
4347
 
3992
4348
  ##
3993
4349