Changed Pyright import resolution order to match that described in PEP 561. In particular, stubs in stubPath are now searched prior to user code, and third-party typeshed stubs are searched only after installed packages are searched for stub packages and inline stubs. There is one place where Pyright's import resolution still differs from a strict interpretation of PEP 561: it searches stdlib typeshed stubs first (unless typeshedPath is defined, in which case it searches there). This is more consistent with the way the Python interpreter resolves stdlib types.

This commit is contained in:
Eric Traut 2020-07-11 13:39:32 -07:00
parent 5a57aaa14f
commit 19c8a47ff8
2 changed files with 49 additions and 45 deletions

View File

@ -7,22 +7,23 @@ For absolute (non-relative) paths, Pyright resolves imports in the following ord
1. Try to resolve using a **stdlib typeshed stub**. If the `typeshedPath` is configured, use this instead of the typeshed stubs that are packaged with Pyright. This allows for the use of a newer or a patched version of the typeshed stdlib stubs.
2. Try to resolve using code within the workspace.
2. Try to resolve using the **stubPath** as defined in the `stubPath` config entry or the `python.analysis.stubPath` setting.
3. Try to resolve using code within the workspace.
a. Try to resolve relative to the **root directory of the execution environment**. If no execution environments are specified in the config file, use the root of the workspace. For more information about execution environments, refer to the [configuration documentation](https://github.com/microsoft/pyright/blob/master/docs/configuration.md#execution-environment-options).
b. Try to resolve using any of the **extra paths** defined for the execution environment in the config file. If no execution environment applies, use the `python.analysis.extraPaths` setting. Extra paths are searched in the order in which they are provided in the config file or setting.
c. If no execution environment or extraPaths are configured, try to resolve using the local directory `src`. It is common for Python projects to place local source files within a directory of this name.
4. Try to resolve using **type stubs found within installed packages**. Pyright uses the configured Python environment to determine whether a package has been installed. For more details about how to configure your Python environment for Pyright, see below. If a Python environment is configured, Pyright looks in the `lib/site-packages`, `Lib/site-packages`, or `python*/site-packages` subdirectory. If no site-packages directory can be found Pyright attempts to run the configured Python interpreter and ask it for its search paths. If no Python environment is configured, Pyright will use the default Python interpreter by invoking `python`.
a. For a given package, try to resolve first using a **stub package**. Stub packages, as defined in [PEP 561](https://www.python.org/dev/peps/pep-0561/#type-checker-module-resolution-order), are named the same as the original package but with `-stubs` appended.
b. Try to resolve using an **inline stub**, a “.pyi” file that ships within the package.
3. Try to resolve using the **stubPath** as defined in the `stubPath` config entry or the `python.analysis.stubPath` setting.
5. Try to resolve using a **third-party typeshed** stub. If the `typeshedPath` is configured, use this instead of the typeshed stubs that are packaged with Pyright. This allows for the use of a newer or a patched version of the typeshed third-party stubs.
4. Try to resolve using a **third-party typeshed** stub. If the `typeshedPath` configured, use this instead of the typeshed stubs that are packaged with Pyright. This allows for the use of a newer or a patched version of the typeshed stdlib stubs.
6. If the `pyright.useLibraryCodeForTypes` is set to true (or the `--lib` command-line argument was specified), try to resolve using the **library implementation** (“.py” file). Some “.py” files may contain partial or complete type annotations. Pyright will use type annotations that are provided and do its best to infer any missing type information.
5. Try to resolve using the **packages installed in the configured Python environment**. For more details about how to configure your Python environment for Pyright, see below. If a Python environment is configured, Pyright looks in the `lib/site-packages`, `Lib/site-packages`, or `python*/site-packages` subdirectory. If no site-packages directory can be found Pyright attempts to run the configured Python interpreter and ask it for its search paths. If no Python environment is configured, Pyright will use the default Python interpreter by invoking `python`.
a. For a given package, try to resolve first using a **stub package**. Stub packages, as defined in [PEP 561](https://www.python.org/dev/peps/pep-0561/#type-checker-module-resolution-order) are named the same as the original package but with `-stubs` appended.
b. Try to resolve using an **inline stub**, a “.pyi” file that ships alongside the corresponding “.py” file within the package.
c. If the `pyright.useLibraryCodeForTypes` is set to true (or the `--lib` command-line argument was specified), try to resolve using the **library implementation** (“.py” file). Some “.py” files may contain partial or complete type annotations. Pyright will use as much type information as it can find and do its best to infer any missing type information.
## Configuring Your Python Environment

View File

@ -154,6 +154,26 @@ export class ImportResolver {
}
}
if (allowPyi) {
// Check for a local stub file using stubPath.
if (this._configOptions.stubPath) {
importFailureInfo.push(`Looking in stubPath '${this._configOptions.stubPath}'`);
const typingsImport = this.resolveAbsoluteImport(
this._configOptions.stubPath,
moduleDescriptor,
importName,
importFailureInfo
);
if (typingsImport && typingsImport.isImportFound) {
// We will treat typings files as "local" rather than "third party".
typingsImport.importType = ImportType.Local;
typingsImport.isLocalTypingsFile = true;
return typingsImport;
}
}
}
let bestResultSoFar: ImportResult | undefined;
// Look for it in the root directory of the execution environment.
@ -200,40 +220,6 @@ export class ImportResolver {
}
}
if (allowPyi) {
// Check for a local stub file using stubPath.
if (this._configOptions.stubPath) {
importFailureInfo.push(`Looking in stubPath '${this._configOptions.stubPath}'`);
const typingsImport = this.resolveAbsoluteImport(
this._configOptions.stubPath,
moduleDescriptor,
importName,
importFailureInfo
);
if (typingsImport && typingsImport.isImportFound) {
// We will treat typings files as "local" rather than "third party".
typingsImport.importType = ImportType.Local;
typingsImport.isLocalTypingsFile = true;
return typingsImport;
}
}
// Check for a third-party typeshed file.
importFailureInfo.push(`Looking for typeshed path`);
const typeshedImport = this._findTypeshedPath(
execEnv,
moduleDescriptor,
importName,
/* isStdLib */ false,
importFailureInfo
);
if (typeshedImport) {
typeshedImport.isTypeshedFile = true;
return typeshedImport;
}
}
// Look for the import in the list of third-party packages.
const pythonSearchPaths = this._getPythonSearchPaths(execEnv, importFailureInfo);
if (pythonSearchPaths.length > 0) {
@ -248,9 +234,10 @@ export class ImportResolver {
importFailureInfo,
/* allowPartial */ true,
/* allowNativeLib */ true,
/* allowStubsFolder */ true,
/* allowStubPackages */ true,
allowPyi
);
if (thirdPartyImport) {
thirdPartyImport.importType = ImportType.ThirdParty;
@ -287,6 +274,22 @@ export class ImportResolver {
return extraResults;
}
if (allowPyi) {
// Check for a third-party typeshed file.
importFailureInfo.push(`Looking for typeshed path`);
const typeshedImport = this._findTypeshedPath(
execEnv,
moduleDescriptor,
importName,
/* isStdLib */ false,
importFailureInfo
);
if (typeshedImport) {
typeshedImport.isTypeshedFile = true;
return typeshedImport;
}
}
// We weren't able to find an exact match, so return the best
// partial match.
return bestResultSoFar;
@ -401,7 +404,7 @@ export class ImportResolver {
// We get the relative path(s) of the stub to its import root(s),
// in theory there can be more than one, then look for source
// files in all the import roots using the same relative path(s).
const importRootPaths = this.getImportRoots(execEnv, /*useTypeshedVersionedFolders*/ true);
const importRootPaths = this.getImportRoots(execEnv, /* useTypeshedVersionedFolders */ true);
const relativeStubPaths: string[] = [];
for (const importRootPath of importRootPaths) {
@ -874,7 +877,7 @@ export class ImportResolver {
importFailureInfo: string[],
allowPartial = false,
allowNativeLib = false,
allowStubsFolder = false,
allowStubPackages = false,
allowPyi = true
): ImportResult | undefined {
importFailureInfo.push(`Attempting to resolve using root path '${rootPath}'`);
@ -914,7 +917,7 @@ export class ImportResolver {
dirPath = combinePaths(dirPath, moduleDescriptor.nameParts[i]);
let foundDirectory = false;
if (allowPyi && allowStubsFolder) {
if (allowPyi && allowStubPackages) {
// PEP 561 indicates that package authors can ship their stubs
// separately from their package implementation by appending
// the string '-stubs' to its top-level directory name. We'll