Merge pull request #34 from ncoghlan/issue-33-add-type-hinting-support

Issue #33: convert to package and include typeshed type hints
This commit is contained in:
Nick Coghlan 2021-06-26 22:36:08 +10:00 committed by GitHub
commit bed62be63b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 242 additions and 13 deletions

View file

@ -1,3 +1,5 @@
[![Jazzband](https://jazzband.co/static/img/jazzband.svg)](https://jazzband.co/)
This is a [Jazzband](https://jazzband.co/) project. By contributing you agree to abide by the [Contributor Code of Conduct](https://jazzband.co/about/conduct) and follow the [guidelines](https://jazzband.co/about/guidelines).
This is a [Jazzband](https://jazzband.co/) project. By contributing you agree
to abide by the [Contributor Code of Conduct](https://jazzband.co/about/conduct)
and follow the [guidelines](https://jazzband.co/about/guidelines).

View file

@ -1,4 +1,6 @@
Note: The type hints included in this package come from the typeshed project,
and are hence distributed under the Apache License 2.0 rather than under the
Python Software License that covers the module implementation and test suite.
A. HISTORY OF THE SOFTWARE
==========================

View file

@ -1,2 +1,5 @@
include *.py *.txt *.rst *.md *.ini MANIFEST.in
recursive-include dev test docs *.rst *.py make.bat Makefile
include *.py *.cfg *.txt *.rst *.md *.ini MANIFEST.in
recursive-include contextlib2 *.py *.pyi py.typed
recursive-include docs *.rst *.py make.bat Makefile
recursive-include test *.py
recursive-include dev *.patch

View file

@ -4,8 +4,12 @@ Release History
21.6.0 (2021-06-27)
^^^^^^^^^^^^^^^^^^^
* Switched to calendar based versioning rather than continuing with pre-1.0
semantic versioning (`#29 <https://github.com/jazzband/contextlib2/issues/29>`__)
* License update: due to the inclusion of type hints from the ``typeshed``
project, the ``contextlib2`` project is now under a combination of the
Python Software License (existing license) and the Apache License 2.0
(``typeshed`` license)
* Switched to calendar based versioning using a "year"-"month"-"serial" scheme,
rather than continuing with pre-1.0 semantic versioning
* Due to the inclusion of asynchronous features from Python 3.7+, the
minimum supported Python version is now Python 3.6
(`#29 <https://github.com/jazzband/contextlib2/issues/29>`__)
@ -20,6 +24,12 @@ Release History
* ``AsyncExitStack`` (added in Python 3.7)
* async support in ``nullcontext`` (Python 3.10)
* ``contextlib2`` now includes an adapted copy of the ``contextlib``
type hints from ``typeshed`` (the adaptation removes the Python version
dependencies from the API definition)
(`#33 <https://github.com/jazzband/contextlib2/issues/33>`__)
* to incorporate the type hints stub file and the ``py.typed`` marker file,
``contextlib2`` is now installed as a package rather than as a module
* Updates to the default compatibility testing matrix:
* Added: CPython 3.9, CPython 3.10

View file

@ -18,9 +18,19 @@ contextlib2 is a backport of the `standard library's contextlib
module <https://docs.python.org/3/library/contextlib.html>`_ to
earlier Python versions.
It also serves as a real world proving ground for possible future
It also sometimes serves as a real world proving ground for possible future
enhancements to the standard library version.
Licensing
---------
As a backport of Python standard library software, the implementation, test
suite and other supporting files for this project are distributed under the
Python Software License used for the CPython reference implementation.
The one exception is the included type hints file, which comes from the
``typeshed`` project, and is hence distributed under the Apache License 2.0.
Development
-----------
@ -50,18 +60,32 @@ Versions currently tested in both tox and GitHub Actions are:
Updating to a new stdlib reference version
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
As of Python 3.10, 3 files needed to be copied from the CPython reference
As of Python 3.10, 4 files needed to be copied from the CPython reference
implementation to contextlib2:
* ``Lib/contextlib.py`` -> ``contextlib2.py``
* ``Doc/contextlib.rst`` -> ``docs/contextlib2.rst``
* ``Lib/contextlib.py`` -> ``contextlib2/__init__.py``
* ``Lib/test/test_contextlib.py`` -> ``test/test_contextlib.py``
* ``Lib/test/test_contextlib_async.py`` -> ``test/test_contextlib_async.py``
The corresponding version of ``contextlib2/__init__.pyi`` also needs to be
retrieved from the ``typeshed`` project::
wget https://raw.githubusercontent.com/python/typeshed/master/stdlib/contextlib.pyi
For the 3.10 sync, the only changes needed to the test files were to import from
``contextlib2`` rather than ``contextlib``. The test directory is laid out so
that the test suite's imports from ``test.support`` work the same way they do in
the main CPython test suite.
The changes made to the ``contextlib2.py`` file to get it to run on the older
versions (and to add back in the deprecated APIs that never graduated to the
standard library version) are saved as a patch file in the ``dev`` directory.
The following patch files are saved in the ``dev`` directory:
* changes made to ``contextlib2/__init__.py`` to get it to run on the older
versions (and to add back in the deprecated APIs that never graduated to
the standard library version)
* changes made to ``contextlib2/__init__.pyi`` to make the Python version
guards unconditional (since the ``contextlib2`` API is the same on all
supported versions)
* changes made to ``docs/contextlib2.rst`` to use ``contextlib2`` version
numbers in the version added/changed notes and to integrate the module
documentation with the rest of the project documentation

125
contextlib2/__init__.pyi Normal file
View file

@ -0,0 +1,125 @@
# Type hints copied from the typeshed project under the Apache License 2.0
# https://github.com/python/typeshed/blob/64c85cdd449ccaff90b546676220c9ecfa6e697f/LICENSE
import sys
from types import TracebackType
from typing import (
IO,
Any,
AsyncContextManager,
AsyncIterator,
Awaitable,
Callable,
ContextManager,
Iterator,
Optional,
Type,
TypeVar,
overload,
)
from typing_extensions import ParamSpec, Protocol
# Note: the various 'if True:' guards replace sys.version checks in the
# original typeshed file that don't apply to the contextlib2 backport API
AbstractContextManager = ContextManager
if True:
AbstractAsyncContextManager = AsyncContextManager
_T = TypeVar("_T")
_T_co = TypeVar("_T_co", covariant=True)
_T_io = TypeVar("_T_io", bound=Optional[IO[str]])
_F = TypeVar("_F", bound=Callable[..., Any])
_P = ParamSpec("_P")
_ExitFunc = Callable[[Optional[Type[BaseException]], Optional[BaseException], Optional[TracebackType]], bool]
_CM_EF = TypeVar("_CM_EF", ContextManager[Any], _ExitFunc)
class _GeneratorContextManager(ContextManager[_T_co]):
def __call__(self, func: _F) -> _F: ...
# type ignore to deal with incomplete ParamSpec support in mypy
def contextmanager(func: Callable[_P, Iterator[_T]]) -> Callable[_P, _GeneratorContextManager[_T]]: ... # type: ignore
if True:
def asynccontextmanager(func: Callable[_P, AsyncIterator[_T]]) -> Callable[_P, AsyncContextManager[_T]]: ... # type: ignore
class _SupportsClose(Protocol):
def close(self) -> object: ...
_SupportsCloseT = TypeVar("_SupportsCloseT", bound=_SupportsClose)
class closing(ContextManager[_SupportsCloseT]):
def __init__(self, thing: _SupportsCloseT) -> None: ...
if True:
class _SupportsAclose(Protocol):
async def aclose(self) -> object: ...
_SupportsAcloseT = TypeVar("_SupportsAcloseT", bound=_SupportsAclose)
class aclosing(AsyncContextManager[_SupportsAcloseT]):
def __init__(self, thing: _SupportsAcloseT) -> None: ...
_AF = TypeVar("_AF", bound=Callable[..., Awaitable[Any]])
class AsyncContextDecorator:
def __call__(self, func: _AF) -> _AF: ...
class suppress(ContextManager[None]):
def __init__(self, *exceptions: Type[BaseException]) -> None: ...
def __exit__(
self, exctype: Optional[Type[BaseException]], excinst: Optional[BaseException], exctb: Optional[TracebackType]
) -> bool: ...
class redirect_stdout(ContextManager[_T_io]):
def __init__(self, new_target: _T_io) -> None: ...
class redirect_stderr(ContextManager[_T_io]):
def __init__(self, new_target: _T_io) -> None: ...
class ContextDecorator:
def __call__(self, func: _F) -> _F: ...
_U = TypeVar("_U", bound=ExitStack)
class ExitStack(ContextManager[ExitStack]):
def __init__(self) -> None: ...
def enter_context(self, cm: ContextManager[_T]) -> _T: ...
def push(self, exit: _CM_EF) -> _CM_EF: ...
def callback(self, callback: Callable[..., Any], *args: Any, **kwds: Any) -> Callable[..., Any]: ...
def pop_all(self: _U) -> _U: ...
def close(self) -> None: ...
def __enter__(self: _U) -> _U: ...
def __exit__(
self,
__exc_type: Optional[Type[BaseException]],
__exc_value: Optional[BaseException],
__traceback: Optional[TracebackType],
) -> bool: ...
if True:
_S = TypeVar("_S", bound=AsyncExitStack)
_ExitCoroFunc = Callable[[Optional[Type[BaseException]], Optional[BaseException], Optional[TracebackType]], Awaitable[bool]]
_CallbackCoroFunc = Callable[..., Awaitable[Any]]
_ACM_EF = TypeVar("_ACM_EF", AsyncContextManager[Any], _ExitCoroFunc)
class AsyncExitStack(AsyncContextManager[AsyncExitStack]):
def __init__(self) -> None: ...
def enter_context(self, cm: ContextManager[_T]) -> _T: ...
def enter_async_context(self, cm: AsyncContextManager[_T]) -> Awaitable[_T]: ...
def push(self, exit: _CM_EF) -> _CM_EF: ...
def push_async_exit(self, exit: _ACM_EF) -> _ACM_EF: ...
def callback(self, callback: Callable[..., Any], *args: Any, **kwds: Any) -> Callable[..., Any]: ...
def push_async_callback(self, callback: _CallbackCoroFunc, *args: Any, **kwds: Any) -> _CallbackCoroFunc: ...
def pop_all(self: _S) -> _S: ...
def aclose(self) -> Awaitable[None]: ...
def __aenter__(self: _S) -> Awaitable[_S]: ...
def __aexit__(
self,
__exc_type: Optional[Type[BaseException]],
__exc_value: Optional[BaseException],
__traceback: Optional[TracebackType],
) -> Awaitable[bool]: ...
if True:
@overload
def nullcontext(enter_result: _T) -> ContextManager[_T]: ...
@overload
def nullcontext() -> ContextManager[None]: ...

0
contextlib2/py.typed Normal file
View file

View file

@ -0,0 +1,58 @@
--- ../contextlib.pyi 2021-06-26 21:36:16.491964153 +1000
+++ contextlib2/__init__.pyi 2021-06-26 21:41:08.109598690 +1000
@@ -1,3 +1,6 @@
+# Type hints copied from the typeshed project under the Apache License 2.0
+# https://github.com/python/typeshed/blob/64c85cdd449ccaff90b546676220c9ecfa6e697f/LICENSE
+
import sys
from types import TracebackType
from typing import (
@@ -16,8 +19,11 @@
)
from typing_extensions import ParamSpec, Protocol
+# Note: the various 'if True:' guards replace sys.version checks in the
+# original typeshed file that don't apply to the contextlib2 backport API
+
AbstractContextManager = ContextManager
-if sys.version_info >= (3, 7):
+if True:
AbstractAsyncContextManager = AsyncContextManager
_T = TypeVar("_T")
@@ -35,7 +41,7 @@
# type ignore to deal with incomplete ParamSpec support in mypy
def contextmanager(func: Callable[_P, Iterator[_T]]) -> Callable[_P, _GeneratorContextManager[_T]]: ... # type: ignore
-if sys.version_info >= (3, 7):
+if True:
def asynccontextmanager(func: Callable[_P, AsyncIterator[_T]]) -> Callable[_P, AsyncContextManager[_T]]: ... # type: ignore
class _SupportsClose(Protocol):
@@ -46,7 +52,7 @@
class closing(ContextManager[_SupportsCloseT]):
def __init__(self, thing: _SupportsCloseT) -> None: ...
-if sys.version_info >= (3, 10):
+if True:
class _SupportsAclose(Protocol):
async def aclose(self) -> object: ...
_SupportsAcloseT = TypeVar("_SupportsAcloseT", bound=_SupportsAclose)
@@ -88,7 +94,7 @@
__traceback: Optional[TracebackType],
) -> bool: ...
-if sys.version_info >= (3, 7):
+if True:
_S = TypeVar("_S", bound=AsyncExitStack)
_ExitCoroFunc = Callable[[Optional[Type[BaseException]], Optional[BaseException], Optional[TracebackType]], Awaitable[bool]]
@@ -112,7 +118,7 @@
__traceback: Optional[TracebackType],
) -> Awaitable[bool]: ...
-if sys.version_info >= (3, 7):
+if True:
@overload
def nullcontext(enter_result: _T) -> ContextManager[_T]: ...
@overload

View file

@ -8,7 +8,8 @@ setup(
name='contextlib2',
version=open('VERSION.txt').read().strip(),
python_requires='>=3.6',
py_modules=['contextlib2'],
packages=['contextlib2'],
include_package_data=True,
license='PSF License',
description='Backports and enhancements for the contextlib module',
long_description=open('README.rst').read(),
@ -17,6 +18,7 @@ setup(
url='http://contextlib2.readthedocs.org',
classifiers=[
'Development Status :: 5 - Production/Stable',
'License :: OSI Approved :: Apache Software License',
'License :: OSI Approved :: Python Software Foundation License',
# These are the Python versions tested, it may work on others
# It definitely won't work on versions without native async support

View file

@ -7,8 +7,11 @@ commands =
coverage run -m unittest discover -t . -s test
coverage report
coverage xml
# mypy won't install on PyPy, so only run the typechecking on CPython
!pypy3: mypy contextlib2
deps =
coverage
!pypy3: mypy
[gh-actions]
python =