Skip to content

Commit 0079cc6

Browse files
authored
[ty] Minor cleanup for site-packages discovery logic (#18446)
1 parent e8ea400 commit 0079cc6

File tree

1 file changed

+36
-25
lines changed

1 file changed

+36
-25
lines changed

crates/ty_python_semantic/src/site_packages.rs

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -95,15 +95,13 @@ impl PythonEnvironment {
9595
origin: SysPrefixPathOrigin,
9696
system: &dyn System,
9797
) -> SitePackagesDiscoveryResult<Self> {
98-
let path = SysPrefixPath::new(path, origin, system)?;
98+
let path = SysPrefixPath::new(path.as_ref(), origin, system)?;
9999

100100
// Attempt to inspect as a virtual environment first
101-
// TODO(zanieb): Consider avoiding the clone here by checking for `pyvenv.cfg` ahead-of-time
102-
match VirtualEnvironment::new(path.clone(), system) {
101+
match VirtualEnvironment::new(path, system) {
103102
Ok(venv) => Ok(Self::Virtual(venv)),
104103
// If there's not a `pyvenv.cfg` marker, attempt to inspect as a system environment
105-
//
106-
Err(SitePackagesDiscoveryError::NoPyvenvCfgFile(_, _))
104+
Err(SitePackagesDiscoveryError::NoPyvenvCfgFile(path, _))
107105
if !origin.must_be_virtual_env() =>
108106
{
109107
Ok(Self::System(SystemEnvironment::new(path)))
@@ -207,9 +205,10 @@ impl VirtualEnvironment {
207205
let pyvenv_cfg_path = path.join("pyvenv.cfg");
208206
tracing::debug!("Attempting to parse virtual environment metadata at '{pyvenv_cfg_path}'");
209207

210-
let pyvenv_cfg = system
211-
.read_to_string(&pyvenv_cfg_path)
212-
.map_err(|io_err| SitePackagesDiscoveryError::NoPyvenvCfgFile(path.origin, io_err))?;
208+
let pyvenv_cfg = match system.read_to_string(&pyvenv_cfg_path) {
209+
Ok(pyvenv_cfg) => pyvenv_cfg,
210+
Err(err) => return Err(SitePackagesDiscoveryError::NoPyvenvCfgFile(path, err)),
211+
};
213212

214213
let parsed_pyvenv_cfg =
215214
PyvenvCfgParser::new(&pyvenv_cfg)
@@ -530,20 +529,40 @@ impl SystemEnvironment {
530529
}
531530
}
532531

532+
/// Enumeration of ways in which `site-packages` discovery can fail.
533533
#[derive(Debug, thiserror::Error)]
534534
pub(crate) enum SitePackagesDiscoveryError {
535+
/// `site-packages` discovery failed because the provided path couldn't be canonicalized.
535536
#[error("Invalid {1}: `{0}` could not be canonicalized")]
536-
EnvDirCanonicalizationError(SystemPathBuf, SysPrefixPathOrigin, #[source] io::Error),
537+
CanonicalizationError(SystemPathBuf, SysPrefixPathOrigin, #[source] io::Error),
538+
539+
/// `site-packages` discovery failed because the [`SysPrefixPathOrigin`] indicated that
540+
/// the provided path should point to `sys.prefix` directly, but the path wasn't a directory.
537541
#[error("Invalid {1}: `{0}` does not point to a directory on disk")]
538-
EnvDirNotDirectory(SystemPathBuf, SysPrefixPathOrigin),
539-
#[error("{0} points to a broken venv with no pyvenv.cfg file")]
540-
NoPyvenvCfgFile(SysPrefixPathOrigin, #[source] io::Error),
542+
SysPrefixNotADirectory(SystemPathBuf, SysPrefixPathOrigin),
543+
544+
/// `site-packages` discovery failed because the [`SysPrefixPathOrigin`] indicated that
545+
/// the provided path should point to the `sys.prefix` of a virtual environment,
546+
/// but there was no file at `<sys.prefix>/pyvenv.cfg`.
547+
#[error("{} points to a broken venv with no pyvenv.cfg file", .0.origin)]
548+
NoPyvenvCfgFile(SysPrefixPath, #[source] io::Error),
549+
550+
/// `site-packages` discovery failed because the `pyvenv.cfg` file could not be parsed.
541551
#[error("Failed to parse the pyvenv.cfg file at {0} because {1}")]
542552
PyvenvCfgParseError(SystemPathBuf, PyvenvCfgParseErrorKind),
553+
554+
/// `site-packages` discovery failed because we're on a Unix system,
555+
/// we weren't able to figure out from the `pyvenv.cfg` file exactly where `site-packages`
556+
/// would be relative to the `sys.prefix` path, and we tried to fallback to iterating
557+
/// through the `<sys.prefix>/lib` directory looking for a `site-packages` directory,
558+
/// but we came across some I/O error while trying to do so.
543559
#[error(
544-
"Failed to search the `lib` directory of the Python installation at {1} for `site-packages`"
560+
"Failed to iterate over the contents of the `lib` directory of the Python installation at {1}"
545561
)]
546562
CouldNotReadLibDirectory(#[source] io::Error, SysPrefixPath),
563+
564+
/// We looked everywhere we could think of for the `site-packages` directory,
565+
/// but none could be found despite our best endeavours.
547566
#[error("Could not find the `site-packages` directory for the Python installation at {0}")]
548567
NoSitePackagesDirFound(SysPrefixPath),
549568
}
@@ -709,14 +728,6 @@ pub(crate) struct SysPrefixPath {
709728

710729
impl SysPrefixPath {
711730
fn new(
712-
unvalidated_path: impl AsRef<SystemPath>,
713-
origin: SysPrefixPathOrigin,
714-
system: &dyn System,
715-
) -> SitePackagesDiscoveryResult<Self> {
716-
Self::new_impl(unvalidated_path.as_ref(), origin, system)
717-
}
718-
719-
fn new_impl(
720731
unvalidated_path: &SystemPath,
721732
origin: SysPrefixPathOrigin,
722733
system: &dyn System,
@@ -727,7 +738,7 @@ impl SysPrefixPath {
727738
let canonicalized = system
728739
.canonicalize_path(unvalidated_path)
729740
.map_err(|io_err| {
730-
SitePackagesDiscoveryError::EnvDirCanonicalizationError(
741+
SitePackagesDiscoveryError::CanonicalizationError(
731742
unvalidated_path.to_path_buf(),
732743
origin,
733744
io_err,
@@ -740,7 +751,7 @@ impl SysPrefixPath {
740751
origin,
741752
})
742753
.ok_or_else(|| {
743-
SitePackagesDiscoveryError::EnvDirNotDirectory(
754+
SitePackagesDiscoveryError::SysPrefixNotADirectory(
744755
unvalidated_path.to_path_buf(),
745756
origin,
746757
)
@@ -1367,7 +1378,7 @@ mod tests {
13671378
let system = TestSystem::default();
13681379
assert!(matches!(
13691380
PythonEnvironment::new("/env", SysPrefixPathOrigin::PythonCliFlag, &system),
1370-
Err(SitePackagesDiscoveryError::EnvDirCanonicalizationError(..))
1381+
Err(SitePackagesDiscoveryError::CanonicalizationError(..))
13711382
));
13721383
}
13731384

@@ -1380,7 +1391,7 @@ mod tests {
13801391
.unwrap();
13811392
assert!(matches!(
13821393
PythonEnvironment::new("/env", SysPrefixPathOrigin::PythonCliFlag, &system),
1383-
Err(SitePackagesDiscoveryError::EnvDirNotDirectory(..))
1394+
Err(SitePackagesDiscoveryError::SysPrefixNotADirectory(..))
13841395
));
13851396
}
13861397

0 commit comments

Comments
 (0)