Skip to content

Commit 9f6fb22

Browse files
author
Kartik Raj
committed
Code reviews
1 parent 3822413 commit 9f6fb22

File tree

1 file changed

+44
-44
lines changed

1 file changed

+44
-44
lines changed

src/client/pythonEnvironments/common/environmentIdentifier.ts

Lines changed: 44 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import * as fsapi from 'fs-extra';
55
import * as path from 'path';
66
import { traceWarning } from '../../common/logger';
7-
import { Resource } from '../../common/types';
87
import { createDeferred } from '../../common/utils/async';
98
import { getEnvironmentVariable } from '../../common/utils/platform';
109
import { EnvironmentType } from '../info';
@@ -17,6 +16,10 @@ function pathExists(absPath: string): Promise<boolean> {
1716
return deferred.promise;
1817
}
1918

19+
function readFile(filePath: string): Promise<string> {
20+
return fsapi.readFile(filePath, 'utf-8');
21+
}
22+
2023
/**
2124
* Checks if the given interpreter path belongs to a conda environment. Using
2225
* known folder layout, and presence of 'conda-meta' directory.
@@ -128,23 +131,22 @@ async function isWindowsStoreEnvironment(interpreterPath: string): Promise<boole
128131
* @param cwd the directory to look into
129132
* @param lookIntoParentDirectories set to true if we should also search for Pipfile in parent directory
130133
*/
131-
function checkIfPipFileExists(cwd: string, lookIntoParentDirectories: boolean): boolean {
132-
const pipFileName = process.env.PIPENV_PIPFILE;
134+
async function checkIfPipFileExists(cwd: string, lookIntoParentDirectories: boolean): Promise<boolean> {
135+
const pipFileName = getEnvironmentVariable('PIPENV_PIPFILE') || 'Pipfile';
133136
let depthToSearch = 1;
134137
if (lookIntoParentDirectories) {
135138
// PIPENV_MAX_DEPTH tells pipenv the maximum number of directories to recursively search for
136139
// a Pipfile, defaults to 3: https://pipenv.pypa.io/en/latest/advanced/#pipenv.environments.PIPENV_MAX_DEPTH
137-
if (process.env.PIPENV_MAX_DEPTH) {
138-
depthToSearch = +process.env.PIPENV_MAX_DEPTH;
140+
const maxDepth = getEnvironmentVariable('PIPENV_MAX_DEPTH');
141+
if (maxDepth) {
142+
depthToSearch = +maxDepth;
139143
} else {
140144
depthToSearch = 3;
141145
}
142146
}
143147
while (depthToSearch > 0 && cwd === path.dirname(cwd)) {
144-
if (typeof pipFileName === 'string' && pathExists(path.join(cwd, pipFileName))) {
145-
return true;
146-
}
147-
if (pathExists(path.join(cwd, 'Pipfile'))) {
148+
// eslint-disable-next-line no-await-in-loop
149+
if (await pathExists(path.join(cwd, pipFileName))) {
148150
return true;
149151
}
150152
cwd = path.dirname(cwd);
@@ -157,74 +159,72 @@ function checkIfPipFileExists(cwd: string, lookIntoParentDirectories: boolean):
157159
* Returns true if interpreter path belongs to a pipenv environment which is located inside a project, false otherwise.
158160
* @param interpreterPath Absolute path to any python interpreter.
159161
*/
160-
function isLocalPipenvEnvironment(interpreterPath: string): boolean {
162+
async function isLocalPipenvEnvironment(interpreterPath: string): Promise<boolean> {
161163
// Local pipenv environments are created by setting PIPENV_VENV_IN_PROJECT to 1, which always names the environment
162164
// folder '.venv': https://pipenv.pypa.io/en/latest/advanced/#pipenv.environments.PIPENV_VENV_IN_PROJECT
163165
// This is the layout we wish to verify.
164166
// project
165167
// |__ Pipfile <--- check if Pipfile exists here
166-
// |__ .venv
168+
// |__ .venv <--- check if name of the folder is '.venv'
167169
// |__ Scripts/bin
168170
// |__ python <--- interpreterPath
169171
const venvFolderName = path.basename(path.dirname(path.dirname(interpreterPath)));
170172
if (venvFolderName !== '.venv') {
171173
return false;
172174
}
173175
const directoryWhereVenvResides = path.dirname(path.dirname(path.dirname(interpreterPath)));
174-
if (checkIfPipFileExists(directoryWhereVenvResides, false)) {
176+
if (await checkIfPipFileExists(directoryWhereVenvResides, false)) {
175177
// The directory must contain a Pipfile
176178
return true;
177179
}
178180
return false;
179181
}
180182

181183
/**
182-
* Returns true if interpreter path belongs to a global pipenv environment created for a particular project,
183-
* false otherwise.
184+
* Returns true if interpreter path belongs to a global pipenv environment, false otherwise.
184185
* @param interpreterPath Absolute path to any python interpreter.
185-
* @param project Absolute path to the project.
186186
*/
187-
async function isGlobalPipenvEnvironmentRelatedToProject(interpreterPath: string, project: string): Promise<boolean> {
187+
async function isGlobalPipenvEnvironment(interpreterPath: string): Promise<boolean> {
188+
// Global pipenv environments have a .project file with the absolute path to the project.
189+
// Also, the name of the project is used as a prefix in the environment folder.
190+
// <Environment folder> <--- check if the name of the project is used as a prefix
191+
// |__ .project <--- check if .project exists here
192+
// |__ Scripts/bin
193+
// |__ python <--- interpreterPath
194+
const dotProjectFile = path.join(path.dirname(path.dirname(interpreterPath)), '.project');
195+
if (!await pathExists(dotProjectFile)) {
196+
return false;
197+
}
198+
199+
const project = await readFile(dotProjectFile);
200+
if (!await pathExists(project)) {
201+
return false;
202+
}
203+
204+
// The name of the project is used as a prefix in the environment folder.
205+
if (interpreterPath.indexOf(`${path.sep}${path.basename(project)}-`) === -1) {
206+
return false;
207+
}
208+
188209
// PIPENV_NO_INHERIT is used to tell pipenv not to look for Pipfile in parent directories
189210
// https://pipenv.pypa.io/en/latest/advanced/#pipenv.environments.PIPENV_NO_INHERIT
190-
if (!checkIfPipFileExists(project, !process.env.PIPENV_NO_INHERIT)) {
211+
if (!checkIfPipFileExists(project, !getEnvironmentVariable('PIPENV_NO_INHERIT'))) {
191212
return false;
192213
}
193214

194-
/**
195-
* Global pipenv environments can be stored in 3 possible locations:
196-
* * WORKON_HOME environment variable: https://pipenv-fork.readthedocs.io/en/latest/advanced.html#custom-virtual-environment-location
197-
* * ~/.virtualenvs - for windows
198-
* * ~/.local/share/virtualenvs
199-
*
200-
* The name of the project is used as a prefix in the virtual env. We're assuming it's unique enough
201-
* detail so we don't have further ensure that the environment exists within these locations.
202-
*/
203-
if (interpreterPath.indexOf(`${path.sep}${path.basename(project)}-`) !== -1) {
204-
// Note it's still possible that this virtual environment belongs to another project with the same name,
205-
// and not this project. But the type of environment would still be pipenv, so we need not care.
206-
return true;
207-
}
208-
return false;
215+
return true;
209216
}
210217

211218
/**
212219
* Checks if the given interpreter path belongs to a pipenv environment, by locating the Pipfile which was used to
213220
* create the environment.
214221
* @param interpreterPath: Absolute path to any python interpreter.
215-
* @param resource Uri of the project
216222
*/
217-
async function isPipenvEnvironment(interpreterPath: string, resource: Resource) {
218-
if (isLocalPipenvEnvironment(interpreterPath)) {
223+
async function isPipenvEnvironment(interpreterPath: string) {
224+
if (await isLocalPipenvEnvironment(interpreterPath)) {
219225
return true;
220226
}
221-
/**
222-
* Otherwise it's a globally stored environment. Unfortunately, we cannot tell if a global environment is a pipenv
223-
* environment just by looking around the interpreter path. Also, because we cannot get project path from the pipenv
224-
* environment, we cannot locate the Pipfile which was used to create the environment. The best we can do is guess
225-
* the project it belongs to and see if everything else adds up. If it does, voila, we have a pipenv environment.
226-
*/
227-
if (resource && isGlobalPipenvEnvironmentRelatedToProject(interpreterPath, resource.fsPath)) {
227+
if (await isGlobalPipenvEnvironment(interpreterPath)) {
228228
return true;
229229
}
230230
return false;
@@ -253,7 +253,7 @@ async function isPipenvEnvironment(interpreterPath: string, resource: Resource)
253253
*
254254
* Last category is globally installed python, or system python.
255255
*/
256-
export async function identifyEnvironment(interpreterPath: string, resource?: Resource): Promise<EnvironmentType> {
256+
export async function identifyEnvironment(interpreterPath: string): Promise<EnvironmentType> {
257257
if (await isCondaEnvironment(interpreterPath)) {
258258
return EnvironmentType.Conda;
259259
}
@@ -262,7 +262,7 @@ export async function identifyEnvironment(interpreterPath: string, resource?: Re
262262
return EnvironmentType.WindowsStore;
263263
}
264264

265-
if (await isPipenvEnvironment(interpreterPath, resource)) {
265+
if (await isPipenvEnvironment(interpreterPath)) {
266266
return EnvironmentType.Pipenv;
267267
}
268268

0 commit comments

Comments
 (0)