Skip to content

Commit

Permalink
feat(python): switch Python spec from poetry to uv
Browse files Browse the repository at this point in the history
  • Loading branch information
ahal committed Sep 5, 2024
1 parent b8b21f8 commit 59c43a7
Show file tree
Hide file tree
Showing 13 changed files with 65 additions and 103 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,19 @@ the REPS definition.

The `reps-new` tool can be used to bootstrap new projects that conform to this
standard. It is recommended to install and run it with
[pipx](https://github.com/pypa/pipx) (so the most up to date version is always
used):
[uvx](https://docs.astral.sh/uv/guides/tools/) (so the most up to date version
is always used). First [install
uv](https://docs.astral.sh/uv/getting-started/installation/), then run:

```bash
pipx run reps-new
uvx reps-new
```

and fill out the prompts. By default, the `python` project template is used.
You may optionally specify a different template to use with the `-t/--template` flag:

```bash
pipx run reps-new -t base
uvx reps-new -t base
```

Available templates can be found in the
Expand Down
11 changes: 5 additions & 6 deletions STANDARD.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,11 +281,11 @@ standards.

### Packaging

Projects should use [Poetry] to manage dependencies, run builds and publish
packages. Running `poetry init` should be sufficient to generate the initial
Projects should use [uv] to manage dependencies, virtualenvs and publish
packages. Running `uv init` should be sufficient to generate the initial
configuration in the top-level `pyproject.toml` file.

[Poetry]: https://python-poetry.org/
[uv]: https://docs.astral.sh/uv/

### Testing

Expand Down Expand Up @@ -467,15 +467,14 @@ type-check:
cwd: '{checkout}'
cache-dotcache: true
command: >-
poetry install --only main --only type &&
poetry run pyright
uv run pyright
```

While it's possible to run as a [pre-commit hook], this method isn't
recommended as Pyright needs to run in an environment where the project's
dependencies are installed. This means either the dependencies need to be
listed a second time in `pre-commit-config.yaml`, or Pyright needs to be
explicitly told about Poetry's virtualenv (which varies from person to person
explicitly told about uv's virtualenv (which varies from person to person
and shouldn't be committed in the config file).

[Pyright]: https://github.com/Microsoft/pyright
Expand Down
37 changes: 22 additions & 15 deletions reps/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,38 +86,45 @@ def merge_pre_commit(items: CookiecutterContext):


@hook("post-gen-py")
def add_poetry_dependencies(items: CookiecutterContext):
def add_uv_dependencies(items: CookiecutterContext):
# Build constraints to ensure we don't try to add versions
# that are incompatible with the minimum Python.
min_python = items["min_python_version"]
constraints = defaultdict(dict)
constraints["coverage"] = {"3.7": "coverage@<7.3.0"}
constraints["tox"] = {"3.7": "tox@<4.9.0"}
constraints["coverage"] = {"3.7": "coverage<7.3.0"}
constraints["tox"] = {"3.7": "tox<4.9.0"}
constraints["sphinx-book-theme"] = {
"3.7": "sphinx-book-theme@<=1.0.1",
"3.8": "sphinx-book-theme@<=1.0.1",
"3.7": "sphinx-book-theme<=1.0.1",
"3.8": "sphinx-book-theme<=1.0.1",
}
constraints["sphinx-autobuild"] = {
"3.7": "sphinx-autobuild@<=2021.3.14",
"3.8": "sphinx-autobuild@<=2021.3.14",
"3.7": "sphinx-autobuild<=2021.3.14",
"3.8": "sphinx-autobuild<=2021.3.14",
}

def build_specifiers(*packages: str) -> Generator[str, None, None]:
for p in packages:
yield constraints[p].get(min_python, p)

run(
["poetry", "add", "--group=test"]
# Until the pyproject.toml spec and/or uv supports dependency groups, we
# need to add these all to "dev-dependencies"
# See: https://github.com/astral-sh/uv/issues/5632
["uv", "add", "--dev"]
+ list(
build_specifiers("coverage", "pytest", "pytest-mock", "responses", "tox")
build_specifiers(
"coverage",
"pyright",
"pytest",
"pytest-mock",
"responses",
"sphinx<7",
"sphinx-autobuild",
"sphinx-book-theme",
"tox",
)
)
)
run(
["poetry", "add", "--group=docs"]
+ list(build_specifiers("sphinx<7", "sphinx-autobuild", "sphinx-book-theme"))
)

run(["poetry", "add", "--group=type"] + list(build_specifiers("pyright")))


@hook("post-gen-py")
Expand Down
3 changes: 2 additions & 1 deletion reps/templates/python/cookiecutter.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"__project_slug": "{{cookiecutter.project_name|lower|replace(' ', '-')}}",
"__package_name": "{{cookiecutter.project_name|lower|replace(' ', '_')|replace('-', '_')}}",
"short_description": "",
"author": "Mozilla Release Engineering <[email protected]>",
"author_name": "Mozilla Release Engineering",
"author_email": "[email protected]",
"github_slug": "mozilla-releng/{{cookiecutter.__project_slug}}",
"__github_org": "{{cookiecutter.github_slug[:cookiecutter.github_slug.find('/')]}}",
"__github_project": "{{cookiecutter.github_slug[cookiecutter.github_slug.find('/')+1:]}}",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
[tool.poetry]
[project]
name = "{{cookiecutter.__project_slug}}"
version = "0.1.0"
description = "{{cookiecutter.short_description}}"
authors = ["{{cookiecutter.author}}"]
license = "MPL-2.0"
requires-python = ">={{cookiecutter.min_python_version}}"
authors = [
{ name = "{{cookiecutter.author_name}}", email = "{{cookiecutter.author_email}}" }
]
classifiers = [
"License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)",
]
readme = "README.md"

[tool.poetry.dependencies]
python = "^{{cookiecutter.min_python_version}}"

[tool.pytest.ini_options]
xfail_strict = true

Expand Down Expand Up @@ -42,5 +44,5 @@ include = ["src/{{cookiecutter.__package_name}}"]
reportUnknownParameterType = "error"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
requires = ["hatchling"]
build-backend = "hatchling.build"
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,10 @@ ENV SHELL=/bin/bash \
VOLUME /builds/worker/checkouts
VOLUME /builds/worker/.cache

# pyenv
# %ARG PYENV_VERSIONS
ENV PYENV_ROOT=/builds/worker/.pyenv \
PATH=/builds/worker/.pyenv/bin:/builds/worker/.pyenv/shims:$PATH
# %include taskcluster/scripts/pyenv-setup
ADD topsrcdir/taskcluster/scripts/pyenv-setup /builds/worker/pyenv-setup
RUN /builds/worker/pyenv-setup "$PYENV_VERSIONS"

# %include taskcluster/scripts/poetry-setup
ADD topsrcdir/taskcluster/scripts/poetry-setup /builds/worker/poetry-setup
RUN /builds/worker/poetry-setup
# uv
COPY --from=ghcr.io/astral-sh/uv:0.3.5 /uv /bin/uv
# %ARG PYTHON_VERSIONS
RUN uv python install $PYTHON_VERSIONS

RUN chown -R worker:worker /builds/worker

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,4 @@ tasks:
using: run-task
cwd: '{checkout}'
command: >-
poetry install --only test &&
poetry run python taskcluster/scripts/codecov-upload.py
uv run python taskcluster/scripts/codecov-upload.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{%- set pylist -%}
{%- for i in range(cookiecutter.__max_tox_python_version[1:]|int, cookiecutter.__min_tox_python_version[1:]|int - 1, -1) -%}
3.{{i}}
{%- if not loop.last %},{% endif -%}
{%- if not loop.last %} {% endif -%}
{%- endfor -%}
{%- endset -%}
---
Expand All @@ -16,4 +16,4 @@ tasks:
fetch: {}
python:
args:
PYENV_VERSIONS: "{{pylist}}"
PYTHON_VERSIONS: "{{pylist}}"
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,12 @@ tasks:
TOX_PARALLEL_NO_SPINNER: "1"
run:
command: >-
poetry install --only test &&
poetry run tox --parallel
uv run tox --parallel
type-check:
description: "Run pyright type checking against code base"
worker:
max-run-time: 300
run:
command: >-
poetry install --only main --only type &&
poetry run pyright
uv run pyright

This file was deleted.

This file was deleted.

19 changes: 8 additions & 11 deletions reps/templates/python/{{cookiecutter.__project_slug}}/tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,24 @@
envlist = clean,{{pylist}},report

[testenv]
allowlist_externals = poetry
allowlist_externals = uv
parallel_show_output = true
depends =
{{pylist}}: clean
report: {{pylist}}
commands =
poetry install --with test
poetry run python --version
poetry run coverage run --context={envname} -p -m pytest -vv {posargs}
uv run python --version
uv run coverage run --context={envname} -p -m pytest -vv {posargs}

[testenv:report]
allowlist_externals = poetry
allowlist_externals = uv
passenv = COVERAGE_REPORT_COMMAND
parallel_show_output = true
commands =
poetry install --only test
poetry run coverage combine
poetry run {env:COVERAGE_REPORT_COMMAND:coverage report}
uv run coverage combine
uv run {env:COVERAGE_REPORT_COMMAND:coverage report}

[testenv:clean]
allowlist_externals = poetry
allowlist_externals = uv
commands =
poetry install --only test
poetry run coverage erase
uv run coverage erase
9 changes: 4 additions & 5 deletions test/test_template_python.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ def test_generated_files(reps_new):
"docs/reference/index.rst",
"docs/tutorials/index.rst",
"pyproject.toml",
"poetry.lock",
f"src/{name}/__init__.py",
"taskcluster/config.yml",
"taskcluster/docker/fetch/Dockerfile",
Expand All @@ -36,17 +35,16 @@ def test_generated_files(reps_new):
"taskcluster/kinds/fetch/kind.yml",
"taskcluster/kinds/test/kind.yml",
"taskcluster/scripts/codecov-upload.py",
"taskcluster/scripts/pyenv-setup",
"taskcluster/scripts/poetry-setup",
"test/conftest.py",
f"test/test_{name}.py",
"tox.ini",
"uv.lock",
]

project = reps_new(name, "python")

actual = []
ignore = ("__pycache__", ".git/", ".pyc")
ignore = ("__pycache__", ".git/", ".pyc", ".venv")
for path in project.rglob("*"):
if path.is_dir() or any(i in str(path) for i in ignore):
continue
Expand All @@ -64,7 +62,8 @@ def test_generated_files(reps_new):
"__project_slug": "my-package",
"__package_name": "my_package",
"short_description": "",
"author": "Mozilla Release Engineering <[email protected]>",
"author_name": "Mozilla Release Engineering",
"author_email": "[email protected]",
"github_slug": "mozilla-releng/my-package",
"min_python_version": "3.8",
"__min_tox_python_version": "38",
Expand Down

0 comments on commit 59c43a7

Please sign in to comment.