Skip to content

Commit

Permalink
Tweak conda binary and conda env resolve
Browse files Browse the repository at this point in the history
  • Loading branch information
karthiknadig committed Apr 11, 2024
1 parent 56ef8c0 commit 47a0974
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 33 deletions.
1 change: 1 addition & 0 deletions native_locator/src/conda.rs
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ pub fn find_and_report_conda_envs() {
"python".to_string(),
])
},
Some(env.path.to_string_lossy().to_string()),
);
let message = messaging::PythonEnvironmentMessage::new(params);
messaging::send_message(message);
Expand Down
1 change: 1 addition & 0 deletions native_locator/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ mod messaging;

fn main() {
conda::find_and_report_conda_envs();
messaging::send_message(messaging::ExitMessage::new());
}
53 changes: 39 additions & 14 deletions native_locator/src/messaging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ use serde::{Deserialize, Serialize};


#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EnvManager {
executable_path: Vec<String>,
version: Option<String>,
pub executable_path: Vec<String>,
pub version: Option<String>,
}

impl EnvManager {
Expand All @@ -20,10 +21,11 @@ impl EnvManager {
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EnvManagerMessage {
jsonrpc: String,
method: String,
params: EnvManager,
pub jsonrpc: String,
pub method: String,
pub params: EnvManager,
}

impl EnvManagerMessage {
Expand All @@ -37,12 +39,14 @@ impl EnvManagerMessage {
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PythonEnvironment {
name: String,
python_executable_path: Vec<String>,
category: String,
version: Option<String>,
activated_run: Option<Vec<String>>,
pub name: String,
pub python_executable_path: Vec<String>,
pub category: String,
pub version: Option<String>,
pub activated_run: Option<Vec<String>>,
pub env_path: Option<String>,
}

impl PythonEnvironment {
Expand All @@ -52,22 +56,25 @@ impl PythonEnvironment {
category: String,
version: Option<String>,
activated_run: Option<Vec<String>>,
env_path: Option<String>,
) -> Self {
Self {
name,
python_executable_path: python_executable_path,
category,
version,
activated_run,
env_path,
}
}
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PythonEnvironmentMessage {
jsonrpc: String,
method: String,
params: PythonEnvironment,
pub jsonrpc: String,
pub method: String,
pub params: PythonEnvironment,
}

impl PythonEnvironmentMessage {
Expand All @@ -80,8 +87,26 @@ impl PythonEnvironmentMessage {
}
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExitMessage {
pub jsonrpc: String,
pub method: String,
pub params: Option<()>,
}

impl ExitMessage {
pub fn new() -> Self {
Self {
jsonrpc: "2.0".to_string(),
method: "exit".to_string(),
params: None,
}
}
}

fn send_rpc_message(message: String) -> () {
println!(
print!(
"Content-Length: {}\r\nContent-Type: application/vscode-jsonrpc; charset=utf-8\r\n\r\n{}",
message.len(),
message
Expand Down
4 changes: 3 additions & 1 deletion src/client/pythonEnvironments/base/locator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import { Event, Uri } from 'vscode';
import { IAsyncIterableIterator, iterEmpty } from '../../common/utils/async';
import { PythonEnvInfo, PythonEnvKind, PythonEnvSource } from './info';
import { PythonEnvInfo, PythonEnvKind, PythonEnvSource, PythonVersion } from './info';
import {
IPythonEnvsWatcher,
PythonEnvCollectionChangedEvent,
Expand Down Expand Up @@ -145,6 +145,8 @@ export type BasicEnvInfo = {
source?: PythonEnvSource[];
envPath?: string;
searchLocation?: Uri;
version?: PythonVersion;
name?: string;
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,19 +186,18 @@ async function resolveCondaEnv(env: BasicEnvInfo): Promise<PythonEnvInfo> {
} else {
executable = await conda.getInterpreterPathForEnvironment({ prefix: envPath });
}
const version = env.version ?? (executable ? await getPythonVersionFromPath(executable) : undefined);
const info = buildEnvInfo({
executable,
kind: PythonEnvKind.Conda,
org: AnacondaCompanyName,
location: envPath,
source: [],
version: executable ? await getPythonVersionFromPath(executable) : undefined,
version,
type: PythonEnvType.Conda,
name: env.name ?? (await conda?.getName(envPath)),
});
const name = await conda?.getName(envPath);
if (name) {
info.name = name;
}

if (env.envPath && path.basename(executable) === executable) {
// For environments without python, set ID using the predicted executable path after python is installed.
// Another alternative could've been to set ID of all conda environments to the environment path, as that
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Event } from 'vscode';
import { Event, EventEmitter } from 'vscode';
import * as ch from 'child_process';
import * as path from 'path';
import * as rpc from 'vscode-jsonrpc/node';
Expand All @@ -7,36 +7,102 @@ import { isWindows } from '../../../../common/platform/platformService';
import { IDisposable } from '../../../../common/types';
import { ILocator, BasicEnvInfo, IPythonEnvsIterator } from '../../locator';
import { PythonEnvsChangedEvent } from '../../watcher';
import { createDeferred } from '../../../../common/utils/async';
import { PythonEnvKind, PythonVersion } from '../../info';
import { Conda } from '../../../common/environmentManagers/conda';

const NATIVE_LOCATOR = isWindows()
? path.join(EXTENSION_ROOT_DIR, 'native_locator', 'bin', 'python-finder.exe')
: path.join(EXTENSION_ROOT_DIR, 'native_locator', 'bin', 'python-finder');

interface NativeEnvInfo {
name: string;
pythonExecutablePath: string[];
category: string;
version?: string;
activatedRun?: string[];
envPath?: string;
}

interface EnvManager {
executablePath: string[];
version?: string;
}

function categoryToKind(category: string): PythonEnvKind {
if (category === 'conda') {
return PythonEnvKind.Conda;
}
return PythonEnvKind.Unknown;
}

function parseVersion(version?: string): PythonVersion | undefined {
if (!version) {
return undefined;
}

try {
const [major, minor, micro] = version.split('.').map((v) => parseInt(v, 10));
return {
major,
minor,
micro,
sysVersion: version,
};
} catch {
return undefined;
}
}

export class NativeLocator implements ILocator<BasicEnvInfo>, IDisposable {
public readonly providerId: string = 'native-locator';

private readonly onChangedEmitter = new EventEmitter<PythonEnvsChangedEvent>();

private readonly disposables: IDisposable[] = [];

constructor() {
this.onChanged = this.onChangedEmitter.event;
this.disposables.push(this.onChangedEmitter);
}

public readonly onChanged: Event<PythonEnvsChangedEvent>;

public async dispose(): Promise<void> {}
public async dispose(): Promise<void> {
this.disposables.forEach((d) => d.dispose());
return Promise.resolve();
}

// eslint-disable-next-line class-methods-use-this
public iterEnvs(): IPythonEnvsIterator<BasicEnvInfo> {
const proc = ch.spawn(NATIVE_LOCATOR, [], { stdio: 'pipe' });
const envs: BasicEnvInfo[] = [];
const deferred = createDeferred<void>();
const connection = rpc.createMessageConnection(
new rpc.StreamMessageReader(proc.stdout),
new rpc.StreamMessageWriter(proc.stdin),
);
connection.onNotification('pythonEnvironment', (data: unknown) => {
console.log(data);
this.disposables.push(connection);
connection.onNotification('pythonEnvironment', (data: NativeEnvInfo) => {
envs.push({
kind: categoryToKind(data.category),
executablePath: data.pythonExecutablePath[0],
envPath: data.envPath,
version: parseVersion(data.version),
name: data.name === '' ? undefined : data.name,
});
});
connection.onNotification('envManager', (data: EnvManager) => {
Conda.setConda(data.executablePath[0]);
});
connection.onNotification('envManager', (data: unknown) => {
console.log(data);
connection.onNotification('exit', () => {
deferred.resolve();
});
connection.listen();
return {
async *[Symbol.asyncIterator]() {
yield* [];
},

const iterator = async function* (): IPythonEnvsIterator<BasicEnvInfo> {
await deferred.promise;
yield* envs;
};
return iterator();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,10 @@ export class Conda {
return Conda.condaPromise.get(shellPath);
}

public static setConda(condaPath: string): void {
Conda.condaPromise.set(undefined, Promise.resolve(new Conda(condaPath)));
}

/**
* Locates the preferred "conda" utility on this system by considering user settings,
* binaries on PATH, Python interpreters in the registry, and known install locations.
Expand Down
6 changes: 4 additions & 2 deletions src/client/pythonEnvironments/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
} from './common/externalDependencies';
import { ExtensionLocators, WatchRootsArgs, WorkspaceLocators } from './base/locators/wrappers';
import { CustomVirtualEnvironmentLocator } from './base/locators/lowLevel/customVirtualEnvLocator';
import { CondaEnvironmentLocator } from './base/locators/lowLevel/condaLocator';
// import { CondaEnvironmentLocator } from './base/locators/lowLevel/condaLocator';
import { GlobalVirtualEnvironmentLocator } from './base/locators/lowLevel/globalVirtualEnvronmentLocator';
import { PosixKnownPathsLocator } from './base/locators/lowLevel/posixKnownPathsLocator';
import { PyenvLocator } from './base/locators/lowLevel/pyenvLocator';
Expand All @@ -39,6 +39,7 @@ import { IDisposable } from '../common/types';
import { traceError } from '../logging';
import { ActiveStateLocator } from './base/locators/lowLevel/activeStateLocator';
import { CustomWorkspaceLocator } from './base/locators/lowLevel/customWorkspaceLocator';
import { NativeLocator } from './base/locators/lowLevel/nativeLocator';

const PYTHON_ENV_INFO_CACHE_KEY = 'PYTHON_ENV_INFO_CACHEv2';

Expand Down Expand Up @@ -141,10 +142,11 @@ function createNonWorkspaceLocators(ext: ExtensionState): ILocator<BasicEnvInfo>
locators.push(
// OS-independent locators go here.
new PyenvLocator(),
new CondaEnvironmentLocator(),
// new CondaEnvironmentLocator(),
new ActiveStateLocator(),
new GlobalVirtualEnvironmentLocator(),
new CustomVirtualEnvironmentLocator(),
new NativeLocator(),
);

if (getOSType() === OSType.Windows) {
Expand Down

0 comments on commit 47a0974

Please sign in to comment.