-
-
Notifications
You must be signed in to change notification settings - Fork 54
Description
Hi,
I recently found out that if a Pipenv environment is defined in some sub directory, Pyright would recognize it and create a workspace folder for it. For instance, with two Pipenv environments in ~/T/d1/ and ~/T/d2/, cwd at ~/T and after opening a file in each directory, the output of :CocCommand workspace.showOutput shows
Workspace: /home/user/T/d1
Using python from /usr/bin/python
[Info - ...] Pyright language server 1.1.352 starting
[Info - ...] Server root directory: file:///home/user/git/coc-pyright/node_modules/pyright/dist
[Info - ...] Starting service instance "d1"
[Info - ...] Setting pythonPath for service "d1": "/usr/bin/python"
[Info - ...] Assuming Python version 3.11
[Info - ...] Found 1 source file
[Info - ...] Starting service instance "d2"
[Info - ...] Setting pythonPath for service "d2": "/usr/bin/python"
[Info - ...] Assuming Python version 3.11
[Info - ...] Found 1 source file
This got me digging and found
Line 43 in c9b271a
| config['pythonPath'] = PythonSettings.getInstance().pythonPath; |
coc-pyright/src/configSettings.ts
Line 27 in c9b271a
| const workspaceFolder = workspace.workspaceFolders.length > 0 ? workspace.workspaceFolders[0] : undefined; |
and
coc-pyright/src/configSettings.ts
Line 77 in c9b271a
| return child_process.spawnSync('pipenv', ['--py'], { encoding: 'utf8' }).stdout.trim(); |
Their combined effect is
- Pipenv is not resolved at
this.workspaceRootlike for instance Poetry (missingcwd: ...) - All workspace folders detected by Pyright are ignored, except the first which is used regardless of context
The following patch changes the behavior such that Pyright uses the virtual environment corresponding to the workspace folder the current file is in.
diff --git a/src/configSettings.ts b/src/configSettings.ts
index 8be347d..6a5853c 100644
--- a/src/configSettings.ts
+++ b/src/configSettings.ts
@@ -18,17 +18,25 @@ export class PythonSettings implements IPythonSettings {
private _pythonPath = '';
private _stdLibs: string[] = [];
- constructor() {
+ constructor(workspaceFolder = undefined) {
+ this.workspaceFolder = workspaceFolder;
this.workspaceRoot = workspace.root ? workspace.root : __dirname;
this.initialize();
}
- public static getInstance(): PythonSettings {
- const workspaceFolder = workspace.workspaceFolders.length > 0 ? workspace.workspaceFolders[0] : undefined;
+ public static getInstance(uri: string = undefined): PythonSettings {
+ let workspaceFolder = undefined;
+ if (uri) {
+ for (const _workspaceFolder of workspace.workspaceFolders) {
+ if (_workspaceFolder.uri.startsWith('file://') && uri.startsWith(_workspaceFolder.uri + '/') && (!workspaceFolder || workspaceFolder.uri.length < _workspaceFolder.uri.length)) {
+ workspaceFolder = _workspaceFolder;
+ }
+ }
+ }
const workspaceFolderKey = workspaceFolder ? workspaceFolder.name : 'unknown';
if (!PythonSettings.pythonSettings.has(workspaceFolderKey)) {
- const settings = new PythonSettings();
+ const settings = new PythonSettings(workspaceFolder);
PythonSettings.pythonSettings.set(workspaceFolderKey, settings);
}
return PythonSettings.pythonSettings.get(workspaceFolderKey)!;
@@ -50,6 +58,8 @@ export class PythonSettings implements IPythonSettings {
return fs.existsSync(fullPath) ? fullPath : undefined;
}
+ const rootPath = this.workspaceFolder ? decodeURI(this.workspaceFolder.uri.substring(7)) : this.workspaceRoot;
+
try {
// virtualenv
if (process.env.VIRTUAL_ENV && fs.existsSync(path.join(process.env.VIRTUAL_ENV, 'pyvenv.cfg'))) {
@@ -62,7 +72,7 @@ export class PythonSettings implements IPythonSettings {
}
// `pyenv local` creates `.python-version`, but not `PYENV_VERSION`
- let p = path.join(this.workspaceRoot, '.python-version');
+ let p = path.join(rootPath, '.python-version');
if (fs.existsSync(p)) {
if (!process.env.PYENV_VERSION) {
// pyenv local can special multiple Python, use first one only
@@ -72,15 +82,15 @@ export class PythonSettings implements IPythonSettings {
}
// pipenv
- p = path.join(this.workspaceRoot, 'Pipfile');
+ p = path.join(rootPath, 'Pipfile');
if (fs.existsSync(p)) {
- return child_process.spawnSync('pipenv', ['--py'], { encoding: 'utf8' }).stdout.trim();
+ return child_process.spawnSync('pipenv', ['--py'], { encoding: 'utf8', cwd: rootPath }).stdout.trim();
}
// poetry
- p = path.join(this.workspaceRoot, 'poetry.lock');
+ p = path.join(rootPath, 'poetry.lock');
if (fs.existsSync(p)) {
- const list = child_process.spawnSync('poetry', ['env', 'list', '--full-path', '--no-ansi'], { encoding: 'utf8', cwd: this.workspaceRoot }).stdout.trim();
+ const list = child_process.spawnSync('poetry', ['env', 'list', '--full-path', '--no-ansi'], { encoding: 'utf8', cwd: rootPath }).stdout.trim();
let info = '';
for (const item of list.split('\n')) {
if (item.includes('(Activated)')) {
@@ -95,15 +105,15 @@ export class PythonSettings implements IPythonSettings {
}
// pdm
- p = path.join(this.workspaceRoot, '.pdm-python');
+ p = path.join(rootPath, '.pdm-python');
if (fs.existsSync(p)) {
- return child_process.spawnSync('pdm', ['info', '--python'], { encoding: 'utf8' }).stdout.trim();
+ return child_process.spawnSync('pdm', ['info', '--python'], { encoding: 'utf8', cwd: rootPath }).stdout.trim();
}
// virtualenv in the workspace root
- const files = fs.readdirSync(this.workspaceRoot);
+ const files = fs.readdirSync(rootPath);
for (const file of files) {
- const x = path.join(this.workspaceRoot, file);
+ const x = path.join(rootPath, file);
if (fs.existsSync(path.join(x, 'pyvenv.cfg'))) {
return pythonBinFromPath(x);
}
diff --git a/src/features/refactor.ts b/src/features/refactor.ts
index c983881..af04d0d 100644
--- a/src/features/refactor.ts
+++ b/src/features/refactor.ts
@@ -218,7 +218,7 @@ export async function extractVariable(root: string, document: TextDocument, rang
const workspaceFolder = workspace.getWorkspaceFolder(doc.uri);
const workspaceRoot = workspaceFolder ? Uri.parse(workspaceFolder.uri).fsPath : workspace.cwd;
- const pythonSettings = PythonSettings.getInstance();
+ const pythonSettings = PythonSettings.getInstance(doc.uri);
return validateDocumentForRefactor(doc).then(() => {
const newName = `newvariable${new Date().getMilliseconds().toString()}`;
const proxy = new RefactorProxy(root, pythonSettings, workspaceRoot);
@@ -243,7 +243,7 @@ export async function extractMethod(root: string, document: TextDocument, range:
const workspaceFolder = workspace.getWorkspaceFolder(doc.uri);
const workspaceRoot = workspaceFolder ? Uri.parse(workspaceFolder.uri).fsPath : workspace.cwd;
- const pythonSettings = PythonSettings.getInstance();
+ const pythonSettings = PythonSettings.getInstance(doc.uri);
return validateDocumentForRefactor(doc).then(() => {
const newName = `newmethod${new Date().getMilliseconds().toString()}`;
const proxy = new RefactorProxy(root, pythonSettings, workspaceRoot);
diff --git a/src/middleware.ts b/src/middleware.ts
index f876938..523bdc1 100644
--- a/src/middleware.ts
+++ b/src/middleware.ts
@@ -40,7 +40,7 @@ export function configuration(params: ConfigurationParams, token: CancellationTo
if (pythonItem) {
const custom = () => {
const config = toJSONObject(workspace.getConfiguration(pythonItem.section, pythonItem.scopeUri));
- config['pythonPath'] = PythonSettings.getInstance().pythonPath;
+ config['pythonPath'] = PythonSettings.getInstance(pythonItem.scopeUri + '/').pythonPath;
// expand relative path
const analysis = config['analysis'];:CocCommand workspace.showOutput:
Workspace: /home/user/T/d1
Using python from /home/user/.local/share/virtualenvs/d1-YdIZU2Ui/bin/python
[Info - ...] Pyright language server 1.1.352 starting
[Info - ...] Server root directory: file:///home/user/git/coc-pyright/node_modules/pyright/dist
[Info - ...] Starting service instance "d1"
[Info - ...] Setting pythonPath for service "d1": "/home/user/.local/share/virtualenvs/d1-YdIZU2Ui/bin/python"
[Info - ...] Assuming Python version 3.11
[Info - ...] Found 1 source file
[Info - ...] Starting service instance "d2"
[Info - ...] Setting pythonPath for service "d2": "/home/user/.local/share/virtualenvs/d2-_IcopAFM/bin/python"
[Info - ...] Assuming Python version 3.11
[Info - ...] Found 1 source file
Motivating use case: start Neovim in a directory containing multiple projects. With this patch, Pyright behaves correctly in each.
What's the output of :CocCommand pyright.version
[coc.nvim] coc-pyright 1.1.351 with Pyright 1.1.352
What's the output of :CocCommand workspace.showOutput Pyright
see above