8000 add typing.ContextManager on all supported Python versions by JelleZijlstra · Pull Request #422 · python/typing · GitHub
[go: up one dir, main page]

Skip to content

add typing.ContextManager on all supported Python versions #422

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

Merged
merged 12 commits into from
May 6, 2017
Merged
Next Next commit
add typing.ContextManager on all supported Python versions
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
  • Loading branch information
JelleZijlstra committed May 3, 2017
commit bf8cb7ea6f26ee4d7286ababd9c4016f108f4046
13 changes: 13 additions & 0 deletions python2/test_typing.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import absolute_import, unicode_literals

import collections
import contextlib
import pickle
import re
import sys
Expand Down Expand Up @@ -1653,6 +1654,18 @@ class B: pass
self.assertIsSubclass(B, typing.Mapping)


class OtherABCTests(BaseTestCase):

def test_contextmanager(self):
@contextlib.contextmanager
def manager():
yield 42

cm = manager()
self.assertIsInstance(cm, typing.ContextManager)
self.assertNotIsInstance(42, typing.ContextManager)


class TypeTests(BaseTestCase):

def test_type_basic(self):
Expand Down
21 changes: 21 additions & 0 deletions python2/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
# for 'Generic' and ABCs below.
'ByteString',
'Container',
'ContextManager',
'Hashable',
'ItemsView',
'Iterable',
Expand Down Expand Up @@ -1857,6 +1858,26 @@ class ValuesView(MappingView[VT_co]):
__extra__ = collections_abc.ValuesView


class ContextManager(Generic[T_co]):
__slots__ = ()

def __enter__(self):
return self

@abc.abstractmethod
def __exit__(self, exc_type, exc_value, traceback):
"""Raise any exception triggered within the runtime context."""
return None

@classmethod
def __subclasshook__(cls, C):
if cls is ContextManager:
if (any("__enter__" in B.__dict__ for B in C.__mro__) and
any("__exit__" in B.__dict__ for B in C.__mro__)):
return True
return NotImplemented


class Dict(dict, MutableMapping[KT, VT]):
__slots__ = ()
__extra__ = dict
Expand Down
2 changes: 0 additions & 2 deletions src/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -2156,8 +2156,6 @@ class B: ...

class OtherABCTests(BaseTestCase):

@skipUnless(hasattr(typing, 'ContextManager'),
'requires typing.ContextManager')
def test_contextmanager(self):
@contextlib.contextmanager
def manager():
Expand Down
22 changes: 20 additions & 2 deletions src/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
# for 'Generic' and ABCs below.
'ByteString',
'Container',
'ContextManager',
'Hashable',
'ItemsView',
'Iterable',
Expand All @@ -57,7 +58,6 @@
# AsyncIterable,
# Coroutine,
# Collection,
# ContextManager,
# AsyncGenerator,

# Structural checks, a.k.a. protocols.
Expand Down Expand Up @@ -1949,7 +1949,25 @@ class ValuesView(MappingView[VT_co], extra=collections_abc.ValuesView):
if hasattr(contextlib, 'AbstractContextManager'):
class ContextManager(Generic[T_co], extra=contextlib.AbstractContextManager):
__slots__ = ()
__all__.append('ContextManager')
else:
class ContextManager(Generic[T_co]):
__slots__ = ()

def __enter__(self):
return self

@abc.abstractmethod
def __exit__(self, exc_type, exc_value, traceback):
"""Raise any exception triggered within the runtime context."""
Copy link
Member

Choose a reason for hiding this comment

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

This docstring confused me. Maybe the code is better off without it?

return None

@classmethod
def __subclasshook__(cls, C):
if cls is ContextManager:
if (any("__enter__" in B.__dict__ for B in C.__mro__) and
any("__exit__" in B.__dict__ for B in C.__mro__)):
return True
return NotImplemented


class Dict(dict, MutableMapping[KT, VT], extra=dict):
Expand Down
0