Skip to content

Commit

Permalink
Implemented first version of uv executor
Browse files Browse the repository at this point in the history
  • Loading branch information
AKuederle committed Jan 12, 2025
1 parent 9997e87 commit 9c50131
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 1 deletion.
9 changes: 9 additions & 0 deletions poethepoet/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,15 @@ def is_poetry_project(self) -> bool:
and "poetry" in self._project_config.full_config.get("tool", {})
)

@property
def is_uv_project(self) -> bool:
# Note: That it can happen that a uv managed project has no uv config
# In this case, this check would fail.
return (
self._project_config.path.name == "pyproject.toml"
and "uv" in self._project_config.full_config.get("tool", {})
)

@property
def project_dir(self) -> Path:
return self._project_dir
Expand Down
9 changes: 8 additions & 1 deletion poethepoet/executor/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
from .base import PoeExecutor
from .poetry import PoetryExecutor
from .simple import SimpleExecutor
from .uv import UvExecutor
from .virtualenv import VirtualenvExecutor

__all__ = ["PoeExecutor", "PoetryExecutor", "SimpleExecutor", "VirtualenvExecutor"]
__all__ = [
"PoeExecutor",
"PoetryExecutor",
"SimpleExecutor",
"UvExecutor",
"VirtualenvExecutor",
]
1 change: 1 addition & 0 deletions poethepoet/executor/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ def _resolve_implementation(cls, context: "RunContext", executor_type: str):
if executor_type == "auto":
for impl in [
cls.__executor_types["poetry"],
cls.__executor_types["uv"],
cls.__executor_types["virtualenv"],
]:
if impl.works_with_context(context):
Expand Down
54 changes: 54 additions & 0 deletions poethepoet/executor/uv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from collections.abc import Sequence
from pathlib import Path
from typing import TYPE_CHECKING, Optional

from .base import PoeExecutor

if TYPE_CHECKING:
from ..context import RunContext


class UvExecutor(PoeExecutor):
"""
A poe task executor implementation that executes inside a uv managed dev
environment
"""

__key__ = "uv"
__options__: dict[str, type] = {}

@classmethod
def works_with_context(cls, context: "RunContext") -> bool:
if not context.config.is_uv_project:
return False
return bool(cls._uv_cmd_from_path())

def execute(
self, cmd: Sequence[str], input: Optional[bytes] = None, use_exec: bool = False
) -> int:
"""
Execute the given cmd as a subprocess inside the uv managed dev environment.
We simply use `uv run`, which handles the virtualenv and other setup for us.
"""

# Run this task with `uv run`
return self._execute_cmd(
(self._uv_cmd(), "run", *cmd),
input=input,
use_exec=use_exec,
)

@classmethod
def _uv_cmd(cls):
from_path = cls._uv_cmd_from_path()
if from_path:
return str(Path(from_path).resolve())

return "uv"

@classmethod
def _uv_cmd_from_path(cls):
import shutil

return shutil.which("uv")
21 changes: 21 additions & 0 deletions tests/fixtures/uv_project/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[project]
name = "uv-project"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []

[tool.uv]
# We need to have an empty table here to ensure that the tool is recognized

[tool.poe.tasks]
show-version = "test_print_version"
test-package-version.script = "scripts:test_package_version"
test-package-exec-version.script = "scripts:test_package_exec_version"
show-env = "poe_test_env"


[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
10 changes: 10 additions & 0 deletions tests/fixtures/uv_project/scripts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
def test_package_version():
import poe_test_package

print(poe_test_package.__version__)


def test_package_exec_version():
from subprocess import Popen

Popen(["test_print_version"])
2 changes: 2 additions & 0 deletions tests/fixtures/uv_project/src/uv_project/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def hello() -> str:
return "Hello from uv-project!"

0 comments on commit 9c50131

Please sign in to comment.