ominfra 0.0.0.dev177__py3-none-any.whl → 0.0.0.dev178__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,7 +5,7 @@ from omlish.lite.logs import log
5
5
 
6
6
  from ..commands.base import Command
7
7
  from ..commands.base import CommandExecutor
8
- from .deploy import DeployManager
8
+ from .driver import DeployDriverFactory
9
9
  from .specs import DeploySpec
10
10
 
11
11
 
@@ -23,11 +23,12 @@ class DeployCommand(Command['DeployCommand.Output']):
23
23
 
24
24
  @dc.dataclass(frozen=True)
25
25
  class DeployCommandExecutor(CommandExecutor[DeployCommand, DeployCommand.Output]):
26
- _deploy: DeployManager
26
+ _driver_factory: DeployDriverFactory
27
27
 
28
28
  async def execute(self, cmd: DeployCommand) -> DeployCommand.Output:
29
29
  log.info('Deploying! %r', cmd.spec)
30
30
 
31
- await self._deploy.run_deploy(cmd.spec)
31
+ with self._driver_factory(cmd.spec) as driver:
32
+ await driver.drive_deploy()
32
33
 
33
34
  return DeployCommand.Output()
@@ -1,18 +1,10 @@
1
1
  # ruff: noqa: UP006 UP007
2
2
  import datetime
3
- import os.path
4
3
  import typing as ta
5
4
 
6
- from omlish.lite.check import check
7
5
  from omlish.lite.typing import Func0
8
6
 
9
- from .apps import DeployAppManager
10
- from .paths.manager import DeployPathsManager
11
- from .specs import DeploySpec
12
- from .tags import DeployAppRev
13
- from .tags import DeployTagMap
14
7
  from .tags import DeployTime
15
- from .types import DeployHome
16
8
 
17
9
 
18
10
  DEPLOY_TAG_DATETIME_FMT = '%Y%m%dT%H%M%SZ'
@@ -25,16 +17,11 @@ class DeployManager:
25
17
  def __init__(
26
18
  self,
27
19
  *,
28
- apps: DeployAppManager,
29
- paths: DeployPathsManager,
30
20
 
31
21
  utc_clock: ta.Optional[DeployManagerUtcClock] = None,
32
22
  ):
33
23
  super().__init__()
34
24
 
35
- self._apps = apps
36
- self._paths = paths
37
-
38
25
  self._utc_clock = utc_clock
39
26
 
40
27
  def _utc_now(self) -> datetime.datetime:
@@ -43,42 +30,5 @@ class DeployManager:
43
30
  else:
44
31
  return datetime.datetime.now(tz=datetime.timezone.utc) # noqa
45
32
 
46
- def _make_deploy_time(self) -> DeployTime:
33
+ def make_deploy_time(self) -> DeployTime:
47
34
  return DeployTime(self._utc_now().strftime(DEPLOY_TAG_DATETIME_FMT))
48
-
49
- async def run_deploy(
50
- self,
51
- spec: DeploySpec,
52
- ) -> None:
53
- self._paths.validate_deploy_paths()
54
-
55
- #
56
-
57
- hs = check.non_empty_str(spec.home)
58
- hs = os.path.expanduser(hs)
59
- hs = os.path.realpath(hs)
60
- hs = os.path.abspath(hs)
61
-
62
- home = DeployHome(hs)
63
-
64
- #
65
-
66
- deploy_tags = DeployTagMap(
67
- self._make_deploy_time(),
68
- spec.key(),
69
- )
70
-
71
- #
72
-
73
- for app in spec.apps:
74
- app_tags = deploy_tags.add(
75
- app.app,
76
- app.key(),
77
- DeployAppRev(app.git.rev),
78
- )
79
-
80
- await self._apps.prepare_app(
81
- app,
82
- home,
83
- app_tags,
84
- )
@@ -0,0 +1,71 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import os.path
3
+ import typing as ta
4
+
5
+ from omlish.lite.check import check
6
+ from omlish.lite.typing import Func1
7
+
8
+ from .apps import DeployAppManager
9
+ from .deploy import DeployManager
10
+ from .paths.manager import DeployPathsManager
11
+ from .specs import DeploySpec
12
+ from .tags import DeployAppRev
13
+ from .tags import DeployTagMap
14
+ from .types import DeployHome
15
+
16
+
17
+ class DeployDriverFactory(Func1[DeploySpec, ta.ContextManager['DeployDriver']]):
18
+ pass
19
+
20
+
21
+ class DeployDriver:
22
+ def __init__(
23
+ self,
24
+ *,
25
+ spec: DeploySpec,
26
+
27
+ deploys: DeployManager,
28
+ paths: DeployPathsManager,
29
+ apps: DeployAppManager,
30
+ ) -> None:
31
+ super().__init__()
32
+
33
+ self._spec = spec
34
+
35
+ self._deploys = deploys
36
+ self._paths = paths
37
+ self._apps = apps
38
+
39
+ async def drive_deploy(self) -> None:
40
+ self._paths.validate_deploy_paths()
41
+
42
+ #
43
+
44
+ hs = check.non_empty_str(self._spec.home)
45
+ hs = os.path.expanduser(hs)
46
+ hs = os.path.realpath(hs)
47
+ hs = os.path.abspath(hs)
48
+
49
+ home = DeployHome(hs)
50
+
51
+ #
52
+
53
+ deploy_tags = DeployTagMap(
54
+ self._deploys.make_deploy_time(),
55
+ self._spec.key(),
56
+ )
57
+
58
+ #
59
+
60
+ for app in self._spec.apps:
61
+ app_tags = deploy_tags.add(
62
+ app.app,
63
+ app.key(),
64
+ DeployAppRev(app.git.rev),
65
+ )
66
+
67
+ await self._apps.prepare_app(
68
+ app,
69
+ home,
70
+ app_tags,
71
+ )
@@ -1,6 +1,9 @@
1
1
  # ruff: noqa: UP006 UP007
2
+ import contextlib
2
3
  import typing as ta
3
4
 
5
+ from omlish.lite.inject import ContextvarInjectorScope
6
+ from omlish.lite.inject import Injector
4
7
  from omlish.lite.inject import InjectorBindingOrBindings
5
8
  from omlish.lite.inject import InjectorBindings
6
9
  from omlish.lite.inject import inj
@@ -12,16 +15,23 @@ from .commands import DeployCommandExecutor
12
15
  from .conf import DeployConfManager
13
16
  from .config import DeployConfig
14
17
  from .deploy import DeployManager
18
+ from .driver import DeployDriver
19
+ from .driver import DeployDriverFactory
15
20
  from .git import DeployGitManager
16
21
  from .interp import InterpCommand
17
22
  from .interp import InterpCommandExecutor
18
23
  from .paths.inject import bind_deploy_paths
19
24
  from .paths.owners import DeployPathOwner
25
+ from .specs import DeploySpec
20
26
  from .tmp import DeployHomeAtomics
21
27
  from .tmp import DeployTmpManager
22
28
  from .venvs import DeployVenvManager
23
29
 
24
30
 
31
+ class DeployInjectorScope(ContextvarInjectorScope):
32
+ pass
33
+
34
+
25
35
  def bind_deploy(
26
36
  *,
27
37
  deploy_config: DeployConfig,
@@ -65,6 +75,25 @@ def bind_deploy(
65
75
 
66
76
  #
67
77
 
78
+ def provide_deploy_driver_factory(injector: Injector, sc: DeployInjectorScope) -> DeployDriverFactory:
79
+ @contextlib.contextmanager
80
+ def factory(spec: DeploySpec) -> ta.Iterator[DeployDriver]:
81
+ with sc.enter({
82
+ inj.as_key(DeploySpec): spec,
83
+ }):
84
+ yield injector[DeployDriver]
85
+ return DeployDriverFactory(factory)
86
+ lst.append(inj.bind(provide_deploy_driver_factory, singleton=True))
87
+
88
+ lst.extend([
89
+ inj.bind_scope(DeployInjectorScope),
90
+ inj.bind_scope_seed(DeploySpec, DeployInjectorScope),
91
+
92
+ inj.bind(DeployDriver, in_=DeployInjectorScope),
93
+ ])
94
+
95
+ #
96
+
68
97
  lst.extend([
69
98
  bind_command(DeployCommand, DeployCommandExecutor),
70
99
  bind_command(InterpCommand, InterpCommandExecutor),
ominfra/scripts/manage.py CHANGED
@@ -17,6 +17,7 @@ import base64
17
17
  import collections
18
18
  import collections.abc
19
19
  import contextlib
20
+ import contextvars
20
21
  import ctypes as ct
21
22
  import dataclasses as dc
22
23
  import datetime
@@ -4905,6 +4906,10 @@ class InjectorBinding:
4905
4906
  key: InjectorKey
4906
4907
  provider: InjectorProvider
4907
4908
 
4909
+ def __post_init__(self) -> None:
4910
+ check.isinstance(self.key, InjectorKey)
4911
+ check.isinstance(self.provider, InjectorProvider)
4912
+
4908
4913
 
4909
4914
  class InjectorBindings(abc.ABC):
4910
4915
  @abc.abstractmethod
@@ -5168,6 +5173,164 @@ def build_injector_provider_map(bs: InjectorBindings) -> ta.Mapping[InjectorKey,
5168
5173
  return pm
5169
5174
 
5170
5175
 
5176
+ ###
5177
+ # scopes
5178
+
5179
+
5180
+ class InjectorScope(abc.ABC): # noqa
5181
+ def __init__(
5182
+ self,
5183
+ *,
5184
+ _i: Injector,
5185
+ ) -> None:
5186
+ check.not_in(abc.ABC, type(self).__bases__)
5187
+
5188
+ super().__init__()
5189
+
5190
+ self._i = _i
5191
+
5192
+ all_seeds: ta.Iterable[_InjectorScopeSeed] = self._i.provide(InjectorKey(_InjectorScopeSeed, array=True))
5193
+ self._sks = {s.k for s in all_seeds if s.sc is type(self)}
5194
+
5195
+ #
5196
+
5197
+ @dc.dataclass(frozen=True)
5198
+ class State:
5199
+ seeds: ta.Dict[InjectorKey, ta.Any]
5200
+ prvs: ta.Dict[InjectorKey, ta.Any] = dc.field(default_factory=dict)
5201
+
5202
+ def new_state(self, vs: ta.Mapping[InjectorKey, ta.Any]) -> State:
5203
+ vs = dict(vs)
5204
+ check.equal(set(vs.keys()), self._sks)
5205
+ return InjectorScope.State(vs)
5206
+
5207
+ #
5208
+
5209
+ @abc.abstractmethod
5210
+ def state(self) -> State:
5211
+ raise NotImplementedError
5212
+
5213
+ @abc.abstractmethod
5214
+ def enter(self, vs: ta.Mapping[InjectorKey, ta.Any]) -> ta.ContextManager[None]:
5215
+ raise NotImplementedError
5216
+
5217
+
5218
+ class ExclusiveInjectorScope(InjectorScope, abc.ABC):
5219
+ _st: ta.Optional[InjectorScope.State] = None
5220
+
5221
+ def state(self) -> InjectorScope.State:
5222
+ return check.not_none(self._st)
5223
+
5224
+ @contextlib.contextmanager
5225
+ def enter(self, vs: ta.Mapping[InjectorKey, ta.Any]) -> ta.Iterator[None]:
5226
+ check.none(self._st)
5227
+ self._st = self.new_state(vs)
5228
+ try:
5229
+ yield
5230
+ finally:
5231
+ self._st = None
5232
+
5233
+
5234
+ class ContextvarInjectorScope(InjectorScope, abc.ABC):
5235
+ _cv: contextvars.ContextVar
5236
+
5237
+ def __init_subclass__(cls, **kwargs: ta.Any) -> None:
5238
+ super().__init_subclass__(**kwargs)
5239
+ check.not_in(abc.ABC, cls.__bases__)
5240
+ check.state(not hasattr(cls, '_cv'))
5241
+ cls._cv = contextvars.ContextVar(f'{cls.__name__}_cv')
5242
+
5243
+ def state(self) -> InjectorScope.State:
5244
+ return self._cv.get()
5245
+
5246
+ @contextlib.contextmanager
5247
+ def enter(self, vs: ta.Mapping[InjectorKey, ta.Any]) -> ta.Iterator[None]:
5248
+ try:
5249
+ self._cv.get()
5250
+ except LookupError:
5251
+ pass
5252
+ else:
5253
+ raise RuntimeError(f'Scope already entered: {self}')
5254
+ st = self.new_state(vs)
5255
+ tok = self._cv.set(st)
5256
+ try:
5257
+ yield
5258
+ finally:
5259
+ self._cv.reset(tok)
5260
+
5261
+
5262
+ #
5263
+
5264
+
5265
+ @dc.dataclass(frozen=True)
5266
+ class ScopedInjectorProvider(InjectorProvider):
5267
+ p: InjectorProvider
5268
+ k: InjectorKey
5269
+ sc: ta.Type[InjectorScope]
5270
+
5271
+ def __post_init__(self) -> None:
5272
+ check.isinstance(self.p, InjectorProvider)
5273
+ check.isinstance(self.k, InjectorKey)
5274
+ check.issubclass(self.sc, InjectorScope)
5275
+
5276
+ def provider_fn(self) -> InjectorProviderFn:
5277
+ def pfn(i: Injector) -> ta.Any:
5278
+ st = i[self.sc].state()
5279
+ try:
5280
+ return st.prvs[self.k]
5281
+ except KeyError:
5282
+ pass
5283
+ v = ufn(i)
5284
+ st.prvs[self.k] = v
5285
+ return v
5286
+
5287
+ ufn = self.p.provider_fn()
5288
+ return pfn
5289
+
5290
+
5291
+ @dc.dataclass(frozen=True)
5292
+ class _ScopeSeedInjectorProvider(InjectorProvider):
5293
+ k: InjectorKey
5294
+ sc: ta.Type[InjectorScope]
5295
+
5296
+ def __post_init__(self) -> None:
5297
+ check.isinstance(self.k, InjectorKey)
5298
+ check.issubclass(self.sc, InjectorScope)
5299
+
5300
+ def provider_fn(self) -> InjectorProviderFn:
5301
+ def pfn(i: Injector) -> ta.Any:
5302
+ st = i[self.sc].state()
5303
+ return st.seeds[self.k]
5304
+ return pfn
5305
+
5306
+
5307
+ def bind_injector_scope(sc: ta.Type[InjectorScope]) -> InjectorBindingOrBindings:
5308
+ return as_injector_bindings(
5309
+ InjectorBinder.bind(sc, singleton=True),
5310
+ )
5311
+
5312
+
5313
+ #
5314
+
5315
+
5316
+ @dc.dataclass(frozen=True)
5317
+ class _InjectorScopeSeed:
5318
+ sc: ta.Type['InjectorScope']
5319
+ k: InjectorKey
5320
+
5321
+ def __post_init__(self) -> None:
5322
+ check.issubclass(self.sc, InjectorScope)
5323
+ check.isinstance(self.k, InjectorKey)
5324
+
5325
+
5326
+ def bind_injector_scope_seed(k: ta.Any, sc: ta.Type[InjectorScope]) -> InjectorBindingOrBindings:
5327
+ kk = as_injector_key(k)
5328
+ return as_injector_bindings(
5329
+ InjectorBinding(kk, _ScopeSeedInjectorProvider(kk, sc)),
5330
+ InjectorBinder.bind(_InjectorScopeSeed(sc, kk), array=True),
5331
+ )
5332
+
5333
+
5171
5334
  ###
5172
5335
  # inspection
5173
5336
 
@@ -5312,13 +5475,21 @@ _INJECTOR_EAGER_ARRAY_KEY: InjectorKey[_InjectorEager] = InjectorKey(_InjectorEa
5312
5475
 
5313
5476
 
5314
5477
  class _Injector(Injector):
5478
+ _DEFAULT_BINDINGS: ta.ClassVar[ta.List[InjectorBinding]] = []
5479
+
5315
5480
  def __init__(self, bs: InjectorBindings, p: ta.Optional[Injector] = None) -> None:
5316
5481
  super().__init__()
5317
5482
 
5318
5483
  self._bs = check.isinstance(bs, InjectorBindings)
5319
5484
  self._p: ta.Optional[Injector] = check.isinstance(p, (Injector, type(None)))
5320
5485
 
5321
- self._pfm = {k: v.provider_fn() for k, v in build_injector_provider_map(bs).items()}
5486
+ self._pfm = {
5487
+ k: v.provider_fn()
5488
+ for k, v in build_injector_provider_map(as_injector_bindings(
5489
+ *self._DEFAULT_BINDINGS,
5490
+ bs,
5491
+ )).items()
5492
+ }
5322
5493
 
5323
5494
  if _INJECTOR_INJECTOR_KEY in self._pfm:
5324
5495
  raise DuplicateInjectorKeyError(_INJECTOR_INJECTOR_KEY)
@@ -5485,6 +5656,7 @@ class InjectorBinder:
5485
5656
  to_const: ta.Any = None,
5486
5657
  to_key: ta.Any = None,
5487
5658
 
5659
+ in_: ta.Optional[ta.Type[InjectorScope]] = None,
5488
5660
  singleton: bool = False,
5489
5661
 
5490
5662
  eager: bool = False,
@@ -5494,12 +5666,12 @@ class InjectorBinder:
5494
5666
  if isinstance(obj, cls._BANNED_BIND_TYPES):
5495
5667
  raise TypeError(obj)
5496
5668
 
5497
- ##
5669
+ #
5498
5670
 
5499
5671
  if key is not None:
5500
5672
  key = as_injector_key(key)
5501
5673
 
5502
- ##
5674
+ #
5503
5675
 
5504
5676
  has_to = (
5505
5677
  to_fn is not None or
@@ -5529,7 +5701,7 @@ class InjectorBinder:
5529
5701
  key = InjectorKey(type(obj))
5530
5702
  del has_to
5531
5703
 
5532
- ##
5704
+ #
5533
5705
 
5534
5706
  if tag is not None:
5535
5707
  if key.tag is not None:
@@ -5539,7 +5711,7 @@ class InjectorBinder:
5539
5711
  if array is not None:
5540
5712
  key = dc.replace(key, array=array)
5541
5713
 
5542
- ##
5714
+ #
5543
5715
 
5544
5716
  providers: ta.List[InjectorProvider] = []
5545
5717
  if to_fn is not None:
@@ -5554,23 +5726,34 @@ class InjectorBinder:
5554
5726
  raise TypeError('Must specify provider')
5555
5727
  if len(providers) > 1:
5556
5728
  raise TypeError('May not specify multiple providers')
5557
- provider, = providers
5729
+ provider = check.single(providers)
5558
5730
 
5559
- ##
5731
+ #
5560
5732
 
5733
+ pws: ta.List[ta.Any] = []
5734
+ if in_ is not None:
5735
+ check.issubclass(in_, InjectorScope)
5736
+ check.not_in(abc.ABC, in_.__bases__)
5737
+ pws.append(functools.partial(ScopedInjectorProvider, k=key, sc=in_))
5561
5738
  if singleton:
5562
- provider = SingletonInjectorProvider(provider)
5739
+ pws.append(SingletonInjectorProvider)
5740
+ if len(pws) > 1:
5741
+ raise TypeError('May not specify multiple provider wrappers')
5742
+ elif pws:
5743
+ provider = check.single(pws)(provider)
5744
+
5745
+ #
5563
5746
 
5564
5747
  binding = InjectorBinding(key, provider)
5565
5748
 
5566
- ##
5749
+ #
5567
5750
 
5568
5751
  extras: ta.List[InjectorBinding] = []
5569
5752
 
5570
5753
  if eager:
5571
5754
  extras.append(bind_injector_eager_key(key))
5572
5755
 
5573
- ##
5756
+ #
5574
5757
 
5575
5758
  if extras:
5576
5759
  return as_injector_bindings(binding, *extras)
@@ -5643,7 +5826,8 @@ def bind_injector_eager_key(key: ta.Any) -> InjectorBinding:
5643
5826
  return InjectorBinding(_INJECTOR_EAGER_ARRAY_KEY, ConstInjectorProvider(_InjectorEager(as_injector_key(key))))
5644
5827
 
5645
5828
 
5646
- ##
5829
+ ###
5830
+ # api
5647
5831
 
5648
5832
 
5649
5833
  class InjectionApi:
@@ -5666,6 +5850,14 @@ class InjectionApi:
5666
5850
  def override(self, p: InjectorBindings, *args: InjectorBindingOrBindings) -> InjectorBindings:
5667
5851
  return injector_override(p, *args)
5668
5852
 
5853
+ # scopes
5854
+
5855
+ def bind_scope(self, sc: ta.Type[InjectorScope]) -> InjectorBindingOrBindings:
5856
+ return bind_injector_scope(sc)
5857
+
5858
+ def bind_scope_seed(self, k: ta.Any, sc: ta.Type[InjectorScope]) -> InjectorBindingOrBindings:
5859
+ return bind_injector_scope_seed(k, sc)
5860
+
5669
5861
  # injector
5670
5862
 
5671
5863
  def create_injector(self, *args: InjectorBindingOrBindings, parent: ta.Optional[Injector] = None) -> Injector:
@@ -5686,6 +5878,7 @@ class InjectionApi:
5686
5878
  to_const: ta.Any = None,
5687
5879
  to_key: ta.Any = None,
5688
5880
 
5881
+ in_: ta.Optional[ta.Type[InjectorScope]] = None,
5689
5882
  singleton: bool = False,
5690
5883
 
5691
5884
  eager: bool = False,
@@ -5702,6 +5895,7 @@ class InjectionApi:
5702
5895
  to_const=to_const,
5703
5896
  to_key=to_key,
5704
5897
 
5898
+ in_=in_,
5705
5899
  singleton=singleton,
5706
5900
 
5707
5901
  eager=eager,
@@ -7547,6 +7741,37 @@ class LocalCommandExecutor(CommandExecutor):
7547
7741
  return await ce.execute(cmd)
7548
7742
 
7549
7743
 
7744
+ ########################################
7745
+ # ../deploy/deploy.py
7746
+
7747
+
7748
+ DEPLOY_TAG_DATETIME_FMT = '%Y%m%dT%H%M%SZ'
7749
+
7750
+
7751
+ DeployManagerUtcClock = ta.NewType('DeployManagerUtcClock', Func0[datetime.datetime])
7752
+
7753
+
7754
+ class DeployManager:
7755
+ def __init__(
7756
+ self,
7757
+ *,
7758
+
7759
+ utc_clock: ta.Optional[DeployManagerUtcClock] = None,
7760
+ ):
7761
+ super().__init__()
7762
+
7763
+ self._utc_clock = utc_clock
7764
+
7765
+ def _utc_now(self) -> datetime.datetime:
7766
+ if self._utc_clock is not None:
7767
+ return self._utc_clock() # noqa
7768
+ else:
7769
+ return datetime.datetime.now(tz=datetime.timezone.utc) # noqa
7770
+
7771
+ def make_deploy_time(self) -> DeployTime:
7772
+ return DeployTime(self._utc_now().strftime(DEPLOY_TAG_DATETIME_FMT))
7773
+
7774
+
7550
7775
  ########################################
7551
7776
  # ../deploy/paths/paths.py
7552
7777
  """
@@ -10929,49 +11154,37 @@ class DeployAppManager(DeployPathOwner):
10929
11154
 
10930
11155
 
10931
11156
  ########################################
10932
- # ../deploy/deploy.py
11157
+ # ../deploy/driver.py
10933
11158
 
10934
11159
 
10935
- DEPLOY_TAG_DATETIME_FMT = '%Y%m%dT%H%M%SZ'
10936
-
10937
-
10938
- DeployManagerUtcClock = ta.NewType('DeployManagerUtcClock', Func0[datetime.datetime])
11160
+ class DeployDriverFactory(Func1[DeploySpec, ta.ContextManager['DeployDriver']]):
11161
+ pass
10939
11162
 
10940
11163
 
10941
- class DeployManager:
11164
+ class DeployDriver:
10942
11165
  def __init__(
10943
11166
  self,
10944
11167
  *,
10945
- apps: DeployAppManager,
10946
- paths: DeployPathsManager,
11168
+ spec: DeploySpec,
10947
11169
 
10948
- utc_clock: ta.Optional[DeployManagerUtcClock] = None,
10949
- ):
11170
+ deploys: DeployManager,
11171
+ paths: DeployPathsManager,
11172
+ apps: DeployAppManager,
11173
+ ) -> None:
10950
11174
  super().__init__()
10951
11175
 
10952
- self._apps = apps
10953
- self._paths = paths
10954
-
10955
- self._utc_clock = utc_clock
10956
-
10957
- def _utc_now(self) -> datetime.datetime:
10958
- if self._utc_clock is not None:
10959
- return self._utc_clock() # noqa
10960
- else:
10961
- return datetime.datetime.now(tz=datetime.timezone.utc) # noqa
11176
+ self._spec = spec
10962
11177
 
10963
- def _make_deploy_time(self) -> DeployTime:
10964
- return DeployTime(self._utc_now().strftime(DEPLOY_TAG_DATETIME_FMT))
11178
+ self._deploys = deploys
11179
+ self._paths = paths
11180
+ self._apps = apps
10965
11181
 
10966
- async def run_deploy(
10967
- self,
10968
- spec: DeploySpec,
10969
- ) -> None:
11182
+ async def drive_deploy(self) -> None:
10970
11183
  self._paths.validate_deploy_paths()
10971
11184
 
10972
11185
  #
10973
11186
 
10974
- hs = check.non_empty_str(spec.home)
11187
+ hs = check.non_empty_str(self._spec.home)
10975
11188
  hs = os.path.expanduser(hs)
10976
11189
  hs = os.path.realpath(hs)
10977
11190
  hs = os.path.abspath(hs)
@@ -10981,13 +11194,13 @@ class DeployManager:
10981
11194
  #
10982
11195
 
10983
11196
  deploy_tags = DeployTagMap(
10984
- self._make_deploy_time(),
10985
- spec.key(),
11197
+ self._deploys.make_deploy_time(),
11198
+ self._spec.key(),
10986
11199
  )
10987
11200
 
10988
11201
  #
10989
11202
 
10990
- for app in spec.apps:
11203
+ for app in self._spec.apps:
10991
11204
  app_tags = deploy_tags.add(
10992
11205
  app.app,
10993
11206
  app.key(),
@@ -11019,12 +11232,13 @@ class DeployCommand(Command['DeployCommand.Output']):
11019
11232
 
11020
11233
  @dc.dataclass(frozen=True)
11021
11234
  class DeployCommandExecutor(CommandExecutor[DeployCommand, DeployCommand.Output]):
11022
- _deploy: DeployManager
11235
+ _driver_factory: DeployDriverFactory
11023
11236
 
11024
11237
  async def execute(self, cmd: DeployCommand) -> DeployCommand.Output:
11025
11238
  log.info('Deploying! %r', cmd.spec)
11026
11239
 
11027
- await self._deploy.run_deploy(cmd.spec)
11240
+ with self._driver_factory(cmd.spec) as driver:
11241
+ await driver.drive_deploy()
11028
11242
 
11029
11243
  return DeployCommand.Output()
11030
11244
 
@@ -11033,6 +11247,10 @@ class DeployCommandExecutor(CommandExecutor[DeployCommand, DeployCommand.Output]
11033
11247
  # ../deploy/inject.py
11034
11248
 
11035
11249
 
11250
+ class DeployInjectorScope(ContextvarInjectorScope):
11251
+ pass
11252
+
11253
+
11036
11254
  def bind_deploy(
11037
11255
  *,
11038
11256
  deploy_config: DeployConfig,
@@ -11076,6 +11294,25 @@ def bind_deploy(
11076
11294
 
11077
11295
  #
11078
11296
 
11297
+ def provide_deploy_driver_factory(injector: Injector, sc: DeployInjectorScope) -> DeployDriverFactory:
11298
+ @contextlib.contextmanager
11299
+ def factory(spec: DeploySpec) -> ta.Iterator[DeployDriver]:
11300
+ with sc.enter({
11301
+ inj.as_key(DeploySpec): spec,
11302
+ }):
11303
+ yield injector[DeployDriver]
11304
+ return DeployDriverFactory(factory)
11305
+ lst.append(inj.bind(provide_deploy_driver_factory, singleton=True))
11306
+
11307
+ lst.extend([
11308
+ inj.bind_scope(DeployInjectorScope),
11309
+ inj.bind_scope_seed(DeploySpec, DeployInjectorScope),
11310
+
11311
+ inj.bind(DeployDriver, in_=DeployInjectorScope),
11312
+ ])
11313
+
11314
+ #
11315
+
11079
11316
  lst.extend([
11080
11317
  bind_command(DeployCommand, DeployCommandExecutor),
11081
11318
  bind_command(InterpCommand, InterpCommandExecutor),
@@ -37,6 +37,7 @@ import base64
37
37
  import collections
38
38
  import collections.abc
39
39
  import contextlib
40
+ import contextvars
40
41
  import ctypes as ct
41
42
  import dataclasses as dc
42
43
  import datetime
@@ -4070,6 +4071,10 @@ class InjectorBinding:
4070
4071
  key: InjectorKey
4071
4072
  provider: InjectorProvider
4072
4073
 
4074
+ def __post_init__(self) -> None:
4075
+ check.isinstance(self.key, InjectorKey)
4076
+ check.isinstance(self.provider, InjectorProvider)
4077
+
4073
4078
 
4074
4079
  class InjectorBindings(abc.ABC):
4075
4080
  @abc.abstractmethod
@@ -4333,6 +4338,164 @@ def build_injector_provider_map(bs: InjectorBindings) -> ta.Mapping[InjectorKey,
4333
4338
  return pm
4334
4339
 
4335
4340
 
4341
+ ###
4342
+ # scopes
4343
+
4344
+
4345
+ class InjectorScope(abc.ABC): # noqa
4346
+ def __init__(
4347
+ self,
4348
+ *,
4349
+ _i: Injector,
4350
+ ) -> None:
4351
+ check.not_in(abc.ABC, type(self).__bases__)
4352
+
4353
+ super().__init__()
4354
+
4355
+ self._i = _i
4356
+
4357
+ all_seeds: ta.Iterable[_InjectorScopeSeed] = self._i.provide(InjectorKey(_InjectorScopeSeed, array=True))
4358
+ self._sks = {s.k for s in all_seeds if s.sc is type(self)}
4359
+
4360
+ #
4361
+
4362
+ @dc.dataclass(frozen=True)
4363
+ class State:
4364
+ seeds: ta.Dict[InjectorKey, ta.Any]
4365
+ prvs: ta.Dict[InjectorKey, ta.Any] = dc.field(default_factory=dict)
4366
+
4367
+ def new_state(self, vs: ta.Mapping[InjectorKey, ta.Any]) -> State:
4368
+ vs = dict(vs)
4369
+ check.equal(set(vs.keys()), self._sks)
4370
+ return InjectorScope.State(vs)
4371
+
4372
+ #
4373
+
4374
+ @abc.abstractmethod
4375
+ def state(self) -> State:
4376
+ raise NotImplementedError
4377
+
4378
+ @abc.abstractmethod
4379
+ def enter(self, vs: ta.Mapping[InjectorKey, ta.Any]) -> ta.ContextManager[None]:
4380
+ raise NotImplementedError
4381
+
4382
+
4383
+ class ExclusiveInjectorScope(InjectorScope, abc.ABC):
4384
+ _st: ta.Optional[InjectorScope.State] = None
4385
+
4386
+ def state(self) -> InjectorScope.State:
4387
+ return check.not_none(self._st)
4388
+
4389
+ @contextlib.contextmanager
4390
+ def enter(self, vs: ta.Mapping[InjectorKey, ta.Any]) -> ta.Iterator[None]:
4391
+ check.none(self._st)
4392
+ self._st = self.new_state(vs)
4393
+ try:
4394
+ yield
4395
+ finally:
4396
+ self._st = None
4397
+
4398
+
4399
+ class ContextvarInjectorScope(InjectorScope, abc.ABC):
4400
+ _cv: contextvars.ContextVar
4401
+
4402
+ def __init_subclass__(cls, **kwargs: ta.Any) -> None:
4403
+ super().__init_subclass__(**kwargs)
4404
+ check.not_in(abc.ABC, cls.__bases__)
4405
+ check.state(not hasattr(cls, '_cv'))
4406
+ cls._cv = contextvars.ContextVar(f'{cls.__name__}_cv')
4407
+
4408
+ def state(self) -> InjectorScope.State:
4409
+ return self._cv.get()
4410
+
4411
+ @contextlib.contextmanager
4412
+ def enter(self, vs: ta.Mapping[InjectorKey, ta.Any]) -> ta.Iterator[None]:
4413
+ try:
4414
+ self._cv.get()
4415
+ except LookupError:
4416
+ pass
4417
+ else:
4418
+ raise RuntimeError(f'Scope already entered: {self}')
4419
+ st = self.new_state(vs)
4420
+ tok = self._cv.set(st)
4421
+ try:
4422
+ yield
4423
+ finally:
4424
+ self._cv.reset(tok)
4425
+
4426
+
4427
+ #
4428
+
4429
+
4430
+ @dc.dataclass(frozen=True)
4431
+ class ScopedInjectorProvider(InjectorProvider):
4432
+ p: InjectorProvider
4433
+ k: InjectorKey
4434
+ sc: ta.Type[InjectorScope]
4435
+
4436
+ def __post_init__(self) -> None:
4437
+ check.isinstance(self.p, InjectorProvider)
4438
+ check.isinstance(self.k, InjectorKey)
4439
+ check.issubclass(self.sc, InjectorScope)
4440
+
4441
+ def provider_fn(self) -> InjectorProviderFn:
4442
+ def pfn(i: Injector) -> ta.Any:
4443
+ st = i[self.sc].state()
4444
+ try:
4445
+ return st.prvs[self.k]
4446
+ except KeyError:
4447
+ pass
4448
+ v = ufn(i)
4449
+ st.prvs[self.k] = v
4450
+ return v
4451
+
4452
+ ufn = self.p.provider_fn()
4453
+ return pfn
4454
+
4455
+
4456
+ @dc.dataclass(frozen=True)
4457
+ class _ScopeSeedInjectorProvider(InjectorProvider):
4458
+ k: InjectorKey
4459
+ sc: ta.Type[InjectorScope]
4460
+
4461
+ def __post_init__(self) -> None:
4462
+ check.isinstance(self.k, InjectorKey)
4463
+ check.issubclass(self.sc, InjectorScope)
4464
+
4465
+ def provider_fn(self) -> InjectorProviderFn:
4466
+ def pfn(i: Injector) -> ta.Any:
4467
+ st = i[self.sc].state()
4468
+ return st.seeds[self.k]
4469
+ return pfn
4470
+
4471
+
4472
+ def bind_injector_scope(sc: ta.Type[InjectorScope]) -> InjectorBindingOrBindings:
4473
+ return as_injector_bindings(
4474
+ InjectorBinder.bind(sc, singleton=True),
4475
+ )
4476
+
4477
+
4478
+ #
4479
+
4480
+
4481
+ @dc.dataclass(frozen=True)
4482
+ class _InjectorScopeSeed:
4483
+ sc: ta.Type['InjectorScope']
4484
+ k: InjectorKey
4485
+
4486
+ def __post_init__(self) -> None:
4487
+ check.issubclass(self.sc, InjectorScope)
4488
+ check.isinstance(self.k, InjectorKey)
4489
+
4490
+
4491
+ def bind_injector_scope_seed(k: ta.Any, sc: ta.Type[InjectorScope]) -> InjectorBindingOrBindings:
4492
+ kk = as_injector_key(k)
4493
+ return as_injector_bindings(
4494
+ InjectorBinding(kk, _ScopeSeedInjectorProvider(kk, sc)),
4495
+ InjectorBinder.bind(_InjectorScopeSeed(sc, kk), array=True),
4496
+ )
4497
+
4498
+
4336
4499
  ###
4337
4500
  # inspection
4338
4501
 
@@ -4477,13 +4640,21 @@ _INJECTOR_EAGER_ARRAY_KEY: InjectorKey[_InjectorEager] = InjectorKey(_InjectorEa
4477
4640
 
4478
4641
 
4479
4642
  class _Injector(Injector):
4643
+ _DEFAULT_BINDINGS: ta.ClassVar[ta.List[InjectorBinding]] = []
4644
+
4480
4645
  def __init__(self, bs: InjectorBindings, p: ta.Optional[Injector] = None) -> None:
4481
4646
  super().__init__()
4482
4647
 
4483
4648
  self._bs = check.isinstance(bs, InjectorBindings)
4484
4649
  self._p: ta.Optional[Injector] = check.isinstance(p, (Injector, type(None)))
4485
4650
 
4486
- self._pfm = {k: v.provider_fn() for k, v in build_injector_provider_map(bs).items()}
4651
+ self._pfm = {
4652
+ k: v.provider_fn()
4653
+ for k, v in build_injector_provider_map(as_injector_bindings(
4654
+ *self._DEFAULT_BINDINGS,
4655
+ bs,
4656
+ )).items()
4657
+ }
4487
4658
 
4488
4659
  if _INJECTOR_INJECTOR_KEY in self._pfm:
4489
4660
  raise DuplicateInjectorKeyError(_INJECTOR_INJECTOR_KEY)
@@ -4650,6 +4821,7 @@ class InjectorBinder:
4650
4821
  to_const: ta.Any = None,
4651
4822
  to_key: ta.Any = None,
4652
4823
 
4824
+ in_: ta.Optional[ta.Type[InjectorScope]] = None,
4653
4825
  singleton: bool = False,
4654
4826
 
4655
4827
  eager: bool = False,
@@ -4659,12 +4831,12 @@ class InjectorBinder:
4659
4831
  if isinstance(obj, cls._BANNED_BIND_TYPES):
4660
4832
  raise TypeError(obj)
4661
4833
 
4662
- ##
4834
+ #
4663
4835
 
4664
4836
  if key is not None:
4665
4837
  key = as_injector_key(key)
4666
4838
 
4667
- ##
4839
+ #
4668
4840
 
4669
4841
  has_to = (
4670
4842
  to_fn is not None or
@@ -4694,7 +4866,7 @@ class InjectorBinder:
4694
4866
  key = InjectorKey(type(obj))
4695
4867
  del has_to
4696
4868
 
4697
- ##
4869
+ #
4698
4870
 
4699
4871
  if tag is not None:
4700
4872
  if key.tag is not None:
@@ -4704,7 +4876,7 @@ class InjectorBinder:
4704
4876
  if array is not None:
4705
4877
  key = dc.replace(key, array=array)
4706
4878
 
4707
- ##
4879
+ #
4708
4880
 
4709
4881
  providers: ta.List[InjectorProvider] = []
4710
4882
  if to_fn is not None:
@@ -4719,23 +4891,34 @@ class InjectorBinder:
4719
4891
  raise TypeError('Must specify provider')
4720
4892
  if len(providers) > 1:
4721
4893
  raise TypeError('May not specify multiple providers')
4722
- provider, = providers
4894
+ provider = check.single(providers)
4723
4895
 
4724
- ##
4896
+ #
4725
4897
 
4898
+ pws: ta.List[ta.Any] = []
4899
+ if in_ is not None:
4900
+ check.issubclass(in_, InjectorScope)
4901
+ check.not_in(abc.ABC, in_.__bases__)
4902
+ pws.append(functools.partial(ScopedInjectorProvider, k=key, sc=in_))
4726
4903
  if singleton:
4727
- provider = SingletonInjectorProvider(provider)
4904
+ pws.append(SingletonInjectorProvider)
4905
+ if len(pws) > 1:
4906
+ raise TypeError('May not specify multiple provider wrappers')
4907
+ elif pws:
4908
+ provider = check.single(pws)(provider)
4909
+
4910
+ #
4728
4911
 
4729
4912
  binding = InjectorBinding(key, provider)
4730
4913
 
4731
- ##
4914
+ #
4732
4915
 
4733
4916
  extras: ta.List[InjectorBinding] = []
4734
4917
 
4735
4918
  if eager:
4736
4919
  extras.append(bind_injector_eager_key(key))
4737
4920
 
4738
- ##
4921
+ #
4739
4922
 
4740
4923
  if extras:
4741
4924
  return as_injector_bindings(binding, *extras)
@@ -4808,7 +4991,8 @@ def bind_injector_eager_key(key: ta.Any) -> InjectorBinding:
4808
4991
  return InjectorBinding(_INJECTOR_EAGER_ARRAY_KEY, ConstInjectorProvider(_InjectorEager(as_injector_key(key))))
4809
4992
 
4810
4993
 
4811
- ##
4994
+ ###
4995
+ # api
4812
4996
 
4813
4997
 
4814
4998
  class InjectionApi:
@@ -4831,6 +5015,14 @@ class InjectionApi:
4831
5015
  def override(self, p: InjectorBindings, *args: InjectorBindingOrBindings) -> InjectorBindings:
4832
5016
  return injector_override(p, *args)
4833
5017
 
5018
+ # scopes
5019
+
5020
+ def bind_scope(self, sc: ta.Type[InjectorScope]) -> InjectorBindingOrBindings:
5021
+ return bind_injector_scope(sc)
5022
+
5023
+ def bind_scope_seed(self, k: ta.Any, sc: ta.Type[InjectorScope]) -> InjectorBindingOrBindings:
5024
+ return bind_injector_scope_seed(k, sc)
5025
+
4834
5026
  # injector
4835
5027
 
4836
5028
  def create_injector(self, *args: InjectorBindingOrBindings, parent: ta.Optional[Injector] = None) -> Injector:
@@ -4851,6 +5043,7 @@ class InjectionApi:
4851
5043
  to_const: ta.Any = None,
4852
5044
  to_key: ta.Any = None,
4853
5045
 
5046
+ in_: ta.Optional[ta.Type[InjectorScope]] = None,
4854
5047
  singleton: bool = False,
4855
5048
 
4856
5049
  eager: bool = False,
@@ -4867,6 +5060,7 @@ class InjectionApi:
4867
5060
  to_const=to_const,
4868
5061
  to_key=to_key,
4869
5062
 
5063
+ in_=in_,
4870
5064
  singleton=singleton,
4871
5065
 
4872
5066
  eager=eager,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ominfra
3
- Version: 0.0.0.dev177
3
+ Version: 0.0.0.dev178
4
4
  Summary: ominfra
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -12,8 +12,8 @@ Classifier: Operating System :: OS Independent
12
12
  Classifier: Operating System :: POSIX
13
13
  Requires-Python: >=3.12
14
14
  License-File: LICENSE
15
- Requires-Dist: omdev==0.0.0.dev177
16
- Requires-Dist: omlish==0.0.0.dev177
15
+ Requires-Dist: omdev==0.0.0.dev178
16
+ Requires-Dist: omlish==0.0.0.dev178
17
17
  Provides-Extra: all
18
18
  Requires-Dist: paramiko~=3.5; extra == "all"
19
19
  Requires-Dist: asyncssh~=2.18; extra == "all"
@@ -45,12 +45,13 @@ ominfra/manage/commands/subprocess.py,sha256=yHGMbAI-xKe_9BUs5IZ3Yav8qRE-I9aGnBt
45
45
  ominfra/manage/commands/types.py,sha256=XFZPeqeIBAaIIQF3pdPbGxLlb-LCrz6WtlDWO2q_vz0,210
46
46
  ominfra/manage/deploy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
47
  ominfra/manage/deploy/apps.py,sha256=LeZ2iX2YHviOAnvgut7Oz15301eJoYYCpmD3ffoIagA,4742
48
- ominfra/manage/deploy/commands.py,sha256=fKFKhFwqIqC_PsgA-W66qIJ5S32xRgBBaRt3lbPX5Zg,763
48
+ ominfra/manage/deploy/commands.py,sha256=U74HYQ4nhvH7znAKyXGOmZfdnll2gMWxKWVW4GzxG-0,830
49
49
  ominfra/manage/deploy/conf.py,sha256=fNfFlIb-bB3KAzaYZcjrbqaqKSiSq0Lpk0mIF6WgXiw,5410
50
50
  ominfra/manage/deploy/config.py,sha256=kPpl8TRisz295cM4oj-RHA6oh5jdcJ_N9pVpkl_doO8,114
51
- ominfra/manage/deploy/deploy.py,sha256=OqZOOjT2C_LjK8kwpP8T5VF2vtiDTHnsI-G6yNs4oDg,1965
51
+ ominfra/manage/deploy/deploy.py,sha256=vyBTbBm51HhRE-MQNvxEt39F8uOYsB4ToqZ3zmVkpqU,819
52
+ ominfra/manage/deploy/driver.py,sha256=ccLiVoZz_GnhQ3FRPFCu9hzsU6wsOWTOmHeYNBRIZkg,1576
52
53
  ominfra/manage/deploy/git.py,sha256=g4wzUuSu9HwWSDhdVX-7BvA2htMwtWbRcHaoDy-xOJ4,3960
53
- ominfra/manage/deploy/inject.py,sha256=vubWT09WfDrSb2f3rLtApl3S_0WJIPwRd_ENVggLmhM,1896
54
+ ominfra/manage/deploy/inject.py,sha256=JHQexbf-789mWL8GTSxyd4SnTbDB0nqwSyiJp-3QvAM,2834
54
55
  ominfra/manage/deploy/interp.py,sha256=OKkenH8YKEW_mEDR6X7_ZLxK9a1Ox6KHSwFPTHT6OzA,1029
55
56
  ominfra/manage/deploy/specs.py,sha256=usi5AmTlv8OGFcwhVHnu8Qrz1Criu5QP6_ScNMi9ehM,3748
56
57
  ominfra/manage/deploy/tags.py,sha256=NVEJhHKMwoDuCFd8lInH_jIe99FQILBX3wrulh3GiDg,5166
@@ -86,8 +87,8 @@ ominfra/manage/targets/inject.py,sha256=P4597xWM-V3I_gCt2O71OLhYQkkXtuJvkYRsIbhh
86
87
  ominfra/manage/targets/targets.py,sha256=7GP6UAZyJFEhpkJN6UQdpr_WN3p7C76v-s445y-WB6U,1885
87
88
  ominfra/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
88
89
  ominfra/scripts/journald2aws.py,sha256=yEnBAd0Q_3lBkVoTvlJ_uCcUxz7Ckn2qoSWZVhMihvQ,157696
89
- ominfra/scripts/manage.py,sha256=4tzrENQzD7G31SoN2gZxyk0ZBTinEOXGhPzhcTCrjK4,317752
90
- ominfra/scripts/supervisor.py,sha256=9pEop3S5txoA2Gip9mcOrOJWFjMJVXCH6-NKYYnOVic,276462
90
+ ominfra/scripts/manage.py,sha256=XDt86ZEFjcShexhLQjmse1N6f_dk7AVVtozzETsNUdY,324293
91
+ ominfra/scripts/supervisor.py,sha256=6RMBLbB4pMNaJlnOBwhdpuhMqv6NDrjTup9SYzKsEjo,281844
91
92
  ominfra/supervisor/LICENSE.txt,sha256=yvqaMNsDhWxziHa9ien6qCW1SkZv-DQlAg96XjfSee8,1746
92
93
  ominfra/supervisor/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
93
94
  ominfra/supervisor/__main__.py,sha256=I0yFw-C08OOiZ3BF6lF1Oiv789EQXu-_j6whDhQUTEA,66
@@ -129,9 +130,9 @@ ominfra/tailscale/api.py,sha256=C5-t_b6jZXUWcy5k8bXm7CFnk73pSdrlMOgGDeGVrpw,1370
129
130
  ominfra/tailscale/cli.py,sha256=h6akQJMl0KuWLHS7Ur6WcBZ2JwF0DJQhsPTnFBdGyNk,3571
130
131
  ominfra/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
131
132
  ominfra/tools/listresources.py,sha256=4qVg5txsb10EHhvqXXeM6gJ2jx9LbroEnPydDv1uXs0,6176
132
- ominfra-0.0.0.dev177.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
133
- ominfra-0.0.0.dev177.dist-info/METADATA,sha256=FHfsifQBdOhfEGn69cR4bKYjNxAx_DBQH0OkVjdPMQ8,731
134
- ominfra-0.0.0.dev177.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
135
- ominfra-0.0.0.dev177.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
136
- ominfra-0.0.0.dev177.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
137
- ominfra-0.0.0.dev177.dist-info/RECORD,,
133
+ ominfra-0.0.0.dev178.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
134
+ ominfra-0.0.0.dev178.dist-info/METADATA,sha256=Kfw95dyWOXq2P3oSLxG3ObbDVl139_CQAnrZy5k6QtE,731
135
+ ominfra-0.0.0.dev178.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
136
+ ominfra-0.0.0.dev178.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
137
+ ominfra-0.0.0.dev178.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
138
+ ominfra-0.0.0.dev178.dist-info/RECORD,,