Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Add Pixi locator #172

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

Merged
merged 5 commits into from
Dec 9, 2024
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
5 changes: 4 additions & 1 deletion .devcontainer/linux-homebrew/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ RUN curl https://raw.githubusercontent.com/DonJayamanne/vscode-jupyter/container
RUN echo "# To customize prompt, run `p10k configure` or edit ~/.p10k.zsh." >> ~/.zshrc
RUN echo "[[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh" >> ~/.zshrc

# Install Pythone
# Install Python
# homebrew/brew:4.4.6 broke running `brew install` as root.
# As a workaround, running `brew update` and ignoring errors coming from it fixes `brew install`.
RUN brew update || true
RUN brew install [email protected] [email protected]

# Install Rust
Expand Down
23 changes: 22 additions & 1 deletion .github/workflows/pr-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,23 @@ jobs:

# endregion venv

# region Pixi
- name: Install Pixi
uses: prefix-dev/[email protected]
with:
run-install: false

- name: Create Pixi environments
run: |
pixi init
pixi add python
pixi add --feature dev python
pixi project environment add --feature dev dev
pixi install --environment dev
shell: bash

# endregion Pixi

# Rust
- name: Rust Tool Chain setup
uses: dtolnay/rust-toolchain@stable
Expand Down Expand Up @@ -395,7 +412,11 @@ jobs:
# Homebrew
- name: Homebrew Python
if: startsWith( matrix.image, 'homebrew')
run: brew install [email protected] [email protected]
run: |
# homebrew/brew:4.4.6 broke running `brew install` as root.
# As a workaround, running `brew update` and ignoring errors coming from it fixes `brew install`.
brew update || true
brew install [email protected] [email protected]
shell: bash

# Rust
Expand Down
12 changes: 12 additions & 0 deletions Cargo.lock

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

5 changes: 0 additions & 5 deletions crates/pet-conda/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,6 @@
- Thus using the `history` file we can find the conda installation folder.
This is useful in cases where conda environments are created using `-p` option.

## Known issues

- Note: pixi seems to use conda envs internall, hence its possible to falsely identify a pixi env as a conda env.
- However pixi is not supported by this tool, hence thats not a concern.

## Miscellanous

- What if conda is installed in some custom locations that we have no idea about?
Expand Down
1 change: 1 addition & 0 deletions crates/pet-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub enum LocatorKind {
MacPythonOrg,
MacXCode,
PipEnv,
Pixi,
Poetry,
PyEnv,
Venv,
Expand Down
1 change: 1 addition & 0 deletions crates/pet-core/src/python_environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::{arch::Architecture, manager::EnvManager};
#[derive(Parser, ValueEnum, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum PythonEnvironmentKind {
Conda,
Pixi,
Homebrew,
Pyenv,
GlobalPaths, // Python found in global locations like PATH, /usr/bin etc.
Expand Down
13 changes: 13 additions & 0 deletions crates/pet-pixi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "pet-pixi"
version = "0.1.0"
edition = "2021"

[target.'cfg(target_os = "windows")'.dependencies]
msvc_spectre_libs = { version = "0.1.1", features = ["error"] }

[dependencies]
pet-conda = { path = "../pet-conda" }
pet-core = { path = "../pet-core" }
pet-python-utils = { path = "../pet-python-utils" }
log = "0.4.21"
11 changes: 11 additions & 0 deletions crates/pet-pixi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Pixi

## Notes

- Pixi environments are detected by:
- Searching for Python interpreters in `.pixi/envs` subdirectories within workspace folders
- Checking for a `conda-meta/pixi` file in potential Pixi environment directories (`.pixi/envs/{env_name}`)
- Determining the version of the Python interpreter from the `conda-meta/python-{version}.json` file

This process ensures fast detection without spawning processes.
Note that the Pixi locator should run before Conda since Conda could incorrectly identify Pixi environments as Conda environments.
87 changes: 87 additions & 0 deletions crates/pet-pixi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

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

use pet_conda::package::{CondaPackageInfo, Package};
use pet_core::{
env::PythonEnv,
python_environment::{PythonEnvironment, PythonEnvironmentBuilder, PythonEnvironmentKind},
reporter::Reporter,
Locator, LocatorKind,
};
use pet_python_utils::executable::find_executables;

pub fn is_pixi_env(path: &Path) -> bool {
path.join("conda-meta").join("pixi").is_file()
}

fn get_pixi_prefix(env: &PythonEnv) -> Option<PathBuf> {
env.prefix.clone().or_else(|| {
env.executable.parent().and_then(|parent_dir| {
if is_pixi_env(parent_dir) {
Some(parent_dir.to_path_buf())
} else if parent_dir.ends_with("bin") || parent_dir.ends_with("Scripts") {
parent_dir
.parent()
.filter(|parent| is_pixi_env(parent))
.map(|parent| parent.to_path_buf())
} else {
None
}
})
})
}

pub struct Pixi {}

impl Pixi {
pub fn new() -> Pixi {
Pixi {}
}
}
impl Default for Pixi {
fn default() -> Self {
Self::new()
}
}

impl Locator for Pixi {
fn get_kind(&self) -> LocatorKind {
LocatorKind::Pixi
}
fn supported_categories(&self) -> Vec<PythonEnvironmentKind> {
vec![PythonEnvironmentKind::Pixi]
}

fn try_from(&self, env: &PythonEnv) -> Option<PythonEnvironment> {
get_pixi_prefix(env).and_then(|prefix| {
if !is_pixi_env(&prefix) {
return None;
}

let name = prefix
.file_name()
.and_then(|name| name.to_str())
.unwrap_or_default()
.to_string();

let symlinks = find_executables(&prefix);

let version = CondaPackageInfo::from(&prefix, &Package::Python)
.map(|package_info| package_info.version);

Some(
PythonEnvironmentBuilder::new(Some(PythonEnvironmentKind::Pixi))
.executable(Some(env.executable.clone()))
.name(Some(name))
.prefix(Some(prefix))
.symlinks(Some(symlinks))
.version(version)
.build(),
)
})
}

fn find(&self, _reporter: &dyn Reporter) {}
}
1 change: 1 addition & 0 deletions crates/pet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pet-homebrew = { path = "../pet-homebrew" }
[dependencies]
pet-core = { path = "../pet-core" }
pet-conda = { path = "../pet-conda" }
pet-pixi = { path = "../pet-pixi" }
pet-jsonrpc = { path = "../pet-jsonrpc" }
pet-fs = { path = "../pet-fs" }
pet-pyenv = { path = "../pet-pyenv" }
Expand Down
26 changes: 14 additions & 12 deletions crates/pet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,53 +66,55 @@ It could have been created by other tools like `poetry`, `pipenv`, etc. Hence we

4. Homebrew
These are always stored in a custom (global) lcoation.
5. Conda

5. Pixi
Copy link
Collaborator

Choose a reason for hiding this comment

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

Awesome,

Pixi should run before Conda as its environments could be falsely identified as Conda environments.

6. Conda
- These are always stored in a custom (global) location.

- At this stage Conda envs cannot be treated as anything else
- Note: pixi seems to use conda envs internall, hence its possible to falsely identify a pixi env as a conda env.
- However pixi is not supported by this tool, hence thats not a concern.

6. Poetry
7. Poetry

- These are always stored in a custom (global) location.
- Environments are created with a special name based on the hash of the project folder.
- This needs to happen before others as these environments are general virtual environments.

7. Pipenv
8. Pipenv

- These are always stored in a custom (global) location.
- This needs to happen before others as these environments are general virtual environments.

8. Virtualenvwrapper
9. Virtualenvwrapper

- These are always stored in a custom (global) location.
- This needs to happen before others as these environments are general virtual environments.

9. Vevn
10. Vevn

- A virtual environment that has a `pyvenv.cfg` file.
- Note, this is a fallback for all other virtual environments.

10. Virtualenv
11. Virtualenv

- A virtual environment that does not have a `pyvenv.cfg` file but has activation scripts such as `/bin/activate` or `Scripts/activate.bat` or the like.
- Note, this is a fallback for all other environments.
- This must happen after conda env discovery, as conda envs have activation scripts as well.

11. Mac XCode
12. Mac XCode

- These are always stored in a custom (global) location.

12. Mac Command Line Tools
13. Mac Command Line Tools

- These are always stored in a custom (global) location.

13. Mac Python Org
14. Mac Python Org

- These are always stored in a custom (global) location.

14. Linux Python
15. Linux Python

- These are always stored in a custom (global) location.
</details>
18 changes: 16 additions & 2 deletions crates/pet/src/find.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use pet_core::reporter::Reporter;
use pet_core::{Configuration, Locator, LocatorKind};
use pet_env_var_path::get_search_paths_from_env_variables;
use pet_global_virtualenvs::list_global_virtual_envs_paths;
use pet_pixi::is_pixi_env;
use pet_python_utils::executable::{
find_executable, find_executables, should_search_for_environments_in_path,
};
Expand Down Expand Up @@ -254,7 +255,7 @@ pub fn find_python_environments_in_workspace_folder_recursive(
environment_directories: &[PathBuf],
) {
// When searching in a directory, give preference to some paths.
let paths_to_search_first = vec![
let mut paths_to_search_first = vec![
// Possible this is a virtual env
workspace_folder.to_path_buf(),
// Optimize for finding these first.
Expand All @@ -264,6 +265,15 @@ pub fn find_python_environments_in_workspace_folder_recursive(
workspace_folder.join("venv"),
];

// Add all subdirectories of .pixi/envs/**
if let Ok(reader) = fs::read_dir(workspace_folder.join(".pixi").join("envs")) {
reader
.filter_map(Result::ok)
.filter(|d| d.file_type().is_ok_and(|f| f.is_dir()))
.map(|p| p.path())
.for_each(|p| paths_to_search_first.push(p));
}

// Possible this is an environment.
find_python_environments_in_paths_with_locators(
paths_to_search_first.clone(),
Expand All @@ -274,7 +284,11 @@ pub fn find_python_environments_in_workspace_folder_recursive(
);

// If this is a virtual env folder, no need to scan this.
if is_virtualenv_dir(workspace_folder) || is_conda_env(workspace_folder) {
// Note: calling is_pixi_env after is_conda_env is redundant but kept for consistency.
if is_virtualenv_dir(workspace_folder)
|| is_conda_env(workspace_folder)
|| is_pixi_env(workspace_folder)
{
return;
}
if let Ok(reader) = fs::read_dir(workspace_folder) {
Expand Down
16 changes: 10 additions & 6 deletions crates/pet/src/locators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use pet_mac_commandlinetools::MacCmdLineTools;
use pet_mac_python_org::MacPythonOrg;
use pet_mac_xcode::MacXCode;
use pet_pipenv::PipEnv;
use pet_pixi::Pixi;
use pet_poetry::Poetry;
use pet_pyenv::PyEnv;
use pet_python_utils::env::ResolvedPythonEnv;
Expand Down Expand Up @@ -48,10 +49,13 @@ pub fn create_locators(
// 3. Pyenv Python
locators.push(Arc::new(PyEnv::from(environment, conda_locator.clone())));

// 4. Conda Python
// 4. Pixi
locators.push(Arc::new(Pixi::new()));

// 5. Conda Python
locators.push(conda_locator);

// 5. Support for Virtual Envs
// 6. Support for Virtual Envs
// The order of these matter.
// Basically PipEnv is a superset of VirtualEnvWrapper, which is a superset of Venv, which is a superset of VirtualEnv.
locators.push(poetry_locator);
Expand All @@ -61,7 +65,7 @@ pub fn create_locators(
// VirtualEnv is the most generic, hence should be the last.
locators.push(Arc::new(VirtualEnv::new()));

// 6. Homebrew Python
// 7. Homebrew Python
if cfg!(unix) {
#[cfg(unix)]
use pet_homebrew::Homebrew;
Expand All @@ -71,14 +75,14 @@ pub fn create_locators(
locators.push(Arc::new(homebrew_locator));
}

// 7. Global Mac Python
// 8. CommandLineTools Python & xcode
// 8. Global Mac Python
// 9. CommandLineTools Python & xcode
if std::env::consts::OS == "macos" {
locators.push(Arc::new(MacXCode::new()));
locators.push(Arc::new(MacCmdLineTools::new()));
locators.push(Arc::new(MacPythonOrg::new()));
}
// 9. Global Linux Python
// 10. Global Linux Python
// All other Linux (not mac, & not windows)
// THIS MUST BE LAST
if std::env::consts::OS != "macos" && std::env::consts::OS != "windows" {
Expand Down
1 change: 1 addition & 0 deletions docs/JSONRPC.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ interface ResolveParams {

enum PythonEnvironmentKind {
Conda,
Pixi,
Homebrew,
Pyenv,
GlobalPaths, // Python found in global locations like PATH, /usr/bin etc.
Expand Down
Loading