Skip to content

Commit

Permalink
prefers uv over pip if found (#1940)
Browse files Browse the repository at this point in the history
* allows to pass run_dir to RunContext init

* uses uv to install deps in Venv if found

* removes semver deprecations
  • Loading branch information
rudolfix authored Oct 10, 2024
1 parent 117220b commit 47633c6
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 38 deletions.
3 changes: 3 additions & 0 deletions dlt/common/known_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@

DLT_JSON_TYPED_PUA_START = "DLT_JSON_TYPED_PUA_START"
"""Start of the unicode block within the PUA used to encode types in typed json"""

DLT_PIP_TOOL = "DLT_PIP_TOOL"
"""Pip tool used to install deps in Venv"""
18 changes: 15 additions & 3 deletions dlt/common/runners/venv.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
import venv
import types
import subprocess
from typing import Any, List, Type
from typing import Any, ClassVar, List, Type

from dlt.common import known_env
from dlt.common.exceptions import CannotInstallDependencies, VenvNotFound


Expand All @@ -22,6 +23,8 @@ def post_setup(self, context: types.SimpleNamespace) -> None:
class Venv:
"""Creates and wraps the Python Virtual Environment to allow for code execution"""

PIP_TOOL: ClassVar[str] = os.environ.get(known_env.DLT_PIP_TOOL, None)

def __init__(self, context: types.SimpleNamespace, current: bool = False) -> None:
"""Please use `Venv.create`, `Venv.restore` or `Venv.restore_current` methods to create Venv instance"""
self.context = context
Expand Down Expand Up @@ -119,8 +122,17 @@ def add_dependencies(self, dependencies: List[str] = None) -> None:

@staticmethod
def _install_deps(context: types.SimpleNamespace, dependencies: List[str]) -> None:
cmd = [context.env_exe, "-Im", "pip", "install"]
# cmd = ["uv", "pip", "install", "--python", context.env_exe]
if Venv.PIP_TOOL is None:
# autodetect tool
import shutil

Venv.PIP_TOOL = "uv" if shutil.which("uv") else "pip"

if Venv.PIP_TOOL == "uv":
cmd = ["uv", "pip", "install", "--python", context.env_exe]
else:
cmd = [context.env_exe, "-Im", Venv.PIP_TOOL, "install"]

try:
subprocess.check_output(cmd + dependencies, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as exc:
Expand Down
5 changes: 4 additions & 1 deletion dlt/common/runtime/run_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,17 @@ class RunContext(SupportsRunContext):

CONTEXT_NAME: ClassVar[str] = "dlt"

def __init__(self, run_dir: str = "."):
self._init_run_dir = run_dir

@property
def global_dir(self) -> str:
return self.data_dir

@property
def run_dir(self) -> str:
"""The default run dir is the current working directory but may be overridden by DLT_PROJECT_DIR env variable."""
return os.environ.get(known_env.DLT_PROJECT_DIR, ".")
return os.environ.get(known_env.DLT_PROJECT_DIR, self._init_run_dir)

@property
def settings_dir(self) -> str:
Expand Down
14 changes: 7 additions & 7 deletions dlt/common/storages/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ class NoMigrationPathException(StorageException):
def __init__(
self,
storage_path: str,
initial_version: semver.VersionInfo,
migrated_version: semver.VersionInfo,
target_version: semver.VersionInfo,
initial_version: semver.Version,
migrated_version: semver.Version,
target_version: semver.Version,
) -> None:
self.storage_path = storage_path
self.initial_version = initial_version
Expand All @@ -31,8 +31,8 @@ class WrongStorageVersionException(StorageException):
def __init__(
self,
storage_path: str,
initial_version: semver.VersionInfo,
target_version: semver.VersionInfo,
initial_version: semver.Version,
target_version: semver.Version,
) -> None:
self.storage_path = storage_path
self.initial_version = initial_version
Expand All @@ -46,8 +46,8 @@ class StorageMigrationError(StorageException):
def __init__(
self,
storage_path: str,
from_version: semver.VersionInfo,
target_version: semver.VersionInfo,
from_version: semver.Version,
target_version: semver.Version,
info: str,
) -> None:
self.storage_path = storage_path
Expand Down
6 changes: 3 additions & 3 deletions dlt/common/storages/normalize_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import semver
from typing import ClassVar, Sequence

from semver import VersionInfo
from semver import Version

from dlt.common.configuration import with_config, known_sections
from dlt.common.configuration.accessors import config
Expand Down Expand Up @@ -57,7 +57,7 @@ def list_files_to_normalize_sorted(self) -> Sequence[str]:
]
)

def migrate_storage(self, from_version: VersionInfo, to_version: VersionInfo) -> None:
def migrate_storage(self, from_version: Version, to_version: Version) -> None:
if from_version == "1.0.0" and from_version < to_version:
# get files in storage
if len(self.list_files_to_normalize_sorted()) > 0:
Expand All @@ -69,5 +69,5 @@ def migrate_storage(self, from_version: VersionInfo, to_version: VersionInfo) ->
" Storage will not migrate automatically duo to possible data loss. Delete the"
" files or normalize it with dlt 0.3.x",
)
from_version = semver.VersionInfo.parse("1.0.1")
from_version = semver.Version.parse("1.0.1")
self._save_version(from_version)
20 changes: 9 additions & 11 deletions dlt/common/storages/versioned_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ class VersionedStorage:
VERSION_FILE = ".version"

def __init__(
self, version: Union[semver.VersionInfo, str], is_owner: bool, storage: FileStorage
self, version: Union[semver.Version, str], is_owner: bool, storage: FileStorage
) -> None:
if isinstance(version, str):
version = semver.VersionInfo.parse(version)
version = semver.Version.parse(version)
self.storage = storage
# read current version
if self.storage.has_file(VersionedStorage.VERSION_FILE):
Expand Down Expand Up @@ -43,28 +43,26 @@ def __init__(
self._save_version(version)
else:
raise WrongStorageVersionException(
storage.storage_path, semver.VersionInfo.parse("0.0.0"), version
storage.storage_path, semver.Version.parse("0.0.0"), version
)

def migrate_storage(
self, from_version: semver.VersionInfo, to_version: semver.VersionInfo
) -> None:
def migrate_storage(self, from_version: semver.Version, to_version: semver.Version) -> None:
# migration example:
# # semver lib supports comparing both to string and other semvers
# if from_version == "1.0.0" and from_version < to_version:
# # do migration
# # save migrated version
# from_version = semver.VersionInfo.parse("1.1.0")
# from_version = semver.Version.parse("1.1.0")
# self._save_version(from_version)
pass

@property
def version(self) -> semver.VersionInfo:
def version(self) -> semver.Version:
return self._load_version()

def _load_version(self) -> semver.VersionInfo:
def _load_version(self) -> semver.Version:
version_str = self.storage.load(VersionedStorage.VERSION_FILE)
return semver.VersionInfo.parse(version_str)
return semver.Version.parse(version_str)

def _save_version(self, version: semver.VersionInfo) -> None:
def _save_version(self, version: semver.Version) -> None:
self.storage.save(VersionedStorage.VERSION_FILE, str(version))
14 changes: 6 additions & 8 deletions dlt/common/warnings.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from dlt.version import __version__

VersionString = typing.Union[str, semver.VersionInfo]
VersionString = typing.Union[str, semver.Version]


class DltDeprecationWarning(DeprecationWarning):
Expand All @@ -30,14 +30,12 @@ def __init__(
) -> None:
super().__init__(message, *args)
self.message = message.rstrip(".")
self.since = (
since if isinstance(since, semver.VersionInfo) else semver.parse_version_info(since)
)
self.since = since if isinstance(since, semver.Version) else semver.Version.parse(since)
if expected_due:
expected_due = (
expected_due
if isinstance(expected_due, semver.VersionInfo)
else semver.parse_version_info(expected_due)
if isinstance(expected_due, semver.Version)
else semver.Version.parse(expected_due)
)
# we deprecate across major version since 1.0.0
self.expected_due = expected_due if expected_due is not None else self.since.bump_major()
Expand All @@ -50,7 +48,7 @@ def __str__(self) -> str:


class Dlt04DeprecationWarning(DltDeprecationWarning):
V04 = semver.parse_version_info("0.4.0")
V04 = semver.Version.parse("0.4.0")

def __init__(self, message: str, *args: typing.Any, expected_due: VersionString = None) -> None:
super().__init__(
Expand All @@ -59,7 +57,7 @@ def __init__(self, message: str, *args: typing.Any, expected_due: VersionString


class Dlt100DeprecationWarning(DltDeprecationWarning):
V100 = semver.parse_version_info("1.0.0")
V100 = semver.Version.parse("1.0.0")

def __init__(self, message: str, *args: typing.Any, expected_due: VersionString = None) -> None:
super().__init__(
Expand Down
8 changes: 3 additions & 5 deletions tests/common/storages/test_versioned_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,13 @@


class MigratedStorage(VersionedStorage):
def migrate_storage(
self, from_version: semver.VersionInfo, to_version: semver.VersionInfo
) -> None:
def migrate_storage(self, from_version: semver.Version, to_version: semver.Version) -> None:
# migration example:
if from_version == "1.0.0" and from_version < to_version:
from_version = semver.VersionInfo.parse("1.1.0")
from_version = semver.Version.parse("1.1.0")
self._save_version(from_version)
if from_version == "1.1.0" and from_version < to_version:
from_version = semver.VersionInfo.parse("1.2.0")
from_version = semver.Version.parse("1.2.0")
self._save_version(from_version)


Expand Down

0 comments on commit 47633c6

Please sign in to comment.