omdev 0.0.0.dev180__py3-none-any.whl → 0.0.0.dev182__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- omdev/interp/inject.py +1 -1
- omdev/interp/inspect.py +9 -4
- omdev/interp/providers/system.py +5 -2
- omdev/interp/pyenv/inject.py +1 -1
- omdev/interp/pyenv/install.py +251 -0
- omdev/interp/pyenv/provider.py +144 -0
- omdev/interp/pyenv/pyenv.py +0 -380
- omdev/interp/venvs.py +114 -0
- omdev/pyproject/configs.py +3 -4
- omdev/pyproject/inject.py +12 -0
- omdev/pyproject/venvs.py +16 -52
- omdev/scripts/interp.py +121 -114
- omdev/scripts/pyproject.py +477 -343
- {omdev-0.0.0.dev180.dist-info → omdev-0.0.0.dev182.dist-info}/METADATA +2 -2
- {omdev-0.0.0.dev180.dist-info → omdev-0.0.0.dev182.dist-info}/RECORD +19 -15
- {omdev-0.0.0.dev180.dist-info → omdev-0.0.0.dev182.dist-info}/LICENSE +0 -0
- {omdev-0.0.0.dev180.dist-info → omdev-0.0.0.dev182.dist-info}/WHEEL +0 -0
- {omdev-0.0.0.dev180.dist-info → omdev-0.0.0.dev182.dist-info}/entry_points.txt +0 -0
- {omdev-0.0.0.dev180.dist-info → omdev-0.0.0.dev182.dist-info}/top_level.txt +0 -0
omdev/scripts/pyproject.py
CHANGED
@@ -66,7 +66,7 @@ import time
|
|
66
66
|
import types
|
67
67
|
import typing as ta
|
68
68
|
import uuid
|
69
|
-
import weakref
|
69
|
+
import weakref
|
70
70
|
import zipfile
|
71
71
|
|
72
72
|
|
@@ -108,6 +108,11 @@ CheckOnRaiseFn = ta.Callable[[Exception], None] # ta.TypeAlias
|
|
108
108
|
CheckExceptionFactory = ta.Callable[..., Exception] # ta.TypeAlias
|
109
109
|
CheckArgsRenderer = ta.Callable[..., ta.Optional[str]] # ta.TypeAlias
|
110
110
|
|
111
|
+
# ../../omlish/lite/typing.py
|
112
|
+
A0 = ta.TypeVar('A0')
|
113
|
+
A1 = ta.TypeVar('A1')
|
114
|
+
A2 = ta.TypeVar('A2')
|
115
|
+
|
111
116
|
# ../packaging/specifiers.py
|
112
117
|
UnparsedVersion = ta.Union['Version', str]
|
113
118
|
UnparsedVersionVar = ta.TypeVar('UnparsedVersionVar', bound=UnparsedVersion)
|
@@ -2575,6 +2580,54 @@ def format_num_bytes(num_bytes: int) -> str:
|
|
2575
2580
|
return f'{num_bytes / 1024 ** (len(FORMAT_NUM_BYTES_SUFFIXES) - 1):.2f}{FORMAT_NUM_BYTES_SUFFIXES[-1]}'
|
2576
2581
|
|
2577
2582
|
|
2583
|
+
########################################
|
2584
|
+
# ../../../omlish/lite/typing.py
|
2585
|
+
|
2586
|
+
|
2587
|
+
##
|
2588
|
+
# A workaround for typing deficiencies (like `Argument 2 to NewType(...) must be subclassable`).
|
2589
|
+
|
2590
|
+
|
2591
|
+
@dc.dataclass(frozen=True)
|
2592
|
+
class AnyFunc(ta.Generic[T]):
|
2593
|
+
fn: ta.Callable[..., T]
|
2594
|
+
|
2595
|
+
def __call__(self, *args: ta.Any, **kwargs: ta.Any) -> T:
|
2596
|
+
return self.fn(*args, **kwargs)
|
2597
|
+
|
2598
|
+
|
2599
|
+
@dc.dataclass(frozen=True)
|
2600
|
+
class Func0(ta.Generic[T]):
|
2601
|
+
fn: ta.Callable[[], T]
|
2602
|
+
|
2603
|
+
def __call__(self) -> T:
|
2604
|
+
return self.fn()
|
2605
|
+
|
2606
|
+
|
2607
|
+
@dc.dataclass(frozen=True)
|
2608
|
+
class Func1(ta.Generic[A0, T]):
|
2609
|
+
fn: ta.Callable[[A0], T]
|
2610
|
+
|
2611
|
+
def __call__(self, a0: A0) -> T:
|
2612
|
+
return self.fn(a0)
|
2613
|
+
|
2614
|
+
|
2615
|
+
@dc.dataclass(frozen=True)
|
2616
|
+
class Func2(ta.Generic[A0, A1, T]):
|
2617
|
+
fn: ta.Callable[[A0, A1], T]
|
2618
|
+
|
2619
|
+
def __call__(self, a0: A0, a1: A1) -> T:
|
2620
|
+
return self.fn(a0, a1)
|
2621
|
+
|
2622
|
+
|
2623
|
+
@dc.dataclass(frozen=True)
|
2624
|
+
class Func3(ta.Generic[A0, A1, A2, T]):
|
2625
|
+
fn: ta.Callable[[A0, A1, A2], T]
|
2626
|
+
|
2627
|
+
def __call__(self, a0: A0, a1: A1, a2: A2) -> T:
|
2628
|
+
return self.fn(a0, a1, a2)
|
2629
|
+
|
2630
|
+
|
2578
2631
|
########################################
|
2579
2632
|
# ../../../omlish/logs/filters.py
|
2580
2633
|
|
@@ -5547,104 +5600,6 @@ def bind_interp_uv() -> InjectorBindings:
|
|
5547
5600
|
return inj.as_bindings(*lst)
|
5548
5601
|
|
5549
5602
|
|
5550
|
-
########################################
|
5551
|
-
# ../configs.py
|
5552
|
-
|
5553
|
-
|
5554
|
-
@dc.dataclass(frozen=True)
|
5555
|
-
class VenvConfig:
|
5556
|
-
inherits: ta.Optional[ta.Sequence[str]] = None
|
5557
|
-
interp: ta.Optional[str] = None
|
5558
|
-
requires: ta.Optional[ta.List[str]] = None
|
5559
|
-
docker: ta.Optional[str] = None
|
5560
|
-
srcs: ta.Optional[ta.List[str]] = None
|
5561
|
-
use_uv: ta.Optional[bool] = None
|
5562
|
-
|
5563
|
-
|
5564
|
-
@dc.dataclass(frozen=True)
|
5565
|
-
class PyprojectConfig:
|
5566
|
-
pkgs: ta.Sequence[str] = dc.field(default_factory=list)
|
5567
|
-
srcs: ta.Mapping[str, ta.Sequence[str]] = dc.field(default_factory=dict)
|
5568
|
-
venvs: ta.Mapping[str, VenvConfig] = dc.field(default_factory=dict)
|
5569
|
-
|
5570
|
-
venvs_dir: str = '.venvs'
|
5571
|
-
versions_file: ta.Optional[str] = '.versions'
|
5572
|
-
|
5573
|
-
|
5574
|
-
class PyprojectConfigPreparer:
|
5575
|
-
def __init__(
|
5576
|
-
self,
|
5577
|
-
*,
|
5578
|
-
python_versions: ta.Optional[ta.Mapping[str, str]] = None,
|
5579
|
-
) -> None:
|
5580
|
-
super().__init__()
|
5581
|
-
|
5582
|
-
self._python_versions = python_versions or {}
|
5583
|
-
|
5584
|
-
def _inherit_venvs(self, m: ta.Mapping[str, VenvConfig]) -> ta.Mapping[str, VenvConfig]:
|
5585
|
-
done: ta.Dict[str, VenvConfig] = {}
|
5586
|
-
|
5587
|
-
def rec(k):
|
5588
|
-
try:
|
5589
|
-
return done[k]
|
5590
|
-
except KeyError:
|
5591
|
-
pass
|
5592
|
-
|
5593
|
-
c = m[k]
|
5594
|
-
kw = dc.asdict(c)
|
5595
|
-
for i in c.inherits or ():
|
5596
|
-
ic = rec(i)
|
5597
|
-
kw.update({k: v for k, v in dc.asdict(ic).items() if v is not None and kw.get(k) is None})
|
5598
|
-
del kw['inherits']
|
5599
|
-
|
5600
|
-
d = done[k] = VenvConfig(**kw)
|
5601
|
-
return d
|
5602
|
-
|
5603
|
-
for k in m:
|
5604
|
-
rec(k)
|
5605
|
-
return done
|
5606
|
-
|
5607
|
-
def _resolve_srcs(
|
5608
|
-
self,
|
5609
|
-
lst: ta.Sequence[str],
|
5610
|
-
aliases: ta.Mapping[str, ta.Sequence[str]],
|
5611
|
-
) -> ta.List[str]:
|
5612
|
-
todo = list(reversed(lst))
|
5613
|
-
raw: ta.List[str] = []
|
5614
|
-
seen: ta.Set[str] = set()
|
5615
|
-
|
5616
|
-
while todo:
|
5617
|
-
cur = todo.pop()
|
5618
|
-
if cur in seen:
|
5619
|
-
continue
|
5620
|
-
|
5621
|
-
seen.add(cur)
|
5622
|
-
if not cur.startswith('@'):
|
5623
|
-
raw.append(cur)
|
5624
|
-
continue
|
5625
|
-
|
5626
|
-
todo.extend(aliases[cur[1:]][::-1])
|
5627
|
-
|
5628
|
-
return raw
|
5629
|
-
|
5630
|
-
def _fixup_interp(self, s: ta.Optional[str]) -> ta.Optional[str]:
|
5631
|
-
if not s or not s.startswith('@'):
|
5632
|
-
return s
|
5633
|
-
return self._python_versions[s[1:]]
|
5634
|
-
|
5635
|
-
def prepare_config(self, dct: ta.Mapping[str, ta.Any]) -> PyprojectConfig:
|
5636
|
-
pcfg: PyprojectConfig = unmarshal_obj(dct, PyprojectConfig)
|
5637
|
-
|
5638
|
-
ivs = dict(self._inherit_venvs(pcfg.venvs or {}))
|
5639
|
-
for k, v in ivs.items():
|
5640
|
-
v = dc.replace(v, srcs=self._resolve_srcs(v.srcs or [], pcfg.srcs or {}))
|
5641
|
-
v = dc.replace(v, interp=self._fixup_interp(v.interp))
|
5642
|
-
ivs[k] = v
|
5643
|
-
|
5644
|
-
pcfg = dc.replace(pcfg, venvs=ivs)
|
5645
|
-
return pcfg
|
5646
|
-
|
5647
|
-
|
5648
5603
|
########################################
|
5649
5604
|
# ../../../omlish/logs/standard.py
|
5650
5605
|
"""
|
@@ -6443,9 +6398,15 @@ class InterpInspection:
|
|
6443
6398
|
|
6444
6399
|
|
6445
6400
|
class InterpInspector:
|
6446
|
-
def __init__(
|
6401
|
+
def __init__(
|
6402
|
+
self,
|
6403
|
+
*,
|
6404
|
+
log: ta.Optional[logging.Logger] = None,
|
6405
|
+
) -> None:
|
6447
6406
|
super().__init__()
|
6448
6407
|
|
6408
|
+
self._log = log
|
6409
|
+
|
6449
6410
|
self._cache: ta.Dict[str, ta.Optional[InterpInspection]] = {}
|
6450
6411
|
|
6451
6412
|
_RAW_INSPECTION_CODE = """
|
@@ -6494,13 +6455,95 @@ class InterpInspector:
|
|
6494
6455
|
try:
|
6495
6456
|
ret = await self._inspect(exe)
|
6496
6457
|
except Exception as e: # noqa
|
6497
|
-
if
|
6498
|
-
|
6458
|
+
if self._log is not None and self._log.isEnabledFor(logging.DEBUG):
|
6459
|
+
self._log.exception('Failed to inspect interp: %s', exe)
|
6499
6460
|
ret = None
|
6500
6461
|
self._cache[exe] = ret
|
6501
6462
|
return ret
|
6502
6463
|
|
6503
6464
|
|
6465
|
+
########################################
|
6466
|
+
# ../../interp/pyenv/pyenv.py
|
6467
|
+
"""
|
6468
|
+
TODO:
|
6469
|
+
- custom tags
|
6470
|
+
- 'aliases'
|
6471
|
+
- https://github.com/pyenv/pyenv/pull/2966
|
6472
|
+
- https://github.com/pyenv/pyenv/issues/218 (lol)
|
6473
|
+
- probably need custom (temp?) definition file
|
6474
|
+
- *or* python-build directly just into the versions dir?
|
6475
|
+
- optionally install / upgrade pyenv itself
|
6476
|
+
- new vers dont need these custom mac opts, only run on old vers
|
6477
|
+
"""
|
6478
|
+
|
6479
|
+
|
6480
|
+
class Pyenv:
|
6481
|
+
def __init__(
|
6482
|
+
self,
|
6483
|
+
*,
|
6484
|
+
root: ta.Optional[str] = None,
|
6485
|
+
) -> None:
|
6486
|
+
if root is not None and not (isinstance(root, str) and root):
|
6487
|
+
raise ValueError(f'pyenv_root: {root!r}')
|
6488
|
+
|
6489
|
+
super().__init__()
|
6490
|
+
|
6491
|
+
self._root_kw = root
|
6492
|
+
|
6493
|
+
@async_cached_nullary
|
6494
|
+
async def root(self) -> ta.Optional[str]:
|
6495
|
+
if self._root_kw is not None:
|
6496
|
+
return self._root_kw
|
6497
|
+
|
6498
|
+
if shutil.which('pyenv'):
|
6499
|
+
return await asyncio_subprocesses.check_output_str('pyenv', 'root')
|
6500
|
+
|
6501
|
+
d = os.path.expanduser('~/.pyenv')
|
6502
|
+
if os.path.isdir(d) and os.path.isfile(os.path.join(d, 'bin', 'pyenv')):
|
6503
|
+
return d
|
6504
|
+
|
6505
|
+
return None
|
6506
|
+
|
6507
|
+
@async_cached_nullary
|
6508
|
+
async def exe(self) -> str:
|
6509
|
+
return os.path.join(check.not_none(await self.root()), 'bin', 'pyenv')
|
6510
|
+
|
6511
|
+
async def version_exes(self) -> ta.List[ta.Tuple[str, str]]:
|
6512
|
+
if (root := await self.root()) is None:
|
6513
|
+
return []
|
6514
|
+
ret = []
|
6515
|
+
vp = os.path.join(root, 'versions')
|
6516
|
+
if os.path.isdir(vp):
|
6517
|
+
for dn in os.listdir(vp):
|
6518
|
+
ep = os.path.join(vp, dn, 'bin', 'python')
|
6519
|
+
if not os.path.isfile(ep):
|
6520
|
+
continue
|
6521
|
+
ret.append((dn, ep))
|
6522
|
+
return ret
|
6523
|
+
|
6524
|
+
async def installable_versions(self) -> ta.List[str]:
|
6525
|
+
if await self.root() is None:
|
6526
|
+
return []
|
6527
|
+
ret = []
|
6528
|
+
s = await asyncio_subprocesses.check_output_str(await self.exe(), 'install', '--list')
|
6529
|
+
for l in s.splitlines():
|
6530
|
+
if not l.startswith(' '):
|
6531
|
+
continue
|
6532
|
+
l = l.strip()
|
6533
|
+
if not l:
|
6534
|
+
continue
|
6535
|
+
ret.append(l)
|
6536
|
+
return ret
|
6537
|
+
|
6538
|
+
async def update(self) -> bool:
|
6539
|
+
if (root := await self.root()) is None:
|
6540
|
+
return False
|
6541
|
+
if not os.path.isdir(os.path.join(root, '.git')):
|
6542
|
+
return False
|
6543
|
+
await asyncio_subprocesses.check_call('git', 'pull', cwd=root)
|
6544
|
+
return True
|
6545
|
+
|
6546
|
+
|
6504
6547
|
########################################
|
6505
6548
|
# ../../interp/resolvers.py
|
6506
6549
|
|
@@ -6755,12 +6798,14 @@ class SystemInterpProvider(InterpProvider):
|
|
6755
6798
|
options: Options = Options(),
|
6756
6799
|
*,
|
6757
6800
|
inspector: ta.Optional[InterpInspector] = None,
|
6801
|
+
log: ta.Optional[logging.Logger] = None,
|
6758
6802
|
) -> None:
|
6759
6803
|
super().__init__()
|
6760
6804
|
|
6761
6805
|
self._options = options
|
6762
6806
|
|
6763
6807
|
self._inspector = inspector
|
6808
|
+
self._log = log
|
6764
6809
|
|
6765
6810
|
#
|
6766
6811
|
|
@@ -6834,7 +6879,8 @@ class SystemInterpProvider(InterpProvider):
|
|
6834
6879
|
lst = []
|
6835
6880
|
for e in self.exes():
|
6836
6881
|
if (ev := await self.get_exe_version(e)) is None:
|
6837
|
-
|
6882
|
+
if self._log is not None:
|
6883
|
+
self._log.debug('Invalid system version: %s', e)
|
6838
6884
|
continue
|
6839
6885
|
lst.append((e, ev))
|
6840
6886
|
return lst
|
@@ -6856,109 +6902,28 @@ class SystemInterpProvider(InterpProvider):
|
|
6856
6902
|
|
6857
6903
|
|
6858
6904
|
########################################
|
6859
|
-
# ../../interp/pyenv/
|
6860
|
-
"""
|
6861
|
-
TODO:
|
6862
|
-
- custom tags
|
6863
|
-
- 'aliases'
|
6864
|
-
- https://github.com/pyenv/pyenv/pull/2966
|
6865
|
-
- https://github.com/pyenv/pyenv/issues/218 (lol)
|
6866
|
-
- probably need custom (temp?) definition file
|
6867
|
-
- *or* python-build directly just into the versions dir?
|
6868
|
-
- optionally install / upgrade pyenv itself
|
6869
|
-
- new vers dont need these custom mac opts, only run on old vers
|
6870
|
-
"""
|
6905
|
+
# ../../interp/pyenv/install.py
|
6871
6906
|
|
6872
6907
|
|
6873
6908
|
##
|
6874
6909
|
|
6875
6910
|
|
6876
|
-
|
6877
|
-
|
6878
|
-
|
6879
|
-
|
6880
|
-
|
6881
|
-
|
6882
|
-
|
6883
|
-
raise ValueError(f'pyenv_root: {root!r}')
|
6911
|
+
@dc.dataclass(frozen=True)
|
6912
|
+
class PyenvInstallOpts:
|
6913
|
+
opts: ta.Sequence[str] = ()
|
6914
|
+
conf_opts: ta.Sequence[str] = ()
|
6915
|
+
cflags: ta.Sequence[str] = ()
|
6916
|
+
ldflags: ta.Sequence[str] = ()
|
6917
|
+
env: ta.Mapping[str, str] = dc.field(default_factory=dict)
|
6884
6918
|
|
6885
|
-
|
6886
|
-
|
6887
|
-
|
6888
|
-
|
6889
|
-
|
6890
|
-
|
6891
|
-
|
6892
|
-
|
6893
|
-
|
6894
|
-
if shutil.which('pyenv'):
|
6895
|
-
return await asyncio_subprocesses.check_output_str('pyenv', 'root')
|
6896
|
-
|
6897
|
-
d = os.path.expanduser('~/.pyenv')
|
6898
|
-
if os.path.isdir(d) and os.path.isfile(os.path.join(d, 'bin', 'pyenv')):
|
6899
|
-
return d
|
6900
|
-
|
6901
|
-
return None
|
6902
|
-
|
6903
|
-
@async_cached_nullary
|
6904
|
-
async def exe(self) -> str:
|
6905
|
-
return os.path.join(check.not_none(await self.root()), 'bin', 'pyenv')
|
6906
|
-
|
6907
|
-
async def version_exes(self) -> ta.List[ta.Tuple[str, str]]:
|
6908
|
-
if (root := await self.root()) is None:
|
6909
|
-
return []
|
6910
|
-
ret = []
|
6911
|
-
vp = os.path.join(root, 'versions')
|
6912
|
-
if os.path.isdir(vp):
|
6913
|
-
for dn in os.listdir(vp):
|
6914
|
-
ep = os.path.join(vp, dn, 'bin', 'python')
|
6915
|
-
if not os.path.isfile(ep):
|
6916
|
-
continue
|
6917
|
-
ret.append((dn, ep))
|
6918
|
-
return ret
|
6919
|
-
|
6920
|
-
async def installable_versions(self) -> ta.List[str]:
|
6921
|
-
if await self.root() is None:
|
6922
|
-
return []
|
6923
|
-
ret = []
|
6924
|
-
s = await asyncio_subprocesses.check_output_str(await self.exe(), 'install', '--list')
|
6925
|
-
for l in s.splitlines():
|
6926
|
-
if not l.startswith(' '):
|
6927
|
-
continue
|
6928
|
-
l = l.strip()
|
6929
|
-
if not l:
|
6930
|
-
continue
|
6931
|
-
ret.append(l)
|
6932
|
-
return ret
|
6933
|
-
|
6934
|
-
async def update(self) -> bool:
|
6935
|
-
if (root := await self.root()) is None:
|
6936
|
-
return False
|
6937
|
-
if not os.path.isdir(os.path.join(root, '.git')):
|
6938
|
-
return False
|
6939
|
-
await asyncio_subprocesses.check_call('git', 'pull', cwd=root)
|
6940
|
-
return True
|
6941
|
-
|
6942
|
-
|
6943
|
-
##
|
6944
|
-
|
6945
|
-
|
6946
|
-
@dc.dataclass(frozen=True)
|
6947
|
-
class PyenvInstallOpts:
|
6948
|
-
opts: ta.Sequence[str] = ()
|
6949
|
-
conf_opts: ta.Sequence[str] = ()
|
6950
|
-
cflags: ta.Sequence[str] = ()
|
6951
|
-
ldflags: ta.Sequence[str] = ()
|
6952
|
-
env: ta.Mapping[str, str] = dc.field(default_factory=dict)
|
6953
|
-
|
6954
|
-
def merge(self, *others: 'PyenvInstallOpts') -> 'PyenvInstallOpts':
|
6955
|
-
return PyenvInstallOpts(
|
6956
|
-
opts=list(itertools.chain.from_iterable(o.opts for o in [self, *others])),
|
6957
|
-
conf_opts=list(itertools.chain.from_iterable(o.conf_opts for o in [self, *others])),
|
6958
|
-
cflags=list(itertools.chain.from_iterable(o.cflags for o in [self, *others])),
|
6959
|
-
ldflags=list(itertools.chain.from_iterable(o.ldflags for o in [self, *others])),
|
6960
|
-
env=dict(itertools.chain.from_iterable(o.env.items() for o in [self, *others])),
|
6961
|
-
)
|
6919
|
+
def merge(self, *others: 'PyenvInstallOpts') -> 'PyenvInstallOpts':
|
6920
|
+
return PyenvInstallOpts(
|
6921
|
+
opts=list(itertools.chain.from_iterable(o.opts for o in [self, *others])),
|
6922
|
+
conf_opts=list(itertools.chain.from_iterable(o.conf_opts for o in [self, *others])),
|
6923
|
+
cflags=list(itertools.chain.from_iterable(o.cflags for o in [self, *others])),
|
6924
|
+
ldflags=list(itertools.chain.from_iterable(o.ldflags for o in [self, *others])),
|
6925
|
+
env=dict(itertools.chain.from_iterable(o.env.items() for o in [self, *others])),
|
6926
|
+
)
|
6962
6927
|
|
6963
6928
|
|
6964
6929
|
# TODO: https://github.com/pyenv/pyenv/blob/master/plugins/python-build/README.md#building-for-maximum-performance
|
@@ -7175,133 +7140,6 @@ class PyenvVersionInstaller:
|
|
7175
7140
|
return exe
|
7176
7141
|
|
7177
7142
|
|
7178
|
-
##
|
7179
|
-
|
7180
|
-
|
7181
|
-
class PyenvInterpProvider(InterpProvider):
|
7182
|
-
@dc.dataclass(frozen=True)
|
7183
|
-
class Options:
|
7184
|
-
inspect: bool = False
|
7185
|
-
|
7186
|
-
try_update: bool = False
|
7187
|
-
|
7188
|
-
def __init__(
|
7189
|
-
self,
|
7190
|
-
options: Options = Options(),
|
7191
|
-
*,
|
7192
|
-
pyenv: Pyenv,
|
7193
|
-
inspector: InterpInspector,
|
7194
|
-
) -> None:
|
7195
|
-
super().__init__()
|
7196
|
-
|
7197
|
-
self._options = options
|
7198
|
-
|
7199
|
-
self._pyenv = pyenv
|
7200
|
-
self._inspector = inspector
|
7201
|
-
|
7202
|
-
#
|
7203
|
-
|
7204
|
-
@staticmethod
|
7205
|
-
def guess_version(s: str) -> ta.Optional[InterpVersion]:
|
7206
|
-
def strip_sfx(s: str, sfx: str) -> ta.Tuple[str, bool]:
|
7207
|
-
if s.endswith(sfx):
|
7208
|
-
return s[:-len(sfx)], True
|
7209
|
-
return s, False
|
7210
|
-
ok = {}
|
7211
|
-
s, ok['debug'] = strip_sfx(s, '-debug')
|
7212
|
-
s, ok['threaded'] = strip_sfx(s, 't')
|
7213
|
-
try:
|
7214
|
-
v = Version(s)
|
7215
|
-
except InvalidVersion:
|
7216
|
-
return None
|
7217
|
-
return InterpVersion(v, InterpOpts(**ok))
|
7218
|
-
|
7219
|
-
class Installed(ta.NamedTuple):
|
7220
|
-
name: str
|
7221
|
-
exe: str
|
7222
|
-
version: InterpVersion
|
7223
|
-
|
7224
|
-
async def _make_installed(self, vn: str, ep: str) -> ta.Optional[Installed]:
|
7225
|
-
iv: ta.Optional[InterpVersion]
|
7226
|
-
if self._options.inspect:
|
7227
|
-
try:
|
7228
|
-
iv = check.not_none(await self._inspector.inspect(ep)).iv
|
7229
|
-
except Exception as e: # noqa
|
7230
|
-
return None
|
7231
|
-
else:
|
7232
|
-
iv = self.guess_version(vn)
|
7233
|
-
if iv is None:
|
7234
|
-
return None
|
7235
|
-
return PyenvInterpProvider.Installed(
|
7236
|
-
name=vn,
|
7237
|
-
exe=ep,
|
7238
|
-
version=iv,
|
7239
|
-
)
|
7240
|
-
|
7241
|
-
async def installed(self) -> ta.Sequence[Installed]:
|
7242
|
-
ret: ta.List[PyenvInterpProvider.Installed] = []
|
7243
|
-
for vn, ep in await self._pyenv.version_exes():
|
7244
|
-
if (i := await self._make_installed(vn, ep)) is None:
|
7245
|
-
log.debug('Invalid pyenv version: %s', vn)
|
7246
|
-
continue
|
7247
|
-
ret.append(i)
|
7248
|
-
return ret
|
7249
|
-
|
7250
|
-
#
|
7251
|
-
|
7252
|
-
async def get_installed_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
|
7253
|
-
return [i.version for i in await self.installed()]
|
7254
|
-
|
7255
|
-
async def get_installed_version(self, version: InterpVersion) -> Interp:
|
7256
|
-
for i in await self.installed():
|
7257
|
-
if i.version == version:
|
7258
|
-
return Interp(
|
7259
|
-
exe=i.exe,
|
7260
|
-
version=i.version,
|
7261
|
-
)
|
7262
|
-
raise KeyError(version)
|
7263
|
-
|
7264
|
-
#
|
7265
|
-
|
7266
|
-
async def _get_installable_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
|
7267
|
-
lst = []
|
7268
|
-
|
7269
|
-
for vs in await self._pyenv.installable_versions():
|
7270
|
-
if (iv := self.guess_version(vs)) is None:
|
7271
|
-
continue
|
7272
|
-
if iv.opts.debug:
|
7273
|
-
raise Exception('Pyenv installable versions not expected to have debug suffix')
|
7274
|
-
for d in [False, True]:
|
7275
|
-
lst.append(dc.replace(iv, opts=dc.replace(iv.opts, debug=d)))
|
7276
|
-
|
7277
|
-
return lst
|
7278
|
-
|
7279
|
-
async def get_installable_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
|
7280
|
-
lst = await self._get_installable_versions(spec)
|
7281
|
-
|
7282
|
-
if self._options.try_update and not any(v in spec for v in lst):
|
7283
|
-
if self._pyenv.update():
|
7284
|
-
lst = await self._get_installable_versions(spec)
|
7285
|
-
|
7286
|
-
return lst
|
7287
|
-
|
7288
|
-
async def install_version(self, version: InterpVersion) -> Interp:
|
7289
|
-
inst_version = str(version.version)
|
7290
|
-
inst_opts = version.opts
|
7291
|
-
if inst_opts.threaded:
|
7292
|
-
inst_version += 't'
|
7293
|
-
inst_opts = dc.replace(inst_opts, threaded=False)
|
7294
|
-
|
7295
|
-
installer = PyenvVersionInstaller(
|
7296
|
-
inst_version,
|
7297
|
-
interp_opts=inst_opts,
|
7298
|
-
pyenv=self._pyenv,
|
7299
|
-
)
|
7300
|
-
|
7301
|
-
exe = await installer.install()
|
7302
|
-
return Interp(exe, version)
|
7303
|
-
|
7304
|
-
|
7305
7143
|
########################################
|
7306
7144
|
# ../pkg.py
|
7307
7145
|
"""
|
@@ -7879,6 +7717,137 @@ def bind_interp_providers() -> InjectorBindings:
|
|
7879
7717
|
return inj.as_bindings(*lst)
|
7880
7718
|
|
7881
7719
|
|
7720
|
+
########################################
|
7721
|
+
# ../../interp/pyenv/provider.py
|
7722
|
+
|
7723
|
+
|
7724
|
+
class PyenvInterpProvider(InterpProvider):
|
7725
|
+
@dc.dataclass(frozen=True)
|
7726
|
+
class Options:
|
7727
|
+
inspect: bool = False
|
7728
|
+
|
7729
|
+
try_update: bool = False
|
7730
|
+
|
7731
|
+
def __init__(
|
7732
|
+
self,
|
7733
|
+
options: Options = Options(),
|
7734
|
+
*,
|
7735
|
+
pyenv: Pyenv,
|
7736
|
+
inspector: InterpInspector,
|
7737
|
+
log: ta.Optional[logging.Logger] = None,
|
7738
|
+
) -> None:
|
7739
|
+
super().__init__()
|
7740
|
+
|
7741
|
+
self._options = options
|
7742
|
+
|
7743
|
+
self._pyenv = pyenv
|
7744
|
+
self._inspector = inspector
|
7745
|
+
self._log = log
|
7746
|
+
|
7747
|
+
#
|
7748
|
+
|
7749
|
+
@staticmethod
|
7750
|
+
def guess_version(s: str) -> ta.Optional[InterpVersion]:
|
7751
|
+
def strip_sfx(s: str, sfx: str) -> ta.Tuple[str, bool]:
|
7752
|
+
if s.endswith(sfx):
|
7753
|
+
return s[:-len(sfx)], True
|
7754
|
+
return s, False
|
7755
|
+
ok = {}
|
7756
|
+
s, ok['debug'] = strip_sfx(s, '-debug')
|
7757
|
+
s, ok['threaded'] = strip_sfx(s, 't')
|
7758
|
+
try:
|
7759
|
+
v = Version(s)
|
7760
|
+
except InvalidVersion:
|
7761
|
+
return None
|
7762
|
+
return InterpVersion(v, InterpOpts(**ok))
|
7763
|
+
|
7764
|
+
class Installed(ta.NamedTuple):
|
7765
|
+
name: str
|
7766
|
+
exe: str
|
7767
|
+
version: InterpVersion
|
7768
|
+
|
7769
|
+
async def _make_installed(self, vn: str, ep: str) -> ta.Optional[Installed]:
|
7770
|
+
iv: ta.Optional[InterpVersion]
|
7771
|
+
if self._options.inspect:
|
7772
|
+
try:
|
7773
|
+
iv = check.not_none(await self._inspector.inspect(ep)).iv
|
7774
|
+
except Exception as e: # noqa
|
7775
|
+
return None
|
7776
|
+
else:
|
7777
|
+
iv = self.guess_version(vn)
|
7778
|
+
if iv is None:
|
7779
|
+
return None
|
7780
|
+
return PyenvInterpProvider.Installed(
|
7781
|
+
name=vn,
|
7782
|
+
exe=ep,
|
7783
|
+
version=iv,
|
7784
|
+
)
|
7785
|
+
|
7786
|
+
async def installed(self) -> ta.Sequence[Installed]:
|
7787
|
+
ret: ta.List[PyenvInterpProvider.Installed] = []
|
7788
|
+
for vn, ep in await self._pyenv.version_exes():
|
7789
|
+
if (i := await self._make_installed(vn, ep)) is None:
|
7790
|
+
if self._log is not None:
|
7791
|
+
self._log.debug('Invalid pyenv version: %s', vn)
|
7792
|
+
continue
|
7793
|
+
ret.append(i)
|
7794
|
+
return ret
|
7795
|
+
|
7796
|
+
#
|
7797
|
+
|
7798
|
+
async def get_installed_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
|
7799
|
+
return [i.version for i in await self.installed()]
|
7800
|
+
|
7801
|
+
async def get_installed_version(self, version: InterpVersion) -> Interp:
|
7802
|
+
for i in await self.installed():
|
7803
|
+
if i.version == version:
|
7804
|
+
return Interp(
|
7805
|
+
exe=i.exe,
|
7806
|
+
version=i.version,
|
7807
|
+
)
|
7808
|
+
raise KeyError(version)
|
7809
|
+
|
7810
|
+
#
|
7811
|
+
|
7812
|
+
async def _get_installable_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
|
7813
|
+
lst = []
|
7814
|
+
|
7815
|
+
for vs in await self._pyenv.installable_versions():
|
7816
|
+
if (iv := self.guess_version(vs)) is None:
|
7817
|
+
continue
|
7818
|
+
if iv.opts.debug:
|
7819
|
+
raise Exception('Pyenv installable versions not expected to have debug suffix')
|
7820
|
+
for d in [False, True]:
|
7821
|
+
lst.append(dc.replace(iv, opts=dc.replace(iv.opts, debug=d)))
|
7822
|
+
|
7823
|
+
return lst
|
7824
|
+
|
7825
|
+
async def get_installable_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
|
7826
|
+
lst = await self._get_installable_versions(spec)
|
7827
|
+
|
7828
|
+
if self._options.try_update and not any(v in spec for v in lst):
|
7829
|
+
if self._pyenv.update():
|
7830
|
+
lst = await self._get_installable_versions(spec)
|
7831
|
+
|
7832
|
+
return lst
|
7833
|
+
|
7834
|
+
async def install_version(self, version: InterpVersion) -> Interp:
|
7835
|
+
inst_version = str(version.version)
|
7836
|
+
inst_opts = version.opts
|
7837
|
+
if inst_opts.threaded:
|
7838
|
+
inst_version += 't'
|
7839
|
+
inst_opts = dc.replace(inst_opts, threaded=False)
|
7840
|
+
|
7841
|
+
installer = PyenvVersionInstaller(
|
7842
|
+
inst_version,
|
7843
|
+
interp_opts=inst_opts,
|
7844
|
+
pyenv=self._pyenv,
|
7845
|
+
)
|
7846
|
+
|
7847
|
+
exe = await installer.install()
|
7848
|
+
return Interp(exe, version)
|
7849
|
+
|
7850
|
+
|
7882
7851
|
########################################
|
7883
7852
|
# ../../interp/pyenv/inject.py
|
7884
7853
|
|
@@ -7945,31 +7914,47 @@ def get_default_interp_resolver() -> InterpResolver:
|
|
7945
7914
|
|
7946
7915
|
|
7947
7916
|
########################################
|
7948
|
-
#
|
7917
|
+
# ../../interp/venvs.py
|
7949
7918
|
|
7950
7919
|
|
7951
7920
|
##
|
7952
7921
|
|
7953
7922
|
|
7954
|
-
|
7923
|
+
@dc.dataclass(frozen=True)
|
7924
|
+
class InterpVenvConfig:
|
7925
|
+
interp: ta.Optional[str] = None
|
7926
|
+
requires: ta.Optional[ta.Sequence[str]] = None
|
7927
|
+
use_uv: ta.Optional[bool] = None
|
7928
|
+
|
7929
|
+
|
7930
|
+
class InterpVenvRequirementsProcessor(Func2['InterpVenv', ta.Sequence[str], ta.Sequence[str]]):
|
7931
|
+
pass
|
7932
|
+
|
7933
|
+
|
7934
|
+
class InterpVenv:
|
7955
7935
|
def __init__(
|
7956
7936
|
self,
|
7957
|
-
|
7958
|
-
cfg:
|
7937
|
+
path: str,
|
7938
|
+
cfg: InterpVenvConfig,
|
7939
|
+
*,
|
7940
|
+
requirements_processor: ta.Optional[InterpVenvRequirementsProcessor] = None,
|
7941
|
+
log: ta.Optional[logging.Logger] = None,
|
7959
7942
|
) -> None:
|
7960
7943
|
super().__init__()
|
7961
|
-
|
7944
|
+
|
7945
|
+
self._path = path
|
7962
7946
|
self._cfg = cfg
|
7963
7947
|
|
7964
|
-
|
7965
|
-
|
7966
|
-
return self._cfg
|
7948
|
+
self._requirements_processor = requirements_processor
|
7949
|
+
self._log = log
|
7967
7950
|
|
7968
|
-
|
7951
|
+
@property
|
7952
|
+
def path(self) -> str:
|
7953
|
+
return self._path
|
7969
7954
|
|
7970
7955
|
@property
|
7971
|
-
def
|
7972
|
-
return
|
7956
|
+
def cfg(self) -> InterpVenvConfig:
|
7957
|
+
return self._cfg
|
7973
7958
|
|
7974
7959
|
@async_cached_nullary
|
7975
7960
|
async def interp_exe(self) -> str:
|
@@ -7978,19 +7963,23 @@ class Venv:
|
|
7978
7963
|
|
7979
7964
|
@cached_nullary
|
7980
7965
|
def exe(self) -> str:
|
7981
|
-
ve = os.path.join(self.
|
7966
|
+
ve = os.path.join(self._path, 'bin/python')
|
7982
7967
|
if not os.path.isfile(ve):
|
7983
7968
|
raise Exception(f'venv exe {ve} does not exist or is not a file!')
|
7984
7969
|
return ve
|
7985
7970
|
|
7986
7971
|
@async_cached_nullary
|
7987
7972
|
async def create(self) -> bool:
|
7988
|
-
if os.path.exists(dn := self.
|
7973
|
+
if os.path.exists(dn := self._path):
|
7989
7974
|
if not os.path.isdir(dn):
|
7990
7975
|
raise Exception(f'{dn} exists but is not a directory!')
|
7991
7976
|
return False
|
7992
7977
|
|
7993
|
-
|
7978
|
+
ie = await self.interp_exe()
|
7979
|
+
|
7980
|
+
if self._log is not None:
|
7981
|
+
self._log.info('Using interpreter %s', ie)
|
7982
|
+
|
7994
7983
|
await asyncio_subprocesses.check_call(ie, '-m', 'venv', dn)
|
7995
7984
|
|
7996
7985
|
ve = self.exe()
|
@@ -8007,8 +7996,9 @@ class Venv:
|
|
8007
7996
|
)
|
8008
7997
|
|
8009
7998
|
if sr := self._cfg.requires:
|
8010
|
-
|
8011
|
-
|
7999
|
+
reqs = list(sr)
|
8000
|
+
if self._requirements_processor is not None:
|
8001
|
+
reqs = list(self._requirements_processor(self, reqs))
|
8012
8002
|
|
8013
8003
|
# TODO: automatically try slower uv download when it fails? lol
|
8014
8004
|
# Caused by: Failed to download distribution due to network timeout. Try increasing UV_HTTP_TIMEOUT (current value: 30s). # noqa
|
@@ -8026,6 +8016,150 @@ class Venv:
|
|
8026
8016
|
|
8027
8017
|
return True
|
8028
8018
|
|
8019
|
+
|
8020
|
+
########################################
|
8021
|
+
# ../configs.py
|
8022
|
+
|
8023
|
+
|
8024
|
+
@dc.dataclass(frozen=True)
|
8025
|
+
class VenvConfig(InterpVenvConfig):
|
8026
|
+
inherits: ta.Optional[ta.Sequence[str]] = None
|
8027
|
+
docker: ta.Optional[str] = None
|
8028
|
+
srcs: ta.Optional[ta.List[str]] = None
|
8029
|
+
|
8030
|
+
|
8031
|
+
@dc.dataclass(frozen=True)
|
8032
|
+
class PyprojectConfig:
|
8033
|
+
pkgs: ta.Sequence[str] = dc.field(default_factory=list)
|
8034
|
+
srcs: ta.Mapping[str, ta.Sequence[str]] = dc.field(default_factory=dict)
|
8035
|
+
venvs: ta.Mapping[str, VenvConfig] = dc.field(default_factory=dict)
|
8036
|
+
|
8037
|
+
venvs_dir: str = '.venvs'
|
8038
|
+
versions_file: ta.Optional[str] = '.versions'
|
8039
|
+
|
8040
|
+
|
8041
|
+
class PyprojectConfigPreparer:
|
8042
|
+
def __init__(
|
8043
|
+
self,
|
8044
|
+
*,
|
8045
|
+
python_versions: ta.Optional[ta.Mapping[str, str]] = None,
|
8046
|
+
) -> None:
|
8047
|
+
super().__init__()
|
8048
|
+
|
8049
|
+
self._python_versions = python_versions or {}
|
8050
|
+
|
8051
|
+
def _inherit_venvs(self, m: ta.Mapping[str, VenvConfig]) -> ta.Mapping[str, VenvConfig]:
|
8052
|
+
done: ta.Dict[str, VenvConfig] = {}
|
8053
|
+
|
8054
|
+
def rec(k):
|
8055
|
+
try:
|
8056
|
+
return done[k]
|
8057
|
+
except KeyError:
|
8058
|
+
pass
|
8059
|
+
|
8060
|
+
c = m[k]
|
8061
|
+
kw = dc.asdict(c)
|
8062
|
+
for i in c.inherits or ():
|
8063
|
+
ic = rec(i)
|
8064
|
+
kw.update({k: v for k, v in dc.asdict(ic).items() if v is not None and kw.get(k) is None})
|
8065
|
+
del kw['inherits']
|
8066
|
+
|
8067
|
+
d = done[k] = VenvConfig(**kw)
|
8068
|
+
return d
|
8069
|
+
|
8070
|
+
for k in m:
|
8071
|
+
rec(k)
|
8072
|
+
return done
|
8073
|
+
|
8074
|
+
def _resolve_srcs(
|
8075
|
+
self,
|
8076
|
+
lst: ta.Sequence[str],
|
8077
|
+
aliases: ta.Mapping[str, ta.Sequence[str]],
|
8078
|
+
) -> ta.List[str]:
|
8079
|
+
todo = list(reversed(lst))
|
8080
|
+
raw: ta.List[str] = []
|
8081
|
+
seen: ta.Set[str] = set()
|
8082
|
+
|
8083
|
+
while todo:
|
8084
|
+
cur = todo.pop()
|
8085
|
+
if cur in seen:
|
8086
|
+
continue
|
8087
|
+
|
8088
|
+
seen.add(cur)
|
8089
|
+
if not cur.startswith('@'):
|
8090
|
+
raw.append(cur)
|
8091
|
+
continue
|
8092
|
+
|
8093
|
+
todo.extend(aliases[cur[1:]][::-1])
|
8094
|
+
|
8095
|
+
return raw
|
8096
|
+
|
8097
|
+
def _fixup_interp(self, s: ta.Optional[str]) -> ta.Optional[str]:
|
8098
|
+
if not s or not s.startswith('@'):
|
8099
|
+
return s
|
8100
|
+
return self._python_versions[s[1:]]
|
8101
|
+
|
8102
|
+
def prepare_config(self, dct: ta.Mapping[str, ta.Any]) -> PyprojectConfig:
|
8103
|
+
pcfg: PyprojectConfig = unmarshal_obj(dct, PyprojectConfig)
|
8104
|
+
|
8105
|
+
ivs = dict(self._inherit_venvs(pcfg.venvs or {}))
|
8106
|
+
for k, v in ivs.items():
|
8107
|
+
v = dc.replace(v, srcs=self._resolve_srcs(v.srcs or [], pcfg.srcs or {}))
|
8108
|
+
v = dc.replace(v, interp=self._fixup_interp(v.interp))
|
8109
|
+
ivs[k] = v
|
8110
|
+
|
8111
|
+
pcfg = dc.replace(pcfg, venvs=ivs)
|
8112
|
+
return pcfg
|
8113
|
+
|
8114
|
+
|
8115
|
+
########################################
|
8116
|
+
# ../venvs.py
|
8117
|
+
|
8118
|
+
|
8119
|
+
##
|
8120
|
+
|
8121
|
+
|
8122
|
+
class Venv:
|
8123
|
+
def __init__(
|
8124
|
+
self,
|
8125
|
+
name: str,
|
8126
|
+
cfg: VenvConfig,
|
8127
|
+
) -> None:
|
8128
|
+
super().__init__()
|
8129
|
+
self._name = name
|
8130
|
+
self._cfg = cfg
|
8131
|
+
|
8132
|
+
@property
|
8133
|
+
def cfg(self) -> VenvConfig:
|
8134
|
+
return self._cfg
|
8135
|
+
|
8136
|
+
DIR_NAME = '.venvs'
|
8137
|
+
|
8138
|
+
@property
|
8139
|
+
def dir_name(self) -> str:
|
8140
|
+
return os.path.join(self.DIR_NAME, self._name)
|
8141
|
+
|
8142
|
+
@cached_nullary
|
8143
|
+
def _iv(self) -> InterpVenv:
|
8144
|
+
rr = RequirementsRewriter(self._name)
|
8145
|
+
|
8146
|
+
return InterpVenv(
|
8147
|
+
self.dir_name,
|
8148
|
+
self._cfg,
|
8149
|
+
requirements_processor=InterpVenvRequirementsProcessor(
|
8150
|
+
lambda iv, reqs: [rr.rewrite(req) for req in reqs] # noqa
|
8151
|
+
),
|
8152
|
+
log=log,
|
8153
|
+
)
|
8154
|
+
|
8155
|
+
@cached_nullary
|
8156
|
+
def exe(self) -> str:
|
8157
|
+
return self._iv().exe()
|
8158
|
+
|
8159
|
+
@async_cached_nullary
|
8160
|
+
async def create(self) -> bool:
|
8161
|
+
return await self._iv().create()
|
8162
|
+
|
8029
8163
|
@staticmethod
|
8030
8164
|
def _resolve_srcs(raw: ta.List[str]) -> ta.List[str]:
|
8031
8165
|
out: list[str] = []
|