omlish 0.0.0.dev237__py3-none-any.whl → 0.0.0.dev238__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/collections/__init__.py +5 -5
- omlish/collections/ranked.py +79 -0
- omlish/configs/classes.py +4 -0
- omlish/daemons/services.py +74 -0
- omlish/daemons/targets.py +10 -22
- omlish/dataclasses/__init__.py +6 -2
- omlish/dataclasses/static.py +176 -0
- omlish/dataclasses/utils.py +0 -9
- omlish/graphs/trees.py +8 -8
- omlish/lang/descriptors.py +8 -7
- omlish/lite/dataclasses.py +18 -0
- omlish/manifests/__init__.py +0 -2
- omlish/manifests/base.py +1 -0
- omlish/manifests/load.py +1 -0
- omlish/manifests/static.py +20 -0
- omlish/manifests/types.py +1 -0
- omlish/metadata.py +153 -0
- {omlish-0.0.0.dev237.dist-info → omlish-0.0.0.dev238.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev237.dist-info → omlish-0.0.0.dev238.dist-info}/RECORD +24 -21
- omlish/collections/indexed.py +0 -73
- {omlish-0.0.0.dev237.dist-info → omlish-0.0.0.dev238.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev237.dist-info → omlish-0.0.0.dev238.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev237.dist-info → omlish-0.0.0.dev238.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev237.dist-info → omlish-0.0.0.dev238.dist-info}/top_level.txt +0 -0
omlish/__about__.py
CHANGED
omlish/collections/__init__.py
CHANGED
@@ -57,11 +57,6 @@ from .identity import ( # noqa
|
|
57
57
|
IdentityWeakSet,
|
58
58
|
)
|
59
59
|
|
60
|
-
from .indexed import ( # noqa
|
61
|
-
IndexedSeq,
|
62
|
-
IndexedSetSeq,
|
63
|
-
)
|
64
|
-
|
65
60
|
from .mappings import ( # noqa
|
66
61
|
MissingDict,
|
67
62
|
TypeMap,
|
@@ -91,6 +86,11 @@ else:
|
|
91
86
|
'new_treap_map',
|
92
87
|
])
|
93
88
|
|
89
|
+
from .ranked import ( # noqa
|
90
|
+
RankedSeq,
|
91
|
+
RankedSetSeq,
|
92
|
+
)
|
93
|
+
|
94
94
|
if _ta.TYPE_CHECKING:
|
95
95
|
from .sorted.skiplist import ( # noqa
|
96
96
|
SkipList,
|
@@ -0,0 +1,79 @@
|
|
1
|
+
import typing as ta
|
2
|
+
|
3
|
+
from .identity import IdentityKeyDict
|
4
|
+
from .identity import IdentitySet
|
5
|
+
|
6
|
+
|
7
|
+
T = ta.TypeVar('T')
|
8
|
+
|
9
|
+
|
10
|
+
##
|
11
|
+
|
12
|
+
|
13
|
+
class RankedSeq(ta.Sequence[T]):
|
14
|
+
def __init__(self, it: ta.Iterable[T], *, identity: bool = False) -> None:
|
15
|
+
super().__init__()
|
16
|
+
|
17
|
+
self._lst = list(it)
|
18
|
+
self._ranks: ta.Mapping[T, int] = (IdentityKeyDict if identity else dict)((e, i) for i, e in enumerate(self._lst)) # noqa
|
19
|
+
if len(self._ranks) != len(self._lst):
|
20
|
+
raise ValueError(f'{len(self._ranks)} != {len(self._lst)}')
|
21
|
+
|
22
|
+
@property
|
23
|
+
def debug(self) -> ta.Sequence[T]:
|
24
|
+
return self._lst
|
25
|
+
|
26
|
+
def __iter__(self) -> ta.Iterator[T]:
|
27
|
+
return iter(self._lst)
|
28
|
+
|
29
|
+
def __getitem__(self, rank: int) -> T: # type: ignore
|
30
|
+
return self._lst[rank]
|
31
|
+
|
32
|
+
def __len__(self) -> int:
|
33
|
+
return len(self._lst)
|
34
|
+
|
35
|
+
def __contains__(self, obj: T) -> bool: # type: ignore
|
36
|
+
return obj in self._ranks
|
37
|
+
|
38
|
+
@property
|
39
|
+
def ranks(self) -> ta.Mapping[T, int]:
|
40
|
+
return self._ranks
|
41
|
+
|
42
|
+
def rank(self, obj: T) -> int:
|
43
|
+
return self._ranks[obj]
|
44
|
+
|
45
|
+
|
46
|
+
##
|
47
|
+
|
48
|
+
|
49
|
+
class RankedSetSeq(ta.Sequence[ta.AbstractSet[T]]):
|
50
|
+
def __init__(self, it: ta.Iterable[ta.Iterable[T]], *, identity: bool = False) -> None:
|
51
|
+
super().__init__()
|
52
|
+
|
53
|
+
self._lst = [(IdentitySet if identity else set)(e) for e in it]
|
54
|
+
self._ranks: ta.Mapping[T, int] = (IdentityKeyDict if identity else dict)((e, i) for i, es in enumerate(self._lst) for e in es) # noqa
|
55
|
+
if len(self._ranks) != sum(map(len, self._lst)):
|
56
|
+
raise ValueError(f'{len(self._ranks)} != {sum(map(len, self._lst))}')
|
57
|
+
|
58
|
+
@property
|
59
|
+
def debug(self) -> ta.Sequence[ta.AbstractSet[T]]:
|
60
|
+
return self._lst
|
61
|
+
|
62
|
+
def __iter__(self) -> ta.Iterator[ta.AbstractSet[T]]:
|
63
|
+
return iter(self._lst)
|
64
|
+
|
65
|
+
def __getitem__(self, rank: int) -> ta.AbstractSet[T]: # type: ignore
|
66
|
+
return self._lst[rank]
|
67
|
+
|
68
|
+
def __len__(self) -> int:
|
69
|
+
return len(self._lst)
|
70
|
+
|
71
|
+
def __contains__(self, obj: T) -> bool: # type: ignore
|
72
|
+
return obj in self._ranks
|
73
|
+
|
74
|
+
@property
|
75
|
+
def ranks(self) -> ta.Mapping[T, int]:
|
76
|
+
return self._ranks
|
77
|
+
|
78
|
+
def rank(self, obj: T) -> int:
|
79
|
+
return self._ranks[obj]
|
omlish/configs/classes.py
CHANGED
@@ -29,3 +29,7 @@ class Configurable(ta.Generic[ConfigurableConfigT], lang.Abstract):
|
|
29
29
|
super().__init__()
|
30
30
|
|
31
31
|
self._config: ConfigurableConfigT = check.isinstance(config, self.Config) # type: ignore[assignment]
|
32
|
+
|
33
|
+
@property
|
34
|
+
def config(self) -> ConfigurableConfigT:
|
35
|
+
return self._config
|
omlish/daemons/services.py
CHANGED
@@ -1,9 +1,15 @@
|
|
1
1
|
import abc
|
2
2
|
import typing as ta
|
3
3
|
|
4
|
+
from .. import cached
|
4
5
|
from .. import check
|
6
|
+
from .. import dataclasses as dc
|
5
7
|
from .. import lang
|
6
8
|
from ..configs.classes import Configurable
|
9
|
+
from .daemon import Daemon
|
10
|
+
from .targets import Target
|
11
|
+
from .targets import TargetRunner
|
12
|
+
from .targets import target_runner_for
|
7
13
|
|
8
14
|
|
9
15
|
ServiceConfigT = ta.TypeVar('ServiceConfigT', bound='Service.Config')
|
@@ -32,3 +38,71 @@ class Service(Configurable[ServiceConfigT], lang.Abstract):
|
|
32
38
|
@classmethod
|
33
39
|
def run_config(cls, config: Config) -> None:
|
34
40
|
return cls.from_config(config).run()
|
41
|
+
|
42
|
+
|
43
|
+
##
|
44
|
+
|
45
|
+
|
46
|
+
class ServiceTarget(Target):
|
47
|
+
svc: Service
|
48
|
+
|
49
|
+
|
50
|
+
class ServiceTargetRunner(TargetRunner, dc.Frozen):
|
51
|
+
target: ServiceTarget
|
52
|
+
|
53
|
+
def run(self) -> None:
|
54
|
+
self.target.svc.run()
|
55
|
+
|
56
|
+
|
57
|
+
@target_runner_for.register
|
58
|
+
def _(target: ServiceTarget) -> ServiceTargetRunner:
|
59
|
+
return ServiceTargetRunner(target)
|
60
|
+
|
61
|
+
|
62
|
+
#
|
63
|
+
|
64
|
+
|
65
|
+
class ServiceConfigTarget(Target):
|
66
|
+
cfg: Service.Config
|
67
|
+
|
68
|
+
|
69
|
+
class ServiceConfigTargetRunner(TargetRunner, dc.Frozen):
|
70
|
+
target: ServiceConfigTarget
|
71
|
+
|
72
|
+
def run(self) -> None:
|
73
|
+
Service.run_config(self.target.cfg)
|
74
|
+
|
75
|
+
|
76
|
+
@target_runner_for.register
|
77
|
+
def _(target: ServiceConfigTarget) -> ServiceConfigTargetRunner:
|
78
|
+
return ServiceConfigTargetRunner(target)
|
79
|
+
|
80
|
+
|
81
|
+
##
|
82
|
+
|
83
|
+
|
84
|
+
@dc.dataclass(frozen=True)
|
85
|
+
class ServiceDaemon(lang.Final):
|
86
|
+
service: Service | Service.Config
|
87
|
+
|
88
|
+
@cached.function
|
89
|
+
def service_(self) -> Service:
|
90
|
+
if isinstance(self.service, Service):
|
91
|
+
return self.service
|
92
|
+
elif isinstance(self.service, Service.Config):
|
93
|
+
return Service.from_config(self.service)
|
94
|
+
else:
|
95
|
+
raise TypeError(self.service)
|
96
|
+
|
97
|
+
#
|
98
|
+
|
99
|
+
daemon: Daemon | Daemon.Config = Daemon.Config()
|
100
|
+
|
101
|
+
@cached.function
|
102
|
+
def daemon_(self) -> Daemon:
|
103
|
+
if isinstance(self.daemon, Daemon):
|
104
|
+
return self.daemon
|
105
|
+
elif isinstance(self.daemon, Daemon.Config):
|
106
|
+
return Daemon(Target.of(self.service_()), self.daemon)
|
107
|
+
else:
|
108
|
+
raise TypeError(self.daemon)
|
omlish/daemons/targets.py
CHANGED
@@ -6,14 +6,18 @@ import typing as ta
|
|
6
6
|
from .. import check
|
7
7
|
from .. import dataclasses as dc
|
8
8
|
from .. import lang
|
9
|
-
from .services import Service
|
10
9
|
|
11
10
|
|
12
11
|
if ta.TYPE_CHECKING:
|
13
12
|
import runpy
|
13
|
+
|
14
|
+
from . import services
|
15
|
+
|
14
16
|
else:
|
15
17
|
runpy = lang.proxy_import('runpy')
|
16
18
|
|
19
|
+
services = lang.proxy_import('.services', __package__)
|
20
|
+
|
17
21
|
|
18
22
|
##
|
19
23
|
|
@@ -30,8 +34,11 @@ class Target(dc.Case):
|
|
30
34
|
elif callable(obj):
|
31
35
|
return FnTarget(obj)
|
32
36
|
|
33
|
-
elif isinstance(obj, Service
|
34
|
-
return
|
37
|
+
elif isinstance(obj, services.Service):
|
38
|
+
return services.ServiceTarget(obj)
|
39
|
+
|
40
|
+
elif isinstance(obj, services.Service.Config):
|
41
|
+
return services.ServiceConfigTarget(obj)
|
35
42
|
|
36
43
|
else:
|
37
44
|
raise TypeError(obj)
|
@@ -136,22 +143,3 @@ class ExecTargetRunner(TargetRunner, dc.Frozen):
|
|
136
143
|
@target_runner_for.register
|
137
144
|
def _(target: ExecTarget) -> ExecTargetRunner:
|
138
145
|
return ExecTargetRunner(target)
|
139
|
-
|
140
|
-
|
141
|
-
##
|
142
|
-
|
143
|
-
|
144
|
-
class ServiceConfigTarget(Target):
|
145
|
-
cfg: Service.Config
|
146
|
-
|
147
|
-
|
148
|
-
class ServiceConfigTargetRunner(TargetRunner, dc.Frozen):
|
149
|
-
target: ServiceConfigTarget
|
150
|
-
|
151
|
-
def run(self) -> None:
|
152
|
-
Service.run_config(self.target.cfg)
|
153
|
-
|
154
|
-
|
155
|
-
@target_runner_for.register
|
156
|
-
def _(target: ServiceConfigTarget) -> ServiceConfigTargetRunner:
|
157
|
-
return ServiceConfigTargetRunner(target)
|
omlish/dataclasses/__init__.py
CHANGED
@@ -91,9 +91,11 @@ from .impl.reflect import ( # noqa
|
|
91
91
|
reflect,
|
92
92
|
)
|
93
93
|
|
94
|
-
from .
|
95
|
-
|
94
|
+
from .static import ( # noqa
|
95
|
+
Static,
|
96
|
+
)
|
96
97
|
|
98
|
+
from .utils import ( # noqa
|
97
99
|
opt_repr,
|
98
100
|
truthy_repr,
|
99
101
|
|
@@ -116,5 +118,7 @@ from .utils import ( # noqa
|
|
116
118
|
##
|
117
119
|
|
118
120
|
from ..lite.dataclasses import ( # noqa
|
121
|
+
is_immediate_dataclass,
|
122
|
+
|
119
123
|
dataclass_maybe_post_init as maybe_post_init,
|
120
124
|
)
|
@@ -0,0 +1,176 @@
|
|
1
|
+
import abc
|
2
|
+
import copy
|
3
|
+
import dataclasses as dc
|
4
|
+
import typing as ta
|
5
|
+
|
6
|
+
from .. import lang
|
7
|
+
from ..lite.dataclasses import is_immediate_dataclass
|
8
|
+
from .impl.api import dataclass
|
9
|
+
|
10
|
+
|
11
|
+
##
|
12
|
+
|
13
|
+
|
14
|
+
class Static(lang.Abstract):
|
15
|
+
__static_dataclass_class__: ta.ClassVar[type]
|
16
|
+
__static_dataclass_instance__: ta.ClassVar[ta.Any]
|
17
|
+
|
18
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
19
|
+
super().__init_subclass__(**kwargs)
|
20
|
+
|
21
|
+
for a in ('__new__', '__init__'):
|
22
|
+
if a in cls.__dict__ and not (cls.__dict__[a] == getattr(Static, a)):
|
23
|
+
raise TypeError(f'Static base class {cls} must not implement {a}')
|
24
|
+
|
25
|
+
if cls.__init__ is not Static.__init__:
|
26
|
+
# This is necessary to make type-checking work (by allowing it to accept zero). This isn't strictly
|
27
|
+
# necessary, but since it's useful to do sometimes it might as well be done everywhere to prevent clashing.
|
28
|
+
raise TypeError(f'Static.__init__ should be first in mro of {cls}')
|
29
|
+
|
30
|
+
#
|
31
|
+
|
32
|
+
b_dc_lst = []
|
33
|
+
|
34
|
+
def b_dc_rec(b_cls: type) -> None: # noqa
|
35
|
+
if issubclass(b_cls, Static):
|
36
|
+
return
|
37
|
+
elif is_immediate_dataclass(b_cls):
|
38
|
+
b_dc_lst.append(b_cls)
|
39
|
+
elif dc.is_dataclass(b_cls):
|
40
|
+
for sb_cls in b_cls.__bases__:
|
41
|
+
b_dc_rec(sb_cls)
|
42
|
+
|
43
|
+
for b_cls in cls.__bases__:
|
44
|
+
b_dc_rec(b_cls)
|
45
|
+
|
46
|
+
b_sdc_dc_lst = [
|
47
|
+
b_cls.__static_dataclass_class__
|
48
|
+
for b_cls in cls.__bases__
|
49
|
+
if issubclass(b_cls, Static)
|
50
|
+
and b_cls is not Static
|
51
|
+
]
|
52
|
+
|
53
|
+
sdc_dc_set = {*b_dc_lst, *b_sdc_dc_lst}
|
54
|
+
if not sdc_dc_set:
|
55
|
+
raise TypeError(f'Static dataclass {cls} inherits from no dataclass')
|
56
|
+
|
57
|
+
# Base static dataclasses of various types are allowed as long as there is exactly one final subclass involved.
|
58
|
+
# For example, a ModAttrManifest dataclass with an abstract StaticModAttrManifest subclass which sets a default
|
59
|
+
# mod_name may be mixed in with a further down use-case specific Manifest subclass.
|
60
|
+
sdc_dc_set -= {
|
61
|
+
m_cls
|
62
|
+
for s_cls in sdc_dc_set
|
63
|
+
for m_cls in s_cls.__mro__
|
64
|
+
if m_cls is not s_cls
|
65
|
+
}
|
66
|
+
|
67
|
+
if len(sdc_dc_set) > 1:
|
68
|
+
raise TypeError(f'Static dataclass {cls} inherits from multiple dataclasses: {sdc_dc_set!r}')
|
69
|
+
[sdc_cls] = sdc_dc_set
|
70
|
+
|
71
|
+
if '__static_dataclass_class__' in cls.__dict__:
|
72
|
+
raise AttributeError
|
73
|
+
setattr(cls, '__static_dataclass_class__', sdc_cls)
|
74
|
+
|
75
|
+
#
|
76
|
+
|
77
|
+
expected_fld_order: ta.Sequence[str] | None = None
|
78
|
+
is_abstract = lang.is_abstract_class(cls) or abc.ABC in cls.__bases__
|
79
|
+
if not is_abstract:
|
80
|
+
if is_immediate_dataclass(cls):
|
81
|
+
raise TypeError(cls)
|
82
|
+
|
83
|
+
flds_dct = {}
|
84
|
+
for b_cls in cls.__mro__:
|
85
|
+
if not dc.is_dataclass(b_cls):
|
86
|
+
continue
|
87
|
+
b_flds = dc.fields(b_cls) # noqa
|
88
|
+
for fld in b_flds:
|
89
|
+
if fld.name in flds_dct:
|
90
|
+
continue
|
91
|
+
flds_dct[fld.name] = fld
|
92
|
+
|
93
|
+
# Annotations are the authoritative source of field order.
|
94
|
+
new_anns = {}
|
95
|
+
|
96
|
+
for fld in flds_dct.values():
|
97
|
+
new_fld = copy.copy(fld)
|
98
|
+
|
99
|
+
try:
|
100
|
+
v = cls.__dict__[fld.name]
|
101
|
+
except KeyError:
|
102
|
+
if fld.default is dc.MISSING and fld.default_factory is dc.MISSING:
|
103
|
+
raise TypeError(f'Field {fld.name!r} of class {cls} is not set and has no default') from None
|
104
|
+
else:
|
105
|
+
if isinstance(v, dc.Field):
|
106
|
+
raise TypeError(f'Static dataclass {cls} may not introduce new fields: {fld.name}: {v}')
|
107
|
+
|
108
|
+
new_fld.default = dc.MISSING
|
109
|
+
# Use a default_factory to allow unsafe (mutable) values.
|
110
|
+
new_fld.default_factory = (lambda v2: lambda: v2)(v) # noqa
|
111
|
+
|
112
|
+
setattr(cls, fld.name, new_fld)
|
113
|
+
new_anns[fld.name] = fld.type
|
114
|
+
|
115
|
+
# Non-abstract static dataclasses may not introduce new fields.
|
116
|
+
expected_fld_order = list(flds_dct)
|
117
|
+
|
118
|
+
new_anns.update({
|
119
|
+
k: v
|
120
|
+
for k, v in getattr(cls, '__annotations__', {}).items()
|
121
|
+
if k not in new_anns
|
122
|
+
})
|
123
|
+
|
124
|
+
cls.__annotations__ = new_anns
|
125
|
+
|
126
|
+
else:
|
127
|
+
for b_cls in cls.__bases__:
|
128
|
+
if hasattr(b_cls, '__static_dataclass_instance__'):
|
129
|
+
raise TypeError(
|
130
|
+
f'Abstract static dataclass {cls} may not inherit from non-abstract static dataclass {b_cls}',
|
131
|
+
)
|
132
|
+
|
133
|
+
# Explicitly forbid dc transforms that rebuild the class, such as slots.
|
134
|
+
if (dc_cls := dataclass(cls, frozen=True)) is not cls:
|
135
|
+
raise TypeError(dc_cls)
|
136
|
+
|
137
|
+
dc_flds = dc.fields(cls) # type: ignore[arg-type] # noqa
|
138
|
+
|
139
|
+
if expected_fld_order is not None:
|
140
|
+
dc_fld_order = [f.name for f in dc_flds]
|
141
|
+
if dc_fld_order != expected_fld_order:
|
142
|
+
raise TypeError(
|
143
|
+
f'Static dataclass field order {dc_fld_order!r} != expected field order {expected_fld_order!r}',
|
144
|
+
)
|
145
|
+
|
146
|
+
if not is_abstract:
|
147
|
+
# This is the only time the Statices are ever actually instantiated, and it's only to produce the
|
148
|
+
# kwargs passed to the underlying dataclass.
|
149
|
+
tmp_inst = cls()
|
150
|
+
inst_kw = dc.asdict(tmp_inst) # type: ignore[call-overload] # noqa
|
151
|
+
inst = sdc_cls(**inst_kw)
|
152
|
+
|
153
|
+
cls.__static_dataclass_instance__ = inst
|
154
|
+
|
155
|
+
# Make all field values available via static `Class.field` access, even those created via a factory. Note
|
156
|
+
# that further inheritance of this non-abstract Static will continue to inherit dc.Field instances
|
157
|
+
# (including things like their metadata) via dc.fields() access, which does not reference `cls.__dict__`.
|
158
|
+
for fld in dc_flds:
|
159
|
+
v = getattr(inst, fld.name)
|
160
|
+
setattr(cls, fld.name, v)
|
161
|
+
|
162
|
+
def __new__(new_cls, *new_args, **new_kwargs): # noqa
|
163
|
+
try:
|
164
|
+
return new_cls.__dict__['__static_dataclass_instance__']
|
165
|
+
except KeyError:
|
166
|
+
return super().__new__(new_cls)
|
167
|
+
|
168
|
+
cls.__new__ = __new__ # type: ignore
|
169
|
+
|
170
|
+
cls.__init__ = Static.__init__ # type: ignore
|
171
|
+
|
172
|
+
@ta.final
|
173
|
+
def __init__(self) -> None:
|
174
|
+
# This stub serves to allow `StaticSubclass()` to typecheck by allowing it to accept only zero arguments. Note
|
175
|
+
# that this is only the case when `Static` is first in mro.
|
176
|
+
raise TypeError('May not instantiate static dataclasses')
|
omlish/dataclasses/utils.py
CHANGED
@@ -4,7 +4,6 @@ import types
|
|
4
4
|
import typing as ta
|
5
5
|
|
6
6
|
from .. import check
|
7
|
-
from .impl.internals import FIELDS_ATTR
|
8
7
|
from .impl.metadata import METADATA_ATTR
|
9
8
|
from .impl.metadata import UserMetadata
|
10
9
|
from .impl.params import DEFAULT_FIELD_EXTRAS
|
@@ -18,14 +17,6 @@ T = ta.TypeVar('T')
|
|
18
17
|
##
|
19
18
|
|
20
19
|
|
21
|
-
def is_immediate_dataclass(cls: type) -> bool:
|
22
|
-
check.isinstance(cls, type)
|
23
|
-
return FIELDS_ATTR in cls.__dict__
|
24
|
-
|
25
|
-
|
26
|
-
##
|
27
|
-
|
28
|
-
|
29
20
|
def opt_repr(o: ta.Any) -> str | None:
|
30
21
|
return repr(o) if o is not None else None
|
31
22
|
|
omlish/graphs/trees.py
CHANGED
@@ -56,7 +56,7 @@ class BasicTreeAnalysis(ta.Generic[NodeT]):
|
|
56
56
|
|
57
57
|
self._set_fac: ta.Callable[..., ta.MutableSet[NodeT]] = col.IdentitySet if identity else set
|
58
58
|
self._dict_fac: ta.Callable[..., ta.MutableMapping[NodeT, ta.Any]] = col.IdentityKeyDict if identity else dict
|
59
|
-
self.
|
59
|
+
self._rank_seq_fac: ta.Callable[..., col.RankedSeq[NodeT]] = functools.partial(col.RankedSeq, identity=identity) # type: ignore # noqa
|
60
60
|
|
61
61
|
def walk(cur: NodeT, parent: NodeT | None) -> None:
|
62
62
|
check.not_none(cur)
|
@@ -88,10 +88,10 @@ class BasicTreeAnalysis(ta.Generic[NodeT]):
|
|
88
88
|
|
89
89
|
walk(root, None)
|
90
90
|
|
91
|
-
self._nodes = self.
|
91
|
+
self._nodes = self._rank_seq_fac(nodes)
|
92
92
|
self._node_set: ta.AbstractSet[NodeT] = node_set
|
93
|
-
self._children_by_node: ta.Mapping[NodeT | None, col.
|
94
|
-
[(n, self.
|
93
|
+
self._children_by_node: ta.Mapping[NodeT | None, col.RankedSeq[NodeT]] = self._dict_fac(
|
94
|
+
[(n, self._rank_seq_fac(cs)) for n, cs in children_by_node.items()])
|
95
95
|
self._child_sets_by_node: ta.Mapping[NodeT | None, ta.AbstractSet[NodeT]] = child_sets_by_node
|
96
96
|
self._parents_by_node: ta.Mapping[NodeT, NodeT | None] = parents_by_node
|
97
97
|
|
@@ -100,7 +100,7 @@ class BasicTreeAnalysis(ta.Generic[NodeT]):
|
|
100
100
|
return self._root
|
101
101
|
|
102
102
|
@property
|
103
|
-
def nodes(self) -> col.
|
103
|
+
def nodes(self) -> col.RankedSeq[NodeT]:
|
104
104
|
return self._nodes
|
105
105
|
|
106
106
|
@property
|
@@ -116,7 +116,7 @@ class BasicTreeAnalysis(ta.Generic[NodeT]):
|
|
116
116
|
return self._node_set
|
117
117
|
|
118
118
|
@property
|
119
|
-
def children_by_node(self) -> ta.Mapping[NodeT | None, col.
|
119
|
+
def children_by_node(self) -> ta.Mapping[NodeT | None, col.RankedSeq[NodeT]]:
|
120
120
|
return self._children_by_node
|
121
121
|
|
122
122
|
@property
|
@@ -224,8 +224,8 @@ class BasicTreeAnalysis(ta.Generic[NodeT]):
|
|
224
224
|
break
|
225
225
|
yield cur
|
226
226
|
|
227
|
-
def get_lineage(self, node: NodeT) -> col.
|
228
|
-
return self.
|
227
|
+
def get_lineage(self, node: NodeT) -> col.RankedSeq[NodeT]:
|
228
|
+
return self._rank_seq_fac(reversed([node, *self.iter_ancestors(node)]))
|
229
229
|
|
230
230
|
def get_first_parent_of_type(self, node: NodeT, ty: type[T]) -> T | None:
|
231
231
|
for cur in self.iter_ancestors(node):
|
omlish/lang/descriptors.py
CHANGED
@@ -48,25 +48,26 @@ def _has_method_descriptor(obj: ta.Any) -> bool:
|
|
48
48
|
return False
|
49
49
|
|
50
50
|
|
51
|
-
def unwrap_method_descriptors(fn: ta.
|
51
|
+
def unwrap_method_descriptors(fn: ta.Any) -> ta.Any:
|
52
52
|
while is_method_descriptor(fn):
|
53
|
-
fn = fn.__func__
|
53
|
+
fn = fn.__func__
|
54
54
|
return fn
|
55
55
|
|
56
56
|
|
57
57
|
##
|
58
58
|
|
59
59
|
|
60
|
-
def unwrap_func_with_partials(fn: ta.
|
60
|
+
def unwrap_func_with_partials(fn: ta.Any) -> tuple[ta.Any, list[functools.partial]]:
|
61
61
|
ps = []
|
62
62
|
while True:
|
63
63
|
if is_method_descriptor(fn) or isinstance(fn, types.MethodType):
|
64
|
-
fn = fn.__func__
|
64
|
+
fn = fn.__func__
|
65
65
|
|
66
66
|
elif hasattr(fn, '__wrapped__'):
|
67
67
|
nxt = fn.__wrapped__
|
68
|
-
|
69
|
-
|
68
|
+
# FIXME: ?
|
69
|
+
# if not callable(nxt):
|
70
|
+
# raise TypeError(nxt)
|
70
71
|
if nxt is fn:
|
71
72
|
raise TypeError(fn)
|
72
73
|
fn = nxt
|
@@ -83,7 +84,7 @@ def unwrap_func_with_partials(fn: ta.Callable) -> tuple[ta.Callable, list[functo
|
|
83
84
|
return fn, ps
|
84
85
|
|
85
86
|
|
86
|
-
def unwrap_func(fn: ta.
|
87
|
+
def unwrap_func(fn: ta.Any) -> ta.Any:
|
87
88
|
uw, _ = unwrap_func_with_partials(fn)
|
88
89
|
return uw
|
89
90
|
|
omlish/lite/dataclasses.py
CHANGED
@@ -3,6 +3,18 @@ import dataclasses as dc
|
|
3
3
|
import typing as ta
|
4
4
|
|
5
5
|
|
6
|
+
##
|
7
|
+
|
8
|
+
|
9
|
+
def is_immediate_dataclass(cls: type) -> bool:
|
10
|
+
if not isinstance(cls, type):
|
11
|
+
raise TypeError(cls)
|
12
|
+
return dc._FIELDS in cls.__dict__ # type: ignore[attr-defined] # noqa
|
13
|
+
|
14
|
+
|
15
|
+
##
|
16
|
+
|
17
|
+
|
6
18
|
def dataclass_cache_hash(
|
7
19
|
*,
|
8
20
|
cached_hash_attr: str = '__dataclass_hash__',
|
@@ -33,6 +45,9 @@ def dataclass_cache_hash(
|
|
33
45
|
return inner
|
34
46
|
|
35
47
|
|
48
|
+
##
|
49
|
+
|
50
|
+
|
36
51
|
def dataclass_maybe_post_init(sup: ta.Any) -> bool:
|
37
52
|
if not isinstance(sup, super):
|
38
53
|
raise TypeError(sup)
|
@@ -44,6 +59,9 @@ def dataclass_maybe_post_init(sup: ta.Any) -> bool:
|
|
44
59
|
return True
|
45
60
|
|
46
61
|
|
62
|
+
##
|
63
|
+
|
64
|
+
|
47
65
|
def dataclass_repr_filtered(
|
48
66
|
obj: ta.Any,
|
49
67
|
fn: ta.Callable[[ta.Any, dc.Field, ta.Any], bool],
|
omlish/manifests/__init__.py
CHANGED
omlish/manifests/base.py
CHANGED
omlish/manifests/load.py
CHANGED
@@ -0,0 +1,20 @@
|
|
1
|
+
import abc
|
2
|
+
import typing as ta
|
3
|
+
|
4
|
+
from .. import dataclasses as dc
|
5
|
+
from .. import lang
|
6
|
+
from .base import ModAttrManifest
|
7
|
+
|
8
|
+
|
9
|
+
##
|
10
|
+
|
11
|
+
|
12
|
+
class StaticModAttrManifest(dc.Static, ModAttrManifest, abc.ABC):
|
13
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
14
|
+
if (
|
15
|
+
not (lang.is_abstract_class(cls) or abc.ABC in cls.__bases__) and
|
16
|
+
'mod_name' not in cls.__dict__
|
17
|
+
):
|
18
|
+
setattr(cls, 'mod_name', cls.__module__)
|
19
|
+
|
20
|
+
super().__init_subclass__(**kwargs)
|
omlish/manifests/types.py
CHANGED
omlish/metadata.py
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
"""
|
2
|
+
These attempt to punch through *all* wrappers. The the usecases involve having things like bound methods, classmethods,
|
3
|
+
functools.partial instances, and needing to get the metadata of the underlying thing the end-user wrote.
|
4
|
+
|
5
|
+
TODO:
|
6
|
+
- re type targets:
|
7
|
+
- unwrap instances of objects to their types?
|
8
|
+
- merge mro?
|
9
|
+
- are these better left up to callers? too usecase-specific to favor either way?
|
10
|
+
"""
|
11
|
+
import types
|
12
|
+
import typing as ta
|
13
|
+
|
14
|
+
from . import check
|
15
|
+
from . import lang
|
16
|
+
|
17
|
+
|
18
|
+
T = ta.TypeVar('T')
|
19
|
+
|
20
|
+
|
21
|
+
##
|
22
|
+
|
23
|
+
|
24
|
+
class ObjectMetadata(lang.Abstract):
|
25
|
+
pass
|
26
|
+
|
27
|
+
|
28
|
+
class ObjectMetadataTarget(lang.Abstract):
|
29
|
+
pass
|
30
|
+
|
31
|
+
|
32
|
+
##
|
33
|
+
|
34
|
+
|
35
|
+
_VALID_OBJECT_METADATA_TARGET_TYPES: tuple[type, ...] = (
|
36
|
+
type,
|
37
|
+
|
38
|
+
types.FunctionType,
|
39
|
+
# *not* types.MethodType - must unwrap to unbound class func
|
40
|
+
# *not* functools.partial - must unwrap to underlying func
|
41
|
+
|
42
|
+
ObjectMetadataTarget,
|
43
|
+
)
|
44
|
+
|
45
|
+
|
46
|
+
class ObjectMetadataTargetTypeError(TypeError):
|
47
|
+
pass
|
48
|
+
|
49
|
+
|
50
|
+
def _unwrap_object_metadata_target(obj: ta.Any) -> ta.Any:
|
51
|
+
tgt: ta.Any = obj
|
52
|
+
tgt = lang.unwrap_func(tgt)
|
53
|
+
|
54
|
+
if not isinstance(tgt, _VALID_OBJECT_METADATA_TARGET_TYPES):
|
55
|
+
raise ObjectMetadataTargetTypeError(tgt)
|
56
|
+
|
57
|
+
return tgt
|
58
|
+
|
59
|
+
|
60
|
+
##
|
61
|
+
|
62
|
+
|
63
|
+
_OBJECT_METADATA_ATTR = '__' + __name__.replace('.', '_') + '__metadata__'
|
64
|
+
|
65
|
+
|
66
|
+
def append_object_metadata(obj: T, *mds: ObjectMetadata) -> T:
|
67
|
+
for md in mds:
|
68
|
+
check.isinstance(md, ObjectMetadata)
|
69
|
+
|
70
|
+
tgt = _unwrap_object_metadata_target(obj)
|
71
|
+
dct = tgt.__dict__
|
72
|
+
|
73
|
+
if isinstance(dct, types.MappingProxyType):
|
74
|
+
for _ in range(2):
|
75
|
+
try:
|
76
|
+
lst = dct[_OBJECT_METADATA_ATTR]
|
77
|
+
except KeyError:
|
78
|
+
setattr(tgt, _OBJECT_METADATA_ATTR, [])
|
79
|
+
else:
|
80
|
+
break
|
81
|
+
else:
|
82
|
+
raise RuntimeError
|
83
|
+
|
84
|
+
else:
|
85
|
+
lst = dct.setdefault(_OBJECT_METADATA_ATTR, [])
|
86
|
+
|
87
|
+
lst.extend(mds)
|
88
|
+
return obj
|
89
|
+
|
90
|
+
|
91
|
+
def get_object_metadata(obj: ta.Any, *, strict: bool = False) -> ta.Sequence[ObjectMetadata]:
|
92
|
+
try:
|
93
|
+
tgt = _unwrap_object_metadata_target(obj)
|
94
|
+
except ObjectMetadataTargetTypeError:
|
95
|
+
if not strict:
|
96
|
+
return ()
|
97
|
+
raise
|
98
|
+
|
99
|
+
try:
|
100
|
+
dct = tgt.__dict__
|
101
|
+
except AttributeError:
|
102
|
+
return ()
|
103
|
+
|
104
|
+
return dct.get(_OBJECT_METADATA_ATTR, ())
|
105
|
+
|
106
|
+
|
107
|
+
##
|
108
|
+
|
109
|
+
|
110
|
+
class DecoratorObjectMetadata(ObjectMetadata, lang.Abstract):
|
111
|
+
_OBJECT_METADATA_TARGET_TYPES: ta.ClassVar[tuple[type, ...] | None] = None
|
112
|
+
|
113
|
+
def __init_subclass__(
|
114
|
+
cls,
|
115
|
+
*,
|
116
|
+
object_metadata_target_types: ta.Iterable[type] | None = None,
|
117
|
+
**kwargs: ta.Any,
|
118
|
+
) -> None:
|
119
|
+
super().__init_subclass__(**kwargs)
|
120
|
+
|
121
|
+
if object_metadata_target_types is not None:
|
122
|
+
tts = tuple(object_metadata_target_types)
|
123
|
+
for tt in tts:
|
124
|
+
check.issubclass(tt, _VALID_OBJECT_METADATA_TARGET_TYPES)
|
125
|
+
setattr(cls, '_OBJECT_METADATA_TARGET_TYPES', tts)
|
126
|
+
|
127
|
+
def __call__(self, obj: T) -> T:
|
128
|
+
tgt: ta.Any = obj
|
129
|
+
if (tts := type(self)._OBJECT_METADATA_TARGET_TYPES) is not None: # noqa
|
130
|
+
tgt = _unwrap_object_metadata_target(tgt)
|
131
|
+
check.isinstance(tgt, tts)
|
132
|
+
|
133
|
+
append_object_metadata(tgt, self)
|
134
|
+
return obj
|
135
|
+
|
136
|
+
|
137
|
+
#
|
138
|
+
|
139
|
+
|
140
|
+
class ClassDecoratorObjectMetadata(
|
141
|
+
DecoratorObjectMetadata,
|
142
|
+
lang.Abstract,
|
143
|
+
object_metadata_target_types=[type],
|
144
|
+
):
|
145
|
+
pass
|
146
|
+
|
147
|
+
|
148
|
+
class FunctionDecoratorObjectMetadata(
|
149
|
+
DecoratorObjectMetadata,
|
150
|
+
lang.Abstract,
|
151
|
+
object_metadata_target_types=[types.FunctionType],
|
152
|
+
):
|
153
|
+
pass
|
@@ -1,5 +1,5 @@
|
|
1
1
|
omlish/.manifests.json,sha256=vQTAIvR8OblSq-uP2GUfnbei0RnmAnM5j0T1-OToh9E,8253
|
2
|
-
omlish/__about__.py,sha256
|
2
|
+
omlish/__about__.py,sha256=-JHhiSxa1m9XW-39IA3EWqH7y7ZB6GG03v7p93wDeA0,3380
|
3
3
|
omlish/__init__.py,sha256=SsyiITTuK0v74XpKV8dqNaCmjOlan1JZKrHQv5rWKPA,253
|
4
4
|
omlish/c3.py,sha256=ubu7lHwss5V4UznbejAI0qXhXahrU01MysuHOZI9C4U,8116
|
5
5
|
omlish/cached.py,sha256=UI-XTFBwA6YXWJJJeBn-WkwBkfzDjLBBaZf4nIJA9y0,510
|
@@ -8,6 +8,7 @@ omlish/datetimes.py,sha256=HajeM1kBvwlTa-uR1TTZHmZ3zTPnnUr1uGGQhiO1XQ0,2152
|
|
8
8
|
omlish/defs.py,sha256=9uUjJuVIbCBL3g14fyzAp-9gH935MFofvlfOGwcBIaM,4913
|
9
9
|
omlish/dynamic.py,sha256=kIZokHHid8a0pIAPXMNiXrVJvJJyBnY49WP1a2m-HUQ,6525
|
10
10
|
omlish/libc.py,sha256=8K4c66YV1ziJerl5poAAYCmsV-VSsHkT3EHhPW04ufg,15639
|
11
|
+
omlish/metadata.py,sha256=IJFczp-bkFk_lCYTUt5UmM_MvCbKICjJunEi2MqnC1w,3495
|
11
12
|
omlish/outcome.py,sha256=ABIE0zjjTyTNtn-ZqQ_9_mUzLiBQ3sDAyqc9JVD8N2k,7852
|
12
13
|
omlish/runmodule.py,sha256=PWvuAaJ9wQQn6bx9ftEL3_d04DyotNn8dR_twm2pgw0,700
|
13
14
|
omlish/shlex.py,sha256=bsW2XUD8GiMTUTDefJejZ5AyqT1pTgWMPD0BMoF02jE,248
|
@@ -134,16 +135,16 @@ omlish/codecs/funcs.py,sha256=p4imNt7TobyZVXWC-WhntHVu9KfJrO4QwdtPRh-cVOk,850
|
|
134
135
|
omlish/codecs/registry.py,sha256=2FnO5YP7ui1LzkguwESY0MP3WIdwgPTIJTM_4RyTOEg,3896
|
135
136
|
omlish/codecs/standard.py,sha256=eiZ4u9ep0XrA4Z_D1zJI0vmWyuN8HLrX4Se_r_Cq_ZM,60
|
136
137
|
omlish/codecs/text.py,sha256=JzrdwMpQPo2NBBg3K1EZszzQy5vEWmd82SIerJd4yeQ,5723
|
137
|
-
omlish/collections/__init__.py,sha256=
|
138
|
+
omlish/collections/__init__.py,sha256=umpclbTE3wsSflMrKmYEiGmWFhiukfJxvBWgtYMZ8mk,2161
|
138
139
|
omlish/collections/abc.py,sha256=sP7BpTVhx6s6C59mTFeosBi4rHOWC6tbFBYbxdZmvh0,2365
|
139
140
|
omlish/collections/coerce.py,sha256=g68ROb_-5HgH-vI8612mU2S0FZ8-wp2ZHK5_Zy_kVC0,7037
|
140
141
|
omlish/collections/exceptions.py,sha256=shcS-NCnEUudF8qC_SmO2TQyjivKlS4TDjaz_faqQ0c,44
|
141
142
|
omlish/collections/frozen.py,sha256=mxhd8pw5zIXUiRHiBVZWYCYT7wYDVG3tAY5PNU-WH-I,4150
|
142
143
|
omlish/collections/hasheq.py,sha256=XcOCE6f2lXizDCOXxSX6vJv-rLcpDo2OWCYIKGSWuic,3697
|
143
144
|
omlish/collections/identity.py,sha256=SwnUE5D3v9RBjATDE1LUr_vO3Rb2NHcmTK64GZIcsO8,2720
|
144
|
-
omlish/collections/indexed.py,sha256=tLa88qgWGzTZGssMFgvhgraIEkNEUvcIk5p4yjNEquQ,2201
|
145
145
|
omlish/collections/mappings.py,sha256=YunNPyADrpitZGTJcXV0k4bmJddj1avDvEavz0coJWU,3203
|
146
146
|
omlish/collections/ordered.py,sha256=tUAl99XHbSbzn7Hdh99jUBl27NcC2J7ZTI67slTMe5M,2333
|
147
|
+
omlish/collections/ranked.py,sha256=rg6DL36oOUiG5JQEAkGnT8b6f9mSndQlIovtt8GQj_w,2229
|
147
148
|
omlish/collections/unmodifiable.py,sha256=-zys__n6L7liWzhmHAIvfxprq7cUE5HoR3foGvXc_7I,4748
|
148
149
|
omlish/collections/utils.py,sha256=Q0lHhNDokVxdOvApmu1QX5fABYwbn1ATiIwp194Ur_E,2889
|
149
150
|
omlish/collections/cache/__init__.py,sha256=D1gO71VcwxFTZP9gAc9isHfg_TEdalwhsJcgGLvS9hg,233
|
@@ -163,7 +164,7 @@ omlish/concurrent/futures.py,sha256=J2s9wYURUskqRJiBbAR0PNEAp1pXbIMYldOVBTQduQY,
|
|
163
164
|
omlish/concurrent/threadlets.py,sha256=JfirbTDJgy9Ouokz_VmHeAAPS7cih8qMUJrN-owwXD4,2423
|
164
165
|
omlish/configs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
165
166
|
omlish/configs/all.py,sha256=kziwjzUBkf8AT0w7Pq7JX2jtkQVOQ5R1wJyn6hfTN5k,1055
|
166
|
-
omlish/configs/classes.py,sha256=
|
167
|
+
omlish/configs/classes.py,sha256=9uelgi9gS5Sf8ZbtydNXhMxjBs7xBxGZgfuK0Vbc2cg,1168
|
167
168
|
omlish/configs/formats.py,sha256=RJw4Rzp7vlTd5YyAvpAoruQnk45v8dGPtPWwqH7aYyE,5301
|
168
169
|
omlish/configs/nginx.py,sha256=XuX9yyb0_MwkJ8esKiMS9gFkqHUPza_uCprhnWykNy8,2051
|
169
170
|
omlish/configs/shadow.py,sha256=-R5nbevC4pFgqDPYOCCIqNcusgXsMWlRIUOEG0HBoJg,2228
|
@@ -181,12 +182,13 @@ omlish/daemons/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
181
182
|
omlish/daemons/daemon.py,sha256=ykdbCPbpxKrdZVZc892SnedTdftTAYt_YdDpYchKcUE,3410
|
182
183
|
omlish/daemons/launching.py,sha256=mhtkuAO16STcznUl3rrX9pacfrKbPQRCP2AllKL4B70,3664
|
183
184
|
omlish/daemons/reparent.py,sha256=UaG2X6VJHJPOlUwHPNRH3aWGgF0Fg771jjO9IRPLlyY,280
|
184
|
-
omlish/daemons/services.py,sha256=
|
185
|
+
omlish/daemons/services.py,sha256=UAzzdP4jG0-piVzz6CsSTPIjTGt4VFXtbzP7KczMCho,2354
|
185
186
|
omlish/daemons/spawning.py,sha256=cx00xeqSrfhlFbjCtKqaBHvMuHwB9hdjuKNHzAAo_dw,4030
|
186
|
-
omlish/daemons/targets.py,sha256=
|
187
|
+
omlish/daemons/targets.py,sha256=00KmtlknMhQ5PyyVAhWl3rpeTMPym0GxvHHq6mYPZ7c,3051
|
187
188
|
omlish/daemons/waiting.py,sha256=RfgD1L33QQVbD2431dkKZGE4w6DUcGvYeRXXi8puAP4,1676
|
188
|
-
omlish/dataclasses/__init__.py,sha256=
|
189
|
-
omlish/dataclasses/
|
189
|
+
omlish/dataclasses/__init__.py,sha256=b7EZCIfHnEHCHWwgD3YXxkdsU-uYd9iD4hM36RgpI1g,1598
|
190
|
+
omlish/dataclasses/static.py,sha256=35tPuneXUXZztFTzjNkW6DnQbZgKJUf68GmjzXV-WkQ,6903
|
191
|
+
omlish/dataclasses/utils.py,sha256=QWsHxVisS-MUCqh89JQsRdCLgdBVeC6EYK6jRwO9akU,3631
|
190
192
|
omlish/dataclasses/impl/LICENSE,sha256=Oy-B_iHRgcSZxZolbI4ZaEVdZonSaaqFNzv7avQdo78,13936
|
191
193
|
omlish/dataclasses/impl/__init__.py,sha256=zqGBC5gSbjJxaqG_zS1LL1PX-zAfhIua8UqOE4IwO2k,789
|
192
194
|
omlish/dataclasses/impl/api.py,sha256=RhU4f50GVdn-dxilia8NA3F7VIm2R5z78pFfpIVXPRQ,6635
|
@@ -302,7 +304,7 @@ omlish/funcs/pipes.py,sha256=E7Sz8Aj8ke_vCs5AMNwg1I36kRdHVGTnzxVQaDyn43U,2490
|
|
302
304
|
omlish/graphs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
303
305
|
omlish/graphs/dags.py,sha256=zp55lYgUdRCxmADwiGDHeehMJczZFA_tzdWqy77icOk,3047
|
304
306
|
omlish/graphs/domination.py,sha256=oCGoWzWTxLwow0LDyGjjEf2AjFiOiDz4WaBtczwSbsQ,7576
|
305
|
-
omlish/graphs/trees.py,sha256=
|
307
|
+
omlish/graphs/trees.py,sha256=OgGDnCJKfpOeWgDJPSkuVEoeYp72o06jpm0jwUB96Xc,8189
|
306
308
|
omlish/graphs/dot/__init__.py,sha256=Y1MZRQBZkcYyG1Tn7K2FhL8aYbm4v4tk6f5g9AqEkUw,359
|
307
309
|
omlish/graphs/dot/items.py,sha256=OWPf0-hjBgS1uyy2QgAEn4IgFHJcEg7sHVWeTx1ghZc,4083
|
308
310
|
omlish/graphs/dot/make.py,sha256=RN30gHfJPiXx5Q51kbDdhVJYf59Fr84Lz9J-mXRt9sI,360
|
@@ -401,7 +403,7 @@ omlish/lang/clsdct.py,sha256=sJYadm-fwzti-gsi98knR5qQUxriBmOqQE_qz3RopNk,1743
|
|
401
403
|
omlish/lang/cmp.py,sha256=5vbzWWbqdzDmNKAGL19z6ZfUKe5Ci49e-Oegf9f4BsE,1346
|
402
404
|
omlish/lang/contextmanagers.py,sha256=Mrn8NJ3pP0Zxi-IoGqSjZDdWUctsyee2vrZ2FtZvNmo,10529
|
403
405
|
omlish/lang/datetimes.py,sha256=ehI_DhQRM-bDxAavnp470XcekbbXc4Gdw9y1KpHDJT0,223
|
404
|
-
omlish/lang/descriptors.py,sha256=
|
406
|
+
omlish/lang/descriptors.py,sha256=mZ2h9zJ__MMpw8hByjRbAiONcwfVb6GD0btNnVi8C5w,6573
|
405
407
|
omlish/lang/exceptions.py,sha256=qJBo3NU1mOWWm-NhQUHCY5feYXR3arZVyEHinLsmRH4,47
|
406
408
|
omlish/lang/functions.py,sha256=0ql9EXA_gEEhvUVzMJCjVhEnVtHecsLKmfmAXuQqeGY,4388
|
407
409
|
omlish/lang/generators.py,sha256=5LX17j-Ej3QXhwBgZvRTm_dq3n9veC4IOUcVmvSu2vU,5243
|
@@ -432,7 +434,7 @@ omlish/lite/cached.py,sha256=O7ozcoDNFm1Hg2wtpHEqYSp_i_nCLNOP6Ueq_Uk-7mU,1300
|
|
432
434
|
omlish/lite/check.py,sha256=OLwtE2x6nlbGx4vS3Rda7zMHpgqzDSLJminTAX2lqLA,13529
|
433
435
|
omlish/lite/configs.py,sha256=Ev_19sbII67pTWzInYjYqa9VyTiZBvyjhZqyG8TtufE,908
|
434
436
|
omlish/lite/contextmanagers.py,sha256=ciaMl0D3QDHToM7M28-kwZ-Q48LtwgCxiud3nekgutA,2863
|
435
|
-
omlish/lite/dataclasses.py,sha256=
|
437
|
+
omlish/lite/dataclasses.py,sha256=t1G5-xOuvE6o6w9RyqHzLT9wHD0HkqBh5P8HUZWxGzs,1912
|
436
438
|
omlish/lite/inject.py,sha256=qBUftFeXMiRgANYbNS2e7TePMYyFAcuLgsJiLyMTW5o,28769
|
437
439
|
omlish/lite/json.py,sha256=7-02Ny4fq-6YAu5ynvqoijhuYXWpLmfCI19GUeZnb1c,740
|
438
440
|
omlish/lite/logs.py,sha256=CWFG0NKGhqNeEgryF5atN2gkPYbUdTINEw_s1phbINM,51
|
@@ -463,10 +465,11 @@ omlish/logs/proxy.py,sha256=A-ROPUUAlF397qTbEqhel6YhQMstNuXL3Xmts7w9dAo,2347
|
|
463
465
|
omlish/logs/standard.py,sha256=FbKdF2Z4Na5i2TNwKn0avLJXyICe2JKsPufjvKCHGn0,3162
|
464
466
|
omlish/logs/timing.py,sha256=XrFUHIPT4EHDujLKbGs9fGFMmoM3NEP8xPRaESJr7bQ,1513
|
465
467
|
omlish/logs/utils.py,sha256=mzHrZ9ji75p5A8qR29eUr05CBAHMb8J753MSkID_VaQ,393
|
466
|
-
omlish/manifests/__init__.py,sha256=
|
467
|
-
omlish/manifests/base.py,sha256=
|
468
|
-
omlish/manifests/load.py,sha256=
|
469
|
-
omlish/manifests/
|
468
|
+
omlish/manifests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
469
|
+
omlish/manifests/base.py,sha256=D1WvJYcBR_njkc0gpALpFCWh1h3agb9qgqphnbbPlm4,935
|
470
|
+
omlish/manifests/load.py,sha256=9mdsS3egmSX9pymO-m-y2Fhs4p6ruOdbsYaKT1-1Hwg,6655
|
471
|
+
omlish/manifests/static.py,sha256=7YwOVh_Ek9_aTrWsWNO8kWS10_j4K7yv3TpXZSHsvDY,501
|
472
|
+
omlish/manifests/types.py,sha256=mxL6N44mu06995YF8qzmg6V94EmfRiryzIUQcXVSCbI,275
|
470
473
|
omlish/marshal/__init__.py,sha256=00D3S6qwUld1TUWd67hVHuNcrj3c_FAFSkCVXgGWT-s,2607
|
471
474
|
omlish/marshal/base.py,sha256=tJ4iNuD7cW2GpGMznOhkAf2hugqp2pF2em0FaQcekrk,6740
|
472
475
|
omlish/marshal/exceptions.py,sha256=jwQWn4LcPnadT2KRI_1JJCOSkwWh0yHnYK9BmSkNN4U,302
|
@@ -711,9 +714,9 @@ omlish/text/indent.py,sha256=YjtJEBYWuk8--b9JU_T6q4yxV85_TR7VEVr5ViRCFwk,1336
|
|
711
714
|
omlish/text/minja.py,sha256=jZC-fp3Xuhx48ppqsf2Sf1pHbC0t8XBB7UpUUoOk2Qw,5751
|
712
715
|
omlish/text/parts.py,sha256=JkNZpyR2tv2CNcTaWJJhpQ9E4F0yPR8P_YfDbZfMtwQ,6182
|
713
716
|
omlish/text/random.py,sha256=jNWpqiaKjKyTdMXC-pWAsSC10AAP-cmRRPVhm59ZWLk,194
|
714
|
-
omlish-0.0.0.
|
715
|
-
omlish-0.0.0.
|
716
|
-
omlish-0.0.0.
|
717
|
-
omlish-0.0.0.
|
718
|
-
omlish-0.0.0.
|
719
|
-
omlish-0.0.0.
|
717
|
+
omlish-0.0.0.dev238.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
|
718
|
+
omlish-0.0.0.dev238.dist-info/METADATA,sha256=Fzwpe0Ssmu0yFpCL5MUoWK0b3rF72OFhaG7rCK_duxU,4176
|
719
|
+
omlish-0.0.0.dev238.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
720
|
+
omlish-0.0.0.dev238.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
|
721
|
+
omlish-0.0.0.dev238.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
|
722
|
+
omlish-0.0.0.dev238.dist-info/RECORD,,
|
omlish/collections/indexed.py
DELETED
@@ -1,73 +0,0 @@
|
|
1
|
-
import typing as ta
|
2
|
-
|
3
|
-
from .identity import IdentityKeyDict
|
4
|
-
from .identity import IdentitySet
|
5
|
-
|
6
|
-
|
7
|
-
T = ta.TypeVar('T')
|
8
|
-
|
9
|
-
|
10
|
-
class IndexedSeq(ta.Sequence[T]):
|
11
|
-
def __init__(self, it: ta.Iterable[T], *, identity: bool = False) -> None:
|
12
|
-
super().__init__()
|
13
|
-
|
14
|
-
self._lst = list(it)
|
15
|
-
self._idxs: ta.Mapping[T, int] = (IdentityKeyDict if identity else dict)((e, i) for i, e in enumerate(self._lst)) # noqa
|
16
|
-
if len(self._idxs) != len(self._lst):
|
17
|
-
raise ValueError(f'{len(self._idxs)} != {len(self._lst)}')
|
18
|
-
|
19
|
-
@property
|
20
|
-
def debug(self) -> ta.Sequence[T]:
|
21
|
-
return self._lst
|
22
|
-
|
23
|
-
def __iter__(self) -> ta.Iterator[T]:
|
24
|
-
return iter(self._lst)
|
25
|
-
|
26
|
-
def __getitem__(self, idx: int) -> T: # type: ignore
|
27
|
-
return self._lst[idx]
|
28
|
-
|
29
|
-
def __len__(self) -> int:
|
30
|
-
return len(self._lst)
|
31
|
-
|
32
|
-
def __contains__(self, obj: T) -> bool: # type: ignore
|
33
|
-
return obj in self._idxs
|
34
|
-
|
35
|
-
@property
|
36
|
-
def idxs(self) -> ta.Mapping[T, int]:
|
37
|
-
return self._idxs
|
38
|
-
|
39
|
-
def idx(self, obj: T) -> int:
|
40
|
-
return self._idxs[obj]
|
41
|
-
|
42
|
-
|
43
|
-
class IndexedSetSeq(ta.Sequence[ta.AbstractSet[T]]):
|
44
|
-
def __init__(self, it: ta.Iterable[ta.Iterable[T]], *, identity: bool = False) -> None:
|
45
|
-
super().__init__()
|
46
|
-
|
47
|
-
self._lst = [(IdentitySet if identity else set)(e) for e in it]
|
48
|
-
self._idxs: ta.Mapping[T, int] = (IdentityKeyDict if identity else dict)((e, i) for i, es in enumerate(self._lst) for e in es) # noqa
|
49
|
-
if len(self._idxs) != sum(map(len, self._lst)):
|
50
|
-
raise ValueError(f'{len(self._idxs)} != {sum(map(len, self._lst))}')
|
51
|
-
|
52
|
-
@property
|
53
|
-
def debug(self) -> ta.Sequence[ta.AbstractSet[T]]:
|
54
|
-
return self._lst
|
55
|
-
|
56
|
-
def __iter__(self) -> ta.Iterator[ta.AbstractSet[T]]:
|
57
|
-
return iter(self._lst)
|
58
|
-
|
59
|
-
def __getitem__(self, idx: int) -> ta.AbstractSet[T]: # type: ignore
|
60
|
-
return self._lst[idx]
|
61
|
-
|
62
|
-
def __len__(self) -> int:
|
63
|
-
return len(self._lst)
|
64
|
-
|
65
|
-
def __contains__(self, obj: T) -> bool: # type: ignore
|
66
|
-
return obj in self._idxs
|
67
|
-
|
68
|
-
@property
|
69
|
-
def idxs(self) -> ta.Mapping[T, int]:
|
70
|
-
return self._idxs
|
71
|
-
|
72
|
-
def idx(self, obj: T) -> int:
|
73
|
-
return self._idxs[obj]
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|