ominfra 0.0.0.dev172__py3-none-any.whl → 0.0.0.dev173__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
- }