diff --git a/NEWS b/NEWS
index 6249cc1e..9981d239 100644
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,9 @@
use.
* When running tests, use Ruff for faster and more comprehensive code linting and formatting,
replacing Flake8, Black, isort, etc.
+ * Switched from pipx to uv for installing development tools, and added tox-uv for speeding up test
+ environment creation. See the developer documentation for more information:
+ https://torsion.org/borgmatic/docs/how-to/develop-on-borgmatic/
2.0.7
* #1032: Fix a bug in which a Borg archive gets created even when a database hook fails.
diff --git a/docs/how-to/develop-on-borgmatic.md b/docs/how-to/develop-on-borgmatic.md
index 0b8da933..96c794d0 100644
--- a/docs/how-to/develop-on-borgmatic.md
+++ b/docs/how-to/develop-on-borgmatic.md
@@ -8,9 +8,8 @@ eleventyNavigation:
## Source code
To get set up to develop on borgmatic, first [`install
-pipx`](https://torsion.org/borgmatic/docs/how-to/set-up-backups/#installation)
-to make managing your borgmatic environment easier without impacting other
-Python applications on your system.
+uv`](https://docs.astral.sh/uv/) to make managing your borgmatic environment
+easier without impacting other Python applications on your system.
Then, clone borgmatic via HTTPS or SSH:
@@ -31,8 +30,8 @@ changes work:
```bash
cd borgmatic
-pipx ensurepath
-pipx install --editable .
+uv tool ensurepath
+uv tool install --editable .
```
Or to work on the [Apprise
@@ -40,7 +39,7 @@ hook](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#apprise-ho
change that last line to:
```bash
-pipx install --editable .[Apprise]
+uv tool install --editable .[Apprise]
```
To get oriented with the borgmatic source code, have a look at the [source
@@ -50,29 +49,37 @@ code reference](https://torsion.org/borgmatic/docs/reference/source-code/).
## Automated tests
Assuming you've cloned the borgmatic source code as described above and you're
-in the `borgmatic/` working copy, install [tox](https://tox.wiki/), which is
-used for setting up testing environments. You can either install a system
-package of tox (likely called `tox` or `python-tox`) or you can install tox with
-pipx:
+in the `borgmatic/` working copy, install [tox](https://tox.wiki/) and
+[tox-uv](https://github.com/tox-dev/tox-uv) using uv, which are used for setting
+up testing environments:
```bash
-pipx install tox
+uv tool install tox --with tox-uv
```
-Also install [Ruff](https://docs.astral.sh/ruff/), which tox calls for code
+Also install [Ruff](https://docs.astral.sh/ruff/), which borgmatic uses for code
linting and formatting:
```bash
-pipx install ruff
+uv tool install ruff
```
-Finally, to actually run tests, run tox from inside the borgmatic
-source directory:
+Finally, to actually run tests, run tox from inside the borgmatic source
+directory:
```bash
tox
```
+That runs tests against all supported versions of Python, which takes a while.
+So if you'd only like to run tests against a single version of Python, e.g.
+Python 3.13:
+
+```bash
+tox -e py313
+```
+
+
### Code style
If when running tests, you get an error from Ruff's linter about files that
@@ -80,7 +87,7 @@ don't meet linting requirements, you can ask Ruff to attempt to fix them for you
via the following:
```bash
-tox -e lint
+tox -e lint-fix
```
And if you get an error from the Ruff's code formatter about files that would be
@@ -122,7 +129,6 @@ you may need to run with `sudo`.
#### Podman
-New in version 1.7.12
borgmatic's end-to-end tests optionally support using
[rootless](https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md)
[Podman](https://podman.io/) instead of Docker.
@@ -145,8 +151,9 @@ will automatically use your non-root Podman socket instead of a Docker socket.
## Code style
-Start with [PEP 8](https://www.python.org/dev/peps/pep-0008/). But then, apply
-the following deviations from it:
+When writing code for borgmatic, start with [PEP
+8](https://www.python.org/dev/peps/pep-0008/). But then, apply the following
+deviations from it:
* For strings, prefer single quotes over double quotes.
* Limit all lines to a maximum of 100 characters.
@@ -183,6 +190,7 @@ which runs the test suite and updates
from the [commits for the main
branch](https://projects.torsion.org/borgmatic-collective/borgmatic/commits/branch/main).
+
## Documentation development
Updates to borgmatic's documentation are welcome. It's formatted in Markdown
@@ -210,7 +218,6 @@ additional documentation changes to take effect.
#### Podman
-New in version 1.7.12
borgmatic's developer build for documentation optionally supports using
[rootless](https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md)
[Podman](https://podman.io/) instead of Docker.
diff --git a/docs/how-to/set-up-backups.md b/docs/how-to/set-up-backups.md
index 5871bd6b..56d7b7a6 100644
--- a/docs/how-to/set-up-backups.md
+++ b/docs/how-to/set-up-backups.md
@@ -94,6 +94,7 @@ installing borgmatic:
* [NixOS](https://search.nixos.org/packages?show=borgmatic&sort=relevance&type=packages&query=borgmatic)
* [Ansible role](https://github.com/borgbase/ansible-role-borgbackup)
* [Unraid](https://unraid.net/community/apps?q=borgmatic#r)
+ * [uv tool install](https://docs.astral.sh/uv/)
## Hosting providers
diff --git a/scripts/run-full-tests b/scripts/run-full-tests
index 66c26a51..b75afdd0 100755
--- a/scripts/run-full-tests
+++ b/scripts/run-full-tests
@@ -18,7 +18,9 @@ if [ -z "$TEST_CONTAINER" ]; then
fi
apk add --no-cache python3 py3-pip borgbackup postgresql17-client mariadb-client mongodb-tools \
- py3-mongo py3-regex py3-ruamel.yaml py3-ruamel.yaml.clib py3-tox py3-yaml ruff sqlite bash fish
+ py3-mongo py3-regex py3-ruamel.yaml py3-ruamel.yaml.clib py3-tox py3-yaml ruff sqlite uv bash \
+ fish
+uv tool install tox --with tox-uv
export COVERAGE_FILE=/tmp/.coverage
tox --workdir /tmp/.tox --sitepackages
diff --git a/test_requirements.txt b/test_requirements.txt
index 65e03126..539c708c 100644
--- a/test_requirements.txt
+++ b/test_requirements.txt
@@ -2,7 +2,7 @@ apprise==1.9.3
attrs==25.3.0
certifi==2025.7.14
charset-normalizer==3.4.2
-click==8.2.1
+click>=8.1.8
codespell==2.4.1
coverage==7.9.2
flexmock==0.12.2
diff --git a/tests/unit/commands/completion/test_fish.py b/tests/unit/commands/completion/test_fish.py
index 7ac1832e..7c7117a6 100644
--- a/tests/unit/commands/completion/test_fish.py
+++ b/tests/unit/commands/completion/test_fish.py
@@ -7,7 +7,6 @@ from flexmock import flexmock
from borgmatic.commands.completion import fish as module
OptionType = namedtuple('OptionType', ['file', 'choice', 'unknown_required'])
-TestCase = tuple[Action, OptionType]
test_data = [
(Action('--flag', 'flag'), OptionType(file=False, choice=False, unknown_required=False)),
diff --git a/tox.ini b/tox.ini
index 65d24d64..5226e0d8 100644
--- a/tox.ini
+++ b/tox.ini
@@ -5,6 +5,7 @@ package = editable
min_version = 4.0
[testenv]
+runner = uv-venv-runner
deps =
-r test_requirements.txt
allowlist_externals =
@@ -32,7 +33,7 @@ pass_env = COVERAGE_FILE
commands =
pytest {posargs} --no-cov tests/end-to-end
-[testenv:lint]
+[testenv:lint-fix]
deps = []
skip_install = true
commands =