ominfra 0.0.0.dev178__py3-none-any.whl → 0.0.0.dev180__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- ominfra/clouds/aws/cli.py +1 -1
- ominfra/manage/deploy/driver.py +7 -16
- ominfra/manage/deploy/inject.py +53 -19
- ominfra/manage/deploy/interp.py +2 -2
- ominfra/manage/deploy/venvs.py +2 -2
- ominfra/manage/main.py +2 -2
- ominfra/manage/remote/spawning.py +1 -1
- ominfra/scripts/manage.py +1001 -866
- ominfra/scripts/supervisor.py +111 -104
- ominfra/supervisor/http.py +1 -1
- ominfra/systemd.py +18 -0
- ominfra/tailscale/cli.py +4 -4
- {ominfra-0.0.0.dev178.dist-info → ominfra-0.0.0.dev180.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev178.dist-info → ominfra-0.0.0.dev180.dist-info}/RECORD +18 -17
- {ominfra-0.0.0.dev178.dist-info → ominfra-0.0.0.dev180.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev178.dist-info → ominfra-0.0.0.dev180.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev178.dist-info → ominfra-0.0.0.dev180.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev178.dist-info → ominfra-0.0.0.dev180.dist-info}/top_level.txt +0 -0
ominfra/scripts/manage.py
CHANGED
@@ -111,7 +111,7 @@ CommandT = ta.TypeVar('CommandT', bound='Command')
|
|
111
111
|
CommandOutputT = ta.TypeVar('CommandOutputT', bound='Command.Output')
|
112
112
|
|
113
113
|
# ../../omlish/argparse/cli.py
|
114
|
-
|
114
|
+
ArgparseCmdFn = ta.Callable[[], ta.Optional[int]] # ta.TypeAlias
|
115
115
|
|
116
116
|
# ../../omlish/lite/contextmanagers.py
|
117
117
|
ExitStackedT = ta.TypeVar('ExitStackedT', bound='ExitStacked')
|
@@ -2766,21 +2766,6 @@ def read_package_resource_text(package: str, resource: str) -> str:
|
|
2766
2766
|
return importlib.resources.read_text(package, resource)
|
2767
2767
|
|
2768
2768
|
|
2769
|
-
########################################
|
2770
|
-
# ../../../omlish/lite/shlex.py
|
2771
|
-
|
2772
|
-
|
2773
|
-
def shlex_needs_quote(s: str) -> bool:
|
2774
|
-
return bool(s) and len(list(shlex.shlex(s))) > 1
|
2775
|
-
|
2776
|
-
|
2777
|
-
def shlex_maybe_quote(s: str) -> str:
|
2778
|
-
if shlex_needs_quote(s):
|
2779
|
-
return shlex.quote(s)
|
2780
|
-
else:
|
2781
|
-
return s
|
2782
|
-
|
2783
|
-
|
2784
2769
|
########################################
|
2785
2770
|
# ../../../omlish/lite/strings.py
|
2786
2771
|
|
@@ -3580,6 +3565,21 @@ def relative_symlink(
|
|
3580
3565
|
)
|
3581
3566
|
|
3582
3567
|
|
3568
|
+
########################################
|
3569
|
+
# ../../../omlish/shlex.py
|
3570
|
+
|
3571
|
+
|
3572
|
+
def shlex_needs_quote(s: str) -> bool:
|
3573
|
+
return bool(s) and len(list(shlex.shlex(s))) > 1
|
3574
|
+
|
3575
|
+
|
3576
|
+
def shlex_maybe_quote(s: str) -> str:
|
3577
|
+
if shlex_needs_quote(s):
|
3578
|
+
return shlex.quote(s)
|
3579
|
+
else:
|
3580
|
+
return s
|
3581
|
+
|
3582
|
+
|
3583
3583
|
########################################
|
3584
3584
|
# ../../../omdev/packaging/specifiers.py
|
3585
3585
|
# Copyright (c) Donald Stufft and individual contributors.
|
@@ -4546,15 +4546,15 @@ def argparse_arg(*args, **kwargs) -> ArgparseArg:
|
|
4546
4546
|
|
4547
4547
|
|
4548
4548
|
@dc.dataclass(eq=False)
|
4549
|
-
class
|
4549
|
+
class ArgparseCmd:
|
4550
4550
|
name: str
|
4551
|
-
fn:
|
4551
|
+
fn: ArgparseCmdFn
|
4552
4552
|
args: ta.Sequence[ArgparseArg] = () # noqa
|
4553
4553
|
|
4554
4554
|
# _: dc.KW_ONLY
|
4555
4555
|
|
4556
4556
|
aliases: ta.Optional[ta.Sequence[str]] = None
|
4557
|
-
parent: ta.Optional['
|
4557
|
+
parent: ta.Optional['ArgparseCmd'] = None
|
4558
4558
|
accepts_unknown: bool = False
|
4559
4559
|
|
4560
4560
|
def __post_init__(self) -> None:
|
@@ -4569,7 +4569,7 @@ class ArgparseCommand:
|
|
4569
4569
|
|
4570
4570
|
check.arg(callable(self.fn))
|
4571
4571
|
check.arg(all(isinstance(a, ArgparseArg) for a in self.args))
|
4572
|
-
check.isinstance(self.parent, (
|
4572
|
+
check.isinstance(self.parent, (ArgparseCmd, type(None)))
|
4573
4573
|
check.isinstance(self.accepts_unknown, bool)
|
4574
4574
|
|
4575
4575
|
functools.update_wrapper(self, self.fn)
|
@@ -4583,21 +4583,21 @@ class ArgparseCommand:
|
|
4583
4583
|
return self.fn(*args, **kwargs)
|
4584
4584
|
|
4585
4585
|
|
4586
|
-
def
|
4586
|
+
def argparse_cmd(
|
4587
4587
|
*args: ArgparseArg,
|
4588
4588
|
name: ta.Optional[str] = None,
|
4589
4589
|
aliases: ta.Optional[ta.Iterable[str]] = None,
|
4590
|
-
parent: ta.Optional[
|
4590
|
+
parent: ta.Optional[ArgparseCmd] = None,
|
4591
4591
|
accepts_unknown: bool = False,
|
4592
|
-
) -> ta.Any: # ta.Callable[[
|
4592
|
+
) -> ta.Any: # ta.Callable[[ArgparseCmdFn], ArgparseCmd]: # FIXME
|
4593
4593
|
for arg in args:
|
4594
4594
|
check.isinstance(arg, ArgparseArg)
|
4595
4595
|
check.isinstance(name, (str, type(None)))
|
4596
|
-
check.isinstance(parent, (
|
4596
|
+
check.isinstance(parent, (ArgparseCmd, type(None)))
|
4597
4597
|
check.not_isinstance(aliases, str)
|
4598
4598
|
|
4599
4599
|
def inner(fn):
|
4600
|
-
return
|
4600
|
+
return ArgparseCmd(
|
4601
4601
|
(name if name is not None else fn.__name__).replace('_', '-'),
|
4602
4602
|
fn,
|
4603
4603
|
args,
|
@@ -4652,7 +4652,7 @@ class ArgparseCli:
|
|
4652
4652
|
for bns in [bcls.__dict__ for bcls in reversed(mro)] + [ns]:
|
4653
4653
|
bseen = set() # type: ignore
|
4654
4654
|
for k, v in bns.items():
|
4655
|
-
if isinstance(v, (
|
4655
|
+
if isinstance(v, (ArgparseCmd, ArgparseArg)):
|
4656
4656
|
check.not_in(v, bseen)
|
4657
4657
|
bseen.add(v)
|
4658
4658
|
objs[k] = v
|
@@ -4679,7 +4679,7 @@ class ArgparseCli:
|
|
4679
4679
|
subparsers = parser.add_subparsers()
|
4680
4680
|
|
4681
4681
|
for att, obj in objs.items():
|
4682
|
-
if isinstance(obj,
|
4682
|
+
if isinstance(obj, ArgparseCmd):
|
4683
4683
|
if obj.parent is not None:
|
4684
4684
|
raise NotImplementedError
|
4685
4685
|
|
@@ -4741,7 +4741,7 @@ class ArgparseCli:
|
|
4741
4741
|
|
4742
4742
|
#
|
4743
4743
|
|
4744
|
-
def _bind_cli_cmd(self, cmd:
|
4744
|
+
def _bind_cli_cmd(self, cmd: ArgparseCmd) -> ta.Callable:
|
4745
4745
|
return cmd.__get__(self, type(self))
|
4746
4746
|
|
4747
4747
|
def prepare_cli_run(self) -> ta.Optional[ta.Callable]:
|
@@ -5126,30 +5126,6 @@ def as_injector_bindings(*args: InjectorBindingOrBindings) -> InjectorBindings:
|
|
5126
5126
|
##
|
5127
5127
|
|
5128
5128
|
|
5129
|
-
@dc.dataclass(frozen=True)
|
5130
|
-
class OverridesInjectorBindings(InjectorBindings):
|
5131
|
-
p: InjectorBindings
|
5132
|
-
m: ta.Mapping[InjectorKey, InjectorBinding]
|
5133
|
-
|
5134
|
-
def bindings(self) -> ta.Iterator[InjectorBinding]:
|
5135
|
-
for b in self.p.bindings():
|
5136
|
-
yield self.m.get(b.key, b)
|
5137
|
-
|
5138
|
-
|
5139
|
-
def injector_override(p: InjectorBindings, *args: InjectorBindingOrBindings) -> InjectorBindings:
|
5140
|
-
m: ta.Dict[InjectorKey, InjectorBinding] = {}
|
5141
|
-
|
5142
|
-
for b in as_injector_bindings(*args).bindings():
|
5143
|
-
if b.key in m:
|
5144
|
-
raise DuplicateInjectorKeyError(b.key)
|
5145
|
-
m[b.key] = b
|
5146
|
-
|
5147
|
-
return OverridesInjectorBindings(p, m)
|
5148
|
-
|
5149
|
-
|
5150
|
-
##
|
5151
|
-
|
5152
|
-
|
5153
5129
|
def build_injector_provider_map(bs: InjectorBindings) -> ta.Mapping[InjectorKey, InjectorProvider]:
|
5154
5130
|
pm: ta.Dict[InjectorKey, InjectorProvider] = {}
|
5155
5131
|
am: ta.Dict[InjectorKey, ta.List[InjectorProvider]] = {}
|
@@ -5173,6 +5149,31 @@ def build_injector_provider_map(bs: InjectorBindings) -> ta.Mapping[InjectorKey,
|
|
5173
5149
|
return pm
|
5174
5150
|
|
5175
5151
|
|
5152
|
+
###
|
5153
|
+
# overrides
|
5154
|
+
|
5155
|
+
|
5156
|
+
@dc.dataclass(frozen=True)
|
5157
|
+
class OverridesInjectorBindings(InjectorBindings):
|
5158
|
+
p: InjectorBindings
|
5159
|
+
m: ta.Mapping[InjectorKey, InjectorBinding]
|
5160
|
+
|
5161
|
+
def bindings(self) -> ta.Iterator[InjectorBinding]:
|
5162
|
+
for b in self.p.bindings():
|
5163
|
+
yield self.m.get(b.key, b)
|
5164
|
+
|
5165
|
+
|
5166
|
+
def injector_override(p: InjectorBindings, *args: InjectorBindingOrBindings) -> InjectorBindings:
|
5167
|
+
m: ta.Dict[InjectorKey, InjectorBinding] = {}
|
5168
|
+
|
5169
|
+
for b in as_injector_bindings(*args).bindings():
|
5170
|
+
if b.key in m:
|
5171
|
+
raise DuplicateInjectorKeyError(b.key)
|
5172
|
+
m[b.key] = b
|
5173
|
+
|
5174
|
+
return OverridesInjectorBindings(p, m)
|
5175
|
+
|
5176
|
+
|
5176
5177
|
###
|
5177
5178
|
# scopes
|
5178
5179
|
|
@@ -5197,7 +5198,7 @@ class InjectorScope(abc.ABC): # noqa
|
|
5197
5198
|
@dc.dataclass(frozen=True)
|
5198
5199
|
class State:
|
5199
5200
|
seeds: ta.Dict[InjectorKey, ta.Any]
|
5200
|
-
|
5201
|
+
provisions: ta.Dict[InjectorKey, ta.Any] = dc.field(default_factory=dict)
|
5201
5202
|
|
5202
5203
|
def new_state(self, vs: ta.Mapping[InjectorKey, ta.Any]) -> State:
|
5203
5204
|
vs = dict(vs)
|
@@ -5277,11 +5278,11 @@ class ScopedInjectorProvider(InjectorProvider):
|
|
5277
5278
|
def pfn(i: Injector) -> ta.Any:
|
5278
5279
|
st = i[self.sc].state()
|
5279
5280
|
try:
|
5280
|
-
return st.
|
5281
|
+
return st.provisions[self.k]
|
5281
5282
|
except KeyError:
|
5282
5283
|
pass
|
5283
5284
|
v = ufn(i)
|
5284
|
-
st.
|
5285
|
+
st.provisions[self.k] = v
|
5285
5286
|
return v
|
5286
5287
|
|
5287
5288
|
ufn = self.p.provider_fn()
|
@@ -5305,9 +5306,7 @@ class _ScopeSeedInjectorProvider(InjectorProvider):
|
|
5305
5306
|
|
5306
5307
|
|
5307
5308
|
def bind_injector_scope(sc: ta.Type[InjectorScope]) -> InjectorBindingOrBindings:
|
5308
|
-
return
|
5309
|
-
InjectorBinder.bind(sc, singleton=True),
|
5310
|
-
)
|
5309
|
+
return InjectorBinder.bind(sc, singleton=True)
|
5311
5310
|
|
5312
5311
|
|
5313
5312
|
#
|
@@ -5847,6 +5846,8 @@ class InjectionApi:
|
|
5847
5846
|
def as_bindings(self, *args: InjectorBindingOrBindings) -> InjectorBindings:
|
5848
5847
|
return as_injector_bindings(*args)
|
5849
5848
|
|
5849
|
+
# overrides
|
5850
|
+
|
5850
5851
|
def override(self, p: InjectorBindings, *args: InjectorBindingOrBindings) -> InjectorBindings:
|
5851
5852
|
return injector_override(p, *args)
|
5852
5853
|
|
@@ -6705,6 +6706,9 @@ class TempDirAtomicPathSwapping(AtomicPathSwapping):
|
|
6705
6706
|
# ../../../omdev/interp/types.py
|
6706
6707
|
|
6707
6708
|
|
6709
|
+
##
|
6710
|
+
|
6711
|
+
|
6708
6712
|
# See https://peps.python.org/pep-3149/
|
6709
6713
|
INTERP_OPT_GLYPHS_BY_ATTR: ta.Mapping[str, str] = collections.OrderedDict([
|
6710
6714
|
('debug', 'd'),
|
@@ -6736,6 +6740,9 @@ class InterpOpts:
|
|
6736
6740
|
return s, cls(**kw)
|
6737
6741
|
|
6738
6742
|
|
6743
|
+
##
|
6744
|
+
|
6745
|
+
|
6739
6746
|
@dc.dataclass(frozen=True)
|
6740
6747
|
class InterpVersion:
|
6741
6748
|
version: Version
|
@@ -6761,6 +6768,9 @@ class InterpVersion:
|
|
6761
6768
|
return None
|
6762
6769
|
|
6763
6770
|
|
6771
|
+
##
|
6772
|
+
|
6773
|
+
|
6764
6774
|
@dc.dataclass(frozen=True)
|
6765
6775
|
class InterpSpecifier:
|
6766
6776
|
specifier: Specifier
|
@@ -6788,12 +6798,25 @@ class InterpSpecifier:
|
|
6788
6798
|
return self.contains(iv)
|
6789
6799
|
|
6790
6800
|
|
6801
|
+
##
|
6802
|
+
|
6803
|
+
|
6791
6804
|
@dc.dataclass(frozen=True)
|
6792
6805
|
class Interp:
|
6793
6806
|
exe: str
|
6794
6807
|
version: InterpVersion
|
6795
6808
|
|
6796
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
|
+
|
6797
6820
|
########################################
|
6798
6821
|
# ../../configs.py
|
6799
6822
|
|
@@ -7707,6 +7730,50 @@ class AbstractAsyncSubprocesses(BaseSubprocesses):
|
|
7707
7730
|
return ret.decode().strip()
|
7708
7731
|
|
7709
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
|
+
|
7710
7777
|
########################################
|
7711
7778
|
# ../bootstrap.py
|
7712
7779
|
|
@@ -8841,7 +8908,92 @@ class InterpInspector:
|
|
8841
8908
|
return ret
|
8842
8909
|
|
8843
8910
|
|
8844
|
-
|
8911
|
+
########################################
|
8912
|
+
# ../../../omdev/interp/resolvers.py
|
8913
|
+
|
8914
|
+
|
8915
|
+
@dc.dataclass(frozen=True)
|
8916
|
+
class InterpResolverProviders:
|
8917
|
+
providers: ta.Sequence[ta.Tuple[str, InterpProvider]]
|
8918
|
+
|
8919
|
+
|
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}')
|
8845
8997
|
|
8846
8998
|
|
8847
8999
|
########################################
|
@@ -9495,929 +9647,937 @@ class YumSystemPackageManager(SystemPackageManager):
|
|
9495
9647
|
|
9496
9648
|
|
9497
9649
|
########################################
|
9498
|
-
# ../../../omdev/interp/providers.py
|
9650
|
+
# ../../../omdev/interp/providers/running.py
|
9651
|
+
|
9652
|
+
|
9653
|
+
class RunningInterpProvider(InterpProvider):
|
9654
|
+
@cached_nullary
|
9655
|
+
def version(self) -> InterpVersion:
|
9656
|
+
return InterpInspector.running().iv
|
9657
|
+
|
9658
|
+
async def get_installed_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
|
9659
|
+
return [self.version()]
|
9660
|
+
|
9661
|
+
async def get_installed_version(self, version: InterpVersion) -> Interp:
|
9662
|
+
if version != self.version():
|
9663
|
+
raise KeyError(version)
|
9664
|
+
return Interp(
|
9665
|
+
exe=sys.executable,
|
9666
|
+
version=self.version(),
|
9667
|
+
)
|
9668
|
+
|
9669
|
+
|
9670
|
+
########################################
|
9671
|
+
# ../../../omdev/interp/providers/system.py
|
9499
9672
|
"""
|
9500
9673
|
TODO:
|
9501
|
-
-
|
9502
|
-
|
9503
|
-
- deadsnakes?
|
9504
|
-
- uv
|
9505
|
-
- loose versions
|
9674
|
+
- python, python3, python3.12, ...
|
9675
|
+
- check if path py's are venvs: sys.prefix != sys.base_prefix
|
9506
9676
|
"""
|
9507
9677
|
|
9508
9678
|
|
9509
9679
|
##
|
9510
9680
|
|
9511
9681
|
|
9512
|
-
class InterpProvider
|
9513
|
-
|
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
|
9514
9687
|
|
9515
|
-
|
9516
|
-
super().__init_subclass__(**kwargs)
|
9517
|
-
if abc.ABC not in cls.__bases__ and 'name' not in cls.__dict__:
|
9518
|
-
sfx = 'InterpProvider'
|
9519
|
-
if not cls.__name__.endswith(sfx):
|
9520
|
-
raise NameError(cls)
|
9521
|
-
setattr(cls, 'name', snake_case(cls.__name__[:-len(sfx)]))
|
9522
|
-
|
9523
|
-
@abc.abstractmethod
|
9524
|
-
def get_installed_versions(self, spec: InterpSpecifier) -> ta.Awaitable[ta.Sequence[InterpVersion]]:
|
9525
|
-
raise NotImplementedError
|
9526
|
-
|
9527
|
-
@abc.abstractmethod
|
9528
|
-
def get_installed_version(self, version: InterpVersion) -> ta.Awaitable[Interp]:
|
9529
|
-
raise NotImplementedError
|
9530
|
-
|
9531
|
-
async def get_installable_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
|
9532
|
-
return []
|
9533
|
-
|
9534
|
-
async def install_version(self, version: InterpVersion) -> Interp:
|
9535
|
-
raise TypeError
|
9688
|
+
inspect: bool = False
|
9536
9689
|
|
9690
|
+
def __init__(
|
9691
|
+
self,
|
9692
|
+
options: Options = Options(),
|
9693
|
+
*,
|
9694
|
+
inspector: ta.Optional[InterpInspector] = None,
|
9695
|
+
) -> None:
|
9696
|
+
super().__init__()
|
9537
9697
|
|
9538
|
-
|
9698
|
+
self._options = options
|
9539
9699
|
|
9700
|
+
self._inspector = inspector
|
9540
9701
|
|
9541
|
-
|
9542
|
-
@cached_nullary
|
9543
|
-
def version(self) -> InterpVersion:
|
9544
|
-
return InterpInspector.running().iv
|
9702
|
+
#
|
9545
9703
|
|
9546
|
-
|
9547
|
-
|
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
|
9548
9718
|
|
9549
|
-
|
9550
|
-
|
9551
|
-
raise KeyError(version)
|
9552
|
-
return Interp(
|
9553
|
-
exe=sys.executable,
|
9554
|
-
version=self.version(),
|
9555
|
-
)
|
9719
|
+
if not path:
|
9720
|
+
return []
|
9556
9721
|
|
9722
|
+
path = os.fsdecode(path)
|
9723
|
+
pathlst = path.split(os.pathsep)
|
9557
9724
|
|
9558
|
-
|
9559
|
-
|
9725
|
+
def _access_check(fn: str, mode: int) -> bool:
|
9726
|
+
return os.path.exists(fn) and os.access(fn, mode)
|
9560
9727
|
|
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)
|
9561
9745
|
|
9562
|
-
|
9746
|
+
return out
|
9563
9747
|
|
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
|
+
)
|
9564
9754
|
|
9565
|
-
|
9566
|
-
command_cls: ta.Type[Command],
|
9567
|
-
executor_cls: ta.Optional[ta.Type[CommandExecutor]],
|
9568
|
-
) -> InjectorBindings:
|
9569
|
-
lst: ta.List[InjectorBindingOrBindings] = [
|
9570
|
-
inj.bind(CommandRegistration(command_cls), array=True),
|
9571
|
-
]
|
9755
|
+
#
|
9572
9756
|
|
9573
|
-
|
9574
|
-
|
9575
|
-
|
9576
|
-
|
9577
|
-
|
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
|
9578
9769
|
|
9579
|
-
|
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
|
9580
9778
|
|
9779
|
+
#
|
9581
9780
|
|
9582
|
-
|
9781
|
+
async def get_installed_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
|
9782
|
+
return [ev for e, ev in await self.exe_versions()]
|
9583
9783
|
|
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,
|
9791
|
+
)
|
9792
|
+
raise KeyError(version)
|
9584
9793
|
|
9585
|
-
@dc.dataclass(frozen=True)
|
9586
|
-
class _FactoryCommandExecutor(CommandExecutor):
|
9587
|
-
factory: ta.Callable[[], CommandExecutor]
|
9588
9794
|
|
9589
|
-
|
9590
|
-
|
9795
|
+
########################################
|
9796
|
+
# ../../../omdev/interp/pyenv/pyenv.py
|
9797
|
+
"""
|
9798
|
+
TODO:
|
9799
|
+
- custom tags
|
9800
|
+
- 'aliases'
|
9801
|
+
- https://github.com/pyenv/pyenv/pull/2966
|
9802
|
+
- https://github.com/pyenv/pyenv/issues/218 (lol)
|
9803
|
+
- probably need custom (temp?) definition file
|
9804
|
+
- *or* python-build directly just into the versions dir?
|
9805
|
+
- optionally install / upgrade pyenv itself
|
9806
|
+
- new vers dont need these custom mac opts, only run on old vers
|
9807
|
+
"""
|
9591
9808
|
|
9592
9809
|
|
9593
9810
|
##
|
9594
9811
|
|
9595
9812
|
|
9596
|
-
|
9597
|
-
|
9598
|
-
|
9599
|
-
|
9600
|
-
|
9601
|
-
|
9602
|
-
|
9603
|
-
|
9604
|
-
inj.bind_array(CommandExecutorRegistration),
|
9605
|
-
inj.bind_array_type(CommandExecutorRegistration, CommandExecutorRegistrations),
|
9606
|
-
|
9607
|
-
inj.bind(build_command_name_map, singleton=True),
|
9608
|
-
]
|
9609
|
-
|
9610
|
-
#
|
9611
|
-
|
9612
|
-
def provide_obj_marshaler_installer(cmds: CommandNameMap) -> ObjMarshalerInstaller:
|
9613
|
-
return ObjMarshalerInstaller(functools.partial(install_command_marshaling, cmds))
|
9614
|
-
|
9615
|
-
lst.append(inj.bind(provide_obj_marshaler_installer, array=True))
|
9616
|
-
|
9617
|
-
#
|
9618
|
-
|
9619
|
-
def provide_command_executor_map(
|
9620
|
-
injector: Injector,
|
9621
|
-
crs: CommandExecutorRegistrations,
|
9622
|
-
) -> CommandExecutorMap:
|
9623
|
-
dct: ta.Dict[ta.Type[Command], CommandExecutor] = {}
|
9624
|
-
|
9625
|
-
cr: CommandExecutorRegistration
|
9626
|
-
for cr in crs:
|
9627
|
-
if cr.command_cls in dct:
|
9628
|
-
raise KeyError(cr.command_cls)
|
9629
|
-
|
9630
|
-
factory = functools.partial(injector.provide, cr.executor_cls)
|
9631
|
-
if main_config.debug:
|
9632
|
-
ce = factory()
|
9633
|
-
else:
|
9634
|
-
ce = _FactoryCommandExecutor(factory)
|
9813
|
+
class Pyenv:
|
9814
|
+
def __init__(
|
9815
|
+
self,
|
9816
|
+
*,
|
9817
|
+
root: ta.Optional[str] = None,
|
9818
|
+
) -> None:
|
9819
|
+
if root is not None and not (isinstance(root, str) and root):
|
9820
|
+
raise ValueError(f'pyenv_root: {root!r}')
|
9635
9821
|
|
9636
|
-
|
9822
|
+
super().__init__()
|
9637
9823
|
|
9638
|
-
|
9824
|
+
self._root_kw = root
|
9639
9825
|
|
9640
|
-
|
9641
|
-
|
9826
|
+
@async_cached_nullary
|
9827
|
+
async def root(self) -> ta.Optional[str]:
|
9828
|
+
if self._root_kw is not None:
|
9829
|
+
return self._root_kw
|
9642
9830
|
|
9643
|
-
|
9644
|
-
|
9831
|
+
if shutil.which('pyenv'):
|
9832
|
+
return await asyncio_subprocesses.check_output_str('pyenv', 'root')
|
9645
9833
|
|
9646
|
-
|
9834
|
+
d = os.path.expanduser('~/.pyenv')
|
9835
|
+
if os.path.isdir(d) and os.path.isfile(os.path.join(d, 'bin', 'pyenv')):
|
9836
|
+
return d
|
9647
9837
|
|
9648
|
-
|
9649
|
-
bind_command(PingCommand, PingCommandExecutor),
|
9650
|
-
bind_command(SubprocessCommand, SubprocessCommandExecutor),
|
9651
|
-
])
|
9838
|
+
return None
|
9652
9839
|
|
9653
|
-
|
9840
|
+
@async_cached_nullary
|
9841
|
+
async def exe(self) -> str:
|
9842
|
+
return os.path.join(check.not_none(await self.root()), 'bin', 'pyenv')
|
9654
9843
|
|
9655
|
-
|
9844
|
+
async def version_exes(self) -> ta.List[ta.Tuple[str, str]]:
|
9845
|
+
if (root := await self.root()) is None:
|
9846
|
+
return []
|
9847
|
+
ret = []
|
9848
|
+
vp = os.path.join(root, 'versions')
|
9849
|
+
if os.path.isdir(vp):
|
9850
|
+
for dn in os.listdir(vp):
|
9851
|
+
ep = os.path.join(vp, dn, 'bin', 'python')
|
9852
|
+
if not os.path.isfile(ep):
|
9853
|
+
continue
|
9854
|
+
ret.append((dn, ep))
|
9855
|
+
return ret
|
9656
9856
|
|
9857
|
+
async def installable_versions(self) -> ta.List[str]:
|
9858
|
+
if await self.root() is None:
|
9859
|
+
return []
|
9860
|
+
ret = []
|
9861
|
+
s = await asyncio_subprocesses.check_output_str(await self.exe(), 'install', '--list')
|
9862
|
+
for l in s.splitlines():
|
9863
|
+
if not l.startswith(' '):
|
9864
|
+
continue
|
9865
|
+
l = l.strip()
|
9866
|
+
if not l:
|
9867
|
+
continue
|
9868
|
+
ret.append(l)
|
9869
|
+
return ret
|
9657
9870
|
|
9658
|
-
|
9659
|
-
|
9871
|
+
async def update(self) -> bool:
|
9872
|
+
if (root := await self.root()) is None:
|
9873
|
+
return False
|
9874
|
+
if not os.path.isdir(os.path.join(root, '.git')):
|
9875
|
+
return False
|
9876
|
+
await asyncio_subprocesses.check_call('git', 'pull', cwd=root)
|
9877
|
+
return True
|
9660
9878
|
|
9661
9879
|
|
9662
|
-
|
9663
|
-
def __init__(
|
9664
|
-
self,
|
9665
|
-
*,
|
9666
|
-
deploy_path_owners: DeployPathOwners,
|
9667
|
-
) -> None:
|
9668
|
-
super().__init__()
|
9880
|
+
##
|
9669
9881
|
|
9670
|
-
self._deploy_path_owners = deploy_path_owners
|
9671
9882
|
|
9672
|
-
|
9673
|
-
|
9674
|
-
|
9675
|
-
|
9676
|
-
|
9677
|
-
|
9678
|
-
|
9679
|
-
dct[p] = o
|
9680
|
-
return dct
|
9883
|
+
@dc.dataclass(frozen=True)
|
9884
|
+
class PyenvInstallOpts:
|
9885
|
+
opts: ta.Sequence[str] = ()
|
9886
|
+
conf_opts: ta.Sequence[str] = ()
|
9887
|
+
cflags: ta.Sequence[str] = ()
|
9888
|
+
ldflags: ta.Sequence[str] = ()
|
9889
|
+
env: ta.Mapping[str, str] = dc.field(default_factory=dict)
|
9681
9890
|
|
9682
|
-
def
|
9683
|
-
|
9891
|
+
def merge(self, *others: 'PyenvInstallOpts') -> 'PyenvInstallOpts':
|
9892
|
+
return PyenvInstallOpts(
|
9893
|
+
opts=list(itertools.chain.from_iterable(o.opts for o in [self, *others])),
|
9894
|
+
conf_opts=list(itertools.chain.from_iterable(o.conf_opts for o in [self, *others])),
|
9895
|
+
cflags=list(itertools.chain.from_iterable(o.cflags for o in [self, *others])),
|
9896
|
+
ldflags=list(itertools.chain.from_iterable(o.ldflags for o in [self, *others])),
|
9897
|
+
env=dict(itertools.chain.from_iterable(o.env.items() for o in [self, *others])),
|
9898
|
+
)
|
9684
9899
|
|
9685
9900
|
|
9686
|
-
|
9687
|
-
|
9901
|
+
# TODO: https://github.com/pyenv/pyenv/blob/master/plugins/python-build/README.md#building-for-maximum-performance
|
9902
|
+
DEFAULT_PYENV_INSTALL_OPTS = PyenvInstallOpts(
|
9903
|
+
opts=[
|
9904
|
+
'-s',
|
9905
|
+
'-v',
|
9906
|
+
'-k',
|
9907
|
+
],
|
9908
|
+
conf_opts=[
|
9909
|
+
# FIXME: breaks on mac for older py's
|
9910
|
+
'--enable-loadable-sqlite-extensions',
|
9688
9911
|
|
9912
|
+
# '--enable-shared',
|
9689
9913
|
|
9690
|
-
|
9691
|
-
|
9914
|
+
'--enable-optimizations',
|
9915
|
+
'--with-lto',
|
9692
9916
|
|
9917
|
+
# '--enable-profiling', # ?
|
9693
9918
|
|
9694
|
-
|
9695
|
-
|
9696
|
-
|
9697
|
-
|
9698
|
-
|
9699
|
-
|
9700
|
-
|
9919
|
+
# '--enable-ipv6', # ?
|
9920
|
+
],
|
9921
|
+
cflags=[
|
9922
|
+
# '-march=native',
|
9923
|
+
# '-mtune=native',
|
9924
|
+
],
|
9925
|
+
)
|
9701
9926
|
|
9702
|
-
|
9703
|
-
return TempDirAtomicPathSwapping(
|
9704
|
-
temp_dir=self._make_dir(home),
|
9705
|
-
root_dir=check.non_empty_str(home),
|
9706
|
-
)
|
9927
|
+
DEBUG_PYENV_INSTALL_OPTS = PyenvInstallOpts(opts=['-g'])
|
9707
9928
|
|
9929
|
+
THREADED_PYENV_INSTALL_OPTS = PyenvInstallOpts(conf_opts=['--disable-gil'])
|
9708
9930
|
|
9709
|
-
########################################
|
9710
|
-
# ../remote/connection.py
|
9711
9931
|
|
9932
|
+
#
|
9712
9933
|
|
9713
|
-
##
|
9714
9934
|
|
9935
|
+
class PyenvInstallOptsProvider(abc.ABC):
|
9936
|
+
@abc.abstractmethod
|
9937
|
+
def opts(self) -> ta.Awaitable[PyenvInstallOpts]:
|
9938
|
+
raise NotImplementedError
|
9715
9939
|
|
9716
|
-
class PyremoteRemoteExecutionConnector:
|
9717
|
-
def __init__(
|
9718
|
-
self,
|
9719
|
-
*,
|
9720
|
-
spawning: RemoteSpawning,
|
9721
|
-
msh: ObjMarshalerManager,
|
9722
|
-
payload_file: ta.Optional[RemoteExecutionPayloadFile] = None,
|
9723
|
-
) -> None:
|
9724
|
-
super().__init__()
|
9725
9940
|
|
9726
|
-
|
9727
|
-
|
9728
|
-
|
9941
|
+
class LinuxPyenvInstallOpts(PyenvInstallOptsProvider):
|
9942
|
+
async def opts(self) -> PyenvInstallOpts:
|
9943
|
+
return PyenvInstallOpts()
|
9729
9944
|
|
9730
|
-
#
|
9731
9945
|
|
9946
|
+
class DarwinPyenvInstallOpts(PyenvInstallOptsProvider):
|
9732
9947
|
@cached_nullary
|
9733
|
-
def
|
9734
|
-
return
|
9948
|
+
def framework_opts(self) -> PyenvInstallOpts:
|
9949
|
+
return PyenvInstallOpts(conf_opts=['--enable-framework'])
|
9735
9950
|
|
9736
9951
|
@cached_nullary
|
9737
|
-
def
|
9738
|
-
return
|
9739
|
-
self._payload_src(),
|
9740
|
-
'_remote_execution_main()',
|
9741
|
-
]
|
9952
|
+
def has_brew(self) -> bool:
|
9953
|
+
return shutil.which('brew') is not None
|
9742
9954
|
|
9743
|
-
|
9744
|
-
|
9745
|
-
|
9955
|
+
BREW_DEPS: ta.Sequence[str] = [
|
9956
|
+
'openssl',
|
9957
|
+
'readline',
|
9958
|
+
'sqlite3',
|
9959
|
+
'zlib',
|
9960
|
+
]
|
9746
9961
|
|
9747
|
-
|
9962
|
+
@async_cached_nullary
|
9963
|
+
async def brew_deps_opts(self) -> PyenvInstallOpts:
|
9964
|
+
cflags = []
|
9965
|
+
ldflags = []
|
9966
|
+
for dep in self.BREW_DEPS:
|
9967
|
+
dep_prefix = await asyncio_subprocesses.check_output_str('brew', '--prefix', dep)
|
9968
|
+
cflags.append(f'-I{dep_prefix}/include')
|
9969
|
+
ldflags.append(f'-L{dep_prefix}/lib')
|
9970
|
+
return PyenvInstallOpts(
|
9971
|
+
cflags=cflags,
|
9972
|
+
ldflags=ldflags,
|
9973
|
+
)
|
9748
9974
|
|
9749
|
-
@
|
9750
|
-
async def
|
9751
|
-
|
9752
|
-
|
9753
|
-
bs: MainBootstrap,
|
9754
|
-
) -> ta.AsyncGenerator[RemoteCommandExecutor, None]:
|
9755
|
-
spawn_src = self._spawn_src()
|
9756
|
-
remote_src = self._remote_src()
|
9975
|
+
@async_cached_nullary
|
9976
|
+
async def brew_tcl_opts(self) -> PyenvInstallOpts:
|
9977
|
+
if await asyncio_subprocesses.try_output('brew', '--prefix', 'tcl-tk') is None:
|
9978
|
+
return PyenvInstallOpts()
|
9757
9979
|
|
9758
|
-
|
9759
|
-
|
9760
|
-
|
9761
|
-
debug=bs.main_config.debug,
|
9762
|
-
) as proc:
|
9763
|
-
res = await PyremoteBootstrapDriver( # noqa
|
9764
|
-
remote_src,
|
9765
|
-
PyremoteBootstrapOptions(
|
9766
|
-
debug=bs.main_config.debug,
|
9767
|
-
),
|
9768
|
-
).async_run(
|
9769
|
-
proc.stdout,
|
9770
|
-
proc.stdin,
|
9771
|
-
)
|
9980
|
+
tcl_tk_prefix = await asyncio_subprocesses.check_output_str('brew', '--prefix', 'tcl-tk')
|
9981
|
+
tcl_tk_ver_str = await asyncio_subprocesses.check_output_str('brew', 'ls', '--versions', 'tcl-tk')
|
9982
|
+
tcl_tk_ver = '.'.join(tcl_tk_ver_str.split()[1].split('.')[:2])
|
9772
9983
|
|
9773
|
-
|
9774
|
-
|
9775
|
-
|
9776
|
-
|
9777
|
-
|
9984
|
+
return PyenvInstallOpts(conf_opts=[
|
9985
|
+
f"--with-tcltk-includes='-I{tcl_tk_prefix}/include'",
|
9986
|
+
f"--with-tcltk-libs='-L{tcl_tk_prefix}/lib -ltcl{tcl_tk_ver} -ltk{tcl_tk_ver}'",
|
9987
|
+
])
|
9988
|
+
|
9989
|
+
# @cached_nullary
|
9990
|
+
# def brew_ssl_opts(self) -> PyenvInstallOpts:
|
9991
|
+
# pkg_config_path = subprocess_check_output_str('brew', '--prefix', 'openssl')
|
9992
|
+
# if 'PKG_CONFIG_PATH' in os.environ:
|
9993
|
+
# pkg_config_path += ':' + os.environ['PKG_CONFIG_PATH']
|
9994
|
+
# return PyenvInstallOpts(env={'PKG_CONFIG_PATH': pkg_config_path})
|
9778
9995
|
|
9779
|
-
|
9996
|
+
async def opts(self) -> PyenvInstallOpts:
|
9997
|
+
return PyenvInstallOpts().merge(
|
9998
|
+
self.framework_opts(),
|
9999
|
+
await self.brew_deps_opts(),
|
10000
|
+
await self.brew_tcl_opts(),
|
10001
|
+
# self.brew_ssl_opts(),
|
10002
|
+
)
|
9780
10003
|
|
9781
|
-
rce: RemoteCommandExecutor
|
9782
|
-
async with aclosing(RemoteCommandExecutor(chan)) as rce:
|
9783
|
-
await rce.start()
|
9784
10004
|
|
9785
|
-
|
10005
|
+
PLATFORM_PYENV_INSTALL_OPTS: ta.Dict[str, PyenvInstallOptsProvider] = {
|
10006
|
+
'darwin': DarwinPyenvInstallOpts(),
|
10007
|
+
'linux': LinuxPyenvInstallOpts(),
|
10008
|
+
}
|
9786
10009
|
|
9787
10010
|
|
9788
10011
|
##
|
9789
10012
|
|
9790
10013
|
|
9791
|
-
class
|
10014
|
+
class PyenvVersionInstaller:
|
10015
|
+
"""
|
10016
|
+
Messy: can install freethreaded build with a 't' suffixed version str _or_ by THREADED_PYENV_INSTALL_OPTS - need
|
10017
|
+
latter to build custom interp with ft, need former to use canned / blessed interps. Muh.
|
10018
|
+
"""
|
10019
|
+
|
9792
10020
|
def __init__(
|
9793
10021
|
self,
|
10022
|
+
version: str,
|
10023
|
+
opts: ta.Optional[PyenvInstallOpts] = None,
|
10024
|
+
interp_opts: InterpOpts = InterpOpts(),
|
9794
10025
|
*,
|
9795
|
-
|
9796
|
-
|
10026
|
+
pyenv: Pyenv,
|
10027
|
+
|
10028
|
+
install_name: ta.Optional[str] = None,
|
10029
|
+
no_default_opts: bool = False,
|
9797
10030
|
) -> None:
|
9798
10031
|
super().__init__()
|
9799
10032
|
|
9800
|
-
self.
|
9801
|
-
self.
|
10033
|
+
self._version = version
|
10034
|
+
self._given_opts = opts
|
10035
|
+
self._interp_opts = interp_opts
|
10036
|
+
self._given_install_name = install_name
|
9802
10037
|
|
9803
|
-
|
9804
|
-
|
9805
|
-
r0, w0 = asyncio_create_bytes_channel()
|
9806
|
-
r1, w1 = asyncio_create_bytes_channel()
|
10038
|
+
self._no_default_opts = no_default_opts
|
10039
|
+
self._pyenv = pyenv
|
9807
10040
|
|
9808
|
-
|
9809
|
-
|
10041
|
+
@property
|
10042
|
+
def version(self) -> str:
|
10043
|
+
return self._version
|
9810
10044
|
|
9811
|
-
|
9812
|
-
|
9813
|
-
|
9814
|
-
|
9815
|
-
|
9816
|
-
|
9817
|
-
|
9818
|
-
|
9819
|
-
|
10045
|
+
@async_cached_nullary
|
10046
|
+
async def opts(self) -> PyenvInstallOpts:
|
10047
|
+
opts = self._given_opts
|
10048
|
+
if self._no_default_opts:
|
10049
|
+
if opts is None:
|
10050
|
+
opts = PyenvInstallOpts()
|
10051
|
+
else:
|
10052
|
+
lst = [self._given_opts if self._given_opts is not None else DEFAULT_PYENV_INSTALL_OPTS]
|
10053
|
+
if self._interp_opts.debug:
|
10054
|
+
lst.append(DEBUG_PYENV_INSTALL_OPTS)
|
10055
|
+
if self._interp_opts.threaded:
|
10056
|
+
lst.append(THREADED_PYENV_INSTALL_OPTS)
|
10057
|
+
lst.append(await PLATFORM_PYENV_INSTALL_OPTS[sys.platform].opts())
|
10058
|
+
opts = PyenvInstallOpts().merge(*lst)
|
10059
|
+
return opts
|
9820
10060
|
|
9821
|
-
|
10061
|
+
@cached_nullary
|
10062
|
+
def install_name(self) -> str:
|
10063
|
+
if self._given_install_name is not None:
|
10064
|
+
return self._given_install_name
|
10065
|
+
return self._version + ('-debug' if self._interp_opts.debug else '')
|
9822
10066
|
|
9823
|
-
|
9824
|
-
|
9825
|
-
|
10067
|
+
@async_cached_nullary
|
10068
|
+
async def install_dir(self) -> str:
|
10069
|
+
return str(os.path.join(check.not_none(await self._pyenv.root()), 'versions', self.install_name()))
|
10070
|
+
|
10071
|
+
@async_cached_nullary
|
10072
|
+
async def install(self) -> str:
|
10073
|
+
opts = await self.opts()
|
10074
|
+
env = {**os.environ, **opts.env}
|
10075
|
+
for k, l in [
|
10076
|
+
('CFLAGS', opts.cflags),
|
10077
|
+
('LDFLAGS', opts.ldflags),
|
10078
|
+
('PYTHON_CONFIGURE_OPTS', opts.conf_opts),
|
10079
|
+
]:
|
10080
|
+
v = ' '.join(l)
|
10081
|
+
if k in os.environ:
|
10082
|
+
v += ' ' + os.environ[k]
|
10083
|
+
env[k] = v
|
9826
10084
|
|
10085
|
+
conf_args = [
|
10086
|
+
*opts.opts,
|
10087
|
+
self._version,
|
10088
|
+
]
|
9827
10089
|
|
9828
|
-
|
9829
|
-
|
10090
|
+
full_args: ta.List[str]
|
10091
|
+
if self._given_install_name is not None:
|
10092
|
+
full_args = [
|
10093
|
+
os.path.join(check.not_none(await self._pyenv.root()), 'plugins', 'python-build', 'bin', 'python-build'), # noqa
|
10094
|
+
*conf_args,
|
10095
|
+
await self.install_dir(),
|
10096
|
+
]
|
10097
|
+
else:
|
10098
|
+
full_args = [
|
10099
|
+
await self._pyenv.exe(),
|
10100
|
+
'install',
|
10101
|
+
*conf_args,
|
10102
|
+
]
|
9830
10103
|
|
10104
|
+
await asyncio_subprocesses.check_call(
|
10105
|
+
*full_args,
|
10106
|
+
env=env,
|
10107
|
+
)
|
9831
10108
|
|
9832
|
-
|
10109
|
+
exe = os.path.join(await self.install_dir(), 'bin', 'python')
|
10110
|
+
if not os.path.isfile(exe):
|
10111
|
+
raise RuntimeError(f'Interpreter not found: {exe}')
|
10112
|
+
return exe
|
9833
10113
|
|
9834
10114
|
|
9835
|
-
|
9836
|
-
class CheckSystemPackageCommand(Command['CheckSystemPackageCommand.Output']):
|
9837
|
-
pkgs: ta.Sequence[str] = ()
|
10115
|
+
##
|
9838
10116
|
|
9839
|
-
def __post_init__(self) -> None:
|
9840
|
-
check.not_isinstance(self.pkgs, str)
|
9841
10117
|
|
10118
|
+
class PyenvInterpProvider(InterpProvider):
|
9842
10119
|
@dc.dataclass(frozen=True)
|
9843
|
-
class
|
9844
|
-
|
10120
|
+
class Options:
|
10121
|
+
inspect: bool = False
|
9845
10122
|
|
10123
|
+
try_update: bool = False
|
9846
10124
|
|
9847
|
-
class CheckSystemPackageCommandExecutor(CommandExecutor[CheckSystemPackageCommand, CheckSystemPackageCommand.Output]):
|
9848
10125
|
def __init__(
|
9849
10126
|
self,
|
10127
|
+
options: Options = Options(),
|
9850
10128
|
*,
|
9851
|
-
|
10129
|
+
pyenv: Pyenv,
|
10130
|
+
inspector: InterpInspector,
|
9852
10131
|
) -> None:
|
9853
10132
|
super().__init__()
|
9854
10133
|
|
9855
|
-
self.
|
10134
|
+
self._options = options
|
9856
10135
|
|
9857
|
-
|
9858
|
-
|
10136
|
+
self._pyenv = pyenv
|
10137
|
+
self._inspector = inspector
|
9859
10138
|
|
9860
|
-
|
10139
|
+
#
|
9861
10140
|
|
9862
|
-
|
10141
|
+
@staticmethod
|
10142
|
+
def guess_version(s: str) -> ta.Optional[InterpVersion]:
|
10143
|
+
def strip_sfx(s: str, sfx: str) -> ta.Tuple[str, bool]:
|
10144
|
+
if s.endswith(sfx):
|
10145
|
+
return s[:-len(sfx)], True
|
10146
|
+
return s, False
|
10147
|
+
ok = {}
|
10148
|
+
s, ok['debug'] = strip_sfx(s, '-debug')
|
10149
|
+
s, ok['threaded'] = strip_sfx(s, 't')
|
10150
|
+
try:
|
10151
|
+
v = Version(s)
|
10152
|
+
except InvalidVersion:
|
10153
|
+
return None
|
10154
|
+
return InterpVersion(v, InterpOpts(**ok))
|
9863
10155
|
|
10156
|
+
class Installed(ta.NamedTuple):
|
10157
|
+
name: str
|
10158
|
+
exe: str
|
10159
|
+
version: InterpVersion
|
9864
10160
|
|
9865
|
-
|
9866
|
-
|
9867
|
-
|
9868
|
-
|
9869
|
-
|
9870
|
-
|
9871
|
-
|
9872
|
-
|
9873
|
-
|
9874
|
-
|
9875
|
-
|
9876
|
-
|
9877
|
-
|
10161
|
+
async def _make_installed(self, vn: str, ep: str) -> ta.Optional[Installed]:
|
10162
|
+
iv: ta.Optional[InterpVersion]
|
10163
|
+
if self._options.inspect:
|
10164
|
+
try:
|
10165
|
+
iv = check.not_none(await self._inspector.inspect(ep)).iv
|
10166
|
+
except Exception as e: # noqa
|
10167
|
+
return None
|
10168
|
+
else:
|
10169
|
+
iv = self.guess_version(vn)
|
10170
|
+
if iv is None:
|
10171
|
+
return None
|
10172
|
+
return PyenvInterpProvider.Installed(
|
10173
|
+
name=vn,
|
10174
|
+
exe=ep,
|
10175
|
+
version=iv,
|
10176
|
+
)
|
9878
10177
|
|
10178
|
+
async def installed(self) -> ta.Sequence[Installed]:
|
10179
|
+
ret: ta.List[PyenvInterpProvider.Installed] = []
|
10180
|
+
for vn, ep in await self._pyenv.version_exes():
|
10181
|
+
if (i := await self._make_installed(vn, ep)) is None:
|
10182
|
+
log.debug('Invalid pyenv version: %s', vn)
|
10183
|
+
continue
|
10184
|
+
ret.append(i)
|
10185
|
+
return ret
|
9879
10186
|
|
9880
|
-
|
10187
|
+
#
|
9881
10188
|
|
10189
|
+
async def get_installed_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
|
10190
|
+
return [i.version for i in await self.installed()]
|
9882
10191
|
|
9883
|
-
|
9884
|
-
|
9885
|
-
|
9886
|
-
|
9887
|
-
|
9888
|
-
|
9889
|
-
|
9890
|
-
|
10192
|
+
async def get_installed_version(self, version: InterpVersion) -> Interp:
|
10193
|
+
for i in await self.installed():
|
10194
|
+
if i.version == version:
|
10195
|
+
return Interp(
|
10196
|
+
exe=i.exe,
|
10197
|
+
version=i.version,
|
10198
|
+
)
|
10199
|
+
raise KeyError(version)
|
9891
10200
|
|
9892
|
-
|
10201
|
+
#
|
9893
10202
|
|
9894
|
-
|
10203
|
+
async def _get_installable_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
|
10204
|
+
lst = []
|
9895
10205
|
|
9896
|
-
|
9897
|
-
|
9898
|
-
|
9899
|
-
|
10206
|
+
for vs in await self._pyenv.installable_versions():
|
10207
|
+
if (iv := self.guess_version(vs)) is None:
|
10208
|
+
continue
|
10209
|
+
if iv.opts.debug:
|
10210
|
+
raise Exception('Pyenv installable versions not expected to have debug suffix')
|
10211
|
+
for d in [False, True]:
|
10212
|
+
lst.append(dc.replace(iv, opts=dc.replace(iv.opts, debug=d)))
|
9900
10213
|
|
9901
|
-
|
9902
|
-
return await asyncio_subprocesses.check_output_str('pyenv', 'root')
|
10214
|
+
return lst
|
9903
10215
|
|
9904
|
-
|
9905
|
-
|
9906
|
-
return d
|
10216
|
+
async def get_installable_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
|
10217
|
+
lst = await self._get_installable_versions(spec)
|
9907
10218
|
|
9908
|
-
|
10219
|
+
if self._options.try_update and not any(v in spec for v in lst):
|
10220
|
+
if self._pyenv.update():
|
10221
|
+
lst = await self._get_installable_versions(spec)
|
10222
|
+
|
10223
|
+
return lst
|
10224
|
+
|
10225
|
+
async def install_version(self, version: InterpVersion) -> Interp:
|
10226
|
+
inst_version = str(version.version)
|
10227
|
+
inst_opts = version.opts
|
10228
|
+
if inst_opts.threaded:
|
10229
|
+
inst_version += 't'
|
10230
|
+
inst_opts = dc.replace(inst_opts, threaded=False)
|
9909
10231
|
|
9910
|
-
|
9911
|
-
|
9912
|
-
|
10232
|
+
installer = PyenvVersionInstaller(
|
10233
|
+
inst_version,
|
10234
|
+
interp_opts=inst_opts,
|
10235
|
+
pyenv=self._pyenv,
|
10236
|
+
)
|
9913
10237
|
|
9914
|
-
|
9915
|
-
|
9916
|
-
return []
|
9917
|
-
ret = []
|
9918
|
-
vp = os.path.join(root, 'versions')
|
9919
|
-
if os.path.isdir(vp):
|
9920
|
-
for dn in os.listdir(vp):
|
9921
|
-
ep = os.path.join(vp, dn, 'bin', 'python')
|
9922
|
-
if not os.path.isfile(ep):
|
9923
|
-
continue
|
9924
|
-
ret.append((dn, ep))
|
9925
|
-
return ret
|
10238
|
+
exe = await installer.install()
|
10239
|
+
return Interp(exe, version)
|
9926
10240
|
|
9927
|
-
async def installable_versions(self) -> ta.List[str]:
|
9928
|
-
if await self.root() is None:
|
9929
|
-
return []
|
9930
|
-
ret = []
|
9931
|
-
s = await asyncio_subprocesses.check_output_str(await self.exe(), 'install', '--list')
|
9932
|
-
for l in s.splitlines():
|
9933
|
-
if not l.startswith(' '):
|
9934
|
-
continue
|
9935
|
-
l = l.strip()
|
9936
|
-
if not l:
|
9937
|
-
continue
|
9938
|
-
ret.append(l)
|
9939
|
-
return ret
|
9940
10241
|
|
9941
|
-
|
9942
|
-
|
9943
|
-
return False
|
9944
|
-
if not os.path.isdir(os.path.join(root, '.git')):
|
9945
|
-
return False
|
9946
|
-
await asyncio_subprocesses.check_call('git', 'pull', cwd=root)
|
9947
|
-
return True
|
10242
|
+
########################################
|
10243
|
+
# ../commands/inject.py
|
9948
10244
|
|
9949
10245
|
|
9950
10246
|
##
|
9951
10247
|
|
9952
10248
|
|
9953
|
-
|
9954
|
-
|
9955
|
-
|
9956
|
-
|
9957
|
-
|
9958
|
-
|
9959
|
-
|
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
|
+
]
|
9960
10256
|
|
9961
|
-
|
9962
|
-
|
9963
|
-
|
9964
|
-
|
9965
|
-
|
9966
|
-
ldflags=list(itertools.chain.from_iterable(o.ldflags for o in [self, *others])),
|
9967
|
-
env=dict(itertools.chain.from_iterable(o.env.items() for o in [self, *others])),
|
9968
|
-
)
|
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
|
+
])
|
9969
10262
|
|
10263
|
+
return inj.as_bindings(*lst)
|
9970
10264
|
|
9971
|
-
# TODO: https://github.com/pyenv/pyenv/blob/master/plugins/python-build/README.md#building-for-maximum-performance
|
9972
|
-
DEFAULT_PYENV_INSTALL_OPTS = PyenvInstallOpts(
|
9973
|
-
opts=[
|
9974
|
-
'-s',
|
9975
|
-
'-v',
|
9976
|
-
'-k',
|
9977
|
-
],
|
9978
|
-
conf_opts=[
|
9979
|
-
# FIXME: breaks on mac for older py's
|
9980
|
-
'--enable-loadable-sqlite-extensions',
|
9981
10265
|
|
9982
|
-
|
10266
|
+
##
|
9983
10267
|
|
9984
|
-
'--enable-optimizations',
|
9985
|
-
'--with-lto',
|
9986
10268
|
|
9987
|
-
|
10269
|
+
@dc.dataclass(frozen=True)
|
10270
|
+
class _FactoryCommandExecutor(CommandExecutor):
|
10271
|
+
factory: ta.Callable[[], CommandExecutor]
|
9988
10272
|
|
9989
|
-
|
9990
|
-
|
9991
|
-
cflags=[
|
9992
|
-
# '-march=native',
|
9993
|
-
# '-mtune=native',
|
9994
|
-
],
|
9995
|
-
)
|
10273
|
+
def execute(self, i: Command) -> ta.Awaitable[Command.Output]:
|
10274
|
+
return self.factory().execute(i)
|
9996
10275
|
|
9997
|
-
DEBUG_PYENV_INSTALL_OPTS = PyenvInstallOpts(opts=['-g'])
|
9998
10276
|
|
9999
|
-
|
10277
|
+
##
|
10000
10278
|
|
10001
10279
|
|
10002
|
-
|
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),
|
10003
10287
|
|
10288
|
+
inj.bind_array(CommandExecutorRegistration),
|
10289
|
+
inj.bind_array_type(CommandExecutorRegistration, CommandExecutorRegistrations),
|
10004
10290
|
|
10005
|
-
|
10006
|
-
|
10007
|
-
def opts(self) -> ta.Awaitable[PyenvInstallOpts]:
|
10008
|
-
raise NotImplementedError
|
10291
|
+
inj.bind(build_command_name_map, singleton=True),
|
10292
|
+
]
|
10009
10293
|
|
10294
|
+
#
|
10010
10295
|
|
10011
|
-
|
10012
|
-
|
10013
|
-
return PyenvInstallOpts()
|
10296
|
+
def provide_obj_marshaler_installer(cmds: CommandNameMap) -> ObjMarshalerInstaller:
|
10297
|
+
return ObjMarshalerInstaller(functools.partial(install_command_marshaling, cmds))
|
10014
10298
|
|
10299
|
+
lst.append(inj.bind(provide_obj_marshaler_installer, array=True))
|
10015
10300
|
|
10016
|
-
|
10017
|
-
@cached_nullary
|
10018
|
-
def framework_opts(self) -> PyenvInstallOpts:
|
10019
|
-
return PyenvInstallOpts(conf_opts=['--enable-framework'])
|
10301
|
+
#
|
10020
10302
|
|
10021
|
-
|
10022
|
-
|
10023
|
-
|
10303
|
+
def provide_command_executor_map(
|
10304
|
+
injector: Injector,
|
10305
|
+
crs: CommandExecutorRegistrations,
|
10306
|
+
) -> CommandExecutorMap:
|
10307
|
+
dct: ta.Dict[ta.Type[Command], CommandExecutor] = {}
|
10024
10308
|
|
10025
|
-
|
10026
|
-
|
10027
|
-
|
10028
|
-
|
10029
|
-
'zlib',
|
10030
|
-
]
|
10309
|
+
cr: CommandExecutorRegistration
|
10310
|
+
for cr in crs:
|
10311
|
+
if cr.command_cls in dct:
|
10312
|
+
raise KeyError(cr.command_cls)
|
10031
10313
|
|
10032
|
-
|
10033
|
-
|
10034
|
-
|
10035
|
-
|
10036
|
-
|
10037
|
-
dep_prefix = await asyncio_subprocesses.check_output_str('brew', '--prefix', dep)
|
10038
|
-
cflags.append(f'-I{dep_prefix}/include')
|
10039
|
-
ldflags.append(f'-L{dep_prefix}/lib')
|
10040
|
-
return PyenvInstallOpts(
|
10041
|
-
cflags=cflags,
|
10042
|
-
ldflags=ldflags,
|
10043
|
-
)
|
10314
|
+
factory = functools.partial(injector.provide, cr.executor_cls)
|
10315
|
+
if main_config.debug:
|
10316
|
+
ce = factory()
|
10317
|
+
else:
|
10318
|
+
ce = _FactoryCommandExecutor(factory)
|
10044
10319
|
|
10045
|
-
|
10046
|
-
async def brew_tcl_opts(self) -> PyenvInstallOpts:
|
10047
|
-
if await asyncio_subprocesses.try_output('brew', '--prefix', 'tcl-tk') is None:
|
10048
|
-
return PyenvInstallOpts()
|
10320
|
+
dct[cr.command_cls] = ce
|
10049
10321
|
|
10050
|
-
|
10051
|
-
tcl_tk_ver_str = await asyncio_subprocesses.check_output_str('brew', 'ls', '--versions', 'tcl-tk')
|
10052
|
-
tcl_tk_ver = '.'.join(tcl_tk_ver_str.split()[1].split('.')[:2])
|
10322
|
+
return CommandExecutorMap(dct)
|
10053
10323
|
|
10054
|
-
|
10055
|
-
|
10056
|
-
f"--with-tcltk-libs='-L{tcl_tk_prefix}/lib -ltcl{tcl_tk_ver} -ltk{tcl_tk_ver}'",
|
10057
|
-
])
|
10324
|
+
lst.extend([
|
10325
|
+
inj.bind(provide_command_executor_map, singleton=True),
|
10058
10326
|
|
10059
|
-
|
10060
|
-
|
10061
|
-
# pkg_config_path = subprocess_check_output_str('brew', '--prefix', 'openssl')
|
10062
|
-
# if 'PKG_CONFIG_PATH' in os.environ:
|
10063
|
-
# pkg_config_path += ':' + os.environ['PKG_CONFIG_PATH']
|
10064
|
-
# return PyenvInstallOpts(env={'PKG_CONFIG_PATH': pkg_config_path})
|
10327
|
+
inj.bind(LocalCommandExecutor, singleton=True, eager=main_config.debug),
|
10328
|
+
])
|
10065
10329
|
|
10066
|
-
|
10067
|
-
return PyenvInstallOpts().merge(
|
10068
|
-
self.framework_opts(),
|
10069
|
-
await self.brew_deps_opts(),
|
10070
|
-
await self.brew_tcl_opts(),
|
10071
|
-
# self.brew_ssl_opts(),
|
10072
|
-
)
|
10330
|
+
#
|
10073
10331
|
|
10332
|
+
lst.extend([
|
10333
|
+
bind_command(PingCommand, PingCommandExecutor),
|
10334
|
+
bind_command(SubprocessCommand, SubprocessCommandExecutor),
|
10335
|
+
])
|
10074
10336
|
|
10075
|
-
|
10076
|
-
'darwin': DarwinPyenvInstallOpts(),
|
10077
|
-
'linux': LinuxPyenvInstallOpts(),
|
10078
|
-
}
|
10337
|
+
#
|
10079
10338
|
|
10339
|
+
return inj.as_bindings(*lst)
|
10080
10340
|
|
10081
|
-
##
|
10082
10341
|
|
10342
|
+
########################################
|
10343
|
+
# ../deploy/paths/manager.py
|
10083
10344
|
|
10084
|
-
class PyenvVersionInstaller:
|
10085
|
-
"""
|
10086
|
-
Messy: can install freethreaded build with a 't' suffixed version str _or_ by THREADED_PYENV_INSTALL_OPTS - need
|
10087
|
-
latter to build custom interp with ft, need former to use canned / blessed interps. Muh.
|
10088
|
-
"""
|
10089
10345
|
|
10346
|
+
class DeployPathsManager:
|
10090
10347
|
def __init__(
|
10091
10348
|
self,
|
10092
|
-
version: str,
|
10093
|
-
opts: ta.Optional[PyenvInstallOpts] = None,
|
10094
|
-
interp_opts: InterpOpts = InterpOpts(),
|
10095
10349
|
*,
|
10096
|
-
|
10097
|
-
no_default_opts: bool = False,
|
10098
|
-
pyenv: Pyenv = Pyenv(),
|
10350
|
+
deploy_path_owners: DeployPathOwners,
|
10099
10351
|
) -> None:
|
10100
10352
|
super().__init__()
|
10101
10353
|
|
10102
|
-
self.
|
10103
|
-
self._given_opts = opts
|
10104
|
-
self._interp_opts = interp_opts
|
10105
|
-
self._given_install_name = install_name
|
10106
|
-
|
10107
|
-
self._no_default_opts = no_default_opts
|
10108
|
-
self._pyenv = pyenv
|
10109
|
-
|
10110
|
-
@property
|
10111
|
-
def version(self) -> str:
|
10112
|
-
return self._version
|
10354
|
+
self._deploy_path_owners = deploy_path_owners
|
10113
10355
|
|
10114
|
-
@
|
10115
|
-
|
10116
|
-
|
10117
|
-
|
10118
|
-
|
10119
|
-
|
10120
|
-
|
10121
|
-
|
10122
|
-
|
10123
|
-
lst.append(DEBUG_PYENV_INSTALL_OPTS)
|
10124
|
-
if self._interp_opts.threaded:
|
10125
|
-
lst.append(THREADED_PYENV_INSTALL_OPTS)
|
10126
|
-
lst.append(await PLATFORM_PYENV_INSTALL_OPTS[sys.platform].opts())
|
10127
|
-
opts = PyenvInstallOpts().merge(*lst)
|
10128
|
-
return opts
|
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
|
10129
10365
|
|
10130
|
-
|
10131
|
-
|
10132
|
-
if self._given_install_name is not None:
|
10133
|
-
return self._given_install_name
|
10134
|
-
return self._version + ('-debug' if self._interp_opts.debug else '')
|
10366
|
+
def validate_deploy_paths(self) -> None:
|
10367
|
+
self.owners_by_path()
|
10135
10368
|
|
10136
|
-
@async_cached_nullary
|
10137
|
-
async def install_dir(self) -> str:
|
10138
|
-
return str(os.path.join(check.not_none(await self._pyenv.root()), 'versions', self.install_name()))
|
10139
10369
|
|
10140
|
-
|
10141
|
-
|
10142
|
-
opts = await self.opts()
|
10143
|
-
env = {**os.environ, **opts.env}
|
10144
|
-
for k, l in [
|
10145
|
-
('CFLAGS', opts.cflags),
|
10146
|
-
('LDFLAGS', opts.ldflags),
|
10147
|
-
('PYTHON_CONFIGURE_OPTS', opts.conf_opts),
|
10148
|
-
]:
|
10149
|
-
v = ' '.join(l)
|
10150
|
-
if k in os.environ:
|
10151
|
-
v += ' ' + os.environ[k]
|
10152
|
-
env[k] = v
|
10370
|
+
########################################
|
10371
|
+
# ../deploy/tmp.py
|
10153
10372
|
|
10154
|
-
conf_args = [
|
10155
|
-
*opts.opts,
|
10156
|
-
self._version,
|
10157
|
-
]
|
10158
10373
|
|
10159
|
-
|
10160
|
-
|
10161
|
-
full_args = [
|
10162
|
-
os.path.join(check.not_none(await self._pyenv.root()), 'plugins', 'python-build', 'bin', 'python-build'), # noqa
|
10163
|
-
*conf_args,
|
10164
|
-
await self.install_dir(),
|
10165
|
-
]
|
10166
|
-
else:
|
10167
|
-
full_args = [
|
10168
|
-
await self._pyenv.exe(),
|
10169
|
-
'install',
|
10170
|
-
*conf_args,
|
10171
|
-
]
|
10374
|
+
class DeployHomeAtomics(Func1[DeployHome, AtomicPathSwapping]):
|
10375
|
+
pass
|
10172
10376
|
|
10173
|
-
|
10174
|
-
|
10175
|
-
|
10377
|
+
|
10378
|
+
class DeployTmpManager(
|
10379
|
+
SingleDirDeployPathOwner,
|
10380
|
+
):
|
10381
|
+
def __init__(self) -> None:
|
10382
|
+
super().__init__(
|
10383
|
+
owned_dir='tmp',
|
10176
10384
|
)
|
10177
10385
|
|
10178
|
-
|
10179
|
-
|
10180
|
-
|
10181
|
-
|
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
|
10182
10395
|
|
10183
10396
|
|
10184
10397
|
##
|
10185
10398
|
|
10186
10399
|
|
10187
|
-
class
|
10400
|
+
class PyremoteRemoteExecutionConnector:
|
10188
10401
|
def __init__(
|
10189
10402
|
self,
|
10190
|
-
pyenv: Pyenv = Pyenv(),
|
10191
|
-
|
10192
|
-
inspect: bool = False,
|
10193
|
-
inspector: InterpInspector = INTERP_INSPECTOR,
|
10194
|
-
|
10195
10403
|
*,
|
10196
|
-
|
10197
|
-
|
10404
|
+
spawning: RemoteSpawning,
|
10405
|
+
msh: ObjMarshalerManager,
|
10406
|
+
payload_file: ta.Optional[RemoteExecutionPayloadFile] = None,
|
10198
10407
|
) -> None:
|
10199
10408
|
super().__init__()
|
10200
10409
|
|
10201
|
-
self.
|
10410
|
+
self._spawning = spawning
|
10411
|
+
self._msh = msh
|
10412
|
+
self._payload_file = payload_file
|
10202
10413
|
|
10203
|
-
|
10204
|
-
self._inspector = inspector
|
10414
|
+
#
|
10205
10415
|
|
10206
|
-
|
10416
|
+
@cached_nullary
|
10417
|
+
def _payload_src(self) -> str:
|
10418
|
+
return get_remote_payload_src(file=self._payload_file)
|
10207
10419
|
|
10208
|
-
|
10420
|
+
@cached_nullary
|
10421
|
+
def _remote_src(self) -> ta.Sequence[str]:
|
10422
|
+
return [
|
10423
|
+
self._payload_src(),
|
10424
|
+
'_remote_execution_main()',
|
10425
|
+
]
|
10209
10426
|
|
10210
|
-
@
|
10211
|
-
def
|
10212
|
-
|
10213
|
-
if s.endswith(sfx):
|
10214
|
-
return s[:-len(sfx)], True
|
10215
|
-
return s, False
|
10216
|
-
ok = {}
|
10217
|
-
s, ok['debug'] = strip_sfx(s, '-debug')
|
10218
|
-
s, ok['threaded'] = strip_sfx(s, 't')
|
10219
|
-
try:
|
10220
|
-
v = Version(s)
|
10221
|
-
except InvalidVersion:
|
10222
|
-
return None
|
10223
|
-
return InterpVersion(v, InterpOpts(**ok))
|
10427
|
+
@cached_nullary
|
10428
|
+
def _spawn_src(self) -> str:
|
10429
|
+
return pyremote_build_bootstrap_cmd(__package__ or 'manage')
|
10224
10430
|
|
10225
|
-
|
10226
|
-
name: str
|
10227
|
-
exe: str
|
10228
|
-
version: InterpVersion
|
10431
|
+
#
|
10229
10432
|
|
10230
|
-
|
10231
|
-
|
10232
|
-
|
10233
|
-
|
10234
|
-
|
10235
|
-
|
10236
|
-
|
10237
|
-
|
10238
|
-
iv = self.guess_version(vn)
|
10239
|
-
if iv is None:
|
10240
|
-
return None
|
10241
|
-
return PyenvInterpProvider.Installed(
|
10242
|
-
name=vn,
|
10243
|
-
exe=ep,
|
10244
|
-
version=iv,
|
10245
|
-
)
|
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()
|
10246
10441
|
|
10247
|
-
|
10248
|
-
|
10249
|
-
|
10250
|
-
|
10251
|
-
|
10252
|
-
|
10253
|
-
|
10254
|
-
|
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
|
+
)
|
10255
10456
|
|
10256
|
-
|
10457
|
+
chan = RemoteChannelImpl(
|
10458
|
+
proc.stdout,
|
10459
|
+
proc.stdin,
|
10460
|
+
msh=self._msh,
|
10461
|
+
)
|
10257
10462
|
|
10258
|
-
|
10259
|
-
return [i.version for i in await self.installed()]
|
10463
|
+
await chan.send_obj(bs)
|
10260
10464
|
|
10261
|
-
|
10262
|
-
|
10263
|
-
|
10264
|
-
return Interp(
|
10265
|
-
exe=i.exe,
|
10266
|
-
version=i.version,
|
10267
|
-
)
|
10268
|
-
raise KeyError(version)
|
10465
|
+
rce: RemoteCommandExecutor
|
10466
|
+
async with aclosing(RemoteCommandExecutor(chan)) as rce:
|
10467
|
+
await rce.start()
|
10269
10468
|
|
10270
|
-
|
10469
|
+
yield rce
|
10271
10470
|
|
10272
|
-
async def _get_installable_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
|
10273
|
-
lst = []
|
10274
10471
|
|
10275
|
-
|
10276
|
-
if (iv := self.guess_version(vs)) is None:
|
10277
|
-
continue
|
10278
|
-
if iv.opts.debug:
|
10279
|
-
raise Exception('Pyenv installable versions not expected to have debug suffix')
|
10280
|
-
for d in [False, True]:
|
10281
|
-
lst.append(dc.replace(iv, opts=dc.replace(iv.opts, debug=d)))
|
10472
|
+
##
|
10282
10473
|
|
10283
|
-
return lst
|
10284
10474
|
|
10285
|
-
|
10286
|
-
|
10475
|
+
class InProcessRemoteExecutionConnector:
|
10476
|
+
def __init__(
|
10477
|
+
self,
|
10478
|
+
*,
|
10479
|
+
msh: ObjMarshalerManager,
|
10480
|
+
local_executor: LocalCommandExecutor,
|
10481
|
+
) -> None:
|
10482
|
+
super().__init__()
|
10287
10483
|
|
10288
|
-
|
10289
|
-
|
10290
|
-
lst = await self._get_installable_versions(spec)
|
10484
|
+
self._msh = msh
|
10485
|
+
self._local_executor = local_executor
|
10291
10486
|
|
10292
|
-
|
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()
|
10293
10491
|
|
10294
|
-
|
10295
|
-
|
10296
|
-
inst_opts = version.opts
|
10297
|
-
if inst_opts.threaded:
|
10298
|
-
inst_version += 't'
|
10299
|
-
inst_opts = dc.replace(inst_opts, threaded=False)
|
10492
|
+
remote_chan = RemoteChannelImpl(r0, w1, msh=self._msh)
|
10493
|
+
local_chan = RemoteChannelImpl(r1, w0, msh=self._msh)
|
10300
10494
|
|
10301
|
-
|
10302
|
-
|
10303
|
-
|
10495
|
+
rch = _RemoteCommandHandler(
|
10496
|
+
remote_chan,
|
10497
|
+
self._local_executor,
|
10304
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()
|
10305
10504
|
|
10306
|
-
|
10307
|
-
|
10505
|
+
yield rce
|
10506
|
+
|
10507
|
+
finally:
|
10508
|
+
rch.stop()
|
10509
|
+
await rch_task
|
10308
10510
|
|
10309
10511
|
|
10310
10512
|
########################################
|
10311
|
-
#
|
10312
|
-
|
10313
|
-
|
10314
|
-
|
10315
|
-
|
10316
|
-
|
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]
|
10317
10529
|
|
10318
10530
|
|
10319
|
-
|
10531
|
+
class CheckSystemPackageCommandExecutor(CommandExecutor[CheckSystemPackageCommand, CheckSystemPackageCommand.Output]):
|
10532
|
+
def __init__(
|
10533
|
+
self,
|
10534
|
+
*,
|
10535
|
+
mgr: SystemPackageManager,
|
10536
|
+
) -> None:
|
10537
|
+
super().__init__()
|
10320
10538
|
|
10539
|
+
self._mgr = mgr
|
10321
10540
|
|
10322
|
-
|
10323
|
-
|
10324
|
-
cmd: str = 'python3'
|
10325
|
-
path: ta.Optional[str] = None
|
10541
|
+
async def execute(self, cmd: CheckSystemPackageCommand) -> CheckSystemPackageCommand.Output:
|
10542
|
+
log.info('Checking system package!')
|
10326
10543
|
|
10327
|
-
|
10328
|
-
inspector: InterpInspector = INTERP_INSPECTOR
|
10544
|
+
ret = await self._mgr.query(*cmd.pkgs)
|
10329
10545
|
|
10330
|
-
|
10546
|
+
return CheckSystemPackageCommand.Output(list(ret.values()))
|
10331
10547
|
|
10332
|
-
@staticmethod
|
10333
|
-
def _re_which(
|
10334
|
-
pat: re.Pattern,
|
10335
|
-
*,
|
10336
|
-
mode: int = os.F_OK | os.X_OK,
|
10337
|
-
path: ta.Optional[str] = None,
|
10338
|
-
) -> ta.List[str]:
|
10339
|
-
if path is None:
|
10340
|
-
path = os.environ.get('PATH', None)
|
10341
|
-
if path is None:
|
10342
|
-
try:
|
10343
|
-
path = os.confstr('CS_PATH')
|
10344
|
-
except (AttributeError, ValueError):
|
10345
|
-
path = os.defpath
|
10346
10548
|
|
10347
|
-
|
10348
|
-
|
10549
|
+
########################################
|
10550
|
+
# ../../../omdev/interp/providers/inject.py
|
10349
10551
|
|
10350
|
-
path = os.fsdecode(path)
|
10351
|
-
pathlst = path.split(os.pathsep)
|
10352
10552
|
|
10353
|
-
|
10354
|
-
|
10553
|
+
def bind_interp_providers() -> InjectorBindings:
|
10554
|
+
lst: ta.List[InjectorBindingOrBindings] = [
|
10555
|
+
inj.bind_array(InterpProvider),
|
10556
|
+
inj.bind_array_type(InterpProvider, InterpProviders),
|
10355
10557
|
|
10356
|
-
|
10357
|
-
|
10358
|
-
for d in pathlst:
|
10359
|
-
normdir = os.path.normcase(d)
|
10360
|
-
if normdir not in seen:
|
10361
|
-
seen.add(normdir)
|
10362
|
-
if not _access_check(normdir, mode):
|
10363
|
-
continue
|
10364
|
-
for thefile in os.listdir(d):
|
10365
|
-
name = os.path.join(d, thefile)
|
10366
|
-
if not (
|
10367
|
-
os.path.isfile(name) and
|
10368
|
-
pat.fullmatch(thefile) and
|
10369
|
-
_access_check(name, mode)
|
10370
|
-
):
|
10371
|
-
continue
|
10372
|
-
out.append(name)
|
10558
|
+
inj.bind(RunningInterpProvider, singleton=True),
|
10559
|
+
inj.bind(InterpProvider, to_key=RunningInterpProvider, array=True),
|
10373
10560
|
|
10374
|
-
|
10561
|
+
inj.bind(SystemInterpProvider, singleton=True),
|
10562
|
+
inj.bind(InterpProvider, to_key=SystemInterpProvider, array=True),
|
10563
|
+
]
|
10375
10564
|
|
10376
|
-
|
10377
|
-
def exes(self) -> ta.List[str]:
|
10378
|
-
return self._re_which(
|
10379
|
-
re.compile(r'python3(\.\d+)?'),
|
10380
|
-
path=self.path,
|
10381
|
-
)
|
10565
|
+
return inj.as_bindings(*lst)
|
10382
10566
|
|
10383
|
-
#
|
10384
10567
|
|
10385
|
-
|
10386
|
-
|
10387
|
-
s = os.path.basename(exe)
|
10388
|
-
if s.startswith('python'):
|
10389
|
-
s = s[len('python'):]
|
10390
|
-
if '.' in s:
|
10391
|
-
try:
|
10392
|
-
return InterpVersion.parse(s)
|
10393
|
-
except InvalidVersion:
|
10394
|
-
pass
|
10395
|
-
ii = await self.inspector.inspect(exe)
|
10396
|
-
return ii.iv if ii is not None else None
|
10568
|
+
########################################
|
10569
|
+
# ../../../omdev/interp/pyenv/inject.py
|
10397
10570
|
|
10398
|
-
async def exe_versions(self) -> ta.Sequence[ta.Tuple[str, InterpVersion]]:
|
10399
|
-
lst = []
|
10400
|
-
for e in self.exes():
|
10401
|
-
if (ev := await self.get_exe_version(e)) is None:
|
10402
|
-
log.debug('Invalid system version: %s', e)
|
10403
|
-
continue
|
10404
|
-
lst.append((e, ev))
|
10405
|
-
return lst
|
10406
10571
|
|
10407
|
-
|
10572
|
+
def bind_interp_pyenv() -> InjectorBindings:
|
10573
|
+
lst: ta.List[InjectorBindingOrBindings] = [
|
10574
|
+
inj.bind(Pyenv, singleton=True),
|
10408
10575
|
|
10409
|
-
|
10410
|
-
|
10576
|
+
inj.bind(PyenvInterpProvider, singleton=True),
|
10577
|
+
inj.bind(InterpProvider, to_key=PyenvInterpProvider, array=True),
|
10578
|
+
]
|
10411
10579
|
|
10412
|
-
|
10413
|
-
for e, ev in await self.exe_versions():
|
10414
|
-
if ev != version:
|
10415
|
-
continue
|
10416
|
-
return Interp(
|
10417
|
-
exe=e,
|
10418
|
-
version=ev,
|
10419
|
-
)
|
10420
|
-
raise KeyError(version)
|
10580
|
+
return inj.as_bindings(*lst)
|
10421
10581
|
|
10422
10582
|
|
10423
10583
|
########################################
|
@@ -10784,101 +10944,44 @@ class SshManageTargetConnector(ManageTargetConnector):
|
|
10784
10944
|
|
10785
10945
|
|
10786
10946
|
########################################
|
10787
|
-
# ../../../omdev/interp/
|
10788
|
-
|
10789
|
-
|
10790
|
-
INTERP_PROVIDER_TYPES_BY_NAME: ta.Mapping[str, ta.Type[InterpProvider]] = {
|
10791
|
-
cls.name: cls for cls in deep_subclasses(InterpProvider) if abc.ABC not in cls.__bases__ # type: ignore
|
10792
|
-
}
|
10793
|
-
|
10794
|
-
|
10795
|
-
class InterpResolver:
|
10796
|
-
def __init__(
|
10797
|
-
self,
|
10798
|
-
providers: ta.Sequence[ta.Tuple[str, InterpProvider]],
|
10799
|
-
) -> None:
|
10800
|
-
super().__init__()
|
10801
|
-
|
10802
|
-
self._providers: ta.Mapping[str, InterpProvider] = collections.OrderedDict(providers)
|
10803
|
-
|
10804
|
-
async def _resolve_installed(self, spec: InterpSpecifier) -> ta.Optional[ta.Tuple[InterpProvider, InterpVersion]]:
|
10805
|
-
lst = [
|
10806
|
-
(i, si)
|
10807
|
-
for i, p in enumerate(self._providers.values())
|
10808
|
-
for si in await p.get_installed_versions(spec)
|
10809
|
-
if spec.contains(si)
|
10810
|
-
]
|
10811
|
-
|
10812
|
-
slst = sorted(lst, key=lambda t: (-t[0], t[1].version))
|
10813
|
-
if not slst:
|
10814
|
-
return None
|
10947
|
+
# ../../../omdev/interp/inject.py
|
10815
10948
|
|
10816
|
-
bi, bv = slst[-1]
|
10817
|
-
bp = list(self._providers.values())[bi]
|
10818
|
-
return (bp, bv)
|
10819
10949
|
|
10820
|
-
|
10821
|
-
|
10822
|
-
|
10823
|
-
*,
|
10824
|
-
install: bool = False,
|
10825
|
-
) -> ta.Optional[Interp]:
|
10826
|
-
tup = await self._resolve_installed(spec)
|
10827
|
-
if tup is not None:
|
10828
|
-
bp, bv = tup
|
10829
|
-
return await bp.get_installed_version(bv)
|
10950
|
+
def bind_interp() -> InjectorBindings:
|
10951
|
+
lst: ta.List[InjectorBindingOrBindings] = [
|
10952
|
+
bind_interp_providers(),
|
10830
10953
|
|
10831
|
-
|
10832
|
-
return None
|
10954
|
+
bind_interp_pyenv(),
|
10833
10955
|
|
10834
|
-
|
10956
|
+
bind_interp_uv(),
|
10835
10957
|
|
10836
|
-
|
10837
|
-
|
10838
|
-
key=lambda s: s.version,
|
10839
|
-
)
|
10840
|
-
if not sv:
|
10841
|
-
return None
|
10958
|
+
inj.bind(InterpInspector, singleton=True),
|
10959
|
+
]
|
10842
10960
|
|
10843
|
-
|
10844
|
-
return await tp.install_version(bv)
|
10961
|
+
#
|
10845
10962
|
|
10846
|
-
|
10847
|
-
|
10848
|
-
|
10849
|
-
|
10850
|
-
|
10851
|
-
|
10852
|
-
|
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,
|
10853
10971
|
]
|
10854
|
-
|
10855
|
-
print(f' {n}')
|
10856
|
-
for si in lst:
|
10857
|
-
print(f' {si}')
|
10858
|
-
|
10859
|
-
print()
|
10972
|
+
]
|
10860
10973
|
|
10861
|
-
|
10862
|
-
for n, p in self._providers.items():
|
10863
|
-
lst = [
|
10864
|
-
si
|
10865
|
-
for si in await p.get_installable_versions(spec)
|
10866
|
-
if spec.contains(si)
|
10867
|
-
]
|
10868
|
-
if lst:
|
10869
|
-
print(f' {n}')
|
10870
|
-
for si in lst:
|
10871
|
-
print(f' {si}')
|
10974
|
+
return InterpResolverProviders([(rp.name, rp) for rp in rps])
|
10872
10975
|
|
10976
|
+
lst.append(inj.bind(provide_interp_resolver_providers, singleton=True))
|
10873
10977
|
|
10874
|
-
|
10875
|
-
|
10876
|
-
|
10978
|
+
lst.extend([
|
10979
|
+
inj.bind(InterpResolver, singleton=True),
|
10980
|
+
])
|
10877
10981
|
|
10878
|
-
|
10982
|
+
#
|
10879
10983
|
|
10880
|
-
|
10881
|
-
]])
|
10984
|
+
return inj.as_bindings(*lst)
|
10882
10985
|
|
10883
10986
|
|
10884
10987
|
########################################
|
@@ -10910,6 +11013,15 @@ def bind_targets() -> InjectorBindings:
|
|
10910
11013
|
return inj.as_bindings(*lst)
|
10911
11014
|
|
10912
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
|
+
|
10913
11025
|
########################################
|
10914
11026
|
# ../deploy/interp.py
|
10915
11027
|
|
@@ -10932,7 +11044,7 @@ class InterpCommand(Command['InterpCommand.Output']):
|
|
10932
11044
|
class InterpCommandExecutor(CommandExecutor[InterpCommand, InterpCommand.Output]):
|
10933
11045
|
async def execute(self, cmd: InterpCommand) -> InterpCommand.Output:
|
10934
11046
|
i = InterpSpecifier.parse(check.not_none(cmd.spec))
|
10935
|
-
o = check.not_none(await
|
11047
|
+
o = check.not_none(await get_default_interp_resolver().resolve(i, install=cmd.install))
|
10936
11048
|
return InterpCommand.Output(
|
10937
11049
|
exe=o.exe,
|
10938
11050
|
version=str(o.version.version),
|
@@ -10959,7 +11071,7 @@ class DeployVenvManager:
|
|
10959
11071
|
) -> None:
|
10960
11072
|
if spec.interp is not None:
|
10961
11073
|
i = InterpSpecifier.parse(check.not_none(spec.interp))
|
10962
|
-
o = check.not_none(await
|
11074
|
+
o = check.not_none(await get_default_interp_resolver().resolve(i))
|
10963
11075
|
sys_exe = o.exe
|
10964
11076
|
else:
|
10965
11077
|
sys_exe = 'python3'
|
@@ -11166,16 +11278,18 @@ class DeployDriver:
|
|
11166
11278
|
self,
|
11167
11279
|
*,
|
11168
11280
|
spec: DeploySpec,
|
11281
|
+
home: DeployHome,
|
11282
|
+
time: DeployTime,
|
11169
11283
|
|
11170
|
-
deploys: DeployManager,
|
11171
11284
|
paths: DeployPathsManager,
|
11172
11285
|
apps: DeployAppManager,
|
11173
11286
|
) -> None:
|
11174
11287
|
super().__init__()
|
11175
11288
|
|
11176
11289
|
self._spec = spec
|
11290
|
+
self._home = home
|
11291
|
+
self._time = time
|
11177
11292
|
|
11178
|
-
self._deploys = deploys
|
11179
11293
|
self._paths = paths
|
11180
11294
|
self._apps = apps
|
11181
11295
|
|
@@ -11184,17 +11298,8 @@ class DeployDriver:
|
|
11184
11298
|
|
11185
11299
|
#
|
11186
11300
|
|
11187
|
-
hs = check.non_empty_str(self._spec.home)
|
11188
|
-
hs = os.path.expanduser(hs)
|
11189
|
-
hs = os.path.realpath(hs)
|
11190
|
-
hs = os.path.abspath(hs)
|
11191
|
-
|
11192
|
-
home = DeployHome(hs)
|
11193
|
-
|
11194
|
-
#
|
11195
|
-
|
11196
11301
|
deploy_tags = DeployTagMap(
|
11197
|
-
self.
|
11302
|
+
self._time,
|
11198
11303
|
self._spec.key(),
|
11199
11304
|
)
|
11200
11305
|
|
@@ -11209,7 +11314,7 @@ class DeployDriver:
|
|
11209
11314
|
|
11210
11315
|
await self._apps.prepare_app(
|
11211
11316
|
app,
|
11212
|
-
|
11317
|
+
self._home,
|
11213
11318
|
app_tags,
|
11214
11319
|
)
|
11215
11320
|
|
@@ -11247,10 +11352,57 @@ class DeployCommandExecutor(CommandExecutor[DeployCommand, DeployCommand.Output]
|
|
11247
11352
|
# ../deploy/inject.py
|
11248
11353
|
|
11249
11354
|
|
11355
|
+
##
|
11356
|
+
|
11357
|
+
|
11250
11358
|
class DeployInjectorScope(ContextvarInjectorScope):
|
11251
11359
|
pass
|
11252
11360
|
|
11253
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
|
+
|
11254
11406
|
def bind_deploy(
|
11255
11407
|
*,
|
11256
11408
|
deploy_config: DeployConfig,
|
@@ -11259,6 +11411,8 @@ def bind_deploy(
|
|
11259
11411
|
inj.bind(deploy_config),
|
11260
11412
|
|
11261
11413
|
bind_deploy_paths(),
|
11414
|
+
|
11415
|
+
bind_deploy_scope(),
|
11262
11416
|
]
|
11263
11417
|
|
11264
11418
|
#
|
@@ -11294,25 +11448,6 @@ def bind_deploy(
|
|
11294
11448
|
|
11295
11449
|
#
|
11296
11450
|
|
11297
|
-
def provide_deploy_driver_factory(injector: Injector, sc: DeployInjectorScope) -> DeployDriverFactory:
|
11298
|
-
@contextlib.contextmanager
|
11299
|
-
def factory(spec: DeploySpec) -> ta.Iterator[DeployDriver]:
|
11300
|
-
with sc.enter({
|
11301
|
-
inj.as_key(DeploySpec): spec,
|
11302
|
-
}):
|
11303
|
-
yield injector[DeployDriver]
|
11304
|
-
return DeployDriverFactory(factory)
|
11305
|
-
lst.append(inj.bind(provide_deploy_driver_factory, singleton=True))
|
11306
|
-
|
11307
|
-
lst.extend([
|
11308
|
-
inj.bind_scope(DeployInjectorScope),
|
11309
|
-
inj.bind_scope_seed(DeploySpec, DeployInjectorScope),
|
11310
|
-
|
11311
|
-
inj.bind(DeployDriver, in_=DeployInjectorScope),
|
11312
|
-
])
|
11313
|
-
|
11314
|
-
#
|
11315
|
-
|
11316
11451
|
lst.extend([
|
11317
11452
|
bind_command(DeployCommand, DeployCommandExecutor),
|
11318
11453
|
bind_command(InterpCommand, InterpCommandExecutor),
|
@@ -11435,7 +11570,7 @@ class MainCli(ArgparseCli):
|
|
11435
11570
|
|
11436
11571
|
#
|
11437
11572
|
|
11438
|
-
@
|
11573
|
+
@argparse_cmd(
|
11439
11574
|
argparse_arg('--_payload-file'),
|
11440
11575
|
|
11441
11576
|
argparse_arg('--pycharm-debug-port', type=int),
|