util.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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. from __future__ import annotations
  5. import importlib
  6. import sys
  7. import warnings
  8. from typing import Any
  9. import lazy_object_proxy
  10. if sys.version_info >= (3, 8):
  11. from typing import Final, Literal
  12. else:
  13. from typing_extensions import Final, Literal
  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: str) -> lazy_object_proxy.Proxy:
  20. return lazy_object_proxy.Proxy(
  21. lambda: importlib.import_module("." + module_name, "astroid")
  22. )
  23. class UninferableBase:
  24. """Special inference object, which is returned when inference fails.
  25. This is meant to be used as a singleton. Use astroid.util.Uninferable to access it.
  26. """
  27. def __repr__(self) -> Literal["Uninferable"]:
  28. return "Uninferable"
  29. __str__ = __repr__
  30. def __getattribute__(self, name: str) -> Any:
  31. if name == "next":
  32. raise AttributeError("next method should not be called")
  33. if name.startswith("__") and name.endswith("__"):
  34. return object.__getattribute__(self, name)
  35. if name == "accept":
  36. return object.__getattribute__(self, name)
  37. return self
  38. def __call__(self, *args: Any, **kwargs: Any) -> UninferableBase:
  39. return self
  40. def __bool__(self) -> Literal[False]:
  41. return False
  42. __nonzero__ = __bool__
  43. def accept(self, visitor):
  44. return visitor.visit_uninferable(self)
  45. Uninferable: Final = UninferableBase()
  46. class BadOperationMessage:
  47. """Object which describes a TypeError occurred somewhere in the inference chain.
  48. This is not an exception, but a container object which holds the types and
  49. the error which occurred.
  50. """
  51. class BadUnaryOperationMessage(BadOperationMessage):
  52. """Object which describes operational failures on UnaryOps."""
  53. def __init__(self, operand, op, error):
  54. self.operand = operand
  55. self.op = op
  56. self.error = error
  57. @property
  58. def _object_type_helper(self):
  59. helpers = lazy_import("helpers")
  60. return helpers.object_type
  61. def _object_type(self, obj):
  62. objtype = self._object_type_helper(obj)
  63. if isinstance(objtype, UninferableBase):
  64. return None
  65. return objtype
  66. def __str__(self) -> str:
  67. if hasattr(self.operand, "name"):
  68. operand_type = self.operand.name
  69. else:
  70. object_type = self._object_type(self.operand)
  71. if hasattr(object_type, "name"):
  72. operand_type = object_type.name
  73. else:
  74. # Just fallback to as_string
  75. operand_type = object_type.as_string()
  76. msg = "bad operand type for unary {}: {}"
  77. return msg.format(self.op, operand_type)
  78. class BadBinaryOperationMessage(BadOperationMessage):
  79. """Object which describes type errors for BinOps."""
  80. def __init__(self, left_type, op, right_type):
  81. self.left_type = left_type
  82. self.right_type = right_type
  83. self.op = op
  84. def __str__(self) -> str:
  85. msg = "unsupported operand type(s) for {}: {!r} and {!r}"
  86. return msg.format(self.op, self.left_type.name, self.right_type.name)
  87. def _instancecheck(cls, other) -> bool:
  88. wrapped = cls.__wrapped__
  89. other_cls = other.__class__
  90. is_instance_of = wrapped is other_cls or issubclass(other_cls, wrapped)
  91. warnings.warn(
  92. "%r is deprecated and slated for removal in astroid "
  93. "2.0, use %r instead" % (cls.__class__.__name__, wrapped.__name__),
  94. PendingDeprecationWarning,
  95. stacklevel=2,
  96. )
  97. return is_instance_of
  98. def proxy_alias(alias_name, node_type):
  99. """Get a Proxy from the given name to the given node type."""
  100. proxy = type(
  101. alias_name,
  102. (lazy_object_proxy.Proxy,),
  103. {
  104. "__class__": object.__dict__["__class__"],
  105. "__instancecheck__": _instancecheck,
  106. },
  107. )
  108. return proxy(lambda: node_type)
  109. def check_warnings_filter() -> bool:
  110. """Return True if any other than the default DeprecationWarning filter is enabled.
  111. https://docs.python.org/3/library/warnings.html#default-warning-filter
  112. """
  113. return any(
  114. issubclass(DeprecationWarning, filter[2])
  115. and filter[0] != "ignore"
  116. and filter[3] != "__main__"
  117. for filter in warnings.filters
  118. )