Skip to content

Commit

Permalink
Add support for Native locator (microsoft#23435)
Browse files Browse the repository at this point in the history
  • Loading branch information
DonJayamanne authored May 15, 2024
2 parents ed61651 + ad7611b commit 889ec9d
Show file tree
Hide file tree
Showing 106 changed files with 4,137 additions and 75 deletions.
7 changes: 7 additions & 0 deletions .github/actions/build-vsix/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ runs:
node-version: ${{ inputs.node_version }}
cache: 'npm'

- name: Rust Tool Chain setup
uses: dtolnay/rust-toolchain@stable

# Jedi LS depends on dataclasses which is not in the stdlib in Python 3.7.
- name: Use Python 3.8 for JediLSP
uses: actions/setup-python@v5
Expand All @@ -44,6 +47,10 @@ runs:
run: nox --session install_python_libs
shell: bash

- name: Build Native Binaries
run: nox --session native_build
shell: bash

- name: Run npm ci
run: npm ci --prefer-offline
shell: bash
Expand Down
24 changes: 24 additions & 0 deletions .github/workflows/pr-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,30 @@ jobs:
run: npm run test:functional
if: matrix.test-suite == 'functional'

native-tests:
name: Native Tests
# The value of runs-on is the OS of the current job (specified in the strategy matrix below) instead of being hardcoded.
runs-on: ${{ matrix.os }}
defaults:
run:
working-directory: ${{ env.special-working-directory }}
strategy:
fail-fast: false
matrix:
# We're not running CI on macOS for now because it's one less matrix entry to lower the number of runners used,
# macOS runners are expensive, and we assume that Ubuntu is enough to cover the Unix case.
os: [ubuntu-latest, windows-latest]

steps:
- name: Checkout
uses: actions/checkout@v4
with:
path: ${{ env.special-working-directory-relative }}

- name: Native Locator tests
run: cargo test -- --nocapture
working-directory: ${{ env.special-working-directory }}/native_locator

smoke-tests:
name: Smoke tests
# The value of runs-on is the OS of the current job (specified in the strategy matrix below) instead of being hardcoded.
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,5 @@ dist/**
*.xlf
package.nls.*.json
l10n/
native_locator/target/**
native_locator/Cargo.lock
9 changes: 8 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
},
"editor.defaultFormatter": "charliermarsh.ruff",
},
"[rust]": {
"editor.defaultFormatter": "rust-lang.rust-analyzer",
"editor.formatOnSave": true
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
Expand Down Expand Up @@ -67,5 +71,8 @@
"python_files/tests"
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
"python.testing.pytestEnabled": true,
"rust-analyzer.linkedProjects": [
".\\native_locator\\Cargo.toml"
]
}
5 changes: 5 additions & 0 deletions .vscodeignore
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,8 @@ test/**
tmp/**
typings/**
types/**
native_locator/src/**
native_locator/tests/**
native_locator/bin/**
native_locator/target/**
native_locator/Cargo.*
19 changes: 19 additions & 0 deletions native_locator/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "python-finder"
version = "0.1.0"
edition = "2021"

[target.'cfg(windows)'.dependencies]
winreg = "0.52.0"

[dependencies]
serde = { version = "1.0.152", features = ["derive"] }
serde_json = "1.0.93"
serde_repr = "0.1.10"
regex = "1.10.4"
log = "0.4.21"
env_logger = "0.10.2"


[lib]
doctest = false
91 changes: 91 additions & 0 deletions native_locator/src/common_python.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

use crate::known::Environment;
use crate::locator::{Locator, LocatorResult};
use crate::messaging::PythonEnvironment;
use crate::utils::{self, PythonEnv};
use std::env;
use std::path::{Path, PathBuf};

fn get_env_path(python_executable_path: &PathBuf) -> Option<PathBuf> {
let parent = python_executable_path.parent()?;
if parent.file_name()? == "Scripts" {
return Some(parent.parent()?.to_path_buf());
} else {
return Some(parent.to_path_buf());
}
}

pub struct PythonOnPath<'a> {
pub environment: &'a dyn Environment,
}

impl PythonOnPath<'_> {
pub fn with<'a>(environment: &'a impl Environment) -> PythonOnPath {
PythonOnPath { environment }
}
}

impl Locator for PythonOnPath<'_> {
fn resolve(&self, env: &PythonEnv) -> Option<PythonEnvironment> {
let bin = if cfg!(windows) {
"python.exe"
} else {
"python"
};
if env.executable.file_name().unwrap().to_ascii_lowercase() != bin {
return None;
}
Some(PythonEnvironment {
display_name: None,
name: None,
python_executable_path: Some(env.executable.clone()),
version: env.version.clone(),
category: crate::messaging::PythonEnvironmentCategory::System,
env_path: env.path.clone(),
env_manager: None,
project_path: None,
python_run_command: Some(vec![env.executable.to_str().unwrap().to_string()]),
})
}

fn find(&mut self) -> Option<LocatorResult> {
let paths = self.environment.get_env_var("PATH".to_string())?;
let bin = if cfg!(windows) {
"python.exe"
} else {
"python"
};

// Exclude files from this folder, as they would have been discovered elsewhere (widows_store)
// Also the exe is merely a pointer to another file.
let home = self.environment.get_user_home()?;
let apps_path = Path::new(&home)
.join("AppData")
.join("Local")
.join("Microsoft")
.join("WindowsApps");
let mut environments: Vec<PythonEnvironment> = vec![];
env::split_paths(&paths)
.filter(|p| !p.starts_with(apps_path.clone()))
.map(|p| p.join(bin))
.filter(|p| p.exists())
.for_each(|full_path| {
let version = utils::get_version(&full_path);
let env_path = get_env_path(&full_path);
if let Some(env) = self.resolve(&PythonEnv::new(full_path, env_path, version)) {
environments.push(env);
}
});

if environments.is_empty() {
None
} else {
Some(LocatorResult {
environments,
managers: vec![],
})
}
}
}
Loading

0 comments on commit 889ec9d

Please sign in to comment.