ominfra 0.0.0.dev177__py3-none-any.whl → 0.0.0.dev178__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.
@@ -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,,