Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions crates/pet-poetry/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,8 @@ sha2 = "0.10.6"
base64 = "0.22.0"
toml = "0.8.14"

[dev-dependencies]
tempfile = "3.12"

[features]
ci = []
74 changes: 68 additions & 6 deletions crates/pet-poetry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ lazy_static! {
.expect("Error generating RegEx for poetry environment name pattern");
}

/// Check if a path looks like a Poetry environment by examining the directory structure
/// Poetry environments typically have names like: {name}-{hash}-py{version}
/// and are located in cache directories or as .venv in project directories
fn is_poetry_environment(path: &Path) -> bool {
/// Check if a path looks like a Poetry environment in the cache directory
/// Poetry cache environments have names like: {name}-{hash}-py{version}
/// and are located in cache directories containing "pypoetry/virtualenvs"
fn is_poetry_cache_environment(path: &Path) -> bool {
// Check if the environment is in a directory that looks like Poetry's virtualenvs cache
// Common patterns:
// - Linux: ~/.cache/pypoetry/virtualenvs/
Expand All @@ -62,6 +62,56 @@ fn is_poetry_environment(path: &Path) -> bool {
false
}

/// Check if a .venv directory is an in-project Poetry environment
/// This is for the case when virtualenvs.in-project = true is set.
/// We check if the parent directory has Poetry configuration files.
fn is_in_project_poetry_environment(path: &Path) -> bool {
// Check if this is a .venv directory
let dir_name = path
.file_name()
.and_then(|n| n.to_str())
.unwrap_or_default();
if dir_name != ".venv" {
return false;
}

// Check if the parent directory has Poetry configuration
if let Some(parent) = path.parent() {
// Check for poetry.toml - a local Poetry configuration file
// Its presence indicates this project uses Poetry
let poetry_toml = parent.join("poetry.toml");
if poetry_toml.is_file() {
trace!(
"Found in-project Poetry environment: {:?} with poetry.toml at {:?}",
path,
poetry_toml
);
return true;
}

// Check if pyproject.toml contains Poetry configuration
let pyproject_toml = parent.join("pyproject.toml");
if pyproject_toml.is_file() {
if let Ok(contents) = std::fs::read_to_string(&pyproject_toml) {
// Look for [tool.poetry] or poetry as build backend
if contents.contains("[tool.poetry]")
|| contents.contains("poetry.core.masonry.api")
|| contents.contains("poetry-core")
{
Comment on lines +95 to +100
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The string matching approach using contains() is too broad and could result in false positives. For example, this would incorrectly identify a Poetry environment if "poetry-core" appears in a comment, URL, or documentation within the pyproject.toml file.

Consider using proper TOML parsing (similar to how it's done in pyproject_toml.rs and config.rs) to check the actual structure:

  • Parse the TOML content with toml::from_str::<toml::Value>()
  • Check for tool.poetry section existence
  • Check for build-system.build-backend specifically containing "poetry.core.masonry.api"
  • Check for build-system.requires array containing items that start with "poetry-core"

This would provide more accurate detection and avoid false positives from arbitrary text content.

Copilot uses AI. Check for mistakes.
trace!(
"Found in-project Poetry environment: {:?} with pyproject.toml at {:?}",
path,
pyproject_toml
);
return true;
}
}
}
}

false
}

pub trait PoetryLocator: Send + Sync {
fn find_and_report_missing_envs(
&self,
Expand Down Expand Up @@ -203,9 +253,9 @@ impl Locator for Poetry {
// This handles cases where the environment wasn't discovered during find()
// (e.g., workspace directories not configured, or pyproject.toml not found)
if let Some(prefix) = &env.prefix {
if is_poetry_environment(prefix) {
if is_poetry_cache_environment(prefix) {
trace!(
"Identified Poetry environment by path pattern: {:?}",
"Identified Poetry environment by cache path pattern: {:?}",
prefix
);
return environment::create_poetry_env(
Expand All @@ -214,6 +264,18 @@ impl Locator for Poetry {
None, // No manager available in this fallback case
);
}

// Check for in-project .venv Poetry environment
if is_in_project_poetry_environment(prefix) {
trace!("Identified in-project Poetry environment: {:?}", prefix);
// For in-project .venv, the project directory is the parent
let project_dir = prefix.parent().unwrap_or(prefix).to_path_buf();
return environment::create_poetry_env(
prefix,
project_dir,
None, // No manager available in this fallback case
);
}
}

None
Expand Down
Loading
Loading