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

Allow multiple source entries for each package in tool.uv.sources #7745

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

charliermarsh
Copy link
Member

@charliermarsh charliermarsh commented Sep 27, 2024

Summary

This PR enables users to provide multiple source entries in tool.uv.sources, e.g.:

[tool.uv.sources]
httpx = [
  { git = "https://github.com/encode/httpx", tag = "0.27.2", marker = "sys_platform == 'darwin'" },
  { git = "https://github.com/encode/httpx", tag = "0.24.1", marker = "sys_platform == 'linux'" },
]

The implementation is relatively straightforward: when we lower the requirement, we now return an iterator rather than a single requirement. In other words, the above is transformed into two requirements:

httpx @ git+https://github.com/encode/[email protected] ; sys_platform == 'darwin'
httpx @ git+https://github.com/encode/[email protected] ; sys_platform == 'linux'

We verify (at deserialization time) that the markers are non-overlapping.

Closes #3397.

@charliermarsh charliermarsh added the enhancement New feature or request label Sep 27, 2024
@charliermarsh charliermarsh marked this pull request as ready for review September 27, 2024 23:23
}
}
}
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@BurntSushi -- I'd love your eyes on this file in particular. Should I be enforcing these at deserialization time? I could easily enforce them when we actually use the lowered requirement. I'm not certain what's best.

@@ -427,6 +513,7 @@ pub enum Source {
rev: Option<String>,
tag: Option<String>,
branch: Option<String>,
marker: Option<MarkerTreeContents>,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ibraheemdev -- Is it correct to use Option<MarkerTreeContents> for this? It's a field that users can provide in TOML.

return Err(LoweringError::UndeclaredWorkspacePackage);
return Either::Left(std::iter::once(Err(
LoweringError::UndeclaredWorkspacePackage,
)));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this slightly awkward: we return an iterator, but if the "overall" requirement fails, we use once with an error. Perhaps the return type should more truthfully be: Result<impl Iterator<Item = Result<LoweredRequirement, LoweringError>>, LoweringError>

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We lower only for workspace packages, there are few items and the iterator looks too complex for this use case, should we collect into a Result<Vec<LoweredRequirement>, LoweringError>?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose that's true -- this does only apply to workspace packages. I'd like @BurntSushi opinion on it too.

marker,
source,
origin: requirement.origin.clone(),
}))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is mostly just the code being indented and put under an iterator. Very minimal changes.

.ok_or_else(|| de::Error::custom("expected at least one marker expression"))?;
Ok(marker)
}
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

\cc @ibraheemdev -- Does this make sense for a user-provided marker field?

Copy link
Member

@ibraheemdev ibraheemdev Sep 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I'm not sure if this (and Option<MarkerTreeContents>) works. If the user wrote x or not x, we simplify to MarkerTree::TRUE, so this deserialize would fail? I don't think serde falls back to None in that case without serde(default).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would you suggest? Just use String and parse these as needed?

"items": {
"$ref": "#/definitions/Source"
}
},
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is wrong (it thinks it has to be a list).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks correct to and check-jsonschema does only complain about "tree" below:

[tool.uv.sources]
tqdm = { git = "https:/github.com/tqdm/tqdm" }
httpx = [
  { git = "https://github.com/encode/httpx", tag = "0.27.2", marker = "sys_platform == 'darwin'" },
  { git = "https://github.com/encode/httpx", tag = "0.24.1", marker = "sys_platform == 'linux'" },
]
foo = "tree"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I think I fixed it between the time I wrote this comment and your testing. Thanks so much for testing it.)

@charliermarsh charliermarsh force-pushed the charlie/multi-sources branch 2 times, most recently from 320be30 to 73703fc Compare September 28, 2024 01:41
httpx = [
{ git = "https://github.com/encode/httpx", tag = "0.27.2", marker = "sys_platform == 'darwin'" },
{ git = "https://github.com/encode/httpx", tag = "0.24.1", marker = "sys_platform == 'linux'" },
]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In these expressions, do we accept extras and support the following (assuming index api has landed)?

[project.optional-dependencies]
cu118 = []
cu121 = []
cu124 = []

[tool.uv.sources]
torch = [
    { index = "torch-cu118", marker = "extra == 'cu118'"},
    { index = "torch-cu124", marker = "extra == 'cu124'"},
    # cu121 is on pypi
]

[[tool.uv.index]]
name = "torch-cu118"
url = "https://download.pytorch.org/whl/cu118"

[[tool.uv.index]]
name = "torch-cu124"
url = "https://download.pytorch.org/whl/cu124"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We definitely can't support the above since we don't support disjoint extras, and those markers are overlapping. I talk about this at length here: https://www.notion.so/astral-sh/PyTorch-10a48797e1ca803ea593c456e9bf23f8?pvs=4#10a48797e1ca80f4ab5af90ebcd77000.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to see if we already propagate extras here though. I.e., this should probably work, right?

[project.optional-dependencies]
cu118 = []

[tool.uv.sources]
torch = [
    { index = "torch-cu118", marker = "extra == 'cu118'"},
]

[[tool.uv.index]]
name = "torch-cu118"
url = "https://download.pytorch.org/whl/cu118"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do think we need to support it once we support disjoint extras. It makes the mixed CPU/GPU stuff so much better.

"items": {
"$ref": "#/definitions/Source"
}
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks correct to and check-jsonschema does only complain about "tree" below:

[tool.uv.sources]
tqdm = { git = "https:/github.com/tqdm/tqdm" }
httpx = [
  { git = "https://github.com/encode/httpx", tag = "0.27.2", marker = "sys_platform == 'darwin'" },
  { git = "https://github.com/encode/httpx", tag = "0.24.1", marker = "sys_platform == 'linux'" },
]
foo = "tree"

.map(move |requirement| match requirement {
Ok(requirement) => Ok(requirement.into_inner()),
Err(err) => Err(err),
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
})
.map_ok(move |requirement| requirement.into_inner())

| ^
Source markers must be disjoint, but the following markers overlap: `python_full_version == '3.12.*' and sys_platform == 'win32'` and `sys_platform == 'win32'`.

hint: replace `sys_platform == 'win32'` with `python_full_version != '3.12.*' and sys_platform == 'win32'`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

powerful

return Err(LoweringError::UndeclaredWorkspacePackage);
return Either::Left(std::iter::once(Err(
LoweringError::UndeclaredWorkspacePackage,
)));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We lower only for workspace packages, there are few items and the iterator looks too complex for this use case, should we collect into a Result<Vec<LoweredRequirement>, LoweringError>?

docs/concepts/dependencies.md Show resolved Hide resolved
[package.optional-dependencies]
cpu = [
{ name = "iniconfig" },
]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is wrong (it doesn't include iniconfig outside of the optional dependencies, but it should). I think it's because the negation of extra == 'cpu' is extra != 'cpu', but when we resolve, we assume all extras are enabled, so that's false? It's like I want the negation of extra == 'cpu' to be, like, just omitting that marker. \cc @konstin

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Accept list of sources with markers in uv.tool.sources
3 participants