Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse env variables once and avoid unnecessary metadata reads #94

Merged
merged 16 commits into from
Jul 5, 2024
14 changes: 5 additions & 9 deletions crates/pet-conda/src/environment_locations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,8 @@ fn get_conda_environment_paths_from_known_paths(env_vars: &EnvVariables) -> Vec<
if let Ok(entries) = fs::read_dir(full_path) {
for entry in entries.filter_map(Result::ok) {
let path = entry.path();
if let Ok(meta) = fs::metadata(&path) {
if meta.is_dir() {
env_paths.push(path);
}
if path.is_dir() {
env_paths.push(path);
}
}
}
Expand All @@ -112,10 +110,8 @@ fn get_conda_environment_paths_from_additional_paths(
if let Ok(entries) = fs::read_dir(path) {
for entry in entries.filter_map(Result::ok) {
let path = entry.path();
if let Ok(meta) = fs::metadata(&path) {
if meta.is_dir() {
env_paths.push(path);
}
if path.is_dir() {
env_paths.push(path);
}
}
}
Expand Down Expand Up @@ -146,7 +142,7 @@ pub fn get_environments(conda_dir: &Path) -> Vec<PathBuf> {
}
} else if is_conda_env(conda_dir) {
envs.push(conda_dir.to_path_buf());
} else if fs::metadata(conda_dir.join("envs")).is_ok() {
} else if conda_dir.join("envs").exists() {
// This could be a directory where conda environments are stored.
// I.e. its not necessarily the root conda install directory.
// E.g. C:\Users\donjayamanne\.conda
Expand Down
7 changes: 2 additions & 5 deletions crates/pet-conda/src/environments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@ use pet_core::{
};
use pet_fs::path::{norm_case, resolve_symlink};
use pet_python_utils::executable::{find_executable, find_executables};
use std::{
fs,
path::{Path, PathBuf},
};
use std::path::{Path, PathBuf};

#[derive(Debug, Clone)]
pub struct CondaEnvironment {
Expand Down Expand Up @@ -84,7 +81,7 @@ pub fn get_conda_environment_info(
None => get_conda_installation_used_to_create_conda_env(env_path),
};
if let Some(conda_dir) = &conda_install_folder {
if fs::metadata(conda_dir).is_err() {
if !conda_dir.exists() {
warn!(
"Conda install folder {}, does not exist, hence will not be used for the Conda Env: {}",
env_path.display(),
Expand Down
8 changes: 3 additions & 5 deletions crates/pet-conda/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ fn get_conda_executable(path: &Path) -> Option<PathBuf> {

for relative_path in relative_path_to_conda_exe {
let exe = path.join(&relative_path);
if exe.metadata().is_ok() {
if exe.exists() {
return Some(exe);
}
}
Expand All @@ -49,10 +49,8 @@ pub fn find_conda_binary(env_vars: &EnvVariables) -> Option<PathBuf> {
for path in env::split_paths(&paths) {
for bin in get_conda_bin_names() {
let conda_path = path.join(bin);
if let Ok(metadata) = std::fs::metadata(&conda_path) {
if metadata.is_file() || metadata.is_symlink() {
return Some(conda_path);
}
if conda_path.is_file() || conda_path.is_symlink() {
return Some(conda_path);
}
}
}
Expand Down
15 changes: 4 additions & 11 deletions crates/pet-conda/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

use std::{
fs,
path::{Path, PathBuf},
};
use std::path::{Path, PathBuf};

/// conda-meta must exist as this contains a mandatory `history` file.
pub fn is_conda_install(path: &Path) -> bool {
(path.join("condabin").metadata().is_ok() || path.join("envs").metadata().is_ok())
&& path.join("conda-meta").metadata().is_ok()
(path.join("condabin").exists() || path.join("envs").exists())
&& path.join("conda-meta").exists()
}

/// conda-meta must exist as this contains a mandatory `history` file.
/// The root conda installation folder is also a conda environment (its the base environment).
pub fn is_conda_env(path: &Path) -> bool {
if let Ok(metadata) = fs::metadata(path.join("conda-meta")) {
metadata.is_dir()
} else {
false
}
path.join("conda-meta").is_dir()
}

/// Only used in tests, noop in production.
Expand Down
2 changes: 1 addition & 1 deletion crates/pet-core/src/os_environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::{
use log::trace;
use pet_fs::path::norm_case;

pub trait Environment {
pub trait Environment: Send + Sync {
fn get_user_home(&self) -> Option<PathBuf>;
/// Only used in tests, None in production.
#[allow(dead_code)]
Expand Down
9 changes: 8 additions & 1 deletion crates/pet-core/src/python_environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,14 @@ impl PythonEnvironment {

impl std::fmt::Display for PythonEnvironment {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
writeln!(f, "Environment ({:?})", self.kind).unwrap_or_default();
writeln!(
f,
"Environment ({})",
self.kind
.map(|v| format!("{v:?}"))
.unwrap_or("Unknown".to_string())
)
.unwrap_or_default();
if let Some(name) = &self.display_name {
writeln!(f, " Display-Name: {name}").unwrap_or_default();
}
Expand Down
12 changes: 6 additions & 6 deletions crates/pet-global-virtualenvs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ fn get_global_virtualenv_dirs(

if let Some(work_on_home) = work_on_home_env_var {
let work_on_home = norm_case(PathBuf::from(work_on_home));
if fs::metadata(&work_on_home).is_ok() {
if work_on_home.exists() {
venv_dirs.push(work_on_home);
} else if let Some(home) = &user_home {
if let Ok(work_on_home) = work_on_home.strip_prefix("~") {
let work_on_home = home.join(work_on_home);
if fs::metadata(&work_on_home).is_ok() {
if work_on_home.exists() {
venv_dirs.push(work_on_home);
}
}
Expand All @@ -28,7 +28,7 @@ fn get_global_virtualenv_dirs(

// Used by pipenv (https://github.com/pypa/pipenv/blob/main/pipenv/utils/shell.py#L184)
if let Some(xdg_data_home) = xdg_data_home.map(|d| PathBuf::from(d).join("virtualenvs")) {
if fs::metadata(&xdg_data_home).is_ok() {
if xdg_data_home.exists() {
venv_dirs.push(xdg_data_home);
}
}
Expand All @@ -41,19 +41,19 @@ fn get_global_virtualenv_dirs(
PathBuf::from(".local").join("share").join("virtualenvs"), // Used by pipenv (https://github.com/pypa/pipenv/blob/main/pipenv/utils/shell.py#L184)
] {
let venv_dir = home.join(dir);
if fs::metadata(&venv_dir).is_ok() {
if venv_dir.exists() {
venv_dirs.push(venv_dir);
}
}
if cfg!(target_os = "linux") {
// https://virtualenvwrapper.readthedocs.io/en/latest/index.html
// Default recommended location for virtualenvwrapper
let envs = PathBuf::from("Envs");
if fs::metadata(&envs).is_ok() {
if envs.exists() {
venv_dirs.push(envs);
}
let envs = PathBuf::from("envs");
if fs::metadata(&envs).is_ok() {
if envs.exists() {
venv_dirs.push(envs);
}
}
Expand Down
6 changes: 2 additions & 4 deletions crates/pet-homebrew/src/environment_locations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use crate::env_variables::EnvVariables;
use lazy_static::lazy_static;
use regex::Regex;
use std::{fs, path::PathBuf};
use std::path::PathBuf;

lazy_static! {
static ref PYTHON_VERSION: Regex =
Expand Down Expand Up @@ -41,9 +41,7 @@ pub fn get_homebrew_prefix_bin(env_vars: &EnvVariables) -> Vec<PathBuf> {
// Check the environment variables
if let Some(homebrew_prefix) = &env_vars.homebrew_prefix {
let homebrew_prefix_bin = PathBuf::from(homebrew_prefix).join("bin");
if fs::metadata(&homebrew_prefix_bin).is_ok()
&& !homebrew_prefixes.contains(&homebrew_prefix_bin)
{
if homebrew_prefix_bin.exists() && !homebrew_prefixes.contains(&homebrew_prefix_bin) {
homebrew_prefixes.push(homebrew_prefix_bin);
}
}
Expand Down
6 changes: 3 additions & 3 deletions crates/pet-pipenv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ fn get_pipenv_project_from_prefix(prefix: &Path) -> Option<PathBuf> {
let project_file = prefix.join(".project");
let contents = fs::read_to_string(project_file).ok()?;
let project_folder = norm_case(PathBuf::from(contents.trim().to_string()));
if fs::metadata(&project_folder).is_ok() {
if project_folder.exists() {
Some(project_folder)
} else {
None
Expand All @@ -46,14 +46,14 @@ fn get_pipenv_project_from_prefix(prefix: &Path) -> Option<PathBuf> {

fn is_pipenv(env: &PythonEnv, env_vars: &EnvVariables) -> bool {
if let Some(project_path) = get_pipenv_project(env) {
if fs::metadata(project_path.join(env_vars.pipenv_pipfile.clone())).is_ok() {
if project_path.join(env_vars.pipenv_pipfile.clone()).exists() {
return true;
}
}
// If we have a Pipfile, then this is a pipenv environment.
// Else likely a virtualenvwrapper or the like.
if let Some(project_path) = get_pipenv_project(env) {
fs::metadata(project_path.join(env_vars.pipenv_pipfile.clone())).is_ok()
project_path.join(env_vars.pipenv_pipfile.clone()).exists()
} else {
false
}
Expand Down
8 changes: 3 additions & 5 deletions crates/pet-pyenv/src/environment_locations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

use crate::env_variables::EnvVariables;
use pet_fs::path::norm_case;
use std::{fs, path::PathBuf};
use std::path::PathBuf;

#[cfg(windows)]
pub fn get_home_pyenv_dir(env_vars: &EnvVariables) -> Option<PathBuf> {
Expand All @@ -24,10 +24,8 @@ pub fn get_binary_from_known_paths(env_vars: &EnvVariables) -> Option<PathBuf> {
} else {
known_path.join("pyenv")
};
if let Ok(metadata) = fs::metadata(&exe) {
if metadata.is_file() {
return Some(norm_case(exe));
}
if exe.is_file() {
return Some(norm_case(exe));
}
}
None
Expand Down
8 changes: 4 additions & 4 deletions crates/pet-pyenv/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ fn get_pyenv_info(environment: &EnvVariables) -> PyEnvInfo {
};
if let Some(dir) = get_pyenv_dir(environment) {
let versions = dir.join("versions");
if fs::metadata(&versions).is_ok() {
if versions.exists() {
pyenv.versions = Some(versions);
}
let exe = dir.join("bin").join("pyenv");
if fs::metadata(&exe).is_ok() {
if exe.exists() {
pyenv.exe = Some(exe);
}
}
Expand All @@ -57,13 +57,13 @@ fn get_pyenv_info(environment: &EnvVariables) -> PyEnvInfo {
if let Some(path) = get_home_pyenv_dir(environment) {
if pyenv.exe.is_none() {
let exe = path.join("bin").join("pyenv");
if fs::metadata(&exe).is_ok() {
if exe.exists() {
pyenv.exe = Some(exe);
}
}
if pyenv.versions.is_none() {
let versions = path.join("versions");
if fs::metadata(&versions).is_ok() {
if versions.exists() {
pyenv.versions = Some(versions);
}
}
Expand Down
16 changes: 7 additions & 9 deletions crates/pet-python-utils/src/executable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub fn find_executable(env_path: &Path) -> Option<PathBuf> {
env_path.join("python3.exe"),
]
.into_iter()
.find(|path| fs::metadata(path).is_ok())
.find(|path| path.exists())
}

#[cfg(unix)]
Expand All @@ -37,7 +37,7 @@ pub fn find_executable(env_path: &Path) -> Option<PathBuf> {
env_path.join("python3"),
]
.into_iter()
.find(|path| fs::metadata(path).is_ok())
.find(|path| path.exists())
}

pub fn find_executables<T: AsRef<Path>>(env_path: T) -> Vec<PathBuf> {
Expand All @@ -48,7 +48,7 @@ pub fn find_executables<T: AsRef<Path>>(env_path: T) -> Vec<PathBuf> {
let mut python_executables = vec![];
let bin = if cfg!(windows) { "Scripts" } else { "bin" };
let mut env_path = env_path.as_ref().to_path_buf();
if env_path.join(bin).metadata().is_ok() {
if env_path.join(bin).exists() {
env_path = env_path.join(bin);
}

Expand All @@ -72,18 +72,16 @@ pub fn find_executables<T: AsRef<Path>>(env_path: T) -> Vec<PathBuf> {
// If you install [email protected], then only a python3.10 exe is created in that bin directory.
// As a compromise, we only enumerate if this is a bin directory and there are no python exes
// Else enumerating entire directories is very expensive.
if env_path.join(python_exe).metadata().is_ok()
|| env_path.join(python3_exe).metadata().is_ok()
if env_path.join(python_exe).exists()
|| env_path.join(python3_exe).exists()
|| env_path.ends_with(bin)
{
// Enumerate this directory and get all `python` & `pythonX.X` files.
if let Ok(entries) = fs::read_dir(env_path) {
for entry in entries.filter_map(Result::ok) {
let file = entry.path();
if let Ok(metadata) = fs::metadata(&file) {
if is_python_executable_name(&entry.path()) && metadata.is_file() {
python_executables.push(file);
}
if file.is_file() && is_python_executable_name(&file) {
python_executables.push(file);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/pet-python-utils/src/headers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub fn get_version(path: &Path) -> Option<String> {
let mut contents = "".to_string();
if let Ok(result) = fs::read_to_string(patchlevel_h) {
contents = result;
} else if fs::metadata(&headers_path).is_err() {
} else if !headers_path.exists() {
// TODO: Remove this check, unnecessary, as we try to read the dir below.
// Such a path does not exist, get out.
continue;
Expand Down
4 changes: 2 additions & 2 deletions crates/pet-python-utils/src/pyvenv_cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@ fn find(path: &Path) -> Option<PathBuf> {
// Check if the pyvenv.cfg file is in the current directory.
// Possible the passed value is the `env`` directory.
let cfg = path.join(PYVENV_CONFIG_FILE);
if fs::metadata(&cfg).is_ok() {
if cfg.exists() {
return Some(cfg);
}

let bin = if cfg!(windows) { "Scripts" } else { "bin" };
if path.ends_with(bin) {
let cfg = path.parent()?.join(PYVENV_CONFIG_FILE);
if fs::metadata(&cfg).is_ok() {
if cfg.exists() {
return Some(cfg);
}
}
Expand Down
5 changes: 5 additions & 0 deletions crates/pet-venv/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use std::path::Path;

use pet_core::{
python_environment::{PythonEnvironment, PythonEnvironmentBuilder, PythonEnvironmentKind},
reporter::Reporter,
Expand All @@ -20,6 +22,9 @@ fn is_venv_internal(env: &PythonEnv) -> Option<bool> {
pub fn is_venv(env: &PythonEnv) -> bool {
is_venv_internal(env).unwrap_or_default()
}
pub fn is_venv_dir(path: &Path) -> bool {
PyVenvCfg::find(path).is_some()
}
pub struct Venv {}

impl Venv {
Expand Down
5 changes: 1 addition & 4 deletions crates/pet-virtualenv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use pet_core::{
};
use pet_python_utils::version;
use pet_python_utils::{env::PythonEnv, executable::find_executables};
use std::fs;

pub fn is_virtualenv(env: &PythonEnv) -> bool {
if env.prefix.is_none() {
Expand All @@ -31,9 +30,7 @@ pub fn is_virtualenv(env: &PythonEnv) -> bool {
// const directory = path.dirname(interpreterPath);
// const files = await fsapi.readdir(directory);
// const regex = /^activate(\.([A-z]|\d)+)?$/i;
if fs::metadata(bin.join("activate")).is_ok()
|| fs::metadata(bin.join("activate.bat")).is_ok()
{
if bin.join("activate").exists() || bin.join("activate.bat").exists() {
return true;
}

Expand Down
Loading
Loading