util.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. # Copyright (c) 2015-2018 Claudiu Popa <pcmanticore@gmail.com>
  2. # Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
  3. # Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
  4. # Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
  5. # Copyright (c) 2020-2021 hippo91 <guillaume.peillex@gmail.com>
  6. # Copyright (c) 2020 Bryce Guinta <bryce.guinta@protonmail.com>
  7. # Copyright (c) 2021 Pierre Sassoulas <pierre.sassoulas@gmail.com>
  8. # Copyright (c) 2021 Marc Mueller <30130371+cdce8p@users.noreply.github.com>
  9. # Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
  10. # For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
  11. import importlib
  12. import warnings
  13. import lazy_object_proxy
  14. def lazy_descriptor(obj):
  15. class DescriptorProxy(lazy_object_proxy.Proxy):
  16. def __get__(self, instance, owner=None):
  17. return self.__class__.__get__(self, instance)
  18. return DescriptorProxy(obj)
  19. def lazy_import(module_name):
  20. return lazy_object_proxy.Proxy(
  21. lambda: importlib.import_module("." + module_name, "astroid")
  22. )
  23. @object.__new__
  24. class Uninferable:
  25. """Special inference object, which is returned when inference fails."""
  26. def __repr__(self):
  27. return "Uninferable"
  28. __str__ = __repr__
  29. def __getattribute__(self, name):
  30. if name == "next":
  31. raise AttributeError("next method should not be called")
  32. if name.startswith("__") and name.endswith("__"):
  33. return object.__getattribute__(self, name)
  34. if name == "accept":
  35. return object.__getattribute__(self, name)
  36. return self
  37. def __call__(self, *args, **kwargs):
  38. return self
  39. def __bool__(self):
  40. return False
  41. __nonzero__ = __bool__
  42. def accept(self, visitor):
  43. return visitor.visit_uninferable(self)
  44. class BadOperationMessage:
  45. """Object which describes a TypeError occurred somewhere in the inference chain
  46. This is not an exception, but a container object which holds the types and
  47. the error which occurred.
  48. """
  49. class BadUnaryOperationMessage(BadOperationMessage):
  50. """Object which describes operational failures on UnaryOps."""
  51. def __init__(self, operand, op, error):
  52. self.operand = operand
  53. self.op = op
  54. self.error = error
  55. @property
  56. def _object_type_helper(self):
  57. helpers = lazy_import("helpers")
  58. return helpers.object_type
  59. def _object_type(self, obj):
  60. objtype = self._object_type_helper(obj)
  61. if objtype is Uninferable:
  62. return None
  63. return objtype
  64. def __str__(self):
  65. if hasattr(self.operand, "name"):
  66. operand_type = self.operand.name
  67. else:
  68. object_type = self._object_type(self.operand)
  69. if hasattr(object_type, "name"):
  70. operand_type = object_type.name
  71. else:
  72. # Just fallback to as_string
  73. operand_type = object_type.as_string()
  74. msg = "bad operand type for unary {}: {}"
  75. return msg.format(self.op, operand_type)
  76. class BadBinaryOperationMessage(BadOperationMessage):
  77. """Object which describes type errors for BinOps."""
  78. def __init__(self, left_type, op, right_type):
  79. self.left_type = left_type
  80. self.right_type = right_type
  81. self.op = op
  82. def __str__(self):
  83. msg = "unsupported operand type(s) for {}: {!r} and {!r}"
  84. return msg.format(self.op, self.left_type.name, self.right_type.name)
  85. def _instancecheck(cls, other):
  86. wrapped = cls.__wrapped__
  87. other_cls = other.__class__
  88. is_instance_of = wrapped is other_cls or issubclass(other_cls, wrapped)
  89. warnings.warn(
  90. "%r is deprecated and slated for removal in astroid "
  91. "2.0, use %r instead" % (cls.__class__.__name__, wrapped.__name__),
  92. PendingDeprecationWarning,
  93. stacklevel=2,
  94. )
  95. return is_instance_of
  96. def proxy_alias(alias_name, node_type):
  97. """Get a Proxy from the given name to the given node type."""
  98. proxy = type(
  99. alias_name,
  100. (lazy_object_proxy.Proxy,),
  101. {
  102. "__class__": object.__dict__["__class__"],
  103. "__instancecheck__": _instancecheck,
  104. },
  105. )
  106. return proxy(lambda: node_type)