Unlike regular packages, specifying all `__init__.py` directories for a
namespace package would be very verbose There is e.g.
https://github.com/python-poetry/poetry/tree/main/src/poetry, which has
18 modules, or https://github.com/googleapis/api-common-protos which is
inconsistently nested. For both the Google Cloud SDK, there are both
packages with a single module and those with complex structures, with
many having multiple modules due to versioning through `<module>_v1`
versioning. The Azure SDK seems to use one module per package (it's not
explicitly documented but seems to follow from the process in
https://azure.github.io/azure-sdk/python_design.html#azure-sdk-distribution-packages
and
ccb0e03a3d/doc/dev/packaging.md).
For simplicity with complex projects, we add a `namespace = true` switch
which disabled checking for an `__init__.py`. We only check that there's
no `<module_root>/<module_name>/__init__.py` and otherwise add the whole
`<module_root>/<module_name>` folder. This comes at the cost of
`namespace = true` effectively creating an opt-out from our usual checks
that allows creating an almost entirely arbitrary package.
For simple projects with only a single module, the module name can be
dotted to point to the target module, so the build still gets checked:
```toml
[tool.uv.build-backend]
module-name = "poetry.core"
```
## Alternatives
### Declare all packages
We could make `module-name` a list and allow or require declaring all
packages:
```toml
[tool.uv.build-backend]
module-name = ["cloud_sdk.service.storage", "cloud_sdk.service.storage_v1", "cloud_sdk.billing.storage"]
```
Or for Poetry:
```toml
[tool.uv.build-backend]
module-name = [
"poetry.config",
"poetry.console",
"poetry.inspection",
"poetry.installation",
"poetry.json",
"poetry.layouts",
"poetry.masonry",
"poetry.mixology",
"poetry.packages",
"poetry.plugins",
"poetry.publishing",
"poetry.puzzle",
"poetry.pyproject",
"poetry.repositories",
"poetry.toml",
"poetry.utils",
"poetry.vcs",
"poetry.version"
]
```
### Support multiple namespaces
We could also allow namespace packages with multiple root level module:
```toml
[tool.uv.build-backend]
module-name = ["cloud_sdk.my_ext", "local_sdk.my_ext"]
```
For lack of use cases, we delegate this to creating a workspace with one
package per module.
## Implementation
Due to the more complex options for the module name, I'm moving
verification on deserialization later, dropping the source span we'd get
from serde. We also don't show similarly named directories anymore.
---------
Co-authored-by: Andrew Gallant <andrew@astral.sh>
Investigating #13744
I tried reproducing here by running the test in a loop, but could not. I
presume it's an interaction with other tests.
This drops the snapshot, but I think it's worth it to try to examine the
flake?
Use TTY detection to determine when we should forward SIGINT instead of
counting signals, which can lead to various problems where multiple
SIGINTs are sent to a child after the first signal. Counting does not
make sense in interactive situations that do not exit on interrupt,
e.g., the Python REPL.
Closes https://github.com/astral-sh/uv/issues/13919
Closes https://github.com/astral-sh/uv/issues/12108
As another follow-up in the vein of
https://github.com/astral-sh/uv/pull/13944, I noticed `uv python pin`
doesn't download Python versions, which is a bit weird because we'll
warn it's not found.
See https://github.com/astral-sh/uv/issues/13935#issuecomment-2957300516
where we fail to write a pin file because we encounter an unusable
interpreter. This is actually a special case where `MissingPython` is
not raised because we want to show why we failed to find a usable
interpreter, which is useful in commands where you _need_ an interpreter
to use, but here we don't actually need it. Here, we just log the
failure and move on.
Related https://github.com/astral-sh/uv/pull/13936
Add basic tests for error messages on retryable network errors.
This test mod is intended to grow to ensure that we handle retryable
errors correctly and that we show the appropriate error message if we
failed after retrying.
The starter tests show some common cases we've seen download errors in:
simple and find links indexes, file downloads and Python installs.
For `io::Error` fault injection to test the reqwest `Err` path besides
the HTTP status code `Ok` path, see
https://github.com/LukeMathWalker/wiremock-rs/issues/149.
As per #13874, passing a relative URL like `test` to `--index` for `uv
add` causes unexpected behavior if the directory does not exist. The
non-existent index is effectively ignored and uv falls back to PyPI. If
a package is found there, the spurious index is then written to
`pyproject.toml`. This doesn't happen for `--default-index` since
resolution will fail without fallback to PyPI.
This PR adds a validation step for indexes provided on the command line.
If a directory does not exist, uv will fail with an error.
Closes#13874
For the case where there was no matching wheel on sync, we previously
added a note about which wheels are available vs. on which platform you
are on. We extend this error message to link directly towards
`tool.uv.required-environments`, which otherwise has a discovery
problem.
On Linux (Setting `tool.uv.required-environments` doesn't help here
either, but it's a clear example):
```
[project]
name = "debug"
version = "0.1.0"
requires-python = "==3.10.*"
dependencies = ["tensorflow-macos>=2.13.1"]
```
```
Resolved 41 packages in 24ms
error: Distribution `tensorflow-macos==2.16.2 @ registry+https://pypi.org/simple` can't be installed because it doesn't have a source distribution or wheel for the current platform
hint: You're on Linux (`manylinux_2_39_x86_64`), but there are no wheels for the current platform, consider configuring `tool.uv.required-environments`.
hint: `tensorflow-macos` (v2.16.2) only has wheels for the following platform: `macosx_12_0_arm64`.
```

---------
Co-authored-by: Zanie Blue <contact@zanie.dev>
Users are not (yet) properly familiar with the concept of universal
resolution and its implication that we need to resolve for all possible
platforms and Python versions. Some projects only target a specific
platform or Python version, and users experience resolution errors due
to failures for other platforms. Indicated by the number of questions we
get about it, `tool.uv.environments` for restricting environments is not
well discoverable.
We add a special hint when resolution failed on a fork disjoint with the
current environment, hinting the user to constrain `requires-python` and
`tool.uv.environments` respectively.
The hint has false positives for cases where the resolution failed on a
different platform, but equally fails on the current platform, in cases
where the non-current fork was tried earlier. Given that conflicts can
be based on `requires-python`, afaik we can't parse whether the current
platform would also be affected from the derivation tree.
Two cases not covered by this are build errors as well as install errors
that need `tool.uv.required-environments`.
- Define all list elements using `-`: it used to be a mix of `*` and
`-`. `-` is what Prettier linter formats it to by default.
- Removed unnecessary blank line between 2 list elements. Other elements
were stitched together without blank lines in between.
- Only the first list element started in sentence case (capital letter
first) - I made all start like so.
Surprisingly, we weren't locking during `uv sync` so far, so running `uv
sync` in parallel could cause errors in filesystem races.
I've also added locks to `uv add` and `uv remove` which concurrently
modify `pyproject.toml`. These locks only apply after we determined the
interpreter, so they don't actually prevent computing the same thing
twice when running `uv add` in parallel.
All other subcommands that I checked were already locking (with no claim
to exhaustiveness)
Fixes#12751
# Test Plan
I don't have CI-sized reproducer for this.
```toml
[project]
name = "debug"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"boto3>=1.38.30",
"fastapi>=0.115.12",
"numba>=0.61.2",
"polars>=1.30.0",
"protobuf>=6.31.1",
"pyarrow>=20.0.0",
"pydantic>=2.11.5",
"requests>=2.32.3",
"urllib3>=2.4.0",
"scikit-learn>=1.6.1",
"jupyter>=1.1.1",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
```
```
rm -rf .venv && parallel -n0 "uv sync -q" ::: {1..10}
```
## Summary
I think `GlobDirFilter::match_directory` should return `true` if it
failed to construct a DFA
to force a full directory traversal. Returning `false` means that the
build backend excludes all files which
is unlikely what we want in that situation.
## Summary
When trying out `uv export --no-editable --format pylock.toml` the
exported contents would still retain `editable = true` regardless.
## Test Plan
Added additional test. Tested locally on few projects where I was
previously using `uv export --no-editable --format requirements.txt` to
ensure the output aligns.
Extends https://github.com/astral-sh/uv/pull/13841 — I'll drop that
commit later after that pull request merges but it's small.
I find the split into a "Configuration" section awkward and don't think
it's helping us. Everything moved into the "Concepts" section, except
the "Environment variables" page which definitely belongs in the
reference and the "Installer" page which is fairly niche and seems
better in the reference.
Before / After
<img
src="https://github.com/user-attachments/assets/80d8304b-17da-4900-a5f4-c3ccac96fcc5"
width="400">
## Summary
Switch the `sync_dry_run` test to use Python 3.9 instead of Python 3.8.
I didn't previously catch it since it was marked as `python_managed`.
## Test Plan
`cargo test --no-default-features --features git,pypi,python` without
Python 3.8 installed.
## Summary
Modify `requirements_txt_ssh_git_username` test to pass `-o
UserKnownHostsFile=/dev/null` to OpenSSH, in order to prevent it from
trying to write into the known-hosts in user's home directory. To add
insult to the injury, OpenSSH ignores `HOME` and writes into the actual
home when it is explicitly overridden for the purpose of testing.
## Test Plan
`cargo test --no-default-features --features=git,pypi,python` in an
environment where the user can't write to `pw_home` (but can to
`${HOME}`). Previously the test would fail:
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Snapshot Summary ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Snapshot: requirements_txt_ssh_git_username
Source: crates/uv/tests/it/export.rs:1252
────────────────────────────────────────────────────────────────────────────────
Expression: snapshot
────────────────────────────────────────────────────────────────────────────────
-old snapshot
+new results
────────────┬───────────────────────────────────────────────────────────────────
7 7 │ ├─▶ failed to clone into: [PATH]
8 8 │ ├─▶ failed to fetch branch, tag, or commit `d780faf0ac91257d4d5a4f0c5a0e4509608c0071`
9 9 │ ╰─▶ process didn't exit successfully: [GIT_COMMAND_ERROR]
10 10 │ --- stderr
11 │+ Could not create directory '/var/lib/portage/home/.ssh' (Permission denied).
12 │+ Failed to add the host to the list of known hosts (/var/lib/portage/home/.ssh/known_hosts).
11 13 │ Load key "[TEMP_DIR]/fake_deploy_key": [ERROR]
12 14 │ git@github.com: Permission denied (publickey).
13 15 │ fatal: Could not read from remote repository.
14 16 │
────────────┴───────────────────────────────────────────────────────────────────
```
---------
Co-authored-by: konstin <konstin@mailbox.org>
## Summary
When missing an operator for version parsing, it would give an error
that was hard to know how to fix if you were not familiar with version
specifiers / PEP-440:
```
Unexpected end of version specifier, expected operator
```
Now, it will attempt to provide a more useful hint if it can parse the
version from the remaining scanner:
```
Unexpected end of version specifier, expected operator (did you mean "==3.12"?)
```
## Test Plan
Unit tests in `version_specifier.rs` were added/updated for the
following cases:
- `test_parse_specifier_missing_operator_error`
- `test_parse_specifier_missing_operator_invalid_version_error`
- `test_invalid_word`
- `test_invalid_specifier`
- `error_message_version_specifiers_parse_error`
A test in `edit.rs` for failing to parse the `pyproject.toml` when using
`add` was also included to match the request in the original issue:
- `add_invalid_requires_python`
I didn't add cases where no version specifier is provided because it
seemed like it doesn't get to the point of parsing in that case, so it
should not happen.
## Reference
Fixes#13126
---------
Co-authored-by: Jacob Woliver <jacob@jmw.sh>
Co-authored-by: konstin <konstin@mailbox.org>
This includes some initial work on adding Pyodide support (issue
#12729). It is enough to get
```
uv pip compile -p /path/to/pyodide --extra-index-url file:/path/to/simple-index
```
to work which should already be quite useful.
## Test Plan
* added a unit test for `pyodide_platform`
* integration tested manually with:
```
cargo run pip install \
-p /home/rchatham/Documents/programming/tmp/pyodide-venv-test/.pyodide-xbuildenv-0.29.3/0.27.4/xbuildenv/pyodide-root/dist/python \
--extra-index-url file:/home/rchatham/Documents/programming/tmp/pyodide-venv-test/.pyodide-xbuildenv-0.29.3/0.27.4/xbuildenv/pyodide-root/package_index \
--index-strategy unsafe-best-match --target blah --no-build \
numpy pydantic
```
---------
Co-authored-by: konsti <konstin@mailbox.org>
Co-authored-by: Zanie Blue <contact@zanie.dev>
This updates the `Debug` implementation of `DisplaySafeUrl` to display
the git username in a case like `git+ssh://git@github.com/org/repo`. It
also factors out the git username check into a shared function and adds
related unit tests to `DisplaySafeUrl`.
`trace!`-log the locations of data directories used by a wheel. These
paths are queried from sysconfig, i.e. they are mostly Python
interpreter defined instead of being computed by uv.
Fixes#13795 specifically by not redacting the username convention `git`
for SSH URLs, though we should never write stars in `uv export`
generally (#13791).