Skip to content

Commit 8809079

Browse files
authored
Merge pull request #15461 from karthiknadig/cherry1
Cherry pick fixes from main to release
2 parents 0eeaab4 + d9c00fb commit 8809079

File tree

9 files changed

+49
-50
lines changed

9 files changed

+49
-50
lines changed

news/2 Fixes/15439.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix for missing pyenv virtual environments from selectable environments.

news/2 Fixes/15452.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Register Jedi regardless of what language server is configured.

src/client/activation/activationService.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ export class LanguageServerExtensionActivationService
243243
if (serverType === LanguageServerType.Jedi) {
244244
throw ex;
245245
}
246+
traceError(ex);
246247
this.output.appendLine(LanguageService.lsFailedToStart());
247248
serverType = LanguageServerType.Jedi;
248249
server = this.serviceContainer.get<ILanguageServerActivator>(ILanguageServerActivator, serverType);

src/client/activation/serviceRegistry.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -136,12 +136,6 @@ export function registerTypes(serviceManager: IServiceManager, languageServerTyp
136136
ILanguageServerFolderService,
137137
NodeLanguageServerFolderService,
138138
);
139-
} else if (languageServerType === LanguageServerType.Jedi) {
140-
serviceManager.add<ILanguageServerActivator>(
141-
ILanguageServerActivator,
142-
JediExtensionActivator,
143-
LanguageServerType.Jedi,
144-
);
145139
} else if (languageServerType === LanguageServerType.JediLSP) {
146140
serviceManager.add<ILanguageServerActivator>(
147141
ILanguageServerActivator,
@@ -165,6 +159,11 @@ export function registerTypes(serviceManager: IServiceManager, languageServerTyp
165159
LanguageServerType.None,
166160
);
167161
}
162+
serviceManager.add<ILanguageServerActivator>(
163+
ILanguageServerActivator,
164+
JediExtensionActivator,
165+
LanguageServerType.Jedi,
166+
); // We fallback to Jedi if for some reason we're unable to use other language servers, hence register this always.
168167

169168
serviceManager.addSingleton<IDownloadChannelRule>(
170169
IDownloadChannelRule,

src/client/pythonEnvironments/common/externalDependencies.ts

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -97,23 +97,35 @@ export async function getFileInfo(filePath: string): Promise<{ ctime: number; mt
9797
}
9898
}
9999

100-
export async function resolveSymbolicLink(filepath: string): Promise<string> {
101-
const stats = await fsapi.lstat(filepath);
100+
export async function resolveSymbolicLink(absPath: string): Promise<string> {
101+
const stats = await fsapi.lstat(absPath);
102102
if (stats.isSymbolicLink()) {
103-
const link = await fsapi.readlink(filepath);
103+
const link = await fsapi.readlink(absPath);
104104
return resolveSymbolicLink(link);
105105
}
106-
return filepath;
106+
return absPath;
107107
}
108108

109-
export async function* getSubDirs(root: string): AsyncIterableIterator<string> {
110-
const dirContents = await fsapi.readdir(root);
109+
/**
110+
* Returns full path to sub directories of a given directory.
111+
* @param root
112+
* @param resolveSymlinks
113+
*/
114+
export async function* getSubDirs(root: string, resolveSymlinks: boolean): AsyncIterableIterator<string> {
115+
const dirContents = await fsapi.promises.readdir(root, { withFileTypes: true });
111116
const generators = dirContents.map((item) => {
112117
async function* generator() {
113-
const stat = await fsapi.lstat(path.join(root, item));
114-
115-
if (stat.isDirectory()) {
116-
yield item;
118+
const fullPath = path.join(root, item.name);
119+
if (item.isDirectory()) {
120+
yield fullPath;
121+
} else if (resolveSymlinks && item.isSymbolicLink()) {
122+
// The current FS item is a symlink. It can potentially be a file
123+
// or a directory. Resolve it first and then check if it is a directory.
124+
const resolvedPath = await resolveSymbolicLink(fullPath);
125+
const resolvedPathStat = await fsapi.lstat(resolvedPath);
126+
if (resolvedPathStat.isDirectory()) {
127+
yield resolvedPath;
128+
}
117129
}
118130
}
119131

src/client/pythonEnvironments/discovery/locators/index.ts

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import {
1616
CURRENT_PATH_SERVICE,
1717
GetInterpreterLocatorOptions,
1818
GLOBAL_VIRTUAL_ENV_SERVICE,
19-
IComponentAdapter,
2019
IInterpreterLocatorHelper,
2120
IInterpreterLocatorService,
2221
KNOWN_PATH_SERVICE,
@@ -182,12 +181,6 @@ export class WorkspaceLocators extends LazyResourceBasedLocator {
182181
}
183182
}
184183

185-
// The parts of IComponentAdapter used here.
186-
interface IComponent {
187-
hasInterpreters: Promise<boolean | undefined>;
188-
getInterpreters(resource?: Uri, options?: GetInterpreterLocatorOptions): Promise<PythonEnvironment[] | undefined>;
189-
}
190-
191184
/**
192185
* Facilitates locating Python interpreters.
193186
*/
@@ -207,10 +200,7 @@ export class PythonInterpreterLocatorService implements IInterpreterLocatorServi
207200
Promise<PythonEnvironment[]>
208201
>();
209202

210-
constructor(
211-
@inject(IServiceContainer) private serviceContainer: IServiceContainer,
212-
@inject(IComponentAdapter) private readonly pyenvs: IComponent,
213-
) {
203+
constructor(@inject(IServiceContainer) private serviceContainer: IServiceContainer) {
214204
this._hasInterpreters = createDeferred<boolean>();
215205
serviceContainer.get<Disposable[]>(IDisposableRegistry).push(this);
216206
this.platform = serviceContainer.get<IPlatformService>(IPlatformService);
@@ -231,12 +221,7 @@ export class PythonInterpreterLocatorService implements IInterpreterLocatorServi
231221
}
232222

233223
public get hasInterpreters(): Promise<boolean> {
234-
return this.pyenvs.hasInterpreters.then((res) => {
235-
if (res !== undefined) {
236-
return res;
237-
}
238-
return this._hasInterpreters.completed ? this._hasInterpreters.promise : Promise.resolve(false);
239-
});
224+
return this._hasInterpreters.completed ? this._hasInterpreters.promise : Promise.resolve(false);
240225
}
241226

242227
/**
@@ -256,10 +241,6 @@ export class PythonInterpreterLocatorService implements IInterpreterLocatorServi
256241
*/
257242
@traceDecorators.verbose('Get Interpreters')
258243
public async getInterpreters(resource?: Uri, options?: GetInterpreterLocatorOptions): Promise<PythonEnvironment[]> {
259-
const envs = await this.pyenvs.getInterpreters(resource, options);
260-
if (envs !== undefined) {
261-
return envs;
262-
}
263244
const locators = this.getLocators(options);
264245
const promises = locators.map(async (provider) => provider.getInterpreters(resource));
265246
locators.forEach((locator) => {

src/client/pythonEnvironments/discovery/locators/services/pyenvLocator.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -259,15 +259,15 @@ export function parsePyenvVersion(str: string): Promise<IPyenvVersionStrings | u
259259
async function* getPyenvEnvironments(): AsyncIterableIterator<PythonEnvInfo> {
260260
const pyenvVersionDir = getPyenvVersionsDir();
261261

262-
const subDirs = getSubDirs(pyenvVersionDir);
263-
for await (const subDir of subDirs) {
264-
const envDir = path.join(pyenvVersionDir, subDir);
265-
const interpreterPath = await getInterpreterPathFromDir(envDir);
262+
const subDirs = getSubDirs(pyenvVersionDir, true);
263+
for await (const subDirPath of subDirs) {
264+
const envDirName = path.basename(subDirPath);
265+
const interpreterPath = await getInterpreterPathFromDir(subDirPath);
266266

267267
if (interpreterPath) {
268268
// The sub-directory name sometimes can contain distro and python versions.
269269
// here we attempt to extract the texts out of the name.
270-
const versionStrings = await parsePyenvVersion(subDir);
270+
const versionStrings = await parsePyenvVersion(envDirName);
271271

272272
// Here we look for near by files, or config files to see if we can get python version info
273273
// without running python itself.
@@ -290,7 +290,7 @@ async function* getPyenvEnvironments(): AsyncIterableIterator<PythonEnvInfo> {
290290
// `pyenv local|global <env-name>` or `pyenv shell <env-name>`
291291
//
292292
// For the display name we are going to treat these as `pyenv` environments.
293-
const display = `${subDir}:pyenv`;
293+
const display = `${envDirName}:pyenv`;
294294

295295
const org = versionStrings && versionStrings.distro ? versionStrings.distro : '';
296296

@@ -299,14 +299,14 @@ async function* getPyenvEnvironments(): AsyncIterableIterator<PythonEnvInfo> {
299299
const envInfo = buildEnvInfo({
300300
kind: PythonEnvKind.Pyenv,
301301
executable: interpreterPath,
302-
location: envDir,
302+
location: subDirPath,
303303
version: pythonVersion,
304304
source: [PythonEnvSource.Pyenv],
305305
display,
306306
org,
307307
fileInfo,
308308
});
309-
envInfo.name = subDir;
309+
envInfo.name = envDirName;
310310

311311
yield envInfo;
312312
}

src/test/activation/serviceRegistry.unit.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { DownloadBetaChannelRule, DownloadDailyChannelRule } from '../../client/
99
import { LanguageServerDownloader } from '../../client/activation/common/downloader';
1010
import { LanguageServerDownloadChannel } from '../../client/activation/common/packageRepository';
1111
import { ExtensionSurveyPrompt } from '../../client/activation/extensionSurvey';
12+
import { JediExtensionActivator } from '../../client/activation/jedi';
1213
import { DotNetLanguageServerActivator } from '../../client/activation/languageServer/activator';
1314
import { DotNetLanguageServerAnalysisOptions } from '../../client/activation/languageServer/analysisOptions';
1415
import { DotNetLanguageClientFactory } from '../../client/activation/languageServer/languageClientFactory';
@@ -161,6 +162,13 @@ suite('Unit Tests - Language Server Activation Service Registry', () => {
161162
LanguageServerType.Microsoft,
162163
),
163164
).once();
165+
verify(
166+
serviceManager.add<ILanguageServerActivator>(
167+
ILanguageServerActivator,
168+
JediExtensionActivator,
169+
LanguageServerType.Jedi,
170+
),
171+
).once();
164172
verify(serviceManager.add<ILanguageServerProxy>(ILanguageServerProxy, DotNetLanguageServerProxy)).once();
165173
verify(serviceManager.add<ILanguageServerManager>(ILanguageServerManager, DotNetLanguageServerManager)).once();
166174
verify(

src/test/pythonEnvironments/discovery/locators/index.unit.test.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import {
1818
CONDA_ENV_SERVICE,
1919
CURRENT_PATH_SERVICE,
2020
GLOBAL_VIRTUAL_ENV_SERVICE,
21-
IComponentAdapter,
2221
IInterpreterLocatorHelper,
2322
IInterpreterLocatorService,
2423
KNOWN_PATH_SERVICE,
@@ -785,21 +784,18 @@ suite('Interpreters - Locators Index', () => {
785784
let serviceContainer: TypeMoq.IMock<IServiceContainer>;
786785
let platformSvc: TypeMoq.IMock<IPlatformService>;
787786
let helper: TypeMoq.IMock<IInterpreterLocatorHelper>;
788-
let pyenvs: TypeMoq.IMock<IComponentAdapter>;
789787
let locator: IInterpreterLocatorService;
790788
setup(() => {
791789
serviceContainer = TypeMoq.Mock.ofType<IServiceContainer>();
792790
platformSvc = TypeMoq.Mock.ofType<IPlatformService>();
793791
helper = TypeMoq.Mock.ofType<IInterpreterLocatorHelper>();
794-
pyenvs = TypeMoq.Mock.ofType<IComponentAdapter>();
795792
serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IDisposableRegistry))).returns(() => []);
796793
serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IPlatformService))).returns(() => platformSvc.object);
797-
serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IComponentAdapter))).returns(() => pyenvs.object);
798794
serviceContainer
799795
.setup((c) => c.get(TypeMoq.It.isValue(IInterpreterLocatorHelper)))
800796
.returns(() => helper.object);
801797

802-
locator = new PythonInterpreterLocatorService(serviceContainer.object, pyenvs.object);
798+
locator = new PythonInterpreterLocatorService(serviceContainer.object);
803799
});
804800
[undefined, Uri.file('Something')].forEach((resource) => {
805801
getNamesAndValues<OSType>(OSType).forEach((osType) => {

0 commit comments

Comments
 (0)