omlish 0.0.0.dev237__py3-none-any.whl → 0.0.0.dev238__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
omlish/__about__.py CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev237'
2
- __revision__ = 'e8c58292f1ec88727fd832f6496545435d992e9d'
1
+ __version__ = '0.0.0.dev238'
2
+ __revision__ = '0ab60b1d75e5b0b298707ba55797958e528c69b5'
3
3
 
4
4
 
5
5
  #
@@ -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
@@ -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.Config):
34
- return ServiceConfigTarget(obj)
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)
@@ -91,9 +91,11 @@ from .impl.reflect import ( # noqa
91
91
  reflect,
92
92
  )
93
93
 
94
- from .utils import ( # noqa
95
- is_immediate_dataclass,
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')
@@ -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._idx_seq_fac: ta.Callable[..., col.IndexedSeq[NodeT]] = functools.partial(col.IndexedSeq, identity=identity) # type: ignore # noqa
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._idx_seq_fac(nodes)
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.IndexedSeq[NodeT]] = self._dict_fac(
94
- [(n, self._idx_seq_fac(cs)) for n, cs in children_by_node.items()])
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.IndexedSeq[NodeT]:
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.IndexedSeq[NodeT]]:
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.IndexedSeq[NodeT]:
228
- return self._idx_seq_fac(reversed([node, *self.iter_ancestors(node)]))
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):
@@ -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.Callable) -> ta.Callable:
51
+ def unwrap_method_descriptors(fn: ta.Any) -> ta.Any:
52
52
  while is_method_descriptor(fn):
53
- fn = fn.__func__ # type: ignore # noqa
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.Callable) -> tuple[ta.Callable, list[functools.partial]]:
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__ # type: ignore
64
+ fn = fn.__func__
65
65
 
66
66
  elif hasattr(fn, '__wrapped__'):
67
67
  nxt = fn.__wrapped__
68
- if not callable(nxt):
69
- raise TypeError(nxt)
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.Callable) -> ta.Callable:
87
+ def unwrap_func(fn: ta.Any) -> ta.Any:
87
88
  uw, _ = unwrap_func_with_partials(fn)
88
89
  return uw
89
90
 
@@ -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],
@@ -1,2 +0,0 @@
1
- # @omlish-lite
2
- from .types import Manifest # noqa
omlish/manifests/base.py CHANGED
@@ -1,4 +1,5 @@
1
1
  # ruff: noqa: UP006 UP007
2
+ # @omlish-lite
2
3
  import dataclasses as dc
3
4
  import typing as ta
4
5
 
omlish/manifests/load.py CHANGED
@@ -1,4 +1,5 @@
1
1
  # ruff: noqa: UP006 UP007
2
+ # @omlish-lite
2
3
  """
3
4
  Should be kept somewhat lightweight - used in cli entrypoints.
4
5
 
@@ -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
@@ -1,4 +1,5 @@
1
1
  # ruff: noqa: UP006 UP007
2
+ # @omlish-lite
2
3
  import dataclasses as dc
3
4
  import typing as ta
4
5
 
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,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: omlish
3
- Version: 0.0.0.dev237
3
+ Version: 0.0.0.dev238
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -1,5 +1,5 @@
1
1
  omlish/.manifests.json,sha256=vQTAIvR8OblSq-uP2GUfnbei0RnmAnM5j0T1-OToh9E,8253
2
- omlish/__about__.py,sha256=0tS5CFbCYIp6LwAIjSNK3lhW-DW7VWodegiD0bAa7U8,3380
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=ddzZS3C2rvDo65hspO9KlFsrinElxPWvYEgVyBUZdb0,2164
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=oCCkGzokn807mAlpRVe9F_JKZo8x6JcIVmo2sBsm7T8,1080
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=XRmYBYNLUumnxaXCr2B7ScNEUlM8oM5FSS0bdWheDNs,719
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=L2BRWsv473X7dE4H7afA8TgTiaevCuZPab9Olh-AKUc,3248
187
+ omlish/daemons/targets.py,sha256=00KmtlknMhQ5PyyVAhWl3rpeTMPym0GxvHHq6mYPZ7c,3051
187
188
  omlish/daemons/waiting.py,sha256=RfgD1L33QQVbD2431dkKZGE4w6DUcGvYeRXXi8puAP4,1676
188
- omlish/dataclasses/__init__.py,sha256=D7I6ZJjEFeLN2re8oOZ_7JKWiG2plrPnaUFq3iEXYmQ,1553
189
- omlish/dataclasses/utils.py,sha256=N2seT8cJtfOv-41D7F3E-q4us-FCTQmnxxPv3dt1OcI,3796
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=zvfjDN8R7J6wStNJEUO6OCioGXJrLGhU8KtcqlWVRPI,8191
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=njkYDS1gn5p4-3v1jr-s_srauC7tvvt571RjE7Q4LXE,6616
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=sjJnZ4tZXMzKRkr-gymMbhuMxJAY8parJ_sopEqBc6M,1704
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=P2B0dpT8D7l5lJwRGPA92IcQj6oeXfd90X5-q9BJrKg,51
467
- omlish/manifests/base.py,sha256=1LGglMh7UUU7JWIqPH_JfIm6czGETibpScEqKI5mSMg,920
468
- omlish/manifests/load.py,sha256=0ZjtsYL2EIKYd_MF3brKTPYdRZmtWdwomx2cFjjFV2M,6640
469
- omlish/manifests/types.py,sha256=d8bv5tknCJqclRfxCpao_8XxHo2yofhLpVHQTB-MfNw,260
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.dev237.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
715
- omlish-0.0.0.dev237.dist-info/METADATA,sha256=xNNscWsZ6ZOLDE0vOj3YCOrR50thYq82_KawDOui9OA,4176
716
- omlish-0.0.0.dev237.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
717
- omlish-0.0.0.dev237.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
718
- omlish-0.0.0.dev237.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
719
- omlish-0.0.0.dev237.dist-info/RECORD,,
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,,
@@ -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]