omlish 0.0.0.dev338__py3-none-any.whl → 0.0.0.dev340__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.
- omlish/__about__.py +2 -2
- omlish/asyncs/bluelet/LICENSE +1 -1
- omlish/asyncs/bluelet/api.py +1 -1
- omlish/asyncs/bluelet/core.py +1 -1
- omlish/asyncs/bluelet/events.py +1 -1
- omlish/asyncs/bluelet/files.py +1 -1
- omlish/asyncs/bluelet/runner.py +1 -1
- omlish/asyncs/bluelet/sockets.py +1 -1
- omlish/collections/__init__.py +19 -0
- omlish/collections/multimaps.py +151 -0
- omlish/formats/json/__init__.py +13 -13
- omlish/formats/json/backends/__init__.py +2 -2
- omlish/formats/json/backends/default.py +56 -12
- omlish/formats/json/backends/orjson.py +6 -5
- omlish/formats/json/backends/std.py +4 -1
- omlish/formats/json/backends/ujson.py +6 -5
- omlish/formats/json/codecs.py +4 -4
- omlish/graphs/dags.py +112 -48
- omlish/graphs/domination.py +5 -1
- omlish/graphs/dot/items.py +3 -0
- omlish/graphs/dot/make.py +3 -0
- omlish/graphs/dot/rendering.py +3 -0
- omlish/graphs/dot/utils.py +3 -0
- omlish/graphs/trees.py +5 -4
- omlish/lang/__init__.py +9 -0
- omlish/lang/classes/bindable.py +43 -0
- omlish/lang/classes/protocols.py +26 -0
- omlish/math/__init__.py +15 -15
- omlish/math/fixed.py +25 -15
- omlish/os/forkhooks.py +4 -4
- omlish/testing/pytest/plugins/switches/plugin.py +2 -0
- omlish/testing/pytest/skip.py +7 -8
- omlish/typedvalues/__init__.py +4 -0
- omlish/typedvalues/accessor.py +5 -1
- omlish/typedvalues/collection.py +1 -0
- omlish/typedvalues/of_.py +51 -0
- {omlish-0.0.0.dev338.dist-info → omlish-0.0.0.dev340.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev338.dist-info → omlish-0.0.0.dev340.dist-info}/RECORD +42 -39
- omlish/formats/json/json.py +0 -17
- {omlish-0.0.0.dev338.dist-info → omlish-0.0.0.dev340.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev338.dist-info → omlish-0.0.0.dev340.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev338.dist-info → omlish-0.0.0.dev340.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev338.dist-info → omlish-0.0.0.dev340.dist-info}/top_level.txt +0 -0
omlish/__about__.py
CHANGED
omlish/asyncs/bluelet/LICENSE
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Based on bluelet ( https://github.com/sampsyo/bluelet ) by Adrian Sampson, original license:
|
2
2
|
|
3
|
-
THE SOFTWARE IS PROVIDED
|
3
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
4
4
|
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
5
5
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
6
6
|
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
omlish/asyncs/bluelet/api.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
2
|
# @omlish-lite
|
3
3
|
# Based on bluelet ( https://github.com/sampsyo/bluelet ) by Adrian Sampson, original license:
|
4
|
-
# THE SOFTWARE IS PROVIDED
|
4
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
5
5
|
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
6
6
|
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
7
7
|
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
omlish/asyncs/bluelet/core.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
2
|
# @omlish-lite
|
3
3
|
# Based on bluelet ( https://github.com/sampsyo/bluelet ) by Adrian Sampson, original license:
|
4
|
-
# THE SOFTWARE IS PROVIDED
|
4
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
5
5
|
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
6
6
|
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
7
7
|
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
omlish/asyncs/bluelet/events.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# ruff: noqa: UP007
|
2
2
|
# @omlish-lite
|
3
3
|
# Based on bluelet ( https://github.com/sampsyo/bluelet ) by Adrian Sampson, original license:
|
4
|
-
# THE SOFTWARE IS PROVIDED
|
4
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
5
5
|
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
6
6
|
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
7
7
|
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
omlish/asyncs/bluelet/files.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
2
|
# @omlish-lite
|
3
3
|
# Based on bluelet ( https://github.com/sampsyo/bluelet ) by Adrian Sampson, original license:
|
4
|
-
# THE SOFTWARE IS PROVIDED
|
4
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
5
5
|
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
6
6
|
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
7
7
|
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
omlish/asyncs/bluelet/runner.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
2
|
# @omlish-lite
|
3
3
|
# Based on bluelet ( https://github.com/sampsyo/bluelet ) by Adrian Sampson, original license:
|
4
|
-
# THE SOFTWARE IS PROVIDED
|
4
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
5
5
|
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
6
6
|
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
7
7
|
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
omlish/asyncs/bluelet/sockets.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
2
|
# @omlish-lite
|
3
3
|
# Based on bluelet ( https://github.com/sampsyo/bluelet ) by Adrian Sampson, original license:
|
4
|
-
# THE SOFTWARE IS PROVIDED
|
4
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
5
5
|
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
6
6
|
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
7
7
|
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
omlish/collections/__init__.py
CHANGED
@@ -76,6 +76,25 @@ from .mappings import ( # noqa
|
|
76
76
|
multikey_dict,
|
77
77
|
)
|
78
78
|
|
79
|
+
from .multimaps import ( # noqa
|
80
|
+
MultiMap,
|
81
|
+
|
82
|
+
SequenceMultiMap,
|
83
|
+
AbstractSetMultiMap,
|
84
|
+
|
85
|
+
BiMultiMap,
|
86
|
+
InverseBiMultiMap,
|
87
|
+
|
88
|
+
SequenceBiMultiMap,
|
89
|
+
AbstractSetBiMultiMap,
|
90
|
+
|
91
|
+
TupleBiMultiMap,
|
92
|
+
seq_bi_multi_map,
|
93
|
+
|
94
|
+
FrozensetBiMultiMap,
|
95
|
+
abs_set_bi_multi_map,
|
96
|
+
)
|
97
|
+
|
79
98
|
from .ordered import ( # noqa
|
80
99
|
OrderedFrozenSet,
|
81
100
|
OrderedSet,
|
@@ -0,0 +1,151 @@
|
|
1
|
+
import abc
|
2
|
+
import typing as ta
|
3
|
+
|
4
|
+
from .. import lang
|
5
|
+
|
6
|
+
|
7
|
+
K = ta.TypeVar('K')
|
8
|
+
V = ta.TypeVar('V')
|
9
|
+
MV = ta.TypeVar('MV', bound=ta.Iterable)
|
10
|
+
|
11
|
+
|
12
|
+
##
|
13
|
+
|
14
|
+
|
15
|
+
class MultiMap(ta.Mapping[K, MV], abc.ABC, ta.Generic[K, V, MV]):
|
16
|
+
pass
|
17
|
+
|
18
|
+
|
19
|
+
SequenceMultiMap: ta.TypeAlias = MultiMap[K, V, ta.Sequence[V]]
|
20
|
+
AbstractSetMultiMap: ta.TypeAlias = MultiMap[K, V, ta.AbstractSet[V]]
|
21
|
+
|
22
|
+
|
23
|
+
##
|
24
|
+
|
25
|
+
|
26
|
+
class BiMultiMap(MultiMap[K, V, MV], abc.ABC, ta.Generic[K, V, MV]):
|
27
|
+
@abc.abstractmethod
|
28
|
+
def inverse(self) -> 'InverseBiMultiMap[K, V, MV]':
|
29
|
+
raise NotImplementedError
|
30
|
+
|
31
|
+
|
32
|
+
class InverseBiMultiMap(ta.Mapping[V, K], abc.ABC, ta.Generic[K, V, MV]):
|
33
|
+
@abc.abstractmethod
|
34
|
+
def inverse(self) -> BiMultiMap[K, V, MV]:
|
35
|
+
raise NotImplementedError
|
36
|
+
|
37
|
+
|
38
|
+
SequenceBiMultiMap: ta.TypeAlias = BiMultiMap[K, V, ta.Sequence[V]]
|
39
|
+
AbstractSetBiMultiMap: ta.TypeAlias = BiMultiMap[K, V, ta.AbstractSet[V]]
|
40
|
+
|
41
|
+
|
42
|
+
##
|
43
|
+
|
44
|
+
|
45
|
+
class InverseBiMultiMapImpl(InverseBiMultiMap[K, V, MV], ta.Generic[K, V, MV]):
|
46
|
+
def __init__(self, m: BiMultiMap[K, V, MV], dct: ta.Mapping[V, K]) -> None:
|
47
|
+
super().__init__()
|
48
|
+
|
49
|
+
self._m = m
|
50
|
+
self._dct = dct
|
51
|
+
|
52
|
+
def inverse(self) -> BiMultiMap[K, V, MV]:
|
53
|
+
return self._m
|
54
|
+
|
55
|
+
def __getitem__(self, key: V, /) -> K:
|
56
|
+
return self._dct[key]
|
57
|
+
|
58
|
+
def __len__(self) -> int:
|
59
|
+
return len(self._dct)
|
60
|
+
|
61
|
+
def __iter__(self) -> ta.Iterator[V]:
|
62
|
+
return iter(self._dct)
|
63
|
+
|
64
|
+
|
65
|
+
class BaseBiMultiMap(BiMultiMap[K, V, MV], abc.ABC, ta.Generic[K, V, MV]):
|
66
|
+
def __init__(self, *args: ta.Any, **kwargs: ta.Any) -> None:
|
67
|
+
super().__init__()
|
68
|
+
|
69
|
+
dct: dict[K, MV] = {}
|
70
|
+
i_dct: dict[V, K] = {}
|
71
|
+
for k, mv in lang.yield_dict_init(*args, **kwargs):
|
72
|
+
l: list[V] = []
|
73
|
+
for v in mv:
|
74
|
+
if v in i_dct:
|
75
|
+
raise KeyError(v)
|
76
|
+
l.append(v)
|
77
|
+
i_dct[v] = k
|
78
|
+
dct[k] = self._aggregate_values(l)
|
79
|
+
|
80
|
+
self._dct = dct
|
81
|
+
self._i: InverseBiMultiMap[K, V, MV] = InverseBiMultiMapImpl(self, i_dct)
|
82
|
+
|
83
|
+
@abc.abstractmethod
|
84
|
+
def _aggregate_values(self, vs: list[V]) -> MV:
|
85
|
+
raise NotImplementedError
|
86
|
+
|
87
|
+
def inverse(self) -> 'InverseBiMultiMap[K, V, MV]':
|
88
|
+
return self._i
|
89
|
+
|
90
|
+
def __getitem__(self, key: K, /) -> MV:
|
91
|
+
return self._dct[key]
|
92
|
+
|
93
|
+
def __len__(self) -> int:
|
94
|
+
return len(self._dct)
|
95
|
+
|
96
|
+
def __iter__(self) -> ta.Iterator[K]:
|
97
|
+
return iter(self._dct)
|
98
|
+
|
99
|
+
|
100
|
+
#
|
101
|
+
|
102
|
+
|
103
|
+
class TupleBiMultiMap(BaseBiMultiMap[K, V, tuple[V, ...]], ta.Generic[K, V]):
|
104
|
+
def _aggregate_values(self, vs: list[V]) -> tuple[V, ...]:
|
105
|
+
return tuple(vs)
|
106
|
+
|
107
|
+
|
108
|
+
# FIXME: lame
|
109
|
+
# lang.static_check_issubclass[BiMultiMap[int, str, tuple[str, ...]]](TupleBiMultiMap[int, str])
|
110
|
+
# lang.static_check_issubclass[BiMultiMap[int, str, ta.Sequence[str]]](TupleBiMultiMap[int, str])
|
111
|
+
# lang.static_check_issubclass[SequenceBiMultiMap[int, str]](TupleBiMultiMap[int, str])
|
112
|
+
|
113
|
+
|
114
|
+
@ta.overload
|
115
|
+
def seq_bi_multi_map(dct: ta.Mapping[K, ta.Iterable[V]]) -> SequenceBiMultiMap[K, V]:
|
116
|
+
...
|
117
|
+
|
118
|
+
|
119
|
+
@ta.overload
|
120
|
+
def seq_bi_multi_map(items: ta.Iterable[tuple[K, ta.Iterable[V]]]) -> SequenceBiMultiMap[K, V]:
|
121
|
+
...
|
122
|
+
|
123
|
+
|
124
|
+
def seq_bi_multi_map(*args, **kwargs):
|
125
|
+
return TupleBiMultiMap(*args, **kwargs)
|
126
|
+
|
127
|
+
|
128
|
+
#
|
129
|
+
|
130
|
+
|
131
|
+
class FrozensetBiMultiMap(BaseBiMultiMap[K, V, frozenset[V]], ta.Generic[K, V]):
|
132
|
+
def _aggregate_values(self, vs: list[V]) -> frozenset[V]:
|
133
|
+
return frozenset(vs)
|
134
|
+
|
135
|
+
|
136
|
+
# FIXME: lame
|
137
|
+
# lang.static_check_issubclass[AbstractSetBiMultiMap[int, str]](FrozensetBiMultiMap[int, str])
|
138
|
+
|
139
|
+
|
140
|
+
@ta.overload
|
141
|
+
def abs_set_bi_multi_map(dct: ta.Mapping[K, ta.Iterable[V]]) -> AbstractSetBiMultiMap[K, V]:
|
142
|
+
...
|
143
|
+
|
144
|
+
|
145
|
+
@ta.overload
|
146
|
+
def abs_set_bi_multi_map(items: ta.Iterable[tuple[K, ta.Iterable[V]]]) -> AbstractSetBiMultiMap[K, V]:
|
147
|
+
...
|
148
|
+
|
149
|
+
|
150
|
+
def abs_set_bi_multi_map(*args, **kwargs):
|
151
|
+
return FrozensetBiMultiMap(*args, **kwargs)
|
omlish/formats/json/__init__.py
CHANGED
@@ -12,10 +12,21 @@ from ... import lang as _lang
|
|
12
12
|
from .backends import ( # noqa
|
13
13
|
Backend,
|
14
14
|
|
15
|
-
|
15
|
+
default_backend,
|
16
16
|
|
17
17
|
StdBackend,
|
18
|
-
|
18
|
+
std_backend,
|
19
|
+
)
|
20
|
+
|
21
|
+
from .backends.default import ( # noqa
|
22
|
+
dump,
|
23
|
+
dump_compact,
|
24
|
+
dump_pretty,
|
25
|
+
dumps,
|
26
|
+
dumps_compact,
|
27
|
+
dumps_pretty,
|
28
|
+
load,
|
29
|
+
loads,
|
19
30
|
)
|
20
31
|
|
21
32
|
from .consts import ( # noqa
|
@@ -32,17 +43,6 @@ from .encoding import ( # noqa
|
|
32
43
|
detect_encoding,
|
33
44
|
)
|
34
45
|
|
35
|
-
from .json import ( # noqa
|
36
|
-
dump,
|
37
|
-
dump_compact,
|
38
|
-
dump_pretty,
|
39
|
-
dumps,
|
40
|
-
dumps_compact,
|
41
|
-
dumps_pretty,
|
42
|
-
load,
|
43
|
-
loads,
|
44
|
-
)
|
45
|
-
|
46
46
|
if _ta.TYPE_CHECKING:
|
47
47
|
from .rendering import ( # noqa
|
48
48
|
JsonRenderer,
|
@@ -1,13 +1,57 @@
|
|
1
|
+
import typing as ta
|
2
|
+
|
3
|
+
from .... import lang
|
1
4
|
from .base import Backend
|
2
|
-
from .orjson import
|
3
|
-
from .std import
|
4
|
-
from .ujson import
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
5
|
+
from .orjson import orjson_backend
|
6
|
+
from .std import std_backend
|
7
|
+
from .ujson import ujson_backend
|
8
|
+
|
9
|
+
|
10
|
+
##
|
11
|
+
|
12
|
+
|
13
|
+
@lang.cached_function
|
14
|
+
def default_backend() -> Backend:
|
15
|
+
for fn in [
|
16
|
+
orjson_backend,
|
17
|
+
ujson_backend,
|
18
|
+
]:
|
19
|
+
if (be := fn()) is not None:
|
20
|
+
return be
|
21
|
+
|
22
|
+
return std_backend()
|
23
|
+
|
24
|
+
|
25
|
+
##
|
26
|
+
|
27
|
+
|
28
|
+
def dump(obj: ta.Any, fp: ta.Any, **kwargs: ta.Any) -> None:
|
29
|
+
return default_backend().dump(obj, fp, **kwargs)
|
30
|
+
|
31
|
+
|
32
|
+
def dumps(obj: ta.Any, **kwargs: ta.Any) -> str:
|
33
|
+
return default_backend().dumps(obj, **kwargs)
|
34
|
+
|
35
|
+
|
36
|
+
def load(fp: ta.Any, **kwargs: ta.Any) -> ta.Any:
|
37
|
+
return default_backend().load(fp, **kwargs)
|
38
|
+
|
39
|
+
|
40
|
+
def loads(s: str | bytes | bytearray, **kwargs: ta.Any) -> ta.Any:
|
41
|
+
return default_backend().loads(s, **kwargs)
|
42
|
+
|
43
|
+
|
44
|
+
def dump_pretty(obj: ta.Any, fp: ta.Any, **kwargs: ta.Any) -> None:
|
45
|
+
return default_backend().dump_pretty(obj, fp, **kwargs)
|
46
|
+
|
47
|
+
|
48
|
+
def dumps_pretty(obj: ta.Any, **kwargs: ta.Any) -> str:
|
49
|
+
return default_backend().dumps_pretty(obj, **kwargs)
|
50
|
+
|
51
|
+
|
52
|
+
def dump_compact(obj: ta.Any, fp: ta.Any, **kwargs: ta.Any) -> None:
|
53
|
+
return default_backend().dump_compact(obj, fp, **kwargs)
|
54
|
+
|
55
|
+
|
56
|
+
def dumps_compact(obj: ta.Any, **kwargs: ta.Any) -> str:
|
57
|
+
return default_backend().dumps_compact(obj, **kwargs)
|
@@ -109,8 +109,9 @@ class OrjsonBackend(Backend):
|
|
109
109
|
return self.dumps(obj, **kwargs)
|
110
110
|
|
111
111
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
112
|
+
@lang.cached_function
|
113
|
+
def orjson_backend() -> OrjsonBackend | None:
|
114
|
+
if lang.can_import('orjson'):
|
115
|
+
return OrjsonBackend()
|
116
|
+
else:
|
117
|
+
return None
|
@@ -8,6 +8,7 @@ import dataclasses as dc
|
|
8
8
|
import json
|
9
9
|
import typing as ta
|
10
10
|
|
11
|
+
from .... import lang
|
11
12
|
from ..consts import COMPACT_KWARGS
|
12
13
|
from ..consts import PRETTY_KWARGS
|
13
14
|
from .base import Backend
|
@@ -73,4 +74,6 @@ class StdBackend(Backend):
|
|
73
74
|
return json.dumps(obj, **COMPACT_KWARGS, **kwargs)
|
74
75
|
|
75
76
|
|
76
|
-
|
77
|
+
@lang.cached_function
|
78
|
+
def std_backend() -> StdBackend:
|
79
|
+
return StdBackend()
|
@@ -67,8 +67,9 @@ class UjsonBackend(Backend):
|
|
67
67
|
return uj.dumps(obj, **kwargs)
|
68
68
|
|
69
69
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
70
|
+
@lang.cached_function
|
71
|
+
def ujson_backend() -> UjsonBackend | None:
|
72
|
+
if lang.can_import('ujson'):
|
73
|
+
return UjsonBackend()
|
74
|
+
else:
|
75
|
+
return None
|
omlish/formats/json/codecs.py
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
from ..codecs import make_object_lazy_loaded_codec
|
2
2
|
from ..codecs import make_str_object_codec
|
3
|
-
from .
|
4
|
-
from .
|
5
|
-
from .
|
6
|
-
from .
|
3
|
+
from .backends.default import dumps
|
4
|
+
from .backends.default import dumps_compact
|
5
|
+
from .backends.default import dumps_pretty
|
6
|
+
from .backends.default import loads
|
7
7
|
|
8
8
|
|
9
9
|
##
|
omlish/graphs/dags.py
CHANGED
@@ -12,100 +12,164 @@ from .. import check
|
|
12
12
|
from .. import lang
|
13
13
|
|
14
14
|
|
15
|
-
K = ta.TypeVar('K')
|
16
|
-
V = ta.TypeVar('V')
|
17
15
|
T = ta.TypeVar('T')
|
18
|
-
U = ta.TypeVar('U')
|
19
16
|
|
20
17
|
|
21
|
-
|
22
|
-
|
23
|
-
|
18
|
+
##
|
19
|
+
|
20
|
+
|
21
|
+
class LinkError(KeyError):
|
22
|
+
pass
|
23
|
+
|
24
|
+
|
25
|
+
def traverse_links(
|
26
|
+
links: ta.Mapping[T, ta.Iterable[T]],
|
27
|
+
roots: ta.Iterable[T],
|
28
|
+
*,
|
29
|
+
include_roots: bool = False,
|
30
|
+
strict: bool = False,
|
31
|
+
) -> set[T]:
|
32
|
+
"""Returns all keys deeply reachable from given roots. Handles cycles."""
|
33
|
+
|
34
|
+
roots = set(roots)
|
35
|
+
|
36
|
+
todo = set(roots)
|
24
37
|
seen: set[T] = set()
|
25
38
|
while todo:
|
26
39
|
key = todo.pop()
|
27
40
|
seen.add(key)
|
28
|
-
cur = data.get(key, [])
|
29
|
-
todo.update(set(cur) - seen)
|
30
|
-
return seen - keys
|
31
|
-
|
32
41
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
42
|
+
try:
|
43
|
+
cur = links[key]
|
44
|
+
except KeyError:
|
45
|
+
if strict:
|
46
|
+
raise LinkError(key) from None
|
47
|
+
else:
|
48
|
+
todo.update(set(cur) - seen)
|
49
|
+
|
50
|
+
if include_roots:
|
51
|
+
return seen
|
52
|
+
else:
|
53
|
+
return seen - roots
|
54
|
+
|
55
|
+
|
56
|
+
def invert_links(
|
57
|
+
links: ta.Mapping[T, ta.Iterable[T]],
|
58
|
+
*,
|
59
|
+
auto_add_roots: bool = False,
|
60
|
+
if_absent: ta.Literal['raise', 'ignore', 'add'] = 'add',
|
61
|
+
) -> dict[T, set[T]]:
|
62
|
+
check.in_(if_absent, ('raise', 'ignore', 'add'))
|
63
|
+
if if_absent != 'add':
|
64
|
+
check.arg(auto_add_roots, 'auto_add_roots must be True with given if_absent is not "add"')
|
65
|
+
|
66
|
+
ret: dict[T, set[T]]
|
67
|
+
if auto_add_roots:
|
68
|
+
ret = {src: set() for src in links}
|
69
|
+
else:
|
70
|
+
ret = {}
|
71
|
+
|
72
|
+
for src, dsts in links.items():
|
73
|
+
for dst in dsts:
|
37
74
|
try:
|
38
|
-
|
75
|
+
tgt = ret[dst]
|
39
76
|
except KeyError:
|
40
|
-
|
41
|
-
|
42
|
-
|
77
|
+
if if_absent == 'raise':
|
78
|
+
raise LinkError(dst) from None
|
79
|
+
elif if_absent == 'ignore':
|
80
|
+
continue
|
81
|
+
elif if_absent == 'add':
|
82
|
+
tgt = ret[dst] = set()
|
83
|
+
else:
|
84
|
+
raise RuntimeError from None
|
85
|
+
|
86
|
+
tgt.add(src)
|
43
87
|
|
88
|
+
return ret
|
44
89
|
|
45
|
-
|
46
|
-
|
47
|
-
for l, rs in src.items():
|
48
|
-
for r in rs:
|
49
|
-
dst[r].add(l)
|
50
|
-
return dst
|
90
|
+
|
91
|
+
##
|
51
92
|
|
52
93
|
|
53
94
|
class Dag(ta.Generic[T]):
|
54
|
-
|
95
|
+
"""Given 'input_its_by_outputs', or a map from nodes to that node's dependencies."""
|
96
|
+
|
97
|
+
def __init__(
|
98
|
+
self,
|
99
|
+
input_its_by_outputs: ta.Mapping[T, ta.Iterable[T]],
|
100
|
+
*,
|
101
|
+
auto_add_outputs: bool = True,
|
102
|
+
if_absent: ta.Literal['raise', 'ignore', 'add'] = 'add',
|
103
|
+
) -> None:
|
55
104
|
super().__init__()
|
56
105
|
|
57
106
|
self._input_sets_by_output = {u: set(d) for u, d in input_its_by_outputs.items()}
|
58
107
|
|
108
|
+
self._output_sets_by_input = invert_links(
|
109
|
+
self._input_sets_by_output,
|
110
|
+
auto_add_roots=auto_add_outputs,
|
111
|
+
if_absent=if_absent,
|
112
|
+
)
|
113
|
+
|
59
114
|
@property
|
60
115
|
def input_sets_by_output(self) -> ta.Mapping[T, ta.AbstractSet[T]]:
|
61
116
|
return self._input_sets_by_output
|
62
117
|
|
63
|
-
@
|
118
|
+
@property
|
64
119
|
def output_sets_by_input(self) -> ta.Mapping[T, ta.AbstractSet[T]]:
|
65
|
-
return
|
120
|
+
return self._output_sets_by_input
|
66
121
|
|
67
|
-
def subdag(
|
68
|
-
|
122
|
+
def subdag(
|
123
|
+
self,
|
124
|
+
roots: ta.Iterable[T],
|
125
|
+
*,
|
126
|
+
ignored: ta.Iterable[T] | None = None,
|
127
|
+
) -> 'Subdag[T]':
|
128
|
+
return Subdag(
|
129
|
+
self,
|
130
|
+
roots,
|
131
|
+
ignored=ignored,
|
132
|
+
)
|
69
133
|
|
70
134
|
|
71
|
-
class Subdag(ta.Generic[
|
135
|
+
class Subdag(ta.Generic[T]):
|
72
136
|
def __init__(
|
73
137
|
self,
|
74
|
-
dag:
|
75
|
-
|
138
|
+
dag: Dag[T],
|
139
|
+
roots: ta.Iterable[T],
|
76
140
|
*,
|
77
|
-
ignored: ta.Iterable[
|
141
|
+
ignored: ta.Iterable[T] | None = None,
|
78
142
|
) -> None:
|
79
143
|
super().__init__()
|
80
144
|
|
81
|
-
self._dag: Dag[
|
82
|
-
self.
|
83
|
-
self._ignored = set(ignored or []) - self.
|
145
|
+
self._dag: Dag[T] = check.isinstance(dag, Dag)
|
146
|
+
self._roots = set(roots)
|
147
|
+
self._ignored = set(ignored or []) - self._roots
|
84
148
|
|
85
149
|
@property
|
86
|
-
def dag(self) ->
|
150
|
+
def dag(self) -> Dag[T]:
|
87
151
|
return self._dag
|
88
152
|
|
89
153
|
@property
|
90
|
-
def
|
91
|
-
return self.
|
154
|
+
def roots(self) -> ta.AbstractSet[T]:
|
155
|
+
return self._roots
|
92
156
|
|
93
157
|
@property
|
94
|
-
def ignored(self) -> ta.AbstractSet[
|
158
|
+
def ignored(self) -> ta.AbstractSet[T]:
|
95
159
|
return self._ignored
|
96
160
|
|
97
161
|
@lang.cached_property
|
98
|
-
def inputs(self) -> ta.AbstractSet[
|
99
|
-
return traverse_links(self.
|
162
|
+
def inputs(self) -> ta.AbstractSet[T]:
|
163
|
+
return traverse_links(self._dag.input_sets_by_output, self._roots) - self._ignored
|
100
164
|
|
101
165
|
@lang.cached_property
|
102
|
-
def outputs(self) -> ta.AbstractSet[
|
103
|
-
return traverse_links(self.
|
166
|
+
def outputs(self) -> ta.AbstractSet[T]:
|
167
|
+
return traverse_links(self._dag.output_sets_by_input, self._roots) - self._ignored
|
104
168
|
|
105
169
|
@lang.cached_property
|
106
|
-
def output_inputs(self) -> ta.AbstractSet[
|
107
|
-
return traverse_links(self.
|
170
|
+
def output_inputs(self) -> ta.AbstractSet[T]:
|
171
|
+
return traverse_links(self._dag.input_sets_by_output, self.outputs) - self._ignored
|
108
172
|
|
109
173
|
@lang.cached_property
|
110
|
-
def all(self) -> ta.AbstractSet[
|
111
|
-
return self.
|
174
|
+
def all(self) -> ta.AbstractSet[T]:
|
175
|
+
return self.roots | self.inputs | self.outputs | self.output_inputs
|
omlish/graphs/domination.py
CHANGED
@@ -9,7 +9,11 @@ from .. import lang
|
|
9
9
|
V = ta.TypeVar('V')
|
10
10
|
MK = ta.TypeVar('MK')
|
11
11
|
MV = ta.TypeVar('MV')
|
12
|
-
|
12
|
+
|
13
|
+
SetMap: ta.TypeAlias = ta.Mapping[MK, ta.AbstractSet[MV]]
|
14
|
+
|
15
|
+
|
16
|
+
##
|
13
17
|
|
14
18
|
|
15
19
|
class DirectedGraph(ta.Generic[V], lang.Abstract):
|