UV Usage part 2
This post follows on from UV Usage written a few days ago, where I wrote about about moving to using UV and the commonly-used commands: run, add, remove. Here I’ll cover adding it to an existing project, which is what I did. Creating one from scratch is pretty well covered in the documentation.
In the case of zpdatafetch, I wasn’t starting clean. I had an existing pyproject.toml. I decided to start just adding things to see how it worked.
Since the venv was not set up in the way that uv
would build it, I simply removed it entirely to start clean. Then I tried uv init
. This didn’t work:
error: Project is already initialized in `/Users/doug/Development/Zwift/zpd/zpdatafetch`
So, I decided to just add the dependencies and see what happened. This also failed, but seemed to be moving in the right direction:
$ uv add beautifulsoup4 httpx lxml keyring keyrings-alt
warning: No `requires-python` value found in the workspace. Defaulting to `>=3.12`.
error: Failed to build: `zpdatafetch @ file:///Users/doug/Development/Zwift/zpd/zpdatafetch`
Caused by: Build backend failed to determine extra requires with `build_editable()` with exit status: 1
--- stdout:
configuration error: You cannot provide a value for `project.dependencies` and list it under `project.dynamic` at the same time
DESCRIPTION:
According to PEP 621: Build back-ends MUST raise an error if the metadata
specifies a field statically as well as being listed in dynamic.
GIVEN VALUE:
{
"dependencies": [
"beautifulsoup4",
"httpx",
"lxml",
"keyring"
],
"...": " # ...",
"dynamic": [
"version",
"authors",
"description",
"readme",
"license",
"urls",
"dependencies"
]
}
OFFENDING RULE: 'PEP 621'
DEFINITION:
{
"see": "https://packaging.python.org/en/latest/specifications/pyproject-toml/#dynamic"
}
--- stderr:
Traceback (most recent call last):
File "<string>", line 14, in <module>
File "/Users/doug/Library/Caches/uv/builds-v0/.tmpnGXgAH/lib/python3.12/site-packages/setuptools/build_meta.py", line 464, in get_requires_for_build_editable
return self.get_requires_for_build_wheel(config_settings)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/doug/Library/Caches/uv/builds-v0/.tmpnGXgAH/lib/python3.12/site-packages/setuptools/build_meta.py", line 332, in get_requires_for_build_wheel
return self._get_build_requires(config_settings, requirements=[])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/doug/Library/Caches/uv/builds-v0/.tmpnGXgAH/lib/python3.12/site-packages/setuptools/build_meta.py", line 302, in _get_build_requires
self.run_setup()
File "/Users/doug/Library/Caches/uv/builds-v0/.tmpnGXgAH/lib/python3.12/site-packages/setuptools/build_meta.py", line 318, in run_setup
exec(code, locals())
File "<string>", line 1, in <module>
File "/Users/doug/Library/Caches/uv/builds-v0/.tmpnGXgAH/lib/python3.12/site-packages/setuptools/__init__.py", line 117, in setup
return distutils.core.setup(**attrs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/doug/Library/Caches/uv/builds-v0/.tmpnGXgAH/lib/python3.12/site-packages/setuptools/_distutils/core.py", line 158, in setup
dist.parse_config_files()
File "/Users/doug/Library/Caches/uv/builds-v0/.tmpnGXgAH/lib/python3.12/site-packages/_virtualenv.py", line 22, in parse_config_files
result = old_parse_config_files(self, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/doug/Library/Caches/uv/builds-v0/.tmpnGXgAH/lib/python3.12/site-packages/setuptools/dist.py", line 608, in parse_config_files
pyprojecttoml.apply_configuration(self, filename, ignore_option_errors)
File "/Users/doug/Library/Caches/uv/builds-v0/.tmpnGXgAH/lib/python3.12/site-packages/setuptools/config/pyprojecttoml.py", line 71, in apply_configuration
config = read_configuration(filepath, True, ignore_option_errors, dist)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/doug/Library/Caches/uv/builds-v0/.tmpnGXgAH/lib/python3.12/site-packages/setuptools/config/pyprojecttoml.py", line 136, in read_configuration
validate(subset, filepath)
File "/Users/doug/Library/Caches/uv/builds-v0/.tmpnGXgAH/lib/python3.12/site-packages/setuptools/config/pyprojecttoml.py", line 60, in validate
raise ValueError(f"{error}\n{summary}") from None
ValueError: invalid pyproject.toml config: `project.dependencies`.
configuration error: You cannot provide a value for `project.dependencies` and list it under `project.dynamic` at the same time
---
In this case, the problem was that I had dependencies listed in the dynamic section of the pyproject.toml. I removed them and tried again:
$ uv add beautifulsoup4 httpx lxml keyring keyrings-alt
warning: No `requires-python` value found in the workspace. Defaulting to `>=3.12`.
Resolved 23 packages in 3.54s
Built zpdatafetch @ file:///Users/doug/Development/Zwift/zpd/zpdatafetch
Prepared 17 packages in 2.39s
Installed 17 packages in 22ms
+ anyio==4.4.0
+ beautifulsoup4==4.12.3
+ certifi==2024.7.4
+ h11==0.14.0
+ httpcore==1.0.5
+ httpx==0.27.2
+ idna==3.8
+ jaraco-classes==3.4.0
+ jaraco-context==6.0.1
+ jaraco-functools==4.0.2
+ keyring==25.3.0
+ keyrings-alt==5.0.2
+ lxml==5.3.0
+ more-itertools==10.4.0
+ sniffio==1.3.1
+ soupsieve==2.6
+ zpdatafetch==1.1.0 (from file:///Users/doug/Development/Zwift/zpd/zpdatafetch)
Bingo!. So next the dev dependencies:
$ uv add --dev pytest ruff setuptools build wheel
warning: No `requires-python` value found in the workspace. Defaulting to `>=3.12`.
Resolved 33 packages in 3.22s
warning: No `requires-python` value found in the workspace. Defaulting to `>=3.12`.
Built zpdatafetch @ file:///Users/doug/Development/Zwift/zpd/zpdatafetch
Prepared 10 packages in 3.27s
Uninstalled 1 package in 3ms
Installed 10 packages in 21ms
+ build==1.2.1
+ iniconfig==2.0.0
+ packaging==24.1
+ pluggy==1.5.0
+ pyproject-hooks==1.1.0
+ pytest==8.3.2
+ ruff==0.6.2
+ setuptools==74.0.0
+ wheel==0.44.0
~ zpdatafetch==1.1.0 (from file:///Users/doug/Development/Zwift/zpd/zpdatafetch)
In both cases the system is giving a warning about the python version. I have tested 3.10 and up. So I added the requirement to the project section:
[project]
requires-python = ">=3.10"
And then ran uv sync
and he that seemed to take care of the issue:
$ uv sync
Resolved 39 packages in 3.08s
Built zpdatafetch @ file:///Users/doug/Development/Zwift/zpd/zpdatafetch
Prepared 1 package in 3.10s
Uninstalled 1 package in 3ms
Installed 1 package in 13ms
~ zpdatafetch==1.1.0 (from file:///Users/doug/Development/Zwift/zpd/zpdatafetch)
At this point, not only were all my dependencies installed, but I had a working install set up in my venv. The zpdata command, which is installed as an executable with the package, was installed in .venv/bin
along with the other tools from the dependencies: pytest
, httpx
, keyring
, ruff
, etc.
A quick test showed that the command installed and works:
$ uv run zpdata -h
usage: zpdata [-h] [-v] [{config,cyclist,primes,result,signup,team}] [id ...]
Module for fetching zwiftpower data using the Zwifpower API
positional arguments:
{config,cyclist,primes,result,signup,team}
which command to run
id the id to search for, ignored for config
options:
-h, --help show this help message and exit
-v, --verbose provide feedback while running
All other commands work as expected under uv run
as well.
In the next update, I’ll write about how I integrated uv into github actions and published the next version of zpdatafetch with uv.
Written on 28 August 2024