ominfra 0.0.0.dev177__py3-none-any.whl → 0.0.0.dev179__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.
- 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
|
#
|