Skip to content

Commit f2bf3a6

Browse files
authored
Use uv more. (#680)
1 parent 2a67454 commit f2bf3a6

14 files changed

Lines changed: 5038 additions & 149 deletions

.github/workflows/main.yml

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,11 @@ jobs:
2222

2323
steps:
2424
- uses: actions/checkout@v4
25-
- uses: actions/setup-python@v5
25+
- uses: astral-sh/setup-uv@v5
2626
with:
27-
python-version-file: .python-version
28-
allow-prereleases: true
29-
cache: pip
30-
- run: pip install tox-uv
31-
- run: tox -e typing
27+
enable-cache: true
28+
- run: uv run --no-dev --group typing mypy
29+
- run: uv run --no-dev --group typing nbqa mypy --ignore-missing-imports .
3230

3331
run-tests:
3432

@@ -43,12 +41,10 @@ jobs:
4341

4442
steps:
4543
- uses: actions/checkout@v4
46-
- uses: actions/setup-python@v5
44+
- uses: astral-sh/setup-uv@v5
4745
with:
4846
python-version: ${{ matrix.python-version }}
49-
cache: pip
50-
allow-prereleases: true
51-
- run: pip install tox-uv
47+
enable-cache: true
5248

5349
- if: matrix.os == 'ubuntu-latest'
5450
run: |
@@ -59,7 +55,7 @@ jobs:
5955

6056
- name: Run unit tests and doctests.
6157
shell: bash -l {0}
62-
run: tox -e test -- -m "unit or (not integration and not end_to_end)" --cov=src --cov=tests --cov-report=xml -n auto
58+
run: uv run --group test pytest --nbmake -m "unit or (not integration and not end_to_end)" --cov=src --cov=tests --cov-report=xml -n auto
6359

6460
- name: Upload unit test coverage reports to Codecov with GitHub Action
6561
uses: codecov/codecov-action@v5
@@ -68,7 +64,7 @@ jobs:
6864

6965
- name: Run integration tests.
7066
shell: bash -l {0}
71-
run: tox -e test -- -m integration --cov=src --cov=tests --cov-report=xml -n auto
67+
run: uv run --group test pytest --nbmake -m integration --cov=src --cov=tests --cov-report=xml -n auto
7268

7369
- name: Upload integration test coverage reports to Codecov with GitHub Action
7470
uses: codecov/codecov-action@v5
@@ -77,7 +73,7 @@ jobs:
7773

7874
- name: Run end-to-end tests.
7975
shell: bash -l {0}
80-
run: tox -e test -- -m end_to_end --cov=src --cov=tests --cov-report=xml -n auto
76+
run: uv run --group test pytest --nbmake -m end_to_end --cov=src --cov=tests --cov-report=xml -n auto
8177

8278
- name: Upload end_to_end test coverage reports to Codecov with GitHub Action
8379
uses: codecov/codecov-action@v5

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,3 @@ tests/test_jupyter/*.txt
2727
.ruff_cache
2828
.venv
2929
docs/jupyter_execute
30-
uv.lock

.readthedocs.yaml

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
version: 2
22

33
build:
4-
os: ubuntu-22.04
4+
os: ubuntu-24.04
55
tools:
66
python: "3.12"
7+
jobs:
8+
create_environment:
9+
- asdf plugin add uv
10+
- asdf install uv latest
11+
- asdf global uv latest
12+
- UV_PROJECT_ENVIRONMENT=$READTHEDOCS_VIRTUALENV_PATH uv sync --group docs
13+
install:
14+
- "true"
715

816
sphinx:
917
configuration: docs/source/conf.py
1018
fail_on_warning: true
11-
12-
python:
13-
install:
14-
- method: pip
15-
path: .
16-
extra_requirements:
17-
- docs

pyproject.toml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,11 @@ text = "MIT"
4646
name = "Tobias Raabe"
4747
email = "raabe@posteo.de"
4848

49-
[project.optional-dependencies]
49+
[dependency-groups]
50+
dev = ["pygraphviz>=1.11;platform_system=='Linux'"]
5051
docs = [
5152
"furo",
52-
"ipython",
53+
"ipython>=8.13.2",
5354
"matplotlib",
5455
"myst-parser",
5556
"myst-nb",
@@ -85,9 +86,6 @@ Tracker = "https://github.com/pytask-dev/pytask/issues"
8586
[project.scripts]
8687
pytask = "pytask:cli"
8788

88-
[tool.uv]
89-
dev-dependencies = ["tox-uv>=1.7.0", "pygraphviz;platform_system=='Linux'"]
90-
9189
[build-system]
9290
requires = ["hatchling", "hatch_vcs"]
9391
build-backend = "hatchling.build"

src/_pytask/build.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ def build( # noqa: C901, PLR0912, PLR0913
275275
session.exit_code = ExitCode.FAILED
276276

277277
except Exception: # noqa: BLE001
278-
console.print(Traceback(sys.exc_info()))
278+
console.print(Traceback(sys.exc_info(), show_locals=True))
279279
session.exit_code = ExitCode.FAILED
280280

281281
session.hook.pytask_unconfigure(session=session)

tests/conftest.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,20 @@ class Result(NamedTuple):
123123

124124
def run_in_subprocess(cmd: tuple[str, ...], cwd: Path | None = None) -> Result:
125125
"""Run a command in a subprocess and return the output."""
126-
result = subprocess.run(cmd, cwd=cwd, check=False, capture_output=True)
126+
kwargs = (
127+
{
128+
"env": os.environ | {"PYTHONIOENCODING": "utf-8", "TERM": "unknown"},
129+
"encoding": "utf-8",
130+
}
131+
if sys.platform == "win32"
132+
else {}
133+
)
134+
135+
result = subprocess.run(
136+
cmd, cwd=cwd, check=False, capture_output=True, text=True, **kwargs
137+
)
127138
return Result(
128-
exit_code=result.returncode,
129-
stdout=result.stdout.decode("utf-8", "replace").replace("\r\n", "\n"),
130-
stderr=result.stderr.decode("utf-8", "replace").replace("\r\n", "\n"),
139+
exit_code=result.returncode, stdout=result.stdout, stderr=result.stderr
131140
)
132141

133142

tests/test_capture.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,11 @@ def test_many(self, capfd): # noqa: ARG002
769769

770770
@pytest.mark.unit
771771
class TestStdCaptureFDinvalidFD:
772+
@pytest.mark.skipif(
773+
sys.platform == "darwin" and sys.version_info[:2] == (3, 9),
774+
reason="Causes following tests to fail and kills the pytest session with exit "
775+
"code 137.",
776+
)
772777
def test_stdcapture_fd_invalid_fd(self, tmp_path, runner):
773778
source = """
774779
import os

tests/test_clean.py

Lines changed: 20 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,31 +8,20 @@
88
from _pytask.git import init_repo
99
from pytask import ExitCode
1010
from pytask import cli
11-
from pytask import storage
1211
from tests.conftest import enter_directory
1312

14-
_PROJECT_TASK = """
15-
import pytask
16-
from pathlib import Path
1713

18-
def task_write_text(path = Path("in.txt"), produces = Path("out.txt")):
19-
produces.write_text("a")
20-
"""
21-
22-
23-
_PROJECT_TASK_NEW_INTERFACE = """
24-
import pytask
25-
from pathlib import Path
26-
27-
def task_write_text(path=Path("in.txt"), produces=Path("out.txt")):
28-
produces.write_text("a")
29-
"""
30-
31-
32-
@pytest.fixture(params=[_PROJECT_TASK, _PROJECT_TASK_NEW_INTERFACE])
33-
def project(request, tmp_path):
14+
@pytest.fixture
15+
def project(tmp_path):
3416
"""Create a sample project to be cleaned."""
35-
tmp_path.joinpath("task_module.py").write_text(textwrap.dedent(request.param))
17+
content = """
18+
import pytask
19+
from pathlib import Path
20+
21+
def task_write_text(path=Path("in.txt"), produces=Path("out.txt")):
22+
produces.write_text("a")
23+
"""
24+
tmp_path.joinpath("task_module.py").write_text(textwrap.dedent(content))
3625
tmp_path.joinpath("in.txt").touch()
3726

3827
tmp_path.joinpath("to_be_deleted_file_1.txt").touch()
@@ -42,28 +31,17 @@ def project(request, tmp_path):
4231
return tmp_path
4332

4433

45-
_GIT_PROJECT_TASK = """
46-
import pytask
47-
from pathlib import Path
48-
49-
def task_write_text(path = Path("in_tracked.txt"), produces = Path("out.txt")):
50-
produces.write_text("a")
51-
"""
52-
53-
54-
_GIT_PROJECT_TASK_NEW_INTERFACE = """
55-
import pytask
56-
from pathlib import Path
57-
58-
def task_write_text(path=Path("in_tracked.txt"), produces=Path("out.txt")):
59-
produces.write_text("a")
60-
"""
61-
62-
63-
@pytest.fixture(params=[_GIT_PROJECT_TASK, _GIT_PROJECT_TASK_NEW_INTERFACE])
64-
def git_project(request, tmp_path):
34+
@pytest.fixture
35+
def git_project(tmp_path):
6536
"""Create a sample project to be cleaned."""
66-
tmp_path.joinpath("task_module.py").write_text(textwrap.dedent(request.param))
37+
content = """
38+
import pytask
39+
from pathlib import Path
40+
41+
def task_write_text(path=Path("in_tracked.txt"), produces=Path("out.txt")):
42+
produces.write_text("a")
43+
"""
44+
tmp_path.joinpath("task_module.py").write_text(textwrap.dedent(content))
6745
tmp_path.joinpath("in_tracked.txt").touch()
6846
tmp_path.joinpath("tracked.txt").touch()
6947

@@ -83,11 +61,9 @@ def test_clean_database_ignored(project, runner):
8361
with enter_directory(project):
8462
result = runner.invoke(cli, ["build"])
8563
assert result.exit_code == ExitCode.OK
86-
storage.create()
8764
result = runner.invoke(cli, ["clean"])
8865
assert result.exit_code == ExitCode.OK
8966

90-
assert result.exit_code == ExitCode.OK
9167
text_without_linebreaks = result.output.replace("\n", "")
9268
assert "to_be_deleted_file_1.txt" in text_without_linebreaks
9369
assert "to_be_deleted_file_2.txt" in text_without_linebreaks

tests/test_config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ def test_paths_are_relative_to_configuration_file(tmp_path):
120120
session = build(paths=[Path("src")])
121121
"""
122122
tmp_path.joinpath("script.py").write_text(textwrap.dedent(source))
123-
result = run_in_subprocess(("python", "script.py"), cwd=tmp_path)
123+
result = run_in_subprocess(("uv", "run", "python", "script.py"), cwd=tmp_path)
124124
assert result.exit_code == ExitCode.OK
125125
assert "1 Succeeded" in result.stdout
126126

tests/test_execute.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,16 @@
2020
from pytask import build
2121
from pytask import cli
2222
from tests.conftest import enter_directory
23+
from tests.conftest import run_in_subprocess
2324

2425

25-
@pytest.mark.xfail(sys.platform == "win32", reason="See #293.")
2626
@pytest.mark.end_to_end
2727
def test_python_m_pytask(tmp_path):
2828
tmp_path.joinpath("task_module.py").write_text("def task_example(): pass")
29-
subprocess.run(["python", "-m", "pytask", tmp_path.as_posix()], check=False)
29+
result = run_in_subprocess(
30+
("uv", "run", "python", "-m", "pytask", tmp_path.as_posix())
31+
)
32+
assert result.exit_code == ExitCode.OK
3033

3134

3235
@pytest.mark.end_to_end
@@ -657,10 +660,10 @@ def task2() -> None: pass
657660
sys.exit(session2.exit_code)
658661
"""
659662
tmp_path.joinpath("task_module.py").write_text(textwrap.dedent(source))
660-
result = subprocess.run(
661-
("python", tmp_path.joinpath("task_module.py").as_posix()), check=False
663+
result = run_in_subprocess(
664+
("uv", "run", "python", tmp_path.joinpath("task_module.py").as_posix())
662665
)
663-
assert result.returncode == ExitCode.OK
666+
assert result.exit_code == ExitCode.OK
664667

665668

666669
@pytest.mark.end_to_end
@@ -765,14 +768,14 @@ def task_example() -> Annotated[str, Path("file.txt")]:
765768
"""
766769
tmp_path.joinpath("task_example.py").write_text(textwrap.dedent(source))
767770

768-
result = subprocess.run(("pytask"), cwd=tmp_path) # noqa: PLW1510
769-
assert result.returncode == ExitCode.OK
771+
result = run_in_subprocess(("pytask",), cwd=tmp_path)
772+
assert result.exit_code == ExitCode.OK
770773

771774
hashes = json.loads(tmp_path.joinpath(".pytask", "file_hashes.json").read_text())
772775
assert len(hashes) == 2
773776

774-
result = subprocess.run(("pytask"), cwd=tmp_path) # noqa: PLW1510
775-
assert result.returncode == ExitCode.OK
777+
result = run_in_subprocess(("pytask",), cwd=tmp_path)
778+
assert result.exit_code == ExitCode.OK
776779

777780
hashes_ = json.loads(tmp_path.joinpath(".pytask", "file_hashes.json").read_text())
778781
assert hashes == hashes_

0 commit comments

Comments
 (0)