ominfra 0.0.0.dev172__py3-none-any.whl → 0.0.0.dev174__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.
@@ -7,11 +7,16 @@ import typing as ta
7
7
  from omlish.lite.cached import cached_nullary
8
8
  from omlish.lite.check import check
9
9
 
10
- from .types import DeployApp
11
- from .types import DeployKey
10
+ from .tags import DeployApp
11
+ from .tags import DeployAppKey
12
+ from .tags import DeployKey
13
+ from .tags import KeyDeployTag # noqa
12
14
  from .types import DeployRev
13
15
 
14
16
 
17
+ KeyDeployTagT = ta.TypeVar('KeyDeployTagT', bound='KeyDeployTag')
18
+
19
+
15
20
  ##
16
21
 
17
22
 
@@ -23,6 +28,16 @@ def check_valid_deploy_spec_path(s: str) -> str:
23
28
  return s
24
29
 
25
30
 
31
+ class DeploySpecKeyed(ta.Generic[KeyDeployTagT]):
32
+ @cached_nullary
33
+ def _key_str(self) -> str:
34
+ return hashlib.sha256(repr(self).encode('utf-8')).hexdigest()[:8]
35
+
36
+ @abc.abstractmethod
37
+ def key(self) -> KeyDeployTagT:
38
+ raise NotImplementedError
39
+
40
+
26
41
  ##
27
42
 
28
43
 
@@ -68,7 +83,7 @@ class DeployVenvSpec:
68
83
 
69
84
 
70
85
  @dc.dataclass(frozen=True)
71
- class DeployConfFile:
86
+ class DeployAppConfFile:
72
87
  path: str
73
88
  body: str
74
89
 
@@ -80,7 +95,7 @@ class DeployConfFile:
80
95
 
81
96
 
82
97
  @dc.dataclass(frozen=True)
83
- class DeployConfLink(abc.ABC): # noqa
98
+ class DeployAppConfLink(abc.ABC): # noqa
84
99
  """
85
100
  May be either:
86
101
  - @conf(.ext)* - links a single file in root of app conf dir to conf/@conf/@dst(.ext)*
@@ -96,11 +111,11 @@ class DeployConfLink(abc.ABC): # noqa
96
111
  check.equal(self.src.count('/'), 1)
97
112
 
98
113
 
99
- class AppDeployConfLink(DeployConfLink):
114
+ class CurrentOnlyDeployAppConfLink(DeployAppConfLink):
100
115
  pass
101
116
 
102
117
 
103
- class TagDeployConfLink(DeployConfLink):
118
+ class AllActiveDeployAppConfLink(DeployAppConfLink):
104
119
  pass
105
120
 
106
121
 
@@ -108,10 +123,10 @@ class TagDeployConfLink(DeployConfLink):
108
123
 
109
124
 
110
125
  @dc.dataclass(frozen=True)
111
- class DeployConfSpec:
112
- files: ta.Optional[ta.Sequence[DeployConfFile]] = None
126
+ class DeployAppConfSpec:
127
+ files: ta.Optional[ta.Sequence[DeployAppConfFile]] = None
113
128
 
114
- links: ta.Optional[ta.Sequence[DeployConfLink]] = None
129
+ links: ta.Optional[ta.Sequence[DeployAppConfLink]] = None
115
130
 
116
131
  def __post_init__(self) -> None:
117
132
  if self.files:
@@ -125,15 +140,34 @@ class DeployConfSpec:
125
140
 
126
141
 
127
142
  @dc.dataclass(frozen=True)
128
- class DeploySpec:
143
+ class DeployAppSpec(DeploySpecKeyed[DeployAppKey]):
129
144
  app: DeployApp
130
145
 
131
146
  git: DeployGitSpec
132
147
 
133
148
  venv: ta.Optional[DeployVenvSpec] = None
134
149
 
135
- conf: ta.Optional[DeployConfSpec] = None
150
+ conf: ta.Optional[DeployAppConfSpec] = None
136
151
 
137
- @cached_nullary
152
+ # @ta.override
153
+ def key(self) -> DeployAppKey:
154
+ return DeployAppKey(self._key_str())
155
+
156
+
157
+ ##
158
+
159
+
160
+ @dc.dataclass(frozen=True)
161
+ class DeploySpec(DeploySpecKeyed[DeployKey]):
162
+ apps: ta.Sequence[DeployAppSpec]
163
+
164
+ def __post_init__(self) -> None:
165
+ seen: ta.Set[DeployApp] = set()
166
+ for a in self.apps:
167
+ if a.app in seen:
168
+ raise KeyError(a.app)
169
+ seen.add(a.app)
170
+
171
+ # @ta.override
138
172
  def key(self) -> DeployKey:
139
- return DeployKey(hashlib.sha256(repr(self).encode('utf-8')).hexdigest()[:8])
173
+ return DeployKey(self._key_str())
@@ -0,0 +1,225 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import abc
3
+ import dataclasses as dc
4
+ import typing as ta
5
+
6
+ from omlish.lite.check import check
7
+
8
+
9
+ ##
10
+
11
+
12
+ DEPLOY_TAG_SIGIL = '@'
13
+
14
+ DEPLOY_TAG_SEPARATOR = '--'
15
+
16
+ DEPLOY_TAG_DELIMITERS: ta.AbstractSet[str] = frozenset([
17
+ DEPLOY_TAG_SEPARATOR,
18
+ '.',
19
+ ])
20
+
21
+ DEPLOY_TAG_ILLEGAL_STRS: ta.AbstractSet[str] = frozenset([
22
+ DEPLOY_TAG_SIGIL,
23
+ *DEPLOY_TAG_DELIMITERS,
24
+ '/',
25
+ ])
26
+
27
+
28
+ ##
29
+
30
+
31
+ @dc.dataclass(frozen=True)
32
+ class DeployTag(abc.ABC): # noqa
33
+ s: str
34
+
35
+ def __post_init__(self) -> None:
36
+ check.not_in(abc.ABC, type(self).__bases__)
37
+ check.non_empty_str(self.s)
38
+ for ch in DEPLOY_TAG_ILLEGAL_STRS:
39
+ check.state(ch not in self.s)
40
+
41
+ #
42
+
43
+ tag_name: ta.ClassVar[str]
44
+ tag_kwarg: ta.ClassVar[str]
45
+
46
+ def __init_subclass__(cls, **kwargs: ta.Any) -> None:
47
+ super().__init_subclass__(**kwargs)
48
+
49
+ if abc.ABC in cls.__bases__:
50
+ return
51
+
52
+ for b in cls.__bases__:
53
+ if issubclass(b, DeployTag):
54
+ check.in_(abc.ABC, b.__bases__)
55
+
56
+ check.non_empty_str(tn := cls.tag_name)
57
+ check.equal(tn, tn.lower().strip())
58
+ check.not_in('_', tn)
59
+
60
+ check.state(not hasattr(cls, 'tag_kwarg'))
61
+ cls.tag_kwarg = tn.replace('-', '_')
62
+
63
+
64
+ ##
65
+
66
+
67
+ _DEPLOY_TAGS: ta.Set[ta.Type[DeployTag]] = set()
68
+ DEPLOY_TAGS: ta.AbstractSet[ta.Type[DeployTag]] = _DEPLOY_TAGS
69
+
70
+ _DEPLOY_TAGS_BY_NAME: ta.Dict[str, ta.Type[DeployTag]] = {}
71
+ DEPLOY_TAGS_BY_NAME: ta.Mapping[str, ta.Type[DeployTag]] = _DEPLOY_TAGS_BY_NAME
72
+
73
+ _DEPLOY_TAGS_BY_KWARG: ta.Dict[str, ta.Type[DeployTag]] = {}
74
+ DEPLOY_TAGS_BY_KWARG: ta.Mapping[str, ta.Type[DeployTag]] = _DEPLOY_TAGS_BY_KWARG
75
+
76
+
77
+ def _register_deploy_tag(cls):
78
+ check.not_in(cls.tag_name, _DEPLOY_TAGS_BY_NAME)
79
+ check.not_in(cls.tag_kwarg, _DEPLOY_TAGS_BY_KWARG)
80
+
81
+ _DEPLOY_TAGS.add(cls)
82
+ _DEPLOY_TAGS_BY_NAME[cls.tag_name] = cls
83
+ _DEPLOY_TAGS_BY_KWARG[cls.tag_kwarg] = cls
84
+
85
+ return cls
86
+
87
+
88
+ ##
89
+
90
+
91
+ @_register_deploy_tag
92
+ class DeployTime(DeployTag):
93
+ tag_name: ta.ClassVar[str] = 'time'
94
+
95
+
96
+ ##
97
+
98
+
99
+ class NameDeployTag(DeployTag, abc.ABC): # noqa
100
+ pass
101
+
102
+
103
+ @_register_deploy_tag
104
+ class DeployApp(NameDeployTag):
105
+ tag_name: ta.ClassVar[str] = 'app'
106
+
107
+
108
+ @_register_deploy_tag
109
+ class DeployConf(NameDeployTag):
110
+ tag_name: ta.ClassVar[str] = 'conf'
111
+
112
+
113
+ ##
114
+
115
+
116
+ class KeyDeployTag(DeployTag, abc.ABC): # noqa
117
+ pass
118
+
119
+
120
+ @_register_deploy_tag
121
+ class DeployKey(KeyDeployTag):
122
+ tag_name: ta.ClassVar[str] = 'deploy-key'
123
+
124
+
125
+ @_register_deploy_tag
126
+ class DeployAppKey(KeyDeployTag):
127
+ tag_name: ta.ClassVar[str] = 'app-key'
128
+
129
+
130
+ ##
131
+
132
+
133
+ class RevDeployTag(DeployTag, abc.ABC): # noqa
134
+ pass
135
+
136
+
137
+ @_register_deploy_tag
138
+ class DeployAppRev(RevDeployTag):
139
+ tag_name: ta.ClassVar[str] = 'app-rev'
140
+
141
+
142
+ ##
143
+
144
+
145
+ class DeployTagMap:
146
+ def __init__(
147
+ self,
148
+ *args: DeployTag,
149
+ **kwargs: str,
150
+ ) -> None:
151
+ super().__init__()
152
+
153
+ dct: ta.Dict[ta.Type[DeployTag], DeployTag] = {}
154
+
155
+ for a in args:
156
+ c = type(check.isinstance(a, DeployTag))
157
+ check.not_in(c, dct)
158
+ dct[c] = a
159
+
160
+ for k, v in kwargs.items():
161
+ c = DEPLOY_TAGS_BY_KWARG[k]
162
+ check.not_in(c, dct)
163
+ dct[c] = c(v)
164
+
165
+ self._dct = dct
166
+ self._tup = tuple(sorted((type(t).tag_kwarg, t.s) for t in dct.values()))
167
+
168
+ #
169
+
170
+ def add(self, *args: ta.Any, **kwargs: ta.Any) -> 'DeployTagMap':
171
+ return DeployTagMap(
172
+ *self,
173
+ *args,
174
+ **kwargs,
175
+ )
176
+
177
+ def remove(self, *tags_or_names: ta.Union[ta.Type[DeployTag], str]) -> 'DeployTagMap':
178
+ dcs = {
179
+ check.issubclass(a, DeployTag) if isinstance(a, type) else DEPLOY_TAGS_BY_NAME[a]
180
+ for a in tags_or_names
181
+ }
182
+
183
+ return DeployTagMap(*[
184
+ t
185
+ for t in self._dct.values()
186
+ if t not in dcs
187
+ ])
188
+
189
+ #
190
+
191
+ def __repr__(self) -> str:
192
+ return f'{self.__class__.__name__}({", ".join(f"{k}={v!r}" for k, v in self._tup)})'
193
+
194
+ def __hash__(self) -> int:
195
+ return hash(self._tup)
196
+
197
+ def __eq__(self, other: object) -> bool:
198
+ if isinstance(other, DeployTagMap):
199
+ return self._tup == other._tup
200
+ else:
201
+ return NotImplemented
202
+
203
+ #
204
+
205
+ def __len__(self) -> int:
206
+ return len(self._dct)
207
+
208
+ def __iter__(self) -> ta.Iterator[DeployTag]:
209
+ return iter(self._dct.values())
210
+
211
+ def __getitem__(self, key: ta.Union[ta.Type[DeployTag], str]) -> DeployTag:
212
+ if isinstance(key, str):
213
+ return self._dct[DEPLOY_TAGS_BY_NAME[key]]
214
+ elif isinstance(key, type):
215
+ return self._dct[key]
216
+ else:
217
+ raise TypeError(key)
218
+
219
+ def __contains__(self, key: ta.Union[ta.Type[DeployTag], str]) -> bool:
220
+ if isinstance(key, str):
221
+ return DEPLOY_TAGS_BY_NAME[key] in self._dct
222
+ elif isinstance(key, type):
223
+ return key in self._dct
224
+ else:
225
+ raise TypeError(key)
@@ -1,39 +1,9 @@
1
- import dataclasses as dc
2
1
  import typing as ta
3
2
 
4
- from omlish.lite.check import check
5
-
6
-
7
- DeployPathKind = ta.Literal['dir', 'file'] # ta.TypeAlias
8
- DeployPathPlaceholder = ta.Literal['app', 'tag', 'conf'] # ta.TypeAlias
9
-
10
3
 
11
4
  ##
12
5
 
13
6
 
14
7
  DeployHome = ta.NewType('DeployHome', str)
15
8
 
16
- DeployApp = ta.NewType('DeployApp', str)
17
- DeployTag = ta.NewType('DeployTag', str)
18
9
  DeployRev = ta.NewType('DeployRev', str)
19
- DeployKey = ta.NewType('DeployKey', str)
20
-
21
-
22
- ##
23
-
24
-
25
- @dc.dataclass(frozen=True)
26
- class DeployAppTag:
27
- app: DeployApp
28
- tag: DeployTag
29
-
30
- def __post_init__(self) -> None:
31
- for s in [self.app, self.tag]:
32
- check.non_empty_str(s)
33
- check.equal(s, s.strip())
34
-
35
- def placeholders(self) -> ta.Mapping[DeployPathPlaceholder, str]:
36
- return {
37
- 'app': self.app,
38
- 'tag': self.tag,
39
- }
@@ -1450,6 +1450,9 @@ log = logging.getLogger(__name__)
1450
1450
  # ../../../../../omlish/lite/reflect.py
1451
1451
 
1452
1452
 
1453
+ ##
1454
+
1455
+
1453
1456
  _GENERIC_ALIAS_TYPES = (
1454
1457
  ta._GenericAlias, # type: ignore # noqa
1455
1458
  *([ta._SpecialGenericAlias] if hasattr(ta, '_SpecialGenericAlias') else []), # noqa
@@ -1467,6 +1470,9 @@ is_union_alias = functools.partial(is_generic_alias, origin=ta.Union)
1467
1470
  is_callable_alias = functools.partial(is_generic_alias, origin=ta.Callable)
1468
1471
 
1469
1472
 
1473
+ ##
1474
+
1475
+
1470
1476
  def is_optional_alias(spec: ta.Any) -> bool:
1471
1477
  return (
1472
1478
  isinstance(spec, _GENERIC_ALIAS_TYPES) and # noqa
@@ -1481,6 +1487,9 @@ def get_optional_alias_arg(spec: ta.Any) -> ta.Any:
1481
1487
  return it
1482
1488
 
1483
1489
 
1490
+ ##
1491
+
1492
+
1484
1493
  def is_new_type(spec: ta.Any) -> bool:
1485
1494
  if isinstance(ta.NewType, type):
1486
1495
  return isinstance(spec, ta.NewType)
@@ -1493,6 +1502,26 @@ def get_new_type_supertype(spec: ta.Any) -> ta.Any:
1493
1502
  return spec.__supertype__
1494
1503
 
1495
1504
 
1505
+ ##
1506
+
1507
+
1508
+ def is_literal_type(spec: ta.Any) -> bool:
1509
+ if hasattr(ta, '_LiteralGenericAlias'):
1510
+ return isinstance(spec, ta._LiteralGenericAlias) # noqa
1511
+ else:
1512
+ return (
1513
+ isinstance(spec, ta._GenericAlias) and # type: ignore # noqa
1514
+ spec.__origin__ is ta.Literal
1515
+ )
1516
+
1517
+
1518
+ def get_literal_type_args(spec: ta.Any) -> ta.Iterable[ta.Any]:
1519
+ return spec.__args__
1520
+
1521
+
1522
+ ##
1523
+
1524
+
1496
1525
  def deep_subclasses(cls: ta.Type[T]) -> ta.Iterator[ta.Type[T]]:
1497
1526
  seen = set()
1498
1527
  todo = list(reversed(cls.__subclasses__()))
@@ -2600,6 +2629,18 @@ class OptionalObjMarshaler(ObjMarshaler):
2600
2629
  return self.item.unmarshal(o, ctx)
2601
2630
 
2602
2631
 
2632
+ @dc.dataclass(frozen=True)
2633
+ class LiteralObjMarshaler(ObjMarshaler):
2634
+ item: ObjMarshaler
2635
+ vs: frozenset
2636
+
2637
+ def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
2638
+ return self.item.marshal(check.in_(o, self.vs), ctx)
2639
+
2640
+ def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
2641
+ return check.in_(self.item.unmarshal(o, ctx), self.vs)
2642
+
2643
+
2603
2644
  @dc.dataclass(frozen=True)
2604
2645
  class MappingObjMarshaler(ObjMarshaler):
2605
2646
  ty: type
@@ -2811,6 +2852,11 @@ class ObjMarshalerManager:
2811
2852
  if is_new_type(ty):
2812
2853
  return rec(get_new_type_supertype(ty))
2813
2854
 
2855
+ if is_literal_type(ty):
2856
+ lvs = frozenset(get_literal_type_args(ty))
2857
+ lty = check.single(set(map(type, lvs)))
2858
+ return LiteralObjMarshaler(rec(lty), lvs)
2859
+
2814
2860
  if is_generic_alias(ty):
2815
2861
  try:
2816
2862
  mt = self._generic_mapping_types[ta.get_origin(ty)]