Fixed a bug that led to a false negative when a non-Generic class has a metaclass that defines a __getitem__ method. Such a class should not be indexable within a type annotation, but pyright was not flagging this error. This addresses https://github.com/microsoft/pyright/discussions/4766#discussioncomment-5291138.

This commit is contained in:
Eric Traut 2023-03-13 12:24:55 -06:00
parent a28fb5d560
commit ce66cc21bb
3 changed files with 37 additions and 2 deletions

View File

@ -6608,7 +6608,10 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
if (
concreteSubtype.details.effectiveMetaclass &&
isInstantiableClass(concreteSubtype.details.effectiveMetaclass) &&
!ClassType.isBuiltIn(concreteSubtype.details.effectiveMetaclass, ['type', '_InitVarMeta'])
!ClassType.isBuiltIn(concreteSubtype.details.effectiveMetaclass, ['type', '_InitVarMeta']) &&
!concreteSubtype.details.mro.some(
(mroClass) => isClass(mroClass) && ClassType.isBuiltIn(mroClass, 'Generic')
)
) {
const itemMethodType = getTypeOfClassMember(
node,
@ -6618,6 +6621,19 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
/* diag */ undefined,
MemberAccessFlags.SkipAttributeAccessOverride | MemberAccessFlags.ConsiderMetaclassOnly
);
if (flags & EvaluatorFlags.ExpectingTypeAnnotation) {
// If the class doesn't derive from Generic, a type argument should not be allowed.
addDiagnostic(
AnalyzerNodeInfo.getFileInfo(node).diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
Localizer.Diagnostic.typeArgsExpectingNone().format({
name: printType(ClassType.cloneAsInstance(concreteSubtype)),
}),
node
);
}
if (itemMethodType) {
return getTypeOfIndexedObjectOrClass(node, concreteSubtype, usage).type;
}

View File

@ -1,7 +1,26 @@
# This sample tests pyright's ability to use metaclasses.
from ctypes import Array, c_uint64
from typing import Any, Generic, TypeVar
myArray1 = (c_uint64 * 5)()
myArray2: Array[c_uint64] = (c_uint64 * 5)()
T = TypeVar("T")
class CustomMeta(type):
def __getitem__(self, key: Any) -> "type[int]":
...
class Custom(metaclass=CustomMeta):
...
# This should generate an errro because the class isn't
# Generic even though it supports a metaclass with a
# __getitem__.
y: Custom[int]

View File

@ -43,7 +43,7 @@ test('Required3', () => {
test('Metaclass1', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['metaclass1.py']);
TestUtils.validateResults(analysisResults, 0);
TestUtils.validateResults(analysisResults, 1);
});
test('Metaclass2', () => {