util.py 4.7 KB

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