Commit d9e2479b authored by Jason Madden's avatar Jason Madden

More removal of obsolete code.

parent 07035b7e
...@@ -427,6 +427,8 @@ def run_setup(ext_modules): ...@@ -427,6 +427,8 @@ def run_setup(ext_modules):
# leak checks. previously we had a hand-rolled version. # leak checks. previously we had a hand-rolled version.
'objgraph', 'objgraph',
# We still have some places we like to test with pkg_resources
'setuptools',
], ],
}, },
# It's always safe to pass the CFFI keyword, even if # It's always safe to pass the CFFI keyword, even if
......
...@@ -12,12 +12,6 @@ import sys ...@@ -12,12 +12,6 @@ import sys
import os 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)
PY39 = sys.version_info[:2] >= (3, 9) PY39 = sys.version_info[:2] >= (3, 9)
PY311 = sys.version_info[:2] >= (3, 11) PY311 = sys.version_info[:2] >= (3, 11)
PYPY = hasattr(sys, 'pypy_version_info') PYPY = hasattr(sys, 'pypy_version_info')
...@@ -30,20 +24,12 @@ PURE_PYTHON = PYPY or os.getenv('PURE_PYTHON') ...@@ -30,20 +24,12 @@ PURE_PYTHON = PYPY or os.getenv('PURE_PYTHON')
## Types ## Types
if PY3:
string_types = (str,) string_types = (str,)
integer_types = (int,) integer_types = (int,)
text_type = str text_type = str
native_path_types = (str, bytes) native_path_types = (str, bytes)
thread_mod_name = '_thread' thread_mod_name = '_thread'
else:
import __builtin__ # pylint:disable=import-error
string_types = (__builtin__.basestring,)
text_type = __builtin__.unicode
integer_types = (int, __builtin__.long)
native_path_types = string_types
thread_mod_name = 'thread'
hostname_types = tuple(set(string_types + (bytearray, bytes))) hostname_types = tuple(set(string_types + (bytearray, bytes)))
...@@ -51,27 +37,20 @@ def NativeStrIO(): ...@@ -51,27 +37,20 @@ def NativeStrIO():
import io import io
return io.BytesIO() if str is bytes else io.StringIO() return io.BytesIO() if str is bytes else io.StringIO()
try:
from abc import ABC from abc import ABC # pylint:disable=unused-import
except ImportError:
import abc
ABC = abc.ABCMeta('ABC', (object,), {'__slots__': ()})
del abc
## Exceptions ## Exceptions
if PY3:
def reraise(t, value, tb=None): # pylint:disable=unused-argument def reraise(t, value, tb=None): # pylint:disable=unused-argument
if value.__traceback__ is not tb and tb is not None: if value.__traceback__ is not tb and tb is not None:
raise value.with_traceback(tb) raise value.with_traceback(tb)
raise value raise value
def exc_clear(): def exc_clear():
pass pass
else:
from gevent._util_py2 import reraise # pylint:disable=import-error,no-name-in-module
reraise = reraise # export
exc_clear = sys.exc_clear
## import locks ## import locks
try: try:
...@@ -86,127 +65,28 @@ imp_acquire_lock = imp.acquire_lock ...@@ -86,127 +65,28 @@ imp_acquire_lock = imp.acquire_lock
imp_release_lock = imp.release_lock imp_release_lock = imp.release_lock
## Functions ## Functions
if PY3: iteritems = dict.items
iteritems = dict.items itervalues = dict.values
itervalues = dict.values xrange = range
xrange = range izip = zip
izip = zip
else:
iteritems = dict.iteritems # python 3: pylint:disable=no-member
itervalues = dict.itervalues # python 3: pylint:disable=no-member
xrange = __builtin__.xrange
from itertools import izip # python 3: pylint:disable=no-member,no-name-in-module
izip = izip
## The __fspath__ protocol
try: ## The __fspath__ protocol
from os import PathLike # pylint:disable=unused-import from os import PathLike # pylint:disable=unused-import
except ImportError: from os import fspath
class PathLike(ABC): _fspath = fspath
@classmethod from os import fsencode # pylint:disable=unused-import
def __subclasshook__(cls, subclass): from os import fsdecode # pylint:disable=unused-import
return hasattr(subclass, '__fspath__')
# fspath from 3.6 os.py, but modified to raise the same exceptions as the
# real native implementation.
# Define for testing
def _fspath(path):
"""
Return the path representation of a path-like object.
If str or bytes is passed in, it is returned unchanged. Otherwise the
os.PathLike interface is used to get the path representation. If the
path representation is not str or bytes, TypeError is raised. If the
provided path is not str, bytes, or os.PathLike, TypeError is raised.
"""
if isinstance(path, native_path_types):
return path
# Work from the object's type to match method resolution of other magic
# methods.
path_type = type(path)
try:
path_type_fspath = path_type.__fspath__
except AttributeError:
raise TypeError("expected str, bytes or os.PathLike object, "
"not " + path_type.__name__)
path_repr = path_type_fspath(path)
if isinstance(path_repr, native_path_types):
return path_repr
raise TypeError("expected {}.__fspath__() to return str or bytes, "
"not {}".format(path_type.__name__,
type(path_repr).__name__))
try:
from os import fspath # pylint: disable=unused-import,no-name-in-module
except ImportError:
# if not available, use the Python version as transparently as
# possible
fspath = _fspath
fspath.__name__ = 'fspath'
try:
from os import fsencode # pylint: disable=unused-import,no-name-in-module
except ImportError:
encoding = sys.getfilesystemencoding() or ('utf-8' if not WIN else 'mbcs')
errors = 'strict' if WIN and encoding == 'mbcs' else 'surrogateescape'
# Added in 3.2, so this is for Python 2.7. Note that it doesn't have
# sys.getfilesystemencodeerrors(), which was added in 3.6
def fsencode(filename):
"""Encode filename (an os.PathLike, bytes, or str) to the filesystem
encoding with 'surrogateescape' error handler, return bytes unchanged.
On Windows, use 'strict' error handler if the file system encoding is
'mbcs' (which is the default encoding).
"""
filename = fspath(filename) # Does type-checking of `filename`.
if isinstance(filename, bytes):
return filename
try:
return filename.encode(encoding, errors)
except LookupError:
# Can't encode it, and the error handler doesn't
# exist. Probably on Python 2 with an astral character.
# Not sure how to handle this.
raise UnicodeEncodeError("Can't encode path to filesystem encoding")
try:
from os import fsdecode # pylint:disable=unused-import
except ImportError:
def fsdecode(filename):
"""Decode filename (an os.PathLike, bytes, or str) from the filesystem
encoding with 'surrogateescape' error handler, return str unchanged. On
Windows, use 'strict' error handler if the file system encoding is
'mbcs' (which is the default encoding).
"""
filename = fspath(filename) # Does type-checking of `filename`.
if PY3 and isinstance(filename, bytes):
return filename.decode(encoding, errors)
return filename
## Clocks ## Clocks
try: # Python 3.3+ (PEP 418)
# Python 3.3+ (PEP 418) from time import perf_counter
from time import perf_counter from time import get_clock_info
from time import get_clock_info from time import monotonic
from time import monotonic perf_counter = perf_counter
perf_counter = perf_counter monotonic = monotonic
monotonic = monotonic get_clock_info = get_clock_info
get_clock_info = get_clock_info
except ImportError:
import time
if sys.platform == "win32":
perf_counter = time.clock # pylint:disable=no-member
else:
perf_counter = time.time
monotonic = perf_counter
def get_clock_info(_):
return 'Unknown'
## Monitoring ## Monitoring
def get_this_psutil_process(): def get_this_psutil_process():
......
...@@ -14,7 +14,6 @@ import sys ...@@ -14,7 +14,6 @@ import sys
import os import os
from gevent.hub import _get_hub_noargs as get_hub from gevent.hub import _get_hub_noargs as get_hub
from gevent._compat import PY2
from gevent._compat import integer_types from gevent._compat import integer_types
from gevent._compat import reraise from gevent._compat import reraise
from gevent._compat import fspath from gevent._compat import fspath
...@@ -393,18 +392,12 @@ class OpenDescriptor(object): # pylint:disable=too-many-instance-attributes ...@@ -393,18 +392,12 @@ class OpenDescriptor(object): # pylint:disable=too-many-instance-attributes
if not self.binary: if not self.binary:
# Either native or text at this point. # Either native or text at this point.
if PY2 and self.native: # Python 2 and text mode, or Python 3 and either text or native (both are the same)
# Neither text mode nor binary mode specified. if not isinstance(raw, io.TextIOBase):
if self.universal: # Avoid double-wrapping a TextIOBase in another TextIOWrapper.
# universal was requested, e.g., 'rU' # That tends not to work. See https://github.com/gevent/gevent/issues/1542
result = UniversalNewlineBytesWrapper(result, line_buffering) result = io.TextIOWrapper(result, self.encoding, self.errors, self.newline,
else: line_buffering)
# Python 2 and text mode, or Python 3 and either text or native (both are the same)
if not isinstance(raw, io.TextIOBase):
# Avoid double-wrapping a TextIOBase in another TextIOWrapper.
# That tends not to work. See https://github.com/gevent/gevent/issues/1542
result = io.TextIOWrapper(result, self.encoding, self.errors, self.newline,
line_buffering)
if result is not raw or self._raw_object_is_new(raw): if result is not raw or self._raw_object_is_new(raw):
# Set the mode, if possible, but only if we created a new # Set the mode, if possible, but only if we created a new
......
...@@ -12,7 +12,7 @@ from __future__ import absolute_import, print_function ...@@ -12,7 +12,7 @@ from __future__ import absolute_import, print_function
import importlib import importlib
import sys import sys
from gevent._compat import PY3
from gevent._compat import iteritems from gevent._compat import iteritems
from gevent._compat import imp_acquire_lock from gevent._compat import imp_acquire_lock
from gevent._compat import imp_release_lock from gevent._compat import imp_release_lock
...@@ -25,23 +25,20 @@ MAPPING = { ...@@ -25,23 +25,20 @@ MAPPING = {
'gevent.local': '_threading_local', 'gevent.local': '_threading_local',
'gevent.socket': 'socket', 'gevent.socket': 'socket',
'gevent.select': 'select', 'gevent.select': 'select',
'gevent.selectors': 'selectors' if PY3 else 'selectors2', 'gevent.selectors': 'selectors',
'gevent.ssl': 'ssl', 'gevent.ssl': 'ssl',
'gevent.thread': '_thread' if PY3 else 'thread', 'gevent.thread': '_thread',
'gevent.subprocess': 'subprocess', 'gevent.subprocess': 'subprocess',
'gevent.os': 'os', 'gevent.os': 'os',
'gevent.threading': 'threading', 'gevent.threading': 'threading',
'gevent.builtins': 'builtins' if PY3 else '__builtin__', 'gevent.builtins': 'builtins',
'gevent.signal': 'signal', 'gevent.signal': 'signal',
'gevent.time': 'time', 'gevent.time': 'time',
'gevent.queue': 'queue' if PY3 else 'Queue', 'gevent.queue': 'queue',
'gevent.contextvars': 'contextvars', 'gevent.contextvars': 'contextvars',
} }
OPTIONAL_STDLIB_MODULES = frozenset() if PY3 else frozenset([ OPTIONAL_STDLIB_MODULES = frozenset()
'selectors2',
])
_PATCH_PREFIX = '__g_patched_module_' _PATCH_PREFIX = '__g_patched_module_'
def _collect_stdlib_gevent_modules(): def _collect_stdlib_gevent_modules():
......
...@@ -270,10 +270,16 @@ def getfqdn(name=''): ...@@ -270,10 +270,16 @@ def getfqdn(name=''):
First the hostname returned by gethostbyaddr() is checked, then First the hostname returned by gethostbyaddr() is checked, then
possibly existing aliases. In case no FQDN is available, hostname possibly existing aliases. In case no FQDN is available, hostname
from gethostname() is returned. from gethostname() is returned.
.. versionchanged:: NEXT
The IPv6 generic address '::' now returns the result of
``gethostname``, like the IPv4 address '0.0.0.0'.
""" """
# pylint: disable=undefined-variable # pylint: disable=undefined-variable
name = name.strip() name = name.strip()
if not name or name == '0.0.0.0': # IPv6 added in a late Python 3.10/3.11 patch release.
# https://github.com/python/cpython/issues/100374
if not name or name in ('0.0.0.0', '::'):
name = gethostname() name = gethostname()
try: try:
hostname, aliases, _ = gethostbyaddr(name) hostname, aliases, _ = gethostbyaddr(name)
......
...@@ -13,13 +13,13 @@ import ssl as __ssl__ ...@@ -13,13 +13,13 @@ import ssl as __ssl__
_ssl = __ssl__._ssl _ssl = __ssl__._ssl
import errno import errno
import sys
from gevent.socket import socket, timeout_default from gevent.socket import socket, timeout_default
from gevent.socket import error as socket_error from gevent.socket import error as socket_error
from gevent.socket import timeout as _socket_timeout from gevent.socket import timeout as _socket_timeout
from gevent._util import copy_globals from gevent._util import copy_globals
from gevent._compat import PY36
from weakref import ref as _wref from weakref import ref as _wref
...@@ -49,8 +49,8 @@ from ssl import SSL_ERROR_EOF ...@@ -49,8 +49,8 @@ from ssl import SSL_ERROR_EOF
from ssl import SSL_ERROR_WANT_READ from ssl import SSL_ERROR_WANT_READ
from ssl import SSL_ERROR_WANT_WRITE from ssl import SSL_ERROR_WANT_WRITE
from ssl import PROTOCOL_SSLv23 from ssl import PROTOCOL_SSLv23
from ssl import SSLObject #from ssl import SSLObject
from ssl import match_hostname
from ssl import CHANNEL_BINDING_TYPES from ssl import CHANNEL_BINDING_TYPES
from ssl import CERT_REQUIRED from ssl import CERT_REQUIRED
from ssl import DER_cert_to_PEM_cert from ssl import DER_cert_to_PEM_cert
...@@ -674,32 +674,14 @@ class SSLSocket(socket): ...@@ -674,32 +674,14 @@ class SSLSocket(socket):
raise raise
self._wait(self._write_event, timeout_exc=_SSLErrorHandshakeTimeout) self._wait(self._write_event, timeout_exc=_SSLErrorHandshakeTimeout)
if sys.version_info[:2] < (3, 7) and self._context.check_hostname: # 3.7+, making it difficult to create these objects.
# In Python 3.7, the underlying OpenSSL name matching is used. # There's a new type, _ssl.SSLSocket, that takes the
# The version implemented in Python doesn't understand IDNA encoding. # place of SSLObject for self._sslobj. This one does it all.
if not self.server_hostname: def __create_sslobj(self, server_side=False, session=None):
raise ValueError("check_hostname needs server_hostname " return self.context._wrap_socket(
"argument") self._sock, server_side, self.server_hostname,
match_hostname(self.getpeercert(), self.server_hostname) # pylint:disable=deprecated-method owner=self._sock, session=session
)
if hasattr(SSLObject, '_create'):
# 3.7+, making it difficult to create these objects.
# There's a new type, _ssl.SSLSocket, that takes the
# place of SSLObject for self._sslobj. This one does it all.
def __create_sslobj(self, server_side=False, session=None):
return self.context._wrap_socket(
self._sock, server_side, self.server_hostname,
owner=self._sock, session=session
)
elif PY36: # 3.6
def __create_sslobj(self, server_side=False, session=None):
sslobj = self._context._wrap_socket(self._sock, server_side, self.server_hostname)
return SSLObject(sslobj, owner=self._sock, session=session)
else: # 3.5
def __create_sslobj(self, server_side=False, session=None): # pylint:disable=unused-argument
sslobj = self._context._wrap_socket(self._sock, server_side, self.server_hostname)
return SSLObject(sslobj, owner=self._sock)
def _real_connect(self, addr, connect_ex): def _real_connect(self, addr, connect_ex):
if self.server_side: if self.server_side:
......
...@@ -9,8 +9,6 @@ from collections import deque ...@@ -9,8 +9,6 @@ from collections import deque
from gevent import monkey from gevent import monkey
from gevent._compat import thread_mod_name from gevent._compat import thread_mod_name
from gevent._compat import PY3
__all__ = [ __all__ = [
'Lock', 'Lock',
...@@ -33,21 +31,10 @@ start_new_thread, Lock, get_thread_ident, = monkey.get_original(thread_mod_name, ...@@ -33,21 +31,10 @@ start_new_thread, Lock, get_thread_ident, = monkey.get_original(thread_mod_name,
# #
# #
# In all cases, a timeout value of -1 means "infinite". Sigh. # In all cases, a timeout value of -1 means "infinite". Sigh.
if PY3: def acquire_with_timeout(lock, timeout=-1):
def acquire_with_timeout(lock, timeout=-1): globals()['acquire_with_timeout'] = type(lock).acquire
globals()['acquire_with_timeout'] = type(lock).acquire return lock.acquire(timeout=timeout)
return lock.acquire(timeout=timeout)
else:
def acquire_with_timeout(lock, timeout=-1,
_time=monkey.get_original('time', 'time'),
_sleep=monkey.get_original('time', 'sleep')):
deadline = _time() + timeout if timeout != -1 else None
while 1:
if lock.acquire(False): # Can we acquire non-blocking?
return True
if deadline is not None and _time() >= deadline:
return False
_sleep(0.005)
class _Condition(object): class _Condition(object):
# We could use libuv's ``uv_cond_wait`` to implement this whole # We could use libuv's ``uv_cond_wait`` to implement this whole
......
...@@ -18,8 +18,7 @@ from gevent.greenlet import Greenlet ...@@ -18,8 +18,7 @@ from gevent.greenlet import Greenlet
from gevent.hub import getcurrent from gevent.hub import getcurrent
from gevent.server import StreamServer from gevent.server import StreamServer
from gevent.pool import Pool from gevent.pool import Pool
from gevent._compat import PY36
from gevent._compat import exc_clear
__all__ = [ __all__ = [
'BackdoorServer', 'BackdoorServer',
...@@ -174,15 +173,13 @@ class BackdoorServer(StreamServer): ...@@ -174,15 +173,13 @@ class BackdoorServer(StreamServer):
getcurrent().switch_in() getcurrent().switch_in()
try: try:
console = InteractiveConsole(self._create_interactive_locals()) console = InteractiveConsole(self._create_interactive_locals())
if PY36: # Beginning in 3.6, the console likes to print "now exiting <class>"
# Beginning in 3.6, the console likes to print "now exiting <class>" # but probably our socket is already closed, so this just causes problems.
# but probably our socket is already closed, so this just causes problems. console.interact(banner=self.banner, exitmsg='') # pylint:disable=unexpected-keyword-arg
console.interact(banner=self.banner, exitmsg='') # pylint:disable=unexpected-keyword-arg
else:
console.interact(banner=self.banner)
except SystemExit: except SystemExit:
# raised by quit(); obviously this cannot propagate. # raised by quit(); obviously this cannot propagate.
exc_clear() # Python 2 pass
finally: finally:
raw_file.close() raw_file.close()
conn.close() conn.close()
......
...@@ -5,25 +5,14 @@ from __future__ import absolute_import ...@@ -5,25 +5,14 @@ from __future__ import absolute_import
import weakref import weakref
from gevent.lock import RLock from gevent.lock import RLock
from gevent._compat import PY3
from gevent._compat import imp_acquire_lock from gevent._compat import imp_acquire_lock
from gevent._compat import imp_release_lock from gevent._compat import imp_release_lock
# 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 builtins as __gbuiltins__
import __builtin__ as __gbuiltins__ _allowed_module_name_types = (str,)
_allowed_module_name_types = (basestring,) # pylint:disable=undefined-variable __target__ = 'builtins'
__target__ = '__builtin__'
except ImportError:
import builtins as __gbuiltins__ # pylint: disable=import-error
_allowed_module_name_types = (str,)
__target__ = 'builtins'
_import = __gbuiltins__.__import__ _import = __gbuiltins__.__import__
...@@ -121,11 +110,9 @@ def _lock_imports(): ...@@ -121,11 +110,9 @@ def _lock_imports():
global __lock_imports global __lock_imports
__lock_imports = True __lock_imports = True
if PY3:
__implements__ = [] __implements__ = []
__import__ = _import __import__ = _import
else:
__implements__ = ['__import__']
__all__ = __implements__ __all__ = __implements__
......
...@@ -53,12 +53,12 @@ try: ...@@ -53,12 +53,12 @@ try:
except ImportError: except ImportError:
from collections import Mapping # pylint:disable=deprecated-class from collections import Mapping # pylint:disable=deprecated-class
from gevent._compat import PY37
from gevent._util import _NONE from gevent._util import _NONE
from gevent.local import local from gevent.local import local
__stdlib_expected__ = __all__ __stdlib_expected__ = __all__
__implements__ = __stdlib_expected__ if PY37 else None __implements__ = __stdlib_expected__
# In the reference implementation, the interpreter level OS thread state # In the reference implementation, the interpreter level OS thread state
# is modified to contain a pointer to the current context. Obviously we can't # is modified to contain a pointer to the current context. Obviously we can't
......
...@@ -130,7 +130,7 @@ def notify_and_call_entry_points(event): ...@@ -130,7 +130,7 @@ def notify_and_call_entry_points(event):
entry_points = set( entry_points = set(
ep ep
for ep for ep
in ep_dict.get(event.ENTRY_POINT_NAME) in ep_dict.get(event.ENTRY_POINT_NAME, ())
) )
for plugin in entry_points: for plugin in entry_points:
......
...@@ -597,21 +597,7 @@ if local.__module__ == 'gevent.local': ...@@ -597,21 +597,7 @@ if local.__module__ == 'gevent.local':
# module has a different name than the pure-Python version and we can check for that. # module has a different name than the pure-Python version and we can check for that.
# It's not as direct, but it works. # It's not as direct, but it works.
# So here we're not compiled # So here we're not compiled
from gevent._compat import PYPY local.__new__ = classmethod(__new__)
from gevent._compat import PY2
if PYPY and PY2:
# The behaviour changed with no warning between PyPy2 7.3.2 and 7.3.3.
local.__new__ = __new__
try:
local() # <= 7.3.2
except TypeError:
# >= 7.3.3
local.__new__ = classmethod(__new__)
else:
local.__new__ = classmethod(__new__)
del PYPY
del PY2
else: # pragma: no cover else: # pragma: no cover
# Make sure we revisit in case of changes to the (accelerator) module names. # Make sure we revisit in case of changes to the (accelerator) module names.
if local.__module__ != 'gevent._gevent_clocal': if local.__module__ != 'gevent._gevent_clocal':
......
...@@ -12,7 +12,7 @@ from __future__ import print_function ...@@ -12,7 +12,7 @@ from __future__ import print_function
from gevent.hub import getcurrent from gevent.hub import getcurrent
from gevent._compat import PURE_PYTHON from gevent._compat import PURE_PYTHON
from gevent._compat import PY2
# This is the one exception to the rule of where to # This is the one exception to the rule of where to
# import Semaphore, obviously # import Semaphore, obviously
from gevent import monkey from gevent import monkey
...@@ -180,8 +180,6 @@ def _fixup_docstrings(): ...@@ -180,8 +180,6 @@ def _fixup_docstrings():
assert c.__doc__ == b.__doc__ assert c.__doc__ == b.__doc__
for m in 'acquire', 'release', 'wait': for m in 'acquire', 'release', 'wait':
c_meth = getattr(c, m) c_meth = getattr(c, m)
if PY2:
c_meth = c_meth.__func__
b_meth = getattr(b, m) b_meth = getattr(b, m)
c_meth.__doc__ = b_meth.__doc__ c_meth.__doc__ = b_meth.__doc__
......
...@@ -149,20 +149,7 @@ __all__ = [ ...@@ -149,20 +149,7 @@ __all__ = [
'main', 'main',
] ]
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") WIN = sys.platform.startswith("win")
PY36 = sys.version_info[:2] >= (3, 6)
PY37 = sys.version_info[:2] >= (3, 7)
class _BadImplements(AttributeError): class _BadImplements(AttributeError):
""" """
...@@ -280,8 +267,8 @@ def get_original(mod_name, item_name): ...@@ -280,8 +267,8 @@ def get_original(mod_name, item_name):
``item_name`` or a sequence of original values if a ``item_name`` or a sequence of original values if a
sequence was passed. sequence was passed.
""" """
mod_names = [mod_name] if isinstance(mod_name, string_types) else mod_name mod_names = [mod_name] if isinstance(mod_name, str) else mod_name
if isinstance(item_name, string_types): if isinstance(item_name, str):
item_names = [item_name] item_names = [item_name]
unpack = True unpack = True
else: else:
...@@ -333,7 +320,6 @@ def __call_module_hook(gevent_module, name, module, items, _warnings): ...@@ -333,7 +320,6 @@ def __call_module_hook(gevent_module, name, module, items, _warnings):
class _GeventDoPatchRequest(object): class _GeventDoPatchRequest(object):
PY3 = PY3
get_original = staticmethod(get_original) get_original = staticmethod(get_original)
def __init__(self, def __init__(self,
...@@ -513,7 +499,7 @@ def _patch_sys_std(name): ...@@ -513,7 +499,7 @@ def _patch_sys_std(name):
patch_item(sys, name, FileObjectThread(orig)) patch_item(sys, name, FileObjectThread(orig))
@_ignores_DoNotPatch @_ignores_DoNotPatch
def patch_sys(stdin=True, stdout=True, stderr=True): def patch_sys(stdin=True, stdout=True, stderr=True): # pylint:disable=unused-argument
""" """
Patch sys.std[in,out,err] to use a cooperative IO via a Patch sys.std[in,out,err] to use a cooperative IO via a
threadpool. threadpool.
...@@ -529,29 +515,11 @@ def patch_sys(stdin=True, stdout=True, stderr=True): ...@@ -529,29 +515,11 @@ def patch_sys(stdin=True, stdout=True, stderr=True):
time leads to a hang. time leads to a hang.
.. _`misinterpreting control keys`: https://github.com/gevent/gevent/issues/274 .. _`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 .. deprecated:: NEXT
_notify_patch(events.GeventWillPatchModuleEvent('sys', None, sys, Does nothing on any supported version.
items)) """
return
for item in items:
_patch_sys_std(item)
_notify_patch(events.GeventDidPatchModuleEvent('sys', None, sys))
@_ignores_DoNotPatch @_ignores_DoNotPatch
def patch_os(): def patch_os():
...@@ -612,21 +580,11 @@ def patch_contextvars(): ...@@ -612,21 +580,11 @@ def patch_contextvars():
natively handles switching context vars when greenlets are switched. natively handles switching context vars when greenlets are switched.
Older versions of Python that have the backport installed will Older versions of Python that have the backport installed will
still be patched. still be patched.
.. deprecated:: NEXT
Does nothing on any supported version.
""" """
if PY37: return
return
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): def _patch_existing_locks(threading):
...@@ -853,103 +811,101 @@ def patch_thread(threading=True, _threading_local=True, Event=True, logging=True ...@@ -853,103 +811,101 @@ def patch_thread(threading=True, _threading_local=True, Event=True, logging=True
continue continue
thread.join = make_join_func(thread, None) thread.join = make_join_func(thread, None)
if PY3: # Issue 18808 changes the nature of Thread.join() to use
# locks. This means that a greenlet spawned in the main thread
# Issue 18808 changes the nature of Thread.join() to use # (which is already running) cannot wait for the main thread---it
# locks. This means that a greenlet spawned in the main thread # hangs forever. We patch around this if possible. See also
# (which is already running) cannot wait for the main thread---it # gevent.threading.
# hangs forever. We patch around this if possible. See also greenlet = __import__('greenlet')
# gevent.threading. already_patched = is_object_patched('threading', '_shutdown')
greenlet = __import__('greenlet') orig_shutdown = threading_mod._shutdown
already_patched = is_object_patched('threading', '_shutdown')
orig_shutdown = threading_mod._shutdown if orig_current_thread == threading_mod.main_thread() and not already_patched:
main_thread = threading_mod.main_thread()
if orig_current_thread == threading_mod.main_thread() and not already_patched: _greenlet = main_thread._greenlet = greenlet.getcurrent()
main_thread = threading_mod.main_thread() main_thread.__real_tstate_lock = main_thread._tstate_lock
_greenlet = main_thread._greenlet = greenlet.getcurrent() assert main_thread.__real_tstate_lock is not None
main_thread.__real_tstate_lock = main_thread._tstate_lock # The interpreter will call threading._shutdown
assert main_thread.__real_tstate_lock is not None # when the main thread exits and is about to
# The interpreter will call threading._shutdown # go away. It is called *in* the main thread. This
# when the main thread exits and is about to # is a perfect place to notify other greenlets that
# go away. It is called *in* the main thread. This # the main thread is done. We do this by overriding the
# is a perfect place to notify other greenlets that # lock of the main thread during operation, and only restoring
# the main thread is done. We do this by overriding the # it to the native blocking version at shutdown time
# lock of the main thread during operation, and only restoring # (the interpreter also has a reference to this lock in a
# it to the native blocking version at shutdown time # C data structure).
# (the interpreter also has a reference to this lock in a main_thread._tstate_lock = threading_mod.Lock()
# C data structure). main_thread._tstate_lock.acquire()
main_thread._tstate_lock = threading_mod.Lock()
main_thread._tstate_lock.acquire() def _shutdown():
# Release anyone trying to join() me,
def _shutdown(): # and let us switch to them.
# Release anyone trying to join() me, if not main_thread._tstate_lock:
# and let us switch to them. return
if not main_thread._tstate_lock:
return
main_thread._tstate_lock.release() main_thread._tstate_lock.release()
from gevent import sleep from gevent import sleep
try: try:
sleep() sleep()
except: # pylint:disable=bare-except except: # pylint:disable=bare-except
# A greenlet could have .kill() us # A greenlet could have .kill() us
# or .throw() to us. I'm the main greenlet, # or .throw() to us. I'm the main greenlet,
# there's no where else for this to go. # there's no where else for this to go.
from gevent import get_hub from gevent import get_hub
get_hub().print_exception(_greenlet, *sys.exc_info()) get_hub().print_exception(_greenlet, *sys.exc_info())
# Now, this may have resulted in us getting stopped # Now, this may have resulted in us getting stopped
# if some other greenlet actually just ran there. # if some other greenlet actually just ran there.
# That's not good, we're not supposed to be stopped # That's not good, we're not supposed to be stopped
# when we enter _shutdown. # when we enter _shutdown.
main_thread._is_stopped = False main_thread._is_stopped = False
main_thread._tstate_lock = main_thread.__real_tstate_lock main_thread._tstate_lock = main_thread.__real_tstate_lock
main_thread.__real_tstate_lock = None main_thread.__real_tstate_lock = None
# The only truly blocking native shutdown lock to # The only truly blocking native shutdown lock to
# acquire should be our own (hopefully), and the call to # acquire should be our own (hopefully), and the call to
# _stop that orig_shutdown makes will discard it. # _stop that orig_shutdown makes will discard it.
orig_shutdown() orig_shutdown()
patch_item(threading_mod, '_shutdown', orig_shutdown) patch_item(threading_mod, '_shutdown', orig_shutdown)
patch_item(threading_mod, '_shutdown', _shutdown) patch_item(threading_mod, '_shutdown', _shutdown)
# We create a bit of a reference cycle here, # We create a bit of a reference cycle here,
# so main_thread doesn't get to be collected in a timely way. # so main_thread doesn't get to be collected in a timely way.
# Not good. Take it out of dangling so we don't get # Not good. Take it out of dangling so we don't get
# warned about it. # warned about it.
threading_mod._dangling.remove(main_thread) threading_mod._dangling.remove(main_thread)
# Patch up the ident of the main thread to match. This # Patch up the ident of the main thread to match. This
# matters if threading was imported before monkey-patching # matters if threading was imported before monkey-patching
# thread # thread
oldid = main_thread.ident 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]
elif not already_patched:
_queue_warning("Monkey-patching not on the main thread; "
"threading.main_thread().join() will hang from a greenlet",
_warnings)
main_thread = threading_mod.main_thread()
def _shutdown():
# We've patched get_ident but *did not* patch the
# main_thread.ident value. Beginning in Python 3.9.8
# and then later releases (3.10.1, probably), the
# _main_thread object is only _stop() if the ident of
# the current thread (the *real* main thread) matches
# the ident of the _main_thread object. But without doing that,
# the main thread's shutdown lock (threading._shutdown_locks) is never
# removed *or released*, thus hanging the interpreter.
# XXX: There's probably a better way to do this. Probably need to take a
# step back and look at the whole picture.
main_thread._ident = threading_mod.get_ident() main_thread._ident = threading_mod.get_ident()
if oldid in threading_mod._active: orig_shutdown()
threading_mod._active[main_thread.ident] = threading_mod._active[oldid] patch_item(threading_mod, '_shutdown', orig_shutdown)
if oldid != main_thread.ident: patch_item(threading_mod, '_shutdown', _shutdown)
del threading_mod._active[oldid]
elif not already_patched:
_queue_warning("Monkey-patching not on the main thread; "
"threading.main_thread().join() will hang from a greenlet",
_warnings)
main_thread = threading_mod.main_thread()
def _shutdown():
# We've patched get_ident but *did not* patch the
# main_thread.ident value. Beginning in Python 3.9.8
# and then later releases (3.10.1, probably), the
# _main_thread object is only _stop() if the ident of
# the current thread (the *real* main thread) matches
# the ident of the _main_thread object. But without doing that,
# the main thread's shutdown lock (threading._shutdown_locks) is never
# removed *or released*, thus hanging the interpreter.
# XXX: There's probably a better way to do this. Probably need to take a
# step back and look at the whole picture.
main_thread._ident = threading_mod.get_ident()
orig_shutdown()
patch_item(threading_mod, '_shutdown', orig_shutdown)
patch_item(threading_mod, '_shutdown', _shutdown)
from gevent import events from gevent import events
_notify_patch(events.GeventDidPatchModuleEvent('thread', gevent_thread_mod, thread_mod)) _notify_patch(events.GeventDidPatchModuleEvent('thread', gevent_thread_mod, thread_mod))
...@@ -1031,7 +987,6 @@ def patch_ssl(_warnings=None, _first_time=True): ...@@ -1031,7 +987,6 @@ def patch_ssl(_warnings=None, _first_time=True):
""" """
may_need_warning = ( may_need_warning = (
_first_time _first_time
and PY36
and 'ssl' in sys.modules and 'ssl' in sys.modules
and hasattr(sys.modules['ssl'], 'SSLContext')) and hasattr(sys.modules['ssl'], 'SSLContext'))
# Previously, we didn't warn on Python 2 if pkg_resources has been imported # Previously, we didn't warn on Python 2 if pkg_resources has been imported
...@@ -1145,9 +1100,10 @@ def patch_builtins(): ...@@ -1145,9 +1100,10 @@ def patch_builtins():
.. _greenlet safe: https://github.com/gevent/gevent/issues/108 .. _greenlet safe: https://github.com/gevent/gevent/issues/108
.. deprecated:: NEXT
Does nothing on any supported platform.
""" """
if PY2:
_patch_module('builtins')
@_ignores_DoNotPatch @_ignores_DoNotPatch
def patch_signal(): def patch_signal():
......
...@@ -48,7 +48,6 @@ import sys ...@@ -48,7 +48,6 @@ import sys
from gevent.hub import _get_hub_noargs as get_hub from gevent.hub import _get_hub_noargs as get_hub
from gevent.hub import reinit from gevent.hub import reinit
from gevent._config import config from gevent._config import config
from gevent._compat import PY3
from gevent._util import copy_globals from gevent._util import copy_globals
import errno import errno
...@@ -102,8 +101,6 @@ if fcntl: ...@@ -102,8 +101,6 @@ if fcntl:
except OSError as e: except OSError as e:
if e.errno not in ignored_errors: if e.errno not in ignored_errors:
raise raise
if not PY3:
sys.exc_clear()
if hub is None: if hub is None:
hub = get_hub() hub = get_hub()
event = hub.loop.io(fd, 1) event = hub.loop.io(fd, 1)
...@@ -133,8 +130,6 @@ if fcntl: ...@@ -133,8 +130,6 @@ if fcntl:
except OSError as e: except OSError as e:
if e.errno not in ignored_errors: if e.errno not in ignored_errors:
raise raise
if not PY3:
sys.exc_clear()
if hub is None: if hub is None:
hub = get_hub() hub = get_hub()
event = hub.loop.io(fd, 2) event = hub.loop.io(fd, 2)
......
...@@ -31,13 +31,10 @@ from gevent import socket ...@@ -31,13 +31,10 @@ from gevent import socket
import gevent import gevent
from gevent.server import StreamServer from gevent.server import StreamServer
from gevent.hub import GreenletExit from gevent.hub import GreenletExit
from gevent._compat import PY3, reraise from gevent._compat import reraise
from functools import partial from functools import partial
if PY3: unquote_latin1 = partial(unquote, encoding='latin-1')
unquote_latin1 = partial(unquote, encoding='latin-1')
else:
unquote_latin1 = unquote
_no_undoc_members = True # Don't put undocumented things into sphinx _no_undoc_members = True # Don't put undocumented things into sphinx
...@@ -89,8 +86,7 @@ def format_date_time(timestamp): ...@@ -89,8 +86,7 @@ def format_date_time(timestamp):
# Return a byte string, not a native string # Return a byte string, not a native string
year, month, day, hh, mm, ss, wd, _y, _z = time.gmtime(timestamp) year, month, day, hh, mm, ss, wd, _y, _z = time.gmtime(timestamp)
value = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (_WEEKDAYNAME[wd], day, _MONTHNAME[month], year, hh, mm, ss) value = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (_WEEKDAYNAME[wd], day, _MONTHNAME[month], year, hh, mm, ss)
if PY3: value = value.encode("latin-1")
value = value.encode("latin-1")
return value return value
...@@ -392,13 +388,9 @@ class WSGIHandler(object): ...@@ -392,13 +388,9 @@ class WSGIHandler(object):
# pylint:disable=too-many-instance-attributes # pylint:disable=too-many-instance-attributes
protocol_version = 'HTTP/1.1' protocol_version = 'HTTP/1.1'
if PY3:
# if we do like Py2, then headers_factory unconditionally def MessageClass(self, *args):
# becomes a bound method, meaning the fp argument becomes WSGIHandler return headers_factory(*args)
def MessageClass(self, *args):
return headers_factory(*args)
else:
MessageClass = headers_factory
# Attributes reset at various times for each request; not public # Attributes reset at various times for each request; not public
# documented. Class attributes to keep the constructor fast # documented. Class attributes to keep the constructor fast
...@@ -562,10 +554,10 @@ class WSGIHandler(object): ...@@ -562,10 +554,10 @@ class WSGIHandler(object):
if self.request_version == "HTTP/1.1": if self.request_version == "HTTP/1.1":
conntype = self.headers.get("Connection", "").lower() conntype = self.headers.get("Connection", "").lower()
self.close_connection = (conntype == 'close') self.close_connection = (conntype == 'close') # pylint:disable=superfluous-parens
elif self.request_version == 'HTTP/1.0': elif self.request_version == 'HTTP/1.0':
conntype = self.headers.get("Connection", "close").lower() conntype = self.headers.get("Connection", "close").lower()
self.close_connection = (conntype != 'keep-alive') self.close_connection = (conntype != 'keep-alive') # pylint:disable=superfluous-parens
else: else:
# XXX: HTTP 0.9. We should drop support # XXX: HTTP 0.9. We should drop support
self.close_connection = True self.close_connection = True
...@@ -606,8 +598,7 @@ class WSGIHandler(object): ...@@ -606,8 +598,7 @@ class WSGIHandler(object):
latin-1). latin-1).
""" """
line = self.rfile.readline(MAX_REQUEST_LINE) line = self.rfile.readline(MAX_REQUEST_LINE)
if PY3: line = line.decode('latin-1')
line = line.decode('latin-1')
return line return line
def handle_one_request(self): def handle_one_request(self):
...@@ -659,7 +650,7 @@ class WSGIHandler(object): ...@@ -659,7 +650,7 @@ class WSGIHandler(object):
try: try:
self.requestline = self.read_requestline() self.requestline = self.read_requestline()
# Account for old subclasses that haven't done this # Account for old subclasses that haven't done this
if PY3 and isinstance(self.requestline, bytes): if isinstance(self.requestline, bytes):
self.requestline = self.requestline.decode('latin-1') self.requestline = self.requestline.decode('latin-1')
except socket.error: except socket.error:
# "Connection reset by peer" or other socket errors aren't interesting here # "Connection reset by peer" or other socket errors aren't interesting here
...@@ -720,8 +711,7 @@ class WSGIHandler(object): ...@@ -720,8 +711,7 @@ class WSGIHandler(object):
if hasattr(self.result, '__len__'): if hasattr(self.result, '__len__'):
total_len = sum(len(chunk) for chunk in self.result) total_len = sum(len(chunk) for chunk in self.result)
total_len_str = str(total_len) total_len_str = str(total_len)
if PY3: total_len_str = total_len_str.encode("latin-1")
total_len_str = total_len_str.encode("latin-1")
self.response_headers.append((b'Content-Length', total_len_str)) self.response_headers.append((b'Content-Length', total_len_str))
else: else:
self.response_use_chunked = ( self.response_use_chunked = (
...@@ -856,8 +846,8 @@ class WSGIHandler(object): ...@@ -856,8 +846,8 @@ class WSGIHandler(object):
# Note: Some Python 2 implementations, like Jython, may allow non-octet (above 255) values # Note: Some Python 2 implementations, like Jython, may allow non-octet (above 255) values
# in their str implementation; this is mentioned in the WSGI spec, but we don't # in their str implementation; this is mentioned in the WSGI spec, but we don't
# run on any platform like that so we can assume that a str value is pure bytes. # run on any platform like that so we can assume that a str value is pure bytes.
response_headers.append((header if not PY3 else header.encode("latin-1"), response_headers.append((header.encode("latin-1"),
value if not PY3 else value.encode("latin-1"))) value.encode("latin-1")))
except UnicodeEncodeError: except UnicodeEncodeError:
# If we get here, we're guaranteed to have a header and value # If we get here, we're guaranteed to have a header and value
raise UnicodeError("Non-latin1 header", repr(header), repr(value)) raise UnicodeError("Non-latin1 header", repr(header), repr(value))
...@@ -871,7 +861,7 @@ class WSGIHandler(object): ...@@ -871,7 +861,7 @@ class WSGIHandler(object):
# code # code
code = int(status.split(' ', 1)[0]) code = int(status.split(' ', 1)[0])
self.status = status if not PY3 else status.encode("latin-1") self.status = status.encode("latin-1")
self._orig_status = status # Preserve the native string for logging self._orig_status = status # Preserve the native string for logging
self.response_headers = response_headers self.response_headers = response_headers
self.code = code self.code = code
...@@ -898,8 +888,7 @@ class WSGIHandler(object): ...@@ -898,8 +888,7 @@ class WSGIHandler(object):
if self.code in (304, 204): if self.code in (304, 204):
if self.provided_content_length is not None and self.provided_content_length != '0': if self.provided_content_length is not None and self.provided_content_length != '0':
msg = 'Invalid Content-Length for %s response: %r (must be absent or zero)' % (self.code, self.provided_content_length) msg = 'Invalid Content-Length for %s response: %r (must be absent or zero)' % (self.code, self.provided_content_length)
if PY3: msg = msg.encode('latin-1')
msg = msg.encode('latin-1')
raise self.ApplicationError(msg) raise self.ApplicationError(msg)
return self.write return self.write
...@@ -1012,8 +1001,6 @@ class WSGIHandler(object): ...@@ -1012,8 +1001,6 @@ class WSGIHandler(object):
except socket.error as ex: except socket.error as ex:
if ex.args[0] in self.ignored_socket_errors: if ex.args[0] in self.ignored_socket_errors:
# See description of self.ignored_socket_errors. # See description of self.ignored_socket_errors.
if not PY3:
sys.exc_clear()
self.close_connection = True self.close_connection = True
else: else:
self.handle_error(*sys.exc_info()) self.handle_error(*sys.exc_info())
...@@ -1032,8 +1019,6 @@ class WSGIHandler(object): ...@@ -1032,8 +1019,6 @@ class WSGIHandler(object):
self.start_response(status, headers[:]) self.start_response(status, headers[:])
self.write(body) self.write(body)
except socket.error: except socket.error:
if not PY3:
sys.exc_clear()
self.close_connection = True self.close_connection = True
def _log_error(self, t, v, tb): def _log_error(self, t, v, tb):
...@@ -1558,7 +1543,7 @@ class WSGIServer(StreamServer): ...@@ -1558,7 +1543,7 @@ class WSGIServer(StreamServer):
name = socket.getfqdn(address[0]) name = socket.getfqdn(address[0])
except socket.error: except socket.error:
name = str(address[0]) name = str(address[0])
if PY3 and not isinstance(name, str): if not isinstance(name, str):
name = name.decode('ascii') name = name.decode('ascii')
self.environ['SERVER_NAME'] = name self.environ['SERVER_NAME'] = name
self.environ.setdefault('SERVER_PORT', str(address[1])) self.environ.setdefault('SERVER_PORT', str(address[1]))
......
...@@ -25,7 +25,6 @@ from gevent._compat import string_types ...@@ -25,7 +25,6 @@ from gevent._compat import string_types
from gevent._compat import text_type from gevent._compat import text_type
from gevent._compat import hostname_types from gevent._compat import hostname_types
from gevent._compat import integer_types from gevent._compat import integer_types
from gevent._compat import PY3
from gevent._compat import PYPY from gevent._compat import PYPY
from gevent._compat import MAC from gevent._compat import MAC
...@@ -98,7 +97,7 @@ def _resolve_special(hostname, family): ...@@ -98,7 +97,7 @@ def _resolve_special(hostname, family):
class AbstractResolver(object): class AbstractResolver(object):
HOSTNAME_ENCODING = 'idna' if PY3 else 'ascii' HOSTNAME_ENCODING = 'idna'
_LOCAL_HOSTNAMES = ( _LOCAL_HOSTNAMES = (
b'localhost', b'localhost',
......
...@@ -4,8 +4,6 @@ from __future__ import print_function ...@@ -4,8 +4,6 @@ from __future__ import print_function
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import division from __future__ import division
from contextlib import closing
import sys import sys
from _socket import error as SocketError from _socket import error as SocketError
...@@ -17,7 +15,7 @@ from _socket import SOCK_DGRAM ...@@ -17,7 +15,7 @@ from _socket import SOCK_DGRAM
from gevent.baseserver import BaseServer from gevent.baseserver import BaseServer
from gevent.socket import EWOULDBLOCK from gevent.socket import EWOULDBLOCK
from gevent.socket import socket as GeventSocket from gevent.socket import socket as GeventSocket
from gevent._compat import PYPY, PY3 from gevent._compat import PYPY
__all__ = ['StreamServer', 'DatagramServer'] __all__ = ['StreamServer', 'DatagramServer']
...@@ -29,13 +27,9 @@ else: ...@@ -29,13 +27,9 @@ else:
DEFAULT_REUSE_ADDR = 1 DEFAULT_REUSE_ADDR = 1
if PY3: # sockets and SSL sockets are context managers on Python 3
# sockets and SSL sockets are context managers on Python 3 def _closing_socket(sock):
def _closing_socket(sock): return sock
return sock
else:
# but they are not guaranteed to be so on Python 2
_closing_socket = closing
class StreamServer(BaseServer): class StreamServer(BaseServer):
...@@ -191,38 +185,21 @@ class StreamServer(BaseServer): ...@@ -191,38 +185,21 @@ class StreamServer(BaseServer):
backlog = cls.backlog backlog = cls.backlog
return _tcp_listener(address, backlog=backlog, reuse_addr=cls.reuse_addr, family=family) return _tcp_listener(address, backlog=backlog, reuse_addr=cls.reuse_addr, family=family)
if PY3: def do_read(self):
def do_read(self): sock = self.socket
sock = self.socket try:
try: fd, address = sock._accept()
fd, address = sock._accept() except BlockingIOError: # python 2: pylint: disable=undefined-variable
except BlockingIOError: # python 2: pylint: disable=undefined-variable if not sock.timeout:
if not sock.timeout: return
return raise
raise
sock = GeventSocket(sock.family, sock.type, sock.proto, fileno=fd)
sock = GeventSocket(sock.family, sock.type, sock.proto, fileno=fd) # XXX Python issue #7995? "if no default timeout is set
# XXX Python issue #7995? "if no default timeout is set # and the listening socket had a (non-zero) timeout, force
# and the listening socket had a (non-zero) timeout, force # the new socket in blocking mode to override
# the new socket in blocking mode to override # platform-specific socket flags inheritance."
# platform-specific socket flags inheritance." return sock, address
return sock, address
else:
def do_read(self):
try:
client_socket, address = self.socket.accept()
except SocketError as err:
if err.args[0] == EWOULDBLOCK:
return
raise
sockobj = GeventSocket(_sock=client_socket)
if PYPY:
# Undo the ref-count bump that the constructor
# did. We gave it ownership.
client_socket._drop()
return sockobj, address
def do_close(self, sock, *args): def do_close(self, sock, *args):
# pylint:disable=arguments-differ # pylint:disable=arguments-differ
......
...@@ -13,16 +13,15 @@ as well as the constants from the :mod:`socket` module are imported into this mo ...@@ -13,16 +13,15 @@ as well as the constants from the :mod:`socket` module are imported into this mo
# Our import magic sadly makes this warning useless # Our import magic sadly makes this warning useless
# pylint: disable=undefined-variable # pylint: disable=undefined-variable
from gevent._compat import PY3
from gevent._compat import PY311 from gevent._compat import PY311
from gevent._compat import exc_clear from gevent._compat import exc_clear
from gevent._util import copy_globals from gevent._util import copy_globals
if PY3:
from gevent import _socket3 as _source # python 2: pylint:disable=no-name-in-module from gevent import _socket3 as _source
else:
from gevent import _socket2 as _source
# define some things we're expecting to overwrite; each module # define some things we're expecting to overwrite; each module
# needs to define these # needs to define these
......
""" """
Secure Sockets Layer (SSL/TLS) module. Secure Sockets Layer (SSL/TLS) module.
""" """
from gevent._compat import PY2
from gevent._util import copy_globals from gevent._util import copy_globals
# things we expect to override, here for static analysis # things we expect to override, here for static analysis
...@@ -9,27 +9,8 @@ def wrap_socket(_sock, **_kwargs): ...@@ -9,27 +9,8 @@ def wrap_socket(_sock, **_kwargs):
# pylint:disable=unused-argument # pylint:disable=unused-argument
raise NotImplementedError() raise NotImplementedError()
if PY2:
if hasattr(__import__('ssl'), 'SSLContext'): from gevent import _ssl3 as _source
# It's not sufficient to check for >= 2.7.9; some distributions
# have backported most of PEP 466. Try to accommodate them. See Issue #702.
# We're just about to import ssl anyway so it's fine to import it here, just
# don't pollute the namespace
from gevent import _sslgte279 as _source
else: # pragma: no cover
from gevent import _ssl2 as _source
import warnings
warnings.warn(
"This version of Python has an insecure SSL implementation. "
"gevent is no longer tested with it, and support will be removed "
"in gevent 1.5. Please use Python 2.7.9 or newer.",
DeprecationWarning,
stacklevel=2,
)
del warnings
else:
# Py3
from gevent import _ssl3 as _source # pragma: no cover
copy_globals(_source, globals()) copy_globals(_source, globals())
...@@ -59,11 +59,11 @@ from gevent.hub import linkproxy ...@@ -59,11 +59,11 @@ from gevent.hub import linkproxy
from gevent.hub import sleep from gevent.hub import sleep
from gevent.hub import getcurrent from gevent.hub import getcurrent
from gevent._compat import integer_types, string_types, xrange from gevent._compat import integer_types, string_types, xrange
from gevent._compat import PY3 # from gevent._compat import PY3
from gevent._compat import PY35 # from gevent._compat import PY35
from gevent._compat import PY36 # from gevent._compat import PY36
from gevent._compat import PY37 # from gevent._compat import PY37
from gevent._compat import PY38 # from gevent._compat import PY38
from gevent._compat import PY311 from gevent._compat import PY311
from gevent._compat import PYPY from gevent._compat import PYPY
from gevent._compat import reraise from gevent._compat import reraise
...@@ -85,7 +85,7 @@ __implements__ = [ ...@@ -85,7 +85,7 @@ __implements__ = [
'check_call', 'check_call',
'check_output', 'check_output',
] ]
if PY3 and not sys.platform.startswith('win32'): if not sys.platform.startswith('win32'):
__implements__.append("_posixsubprocess") __implements__.append("_posixsubprocess")
_posixsubprocess = None _posixsubprocess = None
...@@ -142,74 +142,69 @@ __extra__ = [ ...@@ -142,74 +142,69 @@ __extra__ = [
'CompletedProcess', 'CompletedProcess',
] ]
if PY3: __imports__ += [
__imports__ += [ 'DEVNULL',
'DEVNULL', 'getstatusoutput',
'getstatusoutput', 'getoutput',
'getoutput', 'SubprocessError',
'SubprocessError', 'TimeoutExpired',
'TimeoutExpired', ]
]
else:
__extra__.append("TimeoutExpired")
if PY35:
__extra__.remove('run')
__extra__.remove('CompletedProcess')
__implements__.append('run')
__implements__.append('CompletedProcess')
# Removed in Python 3.5; this is the exact code that was removed:
# https://hg.python.org/cpython/rev/f98b0a5e5ef5
__extra__.remove('MAXFD')
try:
MAXFD = os.sysconf("SC_OPEN_MAX")
except:
MAXFD = 256
if PY36:
# This was added to __all__ for windows in 3.6
__extra__.remove('STARTUPINFO')
__imports__.append('STARTUPINFO')
if PY37: # Became standard in 3.5
__imports__.extend([ __extra__.remove('run')
'ABOVE_NORMAL_PRIORITY_CLASS', 'BELOW_NORMAL_PRIORITY_CLASS', __extra__.remove('CompletedProcess')
'HIGH_PRIORITY_CLASS', 'IDLE_PRIORITY_CLASS', __implements__.append('run')
'NORMAL_PRIORITY_CLASS', __implements__.append('CompletedProcess')
'REALTIME_PRIORITY_CLASS',
'CREATE_NO_WINDOW', 'DETACHED_PROCESS',
'CREATE_DEFAULT_ERROR_MODE',
'CREATE_BREAKAWAY_FROM_JOB'
])
if PY38: # Removed in Python 3.5; this is the exact code that was removed:
# Using os.posix_spawn() to start subprocesses # https://hg.python.org/cpython/rev/f98b0a5e5ef5
# bypasses our child watchers on certain operating systems, __extra__.remove('MAXFD')
# and with certain library versions. Possibly the right try:
# fix is to monkey-patch os.posix_spawn like we do os.fork? MAXFD = os.sysconf("SC_OPEN_MAX")
# These have no effect, they're just here to match the stdlib. except:
# TODO: When available, given a monkey patch on them, I think MAXFD = 256
# we ought to be able to use them if the stdlib has identified them
# as suitable.
# This was added to __all__ for windows in 3.6
__extra__.remove('STARTUPINFO')
__imports__.append('STARTUPINFO')
__imports__.extend([
'ABOVE_NORMAL_PRIORITY_CLASS', 'BELOW_NORMAL_PRIORITY_CLASS',
'HIGH_PRIORITY_CLASS', 'IDLE_PRIORITY_CLASS',
'NORMAL_PRIORITY_CLASS',
'REALTIME_PRIORITY_CLASS',
'CREATE_NO_WINDOW', 'DETACHED_PROCESS',
'CREATE_DEFAULT_ERROR_MODE',
'CREATE_BREAKAWAY_FROM_JOB'
])
# Using os.posix_spawn() to start subprocesses
# bypasses our child watchers on certain operating systems,
# and with certain library versions. Possibly the right
# fix is to monkey-patch os.posix_spawn like we do os.fork?
# These have no effect, they're just here to match the stdlib.
# TODO: When available, given a monkey patch on them, I think
# we ought to be able to use them if the stdlib has identified them
# as suitable.
__implements__.extend([
'_use_posix_spawn',
])
def _use_posix_spawn():
return False
_USE_POSIX_SPAWN = False
if __subprocess__._USE_POSIX_SPAWN:
__implements__.extend([ __implements__.extend([
'_use_posix_spawn', '_USE_POSIX_SPAWN',
])
else:
__imports__.extend([
'_USE_POSIX_SPAWN',
]) ])
def _use_posix_spawn():
return False
_USE_POSIX_SPAWN = False
if __subprocess__._USE_POSIX_SPAWN:
__implements__.extend([
'_USE_POSIX_SPAWN',
])
else:
__imports__.extend([
'_USE_POSIX_SPAWN',
])
if PY311: if PY311:
# Python 3.11 added some module-level attributes to control the # Python 3.11 added some module-level attributes to control the
...@@ -270,26 +265,25 @@ for _x in ('run', 'CompletedProcess', 'TimeoutExpired'): ...@@ -270,26 +265,25 @@ for _x in ('run', 'CompletedProcess', 'TimeoutExpired'):
mswindows = sys.platform == 'win32' mswindows = sys.platform == 'win32'
if mswindows: if mswindows:
import msvcrt # pylint: disable=import-error import msvcrt # pylint: disable=import-error
if PY3: class Handle(int):
class Handle(int): closed = False
closed = False
def Close(self):
def Close(self): if not self.closed:
if not self.closed: self.closed = True
self.closed = True _winapi.CloseHandle(self)
_winapi.CloseHandle(self)
def Detach(self):
def Detach(self): if not self.closed:
if not self.closed: self.closed = True
self.closed = True return int(self)
return int(self) raise ValueError("already closed")
raise ValueError("already closed")
def __repr__(self):
def __repr__(self): return "Handle(%d)" % int(self)
return "Handle(%d)" % int(self)
__del__ = Close
__del__ = Close __str__ = __repr__
__str__ = __repr__
else: else:
import fcntl import fcntl
import pickle import pickle
...@@ -478,14 +472,6 @@ def FileObject(*args, **kwargs): ...@@ -478,14 +472,6 @@ def FileObject(*args, **kwargs):
# Defer importing FileObject until we need it # Defer importing FileObject until we need it
# to allow it to be configured more easily. # to allow it to be configured more easily.
from gevent.fileobject import FileObject as _FileObject from gevent.fileobject import FileObject as _FileObject
if not PY3:
# Make write behave like the old Python 2 file
# write and loop to consume output, even when not
# buffered.
__FileObject = _FileObject
def _FileObject(*args, **kwargs):
kwargs['atomic_write'] = True
return __FileObject(*args, **kwargs)
globals()['FileObject'] = _FileObject globals()['FileObject'] = _FileObject
return _FileObject(*args) return _FileObject(*args)
...@@ -666,13 +652,13 @@ class Popen(object): ...@@ -666,13 +652,13 @@ class Popen(object):
_communicate_empty_value = b'' _communicate_empty_value = b''
def __init__(self, args, def __init__(self, args,
bufsize=-1 if PY3 else 0, bufsize=-1,
executable=None, executable=None,
stdin=None, stdout=None, stderr=None, stdin=None, stdout=None, stderr=None,
preexec_fn=None, close_fds=_PLATFORM_DEFAULT_CLOSE_FDS, shell=False, preexec_fn=None, close_fds=_PLATFORM_DEFAULT_CLOSE_FDS, shell=False,
cwd=None, env=None, universal_newlines=None, cwd=None, env=None, universal_newlines=None,
startupinfo=None, creationflags=0, startupinfo=None, creationflags=0,
restore_signals=PY3, start_new_session=False, restore_signals=True, start_new_session=False,
pass_fds=(), pass_fds=(),
# Added in 3.6. These are kept as ivars # Added in 3.6. These are kept as ivars
encoding=None, errors=None, encoding=None, errors=None,
...@@ -696,7 +682,7 @@ class Popen(object): ...@@ -696,7 +682,7 @@ class Popen(object):
if bufsize is None: if bufsize is None:
# Python 2 doesn't allow None at all, but Python 3 treats # Python 2 doesn't allow None at all, but Python 3 treats
# it the same as the default. We do as well. # it the same as the default. We do as well.
bufsize = -1 if PY3 else 0 bufsize = -1
if not isinstance(bufsize, integer_types): if not isinstance(bufsize, integer_types):
raise TypeError("bufsize must be an integer") raise TypeError("bufsize must be an integer")
...@@ -704,20 +690,10 @@ class Popen(object): ...@@ -704,20 +690,10 @@ class Popen(object):
if preexec_fn is not None: if preexec_fn is not None:
raise ValueError("preexec_fn is not supported on Windows " raise ValueError("preexec_fn is not supported on Windows "
"platforms") "platforms")
if PY37:
if close_fds is _PLATFORM_DEFAULT_CLOSE_FDS: if close_fds is _PLATFORM_DEFAULT_CLOSE_FDS:
close_fds = True close_fds = True
else:
any_stdio_set = (stdin is not None or stdout is not None or
stderr is not None)
if close_fds is _PLATFORM_DEFAULT_CLOSE_FDS:
if any_stdio_set:
close_fds = False
else:
close_fds = True
elif close_fds and any_stdio_set:
raise ValueError("close_fds is not supported on Windows "
"platforms if you redirect stdin/stdout/stderr")
if threadpool is None: if threadpool is None:
threadpool = hub.threadpool threadpool = hub.threadpool
self.threadpool = threadpool self.threadpool = threadpool
...@@ -726,10 +702,7 @@ class Popen(object): ...@@ -726,10 +702,7 @@ class Popen(object):
# POSIX # POSIX
if close_fds is _PLATFORM_DEFAULT_CLOSE_FDS: if close_fds is _PLATFORM_DEFAULT_CLOSE_FDS:
# close_fds has different defaults on Py3/Py2 # close_fds has different defaults on Py3/Py2
if PY3: # pylint: disable=simplifiable-if-statement close_fds = True
close_fds = True
else:
close_fds = False
if pass_fds and not close_fds: if pass_fds and not close_fds:
import warnings import warnings
...@@ -791,7 +764,7 @@ class Popen(object): ...@@ -791,7 +764,7 @@ class Popen(object):
if errread != -1: if errread != -1:
errread = msvcrt.open_osfhandle(errread.Detach(), 0) errread = msvcrt.open_osfhandle(errread.Detach(), 0)
text_mode = PY3 and (self.encoding or self.errors or universal_newlines or text) text_mode = self.encoding or self.errors or universal_newlines or text
if text_mode or universal_newlines: if text_mode or universal_newlines:
# Always a native str in universal_newlines mode, even when that # Always a native str in universal_newlines mode, even when that
# str type is bytes. Additionally, text_mode is only true under # str type is bytes. Additionally, text_mode is only true under
...@@ -801,7 +774,7 @@ class Popen(object): ...@@ -801,7 +774,7 @@ class Popen(object):
uid, gid, gids = self.__handle_uids(user, group, extra_groups) uid, gid, gids = self.__handle_uids(user, group, extra_groups)
if p2cwrite != -1: if p2cwrite != -1:
if PY3 and text_mode: if text_mode:
# Under Python 3, if we left on the 'b' we'd get different results # Under Python 3, if we left on the 'b' we'd get different results
# depending on whether we used FileObjectPosix or FileObjectThread # depending on whether we used FileObjectPosix or FileObjectThread
self.stdin = FileObject(p2cwrite, 'w', bufsize, self.stdin = FileObject(p2cwrite, 'w', bufsize,
...@@ -811,25 +784,20 @@ class Popen(object): ...@@ -811,25 +784,20 @@ class Popen(object):
if c2pread != -1: if c2pread != -1:
if universal_newlines or text_mode: if universal_newlines or text_mode:
if PY3: self.stdout = FileObject(c2pread, 'r', bufsize,
self.stdout = FileObject(c2pread, 'r', bufsize, encoding=self.encoding, errors=self.errors)
encoding=self.encoding, errors=self.errors) # NOTE: Universal Newlines are broken on Windows/Py3, at least
# NOTE: Universal Newlines are broken on Windows/Py3, at least # in some cases. This is true in the stdlib subprocess module
# in some cases. This is true in the stdlib subprocess module # as well; the following line would fix the test cases in
# as well; the following line would fix the test cases in # test__subprocess.py that depend on python_universal_newlines,
# test__subprocess.py that depend on python_universal_newlines, # but would be inconsistent with the stdlib:
# but would be inconsistent with the stdlib:
else:
self.stdout = FileObject(c2pread, 'rU', bufsize)
else: else:
self.stdout = FileObject(c2pread, 'rb', bufsize) self.stdout = FileObject(c2pread, 'rb', bufsize)
if errread != -1: if errread != -1:
if universal_newlines or text_mode: if universal_newlines or text_mode:
if PY3: self.stderr = FileObject(errread, 'r', bufsize,
self.stderr = FileObject(errread, 'r', bufsize, encoding=encoding, errors=errors)
encoding=encoding, errors=errors)
else:
self.stderr = FileObject(errread, 'rU', bufsize)
else: else:
self.stderr = FileObject(errread, 'rb', bufsize) self.stderr = FileObject(errread, 'rb', bufsize)
...@@ -853,8 +821,6 @@ class Popen(object): ...@@ -853,8 +821,6 @@ class Popen(object):
# (gevent: New in python3, but reported as gevent bug in #347. # (gevent: New in python3, but reported as gevent bug in #347.
# Note that under Py2, any error raised below will replace the # Note that under Py2, any error raised below will replace the
# original error so we have to use reraise) # original error so we have to use reraise)
if not PY3:
exc_info = sys.exc_info()
for f in filter(None, (self.stdin, self.stdout, self.stderr)): for f in filter(None, (self.stdin, self.stdout, self.stderr)):
try: try:
f.close() f.close()
...@@ -876,11 +842,6 @@ class Popen(object): ...@@ -876,11 +842,6 @@ class Popen(object):
os.close(fd) os.close(fd)
except (OSError, IOError): except (OSError, IOError):
pass pass
if not PY3:
try:
reraise(*exc_info)
finally:
del exc_info
raise raise
def __handle_uids(self, user, group, extra_groups): def __handle_uids(self, user, group, extra_groups):
...@@ -1074,7 +1035,7 @@ class Popen(object): ...@@ -1074,7 +1035,7 @@ class Popen(object):
# blocks forever. # blocks forever.
self.wait() self.wait()
def _gevent_result_wait(self, timeout=None, raise_exc=PY3): def _gevent_result_wait(self, timeout=None, raise_exc=True):
result = self.result.wait(timeout=timeout) result = self.result.wait(timeout=timeout)
if raise_exc and timeout is not None and not self.result.ready(): if raise_exc and timeout is not None and not self.result.ready():
raise TimeoutExpired(self.args, timeout) raise TimeoutExpired(self.args, timeout)
...@@ -1108,13 +1069,11 @@ class Popen(object): ...@@ -1108,13 +1069,11 @@ class Popen(object):
p2cread = GetStdHandle(STD_INPUT_HANDLE) p2cread = GetStdHandle(STD_INPUT_HANDLE)
if p2cread is None: if p2cread is None:
p2cread, _ = CreatePipe(None, 0) p2cread, _ = CreatePipe(None, 0)
if PY3: p2cread = Handle(p2cread)
p2cread = Handle(p2cread) _winapi.CloseHandle(_)
_winapi.CloseHandle(_)
elif stdin == PIPE: elif stdin == PIPE:
p2cread, p2cwrite = CreatePipe(None, 0) p2cread, p2cwrite = CreatePipe(None, 0)
if PY3: p2cread, p2cwrite = Handle(p2cread), Handle(p2cwrite)
p2cread, p2cwrite = Handle(p2cread), Handle(p2cwrite)
elif stdin == _devnull: elif stdin == _devnull:
p2cread = msvcrt.get_osfhandle(self._get_devnull()) p2cread = msvcrt.get_osfhandle(self._get_devnull())
elif isinstance(stdin, int): elif isinstance(stdin, int):
...@@ -1128,13 +1087,11 @@ class Popen(object): ...@@ -1128,13 +1087,11 @@ class Popen(object):
c2pwrite = GetStdHandle(STD_OUTPUT_HANDLE) c2pwrite = GetStdHandle(STD_OUTPUT_HANDLE)
if c2pwrite is None: if c2pwrite is None:
_, c2pwrite = CreatePipe(None, 0) _, c2pwrite = CreatePipe(None, 0)
if PY3: c2pwrite = Handle(c2pwrite)
c2pwrite = Handle(c2pwrite) _winapi.CloseHandle(_)
_winapi.CloseHandle(_)
elif stdout == PIPE: elif stdout == PIPE:
c2pread, c2pwrite = CreatePipe(None, 0) c2pread, c2pwrite = CreatePipe(None, 0)
if PY3: c2pread, c2pwrite = Handle(c2pread), Handle(c2pwrite)
c2pread, c2pwrite = Handle(c2pread), Handle(c2pwrite)
elif stdout == _devnull: elif stdout == _devnull:
c2pwrite = msvcrt.get_osfhandle(self._get_devnull()) c2pwrite = msvcrt.get_osfhandle(self._get_devnull())
elif isinstance(stdout, int): elif isinstance(stdout, int):
...@@ -1148,13 +1105,11 @@ class Popen(object): ...@@ -1148,13 +1105,11 @@ class Popen(object):
errwrite = GetStdHandle(STD_ERROR_HANDLE) errwrite = GetStdHandle(STD_ERROR_HANDLE)
if errwrite is None: if errwrite is None:
_, errwrite = CreatePipe(None, 0) _, errwrite = CreatePipe(None, 0)
if PY3: errwrite = Handle(errwrite)
errwrite = Handle(errwrite) _winapi.CloseHandle(_)
_winapi.CloseHandle(_)
elif stderr == PIPE: elif stderr == PIPE:
errread, errwrite = CreatePipe(None, 0) errread, errwrite = CreatePipe(None, 0)
if PY3: errread, errwrite = Handle(errread), Handle(errwrite)
errread, errwrite = Handle(errread), Handle(errwrite)
elif stderr == STDOUT: elif stderr == STDOUT:
errwrite = c2pwrite errwrite = c2pwrite
elif stderr == _devnull: elif stderr == _devnull:
...@@ -1223,7 +1178,7 @@ class Popen(object): ...@@ -1223,7 +1178,7 @@ class Popen(object):
if isinstance(args, str): if isinstance(args, str):
pass pass
elif isinstance(args, bytes): elif isinstance(args, bytes):
if shell and PY3: if shell:
raise TypeError('bytes args is not allowed on Windows') raise TypeError('bytes args is not allowed on Windows')
args = list2cmdline([args]) args = list2cmdline([args])
elif isinstance(args, PathLike): elif isinstance(args, PathLike):
...@@ -1317,7 +1272,7 @@ class Popen(object): ...@@ -1317,7 +1272,7 @@ class Popen(object):
# CreateProcess call on PyPy. Currently we don't test PyPy3 on Windows, # CreateProcess call on PyPy. Currently we don't test PyPy3 on Windows,
# so we don't know for sure if it's built into CreateProcess there. # so we don't know for sure if it's built into CreateProcess there.
if PYPY: if PYPY:
def _check_nul(s, err_kind=(ValueError if PY3 else TypeError)): def _check_nul(s, err_kind=ValueError):
if not s: if not s:
return return
nul = b'\0' if isinstance(s, bytes) else '\0' nul = b'\0' if isinstance(s, bytes) else '\0'
...@@ -1347,14 +1302,13 @@ class Popen(object): ...@@ -1347,14 +1302,13 @@ class Popen(object):
env, env,
cwd, # fsdecode handled earlier cwd, # fsdecode handled earlier
startupinfo) startupinfo)
except IOError as e: # From 2.6 on, pywintypes.error was defined as IOError # except IOError as e: # From 2.6 on, pywintypes.error was defined as IOError
# Translate pywintypes.error to WindowsError, which is # # Translate pywintypes.error to WindowsError, which is
# a subclass of OSError. FIXME: We should really # # a subclass of OSError. FIXME: We should really
# translate errno using _sys_errlist (or similar), but # # translate errno using _sys_errlist (or similar), but
# how can this be done from Python? # # how can this be done from Python?
if PY3: # raise # don't remap here
raise # don't remap here # raise WindowsError(*e.args)
raise WindowsError(*e.args)
finally: finally:
# Child is launched. Close the parent's copy of those pipe # Child is launched. Close the parent's copy of those pipe
# handles that only the child should have open. You need # handles that only the child should have open. You need
...@@ -1408,7 +1362,7 @@ class Popen(object): ...@@ -1408,7 +1362,7 @@ class Popen(object):
def _wait(self): def _wait(self):
self.threadpool.spawn(self._blocking_wait).rawlink(self.result) self.threadpool.spawn(self._blocking_wait).rawlink(self.result)
def wait(self, timeout=None, _raise_exc=PY3): def wait(self, timeout=None, _raise_exc=True):
"""Wait for child process to terminate. Returns returncode """Wait for child process to terminate. Returns returncode
attribute.""" attribute."""
if self.returncode is None: if self.returncode is None:
...@@ -1628,9 +1582,7 @@ class Popen(object): ...@@ -1628,9 +1582,7 @@ class Popen(object):
start_new_session, process_group): start_new_session, process_group):
"""Execute program (POSIX version)""" """Execute program (POSIX version)"""
if PY3 and isinstance(args, (str, bytes)): if isinstance(args, (str, bytes)):
args = [args]
elif not PY3 and isinstance(args, string_types):
args = [args] args = [args]
elif isinstance(args, PathLike): elif isinstance(args, PathLike):
if shell: if shell:
...@@ -1728,7 +1680,7 @@ class Popen(object): ...@@ -1728,7 +1680,7 @@ class Popen(object):
# Close pipe fds. Make sure we don't close the # Close pipe fds. Make sure we don't close the
# same fd more than once, or standard fds. # same fd more than once, or standard fds.
if not PY3: if not True:
closed = set([None]) closed = set([None])
for fd in [p2cread, c2pwrite, errwrite]: for fd in [p2cread, c2pwrite, errwrite]:
if fd not in closed and fd > 2: if fd not in closed and fd > 2:
...@@ -1785,17 +1737,16 @@ class Popen(object): ...@@ -1785,17 +1737,16 @@ class Popen(object):
if env is None: if env is None:
os.execvp(executable, args) os.execvp(executable, args)
else: else:
if PY3: # Python 3.6 started testing for
# Python 3.6 started testing for # bytes values in the env; it also
# bytes values in the env; it also # started encoding strs using
# started encoding strs using # fsencode and using a lower-level
# fsencode and using a lower-level # API that takes a list of keys
# API that takes a list of keys # and values. We don't have access
# and values. We don't have access # to that API, so we go the reverse direction.
# to that API, so we go the reverse direction. env = {os.fsdecode(k) if isinstance(k, bytes) else k:
env = {os.fsdecode(k) if isinstance(k, bytes) else k: os.fsdecode(v) if isinstance(v, bytes) else v
os.fsdecode(v) if isinstance(v, bytes) else v for k, v in env.items()}
for k, v in env.items()}
os.execvpe(executable, args, env) os.execvpe(executable, args, env)
except: except:
...@@ -1893,7 +1844,7 @@ class Popen(object): ...@@ -1893,7 +1844,7 @@ class Popen(object):
sleep(0.00001) sleep(0.00001)
return self.returncode return self.returncode
def wait(self, timeout=None, _raise_exc=PY3): def wait(self, timeout=None, _raise_exc=True):
""" """
Wait for child process to terminate. Returns :attr:`returncode` Wait for child process to terminate. Returns :attr:`returncode`
attribute. attribute.
......
...@@ -115,7 +115,6 @@ from .skipping import skipOnPy310 ...@@ -115,7 +115,6 @@ from .skipping import skipOnPy310
from .skipping import skipOnPy3 from .skipping import skipOnPy3
from .skipping import skipWithoutResource from .skipping import skipWithoutResource
from .skipping import skipWithoutExternalNetwork from .skipping import skipWithoutExternalNetwork
from .skipping import skipOnPy2
from .skipping import skipOnManylinux from .skipping import skipOnManylinux
from .skipping import skipOnMacOnCI from .skipping import skipOnMacOnCI
......
...@@ -45,7 +45,6 @@ skipOnPyPy3OnCI = _do_not_skip ...@@ -45,7 +45,6 @@ skipOnPyPy3OnCI = _do_not_skip
skipOnPyPy3 = _do_not_skip skipOnPyPy3 = _do_not_skip
skipOnPyPyOnWindows = _do_not_skip skipOnPyPyOnWindows = _do_not_skip
skipOnPy2 = unittest.skip if sysinfo.PY2 else _do_not_skip
skipOnPy3 = unittest.skip if sysinfo.PY3 else _do_not_skip skipOnPy3 = unittest.skip if sysinfo.PY3 else _do_not_skip
skipOnPy37 = unittest.skip if sysinfo.PY37 else _do_not_skip skipOnPy37 = unittest.skip if sysinfo.PY37 else _do_not_skip
skipOnPy310 = unittest.skip if sysinfo.PY310 else _do_not_skip skipOnPy310 = unittest.skip if sysinfo.PY310 else _do_not_skip
......
...@@ -17,18 +17,10 @@ try: ...@@ -17,18 +17,10 @@ try:
except ImportError: except ImportError:
GreenOpenDescriptor = None GreenOpenDescriptor = None
from gevent._compat import PY2
from gevent._compat import PY3
from gevent._compat import text_type
import gevent.testing as greentest import gevent.testing as greentest
from gevent.testing import sysinfo from gevent.testing import sysinfo
try:
ResourceWarning # pylint:disable=used-before-assignment
except NameError:
class ResourceWarning(Warning):
"Python 2 fallback"
# pylint:disable=unspecified-encoding # pylint:disable=unspecified-encoding
...@@ -136,11 +128,9 @@ class TestFileObjectBlock(CleanupMixin, ...@@ -136,11 +128,9 @@ class TestFileObjectBlock(CleanupMixin,
with open(path, 'rb') as f_raw: with open(path, 'rb') as f_raw:
f = self._makeOne(f_raw, 'rb', close=False) f = self._makeOne(f_raw, 'rb', close=False)
# On Python 3, all objects should have seekable.
if PY3 or hasattr(f, 'seekable'): # On Python 2, only our custom objects do.
# On Python 3, all objects should have seekable. self.assertTrue(f.seekable())
# On Python 2, only our custom objects do.
self.assertTrue(f.seekable())
f.seek(15) f.seek(15)
self.assertEqual(15, f.tell()) self.assertEqual(15, f.tell())
...@@ -169,7 +159,7 @@ class TestFileObjectBlock(CleanupMixin, ...@@ -169,7 +159,7 @@ class TestFileObjectBlock(CleanupMixin,
else: else:
# Note that we don't use ``io.open()`` for the raw file, # Note that we don't use ``io.open()`` for the raw file,
# on Python 2. We want 'r' to mean what the usual call to open() means. # on Python 2. We want 'r' to mean what the usual call to open() means.
opener = io.open if PY3 else open opener = io.open
with opener(path, open_mode, **open_kwargs) as raw: with opener(path, open_mode, **open_kwargs) as raw:
with self._makeOne(raw) as f: with self._makeOne(raw) as f:
gevent_data = getattr(f, meth)() gevent_data = getattr(f, meth)()
...@@ -190,7 +180,7 @@ class TestFileObjectBlock(CleanupMixin, ...@@ -190,7 +180,7 @@ class TestFileObjectBlock(CleanupMixin,
'r+', 'r+',
buffering=5, encoding='utf-8' buffering=5, encoding='utf-8'
) )
self.assertIsInstance(gevent_data, text_type) self.assertIsInstance(gevent_data, str)
@skipUnlessWorksWithRegularFiles @skipUnlessWorksWithRegularFiles
def test_does_not_leak_on_exception(self): def test_does_not_leak_on_exception(self):
...@@ -341,7 +331,7 @@ class ConcurrentFileObjectMixin(object): ...@@ -341,7 +331,7 @@ class ConcurrentFileObjectMixin(object):
self.addCleanup(os.close, w) self.addCleanup(os.close, w)
reader = self._makeOne(r) reader = self._makeOne(r)
self._close_on_teardown(reader) self._close_on_teardown(reader)
self.assertEqual(PY2, hasattr(reader, 'read1')) self.assertFalse(hasattr(reader, 'read1'))
def test_bufsize_0(self): def test_bufsize_0(self):
# Issue #840 # Issue #840
......
import sys import sys
import unittest import unittest
from gevent.testing import skipOnPy2
class TestSubnormalFloatsAreNotDisabled(unittest.TestCase): class TestSubnormalFloatsAreNotDisabled(unittest.TestCase):
@skipOnPy2('This test always fails on Python 2')
def test_subnormal_is_not_zero(self): def test_subnormal_is_not_zero(self):
# Enabling the -Ofast compiler flag resulted in subnormal floats getting # Enabling the -Ofast compiler flag resulted in subnormal floats getting
# disabled the moment when gevent was imported. This impacted libraries # disabled the moment when gevent was imported. This impacted libraries
...@@ -21,7 +19,7 @@ class TestSubnormalFloatsAreNotDisabled(unittest.TestCase): ...@@ -21,7 +19,7 @@ class TestSubnormalFloatsAreNotDisabled(unittest.TestCase):
# `sys.float_info.min` is the minimum representable positive normalized # `sys.float_info.min` is the minimum representable positive normalized
# float, so dividing it by two gives us a positive subnormal float, # float, so dividing it by two gives us a positive subnormal float,
# as long as subnormals floats are not disabled. # as long as subnormals floats are not disabled.
self.assertGreater(sys.float_info.min / 2, 0) self.assertGreater(sys.float_info.min / 2, 0.0)
if __name__ == "__main__": if __name__ == "__main__":
......
...@@ -104,21 +104,19 @@ class TestRun(greentest.TestCase): ...@@ -104,21 +104,19 @@ class TestRun(greentest.TestCase):
# Also, occasionally, they get '3' instead of '2' for the number of threads. # Also, occasionally, they get '3' instead of '2' for the number of threads.
# That could have something to do with...? Most commonly that's PyPy, but # That could have something to do with...? Most commonly that's PyPy, but
# sometimes CPython. Again, haven't reproduced. # sometimes CPython. Again, haven't reproduced.
@greentest.skipOnPy2("lost sys.stderr sometimes") # Not relevant since Py2 has been dropped.
def test_threadpool_in_patched_after_patch(self): def test_threadpool_in_patched_after_patch(self):
# Issue 1484 # Issue 1484
# If we don't have this correct, then we get exceptions # If we don't have this correct, then we get exceptions
out = self._run(os.path.join('monkey_package', 'threadpool_monkey_patches.py')) out = self._run(os.path.join('monkey_package', 'threadpool_monkey_patches.py'))
self.assertEqual(out, ['False', '2']) self.assertEqual(out, ['False', '2'])
@greentest.skipOnPy2("lost sys.stderr sometimes")
def test_threadpool_in_patched_after_patch_module(self): def test_threadpool_in_patched_after_patch_module(self):
# Issue 1484 # Issue 1484
# If we don't have this correct, then we get exceptions # If we don't have this correct, then we get exceptions
out = self._run('monkey_package.threadpool_monkey_patches', module=True) out = self._run('monkey_package.threadpool_monkey_patches', module=True)
self.assertEqual(out, ['False', '2']) self.assertEqual(out, ['False', '2'])
@greentest.skipOnPy2("lost sys.stderr sometimes")
def test_threadpool_not_patched_after_patch_module(self): def test_threadpool_not_patched_after_patch_module(self):
# Issue 1484 # Issue 1484
# If we don't have this correct, then we get exceptions # If we don't have this correct, then we get exceptions
......
...@@ -17,9 +17,6 @@ from gevent.tests.test__selectors import SelectorTestMixin ...@@ -17,9 +17,6 @@ from gevent.tests.test__selectors import SelectorTestMixin
class TestSelectors(SelectorTestMixin, greentest.TestCase): class TestSelectors(SelectorTestMixin, greentest.TestCase):
@greentest.skipOnPy2(
'selectors2 backport does not use _select'
)
@greentest.skipOnWindows( @greentest.skipOnWindows(
"SelectSelector._select is a normal function on Windows" "SelectSelector._select is a normal function on Windows"
) )
......
...@@ -11,6 +11,7 @@ class Test(unittest.TestCase): ...@@ -11,6 +11,7 @@ class Test(unittest.TestCase):
# Issue 1108: Python 2, importing pkg_resources, # Issue 1108: Python 2, importing pkg_resources,
# as is done for namespace packages, imports ssl, # as is done for namespace packages, imports ssl,
# leading to an unwanted SSL warning. # leading to an unwanted SSL warning.
# This is a deprecated API though.
__import__('pkg_resources') __import__('pkg_resources')
from gevent import monkey from gevent import monkey
......
...@@ -111,7 +111,6 @@ class TestPopen(greentest.TestCase): ...@@ -111,7 +111,6 @@ class TestPopen(greentest.TestCase):
'pineapple\n\xff\xff\xf2\xf9\n') 'pineapple\n\xff\xff\xf2\xf9\n')
@greentest.skipOnWindows("Windows IO is weird; this doesn't raise") @greentest.skipOnWindows("Windows IO is weird; this doesn't raise")
@greentest.skipOnPy2("Only Python 2 decodes")
def test_communicate_undecodable(self): def test_communicate_undecodable(self):
# If the subprocess writes non-decodable data, `communicate` raises the # If the subprocess writes non-decodable data, `communicate` raises the
# same UnicodeDecodeError that the stdlib does, instead of # same UnicodeDecodeError that the stdlib does, instead of
......
...@@ -9,7 +9,6 @@ Implementation of the standard :mod:`thread` module that spawns greenlets. ...@@ -9,7 +9,6 @@ Implementation of the standard :mod:`thread` module that spawns greenlets.
:class:`gevent.Greenlet` class or :func:`gevent.spawn`. :class:`gevent.Greenlet` class or :func:`gevent.spawn`.
""" """
from __future__ import absolute_import from __future__ import absolute_import
import sys
__implements__ = [ __implements__ = [
'allocate_lock', 'allocate_lock',
...@@ -22,31 +21,23 @@ __implements__ = [ ...@@ -22,31 +21,23 @@ __implements__ = [
] ]
__imports__ = ['error'] __imports__ = ['error']
if sys.version_info[0] == 2:
import thread as __thread__ # pylint:disable=import-error import _thread as __thread__ # pylint:disable=import-error
PY2 = True
PY3 = False __target__ = '_thread'
# Name the `future` backport that might already have been imported; __imports__ += [
# Importing `pkg_resources` imports this, for example. 'TIMEOUT_MAX',
__alternate_targets__ = ('_thread',) 'allocate',
else: 'exit_thread',
import _thread as __thread__ # pylint:disable=import-error 'interrupt_main',
PY2 = False 'start_new'
PY3 = True ]
__target__ = '_thread'
__imports__ += [ # We can't actually produce a value that "may be used
'TIMEOUT_MAX', # to identify this particular thread system-wide", right?
'allocate', # Even if we could, I imagine people will want to pass this to
'exit_thread', # non-Python (native) APIs, so we shouldn't mess with it.
'interrupt_main', __imports__.append('get_native_id')
'start_new'
]
if sys.version_info[:2] >= (3, 8):
# We can't actually produce a value that "may be used
# to identify this particular thread system-wide", right?
# Even if we could, I imagine people will want to pass this to
# non-Python (native) APIs, so we shouldn't mess with it.
__imports__.append('get_native_id')
error = __thread__.error error = __thread__.error
...@@ -63,7 +54,6 @@ from gevent.local import local as _local ...@@ -63,7 +54,6 @@ from gevent.local import local as _local
from gevent.exceptions import LoopExit from gevent.exceptions import LoopExit
if hasattr(__thread__, 'RLock'): if hasattr(__thread__, 'RLock'):
assert PY3 or PYPY
# Added in Python 3.4, backported to PyPy 2.7-7.0 # Added in Python 3.4, backported to PyPy 2.7-7.0
__imports__.append("RLock") __imports__.append("RLock")
...@@ -88,13 +78,11 @@ class LockType(BoundedSemaphore): ...@@ -88,13 +78,11 @@ class LockType(BoundedSemaphore):
# and any other API changes we need to make to match behaviour # and any other API changes we need to make to match behaviour
_OVER_RELEASE_ERROR = __thread__.error _OVER_RELEASE_ERROR = __thread__.error
if PYPY and PY3: if PYPY:
_OVER_RELEASE_ERROR = RuntimeError _OVER_RELEASE_ERROR = RuntimeError
if PY3:
_TIMEOUT_MAX = __thread__.TIMEOUT_MAX # python 2: pylint:disable=no-member _TIMEOUT_MAX = __thread__.TIMEOUT_MAX # pylint:disable=no-member
else:
_TIMEOUT_MAX = 9223372036.0
def acquire(self, blocking=True, timeout=-1): def acquire(self, blocking=True, timeout=-1):
# This is the Python 3 signature. # This is the Python 3 signature.
......
...@@ -50,8 +50,7 @@ from gevent.thread import get_ident as _get_ident ...@@ -50,8 +50,7 @@ from gevent.thread import get_ident as _get_ident
from gevent.hub import sleep as _sleep, getcurrent from gevent.hub import sleep as _sleep, getcurrent
from gevent.lock import RLock from gevent.lock import RLock
from gevent._compat import PY3
from gevent._compat import PYPY
from gevent._util import LazyOnClass from gevent._util import LazyOnClass
# Exports, prevent unused import warnings. # Exports, prevent unused import warnings.
...@@ -168,46 +167,45 @@ else: ...@@ -168,46 +167,45 @@ else:
return main_threads[0] return main_threads[0]
if PY3:
# XXX: Issue 18808 breaks us on Python 3.4+.
# Thread objects now expect a callback from the interpreter itself
# (threadmodule.c:release_sentinel) when the C-level PyThreadState
# object is being deallocated. Because this never happens
# when a greenlet exits, join() and friends will block forever.
# Fortunately this is easy to fix: just ensure that the allocation of the
# lock, _set_sentinel, creates a *gevent* lock, and release it when
# we're done. The main _shutdown code is in Python and deals with
# this gracefully.
class Thread(__threading__.Thread): # XXX: Issue 18808 breaks us on Python 3.4+.
# Thread objects now expect a callback from the interpreter itself
# (threadmodule.c:release_sentinel) when the C-level PyThreadState
# object is being deallocated. Because this never happens
# when a greenlet exits, join() and friends will block forever.
# Fortunately this is easy to fix: just ensure that the allocation of the
# lock, _set_sentinel, creates a *gevent* lock, and release it when
# we're done. The main _shutdown code is in Python and deals with
# this gracefully.
def _set_tstate_lock(self): class Thread(__threading__.Thread):
super(Thread, self)._set_tstate_lock()
greenlet = getcurrent()
greenlet.rawlink(self.__greenlet_finished)
def __greenlet_finished(self, _): def _set_tstate_lock(self):
if self._tstate_lock: super(Thread, self)._set_tstate_lock()
self._tstate_lock.release() greenlet = getcurrent()
self._stop() greenlet.rawlink(self.__greenlet_finished)
__implements__.append('Thread') def __greenlet_finished(self, _):
if self._tstate_lock:
self._tstate_lock.release()
self._stop()
class Timer(Thread, __threading__.Timer): # pylint:disable=abstract-method,inherit-non-class __implements__.append('Thread')
pass
class Timer(Thread, __threading__.Timer): # pylint:disable=abstract-method,inherit-non-class
pass
__implements__.append('Timer') __implements__.append('Timer')
_set_sentinel = allocate_lock _set_sentinel = allocate_lock
__implements__.append('_set_sentinel') __implements__.append('_set_sentinel')
# The main thread is patched up with more care # The main thread is patched up with more care
# in _gevent_will_monkey_patch # in _gevent_will_monkey_patch
if PY3: __implements__.remove('_get_ident')
__implements__.remove('_get_ident') __implements__.append('get_ident')
__implements__.append('get_ident') get_ident = _get_ident
get_ident = _get_ident __implements__.remove('_sleep')
__implements__.remove('_sleep')
if hasattr(__threading__, '_CRLock'): if hasattr(__threading__, '_CRLock'):
# Python 3 changed the implementation of threading.RLock # Python 3 changed the implementation of threading.RLock
...@@ -219,7 +217,6 @@ if hasattr(__threading__, '_CRLock'): ...@@ -219,7 +217,6 @@ if hasattr(__threading__, '_CRLock'):
# if the imported _CRLock is None; this arranges for that to be the case. # if the imported _CRLock is None; this arranges for that to be the case.
# This was also backported to PyPy 2.7-7.0 # This was also backported to PyPy 2.7-7.0
assert PY3 or PYPY, "Unsupported Python version"
_CRLock = None _CRLock = None
__implements__.append('_CRLock') __implements__.append('_CRLock')
......
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