mirror of
https://github.com/astral-sh/uv.git
synced 2025-10-30 03:27:31 +00:00
[uv-settings]: Correct behavior for relative find-links paths when run from a subdir (#10827)
## One-liner
Relative find-links configuration to local path from a pyproject.toml or
uv.toml is now relative to the config file
## Summary
### Background
One can configure find-links in a `pyproject.toml` or `uv.toml` file,
which are located from the cli arg, system directory, user directory, or
by traversing parent directories until one is encountered.
This PR addresses the following scenario:
- A project directory which includes a `pyproject.toml` or `uv.toml`
file
- The config file includes a `find-links` option. (eg under `[tool.uv]`
for `pyproject.toml`)
- The `find-links` option is configured to point to a local subdirectory
in the project: `packages/`
- There is a subdirectory called `subdir`, which is the current working
directory
- I run `uv run my_script.py`. This will locate the `pyproject.toml` in
the parent directory
### Current Behavior
- uv tries to use the path `subdir/packages/` to find packages, and
fails.
### New Behavior
- uv tries to use the path `packages/` to find the packages, and
succeeds
- Specifically, any relative local find-links path will resolve to be
relative to the configuration file.
### Why is this behavior change OK?
- I believe no one depends on the behavior that a relative find-links
when running in a subdir will refer to different directories each time
- Thus this change only allows a more common use case which didn't work
previously.
## Test Plan
- I re-created the setup mentioned above:
```
UvTest/
├── packages/
│ ├── colorama-0.4.6-py2.py3-none-any.whl
│ └── tqdm-4.67.1-py3-none-any.whl
├── subdir/
│ └── my_script.py
└── pyproject.toml
```
```toml
# pyproject.toml
[project]
name = "uvtest"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"tqdm>=4.67.1",
]
[tool.uv]
offline = true
no-index = true
find-links = ["packages/"]
```
- With working directory under `subdir`, previously, running `uv sync
--offline` would fail resolving the tdqm package, and after the change
it succeeds.
- Additionally, one can use `uv sync --show-settings` to show the
actually-resolved settings - now having the desired path in
`flat_index.url.path`
## Alternative designs considered
- I considered modifying the `impl Deserialize for IndexUrl` to parse
ahead of time directly with a base directory by having a custom
`Deserializer` with a base dir field, but it seems to contradict the
design of the serde `Deserialize` trait - which should work with all
`Deserializer`s
## Future work
- Support for adjusting all other local-relative paths in `Options`
would be desired, but is out of scope for the current PR.
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
This commit is contained in:
parent
d6d0593b71
commit
b1e4bc779c
6 changed files with 208 additions and 25 deletions
|
|
@ -108,8 +108,9 @@ impl FilesystemOptions {
|
|||
let path = dir.join("uv.toml");
|
||||
match fs_err::read_to_string(&path) {
|
||||
Ok(content) => {
|
||||
let options: Options = toml::from_str(&content)
|
||||
.map_err(|err| Error::UvToml(path.clone(), Box::new(err)))?;
|
||||
let options = toml::from_str::<Options>(&content)
|
||||
.map_err(|err| Error::UvToml(path.clone(), Box::new(err)))?
|
||||
.relative_to(&std::path::absolute(dir)?)?;
|
||||
|
||||
// If the directory also contains a `[tool.uv]` table in a `pyproject.toml` file,
|
||||
// warn.
|
||||
|
|
@ -155,6 +156,8 @@ impl FilesystemOptions {
|
|||
return Ok(None);
|
||||
};
|
||||
|
||||
let options = options.relative_to(&std::path::absolute(dir)?)?;
|
||||
|
||||
tracing::debug!("Found workspace configuration at `{}`", path.display());
|
||||
return Ok(Some(Self(options)));
|
||||
}
|
||||
|
|
@ -252,8 +255,13 @@ fn system_config_file() -> Option<PathBuf> {
|
|||
/// Load [`Options`] from a `uv.toml` file.
|
||||
fn read_file(path: &Path) -> Result<Options, Error> {
|
||||
let content = fs_err::read_to_string(path)?;
|
||||
let options: Options =
|
||||
toml::from_str(&content).map_err(|err| Error::UvToml(path.to_path_buf(), Box::new(err)))?;
|
||||
let options = toml::from_str::<Options>(&content)
|
||||
.map_err(|err| Error::UvToml(path.to_path_buf(), Box::new(err)))?;
|
||||
let options = if let Some(parent) = std::path::absolute(path)?.parent() {
|
||||
options.relative_to(parent)?
|
||||
} else {
|
||||
options
|
||||
};
|
||||
Ok(options)
|
||||
}
|
||||
|
||||
|
|
@ -294,6 +302,9 @@ pub enum Error {
|
|||
#[error(transparent)]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
Index(#[from] uv_distribution_types::IndexUrlError),
|
||||
|
||||
#[error("Failed to parse: `{}`", _0.user_display())]
|
||||
PyprojectToml(PathBuf, #[source] Box<toml::de::Error>),
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue