aboutsummaryrefslogtreecommitdiffstats
path: root/python/gevent/monkey.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/gevent/monkey.py')
-rw-r--r--python/gevent/monkey.py1079
1 files changed, 0 insertions, 1079 deletions
diff --git a/python/gevent/monkey.py b/python/gevent/monkey.py
deleted file mode 100644
index 6c1fa00..0000000
--- a/python/gevent/monkey.py
+++ /dev/null
@@ -1,1079 +0,0 @@
-# Copyright (c) 2009-2012 Denis Bilenko. See LICENSE for details.
-# pylint: disable=redefined-outer-name
-"""
-Make the standard library cooperative.
-
-The primary purpose of this module is to carefully patch, in place,
-portions of the standard library with gevent-friendly functions that
-behave in the same way as the original (at least as closely as possible).
-
-The primary interface to this is the :func:`patch_all` function, which
-performs all the available patches. It accepts arguments to limit the
-patching to certain modules, but most programs **should** use the
-default values as they receive the most wide-spread testing, and some monkey
-patches have dependencies on others.
-
-Patching **should be done as early as possible** in the lifecycle of the
-program. For example, the main module (the one that tests against
-``__main__`` or is otherwise the first imported) should begin with
-this code, ideally before any other imports::
-
- from gevent import monkey
- monkey.patch_all()
-
-A corollary of the above is that patching **should be done on the main
-thread** and **should be done while the program is single-threaded**.
-
-.. tip::
-
- Some frameworks, such as gunicorn, handle monkey-patching for you.
- Check their documentation to be sure.
-
-.. warning::
-
- Patching too late can lead to unreliable behaviour (for example, some
- modules may still use blocking sockets) or even errors.
-
-Querying
-========
-
-Sometimes it is helpful to know if objects have been monkey-patched, and in
-advanced cases even to have access to the original standard library functions. This
-module provides functions for that purpose.
-
-- :func:`is_module_patched`
-- :func:`is_object_patched`
-- :func:`get_original`
-
-Plugins
-=======
-
-Beginning in gevent 1.3, events are emitted during the monkey patching process.
-These events are delivered first to :mod:`gevent.events` subscribers, and then
-to `setuptools entry points`_.
-
-The following events are defined. They are listed in (roughly) the order
-that a call to :func:`patch_all` will emit them.
-
-- :class:`gevent.events.GeventWillPatchAllEvent`
-- :class:`gevent.events.GeventWillPatchModuleEvent`
-- :class:`gevent.events.GeventDidPatchModuleEvent`
-- :class:`gevent.events.GeventDidPatchBuiltinModulesEvent`
-- :class:`gevent.events.GeventDidPatchAllEvent`
-
-Each event class documents the corresponding setuptools entry point name. The
-entry points will be called with a single argument, the same instance of
-the class that was sent to the subscribers.
-
-You can subscribe to the events to monitor the monkey-patching process and
-to manipulate it, for example by raising :exc:`gevent.events.DoNotPatch`.
-
-You can also subscribe to the events to provide additional patching beyond what
-gevent distributes, either for additional standard library modules, or
-for third-party packages. The suggested time to do this patching is in
-the subscriber for :class:`gevent.events.GeventDidPatchBuiltinModulesEvent`.
-For example, to automatically patch `psycopg2`_ using `psycogreen`_
-when the call to :func:`patch_all` is made, you could write code like this::
-
- # mypackage.py
- def patch_psycopg(event):
- from psycogreen.gevent import patch_psycopg
- patch_psycopg()
-
-In your ``setup.py`` you would register it like this::
-
- from setuptools import setup
- setup(
- ...
- entry_points={
- 'gevent.plugins.monkey.did_patch_builtins': [
- 'psycopg2 = mypackage:patch_psycopg',
- ],
- },
- ...
- )
-
-For more complex patching, gevent provides a helper method
-that you can call to replace attributes of modules with attributes of your
-own modules. This function also takes care of emitting the appropriate events.
-
-- :func:`patch_module`
-
-.. _setuptools entry points: http://setuptools.readthedocs.io/en/latest/setuptools.html#dynamic-discovery-of-services-and-plugins
-.. _psycopg2: https://pypi.python.org/pypi/psycopg2
-.. _psycogreen: https://pypi.python.org/pypi/psycogreen
-
-Use as a module
-===============
-
-Sometimes it is useful to run existing python scripts or modules that
-were not built to be gevent aware under gevent. To do so, this module
-can be run as the main module, passing the script and its arguments.
-For details, see the :func:`main` function.
-
-.. versionchanged:: 1.3b1
- Added support for plugins and began emitting will/did patch events.
-"""
-from __future__ import absolute_import
-from __future__ import print_function
-import sys
-
-__all__ = [
- 'patch_all',
- 'patch_builtins',
- 'patch_dns',
- 'patch_os',
- 'patch_queue',
- 'patch_select',
- 'patch_signal',
- 'patch_socket',
- 'patch_ssl',
- 'patch_subprocess',
- 'patch_sys',
- 'patch_thread',
- 'patch_time',
- # query functions
- 'get_original',
- 'is_module_patched',
- 'is_object_patched',
- # plugin API
- 'patch_module',
- # module functions
- 'main',
-]
-
-
-if sys.version_info[0] >= 3:
- string_types = (str,)
- PY3 = True
-else:
- import __builtin__ # pylint:disable=import-error
- string_types = (__builtin__.basestring,)
- PY3 = False
-
-WIN = sys.platform.startswith("win")
-
-class MonkeyPatchWarning(RuntimeWarning):
- """
- The type of warnings we issue.
-
- .. versionadded:: 1.3a2
- """
-
-def _notify_patch(event, _warnings=None):
- # Raises DoNotPatch if we're not supposed to patch
- from gevent.events import notify_and_call_entry_points
-
- event._warnings = _warnings
- notify_and_call_entry_points(event)
-
-def _ignores_DoNotPatch(func):
-
- from functools import wraps
-
- @wraps(func)
- def ignores(*args, **kwargs):
- from gevent.events import DoNotPatch
- try:
- return func(*args, **kwargs)
- except DoNotPatch:
- return False
-
- return ignores
-
-
-# maps module name -> {attribute name: original item}
-# e.g. "time" -> {"sleep": built-in function sleep}
-saved = {}
-
-
-def is_module_patched(mod_name):
- """
- Check if a module has been replaced with a cooperative version.
-
- :param str mod_name: The name of the standard library module,
- e.g., ``'socket'``.
-
- """
- return mod_name in saved
-
-
-def is_object_patched(mod_name, item_name):
- """
- Check if an object in a module has been replaced with a
- cooperative version.
-
- :param str mod_name: The name of the standard library module,
- e.g., ``'socket'``.
- :param str item_name: The name of the attribute in the module,
- e.g., ``'create_connection'``.
-
- """
- return is_module_patched(mod_name) and item_name in saved[mod_name]
-
-
-def _get_original(name, items):
- d = saved.get(name, {})
- values = []
- module = None
- for item in items:
- if item in d:
- values.append(d[item])
- else:
- if module is None:
- module = __import__(name)
- values.append(getattr(module, item))
- return values
-
-
-def get_original(mod_name, item_name):
- """
- Retrieve the original object from a module.
-
- If the object has not been patched, then that object will still be
- retrieved.
-
- :param str mod_name: The name of the standard library module,
- e.g., ``'socket'``.
- :param item_name: A string or sequence of strings naming the
- attribute(s) on the module ``mod_name`` to return.
-
- :return: The original value if a string was given for
- ``item_name`` or a sequence of original values if a
- sequence was passed.
- """
- if isinstance(item_name, string_types):
- return _get_original(mod_name, [item_name])[0]
- return _get_original(mod_name, item_name)
-
-
-_NONE = object()
-
-
-def patch_item(module, attr, newitem):
- olditem = getattr(module, attr, _NONE)
- if olditem is not _NONE:
- saved.setdefault(module.__name__, {}).setdefault(attr, olditem)
- setattr(module, attr, newitem)
-
-
-def remove_item(module, attr):
- olditem = getattr(module, attr, _NONE)
- if olditem is _NONE:
- return
- saved.setdefault(module.__name__, {}).setdefault(attr, olditem)
- delattr(module, attr)
-
-
-def __call_module_hook(gevent_module, name, module, items, _warnings):
- # This function can raise DoNotPatch on 'will'
-
- def warn(message):
- _queue_warning(message, _warnings)
-
- func_name = '_gevent_' + name + '_monkey_patch'
- try:
- func = getattr(gevent_module, func_name)
- except AttributeError:
- func = lambda *args: None
-
-
- func(module, items, warn)
-
-
-def patch_module(target_module, source_module, items=None,
- _warnings=None,
- _notify_did_subscribers=True):
- """
- patch_module(target_module, source_module, items=None)
-
- Replace attributes in *target_module* with the attributes of the
- same name in *source_module*.
-
- 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.
- * ``_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,
- before and after copying attributes, respectively. The "will" function
- may modify *items*. The value of *warn* is a function that should be called
- with a single string argument to issue a warning to the user. If the "will"
- function raises :exc:`gevent.events.DoNotPatch`, no patching will be done. These functions
- are called before any event subscribers or plugins.
-
- :keyword list items: A list of attribute names to replace. If
- not given, this will be taken from the *source_module* ``__implements__``
- attribute.
- :return: A true value if patching was done, a false value if patching was canceled.
-
- .. versionadded:: 1.3b1
- """
- from gevent import events
-
- if items is None:
- items = getattr(source_module, '__implements__', None)
- if items is None:
- raise AttributeError('%r does not have __implements__' % source_module)
-
- try:
- __call_module_hook(source_module, 'will', target_module, items, _warnings)
- _notify_patch(
- events.GeventWillPatchModuleEvent(target_module.__name__, source_module,
- target_module, items),
- _warnings)
- except events.DoNotPatch:
- return False
-
- for attr in items:
- patch_item(target_module, attr, getattr(source_module, attr))
-
- __call_module_hook(source_module, 'did', target_module, items, _warnings)
-
- if _notify_did_subscribers:
- # We allow turning off the broadcast of the 'did' event for the benefit
- # of our internal functions which need to do additional work (besides copying
- # attributes) before their patch can be considered complete.
- _notify_patch(
- events.GeventDidPatchModuleEvent(target_module.__name__, source_module,
- target_module)
- )
-
- return True
-
-def _patch_module(name, items=None, _warnings=None, _notify_did_subscribers=True):
-
- gevent_module = getattr(__import__('gevent.' + name), name)
- module_name = getattr(gevent_module, '__target__', name)
- target_module = __import__(module_name)
-
- patch_module(target_module, gevent_module, items=items,
- _warnings=_warnings,
- _notify_did_subscribers=_notify_did_subscribers)
-
- return gevent_module, target_module
-
-
-def _queue_warning(message, _warnings):
- # Queues a warning to show after the monkey-patching process is all done.
- # Done this way to avoid extra imports during the process itself, just
- # in case. If we're calling a function one-off (unusual) go ahead and do it
- if _warnings is None:
- _process_warnings([message])
- else:
- _warnings.append(message)
-
-
-def _process_warnings(_warnings):
- import warnings
- for warning in _warnings:
- warnings.warn(warning, MonkeyPatchWarning, stacklevel=3)
-
-
-def _patch_sys_std(name):
- from gevent.fileobject import FileObjectThread
- orig = getattr(sys, name)
- if not isinstance(orig, FileObjectThread):
- patch_item(sys, name, FileObjectThread(orig))
-
-@_ignores_DoNotPatch
-def patch_sys(stdin=True, stdout=True, stderr=True):
- """
- Patch sys.std[in,out,err] to use a cooperative IO via a
- threadpool.
-
- This is relatively dangerous and can have unintended consequences
- such as hanging the process or `misinterpreting control keys`_
- when :func:`input` and :func:`raw_input` are used. :func:`patch_all`
- does *not* call this function by default.
-
- This method does nothing on Python 3. The Python 3 interpreter
- wants to flush the TextIOWrapper objects that make up
- stderr/stdout at shutdown time, but using a threadpool at that
- time leads to a hang.
-
- .. _`misinterpreting control keys`: https://github.com/gevent/gevent/issues/274
- """
- # test__issue6.py demonstrates the hang if these lines are removed;
- # strangely enough that test passes even without monkey-patching sys
- if PY3:
- items = None
- else:
- items = set([('stdin' if stdin else None),
- ('stdout' if stdout else None),
- ('stderr' if stderr else None)])
- items.discard(None)
- items = list(items)
-
- if not items:
- return
-
- from gevent import events
- _notify_patch(events.GeventWillPatchModuleEvent('sys', None, sys,
- items))
-
- for item in items:
- _patch_sys_std(item)
-
- _notify_patch(events.GeventDidPatchModuleEvent('sys', None, sys))
-
-@_ignores_DoNotPatch
-def patch_os():
- """
- Replace :func:`os.fork` with :func:`gevent.fork`, and, on POSIX,
- :func:`os.waitpid` with :func:`gevent.os.waitpid` (if the
- environment variable ``GEVENT_NOWAITPID`` is not defined). Does
- nothing if fork is not available.
-
- .. caution:: This method must be used with :func:`patch_signal` to have proper `SIGCHLD`
- handling and thus correct results from ``waitpid``.
- :func:`patch_all` calls both by default.
-
- .. caution:: For `SIGCHLD` handling to work correctly, the event loop must run.
- The easiest way to help ensure this is to use :func:`patch_all`.
- """
- _patch_module('os')
-
-
-@_ignores_DoNotPatch
-def patch_queue():
- """
- On Python 3.7 and above, replace :class:`queue.SimpleQueue` (implemented
- in C) with its Python counterpart.
-
- .. versionadded:: 1.3.5
- """
-
- import gevent.queue
- if 'SimpleQueue' in gevent.queue.__all__:
- _patch_module('queue', items=['SimpleQueue'])
-
-
-@_ignores_DoNotPatch
-def patch_time():
- """
- Replace :func:`time.sleep` with :func:`gevent.sleep`.
- """
- _patch_module('time')
-
-
-def _patch_existing_locks(threading):
- if len(list(threading.enumerate())) != 1:
- return
- try:
- tid = threading.get_ident()
- except AttributeError:
- tid = threading._get_ident()
- rlock_type = type(threading.RLock())
- try:
- import importlib._bootstrap
- except ImportError:
- class _ModuleLock(object):
- pass
- else:
- _ModuleLock = importlib._bootstrap._ModuleLock # python 2 pylint: disable=no-member
- # It might be possible to walk up all the existing stack frames to find
- # locked objects...at least if they use `with`. To be sure, we look at every object
- # Since we're supposed to be done very early in the process, there shouldn't be
- # too many.
-
- # By definition there's only one thread running, so the various
- # owner attributes were the old (native) thread id. Make it our
- # current greenlet id so that when it wants to unlock and compare
- # self.__owner with _get_ident(), they match.
- gc = __import__('gc')
- for o in gc.get_objects():
- if isinstance(o, rlock_type):
- if hasattr(o, '_owner'): # Py3
- if o._owner is not None:
- o._owner = tid
- else:
- if o._RLock__owner is not None:
- o._RLock__owner = tid
- elif isinstance(o, _ModuleLock):
- if o.owner is not None:
- o.owner = tid
-
-@_ignores_DoNotPatch
-def patch_thread(threading=True, _threading_local=True, Event=True, logging=True,
- existing_locks=True,
- _warnings=None):
- """
- patch_thread(threading=True, _threading_local=True, Event=True, logging=True, existing_locks=True) -> None
-
- Replace the standard :mod:`thread` module to make it greenlet-based.
-
- :keyword bool threading: When True (the default),
- also patch :mod:`threading`.
- :keyword bool _threading_local: When True (the default),
- also patch :class:`_threading_local.local`.
- :keyword bool logging: When True (the default), also patch locks
- taken if the logging module has been configured.
-
- :keyword bool existing_locks: When True (the default), and the
- process is still single threaded, make sure that any
- :class:`threading.RLock` (and, under Python 3, :class:`importlib._bootstrap._ModuleLock`)
- instances that are currently locked can be properly unlocked.
-
- .. caution::
- Monkey-patching :mod:`thread` and using
- :class:`multiprocessing.Queue` or
- :class:`concurrent.futures.ProcessPoolExecutor` (which uses a
- ``Queue``) will hang the process.
-
- .. versionchanged:: 1.1b1
- Add *logging* and *existing_locks* params.
- .. versionchanged:: 1.3a2
- ``Event`` defaults to True.
- """
- # XXX: Simplify
- # pylint:disable=too-many-branches,too-many-locals,too-many-statements
-
- # Description of the hang:
- # There is an incompatibility with patching 'thread' and the 'multiprocessing' module:
- # The problem is that multiprocessing.queues.Queue uses a half-duplex multiprocessing.Pipe,
- # which is implemented with os.pipe() and _multiprocessing.Connection. os.pipe isn't patched
- # by gevent, as it returns just a fileno. _multiprocessing.Connection is an internal implementation
- # class implemented in C, which exposes a 'poll(timeout)' method; under the covers, this issues a
- # (blocking) select() call: hence the need for a real thread. Except for that method, we could
- # almost replace Connection with gevent.fileobject.SocketAdapter, plus a trivial
- # patch to os.pipe (below). Sigh, so close. (With a little work, we could replicate that method)
-
- # import os
- # import fcntl
- # os_pipe = os.pipe
- # def _pipe():
- # r, w = os_pipe()
- # fcntl.fcntl(r, fcntl.F_SETFL, os.O_NONBLOCK)
- # fcntl.fcntl(w, fcntl.F_SETFL, os.O_NONBLOCK)
- # return r, w
- # os.pipe = _pipe
-
- # The 'threading' module copies some attributes from the
- # thread module the first time it is imported. If we patch 'thread'
- # before that happens, then we store the wrong values in 'saved',
- # So if we're going to patch threading, we either need to import it
- # before we patch thread, or manually clean up the attributes that
- # are in trouble. The latter is tricky because of the different names
- # on different versions.
- if threading:
- threading_mod = __import__('threading')
- # Capture the *real* current thread object before
- # we start returning DummyThread objects, for comparison
- # to the main thread.
- orig_current_thread = threading_mod.current_thread()
- else:
- threading_mod = None
- gevent_threading_mod = None
- orig_current_thread = None
-
- gevent_thread_mod, thread_mod = _patch_module('thread',
- _warnings=_warnings, _notify_did_subscribers=False)
-
- if threading:
- gevent_threading_mod, _ = _patch_module('threading',
- _warnings=_warnings, _notify_did_subscribers=False)
-
- if Event:
- from gevent.event import Event
- patch_item(threading_mod, 'Event', Event)
- # Python 2 had `Event` as a function returning
- # the private class `_Event`. Some code may be relying
- # on that.
- if hasattr(threading_mod, '_Event'):
- patch_item(threading_mod, '_Event', Event)
-
- if existing_locks:
- _patch_existing_locks(threading_mod)
-
- if logging and 'logging' in sys.modules:
- logging = __import__('logging')
- patch_item(logging, '_lock', threading_mod.RLock())
- for wr in logging._handlerList:
- # In py26, these are actual handlers, not weakrefs
- handler = wr() if callable(wr) else wr
- if handler is None:
- continue
- if not hasattr(handler, 'lock'):
- raise TypeError("Unknown/unsupported handler %r" % handler)
- handler.lock = threading_mod.RLock()
-
- if _threading_local:
- _threading_local = __import__('_threading_local')
- from gevent.local import local
- patch_item(_threading_local, 'local', local)
-
- def make_join_func(thread, thread_greenlet):
- from gevent.hub import sleep
- from time import time
-
- def join(timeout=None):
- end = None
- if threading_mod.current_thread() is thread:
- raise RuntimeError("Cannot join current thread")
- if thread_greenlet is not None and thread_greenlet.dead:
- return
- if not thread.is_alive():
- return
-
- if timeout:
- end = time() + timeout
-
- while thread.is_alive():
- if end is not None and time() > end:
- return
- sleep(0.01)
- return join
-
- if threading:
- from gevent.threading import main_native_thread
-
- for thread in threading_mod._active.values():
- if thread == main_native_thread():
- continue
- thread.join = make_join_func(thread, None)
-
- if sys.version_info[:2] >= (3, 4):
-
- # Issue 18808 changes the nature of Thread.join() to use
- # locks. This means that a greenlet spawned in the main thread
- # (which is already running) cannot wait for the main thread---it
- # hangs forever. We patch around this if possible. See also
- # gevent.threading.
- greenlet = __import__('greenlet')
-
- if orig_current_thread == threading_mod.main_thread():
- main_thread = threading_mod.main_thread()
- _greenlet = main_thread._greenlet = greenlet.getcurrent()
-
- main_thread.join = make_join_func(main_thread, _greenlet)
-
- # Patch up the ident of the main thread to match. This
- # matters if threading was imported before monkey-patching
- # thread
- oldid = main_thread.ident
- main_thread._ident = threading_mod.get_ident()
- if oldid in threading_mod._active:
- threading_mod._active[main_thread.ident] = threading_mod._active[oldid]
- if oldid != main_thread.ident:
- del threading_mod._active[oldid]
- else:
- _queue_warning("Monkey-patching not on the main thread; "
- "threading.main_thread().join() will hang from a greenlet",
- _warnings)
-
- from gevent import events
- _notify_patch(events.GeventDidPatchModuleEvent('thread', gevent_thread_mod, thread_mod))
- _notify_patch(events.GeventDidPatchModuleEvent('threading', gevent_threading_mod, threading_mod))
-
-@_ignores_DoNotPatch
-def patch_socket(dns=True, aggressive=True):
- """
- Replace the standard socket object with gevent's cooperative
- sockets.
-
- :keyword bool dns: When true (the default), also patch address
- resolution functions in :mod:`socket`. See :doc:`/dns` for details.
- """
- from gevent import socket
- # Note: although it seems like it's not strictly necessary to monkey patch 'create_connection',
- # it's better to do it. If 'create_connection' was not monkey patched, but the rest of socket module
- # was, create_connection would still use "green" getaddrinfo and "green" socket.
- # However, because gevent.socket.socket.connect is a Python function, the exception raised by it causes
- # _socket object to be referenced by the frame, thus causing the next invocation of bind(source_address) to fail.
- if dns:
- items = socket.__implements__ # pylint:disable=no-member
- else:
- items = set(socket.__implements__) - set(socket.__dns__) # pylint:disable=no-member
- _patch_module('socket', items=items)
- if aggressive:
- if 'ssl' not in socket.__implements__: # pylint:disable=no-member
- remove_item(socket, 'ssl')
-
-@_ignores_DoNotPatch
-def patch_dns():
- """
- Replace :doc:`DNS functions </dns>` in :mod:`socket` with
- cooperative versions.
-
- This is only useful if :func:`patch_socket` has been called and is
- done automatically by that method if requested.
- """
- from gevent import socket
- _patch_module('socket', items=socket.__dns__) # pylint:disable=no-member
-
-
-def _find_module_refs(to, excluding_names=()):
- # Looks specifically for module-level references,
- # i.e., 'from foo import Bar'. We define a module reference
- # as a dict (subclass) that also has a __name__ attribute.
- # This does not handle subclasses, but it does find them.
- # Returns two sets. The first is modules (name, file) that were
- # found. The second is subclasses that were found.
- gc = __import__('gc')
- direct_ref_modules = set()
- subclass_modules = set()
-
- def report(mod):
- return mod['__name__'], mod.get('__file__', '<unknown>')
-
- for r in gc.get_referrers(to):
- if isinstance(r, dict) and '__name__' in r:
- if r['__name__'] in excluding_names:
- continue
-
- for v in r.values():
- if v is to:
- direct_ref_modules.add(report(r))
- elif isinstance(r, type) and to in r.__bases__ and 'gevent.' not in r.__module__:
- subclass_modules.add(r)
-
- return direct_ref_modules, subclass_modules
-
-@_ignores_DoNotPatch
-def patch_ssl(_warnings=None, _first_time=True):
- """
- patch_ssl() -> None
-
- Replace :class:`ssl.SSLSocket` object and socket wrapping functions in
- :mod:`ssl` with cooperative versions.
-
- This is only useful if :func:`patch_socket` has been called.
- """
- may_need_warning = (
- _first_time
- and sys.version_info[:2] >= (3, 6)
- and 'ssl' in sys.modules
- and hasattr(sys.modules['ssl'], 'SSLContext'))
- # Previously, we didn't warn on Python 2 if pkg_resources has been imported
- # because that imports ssl and it's commonly used for namespace packages,
- # which typically means we're still in some early part of the import cycle.
- # However, with our new more discriminating check, that no longer seems to be a problem.
- # Prior to 3.6, we don't have the RecursionError problem, and prior to 3.7 we don't have the
- # SSLContext.sslsocket_class/SSLContext.sslobject_class problem.
-
- gevent_mod, _ = _patch_module('ssl', _warnings=_warnings)
- if may_need_warning:
- direct_ref_modules, subclass_modules = _find_module_refs(
- gevent_mod.orig_SSLContext,
- excluding_names=('ssl', 'gevent.ssl', 'gevent._ssl3', 'gevent._sslgte279'))
- if direct_ref_modules or subclass_modules:
- # Normally you don't want to have dynamic warning strings, because
- # the cache in the warning module is based on the string. But we
- # specifically only do this the first time we patch ourself, so it's
- # ok.
- direct_ref_mod_str = subclass_str = ''
- if direct_ref_modules:
- direct_ref_mod_str = 'Modules that had direct imports (NOT patched): %s. ' % ([
- "%s (%s)" % (name, fname)
- for name, fname in direct_ref_modules
- ])
- if subclass_modules:
- subclass_str = 'Subclasses (NOT patched): %s. ' % ([
- str(t) for t in subclass_modules
- ])
- _queue_warning(
- 'Monkey-patching ssl after ssl has already been imported '
- 'may lead to errors, including RecursionError on Python 3.6. '
- 'It may also silently lead to incorrect behaviour on Python 3.7. '
- 'Please monkey-patch earlier. '
- 'See https://github.com/gevent/gevent/issues/1016. '
- + direct_ref_mod_str + subclass_str,
- _warnings)
-
-
-@_ignores_DoNotPatch
-def patch_select(aggressive=True):
- """
- Replace :func:`select.select` with :func:`gevent.select.select`
- and :func:`select.poll` with :class:`gevent.select.poll` (where available).
-
- If ``aggressive`` is true (the default), also remove other
- blocking functions from :mod:`select` and (on Python 3.4 and
- above) :mod:`selectors`:
-
- - :func:`select.epoll`
- - :func:`select.kqueue`
- - :func:`select.kevent`
- - :func:`select.devpoll` (Python 3.5+)
- - :class:`selectors.EpollSelector`
- - :class:`selectors.KqueueSelector`
- - :class:`selectors.DevpollSelector` (Python 3.5+)
- """
-
- source_mod, target_mod = _patch_module('select', _notify_did_subscribers=False)
- if aggressive:
- select = target_mod
- # since these are blocking we're removing them here. This makes some other
- # modules (e.g. asyncore) non-blocking, as they use select that we provide
- # when none of these are available.
- remove_item(select, 'epoll')
- remove_item(select, 'kqueue')
- remove_item(select, 'kevent')
- remove_item(select, 'devpoll')
-
- if sys.version_info[:2] >= (3, 4):
- # Python 3 wants to use `select.select` as a member function,
- # leading to this error in selectors.py (because gevent.select.select is
- # not a builtin and doesn't get the magic auto-static that they do)
- # r, w, _ = self._select(self._readers, self._writers, [], timeout)
- # TypeError: select() takes from 3 to 4 positional arguments but 5 were given
- # Note that this obviously only happens if selectors was imported after we had patched
- # select; but there is a code path that leads to it being imported first (but now we've
- # patched select---so we can't compare them identically)
- select = target_mod # Should be gevent-patched now
- orig_select_select = get_original('select', 'select')
- assert select.select is not orig_select_select
- selectors = __import__('selectors')
- if selectors.SelectSelector._select in (select.select, orig_select_select):
- def _select(self, *args, **kwargs): # pylint:disable=unused-argument
- return select.select(*args, **kwargs)
- selectors.SelectSelector._select = _select
- _select._gevent_monkey = True
-
- # Python 3.7 refactors the poll-like selectors to use a common
- # base class and capture a reference to select.poll, etc, at
- # import time. selectors tends to get imported early
- # (importing 'platform' does it: platform -> subprocess -> selectors),
- # so we need to clean that up.
- if hasattr(selectors, 'PollSelector') and hasattr(selectors.PollSelector, '_selector_cls'):
- selectors.PollSelector._selector_cls = select.poll
-
- if aggressive:
- # If `selectors` had already been imported before we removed
- # select.epoll|kqueue|devpoll, these may have been defined in terms
- # of those functions. They'll fail at runtime.
- remove_item(selectors, 'EpollSelector')
- remove_item(selectors, 'KqueueSelector')
- remove_item(selectors, 'DevpollSelector')
- selectors.DefaultSelector = selectors.SelectSelector
-
- from gevent import events
- _notify_patch(events.GeventDidPatchModuleEvent('select', source_mod, target_mod))
-
-@_ignores_DoNotPatch
-def patch_subprocess():
- """
- Replace :func:`subprocess.call`, :func:`subprocess.check_call`,
- :func:`subprocess.check_output` and :class:`subprocess.Popen` with
- :mod:`cooperative versions <gevent.subprocess>`.
-
- .. note::
- On Windows under Python 3, the API support may not completely match
- the standard library.
-
- """
- _patch_module('subprocess')
-
-@_ignores_DoNotPatch
-def patch_builtins():
- """
- Make the builtin :func:`__import__` function `greenlet safe`_ under Python 2.
-
- .. note::
- This does nothing under Python 3 as it is not necessary. Python 3 features
- improved import locks that are per-module, not global.
-
- .. _greenlet safe: https://github.com/gevent/gevent/issues/108
-
- """
- if sys.version_info[:2] < (3, 3):
- _patch_module('builtins')
-
-@_ignores_DoNotPatch
-def patch_signal():
- """
- Make the :func:`signal.signal` function work with a :func:`monkey-patched os <patch_os>`.
-
- .. caution:: This method must be used with :func:`patch_os` to have proper ``SIGCHLD``
- handling. :func:`patch_all` calls both by default.
-
- .. caution:: For proper ``SIGCHLD`` handling, you must yield to the event loop.
- Using :func:`patch_all` is the easiest way to ensure this.
-
- .. seealso:: :mod:`gevent.signal`
- """
- _patch_module("signal")
-
-
-def _check_repatching(**module_settings):
- _warnings = []
- key = '_gevent_saved_patch_all'
- del module_settings['kwargs']
- if saved.get(key, module_settings) != module_settings:
- _queue_warning("Patching more than once will result in the union of all True"
- " parameters being patched",
- _warnings)
-
- first_time = key not in saved
- saved[key] = module_settings
- return _warnings, first_time, module_settings
-
-
-def _subscribe_signal_os(will_patch_all):
- if will_patch_all.will_patch_module('signal') and not will_patch_all.will_patch_module('os'):
- warnings = will_patch_all._warnings # Internal
- _queue_warning('Patching signal but not os will result in SIGCHLD handlers'
- ' installed after this not being called and os.waitpid may not'
- ' function correctly if gevent.subprocess is used. This may raise an'
- ' error in the future.',
- warnings)
-
-def patch_all(socket=True, dns=True, time=True, select=True, thread=True, os=True, ssl=True,
- httplib=False, # Deprecated, to be removed.
- subprocess=True, sys=False, aggressive=True, Event=True,
- builtins=True, signal=True,
- queue=True,
- **kwargs):
- """
- Do all of the default monkey patching (calls every other applicable
- function in this module).
-
- :return: A true value if patching all modules wasn't cancelled, a false
- value if it was.
-
- .. versionchanged:: 1.1
- Issue a :mod:`warning <warnings>` if this function is called multiple times
- with different arguments. The second and subsequent calls will only add more
- patches, they can never remove existing patches by setting an argument to ``False``.
- .. versionchanged:: 1.1
- Issue a :mod:`warning <warnings>` if this function is called with ``os=False``
- and ``signal=True``. This will cause SIGCHLD handlers to not be called. This may
- be an error in the future.
- .. versionchanged:: 1.3a2
- ``Event`` defaults to True.
- .. versionchanged:: 1.3b1
- Defined the return values.
- .. versionchanged:: 1.3b1
- Add ``**kwargs`` for the benefit of event subscribers. CAUTION: gevent may add
- and interpret additional arguments in the future, so it is suggested to use prefixes
- for kwarg values to be interpreted by plugins, for example, `patch_all(mylib_futures=True)`.
- .. versionchanged:: 1.3.5
- Add *queue*, defaulting to True, for Python 3.7.
- """
- # pylint:disable=too-many-locals,too-many-branches
-
- # Check to see if they're changing the patched list
- _warnings, first_time, modules_to_patch = _check_repatching(**locals())
- if not _warnings and not first_time:
- # Nothing to do, identical args to what we just
- # did
- return
-
- from gevent import events
- try:
- _notify_patch(events.GeventWillPatchAllEvent(modules_to_patch, kwargs), _warnings)
- except events.DoNotPatch:
- return False
-
- # order is important
- if os:
- patch_os()
- if time:
- patch_time()
- if thread:
- patch_thread(Event=Event, _warnings=_warnings)
- # sys must be patched after thread. in other cases threading._shutdown will be
- # initiated to _MainThread with real thread ident
- if sys:
- patch_sys()
- if socket:
- patch_socket(dns=dns, aggressive=aggressive)
- if select:
- patch_select(aggressive=aggressive)
- if ssl:
- patch_ssl(_warnings=_warnings, _first_time=first_time)
- if httplib:
- raise ValueError('gevent.httplib is no longer provided, httplib must be False')
- if subprocess:
- patch_subprocess()
- if builtins:
- patch_builtins()
- if signal:
- patch_signal()
- if queue:
- patch_queue()
-
- _notify_patch(events.GeventDidPatchBuiltinModulesEvent(modules_to_patch, kwargs), _warnings)
- _notify_patch(events.GeventDidPatchAllEvent(modules_to_patch, kwargs), _warnings)
-
- _process_warnings(_warnings)
- return True
-
-
-def main():
- args = {}
- argv = sys.argv[1:]
- verbose = False
- script_help, patch_all_args, modules = _get_script_help()
- while argv and argv[0].startswith('--'):
- option = argv[0][2:]
- if option == 'verbose':
- verbose = True
- elif option.startswith('no-') and option.replace('no-', '') in patch_all_args:
- args[option[3:]] = False
- elif option in patch_all_args:
- args[option] = True
- if option in modules:
- for module in modules:
- args.setdefault(module, False)
- else:
- sys.exit(script_help + '\n\n' + 'Cannot patch %r' % option)
- del argv[0]
- # TODO: break on --
- if verbose:
- import pprint
- import os
- print('gevent.monkey.patch_all(%s)' % ', '.join('%s=%s' % item for item in args.items()))
- print('sys.version=%s' % (sys.version.strip().replace('\n', ' '), ))
- print('sys.path=%s' % pprint.pformat(sys.path))
- print('sys.modules=%s' % pprint.pformat(sorted(sys.modules.keys())))
- print('cwd=%s' % os.getcwd())
-
- patch_all(**args)
- if argv:
- sys.argv = argv
- import runpy
- # Use runpy.run_path to closely (exactly) match what the
- # interpreter does given 'python <path>'. This includes allowing
- # passing .pyc/.pyo files and packages with a __main__ and
- # potentially even zip files. Previously we used exec, which only
- # worked if we directly read a python source file.
- runpy.run_path(sys.argv[0],
- run_name='__main__')
- else:
- print(script_help)
-
-
-def _get_script_help():
- # pylint:disable=deprecated-method
- import inspect
- try:
- getter = inspect.getfullargspec # deprecated in 3.5, un-deprecated in 3.6
- except AttributeError:
- getter = inspect.getargspec
- patch_all_args = getter(patch_all)[0]
- modules = [x for x in patch_all_args if 'patch_' + x in globals()]
- script_help = """gevent.monkey - monkey patch the standard modules to use gevent.
-
-USAGE: ``python -m gevent.monkey [MONKEY OPTIONS] script [SCRIPT OPTIONS]``
-
-If no OPTIONS present, monkey patches all the modules it can patch.
-You can exclude a module with --no-module, e.g. --no-thread. You can
-specify a module to patch with --module, e.g. --socket. In the latter
-case only the modules specified on the command line will be patched.
-
-.. versionchanged:: 1.3b1
- The *script* argument can now be any argument that can be passed to `runpy.run_path`,
- just like the interpreter itself does, for example a package directory containing ``__main__.py``.
- Previously it had to be the path to
- a .py source file.
-
-MONKEY OPTIONS: ``--verbose %s``""" % ', '.join('--[no-]%s' % m for m in modules)
- return script_help, patch_all_args, modules
-
-main.__doc__ = _get_script_help()[0]
-
-if __name__ == '__main__':
- main()