8000 add typing.ContextManager on all supported Python versions (#422) · python/typing@47e0860 · GitHub
[go: up one dir, main page]

Skip to content

Commit 47e0860

Browse files
JelleZijlstragvanrossum
authored andcommitted
add typing.ContextManager on all supported Python versions (#422)
So that there is a way to type hint for a context manager even on language versions before 3.6. This is just copied from contextlib.AbstractContextManager in 3.6. Fixes #274
1 parent d9e477e commit 47e0860

File tree

5 files changed

+65
-5
lines changed

5 files changed

+65
-5
lines changed

python2/test_typing.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import absolute_import, unicode_literals
22

33
import collections
4+
import contextlib
45
import pickle
56
import re
67
import sys
@@ -1653,6 +1654,18 @@ class B: pass
16531654
self.assertIsSubclass(B, typing.Mapping)
16541655

16551656

1657+
class OtherABCTests(BaseTestCase):
1658+
1659+
def test_contextmanager(self):
1660+
@contextlib.contextmanager
1661+
def manager():
1662+
yield 42
1663+
1664+
cm = manager()
1665+
self.assertIsInstance(cm, typing.ContextManager)
1666+
self.assertNotIsInstance(42, typing.ContextManager)
1667+
1668+
16561669
class TypeTests(BaseTestCase):
16571670

16581671
def test_type_basic(self):

python2/typing.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
# for 'Generic' and ABCs below.
3333
'ByteString',
3434
'Container',
35+
'ContextManager',
3536
'Hashable',
3637
'ItemsView',
3738
'Iterable',
@@ -1857,6 +1858,30 @@ class ValuesView(MappingView[VT_co]):
18571858
__extra__ = collections_abc.ValuesView
18581859

18591860

1861+
class ContextManager(Generic[T_co]):
1862+
__slots__ = ()
1863+
1864+
def __enter__(self):
1865+
return self
1866+
1867+
@abc.abstractmethod
1868+
def __exit__(self, exc_type, exc_value, traceback):
1869+
return None
1870+
1871+
@classmethod
1872+
def __subclasshook__(cls, C):
1873+
if cls is ContextManager:
1874+
# In Python 3.6+, it is possible to set a method to None to
1875+
# explicitly indicate that the class does not implement an ABC
1876+
# (https://bugs.python.org/issue25958), but we do not support
1877+
# that pattern here because this fallback class is only used
1878+
# in Python 3.5 and earlier.
1879+
if (any("__enter__" in B.__dict__ for B in C.__mro__) and
1880+
any("__exit__" in B.__dict__ for B in C.__mro__)):
1881+
return True
1882+
return NotImplemented
1883+
1884+
18601885
class Dict(dict, MutableMapping[KT, VT]):
18611886
__slots__ = ()
18621887
__extra__ = dict

src/test_typing.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2156,8 +2156,6 @@ class B: ...
21562156

21572157
class OtherABCTests(BaseTestCase):
21582158

2159-
@skipUnless(hasattr(typing, 'ContextManager'),
2160-
'requires typing.ContextManager')
21612159
def test_contextmanager(self):
21622160
@contextlib.contextmanager
21632161
def manager():

src/typing.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
# for 'Generic' and ABCs below.
3838
'ByteString',
3939
'Container',
40+
'ContextManager',
4041
'Hashable',
4142
'ItemsView',
4243
'Iterable',
@@ -57,7 +58,6 @@
5758
# AsyncIterable,
5859
# Coroutine,
5960
# Collection,
60-
# ContextManager,
6161
# AsyncGenerator,
6262

6363
# Structural checks, a.k.a. protocols.
@@ -1949,7 +1949,29 @@ class ValuesView(MappingView[VT_co], extra=collections_abc.ValuesView):
19491949
if hasattr(contextlib, 'AbstractContextManager'):
19501950
class ContextManager(Generic[T_co], extra=contextlib.AbstractContextManager):
19511951
__slots__ = ()
1952-
__all__.append('ContextManager')
1952+
else:
1953+
class ContextManager(Generic[T_co]):
1954+
__slots__ = ()
1955+
1956+
def __enter__(self):
1957+
return self
1958+
1959+
@abc.abstractmethod
1960+
def __exit__(self, exc_type, exc_value, traceback):
1961+
return None
1962+
1963+
@classmethod
1964+
def __subclasshook__(cls, C):
1965+
if cls is ContextManager:
1966+
# In Python 3.6+, it is possible to set a method to None to
1967+
# explicitly indicate that the class does not implement an ABC
1968+
# (https://bugs.python.org/issue25958), but we do not support
1969+
# that pattern here because this fallback class is only used
1970+
# in Python 3.5 and earlier.
1971+
if (any("__enter__" in B.__dict__ for B in C.__mro__) and
1972+
any("__exit__" in B.__dict__ for B in C.__mro__)):
1973+
return True
1974+
return NotImplemented
19531975

19541976

19551977
class Dict(dict, MutableMapping[KT, VT], extra=dict):

tox.ini

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ max-line-length = 90
1515
ignore =
1616
# irrelevant plugins
1717
B3,
18-
DW12
18+
DW12,
19+
# code is sometimes better without this
20+
E129
1921
exclude =
2022
# tests have more relaxed formatting rules
2123
# and its own specific config in .flake8-tests

0 commit comments

Comments
 (0)
0