Skip to content

Commit

Permalink
Fix false positive error when calling an UnboundMethodValue (#548)
Browse files Browse the repository at this point in the history
  • Loading branch information
JelleZijlstra authored Nov 5, 2022
1 parent 15104a7 commit 079f8af
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 6 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

- Fix false positive error certain method calls on literals (#548)
- Preserve `Annotated` annotations on access to methods of
literals (#541)
- `allow_call` callables are now also called if the arguments
Expand Down
12 changes: 12 additions & 0 deletions pyanalyze/test_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,3 +351,15 @@ def capybara(x: Union[A, B]):
if hasattr(x, "a") and hasattr(x, "b"):
assert_is_value(x.a, AnyValue(AnySource.inference))
assert_is_value(x.b, AnyValue(AnySource.inference))

@assert_passes()
def test_hasattr_plus_call(self):
class X:
@classmethod
def types(cls):
return []

def capybara(x: X) -> None:
cls = X
if hasattr(cls, "types"): # E: value_always_true
assert_is_value(cls.types(), AnyValue(AnySource.unannotated))
14 changes: 8 additions & 6 deletions pyanalyze/value.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ def function(x: int, y: list[int], z: Any):

import collections.abc
import enum
import inspect
import textwrap
from collections import deque, OrderedDict
from dataclasses import dataclass, field, InitVar
Expand Down Expand Up @@ -544,17 +543,20 @@ class UnboundMethodValue(Value):
def get_method(self) -> Optional[Any]:
"""Return the runtime callable for this ``UnboundMethodValue``, or
None if it cannot be found."""
root = self.composite.value
if isinstance(root, AnnotatedValue):
root = root.value
if isinstance(root, KnownValue):
typ = root.val
else:
typ = root.get_type()
try:
typ = self.composite.value.get_type()
method = getattr(typ, self.attr_name)
if self.secondary_attr_name is not None:
method = getattr(method, self.secondary_attr_name)
# don't use unbound methods in py2
if inspect.ismethod(method) and method.__self__ is None:
method = method.__func__
return method
except AttributeError:
return None
return method

def is_type(self, typ: type) -> bool:
return isinstance(self.get_method(), typ)
Expand Down

0 comments on commit 079f8af

Please sign in to comment.