omlish 0.0.0.dev237__py3-none-any.whl → 0.0.0.dev239__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 +189 -0
- omlish/dataclasses/utils.py +0 -9
- omlish/graphs/trees.py +8 -8
- omlish/lang/__init__.py +6 -3
- omlish/lang/descriptors.py +8 -7
- omlish/lang/imports.py +0 -43
- omlish/lite/dataclasses.py +18 -0
- omlish/lite/imports.py +47 -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 +2 -1
- omlish/metadata.py +153 -0
- omlish/sql/abc.py +7 -0
- omlish/sql/api/__init__.py +0 -0
- omlish/sql/api/base.py +89 -0
- omlish/sql/api/columns.py +90 -0
- omlish/sql/api/dbapi.py +105 -0
- omlish/sql/api/errors.py +24 -0
- omlish/sql/api/funcs.py +73 -0
- omlish/sql/api/queries.py +63 -0
- omlish/sql/api/rows.py +48 -0
- {omlish-0.0.0.dev237.dist-info → omlish-0.0.0.dev239.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev237.dist-info → omlish-0.0.0.dev239.dist-info}/RECORD +36 -24
- omlish/collections/indexed.py +0 -73
- {omlish-0.0.0.dev237.dist-info → omlish-0.0.0.dev239.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev237.dist-info → omlish-0.0.0.dev239.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev237.dist-info → omlish-0.0.0.dev239.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev237.dist-info → omlish-0.0.0.dev239.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,189 @@
|
|
1
|
+
"""
|
2
|
+
TODO:
|
3
|
+
- metaclass, to forbid __subclasscheck__ / __instancecheck__? Clash with dc.Meta, don't want that lock-in, not
|
4
|
+
necessary for functionality, just a helpful misuse prevention.
|
5
|
+
"""
|
6
|
+
import abc
|
7
|
+
import copy
|
8
|
+
import dataclasses as dc
|
9
|
+
import typing as ta
|
10
|
+
|
11
|
+
from .. import lang
|
12
|
+
from ..lite.dataclasses import is_immediate_dataclass
|
13
|
+
from .impl.api import dataclass
|
14
|
+
|
15
|
+
|
16
|
+
##
|
17
|
+
|
18
|
+
|
19
|
+
class Static(lang.Abstract):
|
20
|
+
"""
|
21
|
+
Dataclass mixin for dataclasses in which all fields have class-level defaults and are not intended for any
|
22
|
+
instance-level overrides - effectively making their subclasses equivalent to instances. For dataclasses which make
|
23
|
+
sense as singletons (such as project specs or manifests), for which dynamic instantiation is not the usecase, this
|
24
|
+
can enable more natural syntax. Inheritance is permitted - equivalent to a `functools.partial` or composing
|
25
|
+
`**kwargs`, as long as there is only ever one unambiguous underlying non-static dataclass to be instantiated.
|
26
|
+
"""
|
27
|
+
|
28
|
+
__static_dataclass_class__: ta.ClassVar[type]
|
29
|
+
__static_dataclass_instance__: ta.ClassVar[ta.Any]
|
30
|
+
|
31
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
32
|
+
super().__init_subclass__(**kwargs)
|
33
|
+
|
34
|
+
for a in ('__new__', '__init__'):
|
35
|
+
if a in cls.__dict__ and not (cls.__dict__[a] == getattr(Static, a)):
|
36
|
+
raise TypeError(f'Static base class {cls} must not implement {a}')
|
37
|
+
|
38
|
+
if cls.__init__ is not Static.__init__:
|
39
|
+
# This is necessary to make type-checking work (by allowing it to accept zero args). This isn't strictly
|
40
|
+
# necessary, but since it's useful to do sometimes it might as well be done everywhere to prevent clashing.
|
41
|
+
raise TypeError(f'Static.__init__ should be first in mro of {cls}')
|
42
|
+
|
43
|
+
#
|
44
|
+
|
45
|
+
b_dc_lst = []
|
46
|
+
|
47
|
+
def b_dc_rec(b_cls: type) -> None: # noqa
|
48
|
+
if issubclass(b_cls, Static):
|
49
|
+
return
|
50
|
+
elif is_immediate_dataclass(b_cls):
|
51
|
+
b_dc_lst.append(b_cls)
|
52
|
+
elif dc.is_dataclass(b_cls):
|
53
|
+
for sb_cls in b_cls.__bases__:
|
54
|
+
b_dc_rec(sb_cls)
|
55
|
+
|
56
|
+
for b_cls in cls.__bases__:
|
57
|
+
b_dc_rec(b_cls)
|
58
|
+
|
59
|
+
b_sdc_dc_lst = [
|
60
|
+
b_cls.__static_dataclass_class__
|
61
|
+
for b_cls in cls.__bases__
|
62
|
+
if issubclass(b_cls, Static)
|
63
|
+
and b_cls is not Static
|
64
|
+
]
|
65
|
+
|
66
|
+
sdc_dc_set = {*b_dc_lst, *b_sdc_dc_lst}
|
67
|
+
if not sdc_dc_set:
|
68
|
+
raise TypeError(f'Static dataclass {cls} inherits from no dataclass')
|
69
|
+
|
70
|
+
# Base static dataclasses of various types are allowed as long as there is exactly one final subclass involved.
|
71
|
+
# For example, a ModAttrManifest dataclass with an abstract StaticModAttrManifest subclass which sets a default
|
72
|
+
# mod_name may be mixed in with a further down use-case specific Manifest subclass.
|
73
|
+
sdc_dc_set -= {
|
74
|
+
m_cls
|
75
|
+
for s_cls in sdc_dc_set
|
76
|
+
for m_cls in s_cls.__mro__
|
77
|
+
if m_cls is not s_cls
|
78
|
+
}
|
79
|
+
|
80
|
+
if len(sdc_dc_set) > 1:
|
81
|
+
raise TypeError(f'Static dataclass {cls} inherits from multiple dataclasses: {sdc_dc_set!r}')
|
82
|
+
[sdc_cls] = sdc_dc_set
|
83
|
+
|
84
|
+
if '__static_dataclass_class__' in cls.__dict__:
|
85
|
+
raise AttributeError
|
86
|
+
setattr(cls, '__static_dataclass_class__', sdc_cls)
|
87
|
+
|
88
|
+
#
|
89
|
+
|
90
|
+
expected_fld_order: ta.Sequence[str] | None = None
|
91
|
+
is_abstract = lang.is_abstract_class(cls) or abc.ABC in cls.__bases__
|
92
|
+
if not is_abstract:
|
93
|
+
if is_immediate_dataclass(cls):
|
94
|
+
raise TypeError(cls)
|
95
|
+
|
96
|
+
flds_dct = {}
|
97
|
+
for b_cls in cls.__mro__:
|
98
|
+
if not dc.is_dataclass(b_cls):
|
99
|
+
continue
|
100
|
+
b_flds = dc.fields(b_cls) # noqa
|
101
|
+
for fld in b_flds:
|
102
|
+
if fld.name in flds_dct:
|
103
|
+
continue
|
104
|
+
flds_dct[fld.name] = fld
|
105
|
+
|
106
|
+
# Annotations are the authoritative source of field order.
|
107
|
+
new_anns = {}
|
108
|
+
|
109
|
+
for fld in flds_dct.values():
|
110
|
+
new_fld = copy.copy(fld)
|
111
|
+
|
112
|
+
try:
|
113
|
+
v = cls.__dict__[fld.name]
|
114
|
+
except KeyError:
|
115
|
+
if fld.default is dc.MISSING and fld.default_factory is dc.MISSING:
|
116
|
+
raise TypeError(f'Field {fld.name!r} of class {cls} is not set and has no default') from None
|
117
|
+
else:
|
118
|
+
if isinstance(v, dc.Field):
|
119
|
+
raise TypeError(f'Static dataclass {cls} may not introduce new fields: {fld.name}: {v}')
|
120
|
+
|
121
|
+
new_fld.default = dc.MISSING
|
122
|
+
# Use a default_factory to allow unsafe (mutable) values.
|
123
|
+
new_fld.default_factory = (lambda v2: lambda: v2)(v) # noqa
|
124
|
+
|
125
|
+
setattr(cls, fld.name, new_fld)
|
126
|
+
new_anns[fld.name] = fld.type
|
127
|
+
|
128
|
+
# Non-abstract static dataclasses may not introduce new fields.
|
129
|
+
expected_fld_order = list(flds_dct)
|
130
|
+
|
131
|
+
new_anns.update({
|
132
|
+
k: v
|
133
|
+
for k, v in getattr(cls, '__annotations__', {}).items()
|
134
|
+
if k not in new_anns
|
135
|
+
})
|
136
|
+
|
137
|
+
cls.__annotations__ = new_anns
|
138
|
+
|
139
|
+
else:
|
140
|
+
for b_cls in cls.__bases__:
|
141
|
+
if hasattr(b_cls, '__static_dataclass_instance__'):
|
142
|
+
raise TypeError(
|
143
|
+
f'Abstract static dataclass {cls} may not inherit from non-abstract static dataclass {b_cls}',
|
144
|
+
)
|
145
|
+
|
146
|
+
# Explicitly forbid dc transforms that rebuild the class, such as slots.
|
147
|
+
if (dc_cls := dataclass(cls, frozen=True)) is not cls:
|
148
|
+
raise TypeError(dc_cls)
|
149
|
+
|
150
|
+
dc_flds = dc.fields(cls) # type: ignore[arg-type] # noqa
|
151
|
+
|
152
|
+
if expected_fld_order is not None:
|
153
|
+
dc_fld_order = [f.name for f in dc_flds]
|
154
|
+
if dc_fld_order != expected_fld_order:
|
155
|
+
raise TypeError(
|
156
|
+
f'Static dataclass field order {dc_fld_order!r} != expected field order {expected_fld_order!r}',
|
157
|
+
)
|
158
|
+
|
159
|
+
if not is_abstract:
|
160
|
+
# This is the only time the Statics are ever actually instantiated, and it's only to produce the kwargs
|
161
|
+
# passed to the underlying dataclass.
|
162
|
+
tmp_inst = cls()
|
163
|
+
inst_kw = dc.asdict(tmp_inst) # type: ignore[call-overload] # noqa
|
164
|
+
inst = sdc_cls(**inst_kw)
|
165
|
+
|
166
|
+
cls.__static_dataclass_instance__ = inst
|
167
|
+
|
168
|
+
# Make all field values available via static `Class.field` access, even those created via a factory. Note
|
169
|
+
# that further inheritance of this non-abstract Static will continue to inherit dc.Field instances
|
170
|
+
# (including things like their metadata) via dc.fields() access, which does not reference `cls.__dict__`.
|
171
|
+
for fld in dc_flds:
|
172
|
+
v = getattr(inst, fld.name)
|
173
|
+
setattr(cls, fld.name, v)
|
174
|
+
|
175
|
+
def __new__(new_cls, *new_args, **new_kwargs): # noqa
|
176
|
+
try:
|
177
|
+
return new_cls.__dict__['__static_dataclass_instance__']
|
178
|
+
except KeyError:
|
179
|
+
return super().__new__(new_cls)
|
180
|
+
|
181
|
+
cls.__new__ = __new__ # type: ignore
|
182
|
+
|
183
|
+
cls.__init__ = Static.__init__ # type: ignore
|
184
|
+
|
185
|
+
@ta.final
|
186
|
+
def __init__(self) -> None:
|
187
|
+
# This stub serves to allow `StaticSubclass()` to typecheck by allowing it to accept only zero arguments. Note
|
188
|
+
# that this is only the case when `Static` is first in mro.
|
189
|
+
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/__init__.py
CHANGED
@@ -137,9 +137,6 @@ from .imports import ( # noqa
|
|
137
137
|
can_import,
|
138
138
|
get_real_module_name,
|
139
139
|
import_all,
|
140
|
-
import_attr,
|
141
|
-
import_module,
|
142
|
-
import_module_attr,
|
143
140
|
lazy_import,
|
144
141
|
proxy_import,
|
145
142
|
proxy_init,
|
@@ -235,6 +232,12 @@ from .typing import ( # noqa
|
|
235
232
|
|
236
233
|
##
|
237
234
|
|
235
|
+
from ..lite.imports import ( # noqa
|
236
|
+
import_attr,
|
237
|
+
import_module,
|
238
|
+
import_module_attr,
|
239
|
+
)
|
240
|
+
|
238
241
|
from ..lite.timeouts import ( # noqa
|
239
242
|
DeadlineTimeout,
|
240
243
|
InfiniteTimeout,
|
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/lang/imports.py
CHANGED
@@ -87,49 +87,6 @@ def proxy_import(
|
|
87
87
|
##
|
88
88
|
|
89
89
|
|
90
|
-
def import_module(dotted_path: str) -> types.ModuleType:
|
91
|
-
if not dotted_path:
|
92
|
-
raise ImportError(dotted_path)
|
93
|
-
mod = __import__(dotted_path, globals(), locals(), [])
|
94
|
-
for name in dotted_path.split('.')[1:]:
|
95
|
-
try:
|
96
|
-
mod = getattr(mod, name)
|
97
|
-
except AttributeError:
|
98
|
-
raise AttributeError(f'Module {mod!r} has no attribute {name!r}') from None
|
99
|
-
return mod
|
100
|
-
|
101
|
-
|
102
|
-
def import_module_attr(dotted_path: str) -> ta.Any:
|
103
|
-
module_name, _, class_name = dotted_path.rpartition('.')
|
104
|
-
mod = import_module(module_name)
|
105
|
-
try:
|
106
|
-
return getattr(mod, class_name)
|
107
|
-
except AttributeError:
|
108
|
-
raise AttributeError(f'Module {module_name!r} has no attr {class_name!r}') from None
|
109
|
-
|
110
|
-
|
111
|
-
def import_attr(dotted_path: str) -> ta.Any:
|
112
|
-
parts = dotted_path.split('.')
|
113
|
-
mod: ta.Any = None
|
114
|
-
mod_pos = 0
|
115
|
-
while mod_pos < len(parts):
|
116
|
-
mod_name = '.'.join(parts[:mod_pos + 1])
|
117
|
-
try:
|
118
|
-
mod = importlib.import_module(mod_name)
|
119
|
-
except ImportError:
|
120
|
-
break
|
121
|
-
mod_pos += 1
|
122
|
-
if mod is None:
|
123
|
-
raise ImportError(dotted_path)
|
124
|
-
obj = mod
|
125
|
-
for att_pos in range(mod_pos, len(parts)):
|
126
|
-
obj = getattr(obj, parts[att_pos])
|
127
|
-
return obj
|
128
|
-
|
129
|
-
|
130
|
-
##
|
131
|
-
|
132
|
-
|
133
90
|
SPECIAL_IMPORTABLE: ta.AbstractSet[str] = frozenset([
|
134
91
|
'__init__.py',
|
135
92
|
'__main__.py',
|