Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inconsistent handling of relative paths in uv build with Hatch #12310

Open
constantin-huetterer opened this issue Mar 19, 2025 · 6 comments
Open
Labels
bug Something isn't working

Comments

@constantin-huetterer
Copy link

constantin-huetterer commented Mar 19, 2025

Summary

Thank you very much for creating UV! It is a joy to use! ❤

There seems to be an inconsistency of how relative paths inside the pyproject.toml of a package are handled between uv build --package X and uv build --package X [--wheel/--sdist].

Context

We are using UV with a MonoRepo including lots of libraries. To avoid duplication in the authors section of each pyproject.toml within our lib folder, we would like to generate this data dynamically from a single source of truth. This is possible with MetaDataHooks built into Hatch.

Our project structure looks roughly like this:

.
├── README.md
├── hatch_build.py # used to fetch dynamic meta-data at build time
├── lib # lots of libraries in here
│   └── a
│       ├── README.md
│       ├── pyproject.toml # duplicated authors, reference to hatch_build.py
│       └── src
│           └── a
│               ├── __init__.py
│               └── py.typed
#   a lot more libs ...
├── main.py
├── pyproject.toml
└── uv.lock

Bug

When using relative paths inside lib/a/pyproject.toml to reference the hatch_build.py, the command uv build --package a fails, claiming that it is unable to find hatch_build.py.

Verbose Error Log

build --package a --verbose
DEBUG uv 0.6.8 (c1ef482 2025-03-18)
DEBUG Found workspace root: REDACTED/uv_bug_reproduction
DEBUG Adding root workspace member: REDACTED/uv_bug_reproduction
DEBUG Adding discovered workspace member: REDACTED/uv_bug_reproduction/lib/a
DEBUG Reading Python requests from version file at REDACTED/uv_bug_reproduction/.python-version
DEBUG Searching for Python 3.9 in virtual environments, managed installations, or search path
DEBUG Found cpython-3.9.19-macos-aarch64-none at REDACTED/uv_bug_reproduction/.venv/bin/python3 (virtual environment)
DEBUG Using request timeout of 30s
Building source distribution...
DEBUG Found workspace root: REDACTED/uv_bug_reproduction
DEBUG Adding root workspace member: REDACTED/uv_bug_reproduction
DEBUG Adding discovered workspace member: REDACTED/uv_bug_reproduction/lib/a
DEBUG Using base executable for virtual environment: REDACTED/uv_bug_reproduction/.venv/bin/python3
DEBUG Ignoring empty directory
DEBUG Resolving build requirements
DEBUG Solving with installed Python version: 3.9.19
DEBUG Solving with target Python version: >=3.9.19
DEBUG Adding direct dependency: hatchling*
DEBUG Found fresh response for: https://pypi.org/simple/hatchling/
DEBUG Searching for a compatible version of hatchling ()
DEBUG Selecting: hatchling==1.27.0 [compatible] (hatchling-1.27.0-py3-none-any.whl)
DEBUG Found fresh response for: https://files.pythonhosted.org/packages/08/e7/ae38d7a6dfba0533684e0b2136817d667588ae3ec984c1a4e5df5eb88482/hatchling-1.27.0-py3-none-any.whl.metadata
DEBUG Adding transitive dependency for hatchling==1.27.0: packaging>=24.2
DEBUG Adding transitive dependency for hatchling==1.27.0: pathspec>=0.10.1
DEBUG Adding transitive dependency for hatchling==1.27.0: pluggy>=1.0.0
DEBUG Adding transitive dependency for hatchling==1.27.0: tomli{python_full_version < '3.11'}>=1.2.2
DEBUG Adding transitive dependency for hatchling==1.27.0: trove-classifiers

DEBUG Found fresh response for: https://pypi.org/simple/packaging/
DEBUG Searching for a compatible version of packaging (>=24.2)
DEBUG Selecting: packaging==24.2 [compatible] (packaging-24.2-py3-none-any.whl)
DEBUG Found fresh response for: https://pypi.org/simple/tomli/
DEBUG Found fresh response for: https://pypi.org/simple/pluggy/
DEBUG Found fresh response for: https://pypi.org/simple/pathspec/
DEBUG Found fresh response for: https://pypi.org/simple/trove-classifiers/
DEBUG Found fresh response for: https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl.metadata
DEBUG Found fresh response for: https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl.metadata
DEBUG Found fresh response for: https://files.pythonhosted.org/packages/46/52/bc0592d1ac02f93983f5e46b0e4872a0abcc498faa2727ff7c184b6bdb6c/trove_classifiers-2025.3.13.13-py3-none-any.whl.metadata
DEBUG Found fresh response for: https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl.metadata
DEBUG Searching for a compatible version of pathspec (>=0.10.1)
DEBUG Selecting: pathspec==0.12.1 [compatible] (pathspec-0.12.1-py3-none-any.whl)
DEBUG Searching for a compatible version of pluggy (>=1.0.0)
DEBUG Selecting: pluggy==1.5.0 [compatible] (pluggy-1.5.0-py3-none-any.whl)
DEBUG Searching for a compatible version of tomli{python_full_version < '3.11'} (>=1.2.2)
DEBUG Selecting: tomli==2.2.1 [compatible] (tomli-2.2.1-py3-none-any.whl)
DEBUG Adding transitive dependency for tomli==2.2.1: tomli==2.2.1
DEBUG Adding transitive dependency for tomli==2.2.1: tomli{python_full_version < '3.11'}==2.2.1
DEBUG Searching for a compatible version of tomli (==2.2.1)
DEBUG Selecting: tomli==2.2.1 [compatible] (tomli-2.2.1-py3-none-any.whl)
DEBUG Found fresh response for: https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl.metadata
DEBUG Searching for a compatible version of tomli{python_full_version < '3.11'} (==2.2.1)
DEBUG Selecting: tomli==2.2.1 [compatible] (tomli-2.2.1-py3-none-any.whl)
DEBUG Searching for a compatible version of trove-classifiers ()
DEBUG Selecting: trove-classifiers==2025.3.13.13 [compatible] (trove_classifiers-2025.3.13.13-py3-none-any.whl)
DEBUG Tried 6 versions: hatchling 1, packaging 1, pathspec 1, pluggy 1, tomli 1, trove-classifiers 1
DEBUG marker environment resolution took 0.013s
DEBUG Installing in pluggy==1.5.0, hatchling==1.27.0, trove-classifiers==2025.3.13.13, packaging==24.2, pathspec==0.12.1, tomli==2.2.1 in REDACTED/.cache/uv/builds-v0/.tmpg5zjKl
DEBUG Registry requirement already cached: pluggy==1.5.0
DEBUG Registry requirement already cached: hatchling==1.27.0
DEBUG Registry requirement already cached: trove-classifiers==2025.3.13.13
DEBUG Registry requirement already cached: packaging==24.2
DEBUG Registry requirement already cached: pathspec==0.12.1
DEBUG Registry requirement already cached: tomli==2.2.1
DEBUG Installing build requirements: pluggy==1.5.0, hatchling==1.27.0, trove-classifiers==2025.3.13.13, packaging==24.2, pathspec==0.12.1, tomli==2.2.1
DEBUG Creating PEP 517 build environment
DEBUG Calling hatchling.build.get_requires_for_build_sdist()
DEBUG Found workspace root: REDACTED/uv_bug_reproduction
DEBUG Calling hatchling.build.build_sdist("REDACTED/uv_bug_reproduction/dist", {})
Building wheel from source distribution...
DEBUG Found workspace root: REDACTED/uv_bug_reproduction
DEBUG Using base executable for virtual environment: REDACTED/uv_bug_reproduction/.venv/bin/python3
DEBUG Ignoring empty directory
DEBUG Resolving build requirements
DEBUG Solving with installed Python version: 3.9.19
DEBUG Solving with target Python version: >=3.9.19
DEBUG Adding direct dependency: hatchling

DEBUG Searching for a compatible version of hatchling ()
DEBUG Selecting: hatchling==1.27.0 [compatible] (hatchling-1.27.0-py3-none-any.whl)
DEBUG Adding transitive dependency for hatchling==1.27.0: packaging>=24.2
DEBUG Adding transitive dependency for hatchling==1.27.0: pathspec>=0.10.1
DEBUG Adding transitive dependency for hatchling==1.27.0: pluggy>=1.0.0
DEBUG Adding transitive dependency for hatchling==1.27.0: tomli{python_full_version < '3.11'}>=1.2.2
DEBUG Adding transitive dependency for hatchling==1.27.0: trove-classifiers

DEBUG Searching for a compatible version of packaging (>=24.2)
DEBUG Selecting: packaging==24.2 [compatible] (packaging-24.2-py3-none-any.whl)
DEBUG Searching for a compatible version of pathspec (>=0.10.1)
DEBUG Selecting: pathspec==0.12.1 [compatible] (pathspec-0.12.1-py3-none-any.whl)
DEBUG Searching for a compatible version of pluggy (>=1.0.0)
DEBUG Selecting: pluggy==1.5.0 [compatible] (pluggy-1.5.0-py3-none-any.whl)
DEBUG Searching for a compatible version of tomli{python_full_version < '3.11'} (>=1.2.2)
DEBUG Selecting: tomli==2.2.1 [compatible] (tomli-2.2.1-py3-none-any.whl)
DEBUG Adding transitive dependency for tomli==2.2.1: tomli==2.2.1
DEBUG Adding transitive dependency for tomli==2.2.1: tomli{python_full_version < '3.11'}==2.2.1
DEBUG Searching for a compatible version of tomli (==2.2.1)
DEBUG Selecting: tomli==2.2.1 [compatible] (tomli-2.2.1-py3-none-any.whl)
DEBUG Searching for a compatible version of tomli{python_full_version < '3.11'} (==2.2.1)
DEBUG Selecting: tomli==2.2.1 [compatible] (tomli-2.2.1-py3-none-any.whl)
DEBUG Searching for a compatible version of trove-classifiers (*)
DEBUG Selecting: trove-classifiers==2025.3.13.13 [compatible] (trove_classifiers-2025.3.13.13-py3-none-any.whl)
DEBUG Tried 6 versions: hatchling 1, packaging 1, pathspec 1, pluggy 1, tomli 1, trove-classifiers 1
DEBUG marker environment resolution took 0.000s
DEBUG Installing in pluggy==1.5.0, hatchling==1.27.0, trove-classifiers==2025.3.13.13, packaging==24.2, pathspec==0.12.1, tomli==2.2.1 in REDACTED/.cache/uv/builds-v0/.tmp9gQggn
DEBUG Registry requirement already cached: pluggy==1.5.0
DEBUG Registry requirement already cached: hatchling==1.27.0
DEBUG Registry requirement already cached: trove-classifiers==2025.3.13.13
DEBUG Registry requirement already cached: packaging==24.2
DEBUG Registry requirement already cached: pathspec==0.12.1
DEBUG Registry requirement already cached: tomli==2.2.1
DEBUG Installing build requirements: pluggy==1.5.0, hatchling==1.27.0, trove-classifiers==2025.3.13.13, packaging==24.2, pathspec==0.12.1, tomli==2.2.1
DEBUG Creating PEP 517 build environment
DEBUG Calling hatchling.build.get_requires_for_build_wheel()
DEBUG Found workspace root: REDACTED/uv_bug_reproduction
DEBUG Calling hatchling.build.build_wheel("REDACTED/uv_bug_reproduction/dist", {}, None)
Traceback (most recent call last):
File "", line 11, in
File "REDACTED/.cache/uv/builds-v0/.tmp9gQggn/lib/python3.9/site-packages/hatchling/build.py", line 58, in build_wheel
return os.path.basename(next(builder.build(directory=wheel_directory, versions=['standard'])))
File "REDACTED/.cache/uv/builds-v0/.tmp9gQggn/lib/python3.9/site-packages/hatchling/builders/plugin/interface.py", line 90, in build
self.metadata.validate_fields()
File "REDACTED/.cache/uv/builds-v0/.tmp9gQggn/lib/python3.9/site-packages/hatchling/metadata/core.py", line 265, in validate_fields
_ = self.version
File "REDACTED/.cache/uv/builds-v0/.tmp9gQggn/lib/python3.9/site-packages/hatchling/metadata/core.py", line 149, in version
self._version = self._get_version()
File "REDACTED/.cache/uv/builds-v0/.tmp9gQggn/lib/python3.9/site-packages/hatchling/metadata/core.py", line 244, in _get_version
core_metadata = self.core
File "REDACTED/.cache/uv/builds-v0/.tmp9gQggn/lib/python3.9/site-packages/hatchling/metadata/core.py", line 187, in core
metadata_hooks = self.hatch.metadata.hooks
File "REDACTED/.cache/uv/builds-v0/.tmp9gQggn/lib/python3.9/site-packages/hatchling/metadata/core.py", line 1589, in hooks
configured_hooks[hook_name] = metadata_hook(self.root, config)
File "REDACTED/.cache/uv/builds-v0/.tmp9gQggn/lib/python3.9/site-packages/hatchling/metadata/custom.py", line 33, in new
raise OSError(message)
OSError: Build script does not exist: ../../hatch_build.py
× Failed to build REDACTED/uv_bug_reproduction/lib/a
├─▶ The build backend returned an error
╰─▶ Call to hatchling.build.build_wheel failed (exit status: 1)
hint: This usually indicates a problem with the package or the build environment.

Executing the same command with --wheel or --dist works, producing the source distribution and wheel including the dynamically added authors:

➜  uv_bug_reproduction git:(main) ✗ uv build --package a --wheel  
Building wheel...
Successfully built dist/a-0.1.0-py3-none-any.whl
➜  uv_bug_reproduction git:(main) ✗ uv build --package a --sdist
Building source distribution...
Successfully built dist/a-0.1.0.tar.gz

Using uvx hatch build directly on lib/a also produces the desired artifacts. Referencing hatch_build.py with absolute paths inside lib/a/pyproject.toml also works.

Reproduction Script

The following shell script will produce a folder named uv_bug_reproduction in the current directory with the project setup described in the Context section. The error can be produced with the uv build --package a command inside the directory.

Bash Script
#!/bin/bash
uv init --app uv_bug_reproduction
mkdir uv_bug_reproduction/lib
uv init --lib uv_bug_reproduction/lib/a
cd uv_bug_reproduction && uv add lib/a

# add a minimal hatch_build.py to add authors dynamically during the build
cat <<-EOF > ./hatch_build.py
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
from hatchling.metadata.plugin.interface import MetadataHookInterface

class JSONMetaDataHook(MetadataHookInterface):
    def update(self, metadata):
        metadata["authors"] = [
            {"name": "A", "email": "[email protected]"},
            {"name": "B", "email": "[email protected]"},
        ]
EOF

# add the hook to hatch_build.py inside of lib/a/pyproject.toml
cat <<-EOF >> lib/a/pyproject.toml

[tool.hatch.metadata.hooks.custom]
path = "../../hatch_build.py"
EOF

# Replace the static authors string in lib/a/pyproject.toml with a dynamic field
sed -i -e '
/^authors = \[/,/\]/c\
dynamic = ["authors"]
' lib/a/pyproject.toml

Platform

MacOS Sonoma 14.7.1 (23H222) (ARM64)

Version

0.6.8 (c1ef482 2025-03-18)

Python version

cpython-3.12.9-macos-aarch64-none

@constantin-huetterer constantin-huetterer added the bug Something isn't working label Mar 19, 2025
@constantin-huetterer constantin-huetterer changed the title Package build fails when using relative paths for dynamic meta-data with hatch Inconsistent handling of relative paths in uv build with Hatch Mar 19, 2025
@charliermarsh
Copy link
Member

This sounds a lot like #11557. Does python -m build produce the same artifacts as us?

@constantin-huetterer
Copy link
Author

Thank you for the quick reply! 🙏
I saw this ticket and thought that our problem is different, because the sdist and wheel files contain exactly what we expect (except the dynamic meta-data). However, after having read it with all the comments, I believe that this issue is likely not a problem with UV since python -m build indeed also complains about the missing hatch_build.py. Feel free to close this issue.

However, I would be very grateful if you could point me in the direction of a solution to this problem or help me understand it better.

If I understand the other discussion correctly, then uv's build frontend builds the source distribution first and then builds the wheel from the source distribution. Hatchlings build-frontend does not do that and this is why uv run hatch build works with the same configuration.

To make the pyproject.toml compatible with a non-hatch build-frontend, I tried to force-include the hatch_build.py into the source-distribution.

[tool.hatch.build.targets.sdist.force-include]
"../../hatch_build.py" =  "hatch_build.py"

This puts the hatch_build.py in the source distribution as expected. However, if I change the search path accordingly:

[tool.hatch.metadata.hooks.custom]
path = "hatch_build.py"
# path = "../../hatch_build.py" # old

Then the building of the source distribution fails because it does not find the hatch_build.py.

It seems like I would require the path to be ../../hatch_build.py for the source distribution and hatch_build.py for the wheel.
However this does not seem to be possible with the Metadata Hook. Do you have another idea?

@zanieb
Copy link
Member

zanieb commented Mar 19, 2025

@ofek is the best resource here

@ofek
Copy link
Contributor

ofek commented Mar 19, 2025

Can you provide something to help reproduce? Any hatch_build.py file at the root is always included in the source distribution: https://hatch.pypa.io/latest/plugins/builder/sdist/#default-file-selection

@constantin-huetterer
Copy link
Author

Wow, I am amazed how fast you guys are! Thanks for the help! 🥇
@ofek you can find a reproduction-script at the bottom of the issue description.

@ofek
Copy link
Contributor

ofek commented Mar 19, 2025

I'll debug in a few hours if that's okay with you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants