Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Print union types with the pipe syntax #1794

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pytype/abstract/abstract_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ def test_signature_annotations(self):
),
)
self.assertEqual(
repr(sig), "def f(self: Any, *args: Tuple[Any, ...]) -> Any"
repr(sig), "def f(self: Any, *args: tuple[Any, ...]) -> Any"
)
self.assertIs(sig.annotations["self"], self._ctx.convert.unsolvable)
args_type = sig.annotations["args"]
Expand Down Expand Up @@ -1221,7 +1221,7 @@ def test_signature_func_output(self):
)
fp = self._ctx.pytd_convert.value_to_pytd_def(node, f, f.name)
f_str = (
"def test(a: str, b: int = ..., *c: str, d: Dict[str, int] = ...,"
"def test(a: str, b: int = ..., *c: str, d: dict[str, int] = ...,"
" e: int, **f: str) -> str: ..."
)
self.assertEqual(pytd_utils.Print(fp), f_str)
Expand Down
2 changes: 1 addition & 1 deletion pytype/abstract/mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ def get_special_attribute(self, node, name, valself):
class NestedAnnotation(metaclass=MixinMeta):
"""An annotation containing inner types, such as a Union.

For example, in `Union[int, str]`, `int` and `str` are the annotation's inner
For example, in `int | str`, `int` and `str` are the annotation's inner
types. Classes that inherit from this mixin should implement:

get_inner_types(): Returns a sequence of (key, typ) of the inner types. A
Expand Down
3 changes: 1 addition & 2 deletions pytype/annotation_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -521,8 +521,7 @@ def _log_illegal_params(self, illegal_params, stack, typ, name):
frame_name = method.name
details += f" for {desc} {frame_name!r}"
if "AnyStr" in out_of_scope_params:
str_type = "Union[str, bytes]"
details += f"\nNote: For all string types, use {str_type}."
details += "\nNote: For all string types, use `str | bytes`."
self.ctx.errorlog.invalid_annotation(stack, typ, details, name)

def eval_multi_arg_annotation(self, node, func, annot, stack):
Expand Down
52 changes: 26 additions & 26 deletions pytype/constant_folding_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,23 +257,23 @@ def test_prim(self):

def test_homogeneous_list(self):
val = self._convert(("list", int))
self.assertPytd(val, "List[int]")
self.assertPytd(val, "list[int]")

def test_heterogeneous_list(self):
val = self._convert(("list", (int, str)))
self.assertPytd(val, "List[Union[int, str]]")
self.assertPytd(val, "list[int | str]")

def test_homogeneous_map(self):
val = self._convert(("map", str, int))
self.assertPytd(val, "Dict[str, int]")
self.assertPytd(val, "dict[str, int]")

def test_heterogeneous_map(self):
val = self._convert(("map", (str, int), (("list", str), str)))
self.assertPytd(val, "Dict[Union[int, str], Union[List[str], str]]")
self.assertPytd(val, "dict[int | str, list[str] | str]")

def test_tuple(self):
val = self._convert(("tuple", str, int, bool))
self.assertPytd(val, "Tuple[str, int, bool]")
self.assertPytd(val, "tuple[str, int, bool]")


class PyvalTest(TypeBuilderTestBase):
Expand All @@ -291,7 +291,7 @@ def test_simple_list(self):
""")
a = defs["a"].data[0]
b = defs["b"].data[0]
self.assertPytd(a, "List[Union[int, str]]")
self.assertPytd(a, "list[int | str]")
self.assertPytd(b, "str")
self.assertEqual(a.pyval[0].data[0].pyval, 1)

Expand All @@ -303,9 +303,9 @@ def test_nested_list(self):
a = defs["a"].data[0]
b = defs["b"].data[0]
c = defs["c"].data[0]
t1 = "List[Union[int, str]]"
t2 = "List[int]"
self.assertPytd(a, f"List[Union[{t2}, {t1}]]")
t1 = "list[int | str]"
t2 = "list[int]"
self.assertPytd(a, f"list[{t2} | {t1}]")
self.assertPytd(b, t1)
self.assertPytd(c, t2)

Expand All @@ -318,12 +318,12 @@ def test_long_list(self):
b = defs["b"].data[0]
c = defs["c"].data[0]
d = defs["d"].data[0]
t1 = "List[int]"
t2 = "List[str]"
self.assertPytd(a, "List[Union[List[int], List[str]]]")
t1 = "list[int]"
t2 = "list[str]"
self.assertPytd(a, "list[list[int] | list[str]]")
self.assertPytd(b, t1)
self.assertPytd(c, t2)
self.assertPytd(d, "List[Union[List[int], List[str]]]")
self.assertPytd(d, "list[list[int] | list[str]]")

def test_long_list_of_tuples(self):
elts = [" (1, 2),", " ('a', False),"] * 82
Expand All @@ -334,12 +334,12 @@ def test_long_list_of_tuples(self):
b = defs["b"].data[0]
c = defs["c"].data[0]
d = defs["d"].data[0]
t1 = "Tuple[int, int]"
t2 = "Tuple[str, bool]"
self.assertPytd(a, f"List[Union[{t1}, {t2}]]")
t1 = "tuple[int, int]"
t2 = "tuple[str, bool]"
self.assertPytd(a, f"list[{t1} | {t2}]")
self.assertPytd(b, t1)
self.assertPytd(c, t2)
self.assertPytd(d, f"List[Union[{t1}, {t2}]]")
self.assertPytd(d, f"list[{t1} | {t2}]")

def test_simple_map(self):
defs = self._process("""
Expand All @@ -350,7 +350,7 @@ def test_simple_map(self):
a = defs["a"].data[0]
b = defs["b"].data[0]
c = defs["c"].data[0]
self.assertPytd(a, "Dict[str, Union[int, str]]")
self.assertPytd(a, "dict[str, int | str]")
self.assertPytd(b, "int")
self.assertPytd(c, "str")
self.assertEqual(a.pyval["b"].data[0].pyval, 1)
Expand All @@ -377,9 +377,9 @@ def test_nested_map(self):
b = defs["b"].data[0]
c = defs["c"].data[0]
d = defs["d"].data[0]
t1 = "List[Union[int, str]]"
t2 = "Dict[str, Union[bool, int]]"
self.assertPytd(a, f"Dict[str, Union[{t2}, {t1}]]")
t1 = "list[int | str]"
t2 = "dict[str, bool | int]"
self.assertPytd(a, f"dict[str, {t2} | {t1}]")
self.assertPytd(b, t1)
self.assertPytd(c, t2)
self.assertPytd(d, "int")
Expand All @@ -401,14 +401,14 @@ def test_long_map(self):
src = ["a = {"] + elts + ["}"]
defs = self._process("\n".join(src))
a = defs["a"].data[0]
self.assertPytd(a, "Dict[str, List[int]]")
self.assertPytd(a, "dict[str, list[int]]")

def test_long_map_with_tuple_keys(self):
elts = [f" ({i}, True): 'a'," for i in range(64)]
src = ["a = {"] + elts + ["}"]
defs = self._process("\n".join(src))
a = defs["a"].data[0]
self.assertPytd(a, "Dict[Tuple[int, bool], str]")
self.assertPytd(a, "dict[tuple[int, bool], str]")
self.assertFalse(a.pyval)

def test_nested_long_map(self):
Expand All @@ -426,10 +426,10 @@ def test_nested_long_map(self):
d = defs["d"].data[0]
e = defs["e"].data[0]
self.assertPytd(a, "int")
self.assertPytd(b, "Dict[str, List[Union[bool, int]]]")
self.assertPytd(c, "Dict[str, int]")
self.assertPytd(b, "dict[str, list[bool | int]]")
self.assertPytd(c, "dict[str, int]")
self.assertPytd(d, "int")
self.assertPytd(e, "List[Union[bool, int]]")
self.assertPytd(e, "list[bool | int]")


if __name__ == "__main__":
Expand Down
4 changes: 2 additions & 2 deletions pytype/convert_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ def f(*args: int): ...
(sig,) = f.signatures
annot = sig.signature.annotations["args"]
self.assertEqual(
pytd_utils.Print(annot.to_pytd_type_of_instance()), "Tuple[int, ...]"
pytd_utils.Print(annot.to_pytd_type_of_instance()), "tuple[int, ...]"
)

def test_function_with_starstarargs(self):
Expand All @@ -304,7 +304,7 @@ def f(**kwargs: int): ...
(sig,) = f.signatures
annot = sig.signature.annotations["kwargs"]
self.assertEqual(
pytd_utils.Print(annot.to_pytd_type_of_instance()), "Dict[str, int]"
pytd_utils.Print(annot.to_pytd_type_of_instance()), "dict[str, int]"
)

def test_mro(self):
Expand Down
1 change: 1 addition & 0 deletions pytype/errors/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ py_library(
pytype._utils
pytype.debug
pytype.pretty_printer_base
pytype.abstract.abstract
pytype.pytd.pytd
pytype.types.types
)
Expand Down
3 changes: 3 additions & 0 deletions pytype/errors/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from pytype import debug
from pytype import pretty_printer_base
from pytype import utils
from pytype.abstract import abstract
from pytype.errors import error_printer
from pytype.errors import error_types
from pytype.pytd import slots
Expand Down Expand Up @@ -1054,6 +1055,8 @@ def wrong_annotation_parameter_count(
):
"""Log an error for an annotation with the wrong number of parameters."""
base_type = self._pp.print_type_of_instance(annot)
if isinstance(annot, abstract.Union):
base_type = f"({base_type})"
full_type = base_type + self._print_params_helper(params)
if template:
templated_type = f"{base_type}[{', '.join(template)}]"
Expand Down
11 changes: 5 additions & 6 deletions pytype/load_pytd_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ def f() -> module1.x: ...
)
module2 = loader.import_name("module2")
(f,) = module2.Lookup("module2.f").signatures
self.assertEqual("List[int]", pytd_utils.Print(f.return_type))
self.assertEqual("list[int]", pytd_utils.Print(f.return_type))

def test_import_map_congruence(self):
with test_utils.Tempdir() as d:
Expand Down Expand Up @@ -782,10 +782,9 @@ def f() -> List[int]: ...
self.assertEqual(
pytd_utils.Print(bar),
textwrap.dedent("""
import typing
from builtins import list as List

def bar.f() -> typing.List[int]: ...
def bar.f() -> list[int]: ...
""").strip(),
)

Expand Down Expand Up @@ -976,7 +975,7 @@ def test_container(self):
""",
)
self.assertEqual(
pytd_utils.Print(ast.Lookup("b.Strings").type), "List[str]"
pytd_utils.Print(ast.Lookup("b.Strings").type), "list[str]"
)

def test_union(self):
Expand All @@ -992,13 +991,13 @@ def test_union(self):
""",
)
self.assertEqual(
pytd_utils.Print(ast.Lookup("b.Strings").type), "Union[str, List[str]]"
pytd_utils.Print(ast.Lookup("b.Strings").type), "str | list[str]"
)

def test_bad_parameterization(self):
with self.assertRaisesRegex(
load_pytd.BadDependencyError,
r"Union\[T, List\[T\]\] expected 1 parameters, got 2",
r"T \| list\[T\] expected 1 parameters, got 2",
):
self._import(
a="""
Expand Down
9 changes: 3 additions & 6 deletions pytype/pretty_printer_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,10 @@ def join_printed_types(self, typs: Iterable[str]) -> str:
if literal_contents:
literal = f"Literal[{', '.join(sorted(literal_contents))}]"
new_types.append(literal)
if len(new_types) > 1:
out = f"Union[{', '.join(sorted(new_types))}]"
else:
out = new_types[0]
new_types.sort()
if optional:
out = f"Optional[{out}]"
return out
new_types.append("None")
return " | ".join(new_types)
else:
# TODO(mdemello): change this to Never
return "nothing"
Expand Down
7 changes: 2 additions & 5 deletions pytype/pyi/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -589,11 +589,8 @@ def _is_builtin_or_typing_member(self, t):
if t.name is None:
return False
module, _, name = t.name.rpartition(".")
return (
not module
and name in pep484.BUILTIN_TO_TYPING
or module == "typing"
and name in pep484.ALL_TYPING_NAMES
return (not module and name in pep484.BUILTIN_TO_TYPING) or (
module == "typing" and name in pep484.ALL_TYPING_NAMES
)

def _check_for_illegal_parameters(self, base_type, parameters, is_callable):
Expand Down
2 changes: 1 addition & 1 deletion pytype/pyi/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ def visit_BinOp(self, node):

def visit_BoolOp(self, node):
if isinstance(node.op, astlib.Or):
raise ParseError("Deprecated syntax `x or y`; use `Union[x, y]` instead")
raise ParseError("Deprecated syntax `x or y`; use `x | y` instead")
else:
raise ParseError(f"Unexpected operator {node.op}")

Expand Down
Loading
Loading