|
20 | 20 | from re import Pattern
|
21 | 21 | from typing import TYPE_CHECKING, Any, Union
|
22 | 22 |
|
| 23 | +import astroid |
23 | 24 | import astroid.exceptions
|
| 25 | +import astroid.helpers |
24 | 26 | from astroid import bases, nodes
|
25 | 27 |
|
26 | 28 | from pylint.checkers import BaseChecker, utils
|
@@ -1903,14 +1905,55 @@ def _detect_unsupported_alternative_union_syntax(self, node: nodes.BinOp) -> Non
|
1903 | 1905 | if not allowed_nested_syntax:
|
1904 | 1906 | self._check_unsupported_alternative_union_syntax(node)
|
1905 | 1907 |
|
| 1908 | + def _includes_version_compatible_overload(self, attrs: list): |
| 1909 | + """Check if a set of overloads of an operator includes one that |
| 1910 | + can be relied upon for our configured Python version. |
| 1911 | +
|
| 1912 | + If we are running under a Python 3.10+ runtime but configured for |
| 1913 | + pre-3.10 compatibility then Astroid will have inferred the |
| 1914 | + existence of __or__ / __ror__ on builtins.type, but these aren't |
| 1915 | + available in the configured version of Python. |
| 1916 | + """ |
| 1917 | + is_py310_builtin = all( |
| 1918 | + isinstance(attr, (nodes.FunctionDef, astroid.BoundMethod)) |
| 1919 | + and attr.parent.qname() == "builtins.type" |
| 1920 | + for attr in attrs |
| 1921 | + ) |
| 1922 | + return not is_py310_builtin or self._py310_plus |
| 1923 | + |
1906 | 1924 | def _check_unsupported_alternative_union_syntax(self, node: nodes.BinOp) -> None:
|
1907 |
| - """Check if left or right node is of type `type`.""" |
| 1925 | + """Check if left or right node is of type `type`. |
| 1926 | +
|
| 1927 | + If either is, and doesn't support an or operator via a metaclass, |
| 1928 | + infer that this is a mistaken attempt to use alternative union |
| 1929 | + syntax when not supported. |
| 1930 | + """ |
1908 | 1931 | msg = "unsupported operand type(s) for |"
|
1909 |
| - for n in (node.left, node.right): |
1910 |
| - n = astroid.helpers.object_type(n) |
1911 |
| - if isinstance(n, nodes.ClassDef) and is_classdef_type(n): |
1912 |
| - self.add_message("unsupported-binary-operation", args=msg, node=node) |
1913 |
| - break |
| 1932 | + left_obj = astroid.helpers.object_type(node.left) |
| 1933 | + right_obj = astroid.helpers.object_type(node.right) |
| 1934 | + left_is_type = False |
| 1935 | + right_is_type = False |
| 1936 | + |
| 1937 | + if isinstance(left_obj, nodes.ClassDef) and is_classdef_type(left_obj): |
| 1938 | + try: |
| 1939 | + attrs = left_obj.getattr("__or__") |
| 1940 | + if self._includes_version_compatible_overload(attrs): |
| 1941 | + return |
| 1942 | + left_is_type = True |
| 1943 | + except astroid.NotFoundError: |
| 1944 | + left_is_type = True |
| 1945 | + |
| 1946 | + if isinstance(right_obj, nodes.ClassDef) and is_classdef_type(right_obj): |
| 1947 | + try: |
| 1948 | + attrs = right_obj.getattr("__ror__") |
| 1949 | + if self._includes_version_compatible_overload(attrs): |
| 1950 | + return |
| 1951 | + right_is_type = True |
| 1952 | + except astroid.NotFoundError: |
| 1953 | + right_is_type = True |
| 1954 | + |
| 1955 | + if left_is_type or right_is_type: |
| 1956 | + self.add_message("unsupported-binary-operation", args=msg, node=node) |
1914 | 1957 |
|
1915 | 1958 | # TODO: This check was disabled (by adding the leading underscore)
|
1916 | 1959 | # due to false positives several years ago - can we re-enable it?
|
|
0 commit comments