ygg 0.1.28__py3-none-any.whl → 0.1.30__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.
- {ygg-0.1.28.dist-info → ygg-0.1.30.dist-info}/METADATA +1 -1
- {ygg-0.1.28.dist-info → ygg-0.1.30.dist-info}/RECORD +16 -16
- yggdrasil/databricks/compute/cluster.py +41 -25
- yggdrasil/databricks/compute/execution_context.py +9 -10
- yggdrasil/databricks/compute/remote.py +10 -6
- yggdrasil/databricks/sql/engine.py +4 -2
- yggdrasil/databricks/sql/statement_result.py +17 -2
- yggdrasil/databricks/workspaces/databricks_path.py +192 -283
- yggdrasil/databricks/workspaces/workspace.py +53 -416
- yggdrasil/pyutils/callable_serde.py +2 -28
- yggdrasil/pyutils/modules.py +1 -1
- yggdrasil/pyutils/python_env.py +81 -264
- {ygg-0.1.28.dist-info → ygg-0.1.30.dist-info}/WHEEL +0 -0
- {ygg-0.1.28.dist-info → ygg-0.1.30.dist-info}/entry_points.txt +0 -0
- {ygg-0.1.28.dist-info → ygg-0.1.30.dist-info}/licenses/LICENSE +0 -0
- {ygg-0.1.28.dist-info → ygg-0.1.30.dist-info}/top_level.txt +0 -0
yggdrasil/pyutils/python_env.py
CHANGED
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
|
|
4
4
|
import ast
|
|
5
5
|
import base64
|
|
6
|
-
import
|
|
6
|
+
import datetime as _dt
|
|
7
7
|
import json
|
|
8
8
|
import logging
|
|
9
9
|
import os
|
|
@@ -13,11 +13,12 @@ import subprocess
|
|
|
13
13
|
import sys
|
|
14
14
|
import tempfile
|
|
15
15
|
import threading
|
|
16
|
-
import zipfile
|
|
17
16
|
from contextlib import contextmanager
|
|
18
17
|
from dataclasses import dataclass
|
|
19
|
-
from pathlib import Path
|
|
20
|
-
from typing import Any, Iterable, Iterator, Mapping, MutableMapping, Optional, Union, List
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Any, Iterable, Iterator, Mapping, MutableMapping, Optional, Union, List, Tuple
|
|
20
|
+
|
|
21
|
+
from yggdrasil.pyutils.modules import PipIndexSettings
|
|
21
22
|
|
|
22
23
|
log = logging.getLogger(__name__)
|
|
23
24
|
|
|
@@ -131,9 +132,20 @@ def _run_cmd(
|
|
|
131
132
|
*,
|
|
132
133
|
cwd: Optional[Path] = None,
|
|
133
134
|
env: Optional[Mapping[str, str]] = None,
|
|
134
|
-
|
|
135
|
+
native_tls: bool = False,
|
|
136
|
+
extra_index_url: Optional[Union[str, List[str]]] = None,
|
|
135
137
|
) -> subprocess.CompletedProcess[str]:
|
|
136
138
|
cmd_s = [str(x) for x in cmd]
|
|
139
|
+
|
|
140
|
+
if native_tls and "--native-tls" not in cmd_s:
|
|
141
|
+
cmd_s = cmd_s + ["--native-tls"]
|
|
142
|
+
|
|
143
|
+
if extra_index_url and "--extra-index-url" not in cmd_s:
|
|
144
|
+
if isinstance(extra_index_url, list):
|
|
145
|
+
extra_index_url = " ".join(extra_index_url)
|
|
146
|
+
|
|
147
|
+
cmd_s = cmd_s + ["--extra-index-url", extra_index_url]
|
|
148
|
+
|
|
137
149
|
log.debug("exec: %s", " ".join(cmd_s))
|
|
138
150
|
if cwd:
|
|
139
151
|
log.debug("cwd: %s", str(cwd))
|
|
@@ -146,9 +158,6 @@ def _run_cmd(
|
|
|
146
158
|
capture_output=True,
|
|
147
159
|
)
|
|
148
160
|
if p.returncode != 0:
|
|
149
|
-
log.warning("fail: rc=%s cmd=%s", p.returncode, " ".join(cmd_s))
|
|
150
|
-
|
|
151
|
-
if check and p.returncode != 0:
|
|
152
161
|
raise PythonEnvError(
|
|
153
162
|
f"Command failed ({p.returncode})\n"
|
|
154
163
|
f"--- cmd ---\n{cmd_s}\n"
|
|
@@ -250,7 +259,7 @@ def _pip_install_uv_in_current(
|
|
|
250
259
|
cmd.append("uv")
|
|
251
260
|
|
|
252
261
|
log.info("installing uv via pip (upgrade=%s user=%s)", upgrade, user)
|
|
253
|
-
_run_cmd(cmd, env=None
|
|
262
|
+
_run_cmd(cmd, env=None)
|
|
254
263
|
|
|
255
264
|
|
|
256
265
|
# -----------------------
|
|
@@ -315,12 +324,6 @@ class PythonEnv:
|
|
|
315
324
|
@classmethod
|
|
316
325
|
def ensure_uv(
|
|
317
326
|
cls,
|
|
318
|
-
*,
|
|
319
|
-
check: bool = True,
|
|
320
|
-
upgrade: bool = True,
|
|
321
|
-
user: bool = True,
|
|
322
|
-
index_url: Optional[str] = None,
|
|
323
|
-
extra_pip_args: Optional[Iterable[str]] = None,
|
|
324
327
|
) -> str:
|
|
325
328
|
uv = _uv_exe_on_path()
|
|
326
329
|
if uv:
|
|
@@ -332,13 +335,7 @@ class PythonEnv:
|
|
|
332
335
|
return uv
|
|
333
336
|
|
|
334
337
|
log.info("uv not found; attempting pip install (thread-safe)")
|
|
335
|
-
_pip_install_uv_in_current(
|
|
336
|
-
upgrade=upgrade,
|
|
337
|
-
user=user,
|
|
338
|
-
index_url=index_url,
|
|
339
|
-
extra_pip_args=extra_pip_args,
|
|
340
|
-
check=check,
|
|
341
|
-
)
|
|
338
|
+
_pip_install_uv_in_current()
|
|
342
339
|
|
|
343
340
|
uv = _uv_exe_on_path()
|
|
344
341
|
if uv:
|
|
@@ -445,11 +442,8 @@ class PythonEnv:
|
|
|
445
442
|
pip_args: Optional[Iterable[str]] = None,
|
|
446
443
|
cwd: Optional[Path] = None,
|
|
447
444
|
env: Optional[Mapping[str, str]] = None,
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
uv_user: bool = True,
|
|
451
|
-
uv_index_url: Optional[str] = None,
|
|
452
|
-
uv_extra_pip_args: Optional[Iterable[str]] = None,
|
|
445
|
+
index_url: Optional[str] = None,
|
|
446
|
+
extra_index_url: str | list[str] | None = None,
|
|
453
447
|
) -> "PythonEnv":
|
|
454
448
|
"""
|
|
455
449
|
Create env under ~/.python/envs/<name>.
|
|
@@ -475,8 +469,6 @@ class PythonEnv:
|
|
|
475
469
|
else:
|
|
476
470
|
env_obj = cls(root)
|
|
477
471
|
if not env_obj.exists():
|
|
478
|
-
import datetime as _dt
|
|
479
|
-
|
|
480
472
|
ts = _dt.datetime.now().strftime("%Y%m%d-%H%M%S")
|
|
481
473
|
moved = root.with_name(root.name + f".broken-{ts}")
|
|
482
474
|
log.warning("env exists but python missing; moving aside: %s -> %s", str(root), str(moved))
|
|
@@ -491,27 +483,16 @@ class PythonEnv:
|
|
|
491
483
|
recreate_if_broken=True,
|
|
492
484
|
cwd=cwd,
|
|
493
485
|
env=env,
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
uv_user=uv_user,
|
|
497
|
-
uv_index_url=uv_index_url,
|
|
498
|
-
uv_extra_pip_args=uv_extra_pip_args,
|
|
486
|
+
index_url=index_url,
|
|
487
|
+
extra_index_url=extra_index_url,
|
|
499
488
|
)
|
|
500
489
|
return env_obj
|
|
501
490
|
|
|
502
|
-
uv = cls.ensure_uv(
|
|
503
|
-
check=check,
|
|
504
|
-
upgrade=uv_upgrade,
|
|
505
|
-
user=uv_user,
|
|
506
|
-
index_url=uv_index_url,
|
|
507
|
-
extra_pip_args=uv_extra_pip_args,
|
|
508
|
-
)
|
|
491
|
+
uv = cls.ensure_uv()
|
|
509
492
|
|
|
510
493
|
py = str(Path(python).expanduser()) if isinstance(python, Path) else str(python)
|
|
511
494
|
log.info("creating env: name=%s root=%s python=%s", name, str(root), py)
|
|
512
|
-
_run_cmd([
|
|
513
|
-
uv, "venv", str(root), "--python", py, "--native-tls"
|
|
514
|
-
], cwd=cwd, env=env, check=check)
|
|
495
|
+
_run_cmd([uv, "venv", str(root), "--python", py], cwd=cwd, env=env, native_tls=True)
|
|
515
496
|
|
|
516
497
|
env_obj = cls(root)
|
|
517
498
|
if not env_obj.exists():
|
|
@@ -526,12 +507,10 @@ class PythonEnv:
|
|
|
526
507
|
recreate_if_broken=True,
|
|
527
508
|
cwd=cwd,
|
|
528
509
|
env=env,
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
uv_user=uv_user,
|
|
532
|
-
uv_index_url=uv_index_url,
|
|
533
|
-
uv_extra_pip_args=uv_extra_pip_args,
|
|
510
|
+
index_url=index_url,
|
|
511
|
+
extra_index_url=extra_index_url,
|
|
534
512
|
)
|
|
513
|
+
|
|
535
514
|
return env_obj
|
|
536
515
|
|
|
537
516
|
def update(
|
|
@@ -545,12 +524,8 @@ class PythonEnv:
|
|
|
545
524
|
recreate_if_broken: bool = True,
|
|
546
525
|
cwd: Optional[Path] = None,
|
|
547
526
|
env: Optional[Mapping[str, str]] = None,
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
uv_upgrade: bool = True,
|
|
551
|
-
uv_user: bool = True,
|
|
552
|
-
uv_index_url: Optional[str] = None,
|
|
553
|
-
uv_extra_pip_args: Optional[Iterable[str]] = None,
|
|
527
|
+
index_url: Optional[str] = None,
|
|
528
|
+
extra_index_url: str | list[str] | None = None,
|
|
554
529
|
) -> "PythonEnv":
|
|
555
530
|
"""
|
|
556
531
|
Install deps into *this* env (uv-only).
|
|
@@ -571,17 +546,13 @@ class PythonEnv:
|
|
|
571
546
|
clear=False,
|
|
572
547
|
cwd=cwd,
|
|
573
548
|
env=env,
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
uv_user=uv_user,
|
|
577
|
-
uv_index_url=uv_index_url,
|
|
578
|
-
uv_extra_pip_args=uv_extra_pip_args,
|
|
549
|
+
index_url=index_url,
|
|
550
|
+
extra_index_url=extra_index_url,
|
|
579
551
|
)
|
|
580
552
|
|
|
581
553
|
if not env_obj.exists():
|
|
582
554
|
if not recreate_if_broken:
|
|
583
555
|
raise PythonEnvError(f"Env exists but python missing: {env_obj.root}")
|
|
584
|
-
import datetime as _dt
|
|
585
556
|
|
|
586
557
|
ts = _dt.datetime.now().strftime("%Y%m%d-%H%M%S")
|
|
587
558
|
moved = env_obj.root.with_name(env_obj.root.name + f".broken-{ts}")
|
|
@@ -595,40 +566,25 @@ class PythonEnv:
|
|
|
595
566
|
clear=False,
|
|
596
567
|
cwd=cwd,
|
|
597
568
|
env=env,
|
|
598
|
-
|
|
599
|
-
uv_upgrade=uv_upgrade,
|
|
600
|
-
uv_user=uv_user,
|
|
601
|
-
uv_index_url=uv_index_url,
|
|
602
|
-
uv_extra_pip_args=uv_extra_pip_args,
|
|
569
|
+
index_url=index_url,
|
|
603
570
|
)
|
|
604
571
|
|
|
605
|
-
uv = self.__class__.ensure_uv(
|
|
606
|
-
check=check,
|
|
607
|
-
upgrade=uv_upgrade,
|
|
608
|
-
user=uv_user,
|
|
609
|
-
index_url=uv_index_url,
|
|
610
|
-
extra_pip_args=uv_extra_pip_args,
|
|
611
|
-
)
|
|
572
|
+
uv = self.__class__.ensure_uv()
|
|
612
573
|
|
|
613
574
|
extra = list(pip_args or [])
|
|
614
575
|
base_uv = [uv, "pip", "install", "--python", str(env_obj.python_executable)]
|
|
615
576
|
|
|
616
|
-
if upgrade_pip:
|
|
617
|
-
log.info("upgrading pip in env: %s", str(env_obj.root))
|
|
618
|
-
_run_cmd(base_uv + ["-U", "pip"] + extra, cwd=cwd, env=env, check=check)
|
|
619
|
-
|
|
620
577
|
if packages:
|
|
621
578
|
pkgs = [packages] if isinstance(packages, str) else list(packages)
|
|
622
579
|
|
|
623
580
|
if pkgs:
|
|
624
581
|
log.info("installing packages into env %s: %s", str(env_obj.root), pkgs)
|
|
625
|
-
_run_cmd(base_uv + ["-U"] + pkgs + extra, cwd=cwd, env=env
|
|
582
|
+
_run_cmd(base_uv + ["-U"] + pkgs + extra, cwd=cwd, env=env)
|
|
626
583
|
|
|
627
584
|
if requirements:
|
|
628
585
|
# requirements can be:
|
|
629
586
|
# - Path / str path to an existing file
|
|
630
587
|
# - raw requirements content (str) if path doesn't exist
|
|
631
|
-
req_path: Optional[Path] = None
|
|
632
588
|
tmp_ctx: Optional[tempfile.TemporaryDirectory] = None
|
|
633
589
|
|
|
634
590
|
try:
|
|
@@ -650,7 +606,11 @@ class PythonEnv:
|
|
|
650
606
|
raise PythonEnvError("requirements must be a path-like string/Path or raw requirements text")
|
|
651
607
|
|
|
652
608
|
log.info("installing requirements into env %s: %s", str(env_obj.root), str(req_path))
|
|
653
|
-
_run_cmd(
|
|
609
|
+
_run_cmd(
|
|
610
|
+
base_uv + ["-U", "-r", str(req_path)] + extra,
|
|
611
|
+
cwd=cwd, env=env,
|
|
612
|
+
extra_index_url=PipIndexSettings.default_settings().extra_index_url
|
|
613
|
+
)
|
|
654
614
|
|
|
655
615
|
finally:
|
|
656
616
|
if tmp_ctx is not None:
|
|
@@ -762,37 +722,33 @@ class PythonEnv:
|
|
|
762
722
|
root = Path(self.root).expanduser().resolve()
|
|
763
723
|
parent = root.parent
|
|
764
724
|
|
|
765
|
-
uv = self.__class__.ensure_uv(
|
|
725
|
+
uv = self.__class__.ensure_uv()
|
|
766
726
|
|
|
767
727
|
frozen_text: Optional[str] = None
|
|
768
728
|
if keep_packages and self.exists():
|
|
769
|
-
p = _run_cmd([uv, "pip", "freeze", "--python", str(self.python_executable)]
|
|
729
|
+
p = _run_cmd([uv, "pip", "freeze", "--python", str(self.python_executable)])
|
|
770
730
|
frozen_text = p.stdout or ""
|
|
771
731
|
|
|
772
732
|
with _locked_env(root):
|
|
773
733
|
if root.exists():
|
|
774
|
-
import datetime as _dt
|
|
775
|
-
|
|
776
734
|
ts = _dt.datetime.now().strftime("%Y%m%d-%H%M%S")
|
|
777
735
|
backup = parent / f"{root.name}.pychange-{_slug(req_str)}-{ts}"
|
|
778
736
|
log.info("changing python version; moving aside: %s -> %s", str(root), str(backup))
|
|
779
737
|
root.rename(backup)
|
|
780
738
|
|
|
781
739
|
log.info("recreating env with python=%s at %s", req_str, str(root))
|
|
782
|
-
_run_cmd([uv, "venv", str(root), "--python", req_str]
|
|
740
|
+
_run_cmd([uv, "venv", str(root), "--python", req_str])
|
|
783
741
|
|
|
784
742
|
new_env = self.__class__(root)
|
|
785
743
|
if not new_env.exists():
|
|
786
744
|
raise PythonEnvError(f"Recreated env but python missing: {new_env.python_executable}")
|
|
787
745
|
|
|
788
746
|
if keep_packages and frozen_text and frozen_text.strip():
|
|
789
|
-
import datetime as _dt
|
|
790
|
-
|
|
791
747
|
ts = _dt.datetime.now().strftime("%Y%m%d-%H%M%S")
|
|
792
748
|
req_path = parent / f".{root.name}.freeze-{ts}.txt"
|
|
793
749
|
req_path.write_text(frozen_text, encoding="utf-8")
|
|
794
750
|
try:
|
|
795
|
-
_run_cmd([uv, "pip", "install", "--python", str(new_env.python_executable), "-r", str(req_path)]
|
|
751
|
+
_run_cmd([uv, "pip", "install", "--python", str(new_env.python_executable), "-r", str(req_path)])
|
|
796
752
|
finally:
|
|
797
753
|
try:
|
|
798
754
|
req_path.unlink(missing_ok=True)
|
|
@@ -804,23 +760,30 @@ class PythonEnv:
|
|
|
804
760
|
# -----------------------
|
|
805
761
|
# requirements matrix
|
|
806
762
|
# -----------------------
|
|
763
|
+
def requirements(
|
|
764
|
+
self,
|
|
765
|
+
out_dir: Optional[Union[str, Path]] = None,
|
|
766
|
+
base_name: str = "requirements",
|
|
767
|
+
include_frozen: bool = True,
|
|
768
|
+
include_input: bool = True,
|
|
769
|
+
buffers: Optional[MutableMapping[str, str]] = None,
|
|
770
|
+
):
|
|
771
|
+
return self.export_requirements_matrix(
|
|
772
|
+
python_versions=[self.python_executable],
|
|
773
|
+
out_dir=out_dir, base_name=base_name, include_frozen=include_frozen,
|
|
774
|
+
include_input=include_input, buffers=buffers or {},
|
|
775
|
+
)[str(self.python_executable)]
|
|
807
776
|
|
|
808
777
|
def export_requirements_matrix(
|
|
809
778
|
self,
|
|
810
|
-
python_versions: Iterable[Union[str, Path]]
|
|
779
|
+
python_versions: Iterable[Union[str, Path]],
|
|
811
780
|
*,
|
|
812
781
|
out_dir: Optional[Union[str, Path]] = None,
|
|
813
782
|
base_name: str = "requirements",
|
|
814
783
|
include_frozen: bool = True,
|
|
815
784
|
include_input: bool = True,
|
|
816
|
-
check: bool = True,
|
|
817
785
|
buffers: Optional[MutableMapping[str, str]] = None,
|
|
818
|
-
|
|
819
|
-
uv_upgrade: bool = True,
|
|
820
|
-
uv_user: bool = True,
|
|
821
|
-
uv_index_url: Optional[str] = None,
|
|
822
|
-
uv_extra_pip_args: Optional[Iterable[str]] = None,
|
|
823
|
-
) -> dict[str, Union[Path, str]]:
|
|
786
|
+
) -> Union[str, dict[str, Union[Path, str]]]:
|
|
824
787
|
"""
|
|
825
788
|
Generate requirements to duplicate this env across multiple Python versions.
|
|
826
789
|
|
|
@@ -837,15 +800,6 @@ class PythonEnv:
|
|
|
837
800
|
- {base_name}.frozen.txt
|
|
838
801
|
- {base_name}-py<slug>.txt
|
|
839
802
|
"""
|
|
840
|
-
if not python_versions:
|
|
841
|
-
return self.export_requirements_matrix(
|
|
842
|
-
python_versions=[self.python_executable],
|
|
843
|
-
out_dir=out_dir, base_name=base_name, include_frozen=include_frozen,
|
|
844
|
-
include_input=include_input, check=check, buffers=buffers or {},
|
|
845
|
-
uv_upgrade=uv_upgrade, uv_user=uv_user, uv_index_url=uv_index_url,
|
|
846
|
-
uv_extra_pip_args=uv_extra_pip_args
|
|
847
|
-
)[str(self.python_executable)]
|
|
848
|
-
|
|
849
803
|
def _slug(s: str) -> str:
|
|
850
804
|
s = (s or "").strip()
|
|
851
805
|
if not s:
|
|
@@ -860,13 +814,7 @@ class PythonEnv:
|
|
|
860
814
|
if not self.exists():
|
|
861
815
|
raise PythonEnvError(f"Python executable not found in env: {self.python_executable}")
|
|
862
816
|
|
|
863
|
-
uv = self.__class__.ensure_uv(
|
|
864
|
-
check=check,
|
|
865
|
-
upgrade=uv_upgrade,
|
|
866
|
-
user=uv_user,
|
|
867
|
-
index_url=uv_index_url,
|
|
868
|
-
extra_pip_args=uv_extra_pip_args,
|
|
869
|
-
)
|
|
817
|
+
uv = self.__class__.ensure_uv()
|
|
870
818
|
|
|
871
819
|
write_files = out_dir is not None
|
|
872
820
|
|
|
@@ -921,12 +869,8 @@ print("RESULT:" + json.dumps(top_level))""".strip()
|
|
|
921
869
|
parse_json=True,
|
|
922
870
|
print_prefix_lines=False,
|
|
923
871
|
strip_payload=True,
|
|
924
|
-
check=check,
|
|
925
|
-
uv_upgrade=uv_upgrade,
|
|
926
|
-
uv_user=uv_user,
|
|
927
|
-
uv_index_url=uv_index_url,
|
|
928
|
-
uv_extra_pip_args=uv_extra_pip_args,
|
|
929
872
|
)
|
|
873
|
+
|
|
930
874
|
if not isinstance(top_level, list) or not all(isinstance(x, str) for x in top_level):
|
|
931
875
|
raise PythonEnvError(f"Unexpected top-level requirements payload: {top_level!r}")
|
|
932
876
|
|
|
@@ -941,7 +885,7 @@ print("RESULT:" + json.dumps(top_level))""".strip()
|
|
|
941
885
|
frozen_path = out_root / f"{base_name}.frozen.txt"
|
|
942
886
|
if include_frozen:
|
|
943
887
|
log.info("exporting frozen requirements: %s", str(frozen_path))
|
|
944
|
-
p = _run_cmd([uv, "pip", "freeze", "--python", str(self.python_executable)]
|
|
888
|
+
p = _run_cmd([uv, "pip", "freeze", "--python", str(self.python_executable)])
|
|
945
889
|
frozen_text = p.stdout or ""
|
|
946
890
|
if write_files:
|
|
947
891
|
frozen_path.write_text(frozen_text, encoding="utf-8")
|
|
@@ -960,7 +904,7 @@ print("RESULT:" + json.dumps(top_level))""".strip()
|
|
|
960
904
|
log.info("compiling requirements for python=%s -> %s", py_req, str(out_path))
|
|
961
905
|
|
|
962
906
|
cmd = [uv, "pip", "compile", str(req_in_path), "--python", py_req, "-o", str(out_path)]
|
|
963
|
-
_run_cmd(cmd
|
|
907
|
+
_run_cmd(cmd)
|
|
964
908
|
|
|
965
909
|
if write_files:
|
|
966
910
|
compiled_paths[py_req] = out_path
|
|
@@ -978,6 +922,21 @@ print("RESULT:" + json.dumps(top_level))""".strip()
|
|
|
978
922
|
if tmp_ctx is not None:
|
|
979
923
|
tmp_ctx.cleanup()
|
|
980
924
|
|
|
925
|
+
def installed_packages(self, parsed: bool = False) -> List[Tuple[str, str]]:
|
|
926
|
+
req = self.requirements()
|
|
927
|
+
|
|
928
|
+
r = [
|
|
929
|
+
line.strip()
|
|
930
|
+
for line in req.splitlines() if line.strip() and not line.strip().startswith("#")
|
|
931
|
+
]
|
|
932
|
+
|
|
933
|
+
if parsed:
|
|
934
|
+
return [
|
|
935
|
+
tuple(re.split(r"==|>=|<=|>|<|~=|!=", line, 1)) if re.search(r"==|>=|<=|>|<|~=|!=", line) else (line, "")
|
|
936
|
+
for line in r
|
|
937
|
+
]
|
|
938
|
+
return r
|
|
939
|
+
|
|
981
940
|
# -----------------------
|
|
982
941
|
# execute (uv run always)
|
|
983
942
|
# -----------------------
|
|
@@ -990,10 +949,6 @@ print("RESULT:" + json.dumps(top_level))""".strip()
|
|
|
990
949
|
cwd: Optional[Path] = None,
|
|
991
950
|
env: Optional[Mapping[str, str]] = None,
|
|
992
951
|
check: bool = True,
|
|
993
|
-
uv_upgrade: bool = True,
|
|
994
|
-
uv_user: bool = True,
|
|
995
|
-
uv_index_url: Optional[str] = None,
|
|
996
|
-
uv_extra_pip_args: Optional[Iterable[str]] = None,
|
|
997
952
|
) -> str:
|
|
998
953
|
# pick interpreter (default = env python)
|
|
999
954
|
if python is None:
|
|
@@ -1005,17 +960,10 @@ print("RESULT:" + json.dumps(top_level))""".strip()
|
|
|
1005
960
|
if not py.exists():
|
|
1006
961
|
raise PythonEnvError(f"Python executable not found: {py}")
|
|
1007
962
|
|
|
1008
|
-
uv = PythonEnv.ensure_uv(
|
|
1009
|
-
check=check,
|
|
1010
|
-
upgrade=uv_upgrade,
|
|
1011
|
-
user=uv_user,
|
|
1012
|
-
index_url=uv_index_url,
|
|
1013
|
-
extra_pip_args=uv_extra_pip_args,
|
|
1014
|
-
)
|
|
1015
|
-
|
|
963
|
+
uv = PythonEnv.ensure_uv()
|
|
1016
964
|
cmd = [uv, "run", "--python", str(py), "--", "python", "-c", code]
|
|
1017
965
|
log.debug("exec_code env=%s python=%s", str(self.root), str(py))
|
|
1018
|
-
p = _run_cmd(cmd, cwd=cwd, env=env
|
|
966
|
+
p = _run_cmd(cmd, cwd=cwd, env=env)
|
|
1019
967
|
return p.stdout or ""
|
|
1020
968
|
|
|
1021
969
|
def exec_code_and_return(
|
|
@@ -1030,10 +978,6 @@ print("RESULT:" + json.dumps(top_level))""".strip()
|
|
|
1030
978
|
parse_json: bool = False,
|
|
1031
979
|
print_prefix_lines: bool = True,
|
|
1032
980
|
strip_payload: bool = True,
|
|
1033
|
-
uv_upgrade: bool = True,
|
|
1034
|
-
uv_user: bool = True,
|
|
1035
|
-
uv_index_url: Optional[str] = None,
|
|
1036
|
-
uv_extra_pip_args: Optional[Iterable[str]] = None,
|
|
1037
981
|
) -> Any:
|
|
1038
982
|
stdout = self.exec_code(
|
|
1039
983
|
code,
|
|
@@ -1041,10 +985,6 @@ print("RESULT:" + json.dumps(top_level))""".strip()
|
|
|
1041
985
|
cwd=cwd,
|
|
1042
986
|
env=env,
|
|
1043
987
|
check=check,
|
|
1044
|
-
uv_upgrade=uv_upgrade,
|
|
1045
|
-
uv_user=uv_user,
|
|
1046
|
-
uv_index_url=uv_index_url,
|
|
1047
|
-
uv_extra_pip_args=uv_extra_pip_args,
|
|
1048
988
|
)
|
|
1049
989
|
|
|
1050
990
|
before_lines, payload = _split_on_tag(stdout, result_tag)
|
|
@@ -1142,124 +1082,6 @@ print("RESULT:" + json.dumps(top_level))""".strip()
|
|
|
1142
1082
|
|
|
1143
1083
|
return payload
|
|
1144
1084
|
|
|
1145
|
-
# -----------------------
|
|
1146
|
-
# zip helpers
|
|
1147
|
-
# -----------------------
|
|
1148
|
-
|
|
1149
|
-
def zip_bytes(
|
|
1150
|
-
self,
|
|
1151
|
-
*,
|
|
1152
|
-
include_cache: bool = False,
|
|
1153
|
-
include_pycache: bool = False,
|
|
1154
|
-
include_dist_info: bool = True,
|
|
1155
|
-
extra_exclude_globs: Optional[Iterable[str]] = None,
|
|
1156
|
-
compression: int = zipfile.ZIP_DEFLATED,
|
|
1157
|
-
) -> bytes:
|
|
1158
|
-
buf = io.BytesIO()
|
|
1159
|
-
self._zip_to_fileobj(
|
|
1160
|
-
buf,
|
|
1161
|
-
include_cache=include_cache,
|
|
1162
|
-
include_pycache=include_pycache,
|
|
1163
|
-
include_dist_info=include_dist_info,
|
|
1164
|
-
extra_exclude_globs=extra_exclude_globs,
|
|
1165
|
-
compression=compression,
|
|
1166
|
-
)
|
|
1167
|
-
return buf.getvalue()
|
|
1168
|
-
|
|
1169
|
-
def zip_to(
|
|
1170
|
-
self,
|
|
1171
|
-
out_zip: Path,
|
|
1172
|
-
*,
|
|
1173
|
-
include_cache: bool = False,
|
|
1174
|
-
include_pycache: bool = False,
|
|
1175
|
-
include_dist_info: bool = True,
|
|
1176
|
-
extra_exclude_globs: Optional[Iterable[str]] = None,
|
|
1177
|
-
compression: int = zipfile.ZIP_DEFLATED,
|
|
1178
|
-
in_memory: bool = False,
|
|
1179
|
-
) -> Union[Path, bytes]:
|
|
1180
|
-
if in_memory:
|
|
1181
|
-
return self.zip_bytes(
|
|
1182
|
-
include_cache=include_cache,
|
|
1183
|
-
include_pycache=include_pycache,
|
|
1184
|
-
include_dist_info=include_dist_info,
|
|
1185
|
-
extra_exclude_globs=extra_exclude_globs,
|
|
1186
|
-
compression=compression,
|
|
1187
|
-
)
|
|
1188
|
-
|
|
1189
|
-
out_zip = Path(out_zip).expanduser().resolve()
|
|
1190
|
-
out_zip.parent.mkdir(parents=True, exist_ok=True)
|
|
1191
|
-
|
|
1192
|
-
with out_zip.open("wb") as f:
|
|
1193
|
-
self._zip_to_fileobj(
|
|
1194
|
-
f,
|
|
1195
|
-
include_cache=include_cache,
|
|
1196
|
-
include_pycache=include_pycache,
|
|
1197
|
-
include_dist_info=include_dist_info,
|
|
1198
|
-
extra_exclude_globs=extra_exclude_globs,
|
|
1199
|
-
compression=compression,
|
|
1200
|
-
out_zip_path=out_zip,
|
|
1201
|
-
)
|
|
1202
|
-
return out_zip
|
|
1203
|
-
|
|
1204
|
-
def _zip_to_fileobj(
|
|
1205
|
-
self,
|
|
1206
|
-
fileobj,
|
|
1207
|
-
*,
|
|
1208
|
-
include_cache: bool,
|
|
1209
|
-
include_pycache: bool,
|
|
1210
|
-
include_dist_info: bool,
|
|
1211
|
-
extra_exclude_globs: Optional[Iterable[str]],
|
|
1212
|
-
compression: int,
|
|
1213
|
-
out_zip_path: Optional[Path] = None,
|
|
1214
|
-
) -> None:
|
|
1215
|
-
root = Path(self.root).expanduser().resolve()
|
|
1216
|
-
if not root.exists():
|
|
1217
|
-
raise PythonEnvError(f"Env root does not exist: {root}")
|
|
1218
|
-
if not root.is_dir():
|
|
1219
|
-
raise PythonEnvError(f"Env root is not a directory: {root}")
|
|
1220
|
-
|
|
1221
|
-
extra_exclude_globs = list(extra_exclude_globs or [])
|
|
1222
|
-
|
|
1223
|
-
def _match_any(rel_posix: str, patterns: list[str]) -> bool:
|
|
1224
|
-
# IMPORTANT: use PurePosixPath for stable glob matching across OSes
|
|
1225
|
-
p = PurePosixPath(rel_posix)
|
|
1226
|
-
for pat in patterns:
|
|
1227
|
-
if p.match(pat):
|
|
1228
|
-
return True
|
|
1229
|
-
return False
|
|
1230
|
-
|
|
1231
|
-
exclude_patterns: list[str] = []
|
|
1232
|
-
if not include_pycache:
|
|
1233
|
-
exclude_patterns += ["**/__pycache__/**", "**/*.pyc", "**/*.pyo"]
|
|
1234
|
-
if not include_cache:
|
|
1235
|
-
exclude_patterns += [
|
|
1236
|
-
"**/.cache/**",
|
|
1237
|
-
"**/pip-cache/**",
|
|
1238
|
-
"**/pip/**/cache/**",
|
|
1239
|
-
"**/Cache/**",
|
|
1240
|
-
"**/Caches/**",
|
|
1241
|
-
]
|
|
1242
|
-
if not include_dist_info:
|
|
1243
|
-
exclude_patterns += ["**/*.dist-info/**"]
|
|
1244
|
-
|
|
1245
|
-
exclude_patterns += extra_exclude_globs
|
|
1246
|
-
|
|
1247
|
-
if out_zip_path is not None:
|
|
1248
|
-
try:
|
|
1249
|
-
rel_out = out_zip_path.relative_to(root).as_posix()
|
|
1250
|
-
exclude_patterns.append(rel_out)
|
|
1251
|
-
except ValueError:
|
|
1252
|
-
pass
|
|
1253
|
-
|
|
1254
|
-
with zipfile.ZipFile(fileobj, mode="w", compression=compression) as zf:
|
|
1255
|
-
for abs_path in root.rglob("*"):
|
|
1256
|
-
if abs_path.is_dir():
|
|
1257
|
-
continue
|
|
1258
|
-
rel_posix = abs_path.relative_to(root).as_posix()
|
|
1259
|
-
if _match_any(rel_posix, exclude_patterns):
|
|
1260
|
-
continue
|
|
1261
|
-
zf.write(abs_path, rel_posix)
|
|
1262
|
-
|
|
1263
1085
|
# -----------------------
|
|
1264
1086
|
# CLI (uv everywhere)
|
|
1265
1087
|
# -----------------------
|
|
@@ -1314,12 +1136,7 @@ print("RESULT:" + json.dumps(top_level))""".strip()
|
|
|
1314
1136
|
|
|
1315
1137
|
try:
|
|
1316
1138
|
if args.cmd == "ensure-uv":
|
|
1317
|
-
uv = cls.ensure_uv(
|
|
1318
|
-
upgrade=not args.no_upgrade,
|
|
1319
|
-
user=not args.no_user,
|
|
1320
|
-
index_url=args.index_url,
|
|
1321
|
-
extra_pip_args=args.pip_arg or None,
|
|
1322
|
-
)
|
|
1139
|
+
uv = cls.ensure_uv()
|
|
1323
1140
|
print(uv)
|
|
1324
1141
|
return 0
|
|
1325
1142
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|