Skip to content

Commit

Permalink
Added a way for the "verifytypes" feature to ignore partially-unknown…
Browse files Browse the repository at this point in the history
… types imported from external packages. To use this feature, append a "!" to the end of the package name provided after the "--verifytypes" option.
  • Loading branch information
msfterictraut committed Feb 2, 2021
1 parent ad408ae commit 438e2ab
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 4 deletions.
53 changes: 50 additions & 3 deletions packages/pyright-internal/src/analyzer/packageTypeVerifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export type AlternateSymbolNameMap = Map<string, string[]>;

export interface PackageTypeReport {
packageName: string;
ignoreUnknownTypesFromImports: boolean;
rootDirectory: string | undefined;
pyTypedPath: string | undefined;
symbolCount: number;
Expand Down Expand Up @@ -115,12 +116,13 @@ export class PackageTypeVerifier {
this._program = new Program(this._importResolver, this._configOptions);
}

verify(packageName: string): PackageTypeReport {
verify(packageName: string, ignoreUnknownTypesFromImports = false): PackageTypeReport {
const trimmedPackageName = packageName.trim();
const packageNameParts = trimmedPackageName.split('.');

const report: PackageTypeReport = {
packageName: packageNameParts[0],
ignoreUnknownTypesFromImports,
rootDirectory: this._getDirectoryForPackage(packageNameParts[0]),
pyTypedPath: undefined,
symbolCount: 0,
Expand Down Expand Up @@ -460,6 +462,11 @@ export class PackageTypeVerifier {
return !!name.match(/[a-z_]+/);
}

private _shouldIgnoreType(report: PackageTypeReport, fullTypeName: string) {
// If we're ignoring unknown types from other packages, see if we should skip.
return report.ignoreUnknownTypesFromImports && !fullTypeName.startsWith(report.packageName);
}

private _verifySymbolsInSymbolTable(
report: PackageTypeReport,
module: PackageModule,
Expand All @@ -469,6 +476,10 @@ export class PackageTypeVerifier {
publicSymbolMap: PublicSymbolMap,
currentSymbol: string
) {
if (this._shouldIgnoreType(report, scopeName)) {
return;
}

symbolTable.forEach((symbol, name) => {
if (
!isPrivateOrProtectedName(name) &&
Expand Down Expand Up @@ -501,7 +512,16 @@ export class PackageTypeVerifier {
}
} else {
const diag = new DiagnosticAddendum();
if (!this._validateTypeIsCompletelyKnown(symbolType, diag, publicSymbolMap, fullName, [])) {
if (
!this._validateTypeIsCompletelyKnown(
report,
symbolType,
diag,
publicSymbolMap,
fullName,
[]
)
) {
errorMessage =
`Type partially unknown for ${packageSymbolTypeText} "${fullName}"` +
diag.getString(diagnosticMaxDepth, diagnosticMaxLineCount);
Expand Down Expand Up @@ -621,6 +641,7 @@ export class PackageTypeVerifier {
// If the type contains a reference to a module or a class, determines
// whether all of the types used by that module or class are known.
private _validateTypeIsCompletelyKnown(
report: PackageTypeReport,
type: Type,
diag: DiagnosticAddendum,
publicSymbolMap: PublicSymbolMap,
Expand All @@ -647,6 +668,7 @@ export class PackageTypeVerifier {

case TypeCategory.Object: {
return this._validateTypeIsCompletelyKnown(
report,
type.classType,
diag,
publicSymbolMap,
Expand All @@ -660,6 +682,7 @@ export class PackageTypeVerifier {
for (const overload of type.overloads) {
if (
!this._validateTypeIsCompletelyKnown(
report,
overload,
diag.createAddendum(),
publicSymbolMap,
Expand All @@ -679,6 +702,7 @@ export class PackageTypeVerifier {
doForEachSubtype(type, (subtype) => {
if (
!this._validateTypeIsCompletelyKnown(
report,
subtype,
diag.createAddendum(),
publicSymbolMap,
Expand All @@ -696,6 +720,10 @@ export class PackageTypeVerifier {
case TypeCategory.Function: {
let isKnown = true;

if (this._shouldIgnoreType(report, type.details.fullName)) {
return true;
}

type.details.parameters.forEach((param) => {
// Skip nameless parameters like "*" and "/".
if (param.name) {
Expand All @@ -714,6 +742,7 @@ export class PackageTypeVerifier {
isKnown = false;
} else if (
!this._validateTypeIsCompletelyKnown(
report,
param.type,
subDiag.createAddendum(),
publicSymbolMap,
Expand All @@ -739,6 +768,7 @@ export class PackageTypeVerifier {
isKnown = false;
} else if (
!this._validateTypeIsCompletelyKnown(
report,
type.details.declaredReturnType,
subDiag.createAddendum(),
publicSymbolMap,
Expand Down Expand Up @@ -767,7 +797,12 @@ export class PackageTypeVerifier {
}

case TypeCategory.Class: {
if (this._shouldIgnoreType(report, type.details.fullName)) {
return true;
}

const typeInfo = this._validateClassTypeIsCompletelyKnown(
report,
type,
publicSymbolMap,
currentSymbol,
Expand Down Expand Up @@ -808,7 +843,11 @@ export class PackageTypeVerifier {
}

case TypeCategory.Module: {
const typeInfo = this._validateModuleTypeIsCompletelyKnown(type, publicSymbolMap, typeStack);
if (this._shouldIgnoreType(report, type.moduleName)) {
return true;
}

const typeInfo = this._validateModuleTypeIsCompletelyKnown(report, type, publicSymbolMap, typeStack);

if (!typeInfo.isFullyKnown) {
diag.addAddendum(typeInfo.diag);
Expand All @@ -820,6 +859,7 @@ export class PackageTypeVerifier {
}

private _validateClassTypeIsCompletelyKnown(
report: PackageTypeReport,
type: ClassType,
publicSymbolMap: PublicSymbolMap,
currentSymbol: string,
Expand Down Expand Up @@ -889,6 +929,7 @@ export class PackageTypeVerifier {
diag.addAddendum(symbolDiag);
} else if (
!this._validateTypeIsCompletelyKnown(
report,
symbolType,
symbolDiag.createAddendum(),
publicSymbolMap,
Expand Down Expand Up @@ -920,6 +961,7 @@ export class PackageTypeVerifier {

if (isClass(mroType)) {
const mroClassInfo = this._validateClassTypeIsCompletelyKnown(
report,
mroType,
publicSymbolMap,
currentSymbol,
Expand Down Expand Up @@ -977,6 +1019,7 @@ export class PackageTypeVerifier {
isKnown = false;
} else if (!ClassType.isBuiltIn(type.details.effectiveMetaclass)) {
const metaclassInfo = this._validateClassTypeIsCompletelyKnown(
report,
type.details.effectiveMetaclass,
publicSymbolMap,
currentSymbol,
Expand Down Expand Up @@ -1015,6 +1058,7 @@ export class PackageTypeVerifier {
isKnown = false;
} else if (!ClassType.isBuiltIn(baseClass)) {
const classInfo = this._validateClassTypeIsCompletelyKnown(
report,
baseClass,
publicSymbolMap,
currentSymbol,
Expand Down Expand Up @@ -1075,6 +1119,7 @@ export class PackageTypeVerifier {
typeInfo!.isFullyKnown = false;
} else if (
!this._validateTypeIsCompletelyKnown(
report,
typeArg,
typeArgDiag,
publicSymbolMap,
Expand All @@ -1094,6 +1139,7 @@ export class PackageTypeVerifier {
}

private _validateModuleTypeIsCompletelyKnown(
report: PackageTypeReport,
type: ModuleType,
publicSymbolMap: PublicSymbolMap,
typeStack: string[]
Expand Down Expand Up @@ -1134,6 +1180,7 @@ export class PackageTypeVerifier {
isKnown = false;
} else if (
!this._validateTypeIsCompletelyKnown(
report,
symbolType,
symbolDiag.createAddendum(),
publicSymbolMap,
Expand Down
17 changes: 16 additions & 1 deletion packages/pyright-internal/src/pyright.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ interface PyrightJsonResults {

interface PyrightTypeCompletenessReport {
packageName: string;
ignoreUnknownTypesFromImports: boolean;
packageRootDirectory?: string;
pyTypedPath?: string;
symbolCount: number;
Expand Down Expand Up @@ -307,7 +308,17 @@ function verifyPackageTypes(
): never {
try {
const verifier = new PackageTypeVerifier(realFileSystem);
const report = verifier.verify(packageName);

// If the package name ends with a bang, we'll take that
// to mean that the caller wants to ignore unknown types from imports
// outside of the package.
let ignoreUnknownTypesFromImports = false;
if (packageName.endsWith('!')) {
ignoreUnknownTypesFromImports = true;
packageName = packageName.substr(0, packageName.length - 1);
}

const report = verifier.verify(packageName, ignoreUnknownTypesFromImports);
const jsonReport = buildTypeCompletenessReport(packageName, report);

if (outputJson) {
Expand Down Expand Up @@ -360,6 +371,7 @@ function buildTypeCompletenessReport(packageName: string, completenessReport: Pa

report.typeCompleteness = {
packageName,
ignoreUnknownTypesFromImports: completenessReport.ignoreUnknownTypesFromImports,
packageRootDirectory: completenessReport.rootDirectory,
pyTypedPath: completenessReport.pyTypedPath,
symbolCount: completenessReport.symbolCount,
Expand Down Expand Up @@ -450,6 +462,9 @@ function printTypeCompletenessReportText(results: PyrightJsonResults, verboseOut
console.log('');
console.log(`Public symbols: ${completenessReport.symbolCount}`);
console.log(` Symbols with unknown type: ${completenessReport.unknownTypeCount}`);
if (completenessReport.ignoreUnknownTypesFromImports) {
console.log(` (Ignoring unknown types imported from other packages)`);
}
console.log(` Functions with missing docstring: ${completenessReport.missingFunctionDocStringCount}`);
console.log(` Functions with missing default param: ${completenessReport.missingDefaultParamCount}`);
console.log(` Classes with missing docstring: ${completenessReport.missingClassDocStringCount}`);
Expand Down

0 comments on commit 438e2ab

Please sign in to comment.