dunder_lookup.py 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. # Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
  2. # For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
  3. # Copyright (c) https://github.com/pylint-dev/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. from __future__ import annotations
  14. import itertools
  15. from typing import TYPE_CHECKING
  16. import astroid
  17. from astroid import nodes
  18. from astroid.exceptions import AttributeInferenceError
  19. if TYPE_CHECKING:
  20. from astroid.context import InferenceContext
  21. def _lookup_in_mro(node, name) -> list:
  22. attrs = node.locals.get(name, [])
  23. nodes_ = itertools.chain.from_iterable(
  24. ancestor.locals.get(name, []) for ancestor in node.ancestors(recurs=True)
  25. )
  26. values = list(itertools.chain(attrs, nodes_))
  27. if not values:
  28. raise AttributeInferenceError(attribute=name, target=node)
  29. return values
  30. def lookup(
  31. node: nodes.NodeNG, name: str, context: InferenceContext | None = None
  32. ) -> list:
  33. """Lookup the given special method name in the given *node*.
  34. If the special method was found, then a list of attributes
  35. will be returned. Otherwise, `astroid.AttributeInferenceError`
  36. is going to be raised.
  37. """
  38. if isinstance(node, (nodes.List, nodes.Tuple, nodes.Const, nodes.Dict, nodes.Set)):
  39. return _builtin_lookup(node, name)
  40. if isinstance(node, astroid.Instance):
  41. return _lookup_in_mro(node, name)
  42. if isinstance(node, nodes.ClassDef):
  43. return _class_lookup(node, name, context=context)
  44. raise AttributeInferenceError(attribute=name, target=node)
  45. def _class_lookup(
  46. node: nodes.ClassDef, name: str, context: InferenceContext | None = None
  47. ) -> list:
  48. metaclass = node.metaclass(context=context)
  49. if metaclass is None:
  50. raise AttributeInferenceError(attribute=name, target=node)
  51. return _lookup_in_mro(metaclass, name)
  52. def _builtin_lookup(node, name) -> list:
  53. values = node.locals.get(name, [])
  54. if not values:
  55. raise AttributeInferenceError(attribute=name, target=node)
  56. return values