Skip to content

Commit 78060a5

Browse files
committed
Migrate from Poetry to uv
1 parent db741ea commit 78060a5

File tree

8 files changed

+3472
-5150
lines changed

8 files changed

+3472
-5150
lines changed

.github/workflows/constraints-poetry.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

.github/workflows/constraints.txt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,2 @@
1-
pip==25.0.1
1+
bump-my-version==2024.1130
22
nox==2025.2.9
3-
nox-poetry==1.2.0
4-
virtualenv==20.29.2

.github/workflows/release.yml

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,16 @@ jobs:
1616
with:
1717
fetch-depth: 2
1818

19-
- name: Set up Python
20-
uses: actions/setup-python@v5
19+
- name: Install the latest version of uv
20+
uses: astral-sh/setup-uv@v5
2121
with:
22-
python-version: "3.12"
22+
python-version: 3.13
23+
pyproject-file: "${{ github.workspace }}/pyproject.toml"
2324

24-
- name: Upgrade pip
25+
- name: Install bumpversion
2526
run: |
26-
pip install --constraint=.github/workflows/constraints.txt pip
27-
pip --version
28-
29-
- name: Install Poetry
30-
run: |
31-
pip install --constraint=.github/workflows/constraints-poetry.txt poetry
32-
poetry --version
27+
uv tool install --constraint=.github/workflows/constraints.txt bump-my-version
28+
bump-my-version --version
3329
3430
- name: Check if there is a parent commit
3531
id: check-parent-commit
@@ -42,18 +38,18 @@ jobs:
4238
uses: salsify/[email protected]
4339
with:
4440
version-command: |
45-
bash -o pipefail -c "poetry version | awk '{ print \$2 }'"
41+
bump-my-version show current_version
4642
4743
- name: Bump version for developmental release
4844
if: "! steps.check-version.outputs.tag"
4945
run: |
50-
poetry version patch &&
51-
version=$(poetry version | awk '{ print $2 }') &&
52-
poetry version $version.dev.$(date +%s)
46+
bump-my-version bump patch &&
47+
version=$(bump-my-version show current_version) &&
48+
bump-my-version bump --new-version $version.dev$(date +%s)
5349
5450
- name: Build package
5551
run: |
56-
poetry build --ansi
52+
uv build
5753
5854
- name: Publish package on PyPI
5955
if: steps.check-version.outputs.tag

.github/workflows/tests.yml

Lines changed: 11 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -42,30 +42,15 @@ jobs:
4242
with:
4343
python-version: ${{ matrix.python }}
4444

45-
- name: Upgrade pip
46-
run: |
47-
pip install -c ${{ github.workspace }}/.github/workflows/constraints.txt pip
48-
pip --version
49-
50-
- name: Upgrade pip in virtual environments
51-
shell: python
52-
run: |
53-
import os
54-
import pip
55-
56-
with open(os.environ["GITHUB_ENV"], mode="a") as io:
57-
print(f"VIRTUALENV_PIP={pip.__version__}", file=io)
58-
59-
- name: Install Poetry
60-
run: |
61-
pipx install --pip-args="-c ${{ github.workspace }}/.github/workflows/constraints-poetry.txt" poetry
62-
pipx inject poetry poetry-plugin-export
63-
poetry --version
45+
- name: Install the latest version of uv
46+
uses: astral-sh/setup-uv@v5
47+
with:
48+
python-version: ${{ matrix.python }}
49+
pyproject-file: ${{ github.workspace }}/pyproject.toml
6450

6551
- name: Install Nox
6652
run: |
67-
pipx install --pip-args="-c ${{ github.workspace }}/.github/workflows/constraints.txt" nox
68-
pipx inject --pip-args="-c ${{ github.workspace }}/.github/workflows/constraints.txt" nox nox-poetry
53+
uv tool install -c "${{ github.workspace }}/.github/workflows/constraints.txt" nox
6954
nox --version
7055
7156
- name: Compute pre-commit cache key
@@ -118,26 +103,15 @@ jobs:
118103
- name: Check out the repository
119104
uses: actions/checkout@v4
120105

121-
- name: Set up Python
122-
uses: actions/setup-python@v5
106+
- name: Install the latest version of uv
107+
uses: astral-sh/setup-uv@v5
123108
with:
124-
python-version: "3.12"
125-
126-
- name: Upgrade pip
127-
run: |
128-
pip install -c ${{ github.workspace }}/.github/workflows/constraints.txt pip
129-
pip --version
130-
131-
- name: Install Poetry
132-
run: |
133-
pipx install --pip-args="-c ${{ github.workspace }}/.github/workflows/constraints-poetry.txt" poetry
134-
pipx inject poetry poetry-plugin-export
135-
poetry --version
109+
python-version: 3.13
110+
pyproject-file: "${{ github.workspace }}/pyproject.toml"
136111

137112
- name: Install Nox
138113
run: |
139-
pipx install --pip-args="-c ${{ github.workspace }}/.github/workflows/constraints.txt" nox
140-
pipx inject --pip-args="-c ${{ github.workspace }}/.github/workflows/constraints.txt" nox nox-poetry
114+
uv tool install -c "${{ github.workspace }}/.github/workflows/constraints.txt" nox
141115
nox --version
142116
143117
- name: Download coverage data

noxfile.py

Lines changed: 50 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,11 @@
66
import sys
77
from pathlib import Path
88
from textwrap import dedent
9+
from tempfile import NamedTemporaryFile
910

1011
import nox
11-
12-
13-
try:
14-
from nox_poetry import Session
15-
from nox_poetry import session
16-
except ImportError:
17-
message = f"""\
18-
Nox failed to import the 'nox-poetry' package.
19-
20-
Please install it using the following command:
21-
22-
{sys.executable} -m pip install nox-poetry"""
23-
raise SystemExit(dedent(message)) from None
12+
from nox import Session
13+
from nox import session
2414

2515

2616
package = "mdio"
@@ -37,80 +27,6 @@
3727
)
3828

3929

40-
def activate_virtualenv_in_precommit_hooks(session: Session) -> None:
41-
"""Activate virtualenv in hooks installed by pre-commit.
42-
43-
This function patches git hooks installed by pre-commit to activate the
44-
session's virtual environment. This allows pre-commit to locate hooks in
45-
that environment when invoked from git.
46-
47-
Args:
48-
session: The Session object.
49-
"""
50-
assert session.bin is not None # noqa: S101
51-
52-
# Only patch hooks containing a reference to this session's bindir. Support
53-
# quoting rules for Python and bash, but strip the outermost quotes so we
54-
# can detect paths within the bindir, like <bindir>/python.
55-
bindirs = [
56-
bindir[1:-1] if bindir[0] in "'\"" else bindir
57-
for bindir in (repr(session.bin), shlex.quote(session.bin))
58-
]
59-
60-
virtualenv = session.env.get("VIRTUAL_ENV")
61-
if virtualenv is None:
62-
return
63-
64-
headers = {
65-
# pre-commit < 2.16.0
66-
"python": f"""\
67-
import os
68-
os.environ["VIRTUAL_ENV"] = {virtualenv!r}
69-
os.environ["PATH"] = os.pathsep.join((
70-
{session.bin!r},
71-
os.environ.get("PATH", ""),
72-
))
73-
""",
74-
# pre-commit >= 2.16.0
75-
"bash": f"""\
76-
VIRTUAL_ENV={shlex.quote(virtualenv)}
77-
PATH={shlex.quote(session.bin)}"{os.pathsep}$PATH"
78-
""",
79-
# pre-commit >= 2.17.0 on Windows forces sh shebang
80-
"/bin/sh": f"""\
81-
VIRTUAL_ENV={shlex.quote(virtualenv)}
82-
PATH={shlex.quote(session.bin)}"{os.pathsep}$PATH"
83-
""",
84-
}
85-
86-
hookdir = Path(".git") / "hooks"
87-
if not hookdir.is_dir():
88-
return
89-
90-
for hook in hookdir.iterdir():
91-
if hook.name.endswith(".sample") or not hook.is_file():
92-
continue
93-
94-
if not hook.read_bytes().startswith(b"#!"):
95-
continue
96-
97-
text = hook.read_text()
98-
99-
if not any(
100-
Path("A") == Path("a") and bindir.lower() in text.lower() or bindir in text
101-
for bindir in bindirs
102-
):
103-
continue
104-
105-
lines = text.splitlines()
106-
107-
for executable, header in headers.items():
108-
if executable in lines[0].lower():
109-
lines.insert(1, dedent(header))
110-
hook.write_text("\n".join(lines))
111-
break
112-
113-
11430
@session(name="pre-commit", python=python_versions[0])
11531
def precommit(session: Session) -> None:
11632
"""Lint using pre-commit."""
@@ -120,7 +36,8 @@ def precommit(session: Session) -> None:
12036
"--hook-stage=manual",
12137
"--show-diff-on-failure",
12238
]
123-
session.install(
39+
session.run(
40+
"uv", "pip", "install",
12441
"black",
12542
"darglint",
12643
"flake8",
@@ -133,34 +50,45 @@ def precommit(session: Session) -> None:
13350
"pre-commit",
13451
"pre-commit-hooks",
13552
"pyupgrade",
53+
external=True
13654
)
13755
session.run("pre-commit", *args)
138-
if args and args[0] == "install":
139-
activate_virtualenv_in_precommit_hooks(session)
14056

14157

14258
@session(python=python_versions[0])
14359
def safety(session: Session) -> None:
14460
"""Scan dependencies for insecure packages."""
145-
requirements = session.poetry.export_requirements()
146-
session.install("safety")
147-
# TODO(Altay): Remove the CVE ignore once its resolved. Its not critical, so ignoring now.
148-
ignore = ["70612"]
149-
session.run(
150-
"safety",
151-
"check",
152-
"--full-report",
153-
f"--file={requirements}",
154-
f"--ignore={','.join(ignore)}",
155-
)
61+
with NamedTemporaryFile(delete=False) as requirements:
62+
session.run(
63+
"uv", "pip", "compile",
64+
"pyproject.toml",
65+
"--output-file", requirements.name,
66+
external=True
67+
)
68+
session.run(
69+
"uv", "pip", "install", "safety",
70+
external=True
71+
)
72+
# TODO(Altay): Remove the CVE ignore once its resolved. Its not critical, so ignoring now.
73+
ignore = ["70612"]
74+
try:
75+
session.run(
76+
"safety",
77+
"check",
78+
"--full-report",
79+
f"--file={requirements.name}",
80+
f"--ignore={','.join(ignore)}",
81+
)
82+
finally:
83+
os.remove(requirements.name)
15684

15785

15886
@session(python=python_versions)
15987
def mypy(session: Session) -> None:
16088
"""Type-check using mypy."""
16189
args = session.posargs or ["src", "tests", "docs/conf.py"]
162-
session.install(".")
163-
session.install("mypy", "pytest")
90+
session.run_always("uv", "pip", "install", "-e", ".", external=True)
91+
session.run("uv", "pip", "install", "mypy", "pytest", external=True)
16492
session.run("mypy", *args)
16593
if not session.posargs:
16694
session.run("mypy", f"--python-executable={sys.executable}", "noxfile.py")
@@ -169,8 +97,12 @@ def mypy(session: Session) -> None:
16997
@session(python=python_versions)
17098
def tests(session: Session) -> None:
17199
"""Run the test suite."""
172-
session.install(".")
173-
session.install("coverage[toml]", "pytest", "pygments", "pytest-dependency", "s3fs")
100+
session.run_always("uv", "pip", "install", "-e", ".[cloud]", external=True)
101+
session.run(
102+
"uv", "pip", "install",
103+
"coverage[toml]", "pytest", "pygments", "pytest-dependency", "s3fs",
104+
external=True
105+
)
174106
try:
175107
session.run("coverage", "run", "--parallel", "-m", "pytest", *session.posargs)
176108
finally:
@@ -183,7 +115,7 @@ def coverage(session: Session) -> None:
183115
"""Produce the coverage report."""
184116
args = session.posargs or ["report"]
185117

186-
session.install("coverage[toml]")
118+
session.run("uv", "pip", "install", "coverage[toml]", external=True)
187119

188120
if not session.posargs and any(Path().glob(".coverage.*")):
189121
session.run("coverage", "combine")
@@ -194,8 +126,8 @@ def coverage(session: Session) -> None:
194126
@session(python=python_versions[0])
195127
def typeguard(session: Session) -> None:
196128
"""Runtime type checking using Typeguard."""
197-
session.install(".")
198-
session.install("pytest", "typeguard", "pygments")
129+
session.run_always("uv", "pip", "install", "-e", ".", external=True)
130+
session.run("uv", "pip", "install", "pytest", "typeguard", "pygments", external=True)
199131
session.run("pytest", f"--typeguard-packages={package}", *session.posargs)
200132

201133

@@ -209,8 +141,8 @@ def xdoctest(session: Session) -> None:
209141
if "FORCE_COLOR" in os.environ:
210142
args.append("--colored=1")
211143

212-
session.install(".")
213-
session.install("xdoctest[colors]")
144+
session.run_always("uv", "pip", "install", "-e", ".", external=True)
145+
session.run("uv", "pip", "install", "xdoctest[colors]", external=True)
214146
session.run("python", "-m", "xdoctest", *args)
215147

216148

@@ -221,14 +153,16 @@ def docs_build(session: Session) -> None:
221153
if not session.posargs and "FORCE_COLOR" in os.environ:
222154
args.insert(0, "--color")
223155

224-
session.install(".")
225-
session.install(
156+
session.run_always("uv", "pip", "install", "-e", ".", external=True)
157+
session.run(
158+
"uv", "pip", "install",
226159
"sphinx",
227160
"sphinx-click",
228161
"sphinx-copybutton",
229162
"furo",
230163
"myst-nb",
231164
"linkify-it-py",
165+
external=True
232166
)
233167

234168
build_dir = Path("docs", "_build")
@@ -242,15 +176,17 @@ def docs_build(session: Session) -> None:
242176
def docs(session: Session) -> None:
243177
"""Build and serve the documentation with live reloading on file changes."""
244178
args = session.posargs or ["--open-browser", "docs", "docs/_build"]
245-
session.install(".")
246-
session.install(
179+
session.run_always("uv", "pip", "install", "-e", ".", external=True)
180+
session.run(
181+
"uv", "pip", "install",
247182
"sphinx",
248183
"sphinx-autobuild",
249184
"sphinx-click",
250185
"sphinx-copybutton",
251186
"furo",
252187
"myst-nb",
253188
"linkify-it-py",
189+
external=True
254190
)
255191

256192
build_dir = Path("docs", "_build")

0 commit comments

Comments
 (0)