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 
<adrian.glaub...@suse.com>
+
+- 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()
 
 
+@pytest.mark.skipif(sys.platform == "darwin", reason="on macOS using fork() is 
discouraged")
 def test_fork():
     tests.run_isolated('hub_fork.py')
 
 
+@pytest.mark.skipif(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")

Reply via email to