Default to current Python minor if Requires-Python is absent (#4070)

## Summary

If `Requires-Python` is omitted in `uv lock` or `uv run`, we now warn
and default to `>=` the current minor version.

Closes https://github.com/astral-sh/uv/issues/4050.
This commit is contained in:
Charlie Marsh 2024-06-05 16:45:50 -04:00 committed by GitHub
parent 0b6d5b37dc
commit e5f95186de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 92 additions and 15 deletions

View file

@ -392,6 +392,14 @@ impl VersionSpecifier {
}
}
/// `>=<version>`
pub fn greater_than_equal_version(version: Version) -> Self {
Self {
operator: Operator::GreaterThanEqual,
version,
}
}
/// Get the operator, e.g. `>=` in `>= 2.0.0`
pub fn operator(&self) -> &Operator {
&self.operator

View file

@ -826,7 +826,7 @@ mod tests {
"root": "[ROOT]/albatross-in-example/examples/bird-feeder",
"project": {
"name": "bird-feeder",
"requires-python": null,
"requires-python": ">=3.12",
"optional-dependencies": null
},
"pyproject_toml": "[PYPROJECT_TOML]"
@ -861,7 +861,7 @@ mod tests {
"root": "[ROOT]/albatross-project-in-excluded/excluded/bird-feeder",
"project": {
"name": "bird-feeder",
"requires-python": null,
"requires-python": ">=3.12",
"optional-dependencies": null
},
"pyproject_toml": "[PYPROJECT_TOML]"
@ -895,7 +895,7 @@ mod tests {
"root": "[ROOT]/albatross-root-workspace",
"project": {
"name": "albatross",
"requires-python": null,
"requires-python": ">=3.12",
"optional-dependencies": null
},
"pyproject_toml": "[PYPROJECT_TOML]"
@ -904,7 +904,7 @@ mod tests {
"root": "[ROOT]/albatross-root-workspace/packages/bird-feeder",
"project": {
"name": "bird-feeder",
"requires-python": null,
"requires-python": ">=3.12",
"optional-dependencies": null
},
"pyproject_toml": "[PYPROJECT_TOML]"
@ -913,7 +913,7 @@ mod tests {
"root": "[ROOT]/albatross-root-workspace/packages/seeds",
"project": {
"name": "seeds",
"requires-python": null,
"requires-python": ">=3.12",
"optional-dependencies": null
},
"pyproject_toml": "[PYPROJECT_TOML]"
@ -953,7 +953,7 @@ mod tests {
"root": "[ROOT]/albatross-virtual-workspace/packages/albatross",
"project": {
"name": "albatross",
"requires-python": null,
"requires-python": ">=3.12",
"optional-dependencies": null
},
"pyproject_toml": "[PYPROJECT_TOML]"
@ -962,7 +962,7 @@ mod tests {
"root": "[ROOT]/albatross-virtual-workspace/packages/bird-feeder",
"project": {
"name": "bird-feeder",
"requires-python": null,
"requires-python": ">=3.12",
"optional-dependencies": null
},
"pyproject_toml": "[PYPROJECT_TOML]"
@ -971,7 +971,7 @@ mod tests {
"root": "[ROOT]/albatross-virtual-workspace/packages/seeds",
"project": {
"name": "seeds",
"requires-python": null,
"requires-python": ">=3.12",
"optional-dependencies": null
},
"pyproject_toml": "[PYPROJECT_TOML]"
@ -1005,7 +1005,7 @@ mod tests {
"root": "[ROOT]/albatross-just-project",
"project": {
"name": "albatross",
"requires-python": null,
"requires-python": ">=3.12",
"optional-dependencies": null
},
"pyproject_toml": "[PYPROJECT_TOML]"

View file

@ -209,16 +209,22 @@ impl Interpreter {
Some(ExternallyManaged { error })
}
/// Returns the Python version.
/// Returns the `python_full_version` marker corresponding to this Python version.
#[inline]
pub fn python_full_version(&self) -> &StringVersion {
self.markers.python_full_version()
}
/// Returns the full Python version.
#[inline]
pub fn python_version(&self) -> &Version {
&self.markers.python_full_version().version
}
/// Returns the `python_full_version` marker corresponding to this Python version.
/// Returns the full minor Python version.
#[inline]
pub fn python_full_version(&self) -> &StringVersion {
self.markers.python_full_version()
pub fn python_minor_version(&self) -> Version {
Version::new(self.python_version().release().iter().take(2).copied())
}
/// Return the major version of this Python version.

View file

@ -1,8 +1,10 @@
use anstream::eprint;
use anyhow::Result;
use std::borrow::Cow;
use distribution_types::{IndexLocations, UnresolvedRequirementSpecification};
use install_wheel_rs::linker::LinkMode;
use pep440_rs::{VersionSpecifier, VersionSpecifiers};
use uv_cache::Cache;
use uv_client::RegistryClientBuilder;
use uv_configuration::{
@ -91,11 +93,27 @@ pub(super) async fn do_lock(
let source_trees = vec![];
let project_name = project.project_name().clone();
// Determine the supported Python range. If no range is defined, and warn and default to the
// current minor version.
let requires_python = if let Some(requires_python) =
project.current_project().project().requires_python.as_ref()
{
Cow::Borrowed(requires_python)
} else {
let requires_python = VersionSpecifiers::from(
VersionSpecifier::greater_than_equal_version(venv.interpreter().python_minor_version()),
);
warn_user!(
"No `requires-python` field found in `{}`. Defaulting to `{requires_python}`.",
project.current_project().project().name,
);
Cow::Owned(requires_python)
};
// Determine the tags, markers, and interpreter to use for resolution.
let interpreter = venv.interpreter();
let tags = venv.interpreter().tags()?;
let markers = venv.interpreter().markers();
let requires_python = project.current_project().project().requires_python.as_ref();
// Initialize the registry client.
// TODO(zanieb): Support client options e.g. offline, tls, etc.
@ -164,7 +182,7 @@ pub(super) async fn do_lock(
interpreter,
tags,
None,
requires_python,
Some(&requires_python),
&client,
&flat_index,
&index,

View file

@ -19,6 +19,7 @@ fn lock_wheel_registry() -> Result<()> {
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["anyio==3.7.0"]
"#,
)?;
@ -41,6 +42,7 @@ fn lock_wheel_registry() -> Result<()> {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.12"
[[distribution]]
name = "anyio"
@ -143,6 +145,7 @@ fn lock_sdist_registry() -> Result<()> {
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["source-distribution==0.0.1"]
"#,
)?;
@ -165,6 +168,7 @@ fn lock_sdist_registry() -> Result<()> {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.12"
[[distribution]]
name = "project"
@ -200,6 +204,7 @@ fn lock_sdist_git() -> Result<()> {
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["uv-public-pypackage @ git+https://github.com/astral-test/uv-public-pypackage@0.0.1"]
"#,
)?;
@ -222,6 +227,7 @@ fn lock_sdist_git() -> Result<()> {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.12"
[[distribution]]
name = "project"
@ -271,6 +277,7 @@ fn lock_wheel_url() -> Result<()> {
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["anyio @ https://files.pythonhosted.org/packages/14/fd/2f20c40b45e4fb4324834aea24bd4afdf1143390242c0b33774da0e2e34f/anyio-4.3.0-py3-none-any.whl"]
"#,
)?;
@ -293,6 +300,7 @@ fn lock_wheel_url() -> Result<()> {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.12"
[[distribution]]
name = "anyio"
@ -394,6 +402,7 @@ fn lock_sdist_url() -> Result<()> {
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["anyio @ https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz"]
"#,
)?;
@ -416,6 +425,7 @@ fn lock_sdist_url() -> Result<()> {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.12"
[[distribution]]
name = "anyio"
@ -517,6 +527,7 @@ fn lock_extra() -> Result<()> {
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["anyio==3.7.0"]
[project.optional-dependencies]
@ -542,6 +553,7 @@ fn lock_extra() -> Result<()> {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.12"
[[distribution]]
name = "anyio"
@ -671,6 +683,7 @@ fn lock_preference() -> Result<()> {
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["iniconfig<2"]
"#,
)?;
@ -693,6 +706,7 @@ fn lock_preference() -> Result<()> {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.12"
[[distribution]]
name = "iniconfig"
@ -721,6 +735,7 @@ fn lock_preference() -> Result<()> {
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["iniconfig"]
"#,
)?;
@ -744,6 +759,7 @@ fn lock_preference() -> Result<()> {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.12"
[[distribution]]
name = "iniconfig"
@ -785,6 +801,7 @@ fn lock_preference() -> Result<()> {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.12"
[[distribution]]
name = "iniconfig"
@ -822,6 +839,7 @@ fn lock_git_sha() -> Result<()> {
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["uv-public-pypackage @ git+https://github.com/astral-test/uv-public-pypackage@0dacfd662c64cb4ceb16e6cf65a157a8b715b979"]
"#,
)?;
@ -844,6 +862,7 @@ fn lock_git_sha() -> Result<()> {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.12"
[[distribution]]
name = "project"
@ -876,6 +895,7 @@ fn lock_git_sha() -> Result<()> {
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["uv-public-pypackage @ git+https://github.com/astral-test/uv-public-pypackage@main"]
"#,
)?;
@ -900,6 +920,7 @@ fn lock_git_sha() -> Result<()> {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.12"
[[distribution]]
name = "project"
@ -942,6 +963,7 @@ fn lock_git_sha() -> Result<()> {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.12"
[[distribution]]
name = "project"

View file

@ -44,6 +44,7 @@ fn fork_basic() -> Result<()> {
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.8"
dependencies = [
'''fork-basic-a>=2; sys_platform == "linux"''',
'''fork-basic-a<2; sys_platform == "darwin"''',
@ -72,6 +73,7 @@ fn fork_basic() -> Result<()> {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.8"
[[distribution]]
name = "package-a"
@ -151,6 +153,7 @@ fn fork_marker_accrue() -> Result<()> {
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.8"
dependencies = [
'''fork-marker-accrue-a==1.0.0; implementation_name == "cpython"''',
'''fork-marker-accrue-b==1.0.0; implementation_name == "pypy"''',
@ -179,6 +182,7 @@ fn fork_marker_accrue() -> Result<()> {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.8"
[[distribution]]
name = "package-a"
@ -277,6 +281,7 @@ fn fork_marker_selection() -> Result<()> {
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.8"
dependencies = [
'''fork-marker-selection-a''',
'''fork-marker-selection-b>=2; sys_platform == "linux"''',
@ -306,6 +311,7 @@ fn fork_marker_selection() -> Result<()> {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.8"
[[distribution]]
name = "package-a"
@ -426,6 +432,7 @@ fn fork_marker_track() -> Result<()> {
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.8"
dependencies = [
'''fork-marker-track-a''',
'''fork-marker-track-b>=2.8; sys_platform == "linux"''',
@ -455,6 +462,7 @@ fn fork_marker_track() -> Result<()> {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.8"
[[distribution]]
name = "package-a"
@ -575,6 +583,7 @@ fn fork_non_fork_marker_transitive() -> Result<()> {
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.8"
dependencies = [
'''fork-non-fork-marker-transitive-a==1.0.0''',
'''fork-non-fork-marker-transitive-b==1.0.0''',
@ -603,6 +612,7 @@ fn fork_non_fork_marker_transitive() -> Result<()> {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.8"
[[distribution]]
name = "package-a"
@ -698,6 +708,7 @@ fn fork_non_local_fork_marker_direct() -> Result<()> {
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.8"
dependencies = [
'''fork-non-local-fork-marker-direct-a==1.0.0; sys_platform == "linux"''',
'''fork-non-local-fork-marker-direct-b==1.0.0; sys_platform == "darwin"''',
@ -771,6 +782,7 @@ fn fork_non_local_fork_marker_transitive() -> Result<()> {
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.8"
dependencies = [
'''fork-non-local-fork-marker-transitive-a==1.0.0''',
'''fork-non-local-fork-marker-transitive-b==1.0.0''',

View file

@ -1,6 +1,7 @@
[project]
name = "bird-feeder"
version = "1.0.0"
requires-python = ">=3.12"
dependencies = ["anyio>=4.3.0,<5"]
[build-system]

View file

@ -1,6 +1,7 @@
[project]
name = "albatross"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["tqdm>=4,<5"]
[build-system]

View file

@ -1,6 +1,7 @@
[project]
name = "albatross"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["tqdm>=4,<5"]
[build-system]

View file

@ -1,6 +1,7 @@
[project]
name = "bird-feeder"
version = "1.0.0"
requires-python = ">=3.12"
dependencies = ["anyio>=4.3.0,<5"]
[build-system]

View file

@ -1,6 +1,7 @@
[project]
name = "albatross"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["tqdm>=4,<5"]
[tool.uv.workspace]

View file

@ -1,6 +1,7 @@
[project]
name = "bird-feeder"
version = "1.0.0"
requires-python = ">=3.12"
dependencies = ["anyio>=4.3.0,<5", "seeds"]
[tool.uv.sources]

View file

@ -1,6 +1,7 @@
[project]
name = "seeds"
version = "1.0.0"
requires-python = ">=3.12"
dependencies = ["idna==3.6"]
[build-system]

View file

@ -1,6 +1,7 @@
[project]
name = "albatross"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["bird-feeder", "tqdm>=4,<5"]
[tool.uv.sources]

View file

@ -1,6 +1,7 @@
[project]
name = "albatross"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["bird-feeder", "tqdm>=4,<5"]
[tool.uv.sources]

View file

@ -1,6 +1,7 @@
[project]
name = "bird-feeder"
version = "1.0.0"
requires-python = ">=3.12"
dependencies = ["anyio>=4.3.0,<5", "seeds"]
[tool.uv.sources]

View file

@ -1,6 +1,7 @@
[project]
name = "seeds"
version = "1.0.0"
requires-python = ">=3.12"
dependencies = ["idna==3.6"]
[build-system]