Skip to content

Commit d031657

Browse files
Ignore the language server if the platform is not supported. (#2351)
The language server depends on a recent version of the .net runtime, which isn't supported on all platforms (particularly older ones). With this change we now let people known when the try to use the language server (instead of Jedi) on a platform where it isn't supported.
1 parent 2b6ee05 commit d031657

13 files changed

Lines changed: 905 additions & 26 deletions

File tree

news/2 Fixes/2245.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Notify the user when the language server does not support their platform.

package-lock.json

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1524,6 +1524,7 @@
15241524
"fs-extra": "4.0.3",
15251525
"fuzzy": "0.1.3",
15261526
"get-port": "3.2.0",
1527+
"getos": "3.1.0",
15271528
"glob": "7.1.2",
15281529
"iconv-lite": "0.4.21",
15291530
"inversify": "4.11.1",

src/client/activation/activationService.ts

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,26 @@ import { ConfigurationChangeEvent, Disposable, OutputChannel, Uri } from 'vscode
88
import { IApplicationShell, ICommandManager, IWorkspaceService } from '../common/application/types';
99
import { isLanguageServerTest, STANDARD_OUTPUT_CHANNEL } from '../common/constants';
1010
import '../common/extensions';
11+
import { IPlatformService, OSDistro, OSType } from '../common/platform/types';
1112
import { IConfigurationService, IDisposableRegistry, IOutputChannel, IPythonSettings } from '../common/types';
1213
import { IServiceContainer } from '../ioc/types';
14+
import { PYTHON_LANGUAGE_SERVER_PLATFORM_NOT_SUPPORTED } from '../telemetry/constants';
15+
import { getTelemetryReporter } from '../telemetry/telemetry';
1316
import { ExtensionActivators, IExtensionActivationService, IExtensionActivator } from './types';
1417

1518
const jediEnabledSetting: keyof IPythonSettings = 'jediEnabled';
19+
const LS_MIN_OS_VERSIONS: [OSType, OSDistro, string][] = [
20+
// See: https://code.visualstudio.com/docs/supporting/requirements
21+
[OSType.OSX, OSDistro.Unknown, '10.12'], // Sierra or higher
22+
[OSType.Windows, OSDistro.Unknown, '6.1'], // Win 7 or higher
23+
// tslint:disable-next-line: no-suspicious-comment
24+
// TODO: Are these right?
25+
[OSType.Linux, OSDistro.Ubuntu, '14.04'], // "precise"
26+
[OSType.Linux, OSDistro.Debian, '7'],
27+
[OSType.Linux, OSDistro.RHEL, '7'],
28+
[OSType.Linux, OSDistro.CentOS, '7'],
29+
[OSType.Linux, OSDistro.Fedora, '23']
30+
];
1631

1732
type ActivatorInfo = { jedi: boolean; activator: IExtensionActivator };
1833

@@ -36,7 +51,15 @@ export class ExtensionActivationService implements IExtensionActivationService,
3651
return;
3752
}
3853

39-
const jedi = this.useJedi();
54+
let jedi = this.useJedi();
55+
if (!jedi && !isLSSupported(this.serviceContainer)) {
56+
this.appShell.showWarningMessage('The Python Language Server is not supported on your platform.');
57+
const reporter = getTelemetryReporter();
58+
// tslint:disable-next-line:no-suspicious-comment
59+
// TODO: Only send once (ever)?
60+
reporter.sendTelemetryEvent(PYTHON_LANGUAGE_SERVER_PLATFORM_NOT_SUPPORTED);
61+
jedi = true;
62+
}
4063

4164
const engineName = jedi ? 'Jedi Python language engine' : 'Microsoft Python language server';
4265
this.output.appendLine(`Starting ${engineName}.`);
@@ -67,9 +90,38 @@ export class ExtensionActivationService implements IExtensionActivationService,
6790
}
6891
}
6992
private useJedi(): boolean {
93+
if (isLanguageServerTest()) {
94+
return false;
95+
}
7096
const workspacesUris: (Uri | undefined)[] = this.workspaceService.hasWorkspaceFolders ? this.workspaceService.workspaceFolders!.map(item => item.uri) : [undefined];
7197
const configuraionService = this.serviceContainer.get<IConfigurationService>(IConfigurationService);
72-
const jediEnabledForAnyWorkspace = workspacesUris.filter(uri => configuraionService.getSettings(uri).jediEnabled).length > 0;
73-
return !isLanguageServerTest() && jediEnabledForAnyWorkspace;
98+
return workspacesUris.filter(uri => configuraionService.getSettings(uri).jediEnabled).length > 0;
99+
}
100+
}
101+
102+
function isLSSupported(services: IServiceContainer): boolean {
103+
const platform = services.get<IPlatformService>(IPlatformService);
104+
let minVer = '';
105+
for (const [osType, distro, ver] of LS_MIN_OS_VERSIONS) {
106+
if (platform.os.type === osType && platform.os.distro === distro) {
107+
minVer = ver;
108+
break;
109+
}
110+
}
111+
if (minVer === '') {
112+
return true;
113+
}
114+
minVer = normalizeVersion(minVer);
115+
return platform.os.version.compare(minVer) >= 0;
116+
}
117+
118+
function normalizeVersion(ver: string): string {
119+
ver = ver.replace(/\.00*/, '.');
120+
if (/^\d\d*$/.test(ver)) {
121+
return `${ver}.0.0`;
122+
} else if (/^\d\d*\.\d\d*$/.test(ver)) {
123+
return `${ver}.0`;
124+
} else {
125+
return ver;
74126
}
75127
}
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
'use strict';
5+
6+
import * as getos from 'getos';
7+
import * as os from 'os';
8+
import * as semver from 'semver';
9+
import { NON_WINDOWS_PATH_VARIABLE_NAME, WINDOWS_PATH_VARIABLE_NAME } from './constants';
10+
import { IOSInfo, OSDistro, OSType } from './types';
11+
12+
let local: OSInfo;
13+
14+
function getLocal(): OSInfo {
15+
if (!local) {
16+
local = getOSInfo();
17+
}
18+
return local;
19+
}
20+
21+
export function getOSType(platform: string = process.platform): OSType {
22+
if (/^win/.test(platform)) {
23+
return OSType.Windows;
24+
} else if (/^darwin/.test(platform)) {
25+
return OSType.OSX;
26+
} else if (/^linux/.test(platform)) {
27+
return OSType.Linux;
28+
} else {
29+
return OSType.Unknown;
30+
}
31+
}
32+
33+
export class OSInfo implements IOSInfo {
34+
constructor(
35+
public readonly type: OSType,
36+
public readonly arch: string = os.arch(),
37+
// See:
38+
// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/semver/index.d.ts#L152
39+
public readonly version: semver.SemVer = new semver.SemVer('0.0.0'),
40+
public readonly distro: OSDistro = OSDistro.Unknown
41+
) {}
42+
43+
public get is64bit(): boolean {
44+
return this.arch === 'x64';
45+
}
46+
}
47+
48+
export function getOSInfo(
49+
getArch: () => string = os.arch,
50+
getRelease: () => string = os.release,
51+
getDistro: () => [OSDistro, semver.SemVer] = getLinuxDistro,
52+
platform?: string
53+
): OSInfo {
54+
const osType = getOSType(platform);
55+
const arch = getArch();
56+
switch (osType) {
57+
case OSType.Windows:
58+
return getDefaultOSInfo(osType, arch, getRelease);
59+
case OSType.OSX:
60+
return getDefaultOSInfo(osType, arch, getRelease);
61+
case OSType.Linux:
62+
return getLinuxInfo(arch, getDistro);
63+
default:
64+
return new OSInfo(OSType.Unknown, arch);
65+
}
66+
}
67+
68+
function getDefaultOSInfo(osType: OSType, arch: string, getRelease: () => string): OSInfo {
69+
const version = parseVersion(getRelease());
70+
return new OSInfo(osType, arch, version);
71+
}
72+
73+
function getLinuxInfo(arch: string, getDistro: () => [OSDistro, semver.SemVer]): OSInfo {
74+
const [distro, version] = getDistro();
75+
return new OSInfo(OSType.Linux, arch, version, distro);
76+
}
77+
78+
function getLinuxDistro(): [OSDistro, semver.SemVer] {
79+
let distro: OSDistro = OSDistro.Unknown;
80+
let version: semver.SemVer = new semver.SemVer('0.0.0');
81+
getos((exc, info) => {
82+
if (exc) {
83+
throw exc;
84+
}
85+
distro = getLinuxDistroFromName(info.dist);
86+
version = parseVersion(info.release);
87+
});
88+
return [distro, version];
89+
}
90+
91+
function getLinuxDistroFromName(name: string): OSDistro {
92+
name = name.toLowerCase();
93+
// See https://github.com/zyga/os-release-zoo.
94+
if (/ubuntu/.test(name)) {
95+
return OSDistro.Ubuntu;
96+
} else if (/debian/.test(name)) {
97+
return OSDistro.Debian;
98+
} else if (/rhel/.test(name) || /red hat/.test(name)) {
99+
return OSDistro.RHEL;
100+
} else if (/fedora/.test(name)) {
101+
return OSDistro.Fedora;
102+
} else if (/centos/.test(name)) {
103+
return OSDistro.CentOS;
104+
}
105+
106+
// The remainder aren't officially supported by VS Code.
107+
if (/suse/.test(name)) {
108+
return OSDistro.Suse;
109+
} else if (/gentoo/.test(name)) {
110+
return OSDistro.Suse;
111+
} else if (/arch/.test(name)) {
112+
return OSDistro.Arch;
113+
} else {
114+
return OSDistro.Unknown;
115+
}
116+
}
117+
118+
// helpers
119+
120+
export function isWindows(info?: IOSInfo): boolean {
121+
if (!info) {
122+
info = getLocal();
123+
}
124+
return info.type === OSType.Windows;
125+
}
126+
127+
export function isMac(info?: IOSInfo): boolean {
128+
if (!info) {
129+
info = getLocal();
130+
}
131+
return info.type === OSType.OSX;
132+
}
133+
134+
export function isLinux(info?: IOSInfo): boolean {
135+
if (!info) {
136+
info = getLocal();
137+
}
138+
return info.type === OSType.Linux;
139+
}
140+
141+
export function is64bit(info?: IOSInfo): boolean {
142+
if (!info) {
143+
info = getLocal();
144+
}
145+
return info.arch === 'x64';
146+
}
147+
148+
export function getPathVariableName(info: IOSInfo) {
149+
return isWindows(info) ? WINDOWS_PATH_VARIABLE_NAME : NON_WINDOWS_PATH_VARIABLE_NAME;
150+
}
151+
152+
export function getVirtualEnvBinName(info: IOSInfo) {
153+
return isWindows(info) ? 'scripts' : 'bin';
154+
}
155+
156+
export function parseVersion(raw: string): semver.SemVer {
157+
raw = raw.replace(/\.00*(?=[1-9]|0\.)/, '.');
158+
const ver = semver.coerce(raw);
159+
if (ver === null || !semver.valid(ver)) {
160+
// tslint:disable-next-line: no-suspicious-comment
161+
// TODO: Raise an exception instead?
162+
return new semver.SemVer('0.0.0');
163+
}
164+
return ver;
165+
}

src/client/common/platform/platformService.ts

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,39 @@
33
'use strict';
44

55
import { injectable } from 'inversify';
6-
import { arch } from 'os';
7-
import { NON_WINDOWS_PATH_VARIABLE_NAME, WINDOWS_PATH_VARIABLE_NAME } from './constants';
6+
import * as osinfo from './osinfo';
87
import { IPlatformService } from './types';
98

109
@injectable()
1110
export class PlatformService implements IPlatformService {
12-
private _isWindows: boolean;
13-
private _isMac: boolean;
11+
private info?: osinfo.OSInfo;
1412

15-
constructor() {
16-
this._isWindows = /^win/.test(process.platform);
17-
this._isMac = /^darwin/.test(process.platform);
13+
public get os(): osinfo.OSInfo {
14+
if (!this.info) {
15+
this.info = osinfo.getOSInfo();
16+
}
17+
return this.info;
1818
}
19+
20+
public get pathVariableName() {
21+
return osinfo.getPathVariableName(this.os);
22+
}
23+
public get virtualEnvBinName() {
24+
return osinfo.getVirtualEnvBinName(this.os);
25+
}
26+
27+
// tslint:disable-next-line: no-suspicious-comment
28+
// TODO: Drop the following (in favor of osType).
1929
public get isWindows(): boolean {
20-
return this._isWindows;
30+
return osinfo.isWindows(this.os);
2131
}
2232
public get isMac(): boolean {
23-
return this._isMac;
33+
return osinfo.isMac(this.os);
2434
}
2535
public get isLinux(): boolean {
26-
return !(this.isWindows || this.isMac);
36+
return osinfo.isLinux(this.os);
2737
}
2838
public get is64bit(): boolean {
29-
return arch() === 'x64';
30-
}
31-
public get pathVariableName() {
32-
return this.isWindows ? WINDOWS_PATH_VARIABLE_NAME : NON_WINDOWS_PATH_VARIABLE_NAME;
33-
}
34-
public get virtualEnvBinName() {
35-
return this.isWindows ? 'scripts' : 'bin';
39+
return osinfo.is64bit(this.os);
3640
}
3741
}

0 commit comments

Comments
 (0)