Skip to content

Commit

Permalink
Patch the 'thread' module provided by 'future' if it's already import…
Browse files Browse the repository at this point in the history
…ed, which is likely because pkg_resources imports it if installed.

Also some more cleanup from removing py3.4 support.
  • Loading branch information
jamadden committed Mar 22, 2019
1 parent f6f5967 commit 7f28e1a
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 132 deletions.
8 changes: 8 additions & 0 deletions CHANGES.rst
Expand Up @@ -30,6 +30,14 @@
<https://bugs.python.org/issue32270>`_ applied to all versions
gevent runs on.

- Python 2: If the backport of the ``_thread_`` module from
``futures`` has already been imported at monkey-patch time, also
patch this module to be consistent. The ``pkg_resources`` package
imports this, and ``pkg_resources`` is often imported early on
Python 2 for namespace packages, so if ``futures`` is installed this
will likely be the case.


1.4.0 (2019-01-04)
==================

Expand Down
7 changes: 7 additions & 0 deletions src/gevent/_compat.py
Expand Up @@ -5,12 +5,19 @@

from __future__ import print_function, absolute_import, division

## Important: This module should generally not have any other gevent
## imports (the exception is _util_py2)

import sys
import os


PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] >= 3
PY35 = sys.version_info[:2] >= (3, 5)
PY36 = sys.version_info[:2] >= (3, 6)
PY37 = sys.version_info[:2] >= (3, 7)
PY38 = sys.version_info[:2] >= (3, 8)
PYPY = hasattr(sys, 'pypy_version_info')
WIN = sys.platform.startswith("win")
LINUX = sys.platform.startswith('linux')
Expand Down
126 changes: 34 additions & 92 deletions src/gevent/_socket3.py
Expand Up @@ -607,25 +607,23 @@ def sendfile(self, file, offset=0, count=None):
"""
return self._sendfile_use_send(file, offset, count)

# get/set_inheritable new in 3.4
if hasattr(os, 'get_inheritable') or hasattr(os, 'get_handle_inheritable'):
# pylint:disable=no-member
if os.name == 'nt':
def get_inheritable(self):
return os.get_handle_inheritable(self.fileno())

def set_inheritable(self, inheritable):
os.set_handle_inheritable(self.fileno(), inheritable)
else:
def get_inheritable(self):
return os.get_inheritable(self.fileno())

def set_inheritable(self, inheritable):
os.set_inheritable(self.fileno(), inheritable)
_added = "\n\n.. versionadded:: 1.1rc4 Added in Python 3.4"
get_inheritable.__doc__ = "Get the inheritable flag of the socket" + _added
set_inheritable.__doc__ = "Set the inheritable flag of the socket" + _added
del _added
if os.name == 'nt':
def get_inheritable(self):
return os.get_handle_inheritable(self.fileno())

def set_inheritable(self, inheritable):
os.set_handle_inheritable(self.fileno(), inheritable)
else:
def get_inheritable(self):
return os.get_inheritable(self.fileno())

def set_inheritable(self, inheritable):
os.set_inheritable(self.fileno(), inheritable)

get_inheritable.__doc__ = "Get the inheritable flag of the socket"
set_inheritable.__doc__ = "Set the inheritable flag of the socket"



SocketType = socket
Expand All @@ -652,84 +650,28 @@ def fromshare(info):

__implements__.append('fromshare')

if hasattr(_socket, "socketpair"):

def socketpair(family=None, type=SOCK_STREAM, proto=0):
"""socketpair([family[, type[, proto]]]) -> (socket object, socket object)
def socketpair(family=None, type=SOCK_STREAM, proto=0):
"""socketpair([family[, type[, proto]]]) -> (socket object, socket object)
Create a pair of socket objects from the sockets returned by the platform
socketpair() function.
The arguments are the same as for socket() except the default family is
AF_UNIX if defined on the platform; otherwise, the default is AF_INET.
Create a pair of socket objects from the sockets returned by the platform
socketpair() function.
The arguments are the same as for socket() except the default family is
AF_UNIX if defined on the platform; otherwise, the default is AF_INET.
.. versionchanged:: 1.2
All Python 3 versions on Windows supply this function (natively
supplied by Python 3.5 and above).
"""
if family is None:
try:
family = AF_UNIX
except NameError:
family = AF_INET
a, b = _socket.socketpair(family, type, proto)
a = socket(family, type, proto, a.detach())
b = socket(family, type, proto, b.detach())
return a, b

else: # pragma: no cover
# Origin: https://gist.github.com/4325783, by Geert Jansen. Public domain.

# gevent: taken from 3.6 release. Expected to be used only on Win. Added to Win/3.5
# gevent: for < 3.5, pass the default value of 128 to lsock.listen()
# (3.5+ uses this as a default and the original code passed no value)

_LOCALHOST = '127.0.0.1'
_LOCALHOST_V6 = '::1'

def socketpair(family=AF_INET, type=SOCK_STREAM, proto=0):
if family == AF_INET:
host = _LOCALHOST
elif family == AF_INET6:
host = _LOCALHOST_V6
else:
raise ValueError("Only AF_INET and AF_INET6 socket address families "
"are supported")
if type != SOCK_STREAM:
raise ValueError("Only SOCK_STREAM socket type is supported")
if proto != 0:
raise ValueError("Only protocol zero is supported")

# We create a connected TCP socket. Note the trick with
# setblocking(False) that prevents us from having to create a thread.
lsock = socket(family, type, proto)
.. versionchanged:: 1.2
All Python 3 versions on Windows supply this function (natively
supplied by Python 3.5 and above).
"""
if family is None:
try:
lsock.bind((host, 0))
lsock.listen(128)
# On IPv6, ignore flow_info and scope_id
addr, port = lsock.getsockname()[:2]
csock = socket(family, type, proto)
try:
csock.setblocking(False)
try:
csock.connect((addr, port))
except (BlockingIOError, InterruptedError):
pass
csock.setblocking(True)
ssock, _ = lsock.accept()
except:
csock.close()
raise
finally:
lsock.close()
return (ssock, csock)

if sys.version_info[:2] < (3, 5):
# Not provided natively
if 'socketpair' in __implements__:
# Multiple imports can cause this to be missing if _socketcommon
# was successfully imported, leading to subsequent imports to cause
# ValueError
__implements__.remove('socketpair')
family = AF_UNIX
except NameError:
family = AF_INET
a, b = _socket.socketpair(family, type, proto)
a = socket(family, type, proto, a.detach())
b = socket(family, type, proto, b.detach())
return a, b


if hasattr(__socket__, 'close'): # Python 3.7b1+
Expand Down
3 changes: 2 additions & 1 deletion src/gevent/backdoor.py
Expand Up @@ -17,6 +17,7 @@
from gevent.hub import getcurrent
from gevent.server import StreamServer
from gevent.pool import Pool
from gevent._compat import PY36

__all__ = ['BackdoorServer']

Expand Down Expand Up @@ -145,7 +146,7 @@ def handle(self, conn, _address): # pylint: disable=method-hidden
getcurrent().switch_in()
try:
console = InteractiveConsole(self._create_interactive_locals())
if sys.version_info[:3] >= (3, 6, 0):
if PY36:
# Beginning in 3.6, the console likes to print "now exiting <class>"
# but probably our socket is already closed, so this just causes problems.
console.interact(banner=self.banner, exitmsg='') # pylint:disable=unexpected-keyword-arg
Expand Down
4 changes: 2 additions & 2 deletions src/gevent/builtins.py
Expand Up @@ -2,10 +2,10 @@
"""gevent friendly implementations of builtin functions."""
from __future__ import absolute_import

import sys
import weakref

from gevent.lock import RLock
from gevent._compat import PY3
from gevent._compat import imp_acquire_lock
from gevent._compat import imp_release_lock

Expand Down Expand Up @@ -121,7 +121,7 @@ def _lock_imports():
global __lock_imports
__lock_imports = True

if sys.version_info[:2] >= (3, 3):
if PY3:
__implements__ = []
__import__ = _import
else:
Expand Down
68 changes: 53 additions & 15 deletions src/gevent/monkey.py
@@ -1,5 +1,5 @@
# Copyright (c) 2009-2012 Denis Bilenko. See LICENSE for details.
# pylint: disable=redefined-outer-name
# pylint: disable=redefined-outer-name,too-many-lines
"""
Make the standard library cooperative.
Expand Down Expand Up @@ -146,12 +146,15 @@ def patch_psycopg(event):
if sys.version_info[0] >= 3:
string_types = (str,)
PY3 = True
PY2 = False
else:
import __builtin__ # pylint:disable=import-error
string_types = (__builtin__.basestring,)
PY3 = False
PY2 = True

WIN = sys.platform.startswith("win")
PY36 = sys.version_info[:2] >= (3, 6)

class MonkeyPatchWarning(RuntimeWarning):
"""
Expand Down Expand Up @@ -283,7 +286,9 @@ def warn(message):

def patch_module(target_module, source_module, items=None,
_warnings=None,
_notify_did_subscribers=True):
_notify_will_subscribers=True,
_notify_did_subscribers=True,
_call_hooks=True):
"""
patch_module(target_module, source_module, items=None)
Expand Down Expand Up @@ -318,18 +323,21 @@ def patch_module(target_module, source_module, items=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)
if _call_hooks:
__call_module_hook(source_module, 'will', target_module, items, _warnings)
if _notify_will_subscribers:
_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 _call_hooks:
__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
Expand All @@ -342,15 +350,42 @@ def patch_module(target_module, source_module, items=None,

return True

def _patch_module(name, items=None, _warnings=None, _notify_did_subscribers=True):
def _patch_module(name,
items=None,
_warnings=None,
_notify_will_subscribers=True,
_notify_did_subscribers=True,
_call_hooks=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)
_notify_will_subscribers=_notify_will_subscribers,
_notify_did_subscribers=_notify_did_subscribers,
_call_hooks=_call_hooks)

# On Python 2, the `futures` package will install
# a bunch of modules with the same name as those from Python 3,
# such as `_thread`; primarily these just do `from thread import *`,
# meaning we have alternate references. If that's already been imported,
# we need to attempt to patch that too.

# Be sure to keep the original states matching also.

alternate_names = getattr(gevent_module, '__alternate_targets__', ())
for alternate_name in alternate_names:
alternate_module = sys.modules.get(alternate_name)
if alternate_module is not None and alternate_module is not target_module:
saved.pop(alternate_name, None)
patch_module(alternate_module, gevent_module, items=items,
_warnings=_warnings,
_notify_will_subscribers=False,
_notify_did_subscribers=False,
_call_hooks=False)
saved[alternate_name] = saved[module_name]

return gevent_module, target_module

Expand Down Expand Up @@ -583,11 +618,14 @@ def patch_thread(threading=True, _threading_local=True, Event=True, logging=True
orig_current_thread = None

gevent_thread_mod, thread_mod = _patch_module('thread',
_warnings=_warnings, _notify_did_subscribers=False)
_warnings=_warnings,
_notify_did_subscribers=False)


if threading:
gevent_threading_mod, _ = _patch_module('threading',
_warnings=_warnings, _notify_did_subscribers=False)
_warnings=_warnings,
_notify_did_subscribers=False)

if Event:
from gevent.event import Event
Expand Down Expand Up @@ -648,7 +686,7 @@ def join(timeout=None):
continue
thread.join = make_join_func(thread, None)

if sys.version_info[0] >= 3:
if PY3:

# Issue 18808 changes the nature of Thread.join() to use
# locks. This means that a greenlet spawned in the main thread
Expand Down Expand Up @@ -757,7 +795,7 @@ def patch_ssl(_warnings=None, _first_time=True):
"""
may_need_warning = (
_first_time
and sys.version_info[:2] >= (3, 6)
and PY36
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
Expand Down Expand Up @@ -892,7 +930,7 @@ def patch_builtins():
.. _greenlet safe: https://github.com/gevent/gevent/issues/108
"""
if sys.version_info[:2] < (3, 3):
if PY2:
_patch_module('builtins')

@_ignores_DoNotPatch
Expand Down

0 comments on commit 7f28e1a

Please sign in to comment.