diff options
author | Marc Mueller <30130371+cdce8p@users.noreply.github.com> | 2023-01-30 09:57:17 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-30 09:57:17 +0100 |
commit | 156db06cfb80c1d247aa963fb8661fe69502a45f (patch) | |
tree | 56e227025ab5d0cf40e241359b94e941a6ebee95 /astroid | |
parent | 054519205e3052d91c1a4a6b695fd245c09ddfc4 (diff) | |
download | astroid-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.py | 43 | ||||
-rw-r--r-- | astroid/inference.py | 25 | ||||
-rw-r--r-- | astroid/raw_building.py | 18 |
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, |