Skip to content

Commit

Permalink
Add PyPy support (#51)
Browse files Browse the repository at this point in the history
  • Loading branch information
gaborbernat authored Apr 29, 2024
1 parent ca592d6 commit 89f9f76
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 33 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ jobs:
- ubuntu-latest
- windows-latest
- macos-latest
include:
- { os: ubuntu-latest, py: "pypy3.10" }
- { os: ubuntu-latest, py: "pypy3.8" }
- { os: windows-latest, py: "pypy3.10" }
- { os: windows-latest, py: "pypy3.8" }
steps:
- name: setup python for tox
uses: actions/setup-python@v5
Expand Down
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ repos:
rev: "1.8.0"
hooks:
- id: pyproject-fmt
additional_dependencies: ["tox>=4.14"]
additional_dependencies: ["tox>=4.15"]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: "v0.4.1"
rev: "v0.4.2"
hooks:
- id: ruff-format
- id: ruff
Expand Down
64 changes: 49 additions & 15 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
build-backend = "hatchling.build"
requires = [
"hatch-vcs>=0.4",
"hatchling>=1.21.1",
"hatchling>=1.24.2",
]

[project]
Expand All @@ -16,7 +16,9 @@ keywords = [
"virtual",
]
license = "MIT"
maintainers = [{ name = "Bernát Gábor", email = "gaborjbernat@gmail.com" }]
maintainers = [
{ name = "Bernát Gábor", email = "gaborjbernat@gmail.com" },
]
requires-python = ">=3.8"
classifiers = [
"Development Status :: 5 - Production/Stable",
Expand All @@ -38,17 +40,19 @@ dynamic = [
"version",
]
dependencies = [
"packaging>=23.2",
"tox<5,>=4.14",
"uv<1,>=0.1.15",
'importlib_resources>=6.4; python_version < "3.9"',
"packaging>=24",
"tox<5,>=4.15",
"uv<1,>=0.1.39",
]
optional-dependencies.test = [
"covdefaults>=2.3",
"devpi-process>=1",
"pytest>=8.0.2",
"pytest-cov>=4.1",
"pytest-mock>=3.12",
"pytest>=8.2",
"pytest-cov>=5",
"pytest-mock>=3.14",
]
urls.Changelog = "https://github.com/tox-dev/tox-uv/releases"
urls.Documentation = "https://github.com/tox-dev/tox-uv#tox-uv"
urls.Homepage = "https://github.com/tox-dev/tox-uv"
urls.Source = "https://github.com/tox-dev/tox-uv"
Expand All @@ -57,7 +61,10 @@ entry-points.tox = {"tox-uv" = "tox_uv.plugin"}

[tool.hatch]
build.hooks.vcs.version-file = "src/tox_uv/version.py"
build.targets.sdist.include = ["/src", "/tests"]
build.targets.sdist.include = [
"/src",
"/tests",
]
version.source = "vcs"

[tool.black]
Expand All @@ -66,8 +73,15 @@ line-length = 120
[tool.ruff]
line-length = 120
target-version = "py38"
lint.isort = { known-first-party = ["tox_uv", "tests"], required-imports = ["from __future__ import annotations"] }
lint.select = ["ALL"]
lint.isort = { known-first-party = [
"tox_uv",
"tests",
], required-imports = [
"from __future__ import annotations",
] }
lint.select = [
"ALL",
]
lint.ignore = [
"ANN101", # Missing type annotation for `self` in method
"D301", # Use `r"""` if any backslashes in a docstring
Expand Down Expand Up @@ -103,14 +117,34 @@ count = true
[tool.coverage]
html.show_contexts = true
html.skip_covered = false
paths.source = ["src", ".tox/*/.venv/lib/*/site-packages", ".tox\\*\\.venv\\Lib\\site-packages", "**/src", "**\\src"]
paths.other = [".", "*/tox_uv", "*\\tox_uv"]
paths.source = [
"src",
".tox/*/.venv/lib/*/site-packages",
".tox\\*\\.venv\\Lib\\site-packages",
"**/src",
"**\\src",
]
paths.other = [
".",
"*/tox_uv",
"*\\tox_uv",
]
report.omit = [
"src/tox_uv/_venv_query.py",
]
report.fail_under = 100
run.parallel = true
run.plugins = ["covdefaults"]
run.plugins = [
"covdefaults",
]

[tool.mypy]
python_version = "3.11"
show_error_codes = true
strict = true
overrides = [{ module = ["virtualenv.*", "uv.*"], ignore_missing_imports = true }]
overrides = [
{ module = [
"virtualenv.*",
"uv.*",
], ignore_missing_imports = true },
]
56 changes: 45 additions & 11 deletions src/tox_uv/_venv.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,17 @@

from __future__ import annotations

import json
import sys
from abc import ABC
from functools import cached_property

if sys.version_info >= (3, 9): # pragma: no cover (py39+)
from importlib.resources import as_file, files
else: # pragma: no cover (py38+)
from importlib_resources import as_file, files


from pathlib import Path
from platform import python_implementation
from typing import TYPE_CHECKING, Any, cast
Expand All @@ -26,6 +35,7 @@ class UvVenv(Python, ABC):
def __init__(self, create_args: ToxEnvCreateArgs) -> None:
self._executor: Execute | None = None
self._installer: UvInstaller | None = None
self._created = False
super().__init__(create_args)

def register_config(self) -> None:
Expand Down Expand Up @@ -112,14 +122,12 @@ def _default_pass_env(self) -> list[str]:
return env

def create_python_env(self) -> None:
base = self.base_python.version_info
version_spec = (
sys.executable
if (base.major, base.minor) == sys.version_info[:2]
else f"{base.major}.{base.minor}"
if base.minor
else f"{base.major}"
)
base, imp = self.base_python.version_info, self.base_python.impl_lower
if (base.major, base.minor) == sys.version_info[:2] and (sys.implementation.name.lower() == imp):
version_spec = sys.executable
else:
uv_imp = "python" if (imp and imp == "cpython") else imp
version_spec = f"{uv_imp or ''}{base.major}.{base.minor}" if base.minor else f"{uv_imp or ''}{base.major}"
cmd: list[str] = [self.uv, "venv", "-p", version_spec]
if self.options.verbosity > 2: # noqa: PLR2004
cmd.append("-v")
Expand All @@ -128,6 +136,7 @@ def create_python_env(self) -> None:
cmd.append(str(self.venv_dir))
outcome = self.execute(cmd, stdin=StdinSource.OFF, run_id="venv", show=None)
outcome.assert_success()
self._created = True

@property
def _allow_externals(self) -> list[str]:
Expand All @@ -152,9 +161,34 @@ def env_site_package_dir(self) -> Path:
if sys.platform == "win32": # pragma: win32 cover
return self.venv_dir / "Lib" / "site-packages"
else: # pragma: win32 no cover # noqa: RET505
assert self.base_python.version_info.major is not None # noqa: S101
assert self.base_python.version_info.minor is not None # noqa: S101
return self.venv_dir / "lib" / f"python{self.base_python.version_dot}" / "site-packages"
py = self._py_info
impl = "pypy" if py.implementation == "pypy" else "python"
return self.venv_dir / "lib" / f"{impl}{py.version_dot}" / "site-packages"

@cached_property
def _py_info(self) -> PythonInfo: # pragma: win32 no cover
if not (self._created or self.env_dir.exists()): # called during config, no environment setup
self.create_python_env()
self._paths = self.prepend_env_var_path()
with as_file(files("tox_uv") / "_venv_query.py") as filename:
cmd = [str(self.env_python()), str(filename)]
outcome = self.execute(cmd, stdin=StdinSource.OFF, run_id="venv-query", show=False)
outcome.assert_success()
res = json.loads(outcome.out)
return PythonInfo(
implementation=res["implementation"],
version_info=VersionInfo(
major=res["version_info"][0],
minor=res["version_info"][1],
micro=res["version_info"][2],
releaselevel=res["version_info"][3],
serial=res["version_info"][4],
),
version=res["version"],
is_64=res["is_64"],
platform=sys.platform,
extra={},
)


__all__ = [
Expand Down
14 changes: 14 additions & 0 deletions src/tox_uv/_venv_query.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from __future__ import annotations

import json
import sys
from platform import python_implementation

print( # noqa: T201
json.dumps({
"implementation": python_implementation().lower(),
"version_info": sys.version_info,
"version": sys.version,
"is_64": sys.maxsize > 2**32,
})
)
20 changes: 18 additions & 2 deletions tests/test_tox_uv_venv.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def test_uv_env_python(tox_project: ToxProjectCreator) -> None:
assert env_bin_dir in result.out


def test_uv_env_site_package_dir(tox_project: ToxProjectCreator) -> None:
def test_uv_env_site_package_dir_run(tox_project: ToxProjectCreator) -> None:
project = tox_project({"tox.ini": "[testenv]\npackage=skip\ncommands=python -c 'print(\"{envsitepackagesdir}\")'"})
result = project.run("-vv")
result.assert_success()
Expand All @@ -89,7 +89,23 @@ def test_uv_env_site_package_dir(tox_project: ToxProjectCreator) -> None:
if sys.platform == "win32": # pragma: win32 cover
path = str(env_dir / "Lib" / "site-packages")
else: # pragma: win32 no cover
path = str(env_dir / "lib" / f"python{ver.major}.{ver.minor}" / "site-packages")
impl = "pypy" if sys.implementation.name.lower() == "pypy" else "python"
path = str(env_dir / "lib" / f"{impl}{ver.major}.{ver.minor}" / "site-packages")
assert path in result.out


def test_uv_env_site_package_dir_conf(tox_project: ToxProjectCreator) -> None:
project = tox_project({"tox.ini": "[testenv]\npackage=skip\ncommands={envsitepackagesdir}"})
result = project.run("c", "-e", "py", "-k", "commands")
result.assert_success()

env_dir = project.path / ".tox" / "py" / ".venv"
ver = sys.version_info
if sys.platform == "win32": # pragma: win32 cover
path = str(env_dir / "Lib" / "site-packages")
else: # pragma: win32 no cover
impl = "pypy" if sys.implementation.name.lower() == "pypy" else "python"
path = str(env_dir / "lib" / f"{impl}{ver.major}.{ver.minor}" / "site-packages")
assert path in result.out


Expand Down
6 changes: 3 additions & 3 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ commands =
description = run static analysis and style check using flake8
skip_install = true
deps =
pre-commit>=3.6.2
pre-commit>=3.7
pass_env =
HOMEPATH
PROGRAMDATA
Expand All @@ -42,7 +42,7 @@ commands =
[testenv:type]
description = run type check on code base
deps =
mypy==1.8
mypy==1.10
set_env =
{tty:MYPY_FORCE_COLOR = 1}
commands =
Expand All @@ -53,7 +53,7 @@ commands =
description = check that the package metadata is correct
skip_install = true
deps =
build[virtualenv]>=1.1.1
build[virtualenv]>=1.2.1
twine>=5
set_env =
{tty:FORCE_COLOR = 1}
Expand Down

0 comments on commit 89f9f76

Please sign in to comment.