Skip to content

Add typing_extensions.utils #45

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

Closed
wants to merge 8 commits into from
Closed
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
3 changes: 2 additions & 1 deletion .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -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,
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
18 changes: 17 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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)

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't comment there, but we should also mention the utils module in the README around line 34.

Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down
5 changes: 5 additions & 0 deletions src/test_typing_extensions/utils.py
Original file line number Diff line number Diff line change
@@ -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
File renamed without changes.
99 changes: 99 additions & 0 deletions src/typing_extensions/utils.py
Original file line number Diff line number Diff line change
@@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add __all__?


_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]):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think we should drop the divmod protocols:

  • They're not widely used: in typeshed we only use them for divmod itself.
  • They're unintuitive in that divmod() normally returns a pair, but the protocols don't reflect that.

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]"]