Log Python choice in uv init (#16182)

Example output:

```
$ uv-debug init foo -v
  DEBUG uv 0.9.0+6 (d3324baf68 2025-10-08)
  DEBUG Acquired shared lock for `/home/konsti/.cache/uv`
  DEBUG No Python version file found in ancestors of working directory: /home/konsti/projects/foo
  DEBUG Checking for Python environment at: `foo/.venv`
  DEBUG Searching for default Python interpreter in managed installations or search path
  DEBUG Searching for managed installations at `/home/konsti/.local/share/uv/python`
  DEBUG Found managed installation `cpython-3.14.0-linux-x86_64-gnu`
  DEBUG Found `cpython-3.14.0-linux-x86_64-gnu` at `/home/konsti/.local/share/uv/python/cpython-3.14.0-linux-x86_64-gnu/bin/python3.14` (managed installations)
  DEBUG Using Python version `>=3.14` from default interpreter
  DEBUG `git rev-parse --is-inside-work-tree` failed but didn't contain `not a git repository` in stderr for `/home/konsti/projects/foo`
  DEBUG No Python version file found in ancestors of working directory: /home/konsti/projects/foo
  DEBUG Writing Python versions to `/home/konsti/projects/foo/.python-version`
  Initialized project `foo` at `/home/konsti/projects/foo`
  DEBUG Released lock at `/home/konsti/.cache/uv/.lock`
```

First commit is refactoring, second commit is the actual change.
This commit is contained in:
konsti 2025-10-09 15:55:22 +02:00 committed by GitHub
parent c96abc93f2
commit 40397ddb4b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -365,212 +365,20 @@ async fn init_project(
None None
}; };
// Add a `requires-python` field to the `pyproject.toml` and return the corresponding interpreter. let (requires_python, python_pin) = determine_requires_python(
let (requires_python, python_request) = if let Some(python_request) = python_request { path,
// (1) A request from the user or `.python-version` file pin_python,
// This can be arbitrary, i.e., not a version — in which case we may need to resolve the install_mirrors,
// interpreter client_builder,
match python_request { python_preference,
PythonRequest::Version(VersionRequest::MajorMinor(major, minor, variant)) => { python_downloads,
let requires_python = RequiresPython::greater_than_equal_version(&Version::new([ cache,
u64::from(major), preview,
u64::from(minor), workspace.as_ref(),
])); &reporter,
python_request,
let python_request = if pin_python { )
Some(PythonRequest::Version(VersionRequest::MajorMinor( .await?;
major, minor, variant,
)))
} else {
None
};
(requires_python, python_request)
}
PythonRequest::Version(VersionRequest::MajorMinorPatch(
major,
minor,
patch,
variant,
)) => {
let requires_python = RequiresPython::greater_than_equal_version(&Version::new([
u64::from(major),
u64::from(minor),
u64::from(patch),
]));
let python_request = if pin_python {
Some(PythonRequest::Version(VersionRequest::MajorMinorPatch(
major, minor, patch, variant,
)))
} else {
None
};
(requires_python, python_request)
}
ref python_request @ PythonRequest::Version(VersionRequest::Range(
ref specifiers,
variant,
)) => {
let requires_python = RequiresPython::from_specifiers(specifiers);
let python_request = if pin_python {
let interpreter = PythonInstallation::find_or_download(
Some(python_request),
EnvironmentPreference::OnlySystem,
python_preference,
python_downloads,
client_builder,
cache,
Some(&reporter),
install_mirrors.python_install_mirror.as_deref(),
install_mirrors.pypy_install_mirror.as_deref(),
install_mirrors.python_downloads_json_url.as_deref(),
preview,
)
.await?
.into_interpreter();
Some(PythonRequest::Version(VersionRequest::MajorMinor(
interpreter.python_major(),
interpreter.python_minor(),
variant,
)))
} else {
None
};
(requires_python, python_request)
}
python_request => {
let interpreter = PythonInstallation::find_or_download(
Some(&python_request),
EnvironmentPreference::OnlySystem,
python_preference,
python_downloads,
client_builder,
cache,
Some(&reporter),
install_mirrors.python_install_mirror.as_deref(),
install_mirrors.pypy_install_mirror.as_deref(),
install_mirrors.python_downloads_json_url.as_deref(),
preview,
)
.await?
.into_interpreter();
let requires_python =
RequiresPython::greater_than_equal_version(&interpreter.python_minor_version());
let python_request = if pin_python {
Some(PythonRequest::Version(VersionRequest::MajorMinor(
interpreter.python_major(),
interpreter.python_minor(),
PythonVariant::Default,
)))
} else {
None
};
(requires_python, python_request)
}
}
} else if let Ok(virtualenv) = PythonEnvironment::from_root(path.join(".venv"), cache) {
// (2) An existing Python environment in the target directory
debug!("Using Python version from existing virtual environment in project");
let interpreter = virtualenv.into_interpreter();
let requires_python =
RequiresPython::greater_than_equal_version(&interpreter.python_minor_version());
// Pin to the minor version.
let python_request = if pin_python {
Some(PythonRequest::Version(VersionRequest::MajorMinor(
interpreter.python_major(),
interpreter.python_minor(),
PythonVariant::Default,
)))
} else {
None
};
(requires_python, python_request)
} else if let Some(requires_python) = workspace
.as_ref()
.map(|workspace| find_requires_python(workspace, &DependencyGroupsWithDefaults::none()))
.transpose()?
.flatten()
{
// (3) `requires-python` from the workspace
debug!("Using Python version from project workspace");
let python_request = PythonRequest::Version(VersionRequest::Range(
requires_python.specifiers().clone(),
PythonVariant::Default,
));
// Pin to the minor version.
let python_request = if pin_python {
let interpreter = PythonInstallation::find_or_download(
Some(&python_request),
EnvironmentPreference::OnlySystem,
python_preference,
python_downloads,
client_builder,
cache,
Some(&reporter),
install_mirrors.python_install_mirror.as_deref(),
install_mirrors.pypy_install_mirror.as_deref(),
install_mirrors.python_downloads_json_url.as_deref(),
preview,
)
.await?
.into_interpreter();
Some(PythonRequest::Version(VersionRequest::MajorMinor(
interpreter.python_major(),
interpreter.python_minor(),
PythonVariant::Default,
)))
} else {
None
};
(requires_python, python_request)
} else {
// (4) Default to the system Python
let interpreter = PythonInstallation::find_or_download(
None,
EnvironmentPreference::OnlySystem,
python_preference,
python_downloads,
client_builder,
cache,
Some(&reporter),
install_mirrors.python_install_mirror.as_deref(),
install_mirrors.pypy_install_mirror.as_deref(),
install_mirrors.python_downloads_json_url.as_deref(),
preview,
)
.await?
.into_interpreter();
let requires_python =
RequiresPython::greater_than_equal_version(&interpreter.python_minor_version());
// Pin to the minor version.
let python_request = if pin_python {
Some(PythonRequest::Version(VersionRequest::MajorMinor(
interpreter.python_major(),
interpreter.python_minor(),
PythonVariant::Default,
)))
} else {
None
};
(requires_python, python_request)
};
project_kind.init( project_kind.init(
name, name,
@ -625,7 +433,7 @@ async fn init_project(
)?; )?;
} }
// Write .python-version if it doesn't exist in the workspace or if the version differs // Write .python-version if it doesn't exist in the workspace or if the version differs
if let Some(python_request) = python_request { if let Some(python_request) = python_pin {
if PythonVersionFile::discover(path, &VersionFileDiscoveryOptions::default()) if PythonVersionFile::discover(path, &VersionFileDiscoveryOptions::default())
.await? .await?
.filter(|file| { .filter(|file| {
@ -645,7 +453,7 @@ async fn init_project(
} }
} else { } else {
// Write .python-version if it doesn't exist in the project directory. // Write .python-version if it doesn't exist in the project directory.
if let Some(python_request) = python_request { if let Some(python_request) = python_pin {
if PythonVersionFile::discover(path, &VersionFileDiscoveryOptions::default()) if PythonVersionFile::discover(path, &VersionFileDiscoveryOptions::default())
.await? .await?
.filter(|file| file.version().is_some()) .filter(|file| file.version().is_some())
@ -663,6 +471,234 @@ async fn init_project(
Ok(()) Ok(())
} }
async fn determine_requires_python(
path: &Path,
pin_python: bool,
install_mirrors: PythonInstallMirrors,
client_builder: &BaseClientBuilder<'_>,
python_preference: PythonPreference,
python_downloads: PythonDownloads,
cache: &Cache,
preview: Preview,
workspace: Option<&Workspace>,
reporter: &PythonDownloadReporter,
python_request: Option<PythonRequest>,
) -> Result<(RequiresPython, Option<PythonRequest>)> {
// Add a `requires-python` field to the `pyproject.toml` and return the corresponding interpreter.
if let Some(python_request) = python_request {
// (1) A request from the user or `.python-version` file
// This can be arbitrary, i.e., not a version — in which case we may need to resolve the
// interpreter
let (requires_python, python_pin) = match &python_request {
PythonRequest::Version(VersionRequest::MajorMinor(major, minor, variant)) => {
let requires_python = RequiresPython::greater_than_equal_version(&Version::new([
u64::from(*major),
u64::from(*minor),
]));
let python_pin = if pin_python {
Some(PythonRequest::Version(VersionRequest::MajorMinor(
*major, *minor, *variant,
)))
} else {
None
};
(requires_python, python_pin)
}
PythonRequest::Version(VersionRequest::MajorMinorPatch(
major,
minor,
patch,
variant,
)) => {
let requires_python = RequiresPython::greater_than_equal_version(&Version::new([
u64::from(*major),
u64::from(*minor),
u64::from(*patch),
]));
let python_pin = if pin_python {
Some(PythonRequest::Version(VersionRequest::MajorMinorPatch(
*major, *minor, *patch, *variant,
)))
} else {
None
};
(requires_python, python_pin)
}
python_request @ PythonRequest::Version(VersionRequest::Range(specifiers, variant)) => {
let requires_python = RequiresPython::from_specifiers(specifiers);
let python_pin = if pin_python {
let interpreter = PythonInstallation::find_or_download(
Some(python_request),
EnvironmentPreference::OnlySystem,
python_preference,
python_downloads,
client_builder,
cache,
Some(reporter),
install_mirrors.python_install_mirror.as_deref(),
install_mirrors.pypy_install_mirror.as_deref(),
install_mirrors.python_downloads_json_url.as_deref(),
preview,
)
.await?
.into_interpreter();
Some(PythonRequest::Version(VersionRequest::MajorMinor(
interpreter.python_major(),
interpreter.python_minor(),
*variant,
)))
} else {
None
};
(requires_python, python_pin)
}
python_request => {
let interpreter = PythonInstallation::find_or_download(
Some(python_request),
EnvironmentPreference::OnlySystem,
python_preference,
python_downloads,
client_builder,
cache,
Some(reporter),
install_mirrors.python_install_mirror.as_deref(),
install_mirrors.pypy_install_mirror.as_deref(),
install_mirrors.python_downloads_json_url.as_deref(),
preview,
)
.await?
.into_interpreter();
let requires_python =
RequiresPython::greater_than_equal_version(&interpreter.python_minor_version());
let python_pin = if pin_python {
Some(PythonRequest::Version(VersionRequest::MajorMinor(
interpreter.python_major(),
interpreter.python_minor(),
PythonVariant::Default,
)))
} else {
None
};
(requires_python, python_pin)
}
};
debug!("Using Python version `{requires_python}` from request `{python_request}`");
Ok((requires_python, python_pin))
} else if let Ok(virtualenv) = PythonEnvironment::from_root(path.join(".venv"), cache) {
// (2) An existing Python environment in the target directory
let interpreter = virtualenv.into_interpreter();
let requires_python =
RequiresPython::greater_than_equal_version(&interpreter.python_minor_version());
// Pin to the minor version.
let python_pin = if pin_python {
Some(PythonRequest::Version(VersionRequest::MajorMinor(
interpreter.python_major(),
interpreter.python_minor(),
PythonVariant::Default,
)))
} else {
None
};
debug!(
"Using Python version `{requires_python}` from existing virtual environment in project"
);
Ok((requires_python, python_pin))
} else if let Some(requires_python) = workspace
.as_ref()
.map(|workspace| find_requires_python(workspace, &DependencyGroupsWithDefaults::none()))
.transpose()?
.flatten()
{
// (3) `requires-python` from the workspace
let python_request = PythonRequest::Version(VersionRequest::Range(
requires_python.specifiers().clone(),
PythonVariant::Default,
));
// Pin to the minor version.
let python_pin = if pin_python {
let interpreter = PythonInstallation::find_or_download(
Some(&python_request),
EnvironmentPreference::OnlySystem,
python_preference,
python_downloads,
client_builder,
cache,
Some(reporter),
install_mirrors.python_install_mirror.as_deref(),
install_mirrors.pypy_install_mirror.as_deref(),
install_mirrors.python_downloads_json_url.as_deref(),
preview,
)
.await?
.into_interpreter();
Some(PythonRequest::Version(VersionRequest::MajorMinor(
interpreter.python_major(),
interpreter.python_minor(),
PythonVariant::Default,
)))
} else {
None
};
debug!("Using Python version `{requires_python}` from project workspace");
Ok((requires_python, python_pin))
} else {
// (4) Default to the system Python
let interpreter = PythonInstallation::find_or_download(
None,
EnvironmentPreference::OnlySystem,
python_preference,
python_downloads,
client_builder,
cache,
Some(reporter),
install_mirrors.python_install_mirror.as_deref(),
install_mirrors.pypy_install_mirror.as_deref(),
install_mirrors.python_downloads_json_url.as_deref(),
preview,
)
.await?
.into_interpreter();
let requires_python =
RequiresPython::greater_than_equal_version(&interpreter.python_minor_version());
// Pin to the minor version.
let python_pin = if pin_python {
Some(PythonRequest::Version(VersionRequest::MajorMinor(
interpreter.python_major(),
interpreter.python_minor(),
PythonVariant::Default,
)))
} else {
None
};
debug!("Using Python version `{requires_python}` from default interpreter");
Ok((requires_python, python_pin))
}
}
/// The kind of entity to initialize (either a PEP 723 script or a Python project). /// The kind of entity to initialize (either a PEP 723 script or a Python project).
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub(crate) enum InitKind { pub(crate) enum InitKind {