Use modern syntax in test-case files where possible (#9261)

This commit is contained in:
Alex Waygood
2022-11-23 19:58:11 +00:00
committed by GitHub
parent 956690dec2
commit 01e78c7e81
19 changed files with 73 additions and 33 deletions

View File

@@ -1,4 +1,5 @@
# pyright: reportUnnecessaryTypeIgnoreComment=true
from __future__ import annotations
from invoke import Context, task

View File

@@ -79,20 +79,26 @@ is because the purpose of this folder is to test the implications of typeshed
changes for end users, who will mainly be using `.py` files rather than `.pyi`
files.
Another difference to the rest of typeshed is that the test cases in this
directory cannot always use modern syntax for type hints.
Another difference to the rest of typeshed
(which stems from the fact that the test-case files are all `.py` files
rather than `.pyi` files)
is that the test cases cannot always use modern syntax for type hints.
While we can use `from __future__ import annotations` to enable the use of
modern typing syntax wherever possible,
type checkers may (correctly) emit errors if PEP 604 syntax or PEP 585 syntax
is used in a runtime context on lower versions of Python. For example:
For example, PEP 604
syntax (unions with a pipe `|` operator) is new in Python 3.10. While this
syntax can be used on older Python versions in a `.pyi` file, code using this
syntax will fail at runtime on Python <=3.9. Since the test cases all use `.py`
extensions, and since the tests need to pass on all Python versions >=3.7, PEP
604 syntax cannot be used in a test case. Use `typing.Union` and
`typing.Optional` instead.
```python
from __future__ import annotations
PEP 585 syntax can also not be used in the `test_cases` directory. Use
`typing.Tuple` instead of `tuple`, `typing.Callable` instead of
`collections.abc.Callable`, and `typing.Match` instead of `re.Match` (etc.).
from typing_extensions import assert_type
x: str | int # PEP 604 syntax: okay on Python >=3.7, due to __future__ annotations
assert_type(x, str | int) # Will fail at runtime on Python <3.10 (use typing.Union instead)
y: dict[str, int] # PEP 585 syntax: okay on Python >= 3.7, due to __future__ annotations
assert_type(y, dict[str, int]) # Will fail at runtime on Python <3.9 (use typing.Dict instead)
```
### Version-dependent tests

View File

@@ -1,13 +1,15 @@
from __future__ import annotations
from asyncio import iscoroutinefunction
from collections.abc import Awaitable, Callable, Coroutine
from typing import Any, Union
from typing import Any
from typing_extensions import assert_type
def test_iscoroutinefunction(
x: Callable[[str, int], Coroutine[str, int, bytes]],
y: Callable[[str, int], Awaitable[bytes]],
z: Callable[[str, int], Union[str, Awaitable[bytes]]],
z: Callable[[str, int], str | Awaitable[bytes]],
xx: object,
) -> None:

View File

@@ -1,3 +1,5 @@
from __future__ import annotations
import asyncio
from typing import Any, Awaitable, List, Tuple, Union
from typing_extensions import assert_type
@@ -21,7 +23,7 @@ async def test_gather(awaitable1: Awaitable[int], awaitable2: Awaitable[str]) ->
c = await asyncio.gather(awaitable1, awaitable2, awaitable1, awaitable1, awaitable1, awaitable1)
assert_type(c, List[Any])
awaitables_list: List[Awaitable[int]] = [awaitable1]
awaitables_list: list[Awaitable[int]] = [awaitable1]
d = await asyncio.gather(*awaitables_list)
assert_type(d, List[Any])

View File

@@ -1,4 +1,6 @@
from typing import Dict, Generic, Iterable, Tuple, TypeVar
from __future__ import annotations
from typing import Dict, Generic, Iterable, TypeVar
from typing_extensions import assert_type
# These do follow `__init__` overloads order:
@@ -6,7 +8,7 @@ from typing_extensions import assert_type
# mypy raises: 'Need type annotation for "bad"'
# pyright is fine with it.
# bad = dict()
good: Dict[str, str] = dict()
good: dict[str, str] = dict()
assert_type(good, Dict[str, str])
assert_type(dict(arg=1), Dict[str, int])
@@ -16,7 +18,7 @@ _VT = TypeVar("_VT")
class KeysAndGetItem(Generic[_KT, _VT]):
data: Dict[_KT, _VT]
data: dict[_KT, _VT]
def keys(self) -> Iterable[_KT]:
return self.data.keys()
@@ -33,15 +35,15 @@ kt2: KeysAndGetItem[str, int] = KeysAndGetItem()
assert_type(dict(kt2, arg=1), Dict[str, int])
def test_iterable_tuple_overload(x: Iterable[Tuple[int, str]]) -> Dict[int, str]:
def test_iterable_tuple_overload(x: Iterable[tuple[int, str]]) -> dict[int, str]:
return dict(x)
i1: Iterable[Tuple[int, str]] = [(1, "a"), (2, "b")]
i1: Iterable[tuple[int, str]] = [(1, "a"), (2, "b")]
test_iterable_tuple_overload(i1)
dict(i1, arg="a") # type: ignore
i2: Iterable[Tuple[str, int]] = [("a", 1), ("b", 2)]
i2: Iterable[tuple[str, int]] = [("a", 1), ("b", 2)]
assert_type(dict(i2, arg=1), Dict[str, int])
i3: Iterable[str] = ["a.b"]

View File

@@ -1,3 +1,5 @@
from __future__ import annotations
from typing import Iterator
from typing_extensions import assert_type

View File

@@ -1,3 +1,5 @@
from __future__ import annotations
from typing import List, Union
from typing_extensions import assert_type

View File

@@ -1,9 +1,11 @@
from typing import Any, Tuple, Union
from __future__ import annotations
from typing import Any
# The following should pass without error (see #6661):
class Diagnostic:
def __reduce__(self) -> Union[str, Tuple[Any, ...]]:
def __reduce__(self) -> str | tuple[Any, ...]:
res = super().__reduce__()
if isinstance(res, tuple) and len(res) >= 3:
res[2]["_info"] = 42

View File

@@ -1,3 +1,5 @@
from __future__ import annotations
from decimal import Decimal
from fractions import Fraction
from typing import Any

View File

@@ -1,26 +1,28 @@
from __future__ import annotations
from typing import Any, List, Union
from typing_extensions import Literal, assert_type
class Foo:
def __add__(self, other: Any) -> "Foo":
def __add__(self, other: Any) -> Foo:
return Foo()
class Bar:
def __radd__(self, other: Any) -> "Bar":
def __radd__(self, other: Any) -> Bar:
return Bar()
class Baz:
def __add__(self, other: Any) -> "Baz":
def __add__(self, other: Any) -> Baz:
return Baz()
def __radd__(self, other: Any) -> "Baz":
def __radd__(self, other: Any) -> Baz:
return Baz()
literal_list: List[Literal[0, 1]] = [0, 1, 1]
literal_list: list[Literal[0, 1]] = [0, 1, 1]
assert_type(sum([2, 4]), int)
assert_type(sum([3, 5], 4), int)

View File

@@ -1,3 +1,5 @@
from __future__ import annotations
from typing import Tuple
from typing_extensions import assert_type

View File

@@ -1,3 +1,5 @@
from __future__ import annotations
import codecs
from typing_extensions import assert_type

View File

@@ -1,3 +1,5 @@
from __future__ import annotations
from contextlib import ExitStack
from typing_extensions import assert_type

View File

@@ -1,3 +1,5 @@
from __future__ import annotations
import logging
from typing import Any

View File

@@ -1,3 +1,5 @@
from __future__ import annotations
import _threading_local
import threading

View File

@@ -1,3 +1,5 @@
from __future__ import annotations
import unittest
from datetime import datetime, timedelta
from decimal import Decimal
@@ -58,14 +60,14 @@ class Eggs:
class Ham:
def __lt__(self, other: "Ham") -> bool:
def __lt__(self, other: Ham) -> bool:
if not isinstance(other, Ham):
return NotImplemented
return True
class Bacon:
def __gt__(self, other: "Bacon") -> bool:
def __gt__(self, other: Bacon) -> bool:
if not isinstance(other, Bacon):
return NotImplemented
return True

View File

@@ -1,3 +1,5 @@
from __future__ import annotations
# pyright: reportWildcardImportFromLibrary=false
"""

View File

@@ -1,3 +1,5 @@
from __future__ import annotations
from typing import Match, Optional, Pattern
from typing_extensions import assert_type

View File

@@ -85,9 +85,10 @@ def check_test_cases() -> None:
bad_test_case_filename = 'Files in a `test_cases` directory must have names starting with "check_"; got "{}"'
for file in testcase_dir.rglob("*.py"):
assert file.stem.startswith("check_"), bad_test_case_filename.format(file)
with open(file, encoding="UTF-8") as f:
lines = {line.strip() for line in f}
assert "from __future__ import annotations" in lines, "Test-case files should use modern typing syntax where possible"
if package_name != "stdlib":
with open(file, encoding="UTF-8") as f:
lines = {line.strip() for line in f}
pyright_setting_not_enabled_msg = (
f'Third-party test-case file "{file}" must have '
f'"# pyright: reportUnnecessaryTypeIgnoreComment=true" '
@@ -205,7 +206,7 @@ def check_requirements() -> None:
precommit_requirements = get_precommit_requirements()
no_txt_entry_msg = "All pre-commit requirements must also be listed in `requirements-tests.txt` (missing {requirement!r})"
for requirement, specifier in precommit_requirements.items():
assert requirement in requirements_txt_requirements, no_txt_entry_msg.format(requirement)
assert requirement in requirements_txt_requirements, no_txt_entry_msg.format(requirement=requirement)
specifier_mismatch = (
f'Specifier "{specifier}" for {requirement!r} in `.pre-commit-config.yaml` '
f'does not match specifier "{requirements_txt_requirements[requirement]}" in `requirements-tests.txt`'