diff --git a/.flake8 b/.flake8 index fc71a6e2..28537367 100644 --- a/.flake8 +++ b/.flake8 @@ -10,6 +10,7 @@ ignore = # consistency with mypy W504 exclude = + .venv*, # tests have more relaxed formatting rules # and its own specific config in .flake8-tests - src/test_typing_extensions.py, + src/test_typing_extensions, diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 43986802..2f0d768e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: # Be wary of running `pip install` here, since it becomes easy for us to # accidentally pick up typing_extensions as installed by a dependency cd src - python -m unittest test_typing_extensions.py + python -m unittest test_typing_extensions linting: name: Lint @@ -61,4 +61,4 @@ jobs: run: flake8 - name: Lint tests - run: flake8 --config=.flake8-tests src/test_typing_extensions.py + run: flake8 --config=.flake8-tests src/test_typing_extensions diff --git a/CHANGELOG.md b/CHANGELOG.md index b6721cd2..1eeedaff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,24 @@ -# Unreleased +# UNRELEASED - Add `typing_extensions.NamedTuple`, allowing for generic `NamedTuple`s on Python <3.11 (backport from python/cpython#92027, by Serhiy Storchaka). Patch by Alex Waygood (@AlexWaygood). +- Add the `typing\_extensions.utils` module, which contains useful protocols + and type aliases that are not scheduled for inclusion in `typing`. + - `SupportsAnext` + - `SupportsDivMod` + - `SupportsGetItem` + - `SupportsNext` + - `SupportsRDivMod` + - `SupportsTrunc` + - `SupportsKeys` + - `SupportsItems` + - `SupportsKeysAndGetItem` + - `SupportsRead` + - `SupportsReadline` + - `SupportsWrite` + - `StrPath` + - `BytesPath` # Release 4.2.0 (April 17, 2022) diff --git a/README.md b/README.md index 79112d1c..c96d8ab0 100644 --- a/README.md +++ b/README.md @@ -144,4 +144,4 @@ These types are only guaranteed to work for static type checking. ## Running tests To run tests, navigate into the appropriate source directory and run -`test_typing_extensions.py`. +`test_typing_extensions/__init__.py`. diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions/__init__.py similarity index 99% rename from src/test_typing_extensions.py rename to src/test_typing_extensions/__init__.py index 407a4860..ab3e4165 100644 --- a/src/test_typing_extensions.py +++ b/src/test_typing_extensions/__init__.py @@ -30,6 +30,9 @@ from typing_extensions import clear_overloads, get_overloads, overload from typing_extensions import NamedTuple +# Importing so that the tests are run. +from .utils import dummy # noqa F401 + # Flags used to mark tests that only apply after a specific # version of the typing module. TYPING_3_8_0 = sys.version_info[:3] >= (3, 8, 0) @@ -2886,7 +2889,7 @@ def test_typing_extensions_defers_when_possible(self): def test_typing_extensions_compiles_with_opt(self): file_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), - 'typing_extensions.py') + '../typing_extensions/__init__.py') try: subprocess.check_output(f'{sys.executable} -OO {file_path}', stderr=subprocess.STDOUT, diff --git a/src/test_typing_extensions/utils.py b/src/test_typing_extensions/utils.py new file mode 100644 index 00000000..2dad338e --- /dev/null +++ b/src/test_typing_extensions/utils.py @@ -0,0 +1,5 @@ +# We just import the utils module to check for syntax or import problems. +import typing_extensions.utils # noqa F401 + +def dummy(): + pass diff --git a/src/typing_extensions.py b/src/typing_extensions/__init__.py similarity index 100% rename from src/typing_extensions.py rename to src/typing_extensions/__init__.py diff --git a/src/typing_extensions/utils.py b/src/typing_extensions/utils.py new file mode 100644 index 00000000..7239cd2b --- /dev/null +++ b/src/typing_extensions/utils.py @@ -0,0 +1,99 @@ +""" +Protocols and type aliases that are not scheduled for inclusion in typing. +""" + +from os import PathLike +from typing import AbstractSet, Awaitable, Iterable, Tuple, TypeVar, Union +from typing_extensions import Protocol, TypeAlias + +_T_co = TypeVar("_T_co", covariant=True) +_T_contra = TypeVar("_T_contra", contravariant=True) +_KT = TypeVar("_KT") +_KT_co = TypeVar("_KT_co", covariant=True) +_KT_contra = TypeVar("_KT_contra", contravariant=True) +_VT_co = TypeVar("_VT_co", covariant=True) + +# +# Protocols for dunder methods +# + + +class SupportsAnext(Protocol[_T_co]): + def __anext__(self) -> Awaitable[_T_co]: + ... + + +class SupportsDivMod(Protocol[_T_contra, _T_co]): + def __divmod__(self, __other: _T_contra) -> _T_co: + ... + + +class SupportsGetItem(Protocol[_KT_contra, _VT_co]): + def __getitem__(self, __k: _KT_contra) -> _VT_co: + ... + + +class SupportsNext(Protocol[_T_co]): + def __next__(self) -> _T_co: + ... + + +class SupportsRDivMod(Protocol[_T_contra, _T_co]): + def __rdivmod__(self, __other: _T_contra) -> _T_co: + ... + + +class SupportsTrunc(Protocol): + def __trunc__(self) -> int: + ... + + +# +# Mapping-like protocols +# + + +class SupportsKeys(Protocol[_KT_co]): + def keys(self) -> Iterable[_KT_co]: + ... + + +class SupportsItems(Protocol[_KT_co, _VT_co]): + def items(self) -> AbstractSet[Tuple[_KT_co, _VT_co]]: + ... + + +class SupportsKeysAndGetItem(Protocol[_KT, _VT_co]): + def keys(self) -> Iterable[_KT]: + ... + + def __getitem__(self, __k: _KT) -> _VT_co: + ... + + +# +# I/O protocols +# + + +class SupportsRead(Protocol[_T_co]): + def read(self, __length: int = ...) -> _T_co: + ... + + +class SupportsReadline(Protocol[_T_co]): + def readline(self, __length: int = ...) -> _T_co: + ... + + +class SupportsWrite(Protocol[_T_contra]): + def write(self, __s: _T_contra) -> object: + ... + + +# +# Path aliases +# + +StrPath: TypeAlias = Union[str, "PathLike[str]"] +BytesPath: TypeAlias = Union[bytes, "PathLike[bytes]"]