Commit 64b24032 authored by Jason Madden's avatar Jason Madden Committed by GitHub

Merge pull request #1576 from gevent/issue1572

Make it possible to monkey-patch contextvars of the non-standard back…
parents 22c5530c 8db2beb5
Make it possible to monkey-patch :mod:`contextvars` before Python 3.7
if a non-standard backport that uses the same name as the standard
library does is installed. Previously this would raise an error.
Reported by Simon Davy.
......@@ -390,6 +390,10 @@ def run_setup(ext_modules):
# leak checks. previously we had a hand-rolled version.
'objgraph',
# The backport for contextvars to test patching. It sadly uses the same
# import name as the stdlib module.
'contextvars == 2.4 ; python_version > "3.0" and python_version < "3.7"',
],
},
# It's always safe to pass the CFFI keyword, even if
......
......@@ -45,7 +45,8 @@ from gevent._compat import PY37
from gevent._util import _NONE
from gevent.local import local
__implements__ = __all__ if PY37 else None
__stdlib_expected__ = __all__
__implements__ = __stdlib_expected__ if PY37 else None
# In the reference implementation, the interpreter level OS thread state
# is modified to contain a pointer to the current context. Obviously we can't
......
......@@ -158,6 +158,17 @@ else:
WIN = sys.platform.startswith("win")
PY36 = sys.version_info[:2] >= (3, 6)
class _BadImplements(AttributeError):
"""
Raised when ``__implements__`` is incorrect.
"""
def __init__(self, module):
AttributeError.__init__(
self,
"Module %r has a bad or missing value for __implements__" % (module,)
)
class MonkeyPatchWarning(RuntimeWarning):
"""
The type of warnings we issue.
......@@ -329,7 +340,8 @@ def patch_module(target_module, source_module, items=None,
The *source_module* can provide some attributes to customize the process:
* ``__implements__`` is a list of attribute names to copy; if not present,
the *items* keyword argument is mandatory.
the *items* keyword argument is mandatory. ``__implements__`` must only have
names from the standard library module in it.
* ``_gevent_will_monkey_patch(target_module, items, warn, **kwargs)``
* ``_gevent_did_monkey_patch(target_module, items, warn, **kwargs)``
These two functions in the *source_module* are called *if* they exist,
......@@ -351,7 +363,7 @@ def patch_module(target_module, source_module, items=None,
if items is None:
items = getattr(source_module, '__implements__', None)
if items is None:
raise AttributeError('%r does not have __implements__' % source_module)
raise _BadImplements(source_module)
try:
if _call_hooks:
......@@ -535,15 +547,29 @@ def patch_time():
@_ignores_DoNotPatch
def patch_contextvars():
"""
On Python 3.7 and above, replaces the implementations of :mod:`contextvars`
with :mod:`gevent.contextvars`.
Replaces the implementations of :mod:`contextvars` with
:mod:`gevent.contextvars`.
On Python 3.7 and above, this is a standard library module. On
earlier versions, a backport that uses the same distribution name
and import name is available on PyPI (though this is not
recommended). If that is installed, it will be patched.
.. versionchanged:: NEXT
Clarify that the backport is also patched.
"""
try:
__import__('contextvars')
except ImportError:
pass
else:
try:
_patch_module('contextvars')
except _BadImplements:
# Prior to Python 3.7, but the backport must be installed.
# *Assume* it has the same things as the standard library would.
import gevent.contextvars
_patch_module('contextvars', gevent.contextvars.__stdlib_expected__)
def _patch_existing_locks(threading):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment