ominfra 0.0.0.dev177__py3-none-any.whl → 0.0.0.dev179__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- ominfra/manage/deploy/commands.py +4 -3
- ominfra/manage/deploy/deploy.py +1 -51
- ominfra/manage/deploy/driver.py +62 -0
- ominfra/manage/deploy/inject.py +63 -0
- ominfra/manage/deploy/interp.py +2 -2
- ominfra/manage/deploy/venvs.py +2 -2
- ominfra/scripts/manage.py +964 -592
- ominfra/scripts/supervisor.py +224 -29
- {ominfra-0.0.0.dev177.dist-info → ominfra-0.0.0.dev179.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev177.dist-info → ominfra-0.0.0.dev179.dist-info}/RECORD +14 -13
- {ominfra-0.0.0.dev177.dist-info → ominfra-0.0.0.dev179.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev177.dist-info → ominfra-0.0.0.dev179.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev177.dist-info → ominfra-0.0.0.dev179.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev177.dist-info → ominfra-0.0.0.dev179.dist-info}/top_level.txt +0 -0
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
|
@@ -5121,6 +5126,33 @@ def as_injector_bindings(*args: InjectorBindingOrBindings) -> InjectorBindings:
|
|
5121
5126
|
##
|
5122
5127
|
|
5123
5128
|
|
5129
|
+
def build_injector_provider_map(bs: InjectorBindings) -> ta.Mapping[InjectorKey, InjectorProvider]:
|
5130
|
+
pm: ta.Dict[InjectorKey, InjectorProvider] = {}
|
5131
|
+
am: ta.Dict[InjectorKey, ta.List[InjectorProvider]] = {}
|
5132
|
+
|
5133
|
+
for b in bs.bindings():
|
5134
|
+
if b.key.array:
|
5135
|
+
al = am.setdefault(b.key, [])
|
5136
|
+
if isinstance(b.provider, ArrayInjectorProvider):
|
5137
|
+
al.extend(b.provider.ps)
|
5138
|
+
else:
|
5139
|
+
al.append(b.provider)
|
5140
|
+
else:
|
5141
|
+
if b.key in pm:
|
5142
|
+
raise KeyError(b.key)
|
5143
|
+
pm[b.key] = b.provider
|
5144
|
+
|
5145
|
+
if am:
|
5146
|
+
for k, aps in am.items():
|
5147
|
+
pm[k] = ArrayInjectorProvider(aps)
|
5148
|
+
|
5149
|
+
return pm
|
5150
|
+
|
5151
|
+
|
5152
|
+
###
|
5153
|
+
# overrides
|
5154
|
+
|
5155
|
+
|
5124
5156
|
@dc.dataclass(frozen=True)
|
5125
5157
|
class OverridesInjectorBindings(InjectorBindings):
|
5126
5158
|
p: InjectorBindings
|
@@ -5142,30 +5174,160 @@ def injector_override(p: InjectorBindings, *args: InjectorBindingOrBindings) ->
|
|
5142
5174
|
return OverridesInjectorBindings(p, m)
|
5143
5175
|
|
5144
5176
|
|
5145
|
-
|
5177
|
+
###
|
5178
|
+
# scopes
|
5146
5179
|
|
5147
5180
|
|
5148
|
-
|
5149
|
-
|
5150
|
-
|
5181
|
+
class InjectorScope(abc.ABC): # noqa
|
5182
|
+
def __init__(
|
5183
|
+
self,
|
5184
|
+
*,
|
5185
|
+
_i: Injector,
|
5186
|
+
) -> None:
|
5187
|
+
check.not_in(abc.ABC, type(self).__bases__)
|
5151
5188
|
|
5152
|
-
|
5153
|
-
|
5154
|
-
|
5155
|
-
|
5156
|
-
|
5157
|
-
|
5158
|
-
|
5189
|
+
super().__init__()
|
5190
|
+
|
5191
|
+
self._i = _i
|
5192
|
+
|
5193
|
+
all_seeds: ta.Iterable[_InjectorScopeSeed] = self._i.provide(InjectorKey(_InjectorScopeSeed, array=True))
|
5194
|
+
self._sks = {s.k for s in all_seeds if s.sc is type(self)}
|
5195
|
+
|
5196
|
+
#
|
5197
|
+
|
5198
|
+
@dc.dataclass(frozen=True)
|
5199
|
+
class State:
|
5200
|
+
seeds: ta.Dict[InjectorKey, ta.Any]
|
5201
|
+
provisions: ta.Dict[InjectorKey, ta.Any] = dc.field(default_factory=dict)
|
5202
|
+
|
5203
|
+
def new_state(self, vs: ta.Mapping[InjectorKey, ta.Any]) -> State:
|
5204
|
+
vs = dict(vs)
|
5205
|
+
check.equal(set(vs.keys()), self._sks)
|
5206
|
+
return InjectorScope.State(vs)
|
5207
|
+
|
5208
|
+
#
|
5209
|
+
|
5210
|
+
@abc.abstractmethod
|
5211
|
+
def state(self) -> State:
|
5212
|
+
raise NotImplementedError
|
5213
|
+
|
5214
|
+
@abc.abstractmethod
|
5215
|
+
def enter(self, vs: ta.Mapping[InjectorKey, ta.Any]) -> ta.ContextManager[None]:
|
5216
|
+
raise NotImplementedError
|
5217
|
+
|
5218
|
+
|
5219
|
+
class ExclusiveInjectorScope(InjectorScope, abc.ABC):
|
5220
|
+
_st: ta.Optional[InjectorScope.State] = None
|
5221
|
+
|
5222
|
+
def state(self) -> InjectorScope.State:
|
5223
|
+
return check.not_none(self._st)
|
5224
|
+
|
5225
|
+
@contextlib.contextmanager
|
5226
|
+
def enter(self, vs: ta.Mapping[InjectorKey, ta.Any]) -> ta.Iterator[None]:
|
5227
|
+
check.none(self._st)
|
5228
|
+
self._st = self.new_state(vs)
|
5229
|
+
try:
|
5230
|
+
yield
|
5231
|
+
finally:
|
5232
|
+
self._st = None
|
5233
|
+
|
5234
|
+
|
5235
|
+
class ContextvarInjectorScope(InjectorScope, abc.ABC):
|
5236
|
+
_cv: contextvars.ContextVar
|
5237
|
+
|
5238
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
5239
|
+
super().__init_subclass__(**kwargs)
|
5240
|
+
check.not_in(abc.ABC, cls.__bases__)
|
5241
|
+
check.state(not hasattr(cls, '_cv'))
|
5242
|
+
cls._cv = contextvars.ContextVar(f'{cls.__name__}_cv')
|
5243
|
+
|
5244
|
+
def state(self) -> InjectorScope.State:
|
5245
|
+
return self._cv.get()
|
5246
|
+
|
5247
|
+
@contextlib.contextmanager
|
5248
|
+
def enter(self, vs: ta.Mapping[InjectorKey, ta.Any]) -> ta.Iterator[None]:
|
5249
|
+
try:
|
5250
|
+
self._cv.get()
|
5251
|
+
except LookupError:
|
5252
|
+
pass
|
5159
5253
|
else:
|
5160
|
-
|
5161
|
-
|
5162
|
-
|
5254
|
+
raise RuntimeError(f'Scope already entered: {self}')
|
5255
|
+
st = self.new_state(vs)
|
5256
|
+
tok = self._cv.set(st)
|
5257
|
+
try:
|
5258
|
+
yield
|
5259
|
+
finally:
|
5260
|
+
self._cv.reset(tok)
|
5163
5261
|
|
5164
|
-
if am:
|
5165
|
-
for k, aps in am.items():
|
5166
|
-
pm[k] = ArrayInjectorProvider(aps)
|
5167
5262
|
|
5168
|
-
|
5263
|
+
#
|
5264
|
+
|
5265
|
+
|
5266
|
+
@dc.dataclass(frozen=True)
|
5267
|
+
class ScopedInjectorProvider(InjectorProvider):
|
5268
|
+
p: InjectorProvider
|
5269
|
+
k: InjectorKey
|
5270
|
+
sc: ta.Type[InjectorScope]
|
5271
|
+
|
5272
|
+
def __post_init__(self) -> None:
|
5273
|
+
check.isinstance(self.p, InjectorProvider)
|
5274
|
+
check.isinstance(self.k, InjectorKey)
|
5275
|
+
check.issubclass(self.sc, InjectorScope)
|
5276
|
+
|
5277
|
+
def provider_fn(self) -> InjectorProviderFn:
|
5278
|
+
def pfn(i: Injector) -> ta.Any:
|
5279
|
+
st = i[self.sc].state()
|
5280
|
+
try:
|
5281
|
+
return st.provisions[self.k]
|
5282
|
+
except KeyError:
|
5283
|
+
pass
|
5284
|
+
v = ufn(i)
|
5285
|
+
st.provisions[self.k] = v
|
5286
|
+
return v
|
5287
|
+
|
5288
|
+
ufn = self.p.provider_fn()
|
5289
|
+
return pfn
|
5290
|
+
|
5291
|
+
|
5292
|
+
@dc.dataclass(frozen=True)
|
5293
|
+
class _ScopeSeedInjectorProvider(InjectorProvider):
|
5294
|
+
k: InjectorKey
|
5295
|
+
sc: ta.Type[InjectorScope]
|
5296
|
+
|
5297
|
+
def __post_init__(self) -> None:
|
5298
|
+
check.isinstance(self.k, InjectorKey)
|
5299
|
+
check.issubclass(self.sc, InjectorScope)
|
5300
|
+
|
5301
|
+
def provider_fn(self) -> InjectorProviderFn:
|
5302
|
+
def pfn(i: Injector) -> ta.Any:
|
5303
|
+
st = i[self.sc].state()
|
5304
|
+
return st.seeds[self.k]
|
5305
|
+
return pfn
|
5306
|
+
|
5307
|
+
|
5308
|
+
def bind_injector_scope(sc: ta.Type[InjectorScope]) -> InjectorBindingOrBindings:
|
5309
|
+
return InjectorBinder.bind(sc, singleton=True)
|
5310
|
+
|
5311
|
+
|
5312
|
+
#
|
5313
|
+
|
5314
|
+
|
5315
|
+
@dc.dataclass(frozen=True)
|
5316
|
+
class _InjectorScopeSeed:
|
5317
|
+
sc: ta.Type['InjectorScope']
|
5318
|
+
k: InjectorKey
|
5319
|
+
|
5320
|
+
def __post_init__(self) -> None:
|
5321
|
+
check.issubclass(self.sc, InjectorScope)
|
5322
|
+
check.isinstance(self.k, InjectorKey)
|
5323
|
+
|
5324
|
+
|
5325
|
+
def bind_injector_scope_seed(k: ta.Any, sc: ta.Type[InjectorScope]) -> InjectorBindingOrBindings:
|
5326
|
+
kk = as_injector_key(k)
|
5327
|
+
return as_injector_bindings(
|
5328
|
+
InjectorBinding(kk, _ScopeSeedInjectorProvider(kk, sc)),
|
5329
|
+
InjectorBinder.bind(_InjectorScopeSeed(sc, kk), array=True),
|
5330
|
+
)
|
5169
5331
|
|
5170
5332
|
|
5171
5333
|
###
|
@@ -5312,13 +5474,21 @@ _INJECTOR_EAGER_ARRAY_KEY: InjectorKey[_InjectorEager] = InjectorKey(_InjectorEa
|
|
5312
5474
|
|
5313
5475
|
|
5314
5476
|
class _Injector(Injector):
|
5477
|
+
_DEFAULT_BINDINGS: ta.ClassVar[ta.List[InjectorBinding]] = []
|
5478
|
+
|
5315
5479
|
def __init__(self, bs: InjectorBindings, p: ta.Optional[Injector] = None) -> None:
|
5316
5480
|
super().__init__()
|
5317
5481
|
|
5318
5482
|
self._bs = check.isinstance(bs, InjectorBindings)
|
5319
5483
|
self._p: ta.Optional[Injector] = check.isinstance(p, (Injector, type(None)))
|
5320
5484
|
|
5321
|
-
self._pfm = {
|
5485
|
+
self._pfm = {
|
5486
|
+
k: v.provider_fn()
|
5487
|
+
for k, v in build_injector_provider_map(as_injector_bindings(
|
5488
|
+
*self._DEFAULT_BINDINGS,
|
5489
|
+
bs,
|
5490
|
+
)).items()
|
5491
|
+
}
|
5322
5492
|
|
5323
5493
|
if _INJECTOR_INJECTOR_KEY in self._pfm:
|
5324
5494
|
raise DuplicateInjectorKeyError(_INJECTOR_INJECTOR_KEY)
|
@@ -5485,6 +5655,7 @@ class InjectorBinder:
|
|
5485
5655
|
to_const: ta.Any = None,
|
5486
5656
|
to_key: ta.Any = None,
|
5487
5657
|
|
5658
|
+
in_: ta.Optional[ta.Type[InjectorScope]] = None,
|
5488
5659
|
singleton: bool = False,
|
5489
5660
|
|
5490
5661
|
eager: bool = False,
|
@@ -5494,12 +5665,12 @@ class InjectorBinder:
|
|
5494
5665
|
if isinstance(obj, cls._BANNED_BIND_TYPES):
|
5495
5666
|
raise TypeError(obj)
|
5496
5667
|
|
5497
|
-
|
5668
|
+
#
|
5498
5669
|
|
5499
5670
|
if key is not None:
|
5500
5671
|
key = as_injector_key(key)
|
5501
5672
|
|
5502
|
-
|
5673
|
+
#
|
5503
5674
|
|
5504
5675
|
has_to = (
|
5505
5676
|
to_fn is not None or
|
@@ -5529,7 +5700,7 @@ class InjectorBinder:
|
|
5529
5700
|
key = InjectorKey(type(obj))
|
5530
5701
|
del has_to
|
5531
5702
|
|
5532
|
-
|
5703
|
+
#
|
5533
5704
|
|
5534
5705
|
if tag is not None:
|
5535
5706
|
if key.tag is not None:
|
@@ -5539,7 +5710,7 @@ class InjectorBinder:
|
|
5539
5710
|
if array is not None:
|
5540
5711
|
key = dc.replace(key, array=array)
|
5541
5712
|
|
5542
|
-
|
5713
|
+
#
|
5543
5714
|
|
5544
5715
|
providers: ta.List[InjectorProvider] = []
|
5545
5716
|
if to_fn is not None:
|
@@ -5554,23 +5725,34 @@ class InjectorBinder:
|
|
5554
5725
|
raise TypeError('Must specify provider')
|
5555
5726
|
if len(providers) > 1:
|
5556
5727
|
raise TypeError('May not specify multiple providers')
|
5557
|
-
provider
|
5728
|
+
provider = check.single(providers)
|
5558
5729
|
|
5559
|
-
|
5730
|
+
#
|
5560
5731
|
|
5732
|
+
pws: ta.List[ta.Any] = []
|
5733
|
+
if in_ is not None:
|
5734
|
+
check.issubclass(in_, InjectorScope)
|
5735
|
+
check.not_in(abc.ABC, in_.__bases__)
|
5736
|
+
pws.append(functools.partial(ScopedInjectorProvider, k=key, sc=in_))
|
5561
5737
|
if singleton:
|
5562
|
-
|
5738
|
+
pws.append(SingletonInjectorProvider)
|
5739
|
+
if len(pws) > 1:
|
5740
|
+
raise TypeError('May not specify multiple provider wrappers')
|
5741
|
+
elif pws:
|
5742
|
+
provider = check.single(pws)(provider)
|
5743
|
+
|
5744
|
+
#
|
5563
5745
|
|
5564
5746
|
binding = InjectorBinding(key, provider)
|
5565
5747
|
|
5566
|
-
|
5748
|
+
#
|
5567
5749
|
|
5568
5750
|
extras: ta.List[InjectorBinding] = []
|
5569
5751
|
|
5570
5752
|
if eager:
|
5571
5753
|
extras.append(bind_injector_eager_key(key))
|
5572
5754
|
|
5573
|
-
|
5755
|
+
#
|
5574
5756
|
|
5575
5757
|
if extras:
|
5576
5758
|
return as_injector_bindings(binding, *extras)
|
@@ -5643,7 +5825,8 @@ def bind_injector_eager_key(key: ta.Any) -> InjectorBinding:
|
|
5643
5825
|
return InjectorBinding(_INJECTOR_EAGER_ARRAY_KEY, ConstInjectorProvider(_InjectorEager(as_injector_key(key))))
|
5644
5826
|
|
5645
5827
|
|
5646
|
-
|
5828
|
+
###
|
5829
|
+
# api
|
5647
5830
|
|
5648
5831
|
|
5649
5832
|
class InjectionApi:
|
@@ -5663,9 +5846,19 @@ class InjectionApi:
|
|
5663
5846
|
def as_bindings(self, *args: InjectorBindingOrBindings) -> InjectorBindings:
|
5664
5847
|
return as_injector_bindings(*args)
|
5665
5848
|
|
5849
|
+
# overrides
|
5850
|
+
|
5666
5851
|
def override(self, p: InjectorBindings, *args: InjectorBindingOrBindings) -> InjectorBindings:
|
5667
5852
|
return injector_override(p, *args)
|
5668
5853
|
|
5854
|
+
# scopes
|
5855
|
+
|
5856
|
+
def bind_scope(self, sc: ta.Type[InjectorScope]) -> InjectorBindingOrBindings:
|
5857
|
+
return bind_injector_scope(sc)
|
5858
|
+
|
5859
|
+
def bind_scope_seed(self, k: ta.Any, sc: ta.Type[InjectorScope]) -> InjectorBindingOrBindings:
|
5860
|
+
return bind_injector_scope_seed(k, sc)
|
5861
|
+
|
5669
5862
|
# injector
|
5670
5863
|
|
5671
5864
|
def create_injector(self, *args: InjectorBindingOrBindings, parent: ta.Optional[Injector] = None) -> Injector:
|
@@ -5686,6 +5879,7 @@ class InjectionApi:
|
|
5686
5879
|
to_const: ta.Any = None,
|
5687
5880
|
to_key: ta.Any = None,
|
5688
5881
|
|
5882
|
+
in_: ta.Optional[ta.Type[InjectorScope]] = None,
|
5689
5883
|
singleton: bool = False,
|
5690
5884
|
|
5691
5885
|
eager: bool = False,
|
@@ -5702,6 +5896,7 @@ class InjectionApi:
|
|
5702
5896
|
to_const=to_const,
|
5703
5897
|
to_key=to_key,
|
5704
5898
|
|
5899
|
+
in_=in_,
|
5705
5900
|
singleton=singleton,
|
5706
5901
|
|
5707
5902
|
eager=eager,
|
@@ -6511,6 +6706,9 @@ class TempDirAtomicPathSwapping(AtomicPathSwapping):
|
|
6511
6706
|
# ../../../omdev/interp/types.py
|
6512
6707
|
|
6513
6708
|
|
6709
|
+
##
|
6710
|
+
|
6711
|
+
|
6514
6712
|
# See https://peps.python.org/pep-3149/
|
6515
6713
|
INTERP_OPT_GLYPHS_BY_ATTR: ta.Mapping[str, str] = collections.OrderedDict([
|
6516
6714
|
('debug', 'd'),
|
@@ -6542,6 +6740,9 @@ class InterpOpts:
|
|
6542
6740
|
return s, cls(**kw)
|
6543
6741
|
|
6544
6742
|
|
6743
|
+
##
|
6744
|
+
|
6745
|
+
|
6545
6746
|
@dc.dataclass(frozen=True)
|
6546
6747
|
class InterpVersion:
|
6547
6748
|
version: Version
|
@@ -6567,6 +6768,9 @@ class InterpVersion:
|
|
6567
6768
|
return None
|
6568
6769
|
|
6569
6770
|
|
6771
|
+
##
|
6772
|
+
|
6773
|
+
|
6570
6774
|
@dc.dataclass(frozen=True)
|
6571
6775
|
class InterpSpecifier:
|
6572
6776
|
specifier: Specifier
|
@@ -6594,12 +6798,25 @@ class InterpSpecifier:
|
|
6594
6798
|
return self.contains(iv)
|
6595
6799
|
|
6596
6800
|
|
6801
|
+
##
|
6802
|
+
|
6803
|
+
|
6597
6804
|
@dc.dataclass(frozen=True)
|
6598
6805
|
class Interp:
|
6599
6806
|
exe: str
|
6600
6807
|
version: InterpVersion
|
6601
6808
|
|
6602
6809
|
|
6810
|
+
########################################
|
6811
|
+
# ../../../omdev/interp/uv/inject.py
|
6812
|
+
|
6813
|
+
|
6814
|
+
def bind_interp_uv() -> InjectorBindings:
|
6815
|
+
lst: ta.List[InjectorBindingOrBindings] = []
|
6816
|
+
|
6817
|
+
return inj.as_bindings(*lst)
|
6818
|
+
|
6819
|
+
|
6603
6820
|
########################################
|
6604
6821
|
# ../../configs.py
|
6605
6822
|
|
@@ -7513,6 +7730,50 @@ class AbstractAsyncSubprocesses(BaseSubprocesses):
|
|
7513
7730
|
return ret.decode().strip()
|
7514
7731
|
|
7515
7732
|
|
7733
|
+
########################################
|
7734
|
+
# ../../../omdev/interp/providers/base.py
|
7735
|
+
"""
|
7736
|
+
TODO:
|
7737
|
+
- backends
|
7738
|
+
- local builds
|
7739
|
+
- deadsnakes?
|
7740
|
+
- uv
|
7741
|
+
- loose versions
|
7742
|
+
"""
|
7743
|
+
|
7744
|
+
|
7745
|
+
##
|
7746
|
+
|
7747
|
+
|
7748
|
+
class InterpProvider(abc.ABC):
|
7749
|
+
name: ta.ClassVar[str]
|
7750
|
+
|
7751
|
+
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
7752
|
+
super().__init_subclass__(**kwargs)
|
7753
|
+
if abc.ABC not in cls.__bases__ and 'name' not in cls.__dict__:
|
7754
|
+
sfx = 'InterpProvider'
|
7755
|
+
if not cls.__name__.endswith(sfx):
|
7756
|
+
raise NameError(cls)
|
7757
|
+
setattr(cls, 'name', snake_case(cls.__name__[:-len(sfx)]))
|
7758
|
+
|
7759
|
+
@abc.abstractmethod
|
7760
|
+
def get_installed_versions(self, spec: InterpSpecifier) -> ta.Awaitable[ta.Sequence[InterpVersion]]:
|
7761
|
+
raise NotImplementedError
|
7762
|
+
|
7763
|
+
@abc.abstractmethod
|
7764
|
+
def get_installed_version(self, version: InterpVersion) -> ta.Awaitable[Interp]:
|
7765
|
+
raise NotImplementedError
|
7766
|
+
|
7767
|
+
async def get_installable_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
|
7768
|
+
return []
|
7769
|
+
|
7770
|
+
async def install_version(self, version: InterpVersion) -> Interp:
|
7771
|
+
raise TypeError
|
7772
|
+
|
7773
|
+
|
7774
|
+
InterpProviders = ta.NewType('InterpProviders', ta.Sequence[InterpProvider])
|
7775
|
+
|
7776
|
+
|
7516
7777
|
########################################
|
7517
7778
|
# ../bootstrap.py
|
7518
7779
|
|
@@ -7547,6 +7808,37 @@ class LocalCommandExecutor(CommandExecutor):
|
|
7547
7808
|
return await ce.execute(cmd)
|
7548
7809
|
|
7549
7810
|
|
7811
|
+
########################################
|
7812
|
+
# ../deploy/deploy.py
|
7813
|
+
|
7814
|
+
|
7815
|
+
DEPLOY_TAG_DATETIME_FMT = '%Y%m%dT%H%M%SZ'
|
7816
|
+
|
7817
|
+
|
7818
|
+
DeployManagerUtcClock = ta.NewType('DeployManagerUtcClock', Func0[datetime.datetime])
|
7819
|
+
|
7820
|
+
|
7821
|
+
class DeployManager:
|
7822
|
+
def __init__(
|
7823
|
+
self,
|
7824
|
+
*,
|
7825
|
+
|
7826
|
+
utc_clock: ta.Optional[DeployManagerUtcClock] = None,
|
7827
|
+
):
|
7828
|
+
super().__init__()
|
7829
|
+
|
7830
|
+
self._utc_clock = utc_clock
|
7831
|
+
|
7832
|
+
def _utc_now(self) -> datetime.datetime:
|
7833
|
+
if self._utc_clock is not None:
|
7834
|
+
return self._utc_clock() # noqa
|
7835
|
+
else:
|
7836
|
+
return datetime.datetime.now(tz=datetime.timezone.utc) # noqa
|
7837
|
+
|
7838
|
+
def make_deploy_time(self) -> DeployTime:
|
7839
|
+
return DeployTime(self._utc_now().strftime(DEPLOY_TAG_DATETIME_FMT))
|
7840
|
+
|
7841
|
+
|
7550
7842
|
########################################
|
7551
7843
|
# ../deploy/paths/paths.py
|
7552
7844
|
"""
|
@@ -8616,25 +8908,110 @@ class InterpInspector:
|
|
8616
8908
|
return ret
|
8617
8909
|
|
8618
8910
|
|
8619
|
-
INTERP_INSPECTOR = InterpInspector()
|
8620
|
-
|
8621
|
-
|
8622
8911
|
########################################
|
8623
|
-
#
|
8624
|
-
|
8625
|
-
|
8626
|
-
##
|
8912
|
+
# ../../../omdev/interp/resolvers.py
|
8627
8913
|
|
8628
8914
|
|
8629
8915
|
@dc.dataclass(frozen=True)
|
8630
|
-
class
|
8631
|
-
|
8916
|
+
class InterpResolverProviders:
|
8917
|
+
providers: ta.Sequence[ta.Tuple[str, InterpProvider]]
|
8632
8918
|
|
8633
|
-
shell: bool = False
|
8634
|
-
cwd: ta.Optional[str] = None
|
8635
|
-
env: ta.Optional[ta.Mapping[str, str]] = None
|
8636
8919
|
|
8637
|
-
|
8920
|
+
class InterpResolver:
|
8921
|
+
def __init__(
|
8922
|
+
self,
|
8923
|
+
providers: InterpResolverProviders,
|
8924
|
+
) -> None:
|
8925
|
+
super().__init__()
|
8926
|
+
|
8927
|
+
self._providers: ta.Mapping[str, InterpProvider] = collections.OrderedDict(providers.providers)
|
8928
|
+
|
8929
|
+
async def _resolve_installed(self, spec: InterpSpecifier) -> ta.Optional[ta.Tuple[InterpProvider, InterpVersion]]:
|
8930
|
+
lst = [
|
8931
|
+
(i, si)
|
8932
|
+
for i, p in enumerate(self._providers.values())
|
8933
|
+
for si in await p.get_installed_versions(spec)
|
8934
|
+
if spec.contains(si)
|
8935
|
+
]
|
8936
|
+
|
8937
|
+
slst = sorted(lst, key=lambda t: (-t[0], t[1].version))
|
8938
|
+
if not slst:
|
8939
|
+
return None
|
8940
|
+
|
8941
|
+
bi, bv = slst[-1]
|
8942
|
+
bp = list(self._providers.values())[bi]
|
8943
|
+
return (bp, bv)
|
8944
|
+
|
8945
|
+
async def resolve(
|
8946
|
+
self,
|
8947
|
+
spec: InterpSpecifier,
|
8948
|
+
*,
|
8949
|
+
install: bool = False,
|
8950
|
+
) -> ta.Optional[Interp]:
|
8951
|
+
tup = await self._resolve_installed(spec)
|
8952
|
+
if tup is not None:
|
8953
|
+
bp, bv = tup
|
8954
|
+
return await bp.get_installed_version(bv)
|
8955
|
+
|
8956
|
+
if not install:
|
8957
|
+
return None
|
8958
|
+
|
8959
|
+
tp = list(self._providers.values())[0] # noqa
|
8960
|
+
|
8961
|
+
sv = sorted(
|
8962
|
+
[s for s in await tp.get_installable_versions(spec) if s in spec],
|
8963
|
+
key=lambda s: s.version,
|
8964
|
+
)
|
8965
|
+
if not sv:
|
8966
|
+
return None
|
8967
|
+
|
8968
|
+
bv = sv[-1]
|
8969
|
+
return await tp.install_version(bv)
|
8970
|
+
|
8971
|
+
async def list(self, spec: InterpSpecifier) -> None:
|
8972
|
+
print('installed:')
|
8973
|
+
for n, p in self._providers.items():
|
8974
|
+
lst = [
|
8975
|
+
si
|
8976
|
+
for si in await p.get_installed_versions(spec)
|
8977
|
+
if spec.contains(si)
|
8978
|
+
]
|
8979
|
+
if lst:
|
8980
|
+
print(f' {n}')
|
8981
|
+
for si in lst:
|
8982
|
+
print(f' {si}')
|
8983
|
+
|
8984
|
+
print()
|
8985
|
+
|
8986
|
+
print('installable:')
|
8987
|
+
for n, p in self._providers.items():
|
8988
|
+
lst = [
|
8989
|
+
si
|
8990
|
+
for si in await p.get_installable_versions(spec)
|
8991
|
+
if spec.contains(si)
|
8992
|
+
]
|
8993
|
+
if lst:
|
8994
|
+
print(f' {n}')
|
8995
|
+
for si in lst:
|
8996
|
+
print(f' {si}')
|
8997
|
+
|
8998
|
+
|
8999
|
+
########################################
|
9000
|
+
# ../commands/subprocess.py
|
9001
|
+
|
9002
|
+
|
9003
|
+
##
|
9004
|
+
|
9005
|
+
|
9006
|
+
@dc.dataclass(frozen=True)
|
9007
|
+
class SubprocessCommand(Command['SubprocessCommand.Output']):
|
9008
|
+
cmd: ta.Sequence[str]
|
9009
|
+
|
9010
|
+
shell: bool = False
|
9011
|
+
cwd: ta.Optional[str] = None
|
9012
|
+
env: ta.Optional[ta.Mapping[str, str]] = None
|
9013
|
+
|
9014
|
+
stdout: str = 'pipe' # SubprocessChannelOption
|
8638
9015
|
stderr: str = 'pipe' # SubprocessChannelOption
|
8639
9016
|
|
8640
9017
|
input: ta.Optional[bytes] = None
|
@@ -9270,47 +9647,7 @@ class YumSystemPackageManager(SystemPackageManager):
|
|
9270
9647
|
|
9271
9648
|
|
9272
9649
|
########################################
|
9273
|
-
# ../../../omdev/interp/providers.py
|
9274
|
-
"""
|
9275
|
-
TODO:
|
9276
|
-
- backends
|
9277
|
-
- local builds
|
9278
|
-
- deadsnakes?
|
9279
|
-
- uv
|
9280
|
-
- loose versions
|
9281
|
-
"""
|
9282
|
-
|
9283
|
-
|
9284
|
-
##
|
9285
|
-
|
9286
|
-
|
9287
|
-
class InterpProvider(abc.ABC):
|
9288
|
-
name: ta.ClassVar[str]
|
9289
|
-
|
9290
|
-
def __init_subclass__(cls, **kwargs: ta.Any) -> None:
|
9291
|
-
super().__init_subclass__(**kwargs)
|
9292
|
-
if abc.ABC not in cls.__bases__ and 'name' not in cls.__dict__:
|
9293
|
-
sfx = 'InterpProvider'
|
9294
|
-
if not cls.__name__.endswith(sfx):
|
9295
|
-
raise NameError(cls)
|
9296
|
-
setattr(cls, 'name', snake_case(cls.__name__[:-len(sfx)]))
|
9297
|
-
|
9298
|
-
@abc.abstractmethod
|
9299
|
-
def get_installed_versions(self, spec: InterpSpecifier) -> ta.Awaitable[ta.Sequence[InterpVersion]]:
|
9300
|
-
raise NotImplementedError
|
9301
|
-
|
9302
|
-
@abc.abstractmethod
|
9303
|
-
def get_installed_version(self, version: InterpVersion) -> ta.Awaitable[Interp]:
|
9304
|
-
raise NotImplementedError
|
9305
|
-
|
9306
|
-
async def get_installable_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
|
9307
|
-
return []
|
9308
|
-
|
9309
|
-
async def install_version(self, version: InterpVersion) -> Interp:
|
9310
|
-
raise TypeError
|
9311
|
-
|
9312
|
-
|
9313
|
-
##
|
9650
|
+
# ../../../omdev/interp/providers/running.py
|
9314
9651
|
|
9315
9652
|
|
9316
9653
|
class RunningInterpProvider(InterpProvider):
|
@@ -9331,314 +9668,132 @@ class RunningInterpProvider(InterpProvider):
|
|
9331
9668
|
|
9332
9669
|
|
9333
9670
|
########################################
|
9334
|
-
#
|
9671
|
+
# ../../../omdev/interp/providers/system.py
|
9672
|
+
"""
|
9673
|
+
TODO:
|
9674
|
+
- python, python3, python3.12, ...
|
9675
|
+
- check if path py's are venvs: sys.prefix != sys.base_prefix
|
9676
|
+
"""
|
9335
9677
|
|
9336
9678
|
|
9337
9679
|
##
|
9338
9680
|
|
9339
9681
|
|
9340
|
-
|
9341
|
-
|
9342
|
-
|
9343
|
-
|
9344
|
-
|
9345
|
-
inj.bind(CommandRegistration(command_cls), array=True),
|
9346
|
-
]
|
9347
|
-
|
9348
|
-
if executor_cls is not None:
|
9349
|
-
lst.extend([
|
9350
|
-
inj.bind(executor_cls, singleton=True),
|
9351
|
-
inj.bind(CommandExecutorRegistration(command_cls, executor_cls), array=True),
|
9352
|
-
])
|
9682
|
+
class SystemInterpProvider(InterpProvider):
|
9683
|
+
@dc.dataclass(frozen=True)
|
9684
|
+
class Options:
|
9685
|
+
cmd: str = 'python3' # FIXME: unused lol
|
9686
|
+
path: ta.Optional[str] = None
|
9353
9687
|
|
9354
|
-
|
9688
|
+
inspect: bool = False
|
9355
9689
|
|
9690
|
+
def __init__(
|
9691
|
+
self,
|
9692
|
+
options: Options = Options(),
|
9693
|
+
*,
|
9694
|
+
inspector: ta.Optional[InterpInspector] = None,
|
9695
|
+
) -> None:
|
9696
|
+
super().__init__()
|
9356
9697
|
|
9357
|
-
|
9698
|
+
self._options = options
|
9358
9699
|
|
9700
|
+
self._inspector = inspector
|
9359
9701
|
|
9360
|
-
|
9361
|
-
class _FactoryCommandExecutor(CommandExecutor):
|
9362
|
-
factory: ta.Callable[[], CommandExecutor]
|
9702
|
+
#
|
9363
9703
|
|
9364
|
-
|
9365
|
-
|
9704
|
+
@staticmethod
|
9705
|
+
def _re_which(
|
9706
|
+
pat: re.Pattern,
|
9707
|
+
*,
|
9708
|
+
mode: int = os.F_OK | os.X_OK,
|
9709
|
+
path: ta.Optional[str] = None,
|
9710
|
+
) -> ta.List[str]:
|
9711
|
+
if path is None:
|
9712
|
+
path = os.environ.get('PATH', None)
|
9713
|
+
if path is None:
|
9714
|
+
try:
|
9715
|
+
path = os.confstr('CS_PATH')
|
9716
|
+
except (AttributeError, ValueError):
|
9717
|
+
path = os.defpath
|
9366
9718
|
|
9719
|
+
if not path:
|
9720
|
+
return []
|
9367
9721
|
|
9368
|
-
|
9722
|
+
path = os.fsdecode(path)
|
9723
|
+
pathlst = path.split(os.pathsep)
|
9369
9724
|
|
9725
|
+
def _access_check(fn: str, mode: int) -> bool:
|
9726
|
+
return os.path.exists(fn) and os.access(fn, mode)
|
9370
9727
|
|
9371
|
-
|
9372
|
-
|
9373
|
-
|
9374
|
-
|
9375
|
-
|
9376
|
-
|
9377
|
-
|
9728
|
+
out = []
|
9729
|
+
seen = set()
|
9730
|
+
for d in pathlst:
|
9731
|
+
normdir = os.path.normcase(d)
|
9732
|
+
if normdir not in seen:
|
9733
|
+
seen.add(normdir)
|
9734
|
+
if not _access_check(normdir, mode):
|
9735
|
+
continue
|
9736
|
+
for thefile in os.listdir(d):
|
9737
|
+
name = os.path.join(d, thefile)
|
9738
|
+
if not (
|
9739
|
+
os.path.isfile(name) and
|
9740
|
+
pat.fullmatch(thefile) and
|
9741
|
+
_access_check(name, mode)
|
9742
|
+
):
|
9743
|
+
continue
|
9744
|
+
out.append(name)
|
9378
9745
|
|
9379
|
-
|
9380
|
-
inj.bind_array_type(CommandExecutorRegistration, CommandExecutorRegistrations),
|
9746
|
+
return out
|
9381
9747
|
|
9382
|
-
|
9383
|
-
]
|
9748
|
+
@cached_nullary
|
9749
|
+
def exes(self) -> ta.List[str]:
|
9750
|
+
return self._re_which(
|
9751
|
+
re.compile(r'python3(\.\d+)?'),
|
9752
|
+
path=self._options.path,
|
9753
|
+
)
|
9384
9754
|
|
9385
9755
|
#
|
9386
9756
|
|
9387
|
-
def
|
9388
|
-
|
9757
|
+
async def get_exe_version(self, exe: str) -> ta.Optional[InterpVersion]:
|
9758
|
+
if not self._options.inspect:
|
9759
|
+
s = os.path.basename(exe)
|
9760
|
+
if s.startswith('python'):
|
9761
|
+
s = s[len('python'):]
|
9762
|
+
if '.' in s:
|
9763
|
+
try:
|
9764
|
+
return InterpVersion.parse(s)
|
9765
|
+
except InvalidVersion:
|
9766
|
+
pass
|
9767
|
+
ii = await check.not_none(self._inspector).inspect(exe)
|
9768
|
+
return ii.iv if ii is not None else None
|
9389
9769
|
|
9390
|
-
|
9770
|
+
async def exe_versions(self) -> ta.Sequence[ta.Tuple[str, InterpVersion]]:
|
9771
|
+
lst = []
|
9772
|
+
for e in self.exes():
|
9773
|
+
if (ev := await self.get_exe_version(e)) is None:
|
9774
|
+
log.debug('Invalid system version: %s', e)
|
9775
|
+
continue
|
9776
|
+
lst.append((e, ev))
|
9777
|
+
return lst
|
9391
9778
|
|
9392
9779
|
#
|
9393
9780
|
|
9394
|
-
def
|
9395
|
-
|
9396
|
-
crs: CommandExecutorRegistrations,
|
9397
|
-
) -> CommandExecutorMap:
|
9398
|
-
dct: ta.Dict[ta.Type[Command], CommandExecutor] = {}
|
9781
|
+
async def get_installed_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
|
9782
|
+
return [ev for e, ev in await self.exe_versions()]
|
9399
9783
|
|
9400
|
-
|
9401
|
-
for
|
9402
|
-
if
|
9403
|
-
|
9404
|
-
|
9405
|
-
|
9406
|
-
|
9407
|
-
ce = factory()
|
9408
|
-
else:
|
9409
|
-
ce = _FactoryCommandExecutor(factory)
|
9410
|
-
|
9411
|
-
dct[cr.command_cls] = ce
|
9412
|
-
|
9413
|
-
return CommandExecutorMap(dct)
|
9414
|
-
|
9415
|
-
lst.extend([
|
9416
|
-
inj.bind(provide_command_executor_map, singleton=True),
|
9417
|
-
|
9418
|
-
inj.bind(LocalCommandExecutor, singleton=True, eager=main_config.debug),
|
9419
|
-
])
|
9420
|
-
|
9421
|
-
#
|
9422
|
-
|
9423
|
-
lst.extend([
|
9424
|
-
bind_command(PingCommand, PingCommandExecutor),
|
9425
|
-
bind_command(SubprocessCommand, SubprocessCommandExecutor),
|
9426
|
-
])
|
9427
|
-
|
9428
|
-
#
|
9429
|
-
|
9430
|
-
return inj.as_bindings(*lst)
|
9431
|
-
|
9432
|
-
|
9433
|
-
########################################
|
9434
|
-
# ../deploy/paths/manager.py
|
9435
|
-
|
9436
|
-
|
9437
|
-
class DeployPathsManager:
|
9438
|
-
def __init__(
|
9439
|
-
self,
|
9440
|
-
*,
|
9441
|
-
deploy_path_owners: DeployPathOwners,
|
9442
|
-
) -> None:
|
9443
|
-
super().__init__()
|
9444
|
-
|
9445
|
-
self._deploy_path_owners = deploy_path_owners
|
9446
|
-
|
9447
|
-
@cached_nullary
|
9448
|
-
def owners_by_path(self) -> ta.Mapping[DeployPath, DeployPathOwner]:
|
9449
|
-
dct: ta.Dict[DeployPath, DeployPathOwner] = {}
|
9450
|
-
for o in self._deploy_path_owners:
|
9451
|
-
for p in o.get_owned_deploy_paths():
|
9452
|
-
if p in dct:
|
9453
|
-
raise DeployPathError(f'Duplicate deploy path owner: {p}')
|
9454
|
-
dct[p] = o
|
9455
|
-
return dct
|
9456
|
-
|
9457
|
-
def validate_deploy_paths(self) -> None:
|
9458
|
-
self.owners_by_path()
|
9459
|
-
|
9460
|
-
|
9461
|
-
########################################
|
9462
|
-
# ../deploy/tmp.py
|
9463
|
-
|
9464
|
-
|
9465
|
-
class DeployHomeAtomics(Func1[DeployHome, AtomicPathSwapping]):
|
9466
|
-
pass
|
9467
|
-
|
9468
|
-
|
9469
|
-
class DeployTmpManager(
|
9470
|
-
SingleDirDeployPathOwner,
|
9471
|
-
):
|
9472
|
-
def __init__(self) -> None:
|
9473
|
-
super().__init__(
|
9474
|
-
owned_dir='tmp',
|
9475
|
-
)
|
9476
|
-
|
9477
|
-
def get_swapping(self, home: DeployHome) -> AtomicPathSwapping:
|
9478
|
-
return TempDirAtomicPathSwapping(
|
9479
|
-
temp_dir=self._make_dir(home),
|
9480
|
-
root_dir=check.non_empty_str(home),
|
9481
|
-
)
|
9482
|
-
|
9483
|
-
|
9484
|
-
########################################
|
9485
|
-
# ../remote/connection.py
|
9486
|
-
|
9487
|
-
|
9488
|
-
##
|
9489
|
-
|
9490
|
-
|
9491
|
-
class PyremoteRemoteExecutionConnector:
|
9492
|
-
def __init__(
|
9493
|
-
self,
|
9494
|
-
*,
|
9495
|
-
spawning: RemoteSpawning,
|
9496
|
-
msh: ObjMarshalerManager,
|
9497
|
-
payload_file: ta.Optional[RemoteExecutionPayloadFile] = None,
|
9498
|
-
) -> None:
|
9499
|
-
super().__init__()
|
9500
|
-
|
9501
|
-
self._spawning = spawning
|
9502
|
-
self._msh = msh
|
9503
|
-
self._payload_file = payload_file
|
9504
|
-
|
9505
|
-
#
|
9506
|
-
|
9507
|
-
@cached_nullary
|
9508
|
-
def _payload_src(self) -> str:
|
9509
|
-
return get_remote_payload_src(file=self._payload_file)
|
9510
|
-
|
9511
|
-
@cached_nullary
|
9512
|
-
def _remote_src(self) -> ta.Sequence[str]:
|
9513
|
-
return [
|
9514
|
-
self._payload_src(),
|
9515
|
-
'_remote_execution_main()',
|
9516
|
-
]
|
9517
|
-
|
9518
|
-
@cached_nullary
|
9519
|
-
def _spawn_src(self) -> str:
|
9520
|
-
return pyremote_build_bootstrap_cmd(__package__ or 'manage')
|
9521
|
-
|
9522
|
-
#
|
9523
|
-
|
9524
|
-
@contextlib.asynccontextmanager
|
9525
|
-
async def connect(
|
9526
|
-
self,
|
9527
|
-
tgt: RemoteSpawning.Target,
|
9528
|
-
bs: MainBootstrap,
|
9529
|
-
) -> ta.AsyncGenerator[RemoteCommandExecutor, None]:
|
9530
|
-
spawn_src = self._spawn_src()
|
9531
|
-
remote_src = self._remote_src()
|
9532
|
-
|
9533
|
-
async with self._spawning.spawn(
|
9534
|
-
tgt,
|
9535
|
-
spawn_src,
|
9536
|
-
debug=bs.main_config.debug,
|
9537
|
-
) as proc:
|
9538
|
-
res = await PyremoteBootstrapDriver( # noqa
|
9539
|
-
remote_src,
|
9540
|
-
PyremoteBootstrapOptions(
|
9541
|
-
debug=bs.main_config.debug,
|
9542
|
-
),
|
9543
|
-
).async_run(
|
9544
|
-
proc.stdout,
|
9545
|
-
proc.stdin,
|
9546
|
-
)
|
9547
|
-
|
9548
|
-
chan = RemoteChannelImpl(
|
9549
|
-
proc.stdout,
|
9550
|
-
proc.stdin,
|
9551
|
-
msh=self._msh,
|
9784
|
+
async def get_installed_version(self, version: InterpVersion) -> Interp:
|
9785
|
+
for e, ev in await self.exe_versions():
|
9786
|
+
if ev != version:
|
9787
|
+
continue
|
9788
|
+
return Interp(
|
9789
|
+
exe=e,
|
9790
|
+
version=ev,
|
9552
9791
|
)
|
9553
|
-
|
9554
|
-
await chan.send_obj(bs)
|
9555
|
-
|
9556
|
-
rce: RemoteCommandExecutor
|
9557
|
-
async with aclosing(RemoteCommandExecutor(chan)) as rce:
|
9558
|
-
await rce.start()
|
9559
|
-
|
9560
|
-
yield rce
|
9561
|
-
|
9562
|
-
|
9563
|
-
##
|
9564
|
-
|
9565
|
-
|
9566
|
-
class InProcessRemoteExecutionConnector:
|
9567
|
-
def __init__(
|
9568
|
-
self,
|
9569
|
-
*,
|
9570
|
-
msh: ObjMarshalerManager,
|
9571
|
-
local_executor: LocalCommandExecutor,
|
9572
|
-
) -> None:
|
9573
|
-
super().__init__()
|
9574
|
-
|
9575
|
-
self._msh = msh
|
9576
|
-
self._local_executor = local_executor
|
9577
|
-
|
9578
|
-
@contextlib.asynccontextmanager
|
9579
|
-
async def connect(self) -> ta.AsyncGenerator[RemoteCommandExecutor, None]:
|
9580
|
-
r0, w0 = asyncio_create_bytes_channel()
|
9581
|
-
r1, w1 = asyncio_create_bytes_channel()
|
9582
|
-
|
9583
|
-
remote_chan = RemoteChannelImpl(r0, w1, msh=self._msh)
|
9584
|
-
local_chan = RemoteChannelImpl(r1, w0, msh=self._msh)
|
9585
|
-
|
9586
|
-
rch = _RemoteCommandHandler(
|
9587
|
-
remote_chan,
|
9588
|
-
self._local_executor,
|
9589
|
-
)
|
9590
|
-
rch_task = asyncio.create_task(rch.run()) # noqa
|
9591
|
-
try:
|
9592
|
-
rce: RemoteCommandExecutor
|
9593
|
-
async with aclosing(RemoteCommandExecutor(local_chan)) as rce:
|
9594
|
-
await rce.start()
|
9595
|
-
|
9596
|
-
yield rce
|
9597
|
-
|
9598
|
-
finally:
|
9599
|
-
rch.stop()
|
9600
|
-
await rch_task
|
9601
|
-
|
9602
|
-
|
9603
|
-
########################################
|
9604
|
-
# ../system/commands.py
|
9605
|
-
|
9606
|
-
|
9607
|
-
##
|
9608
|
-
|
9609
|
-
|
9610
|
-
@dc.dataclass(frozen=True)
|
9611
|
-
class CheckSystemPackageCommand(Command['CheckSystemPackageCommand.Output']):
|
9612
|
-
pkgs: ta.Sequence[str] = ()
|
9613
|
-
|
9614
|
-
def __post_init__(self) -> None:
|
9615
|
-
check.not_isinstance(self.pkgs, str)
|
9616
|
-
|
9617
|
-
@dc.dataclass(frozen=True)
|
9618
|
-
class Output(Command.Output):
|
9619
|
-
pkgs: ta.Sequence[SystemPackage]
|
9620
|
-
|
9621
|
-
|
9622
|
-
class CheckSystemPackageCommandExecutor(CommandExecutor[CheckSystemPackageCommand, CheckSystemPackageCommand.Output]):
|
9623
|
-
def __init__(
|
9624
|
-
self,
|
9625
|
-
*,
|
9626
|
-
mgr: SystemPackageManager,
|
9627
|
-
) -> None:
|
9628
|
-
super().__init__()
|
9629
|
-
|
9630
|
-
self._mgr = mgr
|
9631
|
-
|
9632
|
-
async def execute(self, cmd: CheckSystemPackageCommand) -> CheckSystemPackageCommand.Output:
|
9633
|
-
log.info('Checking system package!')
|
9634
|
-
|
9635
|
-
ret = await self._mgr.query(*cmd.pkgs)
|
9636
|
-
|
9637
|
-
return CheckSystemPackageCommand.Output(list(ret.values()))
|
9792
|
+
raise KeyError(version)
|
9638
9793
|
|
9639
9794
|
|
9640
9795
|
########################################
|
9641
|
-
# ../../../omdev/interp/pyenv.py
|
9796
|
+
# ../../../omdev/interp/pyenv/pyenv.py
|
9642
9797
|
"""
|
9643
9798
|
TODO:
|
9644
9799
|
- custom tags
|
@@ -9868,9 +10023,10 @@ class PyenvVersionInstaller:
|
|
9868
10023
|
opts: ta.Optional[PyenvInstallOpts] = None,
|
9869
10024
|
interp_opts: InterpOpts = InterpOpts(),
|
9870
10025
|
*,
|
10026
|
+
pyenv: Pyenv,
|
10027
|
+
|
9871
10028
|
install_name: ta.Optional[str] = None,
|
9872
10029
|
no_default_opts: bool = False,
|
9873
|
-
pyenv: Pyenv = Pyenv(),
|
9874
10030
|
) -> None:
|
9875
10031
|
super().__init__()
|
9876
10032
|
|
@@ -9960,26 +10116,26 @@ class PyenvVersionInstaller:
|
|
9960
10116
|
|
9961
10117
|
|
9962
10118
|
class PyenvInterpProvider(InterpProvider):
|
9963
|
-
|
9964
|
-
|
9965
|
-
|
10119
|
+
@dc.dataclass(frozen=True)
|
10120
|
+
class Options:
|
10121
|
+
inspect: bool = False
|
9966
10122
|
|
9967
|
-
|
9968
|
-
inspector: InterpInspector = INTERP_INSPECTOR,
|
10123
|
+
try_update: bool = False
|
9969
10124
|
|
10125
|
+
def __init__(
|
10126
|
+
self,
|
10127
|
+
options: Options = Options(),
|
9970
10128
|
*,
|
9971
|
-
|
9972
|
-
|
10129
|
+
pyenv: Pyenv,
|
10130
|
+
inspector: InterpInspector,
|
9973
10131
|
) -> None:
|
9974
10132
|
super().__init__()
|
9975
10133
|
|
9976
|
-
self.
|
10134
|
+
self._options = options
|
9977
10135
|
|
9978
|
-
self.
|
10136
|
+
self._pyenv = pyenv
|
9979
10137
|
self._inspector = inspector
|
9980
10138
|
|
9981
|
-
self._try_update = try_update
|
9982
|
-
|
9983
10139
|
#
|
9984
10140
|
|
9985
10141
|
@staticmethod
|
@@ -10004,7 +10160,7 @@ class PyenvInterpProvider(InterpProvider):
|
|
10004
10160
|
|
10005
10161
|
async def _make_installed(self, vn: str, ep: str) -> ta.Optional[Installed]:
|
10006
10162
|
iv: ta.Optional[InterpVersion]
|
10007
|
-
if self.
|
10163
|
+
if self._options.inspect:
|
10008
10164
|
try:
|
10009
10165
|
iv = check.not_none(await self._inspector.inspect(ep)).iv
|
10010
10166
|
except Exception as e: # noqa
|
@@ -10060,7 +10216,7 @@ class PyenvInterpProvider(InterpProvider):
|
|
10060
10216
|
async def get_installable_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
|
10061
10217
|
lst = await self._get_installable_versions(spec)
|
10062
10218
|
|
10063
|
-
if self.
|
10219
|
+
if self._options.try_update and not any(v in spec for v in lst):
|
10064
10220
|
if self._pyenv.update():
|
10065
10221
|
lst = await self._get_installable_versions(spec)
|
10066
10222
|
|
@@ -10076,6 +10232,7 @@ class PyenvInterpProvider(InterpProvider):
|
|
10076
10232
|
installer = PyenvVersionInstaller(
|
10077
10233
|
inst_version,
|
10078
10234
|
interp_opts=inst_opts,
|
10235
|
+
pyenv=self._pyenv,
|
10079
10236
|
)
|
10080
10237
|
|
10081
10238
|
exe = await installer.install()
|
@@ -10083,116 +10240,344 @@ class PyenvInterpProvider(InterpProvider):
|
|
10083
10240
|
|
10084
10241
|
|
10085
10242
|
########################################
|
10086
|
-
#
|
10087
|
-
|
10088
|
-
|
10089
|
-
|
10090
|
-
|
10091
|
-
|
10243
|
+
# ../commands/inject.py
|
10244
|
+
|
10245
|
+
|
10246
|
+
##
|
10247
|
+
|
10248
|
+
|
10249
|
+
def bind_command(
|
10250
|
+
command_cls: ta.Type[Command],
|
10251
|
+
executor_cls: ta.Optional[ta.Type[CommandExecutor]],
|
10252
|
+
) -> InjectorBindings:
|
10253
|
+
lst: ta.List[InjectorBindingOrBindings] = [
|
10254
|
+
inj.bind(CommandRegistration(command_cls), array=True),
|
10255
|
+
]
|
10256
|
+
|
10257
|
+
if executor_cls is not None:
|
10258
|
+
lst.extend([
|
10259
|
+
inj.bind(executor_cls, singleton=True),
|
10260
|
+
inj.bind(CommandExecutorRegistration(command_cls, executor_cls), array=True),
|
10261
|
+
])
|
10262
|
+
|
10263
|
+
return inj.as_bindings(*lst)
|
10264
|
+
|
10265
|
+
|
10266
|
+
##
|
10267
|
+
|
10268
|
+
|
10269
|
+
@dc.dataclass(frozen=True)
|
10270
|
+
class _FactoryCommandExecutor(CommandExecutor):
|
10271
|
+
factory: ta.Callable[[], CommandExecutor]
|
10272
|
+
|
10273
|
+
def execute(self, i: Command) -> ta.Awaitable[Command.Output]:
|
10274
|
+
return self.factory().execute(i)
|
10275
|
+
|
10276
|
+
|
10277
|
+
##
|
10278
|
+
|
10279
|
+
|
10280
|
+
def bind_commands(
|
10281
|
+
*,
|
10282
|
+
main_config: MainConfig,
|
10283
|
+
) -> InjectorBindings:
|
10284
|
+
lst: ta.List[InjectorBindingOrBindings] = [
|
10285
|
+
inj.bind_array(CommandRegistration),
|
10286
|
+
inj.bind_array_type(CommandRegistration, CommandRegistrations),
|
10287
|
+
|
10288
|
+
inj.bind_array(CommandExecutorRegistration),
|
10289
|
+
inj.bind_array_type(CommandExecutorRegistration, CommandExecutorRegistrations),
|
10290
|
+
|
10291
|
+
inj.bind(build_command_name_map, singleton=True),
|
10292
|
+
]
|
10293
|
+
|
10294
|
+
#
|
10295
|
+
|
10296
|
+
def provide_obj_marshaler_installer(cmds: CommandNameMap) -> ObjMarshalerInstaller:
|
10297
|
+
return ObjMarshalerInstaller(functools.partial(install_command_marshaling, cmds))
|
10298
|
+
|
10299
|
+
lst.append(inj.bind(provide_obj_marshaler_installer, array=True))
|
10300
|
+
|
10301
|
+
#
|
10302
|
+
|
10303
|
+
def provide_command_executor_map(
|
10304
|
+
injector: Injector,
|
10305
|
+
crs: CommandExecutorRegistrations,
|
10306
|
+
) -> CommandExecutorMap:
|
10307
|
+
dct: ta.Dict[ta.Type[Command], CommandExecutor] = {}
|
10308
|
+
|
10309
|
+
cr: CommandExecutorRegistration
|
10310
|
+
for cr in crs:
|
10311
|
+
if cr.command_cls in dct:
|
10312
|
+
raise KeyError(cr.command_cls)
|
10313
|
+
|
10314
|
+
factory = functools.partial(injector.provide, cr.executor_cls)
|
10315
|
+
if main_config.debug:
|
10316
|
+
ce = factory()
|
10317
|
+
else:
|
10318
|
+
ce = _FactoryCommandExecutor(factory)
|
10319
|
+
|
10320
|
+
dct[cr.command_cls] = ce
|
10321
|
+
|
10322
|
+
return CommandExecutorMap(dct)
|
10323
|
+
|
10324
|
+
lst.extend([
|
10325
|
+
inj.bind(provide_command_executor_map, singleton=True),
|
10326
|
+
|
10327
|
+
inj.bind(LocalCommandExecutor, singleton=True, eager=main_config.debug),
|
10328
|
+
])
|
10329
|
+
|
10330
|
+
#
|
10331
|
+
|
10332
|
+
lst.extend([
|
10333
|
+
bind_command(PingCommand, PingCommandExecutor),
|
10334
|
+
bind_command(SubprocessCommand, SubprocessCommandExecutor),
|
10335
|
+
])
|
10336
|
+
|
10337
|
+
#
|
10338
|
+
|
10339
|
+
return inj.as_bindings(*lst)
|
10340
|
+
|
10341
|
+
|
10342
|
+
########################################
|
10343
|
+
# ../deploy/paths/manager.py
|
10344
|
+
|
10345
|
+
|
10346
|
+
class DeployPathsManager:
|
10347
|
+
def __init__(
|
10348
|
+
self,
|
10349
|
+
*,
|
10350
|
+
deploy_path_owners: DeployPathOwners,
|
10351
|
+
) -> None:
|
10352
|
+
super().__init__()
|
10353
|
+
|
10354
|
+
self._deploy_path_owners = deploy_path_owners
|
10355
|
+
|
10356
|
+
@cached_nullary
|
10357
|
+
def owners_by_path(self) -> ta.Mapping[DeployPath, DeployPathOwner]:
|
10358
|
+
dct: ta.Dict[DeployPath, DeployPathOwner] = {}
|
10359
|
+
for o in self._deploy_path_owners:
|
10360
|
+
for p in o.get_owned_deploy_paths():
|
10361
|
+
if p in dct:
|
10362
|
+
raise DeployPathError(f'Duplicate deploy path owner: {p}')
|
10363
|
+
dct[p] = o
|
10364
|
+
return dct
|
10365
|
+
|
10366
|
+
def validate_deploy_paths(self) -> None:
|
10367
|
+
self.owners_by_path()
|
10368
|
+
|
10369
|
+
|
10370
|
+
########################################
|
10371
|
+
# ../deploy/tmp.py
|
10372
|
+
|
10373
|
+
|
10374
|
+
class DeployHomeAtomics(Func1[DeployHome, AtomicPathSwapping]):
|
10375
|
+
pass
|
10376
|
+
|
10377
|
+
|
10378
|
+
class DeployTmpManager(
|
10379
|
+
SingleDirDeployPathOwner,
|
10380
|
+
):
|
10381
|
+
def __init__(self) -> None:
|
10382
|
+
super().__init__(
|
10383
|
+
owned_dir='tmp',
|
10384
|
+
)
|
10385
|
+
|
10386
|
+
def get_swapping(self, home: DeployHome) -> AtomicPathSwapping:
|
10387
|
+
return TempDirAtomicPathSwapping(
|
10388
|
+
temp_dir=self._make_dir(home),
|
10389
|
+
root_dir=check.non_empty_str(home),
|
10390
|
+
)
|
10391
|
+
|
10392
|
+
|
10393
|
+
########################################
|
10394
|
+
# ../remote/connection.py
|
10395
|
+
|
10396
|
+
|
10397
|
+
##
|
10398
|
+
|
10399
|
+
|
10400
|
+
class PyremoteRemoteExecutionConnector:
|
10401
|
+
def __init__(
|
10402
|
+
self,
|
10403
|
+
*,
|
10404
|
+
spawning: RemoteSpawning,
|
10405
|
+
msh: ObjMarshalerManager,
|
10406
|
+
payload_file: ta.Optional[RemoteExecutionPayloadFile] = None,
|
10407
|
+
) -> None:
|
10408
|
+
super().__init__()
|
10409
|
+
|
10410
|
+
self._spawning = spawning
|
10411
|
+
self._msh = msh
|
10412
|
+
self._payload_file = payload_file
|
10413
|
+
|
10414
|
+
#
|
10415
|
+
|
10416
|
+
@cached_nullary
|
10417
|
+
def _payload_src(self) -> str:
|
10418
|
+
return get_remote_payload_src(file=self._payload_file)
|
10419
|
+
|
10420
|
+
@cached_nullary
|
10421
|
+
def _remote_src(self) -> ta.Sequence[str]:
|
10422
|
+
return [
|
10423
|
+
self._payload_src(),
|
10424
|
+
'_remote_execution_main()',
|
10425
|
+
]
|
10426
|
+
|
10427
|
+
@cached_nullary
|
10428
|
+
def _spawn_src(self) -> str:
|
10429
|
+
return pyremote_build_bootstrap_cmd(__package__ or 'manage')
|
10430
|
+
|
10431
|
+
#
|
10432
|
+
|
10433
|
+
@contextlib.asynccontextmanager
|
10434
|
+
async def connect(
|
10435
|
+
self,
|
10436
|
+
tgt: RemoteSpawning.Target,
|
10437
|
+
bs: MainBootstrap,
|
10438
|
+
) -> ta.AsyncGenerator[RemoteCommandExecutor, None]:
|
10439
|
+
spawn_src = self._spawn_src()
|
10440
|
+
remote_src = self._remote_src()
|
10441
|
+
|
10442
|
+
async with self._spawning.spawn(
|
10443
|
+
tgt,
|
10444
|
+
spawn_src,
|
10445
|
+
debug=bs.main_config.debug,
|
10446
|
+
) as proc:
|
10447
|
+
res = await PyremoteBootstrapDriver( # noqa
|
10448
|
+
remote_src,
|
10449
|
+
PyremoteBootstrapOptions(
|
10450
|
+
debug=bs.main_config.debug,
|
10451
|
+
),
|
10452
|
+
).async_run(
|
10453
|
+
proc.stdout,
|
10454
|
+
proc.stdin,
|
10455
|
+
)
|
10456
|
+
|
10457
|
+
chan = RemoteChannelImpl(
|
10458
|
+
proc.stdout,
|
10459
|
+
proc.stdin,
|
10460
|
+
msh=self._msh,
|
10461
|
+
)
|
10462
|
+
|
10463
|
+
await chan.send_obj(bs)
|
10464
|
+
|
10465
|
+
rce: RemoteCommandExecutor
|
10466
|
+
async with aclosing(RemoteCommandExecutor(chan)) as rce:
|
10467
|
+
await rce.start()
|
10468
|
+
|
10469
|
+
yield rce
|
10470
|
+
|
10471
|
+
|
10472
|
+
##
|
10473
|
+
|
10474
|
+
|
10475
|
+
class InProcessRemoteExecutionConnector:
|
10476
|
+
def __init__(
|
10477
|
+
self,
|
10478
|
+
*,
|
10479
|
+
msh: ObjMarshalerManager,
|
10480
|
+
local_executor: LocalCommandExecutor,
|
10481
|
+
) -> None:
|
10482
|
+
super().__init__()
|
10483
|
+
|
10484
|
+
self._msh = msh
|
10485
|
+
self._local_executor = local_executor
|
10486
|
+
|
10487
|
+
@contextlib.asynccontextmanager
|
10488
|
+
async def connect(self) -> ta.AsyncGenerator[RemoteCommandExecutor, None]:
|
10489
|
+
r0, w0 = asyncio_create_bytes_channel()
|
10490
|
+
r1, w1 = asyncio_create_bytes_channel()
|
10491
|
+
|
10492
|
+
remote_chan = RemoteChannelImpl(r0, w1, msh=self._msh)
|
10493
|
+
local_chan = RemoteChannelImpl(r1, w0, msh=self._msh)
|
10494
|
+
|
10495
|
+
rch = _RemoteCommandHandler(
|
10496
|
+
remote_chan,
|
10497
|
+
self._local_executor,
|
10498
|
+
)
|
10499
|
+
rch_task = asyncio.create_task(rch.run()) # noqa
|
10500
|
+
try:
|
10501
|
+
rce: RemoteCommandExecutor
|
10502
|
+
async with aclosing(RemoteCommandExecutor(local_chan)) as rce:
|
10503
|
+
await rce.start()
|
10504
|
+
|
10505
|
+
yield rce
|
10506
|
+
|
10507
|
+
finally:
|
10508
|
+
rch.stop()
|
10509
|
+
await rch_task
|
10510
|
+
|
10511
|
+
|
10512
|
+
########################################
|
10513
|
+
# ../system/commands.py
|
10514
|
+
|
10515
|
+
|
10516
|
+
##
|
10517
|
+
|
10518
|
+
|
10519
|
+
@dc.dataclass(frozen=True)
|
10520
|
+
class CheckSystemPackageCommand(Command['CheckSystemPackageCommand.Output']):
|
10521
|
+
pkgs: ta.Sequence[str] = ()
|
10522
|
+
|
10523
|
+
def __post_init__(self) -> None:
|
10524
|
+
check.not_isinstance(self.pkgs, str)
|
10525
|
+
|
10526
|
+
@dc.dataclass(frozen=True)
|
10527
|
+
class Output(Command.Output):
|
10528
|
+
pkgs: ta.Sequence[SystemPackage]
|
10092
10529
|
|
10093
10530
|
|
10094
|
-
|
10531
|
+
class CheckSystemPackageCommandExecutor(CommandExecutor[CheckSystemPackageCommand, CheckSystemPackageCommand.Output]):
|
10532
|
+
def __init__(
|
10533
|
+
self,
|
10534
|
+
*,
|
10535
|
+
mgr: SystemPackageManager,
|
10536
|
+
) -> None:
|
10537
|
+
super().__init__()
|
10095
10538
|
|
10539
|
+
self._mgr = mgr
|
10096
10540
|
|
10097
|
-
|
10098
|
-
|
10099
|
-
cmd: str = 'python3'
|
10100
|
-
path: ta.Optional[str] = None
|
10541
|
+
async def execute(self, cmd: CheckSystemPackageCommand) -> CheckSystemPackageCommand.Output:
|
10542
|
+
log.info('Checking system package!')
|
10101
10543
|
|
10102
|
-
|
10103
|
-
inspector: InterpInspector = INTERP_INSPECTOR
|
10544
|
+
ret = await self._mgr.query(*cmd.pkgs)
|
10104
10545
|
|
10105
|
-
|
10546
|
+
return CheckSystemPackageCommand.Output(list(ret.values()))
|
10106
10547
|
|
10107
|
-
@staticmethod
|
10108
|
-
def _re_which(
|
10109
|
-
pat: re.Pattern,
|
10110
|
-
*,
|
10111
|
-
mode: int = os.F_OK | os.X_OK,
|
10112
|
-
path: ta.Optional[str] = None,
|
10113
|
-
) -> ta.List[str]:
|
10114
|
-
if path is None:
|
10115
|
-
path = os.environ.get('PATH', None)
|
10116
|
-
if path is None:
|
10117
|
-
try:
|
10118
|
-
path = os.confstr('CS_PATH')
|
10119
|
-
except (AttributeError, ValueError):
|
10120
|
-
path = os.defpath
|
10121
10548
|
|
10122
|
-
|
10123
|
-
|
10549
|
+
########################################
|
10550
|
+
# ../../../omdev/interp/providers/inject.py
|
10124
10551
|
|
10125
|
-
path = os.fsdecode(path)
|
10126
|
-
pathlst = path.split(os.pathsep)
|
10127
10552
|
|
10128
|
-
|
10129
|
-
|
10553
|
+
def bind_interp_providers() -> InjectorBindings:
|
10554
|
+
lst: ta.List[InjectorBindingOrBindings] = [
|
10555
|
+
inj.bind_array(InterpProvider),
|
10556
|
+
inj.bind_array_type(InterpProvider, InterpProviders),
|
10130
10557
|
|
10131
|
-
|
10132
|
-
|
10133
|
-
for d in pathlst:
|
10134
|
-
normdir = os.path.normcase(d)
|
10135
|
-
if normdir not in seen:
|
10136
|
-
seen.add(normdir)
|
10137
|
-
if not _access_check(normdir, mode):
|
10138
|
-
continue
|
10139
|
-
for thefile in os.listdir(d):
|
10140
|
-
name = os.path.join(d, thefile)
|
10141
|
-
if not (
|
10142
|
-
os.path.isfile(name) and
|
10143
|
-
pat.fullmatch(thefile) and
|
10144
|
-
_access_check(name, mode)
|
10145
|
-
):
|
10146
|
-
continue
|
10147
|
-
out.append(name)
|
10558
|
+
inj.bind(RunningInterpProvider, singleton=True),
|
10559
|
+
inj.bind(InterpProvider, to_key=RunningInterpProvider, array=True),
|
10148
10560
|
|
10149
|
-
|
10561
|
+
inj.bind(SystemInterpProvider, singleton=True),
|
10562
|
+
inj.bind(InterpProvider, to_key=SystemInterpProvider, array=True),
|
10563
|
+
]
|
10150
10564
|
|
10151
|
-
|
10152
|
-
def exes(self) -> ta.List[str]:
|
10153
|
-
return self._re_which(
|
10154
|
-
re.compile(r'python3(\.\d+)?'),
|
10155
|
-
path=self.path,
|
10156
|
-
)
|
10565
|
+
return inj.as_bindings(*lst)
|
10157
10566
|
|
10158
|
-
#
|
10159
10567
|
|
10160
|
-
|
10161
|
-
|
10162
|
-
s = os.path.basename(exe)
|
10163
|
-
if s.startswith('python'):
|
10164
|
-
s = s[len('python'):]
|
10165
|
-
if '.' in s:
|
10166
|
-
try:
|
10167
|
-
return InterpVersion.parse(s)
|
10168
|
-
except InvalidVersion:
|
10169
|
-
pass
|
10170
|
-
ii = await self.inspector.inspect(exe)
|
10171
|
-
return ii.iv if ii is not None else None
|
10568
|
+
########################################
|
10569
|
+
# ../../../omdev/interp/pyenv/inject.py
|
10172
10570
|
|
10173
|
-
async def exe_versions(self) -> ta.Sequence[ta.Tuple[str, InterpVersion]]:
|
10174
|
-
lst = []
|
10175
|
-
for e in self.exes():
|
10176
|
-
if (ev := await self.get_exe_version(e)) is None:
|
10177
|
-
log.debug('Invalid system version: %s', e)
|
10178
|
-
continue
|
10179
|
-
lst.append((e, ev))
|
10180
|
-
return lst
|
10181
10571
|
|
10182
|
-
|
10572
|
+
def bind_interp_pyenv() -> InjectorBindings:
|
10573
|
+
lst: ta.List[InjectorBindingOrBindings] = [
|
10574
|
+
inj.bind(Pyenv, singleton=True),
|
10183
10575
|
|
10184
|
-
|
10185
|
-
|
10576
|
+
inj.bind(PyenvInterpProvider, singleton=True),
|
10577
|
+
inj.bind(InterpProvider, to_key=PyenvInterpProvider, array=True),
|
10578
|
+
]
|
10186
10579
|
|
10187
|
-
|
10188
|
-
for e, ev in await self.exe_versions():
|
10189
|
-
if ev != version:
|
10190
|
-
continue
|
10191
|
-
return Interp(
|
10192
|
-
exe=e,
|
10193
|
-
version=ev,
|
10194
|
-
)
|
10195
|
-
raise KeyError(version)
|
10580
|
+
return inj.as_bindings(*lst)
|
10196
10581
|
|
10197
10582
|
|
10198
10583
|
########################################
|
@@ -10559,101 +10944,44 @@ class SshManageTargetConnector(ManageTargetConnector):
|
|
10559
10944
|
|
10560
10945
|
|
10561
10946
|
########################################
|
10562
|
-
# ../../../omdev/interp/
|
10563
|
-
|
10564
|
-
|
10565
|
-
INTERP_PROVIDER_TYPES_BY_NAME: ta.Mapping[str, ta.Type[InterpProvider]] = {
|
10566
|
-
cls.name: cls for cls in deep_subclasses(InterpProvider) if abc.ABC not in cls.__bases__ # type: ignore
|
10567
|
-
}
|
10568
|
-
|
10569
|
-
|
10570
|
-
class InterpResolver:
|
10571
|
-
def __init__(
|
10572
|
-
self,
|
10573
|
-
providers: ta.Sequence[ta.Tuple[str, InterpProvider]],
|
10574
|
-
) -> None:
|
10575
|
-
super().__init__()
|
10576
|
-
|
10577
|
-
self._providers: ta.Mapping[str, InterpProvider] = collections.OrderedDict(providers)
|
10578
|
-
|
10579
|
-
async def _resolve_installed(self, spec: InterpSpecifier) -> ta.Optional[ta.Tuple[InterpProvider, InterpVersion]]:
|
10580
|
-
lst = [
|
10581
|
-
(i, si)
|
10582
|
-
for i, p in enumerate(self._providers.values())
|
10583
|
-
for si in await p.get_installed_versions(spec)
|
10584
|
-
if spec.contains(si)
|
10585
|
-
]
|
10586
|
-
|
10587
|
-
slst = sorted(lst, key=lambda t: (-t[0], t[1].version))
|
10588
|
-
if not slst:
|
10589
|
-
return None
|
10947
|
+
# ../../../omdev/interp/inject.py
|
10590
10948
|
|
10591
|
-
bi, bv = slst[-1]
|
10592
|
-
bp = list(self._providers.values())[bi]
|
10593
|
-
return (bp, bv)
|
10594
10949
|
|
10595
|
-
|
10596
|
-
|
10597
|
-
|
10598
|
-
*,
|
10599
|
-
install: bool = False,
|
10600
|
-
) -> ta.Optional[Interp]:
|
10601
|
-
tup = await self._resolve_installed(spec)
|
10602
|
-
if tup is not None:
|
10603
|
-
bp, bv = tup
|
10604
|
-
return await bp.get_installed_version(bv)
|
10950
|
+
def bind_interp() -> InjectorBindings:
|
10951
|
+
lst: ta.List[InjectorBindingOrBindings] = [
|
10952
|
+
bind_interp_providers(),
|
10605
10953
|
|
10606
|
-
|
10607
|
-
return None
|
10954
|
+
bind_interp_pyenv(),
|
10608
10955
|
|
10609
|
-
|
10956
|
+
bind_interp_uv(),
|
10610
10957
|
|
10611
|
-
|
10612
|
-
|
10613
|
-
key=lambda s: s.version,
|
10614
|
-
)
|
10615
|
-
if not sv:
|
10616
|
-
return None
|
10958
|
+
inj.bind(InterpInspector, singleton=True),
|
10959
|
+
]
|
10617
10960
|
|
10618
|
-
|
10619
|
-
return await tp.install_version(bv)
|
10961
|
+
#
|
10620
10962
|
|
10621
|
-
|
10622
|
-
|
10623
|
-
|
10624
|
-
|
10625
|
-
|
10626
|
-
|
10627
|
-
|
10963
|
+
def provide_interp_resolver_providers(injector: Injector) -> InterpResolverProviders:
|
10964
|
+
# FIXME: lol
|
10965
|
+
rps: ta.List[ta.Any] = [
|
10966
|
+
injector.provide(c)
|
10967
|
+
for c in [
|
10968
|
+
PyenvInterpProvider,
|
10969
|
+
RunningInterpProvider,
|
10970
|
+
SystemInterpProvider,
|
10628
10971
|
]
|
10629
|
-
|
10630
|
-
print(f' {n}')
|
10631
|
-
for si in lst:
|
10632
|
-
print(f' {si}')
|
10633
|
-
|
10634
|
-
print()
|
10972
|
+
]
|
10635
10973
|
|
10636
|
-
|
10637
|
-
for n, p in self._providers.items():
|
10638
|
-
lst = [
|
10639
|
-
si
|
10640
|
-
for si in await p.get_installable_versions(spec)
|
10641
|
-
if spec.contains(si)
|
10642
|
-
]
|
10643
|
-
if lst:
|
10644
|
-
print(f' {n}')
|
10645
|
-
for si in lst:
|
10646
|
-
print(f' {si}')
|
10974
|
+
return InterpResolverProviders([(rp.name, rp) for rp in rps])
|
10647
10975
|
|
10976
|
+
lst.append(inj.bind(provide_interp_resolver_providers, singleton=True))
|
10648
10977
|
|
10649
|
-
|
10650
|
-
|
10651
|
-
|
10978
|
+
lst.extend([
|
10979
|
+
inj.bind(InterpResolver, singleton=True),
|
10980
|
+
])
|
10652
10981
|
|
10653
|
-
|
10982
|
+
#
|
10654
10983
|
|
10655
|
-
|
10656
|
-
]])
|
10984
|
+
return inj.as_bindings(*lst)
|
10657
10985
|
|
10658
10986
|
|
10659
10987
|
########################################
|
@@ -10685,6 +11013,15 @@ def bind_targets() -> InjectorBindings:
|
|
10685
11013
|
return inj.as_bindings(*lst)
|
10686
11014
|
|
10687
11015
|
|
11016
|
+
########################################
|
11017
|
+
# ../../../omdev/interp/default.py
|
11018
|
+
|
11019
|
+
|
11020
|
+
@cached_nullary
|
11021
|
+
def get_default_interp_resolver() -> InterpResolver:
|
11022
|
+
return inj.create_injector(bind_interp())[InterpResolver]
|
11023
|
+
|
11024
|
+
|
10688
11025
|
########################################
|
10689
11026
|
# ../deploy/interp.py
|
10690
11027
|
|
@@ -10707,7 +11044,7 @@ class InterpCommand(Command['InterpCommand.Output']):
|
|
10707
11044
|
class InterpCommandExecutor(CommandExecutor[InterpCommand, InterpCommand.Output]):
|
10708
11045
|
async def execute(self, cmd: InterpCommand) -> InterpCommand.Output:
|
10709
11046
|
i = InterpSpecifier.parse(check.not_none(cmd.spec))
|
10710
|
-
o = check.not_none(await
|
11047
|
+
o = check.not_none(await get_default_interp_resolver().resolve(i, install=cmd.install))
|
10711
11048
|
return InterpCommand.Output(
|
10712
11049
|
exe=o.exe,
|
10713
11050
|
version=str(o.version.version),
|
@@ -10734,7 +11071,7 @@ class DeployVenvManager:
|
|
10734
11071
|
) -> None:
|
10735
11072
|
if spec.interp is not None:
|
10736
11073
|
i = InterpSpecifier.parse(check.not_none(spec.interp))
|
10737
|
-
o = check.not_none(await
|
11074
|
+
o = check.not_none(await get_default_interp_resolver().resolve(i))
|
10738
11075
|
sys_exe = o.exe
|
10739
11076
|
else:
|
10740
11077
|
sys_exe = 'python3'
|
@@ -10929,65 +11266,46 @@ class DeployAppManager(DeployPathOwner):
|
|
10929
11266
|
|
10930
11267
|
|
10931
11268
|
########################################
|
10932
|
-
# ../deploy/
|
10933
|
-
|
10934
|
-
|
10935
|
-
DEPLOY_TAG_DATETIME_FMT = '%Y%m%dT%H%M%SZ'
|
11269
|
+
# ../deploy/driver.py
|
10936
11270
|
|
10937
11271
|
|
10938
|
-
|
11272
|
+
class DeployDriverFactory(Func1[DeploySpec, ta.ContextManager['DeployDriver']]):
|
11273
|
+
pass
|
10939
11274
|
|
10940
11275
|
|
10941
|
-
class
|
11276
|
+
class DeployDriver:
|
10942
11277
|
def __init__(
|
10943
11278
|
self,
|
10944
11279
|
*,
|
10945
|
-
|
10946
|
-
|
11280
|
+
spec: DeploySpec,
|
11281
|
+
home: DeployHome,
|
11282
|
+
time: DeployTime,
|
10947
11283
|
|
10948
|
-
|
10949
|
-
|
11284
|
+
paths: DeployPathsManager,
|
11285
|
+
apps: DeployAppManager,
|
11286
|
+
) -> None:
|
10950
11287
|
super().__init__()
|
10951
11288
|
|
10952
|
-
self.
|
10953
|
-
self.
|
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
|
11289
|
+
self._spec = spec
|
11290
|
+
self._home = home
|
11291
|
+
self._time = time
|
10962
11292
|
|
10963
|
-
|
10964
|
-
|
11293
|
+
self._paths = paths
|
11294
|
+
self._apps = apps
|
10965
11295
|
|
10966
|
-
async def
|
10967
|
-
self,
|
10968
|
-
spec: DeploySpec,
|
10969
|
-
) -> None:
|
11296
|
+
async def drive_deploy(self) -> None:
|
10970
11297
|
self._paths.validate_deploy_paths()
|
10971
11298
|
|
10972
11299
|
#
|
10973
11300
|
|
10974
|
-
hs = check.non_empty_str(spec.home)
|
10975
|
-
hs = os.path.expanduser(hs)
|
10976
|
-
hs = os.path.realpath(hs)
|
10977
|
-
hs = os.path.abspath(hs)
|
10978
|
-
|
10979
|
-
home = DeployHome(hs)
|
10980
|
-
|
10981
|
-
#
|
10982
|
-
|
10983
11301
|
deploy_tags = DeployTagMap(
|
10984
|
-
self.
|
10985
|
-
|
11302
|
+
self._time,
|
11303
|
+
self._spec.key(),
|
10986
11304
|
)
|
10987
11305
|
|
10988
11306
|
#
|
10989
11307
|
|
10990
|
-
for app in
|
11308
|
+
for app in self._spec.apps:
|
10991
11309
|
app_tags = deploy_tags.add(
|
10992
11310
|
app.app,
|
10993
11311
|
app.key(),
|
@@ -10996,7 +11314,7 @@ class DeployManager:
|
|
10996
11314
|
|
10997
11315
|
await self._apps.prepare_app(
|
10998
11316
|
app,
|
10999
|
-
|
11317
|
+
self._home,
|
11000
11318
|
app_tags,
|
11001
11319
|
)
|
11002
11320
|
|
@@ -11019,12 +11337,13 @@ class DeployCommand(Command['DeployCommand.Output']):
|
|
11019
11337
|
|
11020
11338
|
@dc.dataclass(frozen=True)
|
11021
11339
|
class DeployCommandExecutor(CommandExecutor[DeployCommand, DeployCommand.Output]):
|
11022
|
-
|
11340
|
+
_driver_factory: DeployDriverFactory
|
11023
11341
|
|
11024
11342
|
async def execute(self, cmd: DeployCommand) -> DeployCommand.Output:
|
11025
11343
|
log.info('Deploying! %r', cmd.spec)
|
11026
11344
|
|
11027
|
-
|
11345
|
+
with self._driver_factory(cmd.spec) as driver:
|
11346
|
+
await driver.drive_deploy()
|
11028
11347
|
|
11029
11348
|
return DeployCommand.Output()
|
11030
11349
|
|
@@ -11033,6 +11352,57 @@ class DeployCommandExecutor(CommandExecutor[DeployCommand, DeployCommand.Output]
|
|
11033
11352
|
# ../deploy/inject.py
|
11034
11353
|
|
11035
11354
|
|
11355
|
+
##
|
11356
|
+
|
11357
|
+
|
11358
|
+
class DeployInjectorScope(ContextvarInjectorScope):
|
11359
|
+
pass
|
11360
|
+
|
11361
|
+
|
11362
|
+
def bind_deploy_scope() -> InjectorBindings:
|
11363
|
+
lst: ta.List[InjectorBindingOrBindings] = [
|
11364
|
+
inj.bind_scope(DeployInjectorScope),
|
11365
|
+
inj.bind_scope_seed(DeploySpec, DeployInjectorScope),
|
11366
|
+
|
11367
|
+
inj.bind(DeployDriver, in_=DeployInjectorScope),
|
11368
|
+
]
|
11369
|
+
|
11370
|
+
#
|
11371
|
+
|
11372
|
+
def provide_deploy_driver_factory(injector: Injector, sc: DeployInjectorScope) -> DeployDriverFactory:
|
11373
|
+
@contextlib.contextmanager
|
11374
|
+
def factory(spec: DeploySpec) -> ta.Iterator[DeployDriver]:
|
11375
|
+
with sc.enter({
|
11376
|
+
inj.as_key(DeploySpec): spec,
|
11377
|
+
}):
|
11378
|
+
yield injector[DeployDriver]
|
11379
|
+
return DeployDriverFactory(factory)
|
11380
|
+
lst.append(inj.bind(provide_deploy_driver_factory, singleton=True))
|
11381
|
+
|
11382
|
+
#
|
11383
|
+
|
11384
|
+
def provide_deploy_home(deploy: DeploySpec) -> DeployHome:
|
11385
|
+
hs = check.non_empty_str(deploy.home)
|
11386
|
+
hs = os.path.expanduser(hs)
|
11387
|
+
hs = os.path.realpath(hs)
|
11388
|
+
hs = os.path.abspath(hs)
|
11389
|
+
return DeployHome(hs)
|
11390
|
+
lst.append(inj.bind(provide_deploy_home, in_=DeployInjectorScope))
|
11391
|
+
|
11392
|
+
#
|
11393
|
+
|
11394
|
+
def provide_deploy_time(deploys: DeployManager) -> DeployTime:
|
11395
|
+
return deploys.make_deploy_time()
|
11396
|
+
lst.append(inj.bind(provide_deploy_time, in_=DeployInjectorScope))
|
11397
|
+
|
11398
|
+
#
|
11399
|
+
|
11400
|
+
return inj.as_bindings(*lst)
|
11401
|
+
|
11402
|
+
|
11403
|
+
##
|
11404
|
+
|
11405
|
+
|
11036
11406
|
def bind_deploy(
|
11037
11407
|
*,
|
11038
11408
|
deploy_config: DeployConfig,
|
@@ -11041,6 +11411,8 @@ def bind_deploy(
|
|
11041
11411
|
inj.bind(deploy_config),
|
11042
11412
|
|
11043
11413
|
bind_deploy_paths(),
|
11414
|
+
|
11415
|
+
bind_deploy_scope(),
|
11044
11416
|
]
|
11045
11417
|
|
11046
11418
|
#
|