Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-eventlet for openSUSE:Factory
checked in at 2025-01-07 20:50:54
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-eventlet (Old)
and /work/SRC/openSUSE:Factory/.python-eventlet.new.1881 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-eventlet"
Tue Jan 7 20:50:54 2025 rev:59 rq:1235524 version:0.38.2
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-eventlet/python-eventlet.changes
2024-12-06 14:25:02.853116597 +0100
+++
/work/SRC/openSUSE:Factory/.python-eventlet.new.1881/python-eventlet.changes
2025-01-07 20:51:17.907650642 +0100
@@ -1,0 +2,11 @@
+Mon Jan 6 18:25:35 UTC 2025 - John Paul Adrian Glaubitz
<[email protected]>
+
+- Update to 0.38.2
+ * [fix] fix the monkey patching with the asyncio hub
+ * [feature] introduce the unmonkeypatching feature
+- from version 0.38.1
+ * [fix] Python 3.13: Use greenthread's dead state where possible (#1000)
+ * [env] bump github Actions (#996)
+ * [fix] Fix bug where asyncio hub didn't support multiple os threads (#995)
+
+-------------------------------------------------------------------
Old:
----
eventlet-0.38.0.tar.gz
New:
----
eventlet-0.38.2.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-eventlet.spec ++++++
--- /var/tmp/diff_new_pack.YKsJ0M/_old 2025-01-07 20:51:18.395670820 +0100
+++ /var/tmp/diff_new_pack.YKsJ0M/_new 2025-01-07 20:51:18.399670985 +0100
@@ -1,7 +1,7 @@
#
# spec file for package python-eventlet
#
-# Copyright (c) 2024 SUSE LLC
+# Copyright (c) 2025 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -18,7 +18,7 @@
%{?sle15_python_module_pythons}
Name: python-eventlet
-Version: 0.38.0
+Version: 0.38.2
Release: 0
Summary: Concurrent networking library for Python
License: MIT
++++++ eventlet-0.38.0.tar.gz -> eventlet-0.38.2.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/eventlet-0.38.0/.github/workflows/docs.yaml
new/eventlet-0.38.2/.github/workflows/docs.yaml
--- old/eventlet-0.38.0/.github/workflows/docs.yaml 2020-02-02
01:00:00.000000000 +0100
+++ new/eventlet-0.38.2/.github/workflows/docs.yaml 2020-02-02
01:00:00.000000000 +0100
@@ -11,9 +11,9 @@
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Set up Python
- uses: actions/setup-python@v3
+ uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/eventlet-0.38.0/.github/workflows/publish.yaml
new/eventlet-0.38.2/.github/workflows/publish.yaml
--- old/eventlet-0.38.0/.github/workflows/publish.yaml 2020-02-02
01:00:00.000000000 +0100
+++ new/eventlet-0.38.2/.github/workflows/publish.yaml 2020-02-02
01:00:00.000000000 +0100
@@ -19,9 +19,9 @@
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Set up Python
- uses: actions/setup-python@v3
+ uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/eventlet-0.38.0/.github/workflows/style.yaml
new/eventlet-0.38.2/.github/workflows/style.yaml
--- old/eventlet-0.38.0/.github/workflows/style.yaml 2020-02-02
01:00:00.000000000 +0100
+++ new/eventlet-0.38.2/.github/workflows/style.yaml 2020-02-02
01:00:00.000000000 +0100
@@ -10,10 +10,10 @@
timeout-minutes: 5
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: cache pip
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{
hashFiles('.github/workflows/style.yaml') }}
@@ -21,7 +21,7 @@
${{ runner.os }}-pip-
${{ runner.os }}-
- name: cache tox
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: .tox
key: ${{ runner.os }}-tox-style-${{ hashFiles('tox.ini') }}
@@ -31,7 +31,7 @@
${{ runner.os }}-
- name: setup python
- uses: actions/setup-python@v3
+ uses: actions/setup-python@v5
with:
python-version: 3.x
- name: install tox
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/eventlet-0.38.0/.github/workflows/test.yaml
new/eventlet-0.38.2/.github/workflows/test.yaml
--- old/eventlet-0.38.0/.github/workflows/test.yaml 2020-02-02
01:00:00.000000000 +0100
+++ new/eventlet-0.38.2/.github/workflows/test.yaml 2020-02-02
01:00:00.000000000 +0100
@@ -50,17 +50,17 @@
- { py: "3.11", toxenv: py311-asyncio, ignore-error: false, os:
ubuntu-latest }
- { py: "3.12", toxenv: py312-epolls, ignore-error: false, os:
ubuntu-latest }
- { py: "3.12", toxenv: py312-asyncio, ignore-error: false, os:
ubuntu-latest }
- - { py: "3.13-dev", toxenv: py313-epolls, ignore-error: false, os:
ubuntu-24.04 }
- - { py: "3.13-dev", toxenv: py313-asyncio, ignore-error: false, os:
ubuntu-24.04 }
+ - { py: "3.13", toxenv: py313-epolls, ignore-error: false, os:
ubuntu-24.04 }
+ - { py: "3.13", toxenv: py313-asyncio, ignore-error: false, os:
ubuntu-24.04 }
- { py: pypy3.9, toxenv: pypy3-epolls, ignore-error: true, os:
ubuntu-20.04 }
steps:
- name: install system packages
run: sudo apt install -y --no-install-recommends ccache libffi-dev
default-libmysqlclient-dev libpq-dev libssl-dev libzmq3-dev
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: cache pip
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ matrix.toxenv }}-${{
hashFiles('.github/workflows/test.yaml', 'setup.py') }}
@@ -68,7 +68,7 @@
${{ runner.os }}-pip-
${{ runner.os }}-
- name: cache tox
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: .tox
key: ${{ runner.os }}-tox-${{ matrix.toxenv }}-${{
hashFiles('tox.ini') }}
@@ -77,7 +77,7 @@
${{ runner.os }}-
- name: setup python ${{ matrix.py }}
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: ${{ matrix.py }}
- name: install codecov, tox
@@ -100,14 +100,14 @@
matrix:
include:
- { py: "3.12", toxenv: py312-asyncio, ignore-error: false, os:
macos-latest }
- - { py: "3.13-dev", toxenv: py313-asyncio, ignore-error: false, os:
macos-latest }
+ - { py: "3.13", toxenv: py313-asyncio, ignore-error: false, os:
macos-latest }
# This isn't working very well at the moment, but that might just be
# tox config? In any case main focus is on asyncio so someone can
# revisit separately.
#- { py: "3.12", toxenv: py312-kqueue, ignore-error: false, os:
macos-latest }
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: install codecov, tox
run: pip install codecov tox
- run: env
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/eventlet-0.38.0/NEWS new/eventlet-0.38.2/NEWS
--- old/eventlet-0.38.0/NEWS 2020-02-02 01:00:00.000000000 +0100
+++ new/eventlet-0.38.2/NEWS 2020-02-02 01:00:00.000000000 +0100
@@ -1,6 +1,19 @@
Unreleased
==========
+0.38.2
+======
+
+* [fix] fix the monkey patching with the asyncio hub
+* [feature] introduce the unmonkeypatching feature
+
+0.38.1
+======
+
+* [fix] Python 3.13: Use greenthread's dead state where possible (#1000)
+* [env] bump github Actions (#996)
+* [fix] Fix bug where asyncio hub didn't support multiple os threads (#995)
+
0.38.0
======
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/eventlet-0.38.0/PKG-INFO new/eventlet-0.38.2/PKG-INFO
--- old/eventlet-0.38.0/PKG-INFO 2020-02-02 01:00:00.000000000 +0100
+++ new/eventlet-0.38.2/PKG-INFO 2020-02-02 01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.3
Name: eventlet
-Version: 0.38.0
+Version: 0.38.2
Summary: Highly concurrent networking library
Project-URL: Homepage, https://github.com/eventlet/eventlet
Project-URL: History, https://github.com/eventlet/eventlet/blob/master/NEWS
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/eventlet-0.38.0/eventlet/_version.py
new/eventlet-0.38.2/eventlet/_version.py
--- old/eventlet-0.38.0/eventlet/_version.py 2020-02-02 01:00:00.000000000
+0100
+++ new/eventlet-0.38.2/eventlet/_version.py 2020-02-02 01:00:00.000000000
+0100
@@ -12,5 +12,5 @@
__version_tuple__: VERSION_TUPLE
version_tuple: VERSION_TUPLE
-__version__ = version = '0.38.0'
-__version_tuple__ = version_tuple = (0, 38, 0)
+__version__ = version = '0.38.2'
+__version_tuple__ = version_tuple = (0, 38, 2)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/eventlet-0.38.0/eventlet/green/thread.py
new/eventlet-0.38.2/eventlet/green/thread.py
--- old/eventlet-0.38.0/eventlet/green/thread.py 2020-02-02
01:00:00.000000000 +0100
+++ new/eventlet-0.38.2/eventlet/green/thread.py 2020-02-02
01:00:00.000000000 +0100
@@ -59,6 +59,8 @@
self._done = True
def is_done(self):
+ if self._greenthread is not None:
+ return self._greenthread.dead
return self._done
@property
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/eventlet-0.38.0/eventlet/hubs/asyncio.py
new/eventlet-0.38.2/eventlet/hubs/asyncio.py
--- old/eventlet-0.38.0/eventlet/hubs/asyncio.py 2020-02-02
01:00:00.000000000 +0100
+++ new/eventlet-0.38.2/eventlet/hubs/asyncio.py 2020-02-02
01:00:00.000000000 +0100
@@ -2,19 +2,19 @@
Asyncio-based hub, originally implemented by Miguel Grinberg.
"""
-import asyncio
-try:
- import concurrent.futures.thread
- concurrent_imported = True
-except RuntimeError:
- # This happens in weird edge cases where asyncio hub is started at
- # shutdown. Not much we can do if this happens.
- concurrent_imported = False
+# The various modules involved in asyncio need to call the original, unpatched
+# standard library APIs to work: socket, select, threading, and so on. We
+# therefore don't import them on the module level, since that would involve
+# their imports getting patched, and instead delay importing them as much as
+# possible. Then, we do a little song and dance in Hub.__init__ below so that
+# when they're imported they import the original modules (select, socket, etc)
+# rather than the patched ones.
+
import os
import sys
from eventlet.hubs import hub
-from eventlet.patcher import original
+from eventlet.patcher import _unmonkey_patch_asyncio_all
def is_available():
@@ -32,22 +32,14 @@
def __init__(self):
super().__init__()
- # Make sure asyncio thread pools use real threads:
- if concurrent_imported:
- concurrent.futures.thread.threading = original("threading")
- concurrent.futures.thread.queue = original("queue")
-
- # Make sure select/poll/epoll/kqueue are usable by asyncio:
- import selectors
- selectors.select = original("select")
-
- # Make sure DNS lookups use normal blocking API (which asyncio will run
- # in a thread):
- import asyncio.base_events
- asyncio.base_events.socket = original("socket")
+
+ # Pre-emptively make sure we're using the right modules:
+ _unmonkey_patch_asyncio_all()
# The presumption is that eventlet is driving the event loop, so we
# want a new one we control.
+ import asyncio
+
self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop)
self.sleep_event = asyncio.Event()
@@ -83,7 +75,7 @@
try:
os.fstat(fileno)
except OSError:
- raise ValueError('Invalid file descriptor')
+ raise ValueError("Invalid file descriptor")
already_listening = self.listeners[evtype].get(fileno) is not None
listener = super().add(evtype, fileno, cb, tb, mark_as_closed)
if not already_listening:
@@ -126,6 +118,8 @@
"""
Start the ``Hub`` running. See the superclass for details.
"""
+ import asyncio
+
async def async_run():
if self.running:
raise RuntimeError("Already running!")
@@ -150,8 +144,7 @@
sleep_time = wakeup_when - self.clock()
if sleep_time > 0:
try:
- await asyncio.wait_for(self.sleep_event.wait(),
- sleep_time)
+ await asyncio.wait_for(self.sleep_event.wait(),
sleep_time)
except asyncio.TimeoutError:
pass
self.sleep_event.clear()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/eventlet-0.38.0/eventlet/patcher.py
new/eventlet-0.38.2/eventlet/patcher.py
--- old/eventlet-0.38.0/eventlet/patcher.py 2020-02-02 01:00:00.000000000
+0100
+++ new/eventlet-0.38.2/eventlet/patcher.py 2020-02-02 01:00:00.000000000
+0100
@@ -1,9 +1,12 @@
from __future__ import annotations
+
try:
import _imp as imp
except ImportError:
import imp
+import importlib
import sys
+
try:
# Only for this purpose, it's irrelevant if `os` was already patched.
# https://github.com/eventlet/eventlet/pull/661
@@ -14,9 +17,9 @@
import eventlet
-__all__ = ['inject', 'import_patched', 'monkey_patch', 'is_monkey_patched']
+__all__ = ["inject", "import_patched", "monkey_patch", "is_monkey_patched"]
-__exclude = {'__builtins__', '__file__', '__name__'}
+__exclude = {"__builtins__", "__file__", "__name__"}
class SysModulesSaver:
@@ -70,7 +73,7 @@
name/module pairs is used, which should cover all use cases but may be
slower because there are inevitably redundant or unnecessary imports.
"""
- patched_name = '__patched_module_' + module_name
+ patched_name = "__patched_module_" + module_name
if patched_name in sys.modules:
# returning already-patched module so as not to destroy existing
# references to patched modules
@@ -79,11 +82,12 @@
if not additional_modules:
# supply some defaults
additional_modules = (
- _green_os_modules() +
- _green_select_modules() +
- _green_socket_modules() +
- _green_thread_modules() +
- _green_time_modules())
+ _green_os_modules()
+ + _green_select_modules()
+ + _green_socket_modules()
+ + _green_thread_modules()
+ + _green_time_modules()
+ )
# _green_MySQLdb()) # enable this after a short baking-in period
# after this we are gonna screw with sys.modules, so capture the
@@ -103,10 +107,10 @@
# because of the pop operations will change the content of sys.modules
# within th loop
for imported_module_name in list(sys.modules.keys()):
- if imported_module_name.startswith(module_name + '.'):
+ if imported_module_name.startswith(module_name + "."):
sys.modules.pop(imported_module_name, None)
try:
- module = __import__(module_name, {}, {}, module_name.split('.')[:-1])
+ module = __import__(module_name, {}, {}, module_name.split(".")[:-1])
if new_globals is not None:
# Update the given globals dictionary with everything from this
new module
@@ -130,9 +134,8 @@
The only required argument is the name of the module to be imported.
"""
return inject(
- module_name,
- None,
- *additional_modules + tuple(kw_additional_modules.items()))
+ module_name, None, *additional_modules +
tuple(kw_additional_modules.items())
+ )
def patch_function(func, *additional_modules):
@@ -144,11 +147,12 @@
if not additional_modules:
# supply some defaults
additional_modules = (
- _green_os_modules() +
- _green_select_modules() +
- _green_socket_modules() +
- _green_thread_modules() +
- _green_time_modules())
+ _green_os_modules()
+ + _green_select_modules()
+ + _green_socket_modules()
+ + _green_thread_modules()
+ + _green_time_modules()
+ )
def patched(*args, **kw):
saver = SysModulesSaver()
@@ -159,6 +163,7 @@
return func(*args, **kw)
finally:
saver.restore()
+
return patched
@@ -169,6 +174,7 @@
patch_function, only the names of the modules need be supplied,
and there are no defaults. This is a gross hack; tell your kids not
to import inside function bodies!"""
+
def patched(*args, **kw):
saver = SysModulesSaver(module_names)
for name in module_names:
@@ -177,17 +183,18 @@
return func(*args, **kw)
finally:
saver.restore()
+
return patched
def original(modname):
- """ This returns an unpatched version of a module; this is useful for
+ """This returns an unpatched version of a module; this is useful for
Eventlet itself (i.e. tpool)."""
# note that it's not necessary to temporarily install unpatched
# versions of all patchable modules during the import of the
# module; this is because none of them import each other, except
# for threading which imports thread
- original_name = '__original_module_' + modname
+ original_name = "__original_module_" + modname
if original_name in sys.modules:
return sys.modules.get(original_name)
@@ -198,20 +205,20 @@
# some rudimentary dependency checking -- fortunately the modules
# we're working on don't have many dependencies so we can just do
# some special-casing here
- deps = {'threading': '_thread', 'queue': 'threading'}
+ deps = {"threading": "_thread", "queue": "threading"}
if modname in deps:
dependency = deps[modname]
saver.save(dependency)
sys.modules[dependency] = original(dependency)
try:
- real_mod = __import__(modname, {}, {}, modname.split('.')[:-1])
- if modname in ('Queue', 'queue') and not hasattr(real_mod,
'_threading'):
+ real_mod = __import__(modname, {}, {}, modname.split(".")[:-1])
+ if modname in ("Queue", "queue") and not hasattr(real_mod,
"_threading"):
# tricky hack: Queue's constructor in <2.7 imports
# threading on every instantiation; therefore we wrap
# it so that it always gets the original threading
real_mod.Queue.__init__ = _original_patch_function(
- real_mod.Queue.__init__,
- 'threading')
+ real_mod.Queue.__init__, "threading"
+ )
# save a reference to the unpatched module so it doesn't get lost
sys.modules[original_name] = real_mod
finally:
@@ -223,6 +230,99 @@
already_patched = {}
+def _unmonkey_patch_asyncio(unmonkeypatch_refs_to_this_module):
+ """
+ When using asyncio hub, we want the asyncio modules to use the original,
+ blocking APIs. So un-monkeypatch references to the given module name, e.g.
+ "select".
+ """
+ to_unpatch = unmonkeypatch_refs_to_this_module
+ original_module = original(to_unpatch)
+
+ # Lower down for asyncio modules, we will switch their imported modules to
+ # original ones instead of the green ones they probably have. This won't
+ # fix "from socket import whatev" but asyncio doesn't seem to do that in
+ # ways we care about for Python 3.8 to 3.13, with the one exception of
+ # get_ident() in some older versions.
+ if to_unpatch == "_thread":
+ import asyncio.base_futures
+
+ if hasattr(asyncio.base_futures, "get_ident"):
+ asyncio.base_futures = original_module.get_ident
+
+ # Asyncio uses these for its blocking thread pool:
+ if to_unpatch in ("threading", "queue"):
+ try:
+ import concurrent.futures.thread
+ except RuntimeError:
+ # This happens in weird edge cases where asyncio hub is started at
+ # shutdown. Not much we can do if this happens.
+ pass
+ else:
+ if to_unpatch == "threading":
+ concurrent.futures.thread.threading = original_module
+ if to_unpatch == "queue":
+ concurrent.futures.thread.queue = original_module
+
+ # Patch asyncio modules:
+ for module_name in [
+ "asyncio.base_events",
+ "asyncio.base_futures",
+ "asyncio.base_subprocess",
+ "asyncio.base_tasks",
+ "asyncio.constants",
+ "asyncio.coroutines",
+ "asyncio.events",
+ "asyncio.exceptions",
+ "asyncio.format_helpers",
+ "asyncio.futures",
+ "asyncio",
+ "asyncio.locks",
+ "asyncio.log",
+ "asyncio.mixins",
+ "asyncio.protocols",
+ "asyncio.queues",
+ "asyncio.runners",
+ "asyncio.selector_events",
+ "asyncio.sslproto",
+ "asyncio.staggered",
+ "asyncio.streams",
+ "asyncio.subprocess",
+ "asyncio.taskgroups",
+ "asyncio.tasks",
+ "asyncio.threads",
+ "asyncio.timeouts",
+ "asyncio.transports",
+ "asyncio.trsock",
+ "asyncio.unix_events",
+ ]:
+ try:
+ module = importlib.import_module(module_name)
+ except ImportError:
+ # The list is from Python 3.13, so some modules may not be present
+ # in older versions of Python:
+ continue
+ if getattr(module, to_unpatch, None) is sys.modules[to_unpatch]:
+ setattr(module, to_unpatch, original_module)
+
+
+def _unmonkey_patch_asyncio_all():
+ """
+ Unmonkey-patch all referred-to modules in asyncio.
+ """
+ for module_name, _ in sum([
+ _green_os_modules(),
+ _green_select_modules(),
+ _green_socket_modules(),
+ _green_thread_modules(),
+ _green_time_modules(),
+ _green_builtins(),
+ _green_subprocess_modules(),
+ ], []):
+ _unmonkey_patch_asyncio(module_name)
+ original("selectors").select = original("select")
+
+
def monkey_patch(**on):
"""Globally patches certain system modules to be greenthread-friendly.
@@ -246,57 +346,68 @@
# the hub calls into monkey-patched modules.
eventlet.hubs.get_hub()
- accepted_args = {'os', 'select', 'socket',
- 'thread', 'time', 'psycopg', 'MySQLdb',
- 'builtins', 'subprocess'}
+ accepted_args = {
+ "os",
+ "select",
+ "socket",
+ "thread",
+ "time",
+ "psycopg",
+ "MySQLdb",
+ "builtins",
+ "subprocess",
+ }
# To make sure only one of them is passed here
- assert not ('__builtin__' in on and 'builtins' in on)
+ assert not ("__builtin__" in on and "builtins" in on)
try:
- b = on.pop('__builtin__')
+ b = on.pop("__builtin__")
except KeyError:
pass
else:
- on['builtins'] = b
+ on["builtins"] = b
default_on = on.pop("all", None)
for k in on.keys():
if k not in accepted_args:
- raise TypeError("monkey_patch() got an unexpected "
- "keyword argument %r" % k)
+ raise TypeError(
+ "monkey_patch() got an unexpected " "keyword argument %r" % k
+ )
if default_on is None:
default_on = True not in on.values()
for modname in accepted_args:
- if modname == 'MySQLdb':
+ if modname == "MySQLdb":
# MySQLdb is only on when explicitly patched for the moment
on.setdefault(modname, False)
- if modname == 'builtins':
+ if modname == "builtins":
on.setdefault(modname, False)
on.setdefault(modname, default_on)
import threading
+
original_rlock_type = type(threading.RLock())
modules_to_patch = []
for name, modules_function in [
- ('os', _green_os_modules),
- ('select', _green_select_modules),
- ('socket', _green_socket_modules),
- ('thread', _green_thread_modules),
- ('time', _green_time_modules),
- ('MySQLdb', _green_MySQLdb),
- ('builtins', _green_builtins),
- ('subprocess', _green_subprocess_modules),
+ ("os", _green_os_modules),
+ ("select", _green_select_modules),
+ ("socket", _green_socket_modules),
+ ("thread", _green_thread_modules),
+ ("time", _green_time_modules),
+ ("MySQLdb", _green_MySQLdb),
+ ("builtins", _green_builtins),
+ ("subprocess", _green_subprocess_modules),
]:
if on[name] and not already_patched.get(name):
modules_to_patch += modules_function()
already_patched[name] = True
- if on['psycopg'] and not already_patched.get('psycopg'):
+ if on["psycopg"] and not already_patched.get("psycopg"):
try:
from eventlet.support import psycopg2_patcher
+
psycopg2_patcher.make_psycopg_green()
- already_patched['psycopg'] = True
+ already_patched["psycopg"] = True
except ImportError:
# note that if we get an importerror from trying to
# monkeypatch psycopg, we will continually retry it
@@ -305,7 +416,7 @@
# tell us whether or not we succeeded
pass
- _threading = original('threading')
+ _threading = original("threading")
imp.acquire_lock()
try:
for name, mod in modules_to_patch:
@@ -316,13 +427,14 @@
patched_attr = getattr(mod, attr_name, None)
if patched_attr is not None:
setattr(orig_mod, attr_name, patched_attr)
- deleted = getattr(mod, '__deleted__', [])
+ deleted = getattr(mod, "__deleted__", [])
for attr_name in deleted:
if hasattr(orig_mod, attr_name):
delattr(orig_mod, attr_name)
# https://github.com/eventlet/eventlet/issues/592
- if name == 'threading' and register_at_fork:
+ if name == "threading" and register_at_fork:
+
def fix_threading_active(
_global_dict=_threading.current_thread.__globals__,
# alias orig_mod as patched to reflect its new state
@@ -332,21 +444,21 @@
_prefork_active = [None]
def before_fork():
- _prefork_active[0] = _global_dict['_active']
- _global_dict['_active'] = _patched._active
+ _prefork_active[0] = _global_dict["_active"]
+ _global_dict["_active"] = _patched._active
def after_fork():
- _global_dict['_active'] = _prefork_active[0]
+ _global_dict["_active"] = _prefork_active[0]
+
+ register_at_fork(before=before_fork,
after_in_parent=after_fork)
- register_at_fork(
- before=before_fork,
- after_in_parent=after_fork)
fix_threading_active()
finally:
imp.release_lock()
import importlib._bootstrap
- thread = original('_thread')
+
+ thread = original("_thread")
# importlib must use real thread locks, not eventlet.Semaphore
importlib._bootstrap._thread = thread
@@ -355,16 +467,20 @@
# threading.get_ident(). Force the Python implementation of RLock which
# calls threading.get_ident() and so is compatible with eventlet.
import threading
+
threading.RLock = threading._PyRLock
# Issue #508: Since Python 3.7 queue.SimpleQueue is implemented in C,
# causing a deadlock. Replace the C implementation with the Python one.
import queue
+
queue.SimpleQueue = queue._PySimpleQueue
# Green existing locks _after_ patching modules, since patching modules
# might involve imports that create new locks:
- _green_existing_locks(original_rlock_type)
+ for name, _ in modules_to_patch:
+ if name == "threading":
+ _green_existing_locks(original_rlock_type)
def is_monkey_patched(module):
@@ -375,8 +491,10 @@
module some other way than with the import keyword (including
import_patched), this might not be correct about that particular
module."""
- return module in already_patched or \
- getattr(module, '__name__', None) in already_patched
+ return (
+ module in already_patched
+ or getattr(module, "__name__", None) in already_patched
+ )
def _green_existing_locks(rlock_type):
@@ -428,10 +546,13 @@
if remaining_rlocks:
import logging
+
logger = logging.Logger("eventlet")
- logger.error("{} RLock(s) were not greened,".format(remaining_rlocks) +
- " to fix this error make sure you run
eventlet.monkey_patch() " +
- "before importing any other modules.")
+ logger.error(
+ "{} RLock(s) were not greened,".format(remaining_rlocks)
+ + " to fix this error make sure you run eventlet.monkey_patch() "
+ + "before importing any other modules."
+ )
def _upgrade_instances(container, klass, upgrade, visited=None,
old_to_new=None):
@@ -492,10 +613,14 @@
setattr(container, k, new)
except:
import logging
+
logger = logging.Logger("eventlet")
- logger.exception("An exception was thrown while monkey_patching
for eventlet. "
- "to fix this error make sure you run
eventlet.monkey_patch() "
- "before importing any other modules.",
exc_info=True)
+ logger.exception(
+ "An exception was thrown while monkey_patching for eventlet. "
+ "to fix this error make sure you run eventlet.monkey_patch() "
+ "before importing any other modules.",
+ exc_info=True,
+ )
def _convert_py3_rlock(old, tid):
@@ -508,14 +633,16 @@
"""
import threading
from eventlet.green.thread import allocate_lock
+
new = threading._PyRLock()
if not hasattr(new, "_block") or not hasattr(new, "_owner"):
# These will only fail if Python changes its internal implementation of
# _PyRLock:
raise RuntimeError(
- "INTERNAL BUG. Perhaps you are using a major version " +
- "of Python that is unsupported by eventlet? Please file a bug " +
- "at https://github.com/eventlet/eventlet/issues/new")
+ "INTERNAL BUG. Perhaps you are using a major version "
+ + "of Python that is unsupported by eventlet? Please file a bug "
+ + "at https://github.com/eventlet/eventlet/issues/new"
+ )
new._block = allocate_lock()
acquired = False
while old._is_owned():
@@ -532,49 +659,58 @@
def _green_os_modules():
from eventlet.green import os
- return [('os', os)]
+
+ return [("os", os)]
def _green_select_modules():
from eventlet.green import select
- modules = [('select', select)]
+
+ modules = [("select", select)]
from eventlet.green import selectors
- modules.append(('selectors', selectors))
+
+ modules.append(("selectors", selectors))
return modules
def _green_socket_modules():
from eventlet.green import socket
+
try:
from eventlet.green import ssl
- return [('socket', socket), ('ssl', ssl)]
+
+ return [("socket", socket), ("ssl", ssl)]
except ImportError:
- return [('socket', socket)]
+ return [("socket", socket)]
def _green_subprocess_modules():
from eventlet.green import subprocess
- return [('subprocess', subprocess)]
+
+ return [("subprocess", subprocess)]
def _green_thread_modules():
from eventlet.green import Queue
from eventlet.green import thread
from eventlet.green import threading
- return [('queue', Queue), ('_thread', thread), ('threading', threading)]
+
+ return [("queue", Queue), ("_thread", thread), ("threading", threading)]
def _green_time_modules():
from eventlet.green import time
- return [('time', time)]
+
+ return [("time", time)]
def _green_MySQLdb():
try:
from eventlet.green import MySQLdb
- return [('MySQLdb', MySQLdb)]
+
+ return [("MySQLdb", MySQLdb)]
except ImportError:
return []
@@ -582,7 +718,8 @@
def _green_builtins():
try:
from eventlet.green import builtin
- return [('builtins', builtin)]
+
+ return [("builtins", builtin)]
except ImportError:
return []
@@ -597,16 +734,18 @@
"""
if srckeys is None:
srckeys = source.__all__
- destination.update({
- name: getattr(source, name)
- for name in srckeys
- if not (name.startswith('__') or name in ignore)
- })
+ destination.update(
+ {
+ name: getattr(source, name)
+ for name in srckeys
+ if not (name.startswith("__") or name in ignore)
+ }
+ )
if __name__ == "__main__":
sys.argv.pop(0)
monkey_patch()
with open(sys.argv[0]) as f:
- code = compile(f.read(), sys.argv[0], 'exec')
+ code = compile(f.read(), sys.argv[0], "exec")
exec(code)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/eventlet-0.38.0/tests/asyncio_test.py
new/eventlet-0.38.2/tests/asyncio_test.py
--- old/eventlet-0.38.0/tests/asyncio_test.py 2020-02-02 01:00:00.000000000
+0100
+++ new/eventlet-0.38.2/tests/asyncio_test.py 2020-02-02 01:00:00.000000000
+0100
@@ -1,17 +1,20 @@
"""Tests for asyncio integration."""
+import pytest
+
+import eventlet
+from eventlet.hubs import get_hub
+from eventlet.hubs.asyncio import Hub as AsyncioHub
+if not isinstance(get_hub(), AsyncioHub):
+ pytest.skip("Only works on asyncio hub", allow_module_level=True)
+
import asyncio
from time import time
import socket
import sys
-import pytest
-
from greenlet import GreenletExit
-import eventlet
-from eventlet.hubs import get_hub
-from eventlet.hubs.asyncio import Hub as AsyncioHub
from eventlet.asyncio import spawn_for_awaitable
from eventlet.greenthread import getcurrent
from eventlet.support import greendns
@@ -19,9 +22,6 @@
import tests
-if not isinstance(get_hub(), AsyncioHub):
- pytest.skip("Only works on asyncio hub", allow_module_level=True)
-
class CallingAsyncFunctionsFromGreenletsHighLevelTests(_TestBase):
"""
@@ -298,9 +298,16 @@
tests.run_isolated("asyncio_to_thread.py")
-def test_asyncio_does_not_use_greendns(monkeypatch):
+def test_asyncio_does_not_use_greendns():
"""
``asyncio`` loops' ``getaddrinfo()`` and ``getnameinfo()`` do not use green
DNS.
"""
tests.run_isolated("asyncio_dns.py")
+
+
+def test_make_sure_monkey_patching_asyncio_is_restricted():
+ """
+ ``asyncio`` continues to have original, unpatched ``socket`` etc classes.
+ """
+ tests.run_isolated("asyncio_correct_patching.py")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/eventlet-0.38.0/tests/hub_test.py
new/eventlet-0.38.2/tests/hub_test.py
--- old/eventlet-0.38.0/tests/hub_test.py 2020-02-02 01:00:00.000000000
+0100
+++ new/eventlet-0.38.2/tests/hub_test.py 2020-02-02 01:00:00.000000000
+0100
@@ -326,10 +326,12 @@
once()
[email protected](sys.platform == "darwin", reason="on macOS using fork() is
discouraged")
def test_fork():
tests.run_isolated('hub_fork.py')
[email protected](sys.platform == "darwin", reason="on macOS using fork() is
discouraged")
def test_fork_simple():
tests.run_isolated('hub_fork_simple.py')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/eventlet-0.38.0/tests/isolated/asyncio_correct_patching.py
new/eventlet-0.38.2/tests/isolated/asyncio_correct_patching.py
--- old/eventlet-0.38.0/tests/isolated/asyncio_correct_patching.py
1970-01-01 01:00:00.000000000 +0100
+++ new/eventlet-0.38.2/tests/isolated/asyncio_correct_patching.py
2020-02-02 01:00:00.000000000 +0100
@@ -0,0 +1,32 @@
+"""
+asyncio submodules continue to have the original, real socket module even after
+monkeypatching.
+"""
+
+import sys
+
+
+def assert_correct_patching():
+ from eventlet.greenio.base import GreenSocket
+ import asyncio.selector_events
+ if asyncio.selector_events.socket.socket is GreenSocket:
+ raise RuntimeError("Wrong socket class, should've been normal
socket.socket")
+
+ import asyncio.selector_events
+ if asyncio.selector_events.selectors is not
sys.modules["__original_module_selectors"]:
+ raise RuntimeError("Wrong selectors")
+
+ if asyncio.selector_events.selectors.select is not
sys.modules["__original_module_select"]:
+ raise RuntimeError("Wrong select")
+
+
+import eventlet.hubs
+eventlet.hubs.get_hub()
+assert_correct_patching()
+
+import eventlet
+eventlet.monkey_patch()
+assert_correct_patching()
+
+
+print("pass")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/eventlet-0.38.0/tests/isolated/asyncio_dns.py
new/eventlet-0.38.2/tests/isolated/asyncio_dns.py
--- old/eventlet-0.38.0/tests/isolated/asyncio_dns.py 2020-02-02
01:00:00.000000000 +0100
+++ new/eventlet-0.38.2/tests/isolated/asyncio_dns.py 2020-02-02
01:00:00.000000000 +0100
@@ -1,3 +1,6 @@
+import eventlet
+eventlet.monkey_patch()
+
import asyncio
import socket
@@ -12,10 +15,6 @@
greendns.resolve = fail
greendns.resolver.query = fail
-import eventlet
-
-eventlet.monkey_patch()
-
async def lookups():
loop = asyncio.get_running_loop()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/eventlet-0.38.0/tests/isolated/asyncio_to_thread.py
new/eventlet-0.38.2/tests/isolated/asyncio_to_thread.py
--- old/eventlet-0.38.0/tests/isolated/asyncio_to_thread.py 2020-02-02
01:00:00.000000000 +0100
+++ new/eventlet-0.38.2/tests/isolated/asyncio_to_thread.py 2020-02-02
01:00:00.000000000 +0100
@@ -1,8 +1,9 @@
import eventlet
+eventlet.monkey_patch()
+
from eventlet.patcher import original
from eventlet.asyncio import spawn_for_awaitable
-eventlet.monkey_patch()
import asyncio
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/eventlet-0.38.0/tests/isolated/osthreads.py
new/eventlet-0.38.2/tests/isolated/osthreads.py
--- old/eventlet-0.38.0/tests/isolated/osthreads.py 1970-01-01
01:00:00.000000000 +0100
+++ new/eventlet-0.38.2/tests/isolated/osthreads.py 2020-02-02
01:00:00.000000000 +0100
@@ -0,0 +1,25 @@
+import eventlet
+import eventlet.patcher
+
+eventlet.monkey_patch()
+
+threading_orig = eventlet.patcher.original("threading")
+
+EVENTS = []
+
+
+def os_thread_2():
+ eventlet.sleep(0.1)
+ EVENTS.append(2)
+ eventlet.sleep(0.1)
+ EVENTS.append(2)
+
+
+threading_orig.Thread(target=os_thread_2).start()
+EVENTS.append(1)
+eventlet.sleep(0.05)
+EVENTS.append(1)
+eventlet.sleep(0.4)
+EVENTS.append(3)
+if EVENTS == [1, 1, 2, 2, 3]:
+ print("pass")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/eventlet-0.38.0/tests/isolated/patcher_existing_locks_preexisting.py
new/eventlet-0.38.2/tests/isolated/patcher_existing_locks_preexisting.py
--- old/eventlet-0.38.0/tests/isolated/patcher_existing_locks_preexisting.py
2020-02-02 01:00:00.000000000 +0100
+++ new/eventlet-0.38.2/tests/isolated/patcher_existing_locks_preexisting.py
2020-02-02 01:00:00.000000000 +0100
@@ -31,6 +31,10 @@
if sys.version_info[:2] > (3, 9):
print(unittest.mock.NonCallableMock._lock)
print(NS.lock)
+ # unittest.mock imports asyncio, so clear out asyncio.
+ for name in list(sys.modules.keys()):
+ if name.startswith("asyncio"):
+ del sys.modules[name]
eventlet.monkey_patch()
ensure_upgraded(NS.lock)
ensure_upgraded(NS.NS2.lock)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/eventlet-0.38.0/tests/isolated/patcher_threading_subclass_done.py
new/eventlet-0.38.2/tests/isolated/patcher_threading_subclass_done.py
--- old/eventlet-0.38.0/tests/isolated/patcher_threading_subclass_done.py
1970-01-01 01:00:00.000000000 +0100
+++ new/eventlet-0.38.2/tests/isolated/patcher_threading_subclass_done.py
2020-02-02 01:00:00.000000000 +0100
@@ -0,0 +1,40 @@
+import queue
+import threading
+
+
+class Worker(threading.Thread):
+ EXIT_SENTINEL = object()
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.q = queue.Queue(maxsize=-1)
+ self.daemon = True
+
+ def run(self):
+ while True:
+ task = self.q.get()
+ if task == self.EXIT_SENTINEL:
+ break
+ print(f"Treating task {task}")
+ # Pretend to work
+
+ def submit(self, job):
+ self.q.put(job)
+
+ def terminate(self):
+ self.q.put(self.EXIT_SENTINEL)
+ self.join()
+
+
+if __name__ == "__main__":
+ import eventlet
+ eventlet.patcher.monkey_patch()
+
+ worker = Worker()
+ assert not worker.is_alive()
+ worker.start()
+ assert worker.is_alive()
+ worker.submit(1)
+ worker.terminate()
+ assert not worker.is_alive()
+ print("pass")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/eventlet-0.38.0/tests/patcher_test.py
new/eventlet-0.38.2/tests/patcher_test.py
--- old/eventlet-0.38.0/tests/patcher_test.py 2020-02-02 01:00:00.000000000
+0100
+++ new/eventlet-0.38.2/tests/patcher_test.py 2020-02-02 01:00:00.000000000
+0100
@@ -536,3 +536,7 @@
def test_patcher_existing_locks_exception():
tests.run_isolated("patcher_existing_locks_exception.py")
+
+
+def test_patcher_threading_subclass_done():
+ tests.run_isolated("patcher_threading_subclass_done.py")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/eventlet-0.38.0/tests/thread_test.py
new/eventlet-0.38.2/tests/thread_test.py
--- old/eventlet-0.38.0/tests/thread_test.py 2020-02-02 01:00:00.000000000
+0100
+++ new/eventlet-0.38.2/tests/thread_test.py 2020-02-02 01:00:00.000000000
+0100
@@ -8,7 +8,7 @@
from eventlet import patcher
from eventlet.green import thread
-from tests import LimitedTestCase
+from tests import LimitedTestCase, run_isolated
class Locals(LimitedTestCase):
@@ -122,3 +122,7 @@
lk._at_fork_reinit()
assert lk.acquire(blocking=False)
assert not lk.acquire(blocking=False)
+
+
+def test_can_use_eventlet_in_os_threads():
+ run_isolated("osthreads.py")