ygg 0.1.21__tar.gz → 0.1.23__tar.gz
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.21 → ygg-0.1.23}/PKG-INFO +1 -1
- {ygg-0.1.21 → ygg-0.1.23}/pyproject.toml +1 -1
- {ygg-0.1.21 → ygg-0.1.23}/src/ygg.egg-info/PKG-INFO +1 -1
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/databricks/compute/cluster.py +9 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/databricks/compute/execution_context.py +1 -1
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/pyutils/python_env.py +28 -19
- {ygg-0.1.21 → ygg-0.1.23}/LICENSE +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/README.md +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/setup.cfg +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/ygg.egg-info/SOURCES.txt +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/ygg.egg-info/dependency_links.txt +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/ygg.egg-info/entry_points.txt +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/ygg.egg-info/requires.txt +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/ygg.egg-info/top_level.txt +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/__init__.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/databricks/__init__.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/databricks/compute/__init__.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/databricks/compute/remote.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/databricks/jobs/__init__.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/databricks/jobs/config.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/databricks/sql/__init__.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/databricks/sql/engine.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/databricks/sql/exceptions.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/databricks/sql/statement_result.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/databricks/sql/types.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/databricks/workspaces/__init__.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/databricks/workspaces/databricks_path.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/databricks/workspaces/workspace.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/dataclasses/__init__.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/dataclasses/dataclass.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/libs/__init__.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/libs/databrickslib.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/libs/extensions/__init__.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/libs/extensions/polars_extensions.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/libs/extensions/spark_extensions.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/libs/pandaslib.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/libs/polarslib.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/libs/sparklib.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/pyutils/__init__.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/pyutils/callable_serde.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/pyutils/exceptions.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/pyutils/modules.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/pyutils/parallel.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/pyutils/retry.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/requests/__init__.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/requests/msal.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/requests/session.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/types/__init__.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/types/cast/__init__.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/types/cast/arrow_cast.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/types/cast/cast_options.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/types/cast/pandas_cast.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/types/cast/polars_cast.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/types/cast/polars_pandas_cast.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/types/cast/registry.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/types/cast/spark_cast.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/types/cast/spark_pandas_cast.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/types/cast/spark_polars_cast.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/types/libs.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/types/python_arrow.py +0 -0
- {ygg-0.1.21 → ygg-0.1.23}/src/yggdrasil/types/python_defaults.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ygg"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.23"
|
|
8
8
|
description = "Type-friendly utilities for moving data between Python objects, Arrow, Polars, Pandas, Spark, and Databricks"
|
|
9
9
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
10
|
license = { file = "LICENSE" }
|
|
@@ -390,6 +390,8 @@ class Cluster(WorkspaceService):
|
|
|
390
390
|
python_version: Optional[Union[str, tuple[int, ...]]] = None,
|
|
391
391
|
**kwargs
|
|
392
392
|
):
|
|
393
|
+
pip_settings = PipIndexSettings.default_settings()
|
|
394
|
+
|
|
393
395
|
if kwargs:
|
|
394
396
|
details = ClusterDetails(**{
|
|
395
397
|
**details.as_shallow_dict(),
|
|
@@ -421,6 +423,13 @@ class Cluster(WorkspaceService):
|
|
|
421
423
|
if details.is_single_node is not None and details.kind is None:
|
|
422
424
|
details.kind = Kind.CLASSIC_PREVIEW
|
|
423
425
|
|
|
426
|
+
if pip_settings.extra_index_urls:
|
|
427
|
+
if details.spark_env_vars is None:
|
|
428
|
+
details.spark_env_vars = {}
|
|
429
|
+
str_urls = " ".join(pip_settings.extra_index_urls)
|
|
430
|
+
details.spark_env_vars["UV_EXTRA_INDEX_URL"] = details.spark_env_vars.get("UV_INDEX", str_urls)
|
|
431
|
+
details.spark_env_vars["PIP_EXTRA_INDEX_URL"] = details.spark_env_vars.get("PIP_EXTRA_INDEX_URL", str_urls)
|
|
432
|
+
|
|
424
433
|
return details
|
|
425
434
|
|
|
426
435
|
def create_or_update(
|
|
@@ -40,6 +40,9 @@ _LOCKS_GUARD: threading.RLock = threading.RLock()
|
|
|
40
40
|
|
|
41
41
|
# Installed into newly created envs (and on create() upsert when env already exists)
|
|
42
42
|
DEFAULT_CREATE_PACKAGES: tuple[str, ...] = ("uv", "ygg")
|
|
43
|
+
EXCLUDED_PACKAGES = {
|
|
44
|
+
"python-apt"
|
|
45
|
+
}
|
|
43
46
|
|
|
44
47
|
|
|
45
48
|
def _is_windows() -> bool:
|
|
@@ -464,7 +467,7 @@ class PythonEnv:
|
|
|
464
467
|
|
|
465
468
|
env_obj = cls(root)
|
|
466
469
|
if not env_obj.exists():
|
|
467
|
-
raise PythonEnvError(f"Created env but python missing: {env_obj.
|
|
470
|
+
raise PythonEnvError(f"Created env but python missing: {env_obj.python_executable}")
|
|
468
471
|
|
|
469
472
|
env_obj.update(
|
|
470
473
|
packages=install_pkgs,
|
|
@@ -560,17 +563,18 @@ class PythonEnv:
|
|
|
560
563
|
)
|
|
561
564
|
|
|
562
565
|
extra = list(pip_args or [])
|
|
563
|
-
|
|
566
|
+
base_uv = [uv, "pip", "install", "--python", str(env_obj.python_executable)]
|
|
564
567
|
|
|
565
568
|
if upgrade_pip:
|
|
566
569
|
log.info("upgrading pip in env: %s", str(env_obj.root))
|
|
567
|
-
_run_cmd(
|
|
570
|
+
_run_cmd(base_uv + ["-U", "pip"] + extra, cwd=cwd, env=env, check=check)
|
|
568
571
|
|
|
569
572
|
if packages:
|
|
570
573
|
pkgs = [packages] if isinstance(packages, str) else list(packages)
|
|
574
|
+
|
|
571
575
|
if pkgs:
|
|
572
576
|
log.info("installing packages into env %s: %s", str(env_obj.root), pkgs)
|
|
573
|
-
_run_cmd(
|
|
577
|
+
_run_cmd(base_uv + ["-U"] + pkgs + extra, cwd=cwd, env=env, check=check)
|
|
574
578
|
|
|
575
579
|
if requirements:
|
|
576
580
|
# requirements can be:
|
|
@@ -598,7 +602,7 @@ class PythonEnv:
|
|
|
598
602
|
raise PythonEnvError("requirements must be a path-like string/Path or raw requirements text")
|
|
599
603
|
|
|
600
604
|
log.info("installing requirements into env %s: %s", str(env_obj.root), str(req_path))
|
|
601
|
-
_run_cmd(
|
|
605
|
+
_run_cmd(base_uv + ["-U", "-r", str(req_path)] + extra, cwd=cwd, env=env, check=check)
|
|
602
606
|
|
|
603
607
|
finally:
|
|
604
608
|
if tmp_ctx is not None:
|
|
@@ -634,12 +638,12 @@ class PythonEnv:
|
|
|
634
638
|
return n if n else str(self.root)
|
|
635
639
|
|
|
636
640
|
@property
|
|
637
|
-
def
|
|
641
|
+
def python_executable(self) -> Path:
|
|
638
642
|
exe = "python.exe" if _is_windows() else "python"
|
|
639
643
|
return self.bindir / exe
|
|
640
644
|
|
|
641
645
|
def exists(self) -> bool:
|
|
642
|
-
return self.
|
|
646
|
+
return self.python_executable.exists()
|
|
643
647
|
|
|
644
648
|
@property
|
|
645
649
|
def version(self) -> str:
|
|
@@ -714,7 +718,7 @@ class PythonEnv:
|
|
|
714
718
|
|
|
715
719
|
frozen_text: Optional[str] = None
|
|
716
720
|
if keep_packages and self.exists():
|
|
717
|
-
p = _run_cmd([uv, "pip", "freeze", "--python", str(self.
|
|
721
|
+
p = _run_cmd([uv, "pip", "freeze", "--python", str(self.python_executable)], check=True)
|
|
718
722
|
frozen_text = p.stdout or ""
|
|
719
723
|
|
|
720
724
|
with _locked_env(root):
|
|
@@ -731,7 +735,7 @@ class PythonEnv:
|
|
|
731
735
|
|
|
732
736
|
new_env = self.__class__(root)
|
|
733
737
|
if not new_env.exists():
|
|
734
|
-
raise PythonEnvError(f"Recreated env but python missing: {new_env.
|
|
738
|
+
raise PythonEnvError(f"Recreated env but python missing: {new_env.python_executable}")
|
|
735
739
|
|
|
736
740
|
if keep_packages and frozen_text and frozen_text.strip():
|
|
737
741
|
import datetime as _dt
|
|
@@ -740,7 +744,7 @@ class PythonEnv:
|
|
|
740
744
|
req_path = parent / f".{root.name}.freeze-{ts}.txt"
|
|
741
745
|
req_path.write_text(frozen_text, encoding="utf-8")
|
|
742
746
|
try:
|
|
743
|
-
_run_cmd([uv, "pip", "install", "--python", str(new_env.
|
|
747
|
+
_run_cmd([uv, "pip", "install", "--python", str(new_env.python_executable), "-r", str(req_path)], check=True)
|
|
744
748
|
finally:
|
|
745
749
|
try:
|
|
746
750
|
req_path.unlink(missing_ok=True)
|
|
@@ -787,12 +791,12 @@ class PythonEnv:
|
|
|
787
791
|
"""
|
|
788
792
|
if not python_versions:
|
|
789
793
|
return self.export_requirements_matrix(
|
|
790
|
-
python_versions=[self.
|
|
794
|
+
python_versions=[self.python_executable],
|
|
791
795
|
out_dir=out_dir, base_name=base_name, include_frozen=include_frozen,
|
|
792
796
|
include_input=include_input, check=check, buffers=buffers or {},
|
|
793
797
|
uv_upgrade=uv_upgrade, uv_user=uv_user, uv_index_url=uv_index_url,
|
|
794
798
|
uv_extra_pip_args=uv_extra_pip_args
|
|
795
|
-
)[str(self.
|
|
799
|
+
)[str(self.python_executable)]
|
|
796
800
|
|
|
797
801
|
def _slug(s: str) -> str:
|
|
798
802
|
s = (s or "").strip()
|
|
@@ -806,7 +810,7 @@ class PythonEnv:
|
|
|
806
810
|
raise PythonEnvError("python_versions cannot be empty")
|
|
807
811
|
|
|
808
812
|
if not self.exists():
|
|
809
|
-
raise PythonEnvError(f"Python executable not found in env: {self.
|
|
813
|
+
raise PythonEnvError(f"Python executable not found in env: {self.python_executable}")
|
|
810
814
|
|
|
811
815
|
uv = self.__class__.ensure_uv(
|
|
812
816
|
check=check,
|
|
@@ -878,7 +882,12 @@ print("RESULT:" + json.dumps(top_level))""".strip()
|
|
|
878
882
|
if not isinstance(top_level, list) or not all(isinstance(x, str) for x in top_level):
|
|
879
883
|
raise PythonEnvError(f"Unexpected top-level requirements payload: {top_level!r}")
|
|
880
884
|
|
|
881
|
-
|
|
885
|
+
# exclude python-apt (yanked/unmaintained; not usable on Databricks)
|
|
886
|
+
_pyapt_re = re.compile(r"^\s*python-apt(\s*(?:==|~=|!=|<=|>=|<|>|\[)|\s*(?:;|$))", re.IGNORECASE)
|
|
887
|
+
|
|
888
|
+
filtered = [line for line in top_level if not _pyapt_re.match(line)]
|
|
889
|
+
|
|
890
|
+
req_in_text = "\n".join(filtered) + "\n"
|
|
882
891
|
req_in_path.write_text(req_in_text, encoding="utf-8")
|
|
883
892
|
if buffers is not None:
|
|
884
893
|
buffers[f"{base_name}.in"] = req_in_text
|
|
@@ -887,7 +896,7 @@ print("RESULT:" + json.dumps(top_level))""".strip()
|
|
|
887
896
|
frozen_path = out_root / f"{base_name}.frozen.txt"
|
|
888
897
|
if include_frozen:
|
|
889
898
|
log.info("exporting frozen requirements: %s", str(frozen_path))
|
|
890
|
-
p = _run_cmd([uv, "pip", "freeze", "--python", str(self.
|
|
899
|
+
p = _run_cmd([uv, "pip", "freeze", "--python", str(self.python_executable)], check=check)
|
|
891
900
|
frozen_text = p.stdout or ""
|
|
892
901
|
if write_files:
|
|
893
902
|
frozen_path.write_text(frozen_text, encoding="utf-8")
|
|
@@ -944,8 +953,8 @@ print("RESULT:" + json.dumps(top_level))""".strip()
|
|
|
944
953
|
# pick interpreter (default = env python)
|
|
945
954
|
if python is None:
|
|
946
955
|
if not self.exists():
|
|
947
|
-
raise PythonEnvError(f"Python executable not found in env: {self.
|
|
948
|
-
py = self.
|
|
956
|
+
raise PythonEnvError(f"Python executable not found in env: {self.python_executable}")
|
|
957
|
+
py = self.python_executable
|
|
949
958
|
else:
|
|
950
959
|
py = Path(python).expanduser().resolve() if isinstance(python, Path) else Path(str(python)).expanduser().resolve()
|
|
951
960
|
if not py.exists():
|
|
@@ -1277,7 +1286,7 @@ print("RESULT:" + json.dumps(top_level))""".strip()
|
|
|
1277
1286
|
require_python=require_python,
|
|
1278
1287
|
dedupe=True,
|
|
1279
1288
|
):
|
|
1280
|
-
py = str(e.
|
|
1289
|
+
py = str(e.python_executable) if e.exists() else "-"
|
|
1281
1290
|
print(f"{e.name}\t{e.root}\t{py}")
|
|
1282
1291
|
return 0
|
|
1283
1292
|
|
|
@@ -1323,7 +1332,7 @@ print("RESULT:" + json.dumps(top_level))""".strip()
|
|
|
1323
1332
|
if args.cmd == "current":
|
|
1324
1333
|
cur = cls.get_current()
|
|
1325
1334
|
print(f"root={cur.root}")
|
|
1326
|
-
print(f"python={cur.
|
|
1335
|
+
print(f"python={cur.python_executable}")
|
|
1327
1336
|
return 0
|
|
1328
1337
|
|
|
1329
1338
|
raise PythonEnvError(f"Unknown command: {args.cmd}")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|