From c3b9f8c4582882cd1f768b0727eca75475bb4f94 Mon Sep 17 00:00:00 2001 From: James Taylor Date: Thu, 12 Jul 2018 23:40:30 -0700 Subject: track embedded python distribution --- python/gevent/builtins.py | 125 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 python/gevent/builtins.py (limited to 'python/gevent/builtins.py') diff --git a/python/gevent/builtins.py b/python/gevent/builtins.py new file mode 100644 index 0000000..eab2099 --- /dev/null +++ b/python/gevent/builtins.py @@ -0,0 +1,125 @@ +# Copyright (c) 2015 gevent contributors. See LICENSE for details. +"""gevent friendly implementations of builtin functions.""" +from __future__ import absolute_import + +import imp # deprecated since 3.4; issues PendingDeprecationWarning in 3.5 +import sys +import weakref +from gevent.lock import RLock + +# Normally we'd have the "expected" case inside the try +# (Python 3, because Python 3 is the way forward). But +# under Python 2, the popular `future` library *also* provides +# a `builtins` module---which lacks the __import__ attribute. +# So we test for the old, deprecated version first + +try: # Py2 + import __builtin__ as builtins + _allowed_module_name_types = (basestring,) # pylint:disable=undefined-variable + __target__ = '__builtin__' +except ImportError: + import builtins # pylint: disable=import-error + _allowed_module_name_types = (str,) + __target__ = 'builtins' + +_import = builtins.__import__ + +# We need to protect imports both across threads and across greenlets. +# And the order matters. Note that under 3.4, the global import lock +# and imp module are deprecated. It seems that in all Py3 versions, a +# module lock is used such that this fix is not necessary. + +# We emulate the per-module locking system under Python 2 in order to +# avoid issues acquiring locks in multiple-level-deep imports +# that attempt to use the gevent blocking API at runtime; using one lock +# could lead to a LoopExit error as a greenlet attempts to block on it while +# it's already held by the main greenlet (issue #798). + +# We base this approach on a simplification of what `importlib._bootstrap` +# does; notably, we don't check for deadlocks + +_g_import_locks = {} # name -> wref of RLock + +__lock_imports = True + + +def __module_lock(name): + # Return the lock for the given module, creating it if necessary. + # It will be removed when no longer needed. + # Nothing in this function yields, so we're multi-greenlet safe + # (But not multi-threading safe.) + # XXX: What about on PyPy, where the GC is asynchronous (not ref-counting)? + # (Does it stop-the-world first?) + lock = None + try: + lock = _g_import_locks[name]() + except KeyError: + pass + + if lock is None: + lock = RLock() + + def cb(_): + # We've seen a KeyError on PyPy on RPi2 + _g_import_locks.pop(name, None) + _g_import_locks[name] = weakref.ref(lock, cb) + return lock + + +def __import__(*args, **kwargs): + """ + __import__(name, globals=None, locals=None, fromlist=(), level=0) -> object + + Normally python protects imports against concurrency by doing some locking + at the C level (at least, it does that in CPython). This function just + wraps the normal __import__ functionality in a recursive lock, ensuring that + we're protected against greenlet import concurrency as well. + """ + if args and not issubclass(type(args[0]), _allowed_module_name_types): + # if a builtin has been acquired as a bound instance method, + # python knows not to pass 'self' when the method is called. + # No such protection exists for monkey-patched builtins, + # however, so this is necessary. + args = args[1:] + + if not __lock_imports: + return _import(*args, **kwargs) + + module_lock = __module_lock(args[0]) # Get a lock for the module name + imp.acquire_lock() + try: + module_lock.acquire() + try: + result = _import(*args, **kwargs) + finally: + module_lock.release() + finally: + imp.release_lock() + return result + + +def _unlock_imports(): + """ + Internal function, called when gevent needs to perform imports + lazily, but does not know the state of the system. It may be impossible + to take the import lock because there are no other running greenlets, for + example. This causes a monkey-patched __import__ to avoid taking any locks. + until the corresponding call to lock_imports. This should only be done for limited + amounts of time and when the set of imports is statically known to be "safe". + """ + global __lock_imports + # This could easily become a list that we push/pop from or an integer + # we increment if we need to do this recursively, but we shouldn't get + # that complex. + __lock_imports = False + + +def _lock_imports(): + global __lock_imports + __lock_imports = True + +if sys.version_info[:2] >= (3, 3): + __implements__ = [] +else: + __implements__ = ['__import__'] +__all__ = __implements__ -- cgit v1.2.3