omlish 0.0.0.dev1__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.
- omlish/__about__.py +7 -0
- omlish/__init__.py +0 -0
- omlish/argparse.py +223 -0
- omlish/asyncs/__init__.py +17 -0
- omlish/asyncs/anyio.py +23 -0
- omlish/asyncs/asyncio.py +19 -0
- omlish/asyncs/asyncs.py +76 -0
- omlish/asyncs/futures.py +179 -0
- omlish/asyncs/trio.py +11 -0
- omlish/c3.py +173 -0
- omlish/cached.py +9 -0
- omlish/check.py +231 -0
- omlish/collections/__init__.py +63 -0
- omlish/collections/_abc.py +156 -0
- omlish/collections/_io_abc.py +78 -0
- omlish/collections/cache/__init__.py +11 -0
- omlish/collections/cache/descriptor.py +188 -0
- omlish/collections/cache/impl.py +485 -0
- omlish/collections/cache/types.py +37 -0
- omlish/collections/coerce.py +337 -0
- omlish/collections/frozen.py +148 -0
- omlish/collections/identity.py +106 -0
- omlish/collections/indexed.py +75 -0
- omlish/collections/mappings.py +127 -0
- omlish/collections/ordered.py +81 -0
- omlish/collections/persistent.py +36 -0
- omlish/collections/skiplist.py +193 -0
- omlish/collections/sorted.py +126 -0
- omlish/collections/treap.py +228 -0
- omlish/collections/treapmap.py +144 -0
- omlish/collections/unmodifiable.py +174 -0
- omlish/collections/utils.py +110 -0
- omlish/configs/__init__.py +0 -0
- omlish/configs/flattening.py +147 -0
- omlish/configs/props.py +64 -0
- omlish/dataclasses/__init__.py +83 -0
- omlish/dataclasses/impl/__init__.py +6 -0
- omlish/dataclasses/impl/api.py +260 -0
- omlish/dataclasses/impl/as_.py +76 -0
- omlish/dataclasses/impl/exceptions.py +2 -0
- omlish/dataclasses/impl/fields.py +148 -0
- omlish/dataclasses/impl/frozen.py +55 -0
- omlish/dataclasses/impl/hashing.py +85 -0
- omlish/dataclasses/impl/init.py +173 -0
- omlish/dataclasses/impl/internals.py +118 -0
- omlish/dataclasses/impl/main.py +150 -0
- omlish/dataclasses/impl/metaclass.py +126 -0
- omlish/dataclasses/impl/metadata.py +74 -0
- omlish/dataclasses/impl/order.py +47 -0
- omlish/dataclasses/impl/params.py +150 -0
- omlish/dataclasses/impl/processing.py +16 -0
- omlish/dataclasses/impl/reflect.py +173 -0
- omlish/dataclasses/impl/replace.py +40 -0
- omlish/dataclasses/impl/repr.py +34 -0
- omlish/dataclasses/impl/simple.py +92 -0
- omlish/dataclasses/impl/slots.py +80 -0
- omlish/dataclasses/impl/utils.py +167 -0
- omlish/defs.py +193 -0
- omlish/dispatch/__init__.py +3 -0
- omlish/dispatch/dispatch.py +137 -0
- omlish/dispatch/functions.py +52 -0
- omlish/dispatch/methods.py +162 -0
- omlish/docker.py +149 -0
- omlish/dynamic.py +220 -0
- omlish/graphs/__init__.py +0 -0
- omlish/graphs/dot/__init__.py +19 -0
- omlish/graphs/dot/items.py +162 -0
- omlish/graphs/dot/rendering.py +147 -0
- omlish/graphs/dot/utils.py +30 -0
- omlish/graphs/trees.py +249 -0
- omlish/http/__init__.py +0 -0
- omlish/http/consts.py +20 -0
- omlish/http/wsgi.py +34 -0
- omlish/inject/__init__.py +85 -0
- omlish/inject/binder.py +12 -0
- omlish/inject/bindings.py +49 -0
- omlish/inject/eagers.py +21 -0
- omlish/inject/elements.py +43 -0
- omlish/inject/exceptions.py +49 -0
- omlish/inject/impl/__init__.py +0 -0
- omlish/inject/impl/bindings.py +19 -0
- omlish/inject/impl/elements.py +154 -0
- omlish/inject/impl/injector.py +182 -0
- omlish/inject/impl/inspect.py +98 -0
- omlish/inject/impl/private.py +109 -0
- omlish/inject/impl/providers.py +132 -0
- omlish/inject/impl/scopes.py +198 -0
- omlish/inject/injector.py +40 -0
- omlish/inject/inspect.py +14 -0
- omlish/inject/keys.py +43 -0
- omlish/inject/managed.py +24 -0
- omlish/inject/overrides.py +18 -0
- omlish/inject/private.py +29 -0
- omlish/inject/providers.py +111 -0
- omlish/inject/proxy.py +48 -0
- omlish/inject/scopes.py +84 -0
- omlish/inject/types.py +21 -0
- omlish/iterators.py +184 -0
- omlish/json.py +194 -0
- omlish/lang/__init__.py +112 -0
- omlish/lang/cached.py +267 -0
- omlish/lang/classes/__init__.py +24 -0
- omlish/lang/classes/abstract.py +74 -0
- omlish/lang/classes/restrict.py +137 -0
- omlish/lang/classes/simple.py +120 -0
- omlish/lang/classes/test/__init__.py +0 -0
- omlish/lang/classes/test/test_abstract.py +89 -0
- omlish/lang/classes/test/test_restrict.py +71 -0
- omlish/lang/classes/test/test_simple.py +58 -0
- omlish/lang/classes/test/test_virtual.py +72 -0
- omlish/lang/classes/virtual.py +130 -0
- omlish/lang/clsdct.py +67 -0
- omlish/lang/cmp.py +63 -0
- omlish/lang/contextmanagers.py +249 -0
- omlish/lang/datetimes.py +67 -0
- omlish/lang/descriptors.py +52 -0
- omlish/lang/functions.py +126 -0
- omlish/lang/imports.py +153 -0
- omlish/lang/iterables.py +54 -0
- omlish/lang/maybes.py +136 -0
- omlish/lang/objects.py +103 -0
- omlish/lang/resolving.py +50 -0
- omlish/lang/strings.py +128 -0
- omlish/lang/typing.py +92 -0
- omlish/libc.py +532 -0
- omlish/logs/__init__.py +9 -0
- omlish/logs/_abc.py +247 -0
- omlish/logs/configs.py +62 -0
- omlish/logs/filters.py +9 -0
- omlish/logs/formatters.py +67 -0
- omlish/logs/utils.py +20 -0
- omlish/marshal/__init__.py +52 -0
- omlish/marshal/any.py +25 -0
- omlish/marshal/base.py +201 -0
- omlish/marshal/base64.py +25 -0
- omlish/marshal/dataclasses.py +115 -0
- omlish/marshal/datetimes.py +90 -0
- omlish/marshal/enums.py +43 -0
- omlish/marshal/exceptions.py +7 -0
- omlish/marshal/factories.py +129 -0
- omlish/marshal/global_.py +33 -0
- omlish/marshal/iterables.py +57 -0
- omlish/marshal/mappings.py +66 -0
- omlish/marshal/naming.py +17 -0
- omlish/marshal/objects.py +106 -0
- omlish/marshal/optionals.py +49 -0
- omlish/marshal/polymorphism.py +147 -0
- omlish/marshal/primitives.py +43 -0
- omlish/marshal/registries.py +57 -0
- omlish/marshal/standard.py +80 -0
- omlish/marshal/utils.py +23 -0
- omlish/marshal/uuids.py +29 -0
- omlish/marshal/values.py +30 -0
- omlish/math.py +184 -0
- omlish/os.py +32 -0
- omlish/reflect.py +359 -0
- omlish/replserver/__init__.py +5 -0
- omlish/replserver/__main__.py +4 -0
- omlish/replserver/console.py +247 -0
- omlish/replserver/server.py +146 -0
- omlish/runmodule.py +28 -0
- omlish/stats.py +342 -0
- omlish/term.py +222 -0
- omlish/testing/__init__.py +7 -0
- omlish/testing/pydevd.py +225 -0
- omlish/testing/pytest/__init__.py +8 -0
- omlish/testing/pytest/helpers.py +35 -0
- omlish/testing/pytest/inject/__init__.py +1 -0
- omlish/testing/pytest/inject/harness.py +159 -0
- omlish/testing/pytest/plugins/__init__.py +20 -0
- omlish/testing/pytest/plugins/_registry.py +6 -0
- omlish/testing/pytest/plugins/logging.py +13 -0
- omlish/testing/pytest/plugins/pycharm.py +54 -0
- omlish/testing/pytest/plugins/repeat.py +19 -0
- omlish/testing/pytest/plugins/skips.py +32 -0
- omlish/testing/pytest/plugins/spacing.py +19 -0
- omlish/testing/pytest/plugins/switches.py +70 -0
- omlish/testing/testing.py +102 -0
- omlish/text/__init__.py +0 -0
- omlish/text/delimit.py +171 -0
- omlish/text/indent.py +50 -0
- omlish/text/parts.py +265 -0
- omlish-0.0.0.dev1.dist-info/LICENSE +21 -0
- omlish-0.0.0.dev1.dist-info/METADATA +17 -0
- omlish-0.0.0.dev1.dist-info/RECORD +187 -0
- omlish-0.0.0.dev1.dist-info/WHEEL +5 -0
- omlish-0.0.0.dev1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import collections.abc
|
|
2
|
+
import typing as ta
|
|
3
|
+
import weakref
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
T = ta.TypeVar('T')
|
|
7
|
+
K = ta.TypeVar('K')
|
|
8
|
+
V = ta.TypeVar('V')
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def multikey_dict(dct: ta.Mapping[ta.Union[ta.Iterable[K], K], V], *, deep: bool = False) -> dict[K, V]:
|
|
12
|
+
ret = {}
|
|
13
|
+
for k, v in dct.items():
|
|
14
|
+
if deep and isinstance(v, dict):
|
|
15
|
+
v = multikey_dict(v, deep=True) # type: ignore
|
|
16
|
+
if isinstance(k, tuple):
|
|
17
|
+
for sk in k:
|
|
18
|
+
ret[sk] = v
|
|
19
|
+
else:
|
|
20
|
+
ret[k] = v
|
|
21
|
+
return ret
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def guarded_map_update(
|
|
25
|
+
dst: ta.MutableMapping[ta.Any, ta.Any],
|
|
26
|
+
*srcs: ta.Mapping[ta.Any, ta.Any]
|
|
27
|
+
) -> ta.MutableMapping[ta.Any, ta.Any]:
|
|
28
|
+
for src in srcs:
|
|
29
|
+
for k, v in src.items():
|
|
30
|
+
if k in dst:
|
|
31
|
+
raise KeyError(k)
|
|
32
|
+
dst[k] = v
|
|
33
|
+
return dst
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def yield_dict_init(*args, **kwargs) -> ta.Iterable[tuple[ta.Any, ta.Any]]:
|
|
37
|
+
if len(args) > 1:
|
|
38
|
+
raise TypeError
|
|
39
|
+
if args:
|
|
40
|
+
[src] = args
|
|
41
|
+
if isinstance(src, collections.abc.Mapping):
|
|
42
|
+
for k in src:
|
|
43
|
+
yield (k, src[k])
|
|
44
|
+
else:
|
|
45
|
+
for k, v in src:
|
|
46
|
+
yield (k, v)
|
|
47
|
+
for k, v in kwargs.items():
|
|
48
|
+
yield (k, v)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class TypeMap(ta.Generic[T]):
|
|
52
|
+
|
|
53
|
+
def __init__(self, items: ta.Iterable[T] = ()) -> None:
|
|
54
|
+
super().__init__()
|
|
55
|
+
|
|
56
|
+
self._items = list(items)
|
|
57
|
+
dct: dict[type, ta.Any] = {}
|
|
58
|
+
for item in items:
|
|
59
|
+
if (ty := type(item)) in dct:
|
|
60
|
+
raise ValueError(ty)
|
|
61
|
+
dct[ty] = item
|
|
62
|
+
self._dct = dct
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def items(self) -> ta.Sequence[T]:
|
|
66
|
+
return self._items
|
|
67
|
+
|
|
68
|
+
def __len__(self) -> int:
|
|
69
|
+
return len(self._items)
|
|
70
|
+
|
|
71
|
+
def __iter__(self) -> ta.Iterable[T]:
|
|
72
|
+
return iter(self._items)
|
|
73
|
+
|
|
74
|
+
def get(self, ty: type[T]) -> ta.Optional[T]:
|
|
75
|
+
return self._dct.get(ty)
|
|
76
|
+
|
|
77
|
+
def __getitem__(self, ty: type[T]) -> ta.Sequence[T]:
|
|
78
|
+
return self._dct[ty]
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class TypeMultiMap(ta.Generic[V]):
|
|
82
|
+
|
|
83
|
+
def __init__(self, items: ta.Iterable[V] = (), *, weak: bool = False) -> None:
|
|
84
|
+
super().__init__()
|
|
85
|
+
|
|
86
|
+
self._items = list(items)
|
|
87
|
+
self._weak = bool(weak)
|
|
88
|
+
|
|
89
|
+
self._cache: ta.MutableMapping[type, ta.Any] = weakref.WeakKeyDictionary()
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def items(self) -> ta.Sequence[V]:
|
|
93
|
+
return self._items
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def weak(self) -> bool:
|
|
97
|
+
return self._weak
|
|
98
|
+
|
|
99
|
+
def __len__(self) -> int:
|
|
100
|
+
return len(self._items)
|
|
101
|
+
|
|
102
|
+
def __iter__(self) -> ta.Iterable[V]:
|
|
103
|
+
return iter(self._items)
|
|
104
|
+
|
|
105
|
+
def __getitem__(self, ty: type[T]) -> ta.Sequence[T]:
|
|
106
|
+
try:
|
|
107
|
+
return self._cache[ty]
|
|
108
|
+
except KeyError:
|
|
109
|
+
ret = []
|
|
110
|
+
for item in self._items:
|
|
111
|
+
if isinstance(item, ty):
|
|
112
|
+
ret.append(item)
|
|
113
|
+
self._cache[ty] = ret
|
|
114
|
+
return ret
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class MissingDict(dict[K, V]):
|
|
118
|
+
|
|
119
|
+
def __init__(self, missing_fn: ta.Callable[[K], V]) -> None:
|
|
120
|
+
if not callable(missing_fn):
|
|
121
|
+
raise TypeError(missing_fn)
|
|
122
|
+
super().__init__()
|
|
123
|
+
self._missing_fn = missing_fn
|
|
124
|
+
|
|
125
|
+
def __missing__(self, key):
|
|
126
|
+
v = self[key] = self._missing_fn(key)
|
|
127
|
+
return v
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
T = ta.TypeVar('T')
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class OrderedSet(ta.MutableSet[T]):
|
|
8
|
+
|
|
9
|
+
def __init__(self, iterable: ta.Optional[ta.Iterable[T]] = None) -> None:
|
|
10
|
+
super().__init__()
|
|
11
|
+
self._dct: dict[T, ta.Any] = {}
|
|
12
|
+
if iterable is not None:
|
|
13
|
+
self |= iterable # type: ignore # noqa
|
|
14
|
+
|
|
15
|
+
def __len__(self) -> int:
|
|
16
|
+
return len(self._dct)
|
|
17
|
+
|
|
18
|
+
def __contains__(self, item: ta.Any) -> bool:
|
|
19
|
+
return item in self._dct
|
|
20
|
+
|
|
21
|
+
def add(self, item: T) -> None:
|
|
22
|
+
if item not in self._dct:
|
|
23
|
+
self._dct[item] = None
|
|
24
|
+
|
|
25
|
+
def update(self, items: ta.Iterable[T]) -> None:
|
|
26
|
+
for item in items:
|
|
27
|
+
if item not in self._dct:
|
|
28
|
+
self._dct[item] = None
|
|
29
|
+
|
|
30
|
+
def discard(self, item: T) -> None:
|
|
31
|
+
if item in self._dct:
|
|
32
|
+
del self._dct[item]
|
|
33
|
+
|
|
34
|
+
def __iter__(self) -> ta.Iterator[T]:
|
|
35
|
+
return iter(self._dct.keys())
|
|
36
|
+
|
|
37
|
+
def __reversed__(self):
|
|
38
|
+
return reversed(self._dct.keys())
|
|
39
|
+
|
|
40
|
+
def pop(self, last=True):
|
|
41
|
+
if not self:
|
|
42
|
+
raise KeyError('set is empty')
|
|
43
|
+
item = next(reversed(self._dct.keys()))
|
|
44
|
+
self.discard(item)
|
|
45
|
+
return item
|
|
46
|
+
|
|
47
|
+
def __repr__(self):
|
|
48
|
+
if not self:
|
|
49
|
+
return '%s()' % (self.__class__.__name__,)
|
|
50
|
+
return '%s(%r)' % (self.__class__.__name__, list(self))
|
|
51
|
+
|
|
52
|
+
def __eq__(self, other) -> bool:
|
|
53
|
+
if isinstance(other, OrderedSet):
|
|
54
|
+
return len(self) == len(other) and list(self) == list(other)
|
|
55
|
+
return set(self) == set(other)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class OrderedFrozenSet(ta.FrozenSet[T]):
|
|
59
|
+
|
|
60
|
+
_list: ta.Sequence[T]
|
|
61
|
+
|
|
62
|
+
def __new__(cls, items: ta.Iterable[T]) -> ta.FrozenSet[T]: # type: ignore
|
|
63
|
+
item_set = set()
|
|
64
|
+
item_list = []
|
|
65
|
+
for item in items:
|
|
66
|
+
if item not in item_set:
|
|
67
|
+
item_set.add(item)
|
|
68
|
+
item_list.append(item)
|
|
69
|
+
obj = super(cls, OrderedFrozenSet).__new__(cls, item_set)
|
|
70
|
+
obj._list = item_list # type: ignore # noqa
|
|
71
|
+
return obj
|
|
72
|
+
|
|
73
|
+
def __repr__(self) -> str:
|
|
74
|
+
return f'{self.__class__.__name__}([{", ".join(map(repr, self))}])'
|
|
75
|
+
|
|
76
|
+
def __iter__(self) -> ta.Iterator[T]:
|
|
77
|
+
return iter(self._list)
|
|
78
|
+
|
|
79
|
+
def __sub__(self, other: ta.Iterable[T]) -> ta.FrozenSet[T]:
|
|
80
|
+
s = set(other)
|
|
81
|
+
return type(self)(i for i in self if i not in s)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
K = ta.TypeVar('K')
|
|
6
|
+
V = ta.TypeVar('V')
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PersistentMap(ta.Generic[K, V], abc.ABC):
|
|
10
|
+
@abc.abstractmethod
|
|
11
|
+
def __len__(self) -> int:
|
|
12
|
+
raise NotImplementedError
|
|
13
|
+
|
|
14
|
+
@abc.abstractmethod
|
|
15
|
+
def __contains__(self, item: K) -> bool:
|
|
16
|
+
raise NotImplementedError
|
|
17
|
+
|
|
18
|
+
@abc.abstractmethod
|
|
19
|
+
def __getitem__(self, item: K) -> V:
|
|
20
|
+
raise NotImplementedError
|
|
21
|
+
|
|
22
|
+
@abc.abstractmethod
|
|
23
|
+
def __iter__(self) -> ta.Iterator[tuple[K, V]]:
|
|
24
|
+
raise NotImplementedError
|
|
25
|
+
|
|
26
|
+
@abc.abstractmethod
|
|
27
|
+
def with_(self, k: K, v: V) -> 'PersistentMap[K, V]':
|
|
28
|
+
raise NotImplementedError
|
|
29
|
+
|
|
30
|
+
@abc.abstractmethod
|
|
31
|
+
def without(self, k: K) -> 'PersistentMap[K, V]':
|
|
32
|
+
raise NotImplementedError
|
|
33
|
+
|
|
34
|
+
@abc.abstractmethod
|
|
35
|
+
def default(self, k: K, v: V) -> 'PersistentMap[K, V]':
|
|
36
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import random
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
from .sorted import SortedCollection
|
|
5
|
+
from .sorted import SortedListDict
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
T = ta.TypeVar('T')
|
|
9
|
+
K = ta.TypeVar('K')
|
|
10
|
+
V = ta.TypeVar('V')
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SkipList(SortedCollection[T]):
|
|
14
|
+
"""https://gist.github.com/icejoywoo/3bf0c54983a725fa3917"""
|
|
15
|
+
|
|
16
|
+
class _Node:
|
|
17
|
+
__slots__ = [
|
|
18
|
+
'value',
|
|
19
|
+
'level',
|
|
20
|
+
'next',
|
|
21
|
+
'prev',
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
next: list[ta.Optional['SkipList._Node']]
|
|
25
|
+
prev: ta.Optional['SkipList._Node']
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
value: T,
|
|
30
|
+
level: int
|
|
31
|
+
) -> None:
|
|
32
|
+
super().__init__()
|
|
33
|
+
|
|
34
|
+
if level <= 0:
|
|
35
|
+
raise TypeError('level must be > 0')
|
|
36
|
+
|
|
37
|
+
self.value = value
|
|
38
|
+
self.level = level
|
|
39
|
+
self.next = [None] * level
|
|
40
|
+
self.prev = None
|
|
41
|
+
|
|
42
|
+
def __repr__(self) -> str:
|
|
43
|
+
return f'{type(self).__name__}(value={self.value!r})'
|
|
44
|
+
|
|
45
|
+
def __init__(
|
|
46
|
+
self,
|
|
47
|
+
*,
|
|
48
|
+
max_height: int = 16,
|
|
49
|
+
comparator: ta.Optional[SortedCollection.Comparator[T]] = None,
|
|
50
|
+
) -> None:
|
|
51
|
+
super().__init__()
|
|
52
|
+
|
|
53
|
+
if comparator is None:
|
|
54
|
+
comparator = SortedCollection.default_comparator
|
|
55
|
+
self._compare = comparator
|
|
56
|
+
self._max_height = max_height
|
|
57
|
+
self._head = SkipList._Node(None, self._max_height)
|
|
58
|
+
self._height = 1
|
|
59
|
+
self._head.next = [None] * self._max_height
|
|
60
|
+
self._length = 0
|
|
61
|
+
|
|
62
|
+
def __len__(self) -> int:
|
|
63
|
+
return self._length
|
|
64
|
+
|
|
65
|
+
def __iter__(self) -> ta.Iterator[T]:
|
|
66
|
+
return iter(self.iter())
|
|
67
|
+
|
|
68
|
+
def __contains__(self, value: T) -> bool: # type: ignore
|
|
69
|
+
return self.find(value) is not None
|
|
70
|
+
|
|
71
|
+
def _random_level(self) -> int:
|
|
72
|
+
result = 1
|
|
73
|
+
while random.uniform(0, 1) < 0.5 and result < self._max_height:
|
|
74
|
+
result += 1
|
|
75
|
+
return result
|
|
76
|
+
|
|
77
|
+
def add(self, value: T) -> bool:
|
|
78
|
+
if value is None:
|
|
79
|
+
raise TypeError(value)
|
|
80
|
+
|
|
81
|
+
node = SkipList._Node(value, self._random_level())
|
|
82
|
+
update = [None] * self._max_height # noqa
|
|
83
|
+
cur = self._head
|
|
84
|
+
|
|
85
|
+
for i in range(self._height - 1, -1, -1):
|
|
86
|
+
while cur.next[i] is not None and self._compare(value, cur.next[i].value) > 0: # type: ignore
|
|
87
|
+
cur = cur.next[i] # type: ignore
|
|
88
|
+
update[i] = cur # type: ignore
|
|
89
|
+
|
|
90
|
+
cur = cur.next[0] # type: ignore
|
|
91
|
+
if cur is not None:
|
|
92
|
+
if self._compare(value, cur.value) == 0: # type: ignore
|
|
93
|
+
return False
|
|
94
|
+
node.prev, cur.prev = cur.prev, node
|
|
95
|
+
else:
|
|
96
|
+
node.prev = update[0] # type: ignore
|
|
97
|
+
|
|
98
|
+
if node.level > self._height:
|
|
99
|
+
for i in range(self._height, node.level):
|
|
100
|
+
update[i] = self._head # type: ignore
|
|
101
|
+
self._height = node.level
|
|
102
|
+
|
|
103
|
+
for i in range(node.level):
|
|
104
|
+
cur = update[i] # type: ignore
|
|
105
|
+
node.next[i] = cur.next[i] # noqa
|
|
106
|
+
cur.next[i] = node # noqa
|
|
107
|
+
|
|
108
|
+
self._length += 1
|
|
109
|
+
return True
|
|
110
|
+
|
|
111
|
+
def _find(self, value: T) -> ta.Optional[_Node]:
|
|
112
|
+
if value is None:
|
|
113
|
+
raise TypeError(value)
|
|
114
|
+
|
|
115
|
+
cur = self._head
|
|
116
|
+
|
|
117
|
+
for i in range(self._height - 1, -1, -1):
|
|
118
|
+
while cur.next[i] and self._compare(value, cur.next[i].value) > 0: # type: ignore
|
|
119
|
+
cur = cur.next[i] # type: ignore
|
|
120
|
+
|
|
121
|
+
return cur.next[0]
|
|
122
|
+
|
|
123
|
+
def find(self, value: T) -> ta.Optional[T]:
|
|
124
|
+
node = self._find(value)
|
|
125
|
+
if node is None:
|
|
126
|
+
return None
|
|
127
|
+
if node is None or self._compare(value, node.value) != 0: # type: ignore
|
|
128
|
+
return None
|
|
129
|
+
return node.value # type: ignore
|
|
130
|
+
|
|
131
|
+
def remove(self, value: T) -> bool:
|
|
132
|
+
if value is None:
|
|
133
|
+
raise TypeError(value)
|
|
134
|
+
|
|
135
|
+
update = [None] * self._max_height # noqa
|
|
136
|
+
cur = self._head
|
|
137
|
+
|
|
138
|
+
for i in range(self._height - 1, -1, -1):
|
|
139
|
+
while cur.next[i] is not None and self._compare(value, cur.next[i].value) > 0: # type: ignore
|
|
140
|
+
cur = cur.next[i] # type: ignore
|
|
141
|
+
update[i] = cur # type: ignore
|
|
142
|
+
|
|
143
|
+
cur = cur.next[0] # type: ignore
|
|
144
|
+
if cur is None or self._compare(value, cur.value) != 0: # type: ignore
|
|
145
|
+
return False
|
|
146
|
+
elif cur.next[0] is not None:
|
|
147
|
+
cur.next[0].prev = cur.prev
|
|
148
|
+
|
|
149
|
+
for i in range(self._height):
|
|
150
|
+
if update[i].next[i] is not cur: # type: ignore
|
|
151
|
+
break
|
|
152
|
+
update[i].next[i] = cur.next[i] # type: ignore
|
|
153
|
+
|
|
154
|
+
while self._height > 0 and self._head.next[self._height - 1] is None:
|
|
155
|
+
self._height -= 1
|
|
156
|
+
|
|
157
|
+
self._length -= 1
|
|
158
|
+
return True
|
|
159
|
+
|
|
160
|
+
def iter(self, base: ta.Optional[T] = None) -> ta.Iterable[T]:
|
|
161
|
+
if base is not None:
|
|
162
|
+
cur = self._find(base)
|
|
163
|
+
while cur is not None and self._compare(base, cur.value) > 0: # type: ignore
|
|
164
|
+
cur = cur.next[0]
|
|
165
|
+
else:
|
|
166
|
+
cur = self._head.next[0]
|
|
167
|
+
|
|
168
|
+
while cur is not None:
|
|
169
|
+
yield cur.value # type: ignore
|
|
170
|
+
cur = cur.next[0]
|
|
171
|
+
|
|
172
|
+
def riter(self, base: ta.Optional[T] = None) -> ta.Iterable[T]:
|
|
173
|
+
if base is not None:
|
|
174
|
+
cur = self._find(base)
|
|
175
|
+
while cur is not self._head and self._compare(base, cur.value) < 0: # type: ignore
|
|
176
|
+
cur = cur.prev # type: ignore
|
|
177
|
+
else:
|
|
178
|
+
cur = self._head.next[self._height - 1]
|
|
179
|
+
while True:
|
|
180
|
+
next = cur.next[cur.next.index(None) - 1 if None in cur.next else -1] # type: ignore # noqa
|
|
181
|
+
if next is None:
|
|
182
|
+
break
|
|
183
|
+
cur = next
|
|
184
|
+
|
|
185
|
+
while cur is not self._head:
|
|
186
|
+
yield cur.value # type: ignore
|
|
187
|
+
cur = cur.prev # type: ignore
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class SkipListDict(SortedListDict[K, V]):
|
|
191
|
+
|
|
192
|
+
def __init__(self, *args, **kwargs) -> None:
|
|
193
|
+
super().__init__(SkipList(comparator=SortedListDict._item_comparator), *args, **kwargs)
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
from .. import lang
|
|
5
|
+
from .mappings import yield_dict_init
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
T = ta.TypeVar('T')
|
|
9
|
+
U = ta.TypeVar('U')
|
|
10
|
+
K = ta.TypeVar('K')
|
|
11
|
+
V = ta.TypeVar('V')
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SortedCollection(lang.Abstract, ta.Collection[T]):
|
|
15
|
+
|
|
16
|
+
Comparator = ta.Callable[[U, U], int]
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
def default_comparator(a: T, b: T) -> int:
|
|
20
|
+
"""https://docs.python.org/3.0/whatsnew/3.0.html#ordering-comparisons"""
|
|
21
|
+
|
|
22
|
+
return (a > b) - (a < b) # type: ignore
|
|
23
|
+
|
|
24
|
+
@abc.abstractmethod
|
|
25
|
+
def __len__(self) -> int:
|
|
26
|
+
raise NotImplementedError
|
|
27
|
+
|
|
28
|
+
@abc.abstractmethod
|
|
29
|
+
def __iter__(self) -> ta.Iterator[T]:
|
|
30
|
+
raise NotImplementedError
|
|
31
|
+
|
|
32
|
+
@abc.abstractmethod
|
|
33
|
+
def __contains__(self, value: T) -> bool: # type: ignore
|
|
34
|
+
raise NotImplementedError
|
|
35
|
+
|
|
36
|
+
@abc.abstractmethod
|
|
37
|
+
def add(self, value: T) -> bool:
|
|
38
|
+
raise NotImplementedError
|
|
39
|
+
|
|
40
|
+
@abc.abstractmethod
|
|
41
|
+
def find(self, value: T) -> ta.Optional[T]:
|
|
42
|
+
raise NotImplementedError
|
|
43
|
+
|
|
44
|
+
@abc.abstractmethod
|
|
45
|
+
def remove(self, value: T) -> bool:
|
|
46
|
+
raise NotImplementedError
|
|
47
|
+
|
|
48
|
+
@abc.abstractmethod
|
|
49
|
+
def iter(self, base: ta.Optional[T] = None) -> ta.Iterable[T]:
|
|
50
|
+
raise NotImplementedError
|
|
51
|
+
|
|
52
|
+
@abc.abstractmethod
|
|
53
|
+
def riter(self, base: ta.Optional[T] = None) -> ta.Iterable[T]:
|
|
54
|
+
raise NotImplementedError
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class SortedMapping(ta.Mapping[K, V]):
|
|
58
|
+
|
|
59
|
+
@abc.abstractmethod
|
|
60
|
+
def items(self) -> ta.Iterator[tuple[K, V]]: # type: ignore
|
|
61
|
+
raise NotImplementedError
|
|
62
|
+
|
|
63
|
+
@abc.abstractmethod
|
|
64
|
+
def ritems(self) -> ta.Iterator[tuple[K, V]]:
|
|
65
|
+
raise NotImplementedError
|
|
66
|
+
|
|
67
|
+
@abc.abstractmethod
|
|
68
|
+
def itemsfrom(self, key: K) -> ta.Iterator[tuple[K, V]]:
|
|
69
|
+
raise NotImplementedError
|
|
70
|
+
|
|
71
|
+
@abc.abstractmethod
|
|
72
|
+
def ritemsfrom(self, key: K) -> ta.Iterator[tuple[K, V]]:
|
|
73
|
+
raise NotImplementedError
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class SortedMutableMapping(ta.MutableMapping[K, V], SortedMapping[K, V]):
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class SortedListDict(SortedMutableMapping[K, V]):
|
|
81
|
+
|
|
82
|
+
@staticmethod
|
|
83
|
+
def _item_comparator(a: tuple[K, V], b: tuple[K, V]) -> int:
|
|
84
|
+
return SortedCollection.default_comparator(a[0], b[0])
|
|
85
|
+
|
|
86
|
+
def __init__(self, impl: SortedCollection, *args, **kwargs) -> None:
|
|
87
|
+
super().__init__()
|
|
88
|
+
self._impl = impl
|
|
89
|
+
for k, v in yield_dict_init(*args, **kwargs):
|
|
90
|
+
self[k] = v
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def debug(self) -> ta.Mapping[K, V]:
|
|
94
|
+
return dict(self)
|
|
95
|
+
|
|
96
|
+
def __getitem__(self, key: K) -> V:
|
|
97
|
+
item = self._impl.find((key, None))
|
|
98
|
+
if item is None:
|
|
99
|
+
raise KeyError(key)
|
|
100
|
+
return item[1]
|
|
101
|
+
|
|
102
|
+
def __setitem__(self, key: K, value: V) -> None:
|
|
103
|
+
self._impl.remove((key, None))
|
|
104
|
+
self._impl.add((key, value))
|
|
105
|
+
|
|
106
|
+
def __delitem__(self, key: K) -> None:
|
|
107
|
+
self._impl.remove((key, None))
|
|
108
|
+
|
|
109
|
+
def __len__(self) -> int:
|
|
110
|
+
return len(self._impl)
|
|
111
|
+
|
|
112
|
+
def __iter__(self) -> ta.Iterator[K]:
|
|
113
|
+
for k, v in self._impl:
|
|
114
|
+
yield k
|
|
115
|
+
|
|
116
|
+
def items(self) -> ta.Iterator[tuple[K, V]]: # type: ignore
|
|
117
|
+
yield from self._impl.iter()
|
|
118
|
+
|
|
119
|
+
def ritems(self) -> ta.Iterator[tuple[K, V]]:
|
|
120
|
+
yield from self._impl.riter()
|
|
121
|
+
|
|
122
|
+
def itemsfrom(self, key: K) -> ta.Iterator[tuple[K, V]]:
|
|
123
|
+
yield from self._impl.iter((key, None))
|
|
124
|
+
|
|
125
|
+
def ritemsfrom(self, key: K) -> ta.Iterator[tuple[K, V]]:
|
|
126
|
+
yield from self._impl.riter((key, None))
|