summaryrefslogtreecommitdiff
path: root/astroid
diff options
context:
space:
mode:
authorMarc Mueller <30130371+cdce8p@users.noreply.github.com>2023-01-30 09:57:17 +0100
committerGitHub <noreply@github.com>2023-01-30 09:57:17 +0100
commit156db06cfb80c1d247aa963fb8661fe69502a45f (patch)
tree56e227025ab5d0cf40e241359b94e941a6ebee95 /astroid
parent054519205e3052d91c1a4a6b695fd245c09ddfc4 (diff)
downloadastroid-git-156db06cfb80c1d247aa963fb8661fe69502a45f.tar.gz
Add support for binary union types - Python 3.10 (#1977)
Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
Diffstat (limited to 'astroid')
-rw-r--r--astroid/bases.py43
-rw-r--r--astroid/inference.py25
-rw-r--r--astroid/raw_building.py18
3 files changed, 84 insertions, 2 deletions
diff --git a/astroid/bases.py b/astroid/bases.py
index d6c830c7..e930328e 100644
--- a/astroid/bases.py
+++ b/astroid/bases.py
@@ -121,11 +121,12 @@ class Proxy:
if proxied is None:
# This is a hack to allow calling this __init__ during bootstrapping of
# builtin classes and their docstrings.
- # For Const and Generator nodes the _proxied attribute is set during bootstrapping
+ # For Const, Generator, and UnionType nodes the _proxied attribute
+ # is set during bootstrapping
# as we first need to build the ClassDef that they can proxy.
# Thus, if proxied is None self should be a Const or Generator
# as that is the only way _proxied will be correctly set as a ClassDef.
- assert isinstance(self, (nodes.Const, Generator))
+ assert isinstance(self, (nodes.Const, Generator, UnionType))
else:
self._proxied = proxied
@@ -669,3 +670,41 @@ class AsyncGenerator(Generator):
def __str__(self) -> str:
return f"AsyncGenerator({self._proxied.name})"
+
+
+class UnionType(BaseInstance):
+ """Special node representing new style typing unions.
+
+ Proxied class is set once for all in raw_building.
+ """
+
+ _proxied: nodes.ClassDef
+
+ def __init__(
+ self,
+ left: UnionType | nodes.ClassDef | nodes.Const,
+ right: UnionType | nodes.ClassDef | nodes.Const,
+ parent: nodes.NodeNG | None = None,
+ ) -> None:
+ super().__init__()
+ self.parent = parent
+ self.left = left
+ self.right = right
+
+ def callable(self) -> Literal[False]:
+ return False
+
+ def bool_value(self, context: InferenceContext | None = None) -> Literal[True]:
+ return True
+
+ def pytype(self) -> Literal["types.UnionType"]:
+ return "types.UnionType"
+
+ def display_type(self) -> str:
+ return "UnionType"
+
+ def __repr__(self) -> str:
+ return f"<UnionType({self._proxied.name}) l.{self.lineno} at 0x{id(self)}>"
+
+ def __str__(self) -> str:
+ return f"UnionType({self._proxied.name})"
diff --git a/astroid/inference.py b/astroid/inference.py
index 59bc4eca..8ec191a4 100644
--- a/astroid/inference.py
+++ b/astroid/inference.py
@@ -15,6 +15,7 @@ from collections.abc import Callable, Generator, Iterable, Iterator
from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union
from astroid import bases, constraint, decorators, helpers, nodes, protocols, util
+from astroid.const import PY310_PLUS
from astroid.context import (
CallContext,
InferenceContext,
@@ -758,6 +759,14 @@ def _bin_op(
)
+def _bin_op_or_union_type(
+ left: bases.UnionType | nodes.ClassDef | nodes.Const,
+ right: bases.UnionType | nodes.ClassDef | nodes.Const,
+) -> Generator[InferenceResult, None, None]:
+ """Create a new UnionType instance for binary or, e.g. int | str."""
+ yield bases.UnionType(left, right)
+
+
def _get_binop_contexts(context, left, right):
"""Get contexts for binary operations.
@@ -817,6 +826,22 @@ def _get_binop_flow(
_bin_op(left, binary_opnode, op, right, context),
_bin_op(right, binary_opnode, op, left, reverse_context, reverse=True),
]
+
+ if (
+ PY310_PLUS
+ and op == "|"
+ and (
+ isinstance(left, (bases.UnionType, nodes.ClassDef))
+ or isinstance(left, nodes.Const)
+ and left.value is None
+ )
+ and (
+ isinstance(right, (bases.UnionType, nodes.ClassDef))
+ or isinstance(right, nodes.Const)
+ and right.value is None
+ )
+ ):
+ methods.extend([functools.partial(_bin_op_or_union_type, left, right)])
return methods
diff --git a/astroid/raw_building.py b/astroid/raw_building.py
index 8575f41e..453ce8b2 100644
--- a/astroid/raw_building.py
+++ b/astroid/raw_building.py
@@ -575,6 +575,24 @@ def _astroid_bootstrapping() -> None:
)
bases.AsyncGenerator._proxied = _AsyncGeneratorType
builder.object_build(bases.AsyncGenerator._proxied, types.AsyncGeneratorType)
+
+ if hasattr(types, "UnionType"):
+ _UnionTypeType = nodes.ClassDef(types.UnionType.__name__)
+ _UnionTypeType.parent = astroid_builtin
+ union_type_doc_node = (
+ nodes.Const(value=types.UnionType.__doc__)
+ if types.UnionType.__doc__
+ else None
+ )
+ _UnionTypeType.postinit(
+ bases=[],
+ body=[],
+ decorators=None,
+ doc_node=union_type_doc_node,
+ )
+ bases.UnionType._proxied = _UnionTypeType
+ builder.object_build(bases.UnionType._proxied, types.UnionType)
+
builtin_types = (
types.GetSetDescriptorType,
types.GeneratorType,