omlish 0.0.0.dev2__py3-none-any.whl → 0.0.0.dev4__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 omlish might be problematic. Click here for more details.

Files changed (124) hide show
  1. omlish/__about__.py +1 -2
  2. omlish/__init__.py +8 -0
  3. omlish/argparse.py +4 -4
  4. omlish/asyncs/__init__.py +23 -2
  5. omlish/asyncs/anyio.py +13 -11
  6. omlish/asyncs/asyncs.py +1 -3
  7. omlish/asyncs/flavors.py +201 -0
  8. omlish/asyncs/futures.py +10 -9
  9. omlish/asyncs/trio_asyncio.py +41 -0
  10. omlish/c3.py +1 -1
  11. omlish/check.py +3 -3
  12. omlish/collections/_abc.py +2 -0
  13. omlish/collections/_io_abc.py +4 -2
  14. omlish/collections/cache/__init__.py +1 -1
  15. omlish/collections/cache/descriptor.py +8 -8
  16. omlish/collections/cache/impl.py +24 -17
  17. omlish/collections/cache/types.py +1 -1
  18. omlish/collections/coerce.py +1 -1
  19. omlish/collections/frozen.py +6 -6
  20. omlish/collections/identity.py +3 -4
  21. omlish/collections/mappings.py +2 -2
  22. omlish/collections/ordered.py +7 -7
  23. omlish/collections/skiplist.py +1 -1
  24. omlish/collections/sorted.py +1 -1
  25. omlish/collections/treap.py +25 -0
  26. omlish/collections/treapmap.py +57 -5
  27. omlish/collections/unmodifiable.py +9 -9
  28. omlish/collections/utils.py +1 -1
  29. omlish/configs/flattening.py +7 -6
  30. omlish/configs/props.py +3 -3
  31. omlish/dataclasses/__init__.py +1 -1
  32. omlish/dataclasses/impl/__init__.py +17 -1
  33. omlish/dataclasses/impl/api.py +10 -11
  34. omlish/dataclasses/impl/as_.py +4 -4
  35. omlish/dataclasses/impl/exceptions.py +1 -1
  36. omlish/dataclasses/impl/fields.py +7 -7
  37. omlish/dataclasses/impl/frozen.py +2 -2
  38. omlish/dataclasses/impl/init.py +5 -5
  39. omlish/dataclasses/impl/internals.py +1 -1
  40. omlish/dataclasses/impl/metaclass.py +1 -1
  41. omlish/dataclasses/impl/order.py +1 -1
  42. omlish/dataclasses/impl/replace.py +1 -1
  43. omlish/dataclasses/impl/repr.py +4 -4
  44. omlish/dataclasses/impl/utils.py +6 -6
  45. omlish/defs.py +13 -17
  46. omlish/{procfs.py → diag/procfs.py} +22 -24
  47. omlish/diag/ps.py +47 -0
  48. omlish/{replserver → diag/replserver}/console.py +18 -20
  49. omlish/{replserver → diag/replserver}/server.py +8 -8
  50. omlish/dispatch/dispatch.py +5 -8
  51. omlish/dispatch/functions.py +1 -1
  52. omlish/dispatch/methods.py +4 -5
  53. omlish/docker.py +1 -1
  54. omlish/dynamic.py +10 -10
  55. omlish/fnpairs.py +400 -0
  56. omlish/graphs/trees.py +13 -13
  57. omlish/inject/__init__.py +7 -7
  58. omlish/inject/elements.py +1 -1
  59. omlish/inject/exceptions.py +7 -7
  60. omlish/inject/impl/elements.py +17 -5
  61. omlish/inject/impl/injector.py +12 -10
  62. omlish/inject/impl/inspect.py +2 -2
  63. omlish/inject/impl/scopes.py +12 -11
  64. omlish/inject/proxy.py +5 -5
  65. omlish/iterators.py +19 -24
  66. omlish/json.py +143 -6
  67. omlish/lang/__init__.py +13 -4
  68. omlish/lang/cached.py +2 -5
  69. omlish/lang/classes/__init__.py +2 -2
  70. omlish/lang/classes/abstract.py +2 -2
  71. omlish/lang/classes/restrict.py +14 -14
  72. omlish/lang/classes/simple.py +1 -1
  73. omlish/lang/classes/virtual.py +5 -5
  74. omlish/lang/clsdct.py +1 -1
  75. omlish/lang/cmp.py +2 -2
  76. omlish/lang/contextmanagers.py +12 -14
  77. omlish/lang/descriptors.py +16 -4
  78. omlish/lang/exceptions.py +1 -1
  79. omlish/lang/functions.py +58 -22
  80. omlish/lang/imports.py +22 -27
  81. omlish/lang/iterables.py +2 -2
  82. omlish/lang/maybes.py +1 -0
  83. omlish/lang/objects.py +15 -9
  84. omlish/lang/resolving.py +1 -1
  85. omlish/lang/strings.py +1 -1
  86. omlish/lang/sys.py +7 -0
  87. omlish/lang/typing.py +3 -3
  88. omlish/libc.py +9 -5
  89. omlish/logs/_abc.py +5 -1
  90. omlish/logs/filters.py +2 -0
  91. omlish/logs/formatters.py +6 -2
  92. omlish/logs/utils.py +1 -1
  93. omlish/marshal/base.py +3 -3
  94. omlish/marshal/exceptions.py +1 -1
  95. omlish/marshal/global_.py +10 -4
  96. omlish/marshal/objects.py +1 -2
  97. omlish/marshal/registries.py +3 -3
  98. omlish/marshal/utils.py +2 -2
  99. omlish/marshal/values.py +1 -1
  100. omlish/math.py +9 -9
  101. omlish/reflect.py +3 -3
  102. omlish/sql/__init__.py +9 -0
  103. omlish/sql/asyncs.py +148 -0
  104. omlish/stats.py +4 -5
  105. omlish/term.py +1 -1
  106. omlish/testing/pydevd.py +28 -6
  107. omlish/testing/pytest/inject/harness.py +1 -1
  108. omlish/testing/pytest/plugins/pydevd.py +1 -1
  109. omlish/testing/pytest/plugins/switches.py +1 -1
  110. omlish/text/delimit.py +3 -6
  111. {omlish-0.0.0.dev2.dist-info → omlish-0.0.0.dev4.dist-info}/METADATA +4 -1
  112. omlish-0.0.0.dev4.dist-info/RECORD +195 -0
  113. {omlish-0.0.0.dev2.dist-info → omlish-0.0.0.dev4.dist-info}/WHEEL +1 -1
  114. omlish/lang/classes/test/test_abstract.py +0 -89
  115. omlish/lang/classes/test/test_restrict.py +0 -71
  116. omlish/lang/classes/test/test_simple.py +0 -58
  117. omlish/lang/classes/test/test_virtual.py +0 -72
  118. omlish-0.0.0.dev2.dist-info/RECORD +0 -193
  119. /omlish/{lang/classes/test → diag}/__init__.py +0 -0
  120. /omlish/{replserver → diag/replserver}/__init__.py +0 -0
  121. /omlish/{replserver → diag/replserver}/__main__.py +0 -0
  122. /omlish/sql/{_abcs.py → _abc.py} +0 -0
  123. {omlish-0.0.0.dev2.dist-info → omlish-0.0.0.dev4.dist-info}/LICENSE +0 -0
  124. {omlish-0.0.0.dev2.dist-info → omlish-0.0.0.dev4.dist-info}/top_level.txt +0 -0
@@ -8,6 +8,7 @@ TODO:
8
8
  - further cythonize hot path?
9
9
  """
10
10
  import collections
11
+ import contextlib
11
12
  import functools
12
13
  import logging
13
14
  import time
@@ -17,7 +18,7 @@ import weakref
17
18
  from ... import lang
18
19
  from .types import Cache
19
20
  from .types import Eviction
20
- from .types import OverweightException
21
+ from .types import OverweightError
21
22
 
22
23
 
23
24
  K = ta.TypeVar('K')
@@ -33,15 +34,15 @@ class SKIP(lang.Marker):
33
34
  pass
34
35
 
35
36
 
36
- def LRU(cache: 'Cache') -> None:
37
+ def LRU(cache: 'Cache') -> None: # noqa
37
38
  cache._kill(cache._root.lru_next) # type: ignore # noqa
38
39
 
39
40
 
40
- def LRI(cache: 'Cache') -> None:
41
+ def LRI(cache: 'Cache') -> None: # noqa
41
42
  cache._kill(cache._root.ins_next) # type: ignore # noqa
42
43
 
43
44
 
44
- def LFU(cache: 'Cache') -> None:
45
+ def LFU(cache: 'Cache') -> None: # noqa
45
46
  cache._kill(cache._root.lfu_prev) # type: ignore # noqa
46
47
 
47
48
 
@@ -92,7 +93,7 @@ class CacheImpl(Cache[K, V]):
92
93
 
93
94
  def __repr__(self) -> str:
94
95
  return (
95
- f'Link@{str(self.seq)}('
96
+ f'Link@{self.seq!s}('
96
97
  f'ins_prev={("@" + str(self.ins_prev.seq)) if self.ins_prev is not None else None}, '
97
98
  f'ins_next={("@" + str(self.ins_next.seq)) if self.ins_next is not None else None}, '
98
99
  f'lru_prev={("@" + str(self.lru_prev.seq)) if self.lru_prev is not None else None}, '
@@ -165,7 +166,7 @@ class CacheImpl(Cache[K, V]):
165
166
  if self._track_frequency:
166
167
  self._root.lfu_next = self._root.lfu_prev = self._root
167
168
 
168
- weak_dead: ta.Deque[CacheImpl.Link] | None
169
+ weak_dead: collections.deque[CacheImpl.Link] | None
169
170
  if weak_keys or weak_values:
170
171
  weak_dead = collections.deque()
171
172
  weak_dead_ref = weakref.ref(weak_dead)
@@ -276,10 +277,8 @@ class CacheImpl(Cache[K, V]):
276
277
  raise Exception
277
278
 
278
279
  def fail():
279
- try:
280
+ with contextlib.suppress(KeyError):
280
281
  del self._cache[cache_key]
281
- except KeyError:
282
- pass
283
282
  self._unlink(link)
284
283
  raise KeyError(key)
285
284
 
@@ -309,7 +308,7 @@ class CacheImpl(Cache[K, V]):
309
308
  link, value = self._get_link(key)
310
309
  except KeyError:
311
310
  self._misses += 1
312
- raise KeyError(key)
311
+ raise KeyError(key) from None
313
312
 
314
313
  if link.lru_next is not self._root:
315
314
  link.lru_prev.lru_next = link.lru_next
@@ -340,7 +339,7 @@ class CacheImpl(Cache[K, V]):
340
339
  return value
341
340
 
342
341
  @staticmethod
343
- def _weak_die(dead_ref: weakref.ref, link: Link, key_ref: weakref.ref) -> None:
342
+ def _weak_die(dead_ref: weakref.ref, link: Link, key_ref: weakref.ref) -> None: # noqa
344
343
  dead = dead_ref()
345
344
  if dead is not None:
346
345
  dead.append(link)
@@ -372,7 +371,7 @@ class CacheImpl(Cache[K, V]):
372
371
 
373
372
  if self._max_weight is not None and weight > self._max_weight:
374
373
  if self._raise_overweight:
375
- raise OverweightException
374
+ raise OverweightError
376
375
  else:
377
376
  return
378
377
 
@@ -387,10 +386,18 @@ class CacheImpl(Cache[K, V]):
387
386
  self._eviction(self)
388
387
 
389
388
  link = CacheImpl.Link()
389
+
390
390
  self._seq += 1
391
391
  link.seq = self._seq
392
- link.key = weakref.ref(key, functools.partial(CacheImpl._weak_die, self._weak_dead_ref, link)) if self._weak_keys else key # noqa
393
- link.value = weakref.ref(value, functools.partial(CacheImpl._weak_die, self._weak_dead_ref, link)) if self._weak_values else value # noqa
392
+
393
+ def make_ref(o, b):
394
+ if not b:
395
+ return o
396
+ return weakref.ref(o, functools.partial(CacheImpl._weak_die, self._weak_dead_ref, link)) # type: ignore # noqa
397
+
398
+ link.key = make_ref(key, self._weak_keys)
399
+ link.value = make_ref(value, self._weak_values)
400
+
394
401
  link.weight = weight
395
402
  link.written = link.accessed = self._clock()
396
403
  link.hits = 0
@@ -463,10 +470,10 @@ class CacheImpl(Cache[K, V]):
463
470
  else:
464
471
  yield key # type: ignore
465
472
 
466
- next = link.ins_prev
467
- if next is link:
473
+ nxt = link.ins_prev
474
+ if nxt is link:
468
475
  raise ValueError
469
- link = next
476
+ link = nxt
470
477
 
471
478
  @property
472
479
  def stats(self) -> Cache.Stats:
@@ -6,7 +6,7 @@ K = ta.TypeVar('K')
6
6
  V = ta.TypeVar('V')
7
7
 
8
8
 
9
- class OverweightException(Exception):
9
+ class OverweightError(Exception):
10
10
  pass
11
11
 
12
12
 
@@ -255,7 +255,7 @@ def frozenset_of_or_none(
255
255
  # region map
256
256
 
257
257
 
258
- def map(
258
+ def map( # noqa
259
259
  src: ta.Mapping[K, V] | ta.Iterable[tuple[K, V]],
260
260
  ) -> ta.Mapping[K, V]:
261
261
  return FrozenDict(src)
@@ -17,7 +17,7 @@ class Frozen(ta.Hashable, abc.ABC):
17
17
 
18
18
  class FrozenDict(ta.Mapping[K, V], Frozen):
19
19
 
20
- def __new__(cls, *args, **kwargs) -> 'FrozenDict[K, V]':
20
+ def __new__(cls, *args: ta.Any, **kwargs: ta.Any) -> 'FrozenDict[K, V]': # noqa
21
21
  if len(args) == 1 and Frozen in type(args[0]).__bases__:
22
22
  return args[0]
23
23
  return super().__new__(cls)
@@ -35,10 +35,10 @@ class FrozenDict(ta.Mapping[K, V], Frozen):
35
35
  return dict(self._dct)
36
36
 
37
37
  def __repr__(self) -> str:
38
- return '(%r)' % (self._dct,)
38
+ return f'({self._dct!r})'
39
39
 
40
40
  def __eq__(self, other) -> bool:
41
- return type(self) == type(other) and self._dct == other._dct
41
+ return type(self) is type(other) and self._dct == other._dct
42
42
 
43
43
  def __getitem__(self, key: K) -> V:
44
44
  return self._dct[key]
@@ -63,11 +63,11 @@ class FrozenDict(ta.Mapping[K, V], Frozen):
63
63
  def __setstate__(self, t):
64
64
  self.__init__(t) # type: ignore
65
65
 
66
- def drop(self, *keys):
66
+ def drop(self, *keys: T) -> 'FrozenDict[K, V]':
67
67
  ks = frozenset(keys)
68
68
  return type(self)((k, self[k]) for k in self if k not in ks)
69
69
 
70
- def set(self, *args, **kwargs):
70
+ def set(self, *args: ta.Any, **kwargs: ta.Any) -> 'FrozenDict[K, V]':
71
71
  new = type(self)(*args, **kwargs)
72
72
  return type(self)(itertools.chain(self.items(), new.items()))
73
73
 
@@ -85,7 +85,7 @@ class FrozenList(ta.Sequence[T], Frozen):
85
85
  return list(self)
86
86
 
87
87
  def __repr__(self) -> str:
88
- return '([%s])' % (', '.join(map(repr, self._tup)),)
88
+ return f'([{", ".join(map(repr, self._tup))}])'
89
89
 
90
90
  def __add__(self, o) -> 'FrozenList[T]':
91
91
  if isinstance(o, FrozenList):
@@ -1,3 +1,4 @@
1
+ import contextlib
1
2
  import operator as op
2
3
  import typing as ta
3
4
 
@@ -63,7 +64,7 @@ class IdentityKeyDict(ta.MutableMapping[K, V]):
63
64
  def __iter__(self) -> ta.Iterator[K]:
64
65
  return iter(map(op.itemgetter(0), self._dict.values()))
65
66
 
66
- def clear(self):
67
+ def clear(self) -> None:
67
68
  self._dict.clear()
68
69
 
69
70
 
@@ -87,10 +88,8 @@ class IdentitySet(ta.MutableSet[T]):
87
88
  self._dict[id(item)] = item
88
89
 
89
90
  def discard(self, item: T) -> None:
90
- try:
91
+ with contextlib.suppress(KeyError):
91
92
  del self._dict[id(item)]
92
- except KeyError:
93
- pass
94
93
 
95
94
  def update(self, items: ta.Iterable[T]) -> None:
96
95
  for item in items:
@@ -68,7 +68,7 @@ class TypeMap(ta.Generic[T]):
68
68
  def __len__(self) -> int:
69
69
  return len(self._items)
70
70
 
71
- def __iter__(self) -> ta.Iterable[T]:
71
+ def __iter__(self) -> ta.Iterator[T]:
72
72
  return iter(self._items)
73
73
 
74
74
  def get(self, ty: type[T]) -> T | None:
@@ -99,7 +99,7 @@ class TypeMultiMap(ta.Generic[V]):
99
99
  def __len__(self) -> int:
100
100
  return len(self._items)
101
101
 
102
- def __iter__(self) -> ta.Iterable[V]:
102
+ def __iter__(self) -> ta.Iterator[V]:
103
103
  return iter(self._items)
104
104
 
105
105
  def __getitem__(self, ty: type[T]) -> ta.Sequence[T]:
@@ -37,17 +37,17 @@ class OrderedSet(ta.MutableSet[T]):
37
37
  def __reversed__(self):
38
38
  return reversed(self._dct.keys())
39
39
 
40
- def pop(self, last=True):
40
+ def pop(self, last: bool = True) -> T:
41
41
  if not self:
42
42
  raise KeyError('set is empty')
43
43
  item = next(reversed(self._dct.keys()))
44
44
  self.discard(item)
45
45
  return item
46
46
 
47
- def __repr__(self):
47
+ def __repr__(self) -> str:
48
48
  if not self:
49
- return '%s()' % (self.__class__.__name__,)
50
- return '%s(%r)' % (self.__class__.__name__, list(self))
49
+ return f'{self.__class__.__name__}()'
50
+ return f'{self.__class__.__name__}({list(self)!r})'
51
51
 
52
52
  def __eq__(self, other) -> bool:
53
53
  if isinstance(other, OrderedSet):
@@ -55,11 +55,11 @@ class OrderedSet(ta.MutableSet[T]):
55
55
  return set(self) == set(other)
56
56
 
57
57
 
58
- class OrderedFrozenSet(ta.FrozenSet[T]):
58
+ class OrderedFrozenSet(ta.FrozenSet[T]): # noqa
59
59
 
60
60
  _list: ta.Sequence[T]
61
61
 
62
- def __new__(cls, items: ta.Iterable[T]) -> ta.FrozenSet[T]: # type: ignore
62
+ def __new__(cls, items: ta.Iterable[T]) -> frozenset[T]: # type: ignore
63
63
  item_set = set()
64
64
  item_list = []
65
65
  for item in items:
@@ -76,6 +76,6 @@ class OrderedFrozenSet(ta.FrozenSet[T]):
76
76
  def __iter__(self) -> ta.Iterator[T]:
77
77
  return iter(self._list)
78
78
 
79
- def __sub__(self, other: ta.Iterable[T]) -> ta.FrozenSet[T]:
79
+ def __sub__(self, other: ta.Iterable[T]) -> frozenset[T]:
80
80
  s = set(other)
81
81
  return type(self)(i for i in self if i not in s)
@@ -190,4 +190,4 @@ class SkipList(SortedCollection[T]):
190
190
  class SkipListDict(SortedListDict[K, V]):
191
191
 
192
192
  def __init__(self, *args, **kwargs) -> None:
193
- super().__init__(SkipList(comparator=SortedListDict._item_comparator), *args, **kwargs)
193
+ super().__init__(SkipList(comparator=SortedListDict._item_comparator), *args, **kwargs) # noqa
@@ -110,7 +110,7 @@ class SortedListDict(SortedMutableMapping[K, V]):
110
110
  return len(self._impl)
111
111
 
112
112
  def __iter__(self) -> ta.Iterator[K]:
113
- for k, v in self._impl:
113
+ for k, _ in self._impl:
114
114
  yield k
115
115
 
116
116
  def items(self) -> ta.Iterator[tuple[K, V]]: # type: ignore
@@ -16,6 +16,8 @@ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEM
16
16
  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
17
17
  OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18
18
  """
19
+ # ruff: noqa: SLF001
20
+
19
21
  import typing as ta
20
22
 
21
23
 
@@ -41,6 +43,9 @@ class TreapNode(ta.Generic[T]):
41
43
  self._left = _left
42
44
  self._right = _right
43
45
 
46
+ def __repr__(self) -> str:
47
+ return f'TreapNode(value={self._value!r}, priority={self._priority!r})'
48
+
44
49
  @property
45
50
  def value(self) -> T:
46
51
  return self._value
@@ -78,6 +83,26 @@ def find(n: TreapNode[T] | None, v: T, c: Comparer[T]) -> TreapNode[T] | None:
78
83
  n = n._left # noqa
79
84
 
80
85
 
86
+ def place(n: TreapNode[T] | None, v: T, c: Comparer[T], *, desc: bool = False) -> list[TreapNode[T]]:
87
+ ret: list[TreapNode[T]] = []
88
+ while True:
89
+ if n is None:
90
+ break
91
+ diff = c(n._value, v) # noqa
92
+ if diff == 0:
93
+ ret.append(n)
94
+ break
95
+ elif diff < 0:
96
+ if desc:
97
+ ret.append(n)
98
+ n = n._right # noqa
99
+ else:
100
+ if not desc:
101
+ ret.append(n)
102
+ n = n._left # noqa
103
+ return ret
104
+
105
+
81
106
  def union(
82
107
  n: TreapNode[T] | None,
83
108
  other: TreapNode[T] | None,
@@ -16,6 +16,7 @@ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEM
16
16
  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
17
17
  OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18
18
  """
19
+ import abc
19
20
  import random
20
21
  import typing as ta
21
22
 
@@ -74,13 +75,38 @@ class TreapMap(PersistentMap[K, V]):
74
75
  i = TreapMapIterator(
75
76
  _st=[],
76
77
  _n=self._n,
77
- _b=False,
78
78
  )
79
79
  while (n := i._n) is not None and n.left is not None: # noqa
80
80
  i._st.append(n) # noqa
81
81
  i._n = n.left # noqa
82
82
  return i
83
83
 
84
+ def iterate_from(self, k: K) -> 'TreapMapIterator[K, V]':
85
+ lst = treap.place(self._n, (k, None), self._c) # type: ignore
86
+ i = TreapMapIterator(
87
+ _st=lst,
88
+ _n=lst.pop(),
89
+ )
90
+ return i
91
+
92
+ def reverse_iterate(self) -> 'TreapMapReverseIterator[K, V]':
93
+ i = TreapMapReverseIterator(
94
+ _st=[],
95
+ _n=self._n,
96
+ )
97
+ while (n := i._n) is not None and n.right is not None: # noqa
98
+ i._st.append(n) # noqa
99
+ i._n = n.right # noqa
100
+ return i
101
+
102
+ def reverse_iterate_from(self, k: K) -> 'TreapMapReverseIterator[K, V]':
103
+ lst = treap.place(self._n, (k, None), self._c, desc=True) # type: ignore
104
+ i = TreapMapReverseIterator(
105
+ _st=lst,
106
+ _n=lst.pop(),
107
+ )
108
+ return i
109
+
84
110
  def with_(self, k: K, v: V) -> 'TreapMap[K, V]':
85
111
  node = treap.TreapNode(
86
112
  _value=(k, v),
@@ -108,25 +134,31 @@ def new_treap_map(cmp: ta.Callable[[tuple[K, V], tuple[K, V]], int]) -> Persiste
108
134
  return TreapMap(_n=None, _c=cmp)
109
135
 
110
136
 
111
- class TreapMapIterator(ta.Generic[K, V]):
112
- __slots__ = ('_st', '_n', '_b')
137
+ class BaseTreapMapIterator(abc.ABC, ta.Generic[K, V]):
138
+ __slots__ = ('_st', '_n')
113
139
 
114
140
  def __init__(
115
141
  self,
116
142
  *,
117
143
  _st: list[treap.TreapNode[tuple[K, V]]],
118
144
  _n: treap.TreapNode[tuple[K, V]] | None,
119
- _b: bool,
120
145
  ) -> None:
121
146
  super().__init__()
122
147
 
123
148
  self._st = _st
124
149
  self._n = _n
125
- self._b = _b
126
150
 
127
151
  def has_next(self) -> bool:
128
152
  return self._n is not None
129
153
 
154
+ @abc.abstractmethod
155
+ def next(self) -> tuple[K, V]:
156
+ raise NotImplementedError
157
+
158
+
159
+ class TreapMapIterator(BaseTreapMapIterator[K, V]):
160
+ __slots__ = BaseTreapMapIterator.__slots__
161
+
130
162
  def next(self) -> tuple[K, V]:
131
163
  n = self._n
132
164
  if n is None:
@@ -142,3 +174,23 @@ class TreapMapIterator(ta.Generic[K, V]):
142
174
  else:
143
175
  self._n = None
144
176
  return n.value
177
+
178
+
179
+ class TreapMapReverseIterator(BaseTreapMapIterator[K, V]):
180
+ __slots__ = BaseTreapMapIterator.__slots__
181
+
182
+ def next(self) -> tuple[K, V]:
183
+ n = self._n
184
+ if n is None:
185
+ raise StopIteration
186
+ if n.left is not None:
187
+ self._n = n.left
188
+ while self._n.right is not None:
189
+ self._st.append(self._n)
190
+ self._n = self._n.right
191
+ elif len(self._st) > 0:
192
+ self._n = self._st[-1]
193
+ self._st.pop()
194
+ else:
195
+ self._n = None
196
+ return n.value
@@ -22,12 +22,12 @@ class UnmodifiableSequence(ta.Sequence[T], Unmodifiable, lang.Final):
22
22
  self._target = target
23
23
 
24
24
  def __repr__(self) -> str:
25
- return '%s(%r)' % (type(self).__name__, self._target)
25
+ return f'{type(self).__name__}({self._target!r})'
26
26
 
27
27
  def __contains__(self, x: ta.Any) -> bool:
28
28
  return x in self._target
29
29
 
30
- def __eq__(self, o: ta.Any) -> bool:
30
+ def __eq__(self, o: object) -> bool:
31
31
  return self._target == o
32
32
 
33
33
  def __ge__(self, other: ta.Any) -> bool:
@@ -51,7 +51,7 @@ class UnmodifiableSequence(ta.Sequence[T], Unmodifiable, lang.Final):
51
51
  def __lt__(self, other: ta.Any) -> bool:
52
52
  return self._target < other
53
53
 
54
- def __ne__(self, o: ta.Any) -> bool:
54
+ def __ne__(self, o: object) -> bool:
55
55
  return self._target != o
56
56
 
57
57
  def __reversed__(self) -> ta.Iterator[T]:
@@ -74,7 +74,7 @@ class UnmodifiableSet(ta.AbstractSet[T], Unmodifiable, lang.Final):
74
74
  self._target = target
75
75
 
76
76
  def __repr__(self) -> str:
77
- return '%s(%r)' % (type(self).__name__, self._target)
77
+ return f'{type(self).__name__}({self._target!r})'
78
78
 
79
79
  def __and__(self, s: ta.AbstractSet[ta.Any]) -> ta.AbstractSet[T]:
80
80
  return self._target & s
@@ -82,7 +82,7 @@ class UnmodifiableSet(ta.AbstractSet[T], Unmodifiable, lang.Final):
82
82
  def __contains__(self, x: ta.Any) -> bool:
83
83
  return x in self._target
84
84
 
85
- def __eq__(self, o: ta.Any) -> bool:
85
+ def __eq__(self, o: object) -> bool:
86
86
  return self._target == o
87
87
 
88
88
  def __ge__(self, s: ta.AbstractSet[ta.Any]) -> bool:
@@ -103,7 +103,7 @@ class UnmodifiableSet(ta.AbstractSet[T], Unmodifiable, lang.Final):
103
103
  def __lt__(self, s: ta.AbstractSet[ta.Any]) -> bool:
104
104
  return self._target > s
105
105
 
106
- def __ne__(self, o: ta.Any) -> bool:
106
+ def __ne__(self, o: object) -> bool:
107
107
  return self._target != o
108
108
 
109
109
  def __or__(self, s: ta.AbstractSet[T]) -> ta.AbstractSet[T]: # type: ignore
@@ -130,12 +130,12 @@ class UnmodifiableMapping(ta.Mapping[K, V], Unmodifiable, lang.Final):
130
130
  self._target = target
131
131
 
132
132
  def __repr__(self) -> str:
133
- return '%s(%r)' % (type(self).__name__, self._target)
133
+ return f'{type(self).__name__}({self._target!r})'
134
134
 
135
135
  def __contains__(self, o: ta.Any) -> bool:
136
136
  return o in self._target
137
137
 
138
- def __eq__(self, o: ta.Any) -> bool:
138
+ def __eq__(self, o: object) -> bool:
139
139
  return self._target == o
140
140
 
141
141
  def __ge__(self, other: ta.Any) -> bool:
@@ -159,7 +159,7 @@ class UnmodifiableMapping(ta.Mapping[K, V], Unmodifiable, lang.Final):
159
159
  def __lt__(self, other: ta.Any) -> bool:
160
160
  return self._target < other
161
161
 
162
- def __ne__(self, o: ta.Any) -> bool:
162
+ def __ne__(self, o: object) -> bool:
163
163
  return self._target != o
164
164
 
165
165
  def get(self, k: K, default=None) -> V | None: # type: ignore
@@ -18,7 +18,7 @@ def mut_toposort(data: dict[T, set[T]]) -> ta.Iterator[set[T]]:
18
18
  extra_items_in_deps = functools.reduce(set.union, data.values()) - set(data.keys())
19
19
  data.update({item: set() for item in extra_items_in_deps})
20
20
  while True:
21
- ordered = set(item for item, dep in data.items() if not dep)
21
+ ordered = {item for item, dep in data.items() if not dep}
22
22
  if not ordered:
23
23
  break
24
24
  yield ordered
@@ -1,4 +1,5 @@
1
1
  import abc
2
+ import itertools
2
3
  import typing as ta
3
4
 
4
5
  from .. import check
@@ -34,10 +35,10 @@ class Flattening:
34
35
  self._index_close = check.not_empty(index_close)
35
36
 
36
37
  def flatten(self, unflattened: StrMap) -> StrMap:
37
- def rec(prefix: ta.List[str], value: ta.Any) -> None:
38
+ def rec(prefix: list[str], value: ta.Any) -> None:
38
39
  if isinstance(value, dict):
39
40
  for k, v in value.items():
40
- rec(prefix + [k], v)
41
+ rec([*prefix, k], v)
41
42
  elif isinstance(value, list):
42
43
  check.not_empty(prefix)
43
44
  for i, v in enumerate(value):
@@ -48,7 +49,7 @@ class Flattening:
48
49
  raise KeyError(k)
49
50
  ret[k] = value
50
51
 
51
- ret: ta.Dict[str, ta.Any] = {}
52
+ ret: dict[str, ta.Any] = {}
52
53
  rec([], unflattened)
53
54
  return ret
54
55
 
@@ -83,7 +84,7 @@ class Flattening:
83
84
  def __init__(self) -> None:
84
85
  super().__init__()
85
86
 
86
- self._dict: ta.Dict[str, ta.Any] = {}
87
+ self._dict: dict[str, ta.Any] = {}
87
88
 
88
89
  def get(self, key: str) -> ta.Any:
89
90
  return self._dict.get(key, _MISSING)
@@ -100,7 +101,7 @@ class Flattening:
100
101
  def __init__(self) -> None:
101
102
  super().__init__()
102
103
 
103
- self._list: ta.List[ta.Any] = []
104
+ self._list: list[ta.Any] = []
104
105
 
105
106
  def get(self, key: int) -> ta.Any:
106
107
  check.arg(key >= 0)
@@ -135,7 +136,7 @@ class Flattening:
135
136
  for fk, v in flattened.items():
136
137
  node: Flattening.UnflattenNode = root
137
138
  fks = list(split_keys(fk))
138
- for key, nkey in zip(fks, fks[1:]):
139
+ for key, nkey in itertools.pairwise(fks):
139
140
  if isinstance(nkey, str):
140
141
  node = node.setdefault(key, Flattening.UnflattenDict)
141
142
  elif isinstance(nkey, int):
omlish/configs/props.py CHANGED
@@ -19,8 +19,8 @@ def escape(token: str) -> str:
19
19
  return _ESCAPE_PATTERN.sub(r'\\\1', token)
20
20
 
21
21
 
22
- def parse_line(line: str) -> ta.Tuple[str, str] | None:
23
- if line and not (line.startswith('#') or line.startswith('!')):
22
+ def parse_line(line: str) -> tuple[str, str] | None:
23
+ if line and not line.startswith(('#', '!')):
24
24
  match = _SEPARATOR_PATTERN.search(line)
25
25
  if match:
26
26
  return normalize(line[:match.start()]), normalize(line[match.end():])
@@ -54,7 +54,7 @@ def coalesce_lines(lines: ta.Iterable[str]) -> ta.Generator[str, None, None]:
54
54
  pass
55
55
 
56
56
 
57
- def parse_lines(lines: ta.Iterable[str]) -> ta.Dict[str, str]:
57
+ def parse_lines(lines: ta.Iterable[str]) -> dict[str, str]:
58
58
  props = {}
59
59
  for line in coalesce_lines(lines):
60
60
  kv_pair = parse_line(line)
@@ -54,7 +54,7 @@ globals()['make_dataclass'] = xmake_dataclass
54
54
 
55
55
 
56
56
  from .impl.exceptions import ( # noqa
57
- CheckException,
57
+ CheckError,
58
58
  )
59
59
 
60
60
  from .impl.metaclass import ( # noqa
@@ -4,5 +4,21 @@ TODO:
4
4
  - cleanup confer
5
5
  - descriptors - check_type/validators don't handle setters lol
6
6
  - deep_frozen?
7
- - repr='redact', 'if_not_none', 'if_truthy', dc.if_(lambda v: ...)? just a lambda returning str | None?
7
+ - field:
8
+ - frozen
9
+ - pickle/transient
10
+ - mangled
11
+ - doc
12
+ - derive
13
+ - check_type
14
+ - class
15
+ - strict_eq
16
+ - allow_setattr
17
+ - mangler
18
+ - observable
19
+ - c/py gen
20
+ - iterable
21
+ - proto/jsonschema gen
22
+ - enums
23
+ - nodal
8
24
  """