ominfra 0.0.0.dev178__py3-none-any.whl → 0.0.0.dev180__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/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),
|