mirror of
https://github.com/jazzband/contextlib2.git
synced 2026-03-16 21:50:24 +00:00
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:
commit
bed62be63b
11 changed files with 242 additions and 13 deletions
|
|
@ -1,3 +1,5 @@
|
|||
[](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).
|
||||
|
|
|
|||
|
|
@ -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
|
||||
==========================
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
14
NEWS.rst
14
NEWS.rst
|
|
@ -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
|
||||
|
|
|
|||
36
README.rst
36
README.rst
|
|
@ -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
125
contextlib2/__init__.pyi
Normal 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
0
contextlib2/py.typed
Normal file
58
dev/py3_10_contextlib_pyi_to_contextlib2_pyi.patch
Normal file
58
dev/py3_10_contextlib_pyi_to_contextlib2_pyi.patch
Normal 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
|
||||
4
setup.py
4
setup.py
|
|
@ -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
|
||||
|
|
|
|||
3
tox.ini
3
tox.ini
|
|
@ -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 =
|
||||
|
|
|
|||
Loading…
Reference in a new issue