dunder_lookup.py 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. # Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
  2. # For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
  3. # Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
  4. """Contains logic for retrieving special methods.
  5. This implementation does not rely on the dot attribute access
  6. logic, found in ``.getattr()``. The difference between these two
  7. is that the dunder methods are looked with the type slots
  8. (you can find more about these here
  9. http://lucumr.pocoo.org/2014/8/16/the-python-i-would-like-to-see/)
  10. As such, the lookup for the special methods is actually simpler than
  11. the dot attribute access.
  12. """
  13. import itertools
  14. import astroid
  15. from astroid.exceptions import AttributeInferenceError
  16. def _lookup_in_mro(node, name) -> list:
  17. attrs = node.locals.get(name, [])
  18. nodes = itertools.chain.from_iterable(
  19. ancestor.locals.get(name, []) for ancestor in node.ancestors(recurs=True)
  20. )
  21. values = list(itertools.chain(attrs, nodes))
  22. if not values:
  23. raise AttributeInferenceError(attribute=name, target=node)
  24. return values
  25. def lookup(node, name) -> list:
  26. """Lookup the given special method name in the given *node*.
  27. If the special method was found, then a list of attributes
  28. will be returned. Otherwise, `astroid.AttributeInferenceError`
  29. is going to be raised.
  30. """
  31. if isinstance(
  32. node, (astroid.List, astroid.Tuple, astroid.Const, astroid.Dict, astroid.Set)
  33. ):
  34. return _builtin_lookup(node, name)
  35. if isinstance(node, astroid.Instance):
  36. return _lookup_in_mro(node, name)
  37. if isinstance(node, astroid.ClassDef):
  38. return _class_lookup(node, name)
  39. raise AttributeInferenceError(attribute=name, target=node)
  40. def _class_lookup(node, name) -> list:
  41. metaclass = node.metaclass()
  42. if metaclass is None:
  43. raise AttributeInferenceError(attribute=name, target=node)
  44. return _lookup_in_mro(metaclass, name)
  45. def _builtin_lookup(node, name) -> list:
  46. values = node.locals.get(name, [])
  47. if not values:
  48. raise AttributeInferenceError(attribute=name, target=node)
  49. return values