Skip to content

Commit

Permalink
Add a dedicated error for members with mismatched Python requirements (
Browse files Browse the repository at this point in the history
…#5695)

## Summary

Gives you a nice error message if you attempt to sync with, e.g., `-p
3.8` when that version is supported by at least one workspace member,
but your project's minimum requirement is `>=3.12`

Closes #5662.
  • Loading branch information
charliermarsh authored Aug 1, 2024
1 parent 69b8b16 commit 7efed62
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 4 deletions.
2 changes: 1 addition & 1 deletion crates/uv-workspace/src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1483,7 +1483,7 @@ mod tests {
"root": "[ROOT]/albatross-root-workspace/packages/bird-feeder",
"project": {
"name": "bird-feeder",
"requires-python": ">=3.12",
"requires-python": ">=3.8",
"optional-dependencies": null
},
"pyproject_toml": "[PYPROJECT_TOML]"
Expand Down
36 changes: 35 additions & 1 deletion crates/uv/src/commands/project/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use std::fmt::Write;
use std::path::PathBuf;

use itertools::Itertools;
use owo_colors::OwoColorize;
use tracing::debug;

use distribution_types::{Resolution, UnresolvedRequirementSpecification};
use pep440_rs::Version;
use pep440_rs::{Version, VersionSpecifiers};
use pypi_types::Requirement;
use uv_auth::store_credentials_from_url;
use uv_cache::Cache;
Expand All @@ -17,6 +18,7 @@ use uv_dispatch::BuildDispatch;
use uv_distribution::DistributionDatabase;
use uv_fs::Simplified;
use uv_installer::{SatisfiesResult, SitePackages};
use uv_normalize::PackageName;
use uv_python::{
request_from_version_file, EnvironmentPreference, Interpreter, PythonEnvironment, PythonFetch,
PythonInstallation, PythonPreference, PythonRequest, VersionRequest,
Expand Down Expand Up @@ -60,6 +62,15 @@ pub(crate) enum ProjectError {
#[error("The requested Python interpreter ({0}) is incompatible with the project Python requirement: `{1}`")]
RequestedPythonIncompatibility(Version, RequiresPython),

#[error("The requested Python interpreter ({0}) is incompatible with the project Python requirement: `{1}`. However, a workspace member (`{member}`) supports Python {3}. To install the workspace member on its own, navigate to `{path}`, then run `{venv}` followed by `{install}`.", member = _2.cyan(), venv = format!("uv venv --python {_0}").green(), install = "uv pip install -e .".green(), path = _4.user_display().cyan() )]
RequestedMemberPythonIncompatibility(
Version,
RequiresPython,
PackageName,
VersionSpecifiers,
PathBuf,
),

#[error(transparent)]
Python(#[from] uv_python::Error),

Expand Down Expand Up @@ -239,6 +250,29 @@ impl FoundInterpreter {

if let Some(requires_python) = requires_python.as_ref() {
if !requires_python.contains(interpreter.python_version()) {
// If the Python version is compatible with one of the workspace _members_, raise
// a dedicated error. For example, if the workspace root requires Python >=3.12, but
// a library in the workspace is compatible with Python >=3.8, the user may attempt
// to sync on Python 3.8. This will fail, but we should provide a more helpful error
// message.
for (name, member) in workspace.packages() {
let Some(project) = member.pyproject_toml().project.as_ref() else {
continue;
};
let Some(specifiers) = project.requires_python.as_ref() else {
continue;
};
if specifiers.contains(interpreter.python_version()) {
return Err(ProjectError::RequestedMemberPythonIncompatibility(
interpreter.python_version().clone(),
requires_python.clone(),
name.clone(),
specifiers.clone(),
member.root().clone(),
));
}
}

return Err(ProjectError::RequestedPythonIncompatibility(
interpreter.python_version().clone(),
requires_python.clone(),
Expand Down
2 changes: 1 addition & 1 deletion crates/uv/tests/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ fn mixed_requires_python() -> Result<()> {
----- stderr -----
warning: `uv sync` is experimental and may change without warning
Using Python 3.8.[X] interpreter at: [PYTHON-3.8]
error: The requested Python interpreter (3.8.[X]) is incompatible with the project Python requirement: `>=3.12`
error: The requested Python interpreter (3.8.[X]) is incompatible with the project Python requirement: `>=3.12`. However, a workspace member (`bird-feeder`) supports Python >=3.8. To install the workspace member on its own, navigate to `packages/bird-feeder`, then run `uv venv --python 3.8.[X]` followed by `uv pip install -e .`.
"###);

Ok(())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[project]
name = "bird-feeder"
version = "1.0.0"
requires-python = ">=3.12"
requires-python = ">=3.8"
dependencies = ["anyio>=4.3.0,<5", "seeds"]

[tool.uv.sources]
Expand Down

0 comments on commit 7efed62

Please sign in to comment.