Skip to content

Commit

Permalink
Do not call .mro() method on non-types (#587)
Browse files Browse the repository at this point in the history
  • Loading branch information
JelleZijlstra authored Jan 16, 2023
1 parent 5772405 commit 78ee166
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 50 deletions.
1 change: 1 addition & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased

- Do not call `.mro()` method on non-types (#587)
- Add `class_attribute_transformers` hook (#585)
- Support for PEP 702 (`@typing.deprecated`) (#578)
- Simplify import handling; stop trying to import modules at type checking time (#566)
Expand Down
101 changes: 51 additions & 50 deletions pyanalyze/attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,61 +471,62 @@ def _get_attribute_from_mro(
if attr_type is not None:
return (attr_type, typ, False)

try:
mro = list(typ.mro())
except Exception:
# broken mro method
pass
else:
for base_cls in mro:
if ctx.skip_mro and base_cls is not typ:
continue

typeshed_type = ctx.get_attribute_from_typeshed(
base_cls, on_class=on_class or ctx.skip_unwrap
)
if typeshed_type is not UNINITIALIZED_VALUE:
if ctx.prefer_typeshed:
return typeshed_type, base_cls, False
# If it's a callable, we'll probably do better
# getting the attribute from the type ourselves,
# because we may have our own implementation.
if not isinstance(typeshed_type, CallableValue):
return typeshed_type, base_cls, False
if safe_isinstance(typ, type):
try:
mro = list(type.mro(typ))
except Exception:
# broken mro method
pass
else:
for base_cls in mro:
if ctx.skip_mro and base_cls is not typ:
continue

try:
base_dict = base_cls.__dict__
except Exception:
continue

try:
# Make sure to use only __annotations__ that are actually on this
# class, not ones inherited from a base class.
annotations = base_dict["__annotations__"]
except Exception:
pass
else:
attr_type = type_from_annotations(
annotations, ctx.attr, ctx=AnnotationsContext(ctx, base_cls)
typeshed_type = ctx.get_attribute_from_typeshed(
base_cls, on_class=on_class or ctx.skip_unwrap
)
if attr_type is not None:
return (attr_type, base_cls, False)

try:
# Make sure we use only the object from this class, but do invoke
# the descriptor protocol with getattr.
base_dict[ctx.attr]
except Exception:
pass
else:
if typeshed_type is not UNINITIALIZED_VALUE:
if ctx.prefer_typeshed:
return typeshed_type, base_cls, False
# If it's a callable, we'll probably do better
# getting the attribute from the type ourselves,
# because we may have our own implementation.
if not isinstance(typeshed_type, CallableValue):
return typeshed_type, base_cls, False

try:
base_dict = base_cls.__dict__
except Exception:
continue

try:
val = KnownValue(getattr(typ, ctx.attr))
# Make sure to use only __annotations__ that are actually on this
# class, not ones inherited from a base class.
annotations = base_dict["__annotations__"]
except Exception:
val = AnyValue(AnySource.inference)
return val, base_cls, True
pass
else:
attr_type = type_from_annotations(
annotations, ctx.attr, ctx=AnnotationsContext(ctx, base_cls)
)
if attr_type is not None:
return (attr_type, base_cls, False)

if typeshed_type is not UNINITIALIZED_VALUE:
return typeshed_type, base_cls, False
try:
# Make sure we use only the object from this class, but do invoke
# the descriptor protocol with getattr.
base_dict[ctx.attr]
except Exception:
pass
else:
try:
val = KnownValue(getattr(typ, ctx.attr))
except Exception:
val = AnyValue(AnySource.inference)
return val, base_cls, True

if typeshed_type is not UNINITIALIZED_VALUE:
return typeshed_type, base_cls, False

attrs_type = get_attrs_attribute(typ, ctx)
if attrs_type is not None:
Expand Down

0 comments on commit 78ee166

Please sign in to comment.