mirror of
https://github.com/microsoft/pyright.git
synced 2024-09-11 07:55:56 +03:00
Add extraPath support, accept minor versions in version_info, make completion extension async (#662)
This commit is contained in:
parent
c3660d2065
commit
11918674e7
@ -17,6 +17,7 @@
|
|||||||
"simple-import-sort"
|
"simple-import-sort"
|
||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
|
"eqeqeq": "error",
|
||||||
"no-constant-condition": 0,
|
"no-constant-condition": 0,
|
||||||
"no-inner-declarations": 0,
|
"no-inner-declarations": 0,
|
||||||
"no-undef": 0,
|
"no-undef": 0,
|
||||||
|
@ -16,6 +16,8 @@ The Pyright VS Code extension honors the following settings.
|
|||||||
|
|
||||||
**python.analysis.autoSearchPaths** [boolean]: Determines whether pyright automatically adds common search paths like "src" if there are no execution environments defined in the config file.
|
**python.analysis.autoSearchPaths** [boolean]: Determines whether pyright automatically adds common search paths like "src" if there are no execution environments defined in the config file.
|
||||||
|
|
||||||
|
**python.analysis.extraPaths** [array of paths]: Paths to add to the default execution environment extra paths if there are no execution environments defined in the config file.
|
||||||
|
|
||||||
**python.pythonPath** [path]: Path to Python interpreter.
|
**python.pythonPath** [path]: Path to Python interpreter.
|
||||||
|
|
||||||
**python.venvPath** [path]: Path to folder with subdirectories that contain virtual environments.
|
**python.venvPath** [path]: Path to folder with subdirectories that contain virtual environments.
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
"simple-import-sort"
|
"simple-import-sort"
|
||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
|
"eqeqeq": "error",
|
||||||
"no-constant-condition": 0,
|
"no-constant-condition": 0,
|
||||||
"no-inner-declarations": 0,
|
"no-inner-declarations": 0,
|
||||||
"no-undef": 0,
|
"no-undef": 0,
|
||||||
|
@ -930,21 +930,21 @@ export class Program {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getCompletionsForPosition(
|
async getCompletionsForPosition(
|
||||||
filePath: string,
|
filePath: string,
|
||||||
position: Position,
|
position: Position,
|
||||||
workspacePath: string,
|
workspacePath: string,
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
): CompletionList | undefined {
|
): Promise<CompletionList | undefined> {
|
||||||
return this._runEvaluatorWithCancellationToken(token, () => {
|
const sourceFileInfo = this._sourceFileMap.get(filePath);
|
||||||
const sourceFileInfo = this._sourceFileMap.get(filePath);
|
if (!sourceFileInfo) {
|
||||||
if (!sourceFileInfo) {
|
return undefined;
|
||||||
return undefined;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
let completionList = this._runEvaluatorWithCancellationToken(token, () => {
|
||||||
this._bindFile(sourceFileInfo);
|
this._bindFile(sourceFileInfo);
|
||||||
|
|
||||||
let completionList = sourceFileInfo.sourceFile.getCompletionsForPosition(
|
return sourceFileInfo.sourceFile.getCompletionsForPosition(
|
||||||
position,
|
position,
|
||||||
workspacePath,
|
workspacePath,
|
||||||
this._configOptions,
|
this._configOptions,
|
||||||
@ -954,25 +954,29 @@ export class Program {
|
|||||||
() => this._buildModuleSymbolsMap(sourceFileInfo),
|
() => this._buildModuleSymbolsMap(sourceFileInfo),
|
||||||
token
|
token
|
||||||
);
|
);
|
||||||
|
|
||||||
if (completionList && this._extension?.completionListExtension) {
|
|
||||||
const pr = sourceFileInfo.sourceFile.getParseResults();
|
|
||||||
if (pr?.parseTree) {
|
|
||||||
const offset = convertPositionToOffset(position, pr.tokenizerOutput.lines);
|
|
||||||
if (offset) {
|
|
||||||
completionList = this._extension.completionListExtension.updateCompletionList(
|
|
||||||
completionList,
|
|
||||||
pr.parseTree,
|
|
||||||
filePath,
|
|
||||||
offset,
|
|
||||||
this._configOptions
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return completionList;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!completionList || !this._extension?.completionListExtension) {
|
||||||
|
return completionList;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pr = sourceFileInfo.sourceFile.getParseResults();
|
||||||
|
const content = sourceFileInfo.sourceFile.getFileContents();
|
||||||
|
if (pr?.parseTree && content) {
|
||||||
|
const offset = convertPositionToOffset(position, pr.tokenizerOutput.lines);
|
||||||
|
if (offset) {
|
||||||
|
completionList = await this._extension.completionListExtension.updateCompletionList(
|
||||||
|
completionList,
|
||||||
|
pr.parseTree,
|
||||||
|
content,
|
||||||
|
offset,
|
||||||
|
this._configOptions,
|
||||||
|
token
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return completionList;
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveCompletionItem(filePath: string, completionItem: CompletionItem, token: CancellationToken) {
|
resolveCompletionItem(filePath: string, completionItem: CompletionItem, token: CancellationToken) {
|
||||||
|
@ -221,7 +221,7 @@ export class AnalyzerService {
|
|||||||
position: Position,
|
position: Position,
|
||||||
workspacePath: string,
|
workspacePath: string,
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
): CompletionList | undefined {
|
): Promise<CompletionList | undefined> {
|
||||||
return this._program.getCompletionsForPosition(filePath, position, workspacePath, token);
|
return this._program.getCompletionsForPosition(filePath, position, workspacePath, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,17 +391,24 @@ export class AnalyzerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the user has defined execution environments, then we ignore
|
// If the user has defined execution environments, then we ignore
|
||||||
// autoSearchPaths and leave it up to them to set extraPaths on them.
|
// autoSearchPaths, extraPaths and leave it up to them to set
|
||||||
if (commandLineOptions.autoSearchPaths && configOptions.executionEnvironments.length === 0) {
|
// extraPaths on the execution environments.
|
||||||
configOptions.addExecEnvironmentForAutoSearchPaths(this._fs);
|
if (configOptions.executionEnvironments.length === 0) {
|
||||||
|
configOptions.addExecEnvironmentForExtraPaths(
|
||||||
|
this._fs,
|
||||||
|
commandLineOptions.autoSearchPaths || false,
|
||||||
|
commandLineOptions.extraPaths || []
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._updateConfigFileWatcher();
|
this._updateConfigFileWatcher();
|
||||||
this._updateLibraryFileWatcher();
|
this._updateLibraryFileWatcher();
|
||||||
} else {
|
} else {
|
||||||
if (commandLineOptions.autoSearchPaths) {
|
configOptions.addExecEnvironmentForExtraPaths(
|
||||||
configOptions.addExecEnvironmentForAutoSearchPaths(this._fs);
|
this._fs,
|
||||||
}
|
commandLineOptions.autoSearchPaths || false,
|
||||||
|
commandLineOptions.extraPaths || []
|
||||||
|
);
|
||||||
|
|
||||||
configOptions.autoExcludeVenv = true;
|
configOptions.autoExcludeVenv = true;
|
||||||
}
|
}
|
||||||
|
@ -117,7 +117,8 @@ export function evaluateStaticBoolLikeExpression(
|
|||||||
|
|
||||||
function _convertTupleToVersion(node: TupleNode): number | undefined {
|
function _convertTupleToVersion(node: TupleNode): number | undefined {
|
||||||
let comparisonVersion: number | undefined;
|
let comparisonVersion: number | undefined;
|
||||||
if (node.expressions.length === 2) {
|
// Ignore patch versions.
|
||||||
|
if (node.expressions.length >= 2) {
|
||||||
if (
|
if (
|
||||||
node.expressions[0].nodeType === ParseNodeType.Number &&
|
node.expressions[0].nodeType === ParseNodeType.Number &&
|
||||||
!node.expressions[0].isImaginary &&
|
!node.expressions[0].isImaginary &&
|
||||||
|
@ -7912,7 +7912,7 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
|
|||||||
const inferredYieldTypes: Type[] = [];
|
const inferredYieldTypes: Type[] = [];
|
||||||
functionDecl.yieldExpressions.forEach((yieldNode) => {
|
functionDecl.yieldExpressions.forEach((yieldNode) => {
|
||||||
if (isNodeReachable(yieldNode)) {
|
if (isNodeReachable(yieldNode)) {
|
||||||
if (yieldNode.nodeType == ParseNodeType.YieldFrom) {
|
if (yieldNode.nodeType === ParseNodeType.YieldFrom) {
|
||||||
const iteratorType = getTypeOfExpression(yieldNode.expression).type;
|
const iteratorType = getTypeOfExpression(yieldNode.expression).type;
|
||||||
const yieldType = getTypeFromIterable(
|
const yieldType = getTypeFromIterable(
|
||||||
iteratorType,
|
iteratorType,
|
||||||
|
@ -207,7 +207,7 @@ export class OperationCanceledException extends ResponseError<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static is(e: any) {
|
static is(e: any) {
|
||||||
return e.code == ErrorCodes.RequestCancelled;
|
return e.code === ErrorCodes.RequestCancelled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +61,10 @@ export class CommandLineOptions {
|
|||||||
// execution environments.
|
// execution environments.
|
||||||
autoSearchPaths?: boolean;
|
autoSearchPaths?: boolean;
|
||||||
|
|
||||||
|
// Extra paths to add to the default execution environment
|
||||||
|
// when user has not explicitly defined execution environments.
|
||||||
|
extraPaths?: string[];
|
||||||
|
|
||||||
// Default type-checking rule set. Should be one of 'off',
|
// Default type-checking rule set. Should be one of 'off',
|
||||||
// 'basic', or 'strict'.
|
// 'basic', or 'strict'.
|
||||||
typeCheckingMode?: string;
|
typeCheckingMode?: string;
|
||||||
|
@ -290,7 +290,7 @@ export function getNoTypeCheckingDiagnosticRuleSet(): DiagnosticRuleSet {
|
|||||||
enableTypeIgnoreComments: true,
|
enableTypeIgnoreComments: true,
|
||||||
reportGeneralTypeIssues: 'none',
|
reportGeneralTypeIssues: 'none',
|
||||||
reportTypeshedErrors: 'none',
|
reportTypeshedErrors: 'none',
|
||||||
reportMissingImports: 'none',
|
reportMissingImports: 'warning',
|
||||||
reportMissingModuleSource: 'warning',
|
reportMissingModuleSource: 'warning',
|
||||||
reportMissingTypeStubs: 'none',
|
reportMissingTypeStubs: 'none',
|
||||||
reportImportCycles: 'none',
|
reportImportCycles: 'none',
|
||||||
@ -494,17 +494,31 @@ export class ConfigOptions {
|
|||||||
return execEnv;
|
return execEnv;
|
||||||
}
|
}
|
||||||
|
|
||||||
addExecEnvironmentForAutoSearchPaths(fs: FileSystem) {
|
addExecEnvironmentForExtraPaths(fs: FileSystem, autoSearchPaths: boolean, extraPaths: string[]) {
|
||||||
// Auto-detect the common scenario where the sources are under the src folder
|
const paths: string[] = [];
|
||||||
const srcPath = combinePaths(this.projectRoot, pathConsts.src);
|
|
||||||
if (fs.existsSync(srcPath) && !fs.existsSync(combinePaths(srcPath, '__init__.py'))) {
|
if (autoSearchPaths) {
|
||||||
|
// Auto-detect the common scenario where the sources are under the src folder
|
||||||
|
const srcPath = normalizePath(combinePaths(this.projectRoot, pathConsts.src));
|
||||||
|
if (fs.existsSync(srcPath) && !fs.existsSync(combinePaths(srcPath, '__init__.py'))) {
|
||||||
|
paths.push(srcPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extraPaths.length > 0) {
|
||||||
|
for (const p of extraPaths) {
|
||||||
|
paths.push(normalizePath(combinePaths(this.projectRoot, p)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paths.length > 0) {
|
||||||
const execEnv = new ExecutionEnvironment(
|
const execEnv = new ExecutionEnvironment(
|
||||||
this.projectRoot,
|
this.projectRoot,
|
||||||
this.defaultPythonVersion,
|
this.defaultPythonVersion,
|
||||||
this.defaultPythonPlatform
|
this.defaultPythonPlatform
|
||||||
);
|
);
|
||||||
|
|
||||||
execEnv.extraPaths.push(srcPath);
|
execEnv.extraPaths.push(...paths);
|
||||||
|
|
||||||
this.executionEnvironments.push(execEnv);
|
this.executionEnvironments.push(execEnv);
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Defines language service completion list extensibility.
|
* Defines language service completion list extensibility.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { CompletionList } from 'vscode-languageserver';
|
import { CancellationToken, CompletionList } from 'vscode-languageserver';
|
||||||
|
|
||||||
import { ModuleNode } from '../parser/parseNodes';
|
import { ModuleNode } from '../parser/parseNodes';
|
||||||
import { ConfigOptions } from './configOptions';
|
import { ConfigOptions } from './configOptions';
|
||||||
@ -21,6 +21,7 @@ export interface CompletionListExtension {
|
|||||||
ast: ModuleNode,
|
ast: ModuleNode,
|
||||||
content: string,
|
content: string,
|
||||||
position: number,
|
position: number,
|
||||||
options: ConfigOptions
|
options: ConfigOptions,
|
||||||
): CompletionList;
|
token: CancellationToken
|
||||||
|
): Promise<CompletionList>;
|
||||||
}
|
}
|
||||||
|
@ -36,9 +36,15 @@ export interface Stats {
|
|||||||
isSocket(): boolean;
|
isSocket(): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MkDirOptions {
|
||||||
|
recursive: boolean;
|
||||||
|
// Not supported on Windows so commented out.
|
||||||
|
// mode: string | number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface FileSystem {
|
export interface FileSystem {
|
||||||
existsSync(path: string): boolean;
|
existsSync(path: string): boolean;
|
||||||
mkdirSync(path: string): void;
|
mkdirSync(path: string, options?: MkDirOptions | number): void;
|
||||||
chdir(path: string): void;
|
chdir(path: string): void;
|
||||||
readdirSync(path: string): string[];
|
readdirSync(path: string): string[];
|
||||||
readFileSync(path: string, encoding?: null): Buffer;
|
readFileSync(path: string, encoding?: null): Buffer;
|
||||||
@ -84,8 +90,8 @@ class RealFileSystem implements FileSystem {
|
|||||||
return fs.existsSync(path);
|
return fs.existsSync(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
mkdirSync(path: string) {
|
mkdirSync(path: string, options?: MkDirOptions | number) {
|
||||||
fs.mkdirSync(path);
|
fs.mkdirSync(path, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
chdir(path: string) {
|
chdir(path: string) {
|
||||||
|
@ -81,6 +81,7 @@ export interface ServerSettings {
|
|||||||
disableLanguageServices?: boolean;
|
disableLanguageServices?: boolean;
|
||||||
disableOrganizeImports?: boolean;
|
disableOrganizeImports?: boolean;
|
||||||
autoSearchPaths?: boolean;
|
autoSearchPaths?: boolean;
|
||||||
|
extraPaths?: string[];
|
||||||
watchForSourceChanges?: boolean;
|
watchForSourceChanges?: boolean;
|
||||||
watchForLibraryChanges?: boolean;
|
watchForLibraryChanges?: boolean;
|
||||||
}
|
}
|
||||||
@ -574,7 +575,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const completions = workspace.serviceInstance.getCompletionsForPosition(
|
const completions = await workspace.serviceInstance.getCompletionsForPosition(
|
||||||
filePath,
|
filePath,
|
||||||
position,
|
position,
|
||||||
workspace.rootPath,
|
workspace.rootPath,
|
||||||
|
@ -81,6 +81,7 @@ function _getCommandLineOptions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
commandLineOptions.autoSearchPaths = serverSettings.autoSearchPaths;
|
commandLineOptions.autoSearchPaths = serverSettings.autoSearchPaths;
|
||||||
|
commandLineOptions.extraPaths = serverSettings.extraPaths;
|
||||||
|
|
||||||
return commandLineOptions;
|
return commandLineOptions;
|
||||||
}
|
}
|
||||||
|
@ -160,7 +160,7 @@ export class ReferencesProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Special case module names, which don't have references.
|
// Special case module names, which don't have references.
|
||||||
if (node.parent?.nodeType == ParseNodeType.ModuleName) {
|
if (node.parent?.nodeType === ParseNodeType.ModuleName) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +46,11 @@ class PyrightServer extends LanguageServerBase {
|
|||||||
serverSettings.typeshedPath = normalizeSlashes(typeshedPaths[0]);
|
serverSettings.typeshedPath = normalizeSlashes(typeshedPaths[0]);
|
||||||
}
|
}
|
||||||
serverSettings.autoSearchPaths = !!pythonAnalysisSection.autoSearchPaths;
|
serverSettings.autoSearchPaths = !!pythonAnalysisSection.autoSearchPaths;
|
||||||
|
|
||||||
|
const extraPaths = pythonAnalysisSection.extraPaths;
|
||||||
|
if (extraPaths && isArray(extraPaths) && extraPaths.length > 0) {
|
||||||
|
serverSettings.extraPaths = extraPaths.map((p) => normalizeSlashes(p));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
serverSettings.autoSearchPaths = true;
|
serverSettings.autoSearchPaths = true;
|
||||||
}
|
}
|
||||||
|
@ -1431,6 +1431,12 @@ test('Callable1', () => {
|
|||||||
validateResults(analysisResults, 3);
|
validateResults(analysisResults, 3);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('ThreePartVersion1', () => {
|
||||||
|
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['threePartVersion1.py']);
|
||||||
|
|
||||||
|
validateResults(analysisResults, 0);
|
||||||
|
});
|
||||||
|
|
||||||
test('Unions1', () => {
|
test('Unions1', () => {
|
||||||
const configOptions = new ConfigOptions('.');
|
const configOptions = new ConfigOptions('.');
|
||||||
|
|
||||||
|
@ -261,3 +261,27 @@ test('AutoSearchPathsOnWithConfigExecEnv', () => {
|
|||||||
|
|
||||||
assert.deepEqual(configOptions.executionEnvironments, expectedExecEnvs);
|
assert.deepEqual(configOptions.executionEnvironments, expectedExecEnvs);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('AutoSearchPathsOnAndExtraPaths', () => {
|
||||||
|
const cwd = normalizePath(combinePaths(process.cwd(), '../server/src/tests/samples/project_src_with_extra_paths'));
|
||||||
|
const nullConsole = new NullConsole();
|
||||||
|
const service = new AnalyzerService('<default>', createFromRealFileSystem(nullConsole), nullConsole);
|
||||||
|
const commandLineOptions = new CommandLineOptions(cwd, false);
|
||||||
|
commandLineOptions.autoSearchPaths = true;
|
||||||
|
commandLineOptions.extraPaths = ['src/_vendored'];
|
||||||
|
service.setOptions(commandLineOptions);
|
||||||
|
|
||||||
|
const configOptions = service.test_getConfigOptions(commandLineOptions);
|
||||||
|
|
||||||
|
// An execution environment is automatically created with src and src/_vendored as extra paths
|
||||||
|
const expectedExecEnvs = [
|
||||||
|
{
|
||||||
|
pythonPlatform: undefined,
|
||||||
|
pythonVersion: 776,
|
||||||
|
root: cwd,
|
||||||
|
extraPaths: [normalizePath(combinePaths(cwd, 'src')), normalizePath(combinePaths(cwd, 'src', '_vendored'))],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
assert.deepEqual(configOptions.executionEnvironments, expectedExecEnvs);
|
||||||
|
});
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/// <reference path="fourslash.ts" />
|
/// <reference path="fourslash.ts" />
|
||||||
|
// @asynctest: true
|
||||||
|
|
||||||
// @filename: test.py
|
// @filename: test.py
|
||||||
//// a = 42
|
//// a = 42
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/// <reference path="fourslash.ts" />
|
/// <reference path="fourslash.ts" />
|
||||||
|
// @asynctest: true
|
||||||
|
|
||||||
// @filename: test.py
|
// @filename: test.py
|
||||||
//// import time
|
//// import time
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/// <reference path="fourslash.ts" />
|
/// <reference path="fourslash.ts" />
|
||||||
|
// @asynctest: true
|
||||||
|
|
||||||
// @filename: test.py
|
// @filename: test.py
|
||||||
//// a = 42
|
//// a = 42
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/// <reference path="fourslash.ts" />
|
/// <reference path="fourslash.ts" />
|
||||||
|
// @asynctest: true
|
||||||
|
|
||||||
// @filename: mspythonconfig.json
|
// @filename: mspythonconfig.json
|
||||||
//// {
|
//// {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/// <reference path="fourslash.ts" />
|
/// <reference path="fourslash.ts" />
|
||||||
|
// @asynctest: true
|
||||||
|
|
||||||
// @filename: mspythonconfig.json
|
// @filename: mspythonconfig.json
|
||||||
//// {
|
//// {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/// <reference path="fourslash.ts" />
|
/// <reference path="fourslash.ts" />
|
||||||
|
// @asynctest: true
|
||||||
|
|
||||||
// @filename: mspythonconfig.json
|
// @filename: mspythonconfig.json
|
||||||
//// {
|
//// {
|
||||||
|
@ -130,7 +130,7 @@ declare namespace _ {
|
|||||||
map: {
|
map: {
|
||||||
[marker: string]: { completions: { label: string; documentation?: { kind: string; value: string } }[] };
|
[marker: string]: { completions: { label: string; documentation?: { kind: string; value: string } }[] };
|
||||||
}
|
}
|
||||||
): void;
|
): Promise<void>;
|
||||||
verifySignature(map: {
|
verifySignature(map: {
|
||||||
[marker: string]: {
|
[marker: string]: {
|
||||||
noSig?: boolean;
|
noSig?: boolean;
|
||||||
|
@ -67,23 +67,41 @@ export function runFourSlashTestContent(
|
|||||||
|
|
||||||
function runCode(code: string, state: TestState): void {
|
function runCode(code: string, state: TestState): void {
|
||||||
// Compile and execute the test
|
// Compile and execute the test
|
||||||
const wrappedCode = `(function(helper, Consts) {
|
|
||||||
${code}
|
|
||||||
})`;
|
|
||||||
|
|
||||||
// TODO: figure out how to use this with async
|
|
||||||
try {
|
try {
|
||||||
const f = eval(wrappedCode);
|
if (state.asyncTest) {
|
||||||
f(state, Consts);
|
runAsyncCode();
|
||||||
|
} else {
|
||||||
markDone();
|
runPlainCode();
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
markDone(error);
|
markDone(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function runAsyncCode() {
|
||||||
|
const wrappedCode = `(async function(helper, Consts) {
|
||||||
|
${code}
|
||||||
|
})`;
|
||||||
|
const f = eval(wrappedCode);
|
||||||
|
f(state, Consts)
|
||||||
|
.then(() => {
|
||||||
|
markDone();
|
||||||
|
})
|
||||||
|
.catch((e: any) => {
|
||||||
|
markDone(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function runPlainCode() {
|
||||||
|
const wrappedCode = `(function(helper, Consts) {
|
||||||
|
${code}
|
||||||
|
})`;
|
||||||
|
const f = eval(wrappedCode);
|
||||||
|
f(state, Consts);
|
||||||
|
markDone();
|
||||||
|
}
|
||||||
|
|
||||||
function markDone(...args: any[]) {
|
function markDone(...args: any[]) {
|
||||||
if (!state.asyncTest) {
|
state.markTestDone(...args);
|
||||||
state.markTestDone(...args);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -648,7 +648,7 @@ export class TestState {
|
|||||||
const actualText = textEdit.newText;
|
const actualText = textEdit.newText;
|
||||||
const expectedText: string = Object.values(files)[0];
|
const expectedText: string = Object.values(files)[0];
|
||||||
|
|
||||||
if (actualText != expectedText) {
|
if (actualText !== expectedText) {
|
||||||
this._raiseError(
|
this._raiseError(
|
||||||
`doesn't contain expected result: ${stringify(expectedText)}, actual: ${stringify(actualText)}`
|
`doesn't contain expected result: ${stringify(expectedText)}, actual: ${stringify(actualText)}`
|
||||||
);
|
);
|
||||||
@ -774,10 +774,10 @@ export class TestState {
|
|||||||
this._verifyTextMatches(this._rangeText(this._getOnlyRange()), !!includeWhiteSpace, expectedText);
|
this._verifyTextMatches(this._rangeText(this._getOnlyRange()), !!includeWhiteSpace, expectedText);
|
||||||
}
|
}
|
||||||
|
|
||||||
verifyCompletion(
|
async verifyCompletion(
|
||||||
verifyMode: 'exact' | 'included' | 'excluded',
|
verifyMode: 'exact' | 'included' | 'excluded',
|
||||||
map: { [marker: string]: { completions: { label: string; documentation?: { kind: string; value: string } }[] } }
|
map: { [marker: string]: { completions: { label: string; documentation?: { kind: string; value: string } }[] } }
|
||||||
) {
|
): Promise<void> {
|
||||||
this._analyze();
|
this._analyze();
|
||||||
|
|
||||||
for (const marker of this.getMarkers()) {
|
for (const marker of this.getMarkers()) {
|
||||||
@ -785,7 +785,7 @@ export class TestState {
|
|||||||
const expectedCompletions = map[this.getMarkerName(marker)].completions;
|
const expectedCompletions = map[this.getMarkerName(marker)].completions;
|
||||||
const completionPosition = this._convertOffsetToPosition(filePath, marker.position);
|
const completionPosition = this._convertOffsetToPosition(filePath, marker.position);
|
||||||
|
|
||||||
const result = this.program.getCompletionsForPosition(
|
const result = await this.program.getCompletionsForPosition(
|
||||||
filePath,
|
filePath,
|
||||||
completionPosition,
|
completionPosition,
|
||||||
this.workspace.rootPath,
|
this.workspace.rootPath,
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
MSG = 'hello'
|
@ -0,0 +1,2 @@
|
|||||||
|
from vendored1 import MSG
|
||||||
|
print(MSG)
|
26
server/src/tests/samples/threePartVersion1.py
Normal file
26
server/src/tests/samples/threePartVersion1.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
from datetime import datetime, timezone, timedelta
|
||||||
|
from typing import overload, Optional
|
||||||
|
|
||||||
|
# Overload was broken before 3.5.2.
|
||||||
|
# This sort of hack is seen in some type-annotated code to prevent crashes.
|
||||||
|
if sys.version_info < (3, 5, 2):
|
||||||
|
def overload(f):
|
||||||
|
return f
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def from_json_timestamp(ts: int) -> datetime:
|
||||||
|
...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def from_json_timestamp(ts: None) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
def from_json_timestamp(ts: Optional[int]) -> Optional[datetime]:
|
||||||
|
return None if ts is None else (datetime(1970, 1, 1, tzinfo=timezone.utc) + timedelta(milliseconds=ts))
|
||||||
|
|
||||||
|
result1: datetime = from_json_timestamp(2418049)
|
||||||
|
result3: None = from_json_timestamp(None)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user