Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/pyright-internal/src/analyzer/typeEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2616,7 +2616,8 @@ export function createTypeEvaluator(
effectiveFlags |=
MemberAccessFlags.SkipClassMembers |
MemberAccessFlags.SkipAttributeAccessOverride |
MemberAccessFlags.SkipTypeBaseClass;
MemberAccessFlags.SkipTypeBaseClass |
MemberAccessFlags.SkipBaseClasses;
effectiveFlags &= ~MemberAccessFlags.SkipInstanceMembers;
}

Expand Down
10 changes: 7 additions & 3 deletions packages/pyright-internal/src/analyzer/typeUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1773,7 +1773,11 @@ export function lookUpClassMember(
// Skip the "type" class as an optimization because it is known to not
// define any instance variables, and it's by far the most common metaclass.
if (metaclass && isClass(metaclass) && !ClassType.isBuiltIn(metaclass, 'type')) {
const metaMemberItr = getClassMemberIterator(metaclass, memberName, MemberAccessFlags.SkipClassMembers);
let metaFlags = MemberAccessFlags.SkipClassMembers;
if (isClassInstance(classType) || (flags & MemberAccessFlags.SkipTypeBaseClass) !== 0) {
metaFlags |= MemberAccessFlags.SkipTypeBaseClass;
}
const metaMemberItr = getClassMemberIterator(metaclass, memberName, metaFlags);
const metaMember = metaMemberItr.next()?.value;

// If the metaclass defines the member and we didn't hit an Unknown
Expand Down Expand Up @@ -2005,7 +2009,7 @@ export function* getClassIterator(classType: Type, flags = ClassIteratorFlags.De

// Should we ignore members on the 'object' base class?
if (flags & ClassIteratorFlags.SkipObjectBaseClass) {
if (isInstantiableClass(specializedMroClass)) {
if (isClass(specializedMroClass)) {
if (ClassType.isBuiltIn(specializedMroClass, 'object')) {
break;
}
Expand All @@ -2014,7 +2018,7 @@ export function* getClassIterator(classType: Type, flags = ClassIteratorFlags.De

// Should we ignore members on the 'type' base class?
if (flags & ClassIteratorFlags.SkipTypeBaseClass) {
if (isInstantiableClass(specializedMroClass)) {
if (isClass(specializedMroClass)) {
if (ClassType.isBuiltIn(specializedMroClass, 'type')) {
break;
}
Expand Down
27 changes: 27 additions & 0 deletions packages/pyright-internal/src/tests/samples/metaclass12.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# This sample tests that class instances with a custom metaclass
# do not inherit members from the metaclass (such as type.__dict__).

from typing import Any


class Meta(type):
def meta_method(self) -> str:
return "meta"


class A(metaclass=Meta):
pass


def func(a: A):
# This should resolve to dict[str, Any] from object, not MappingProxyType[str, Any] from type.
reveal_type(a.__dict__, expected_text="dict[str, Any]")

# This should generate an error because meta_method is on the metaclass,
# not the instance.
a.meta_method()


# Access through the class itself should resolve via the metaclass.
reveal_type(A.__dict__, expected_text="MappingProxyType[str, Any]")
reveal_type(A.meta_method(), expected_text="str")
5 changes: 5 additions & 0 deletions packages/pyright-internal/src/tests/typeEvaluator1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1200,6 +1200,11 @@ test('Metaclass11', () => {
TestUtils.validateResults(analysisResults, 4);
});

test('Metaclass12', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['metaclass12.py']);
TestUtils.validateResults(analysisResults, 1);
});

test('AssignmentExpr1', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['assignmentExpr1.py']);
TestUtils.validateResults(analysisResults, 7);
Expand Down