[ty] Minor cleanup for site-packages discovery logic (#18446)
Some checks are pending
CI / cargo test (linux) (push) Blocked by required conditions
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run

This commit is contained in:
Alex Waygood 2025-06-03 19:49:14 +01:00 committed by GitHub
parent e8ea40012a
commit 0079cc6817
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -95,15 +95,13 @@ impl PythonEnvironment {
origin: SysPrefixPathOrigin, origin: SysPrefixPathOrigin,
system: &dyn System, system: &dyn System,
) -> SitePackagesDiscoveryResult<Self> { ) -> SitePackagesDiscoveryResult<Self> {
let path = SysPrefixPath::new(path, origin, system)?; let path = SysPrefixPath::new(path.as_ref(), origin, system)?;
// Attempt to inspect as a virtual environment first // Attempt to inspect as a virtual environment first
// TODO(zanieb): Consider avoiding the clone here by checking for `pyvenv.cfg` ahead-of-time match VirtualEnvironment::new(path, system) {
match VirtualEnvironment::new(path.clone(), system) {
Ok(venv) => Ok(Self::Virtual(venv)), Ok(venv) => Ok(Self::Virtual(venv)),
// If there's not a `pyvenv.cfg` marker, attempt to inspect as a system environment // If there's not a `pyvenv.cfg` marker, attempt to inspect as a system environment
// Err(SitePackagesDiscoveryError::NoPyvenvCfgFile(path, _))
Err(SitePackagesDiscoveryError::NoPyvenvCfgFile(_, _))
if !origin.must_be_virtual_env() => if !origin.must_be_virtual_env() =>
{ {
Ok(Self::System(SystemEnvironment::new(path))) Ok(Self::System(SystemEnvironment::new(path)))
@ -207,9 +205,10 @@ impl VirtualEnvironment {
let pyvenv_cfg_path = path.join("pyvenv.cfg"); let pyvenv_cfg_path = path.join("pyvenv.cfg");
tracing::debug!("Attempting to parse virtual environment metadata at '{pyvenv_cfg_path}'"); tracing::debug!("Attempting to parse virtual environment metadata at '{pyvenv_cfg_path}'");
let pyvenv_cfg = system let pyvenv_cfg = match system.read_to_string(&pyvenv_cfg_path) {
.read_to_string(&pyvenv_cfg_path) Ok(pyvenv_cfg) => pyvenv_cfg,
.map_err(|io_err| SitePackagesDiscoveryError::NoPyvenvCfgFile(path.origin, io_err))?; Err(err) => return Err(SitePackagesDiscoveryError::NoPyvenvCfgFile(path, err)),
};
let parsed_pyvenv_cfg = let parsed_pyvenv_cfg =
PyvenvCfgParser::new(&pyvenv_cfg) PyvenvCfgParser::new(&pyvenv_cfg)
@ -530,20 +529,40 @@ impl SystemEnvironment {
} }
} }
/// Enumeration of ways in which `site-packages` discovery can fail.
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub(crate) enum SitePackagesDiscoveryError { pub(crate) enum SitePackagesDiscoveryError {
/// `site-packages` discovery failed because the provided path couldn't be canonicalized.
#[error("Invalid {1}: `{0}` could not be canonicalized")] #[error("Invalid {1}: `{0}` could not be canonicalized")]
EnvDirCanonicalizationError(SystemPathBuf, SysPrefixPathOrigin, #[source] io::Error), CanonicalizationError(SystemPathBuf, SysPrefixPathOrigin, #[source] io::Error),
/// `site-packages` discovery failed because the [`SysPrefixPathOrigin`] indicated that
/// the provided path should point to `sys.prefix` directly, but the path wasn't a directory.
#[error("Invalid {1}: `{0}` does not point to a directory on disk")] #[error("Invalid {1}: `{0}` does not point to a directory on disk")]
EnvDirNotDirectory(SystemPathBuf, SysPrefixPathOrigin), SysPrefixNotADirectory(SystemPathBuf, SysPrefixPathOrigin),
#[error("{0} points to a broken venv with no pyvenv.cfg file")]
NoPyvenvCfgFile(SysPrefixPathOrigin, #[source] io::Error), /// `site-packages` discovery failed because the [`SysPrefixPathOrigin`] indicated that
/// the provided path should point to the `sys.prefix` of a virtual environment,
/// but there was no file at `<sys.prefix>/pyvenv.cfg`.
#[error("{} points to a broken venv with no pyvenv.cfg file", .0.origin)]
NoPyvenvCfgFile(SysPrefixPath, #[source] io::Error),
/// `site-packages` discovery failed because the `pyvenv.cfg` file could not be parsed.
#[error("Failed to parse the pyvenv.cfg file at {0} because {1}")] #[error("Failed to parse the pyvenv.cfg file at {0} because {1}")]
PyvenvCfgParseError(SystemPathBuf, PyvenvCfgParseErrorKind), PyvenvCfgParseError(SystemPathBuf, PyvenvCfgParseErrorKind),
/// `site-packages` discovery failed because we're on a Unix system,
/// we weren't able to figure out from the `pyvenv.cfg` file exactly where `site-packages`
/// would be relative to the `sys.prefix` path, and we tried to fallback to iterating
/// through the `<sys.prefix>/lib` directory looking for a `site-packages` directory,
/// but we came across some I/O error while trying to do so.
#[error( #[error(
"Failed to search the `lib` directory of the Python installation at {1} for `site-packages`" "Failed to iterate over the contents of the `lib` directory of the Python installation at {1}"
)] )]
CouldNotReadLibDirectory(#[source] io::Error, SysPrefixPath), CouldNotReadLibDirectory(#[source] io::Error, SysPrefixPath),
/// We looked everywhere we could think of for the `site-packages` directory,
/// but none could be found despite our best endeavours.
#[error("Could not find the `site-packages` directory for the Python installation at {0}")] #[error("Could not find the `site-packages` directory for the Python installation at {0}")]
NoSitePackagesDirFound(SysPrefixPath), NoSitePackagesDirFound(SysPrefixPath),
} }
@ -709,14 +728,6 @@ pub(crate) struct SysPrefixPath {
impl SysPrefixPath { impl SysPrefixPath {
fn new( fn new(
unvalidated_path: impl AsRef<SystemPath>,
origin: SysPrefixPathOrigin,
system: &dyn System,
) -> SitePackagesDiscoveryResult<Self> {
Self::new_impl(unvalidated_path.as_ref(), origin, system)
}
fn new_impl(
unvalidated_path: &SystemPath, unvalidated_path: &SystemPath,
origin: SysPrefixPathOrigin, origin: SysPrefixPathOrigin,
system: &dyn System, system: &dyn System,
@ -727,7 +738,7 @@ impl SysPrefixPath {
let canonicalized = system let canonicalized = system
.canonicalize_path(unvalidated_path) .canonicalize_path(unvalidated_path)
.map_err(|io_err| { .map_err(|io_err| {
SitePackagesDiscoveryError::EnvDirCanonicalizationError( SitePackagesDiscoveryError::CanonicalizationError(
unvalidated_path.to_path_buf(), unvalidated_path.to_path_buf(),
origin, origin,
io_err, io_err,
@ -740,7 +751,7 @@ impl SysPrefixPath {
origin, origin,
}) })
.ok_or_else(|| { .ok_or_else(|| {
SitePackagesDiscoveryError::EnvDirNotDirectory( SitePackagesDiscoveryError::SysPrefixNotADirectory(
unvalidated_path.to_path_buf(), unvalidated_path.to_path_buf(),
origin, origin,
) )
@ -1367,7 +1378,7 @@ mod tests {
let system = TestSystem::default(); let system = TestSystem::default();
assert!(matches!( assert!(matches!(
PythonEnvironment::new("/env", SysPrefixPathOrigin::PythonCliFlag, &system), PythonEnvironment::new("/env", SysPrefixPathOrigin::PythonCliFlag, &system),
Err(SitePackagesDiscoveryError::EnvDirCanonicalizationError(..)) Err(SitePackagesDiscoveryError::CanonicalizationError(..))
)); ));
} }
@ -1380,7 +1391,7 @@ mod tests {
.unwrap(); .unwrap();
assert!(matches!( assert!(matches!(
PythonEnvironment::new("/env", SysPrefixPathOrigin::PythonCliFlag, &system), PythonEnvironment::new("/env", SysPrefixPathOrigin::PythonCliFlag, &system),
Err(SitePackagesDiscoveryError::EnvDirNotDirectory(..)) Err(SitePackagesDiscoveryError::SysPrefixNotADirectory(..))
)); ));
} }