Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-asgiref for openSUSE:Factory 
checked in at 2022-06-06 11:10:34
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-asgiref (Old)
 and      /work/SRC/openSUSE:Factory/.python-asgiref.new.1548 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-asgiref"

Mon Jun  6 11:10:34 2022 rev:7 rq:980784 version:3.5.2

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-asgiref/python-asgiref.changes    
2021-09-04 22:32:11.719900300 +0200
+++ /work/SRC/openSUSE:Factory/.python-asgiref.new.1548/python-asgiref.changes  
2022-06-06 11:10:43.195324766 +0200
@@ -1,0 +2,20 @@
+Sat Jun  4 15:28:14 UTC 2022 - Dirk M??ller <[email protected]>
+
+- update to 3.5.2:
+  * Fix async-to-async typo
+  * Add tests for sync_to_async
+  * Improved docs - Starlette supports WebSockets
+  * Use get_event_loop in class-level code
+  * Changed how StatelessServer handles event loops
+  * Fixed pytest_asyncio deprecation warning. 
+  * Drop python 3.6, add python 3.10
+  * Fix allowed values for spec_version
+  * Rewrote multiprocessing test to use no local functions 
+  * Fixed a typographical error
+  * Remove SOCK_NONBLOCK from socket creation on tests 
+  * Preserve CurrentThreadExecutor across create_task
+  * Don't warn 'non-async-marked callable' for async callable instance
+  * Disallow async callable class instances as callable
+  * Fix root_path in WebSocket Connection Scope
+
+-------------------------------------------------------------------

Old:
----
  asgiref-3.4.1.tar.gz

New:
----
  asgiref-3.5.2.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-asgiref.spec ++++++
--- /var/tmp/diff_new_pack.hwqtOf/_old  2022-06-06 11:10:43.775325605 +0200
+++ /var/tmp/diff_new_pack.hwqtOf/_new  2022-06-06 11:10:43.783325617 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-asgiref
 #
-# Copyright (c) 2021 SUSE LLC
+# Copyright (c) 2022 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -19,7 +19,7 @@
 %define skip_python2 1
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-asgiref
-Version:        3.4.1
+Version:        3.5.2
 Release:        0
 Summary:        ASGI specs, helper code, and adapters
 License:        BSD-3-Clause

++++++ asgiref-3.4.1.tar.gz -> asgiref-3.5.2.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/asgiref-3.4.1/PKG-INFO new/asgiref-3.5.2/PKG-INFO
--- old/asgiref-3.4.1/PKG-INFO  2021-07-01 18:17:25.970000000 +0200
+++ new/asgiref-3.5.2/PKG-INFO  2022-05-16 22:39:02.462978600 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: asgiref
-Version: 3.4.1
+Version: 3.5.2
 Summary: ASGI specs, helper code, and adapters
 Home-page: https://github.com/django/asgiref/
 Author: Django Software Foundation
@@ -9,7 +9,6 @@
 Project-URL: Documentation, https://asgi.readthedocs.io/
 Project-URL: Further Documentation, 
https://docs.djangoproject.com/en/stable/topics/async/#async-adapter-functions
 Project-URL: Changelog, 
https://github.com/django/asgiref/blob/master/CHANGELOG.txt
-Platform: UNKNOWN
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Environment :: Web Environment
 Classifier: Intended Audience :: Developers
@@ -18,12 +17,12 @@
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 3
 Classifier: Programming Language :: Python :: 3 :: Only
-Classifier: Programming Language :: Python :: 3.6
 Classifier: Programming Language :: Python :: 3.7
 Classifier: Programming Language :: Python :: 3.8
 Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
 Classifier: Topic :: Internet :: WWW/HTTP
-Requires-Python: >=3.6
+Requires-Python: >=3.7
 Provides-Extra: tests
 License-File: LICENSE
 
@@ -125,7 +124,7 @@
 Dependencies
 ------------
 
-``asgiref`` requires Python 3.6 or higher.
+``asgiref`` requires Python 3.7 or higher.
 
 
 Contributing
@@ -240,5 +239,3 @@
 
 This repository is part of the Channels project. For the shepherd and 
maintenance team, please see the
 `main Channels readme 
<https://github.com/django/channels/blob/master/README.rst>`_.
-
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/asgiref-3.4.1/README.rst new/asgiref-3.5.2/README.rst
--- old/asgiref-3.4.1/README.rst        2021-06-27 22:11:39.000000000 +0200
+++ new/asgiref-3.5.2/README.rst        2022-01-22 17:43:01.000000000 +0100
@@ -96,7 +96,7 @@
 Dependencies
 ------------
 
-``asgiref`` requires Python 3.6 or higher.
+``asgiref`` requires Python 3.7 or higher.
 
 
 Contributing
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/asgiref-3.4.1/asgiref/__init__.py 
new/asgiref-3.5.2/asgiref/__init__.py
--- old/asgiref-3.4.1/asgiref/__init__.py       2021-07-01 18:16:27.000000000 
+0200
+++ new/asgiref-3.5.2/asgiref/__init__.py       2022-05-16 22:29:01.000000000 
+0200
@@ -1 +1 @@
-__version__ = "3.4.1"
+__version__ = "3.5.2"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/asgiref-3.4.1/asgiref/_pep562.py 
new/asgiref-3.5.2/asgiref/_pep562.py
--- old/asgiref-3.4.1/asgiref/_pep562.py        2021-06-27 22:22:20.000000000 
+0200
+++ new/asgiref-3.5.2/asgiref/_pep562.py        1970-01-01 01:00:00.000000000 
+0100
@@ -1,61 +0,0 @@
-"""
-Backport of PEP 562.
-https://pypi.org/search/?q=pep562
-Licensed under MIT
-Copyright (c) 2018 Isaac Muse <[email protected]>
-Permission is hereby granted, free of charge, to any person obtaining a copy 
of this software and associated
-documentation files (the "Software"), to deal in the Software without 
restriction, including without limitation
-the rights to use, copy, modify, merge, publish, distribute, sublicense, 
and/or sell copies of the Software,
-and to permit persons to whom the Software is furnished to do so, subject to 
the following conditions:
-The above copyright notice and this permission notice shall be included in all 
copies or substantial portions
-of the Software.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
IMPLIED, INCLUDING BUT NOT LIMITED
-TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
NONINFRINGEMENT. IN NO EVENT SHALL
-THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
LIABILITY, WHETHER IN AN ACTION OF
-CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
SOFTWARE OR THE USE OR OTHER DEALINGS
-IN THE SOFTWARE.
-"""
-import sys
-from typing import Any, Callable, List, Optional
-
-
-class Pep562:
-    """
-    Backport of PEP 562 <https://pypi.org/search/?q=pep562>.
-    Wraps the module in a class that exposes the mechanics to override 
`__dir__` and `__getattr__`.
-    The given module will be searched for overrides of `__dir__` and 
`__getattr__` and use them when needed.
-    """
-
-    def __init__(self, name: str) -> None:
-        """Acquire `__getattr__` and `__dir__`, but only replace module for 
versions less than Python 3.7."""
-
-        self._module = sys.modules[name]
-        self._get_attr = getattr(self._module, "__getattr__", None)
-        self._get_dir: Optional[Callable[..., List[str]]] = getattr(
-            self._module, "__dir__", None
-        )
-        sys.modules[name] = self  # type: ignore[assignment]
-
-    def __dir__(self) -> List[str]:
-        """Return the overridden `dir` if one was provided, else apply `dir` 
to the module."""
-
-        return self._get_dir() if self._get_dir else dir(self._module)
-
-    def __getattr__(self, name: str) -> Any:
-        """
-        Attempt to retrieve the attribute from the module, and if missing, use 
the overridden function if present.
-        """
-
-        try:
-            return getattr(self._module, name)
-        except AttributeError:
-            if self._get_attr:
-                return self._get_attr(name)
-            raise
-
-
-def pep562(module_name: str) -> None:
-    """Helper function to apply PEP 562."""
-
-    if sys.version_info < (3, 7):
-        Pep562(module_name)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/asgiref-3.4.1/asgiref/compatibility.py 
new/asgiref-3.5.2/asgiref/compatibility.py
--- old/asgiref-3.4.1/asgiref/compatibility.py  2021-06-27 22:11:39.000000000 
+0200
+++ new/asgiref-3.5.2/asgiref/compatibility.py  2022-01-22 17:43:01.000000000 
+0100
@@ -1,6 +1,5 @@
 import asyncio
 import inspect
-import sys
 
 
 def is_double_callable(application):
@@ -46,16 +45,3 @@
     if is_double_callable(application):
         application = double_to_single_callable(application)
     return application
-
-
-if sys.version_info >= (3, 7):
-    # these were introduced in 3.7
-    get_running_loop = asyncio.get_running_loop
-    run_future = asyncio.run
-    create_task = asyncio.create_task
-else:
-    # marked as deprecated in 3.10, did not exist before 3.7
-    get_running_loop = asyncio.get_event_loop
-    run_future = asyncio.ensure_future
-    # does nothing, this is fine for <3.7
-    create_task = lambda task: task
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/asgiref-3.4.1/asgiref/local.py 
new/asgiref-3.5.2/asgiref/local.py
--- old/asgiref-3.4.1/asgiref/local.py  2021-04-05 18:48:44.000000000 +0200
+++ new/asgiref-3.5.2/asgiref/local.py  2021-08-18 16:20:12.000000000 +0200
@@ -30,8 +30,6 @@
     3.7 only, we can then reimplement the storage more nicely.
     """
 
-    CLEANUP_INTERVAL = 60  # seconds
-
     def __init__(self, thread_critical: bool = False) -> None:
         self._thread_critical = thread_critical
         self._thread_lock = threading.RLock()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/asgiref-3.4.1/asgiref/server.py 
new/asgiref-3.5.2/asgiref/server.py
--- old/asgiref-3.4.1/asgiref/server.py 2021-06-27 22:11:39.000000000 +0200
+++ new/asgiref-3.5.2/asgiref/server.py 2021-11-22 18:42:08.000000000 +0100
@@ -3,7 +3,7 @@
 import time
 import traceback
 
-from .compatibility import get_running_loop, guarantee_single_callable, 
run_future
+from .compatibility import guarantee_single_callable
 
 logger = logging.getLogger(__name__)
 
@@ -56,7 +56,7 @@
         """
         Runs the asyncio event loop with our handler loop.
         """
-        event_loop = get_running_loop()
+        event_loop = asyncio.get_event_loop()
         asyncio.ensure_future(self.application_checker())
         try:
             event_loop.run_until_complete(self.handle())
@@ -88,7 +88,7 @@
         input_queue = asyncio.Queue()
         application_instance = guarantee_single_callable(self.application)
         # Run it, and stash the future for later checking
-        future = run_future(
+        future = asyncio.ensure_future(
             application_instance(
                 scope=scope,
                 receive=input_queue.get,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/asgiref-3.4.1/asgiref/sync.py 
new/asgiref-3.5.2/asgiref/sync.py
--- old/asgiref-3.4.1/asgiref/sync.py   2021-06-30 21:02:12.000000000 +0200
+++ new/asgiref-3.5.2/asgiref/sync.py   2022-05-16 22:28:11.000000000 +0200
@@ -1,4 +1,6 @@
+import asyncio
 import asyncio.coroutines
+import contextvars
 import functools
 import inspect
 import os
@@ -9,15 +11,9 @@
 from concurrent.futures import Future, ThreadPoolExecutor
 from typing import Any, Callable, Dict, Optional, overload
 
-from .compatibility import get_running_loop
 from .current_thread_executor import CurrentThreadExecutor
 from .local import Local
 
-if sys.version_info >= (3, 7):
-    import contextvars
-else:
-    contextvars = None
-
 
 def _restore_context(context):
     # Check for changes in contextvars, and set them to the current
@@ -55,8 +51,6 @@
     In Python 3.7+, the ThreadSensitiveContext() context manager may be used to
     specify a thread pool per context.
 
-    In Python 3.6, usage of this context manager has no effect.
-
     This context manager is re-entrant, so only the outer-most call to
     ThreadSensitiveContext will set the context.
 
@@ -70,32 +64,22 @@
     def __init__(self):
         self.token = None
 
-    if contextvars:
-
-        async def __aenter__(self):
-            try:
-                SyncToAsync.thread_sensitive_context.get()
-            except LookupError:
-                self.token = SyncToAsync.thread_sensitive_context.set(self)
-
-            return self
-
-        async def __aexit__(self, exc, value, tb):
-            if not self.token:
-                return
-
-            executor = SyncToAsync.context_to_thread_executor.pop(self, None)
-            if executor:
-                executor.shutdown()
-            SyncToAsync.thread_sensitive_context.reset(self.token)
-
-    else:
+    async def __aenter__(self):
+        try:
+            SyncToAsync.thread_sensitive_context.get()
+        except LookupError:
+            self.token = SyncToAsync.thread_sensitive_context.set(self)
 
-        async def __aenter__(self):
-            return self
+        return self
 
-        async def __aexit__(self, exc, value, tb):
-            pass
+    async def __aexit__(self, exc, value, tb):
+        if not self.token:
+            return
+
+        executor = SyncToAsync.context_to_thread_executor.pop(self, None)
+        if executor:
+            executor.shutdown()
+        SyncToAsync.thread_sensitive_context.reset(self.token)
 
 
 class AsyncToSync:
@@ -118,11 +102,22 @@
     # Local, not a threadlocal, so that tasks can work out what their parent 
used.
     executors = Local()
 
+    # When we can't find a CurrentThreadExecutor from the context, such as
+    # inside create_task, we'll look it up here from the running event loop.
+    loop_thread_executors: "Dict[asyncio.AbstractEventLoop, 
CurrentThreadExecutor]" = {}
+
     def __init__(self, awaitable, force_new_loop=False):
-        if not callable(awaitable) or not 
_iscoroutinefunction_or_partial(awaitable):
+        if not callable(awaitable) or (
+            not _iscoroutinefunction_or_partial(awaitable)
+            and not _iscoroutinefunction_or_partial(
+                getattr(awaitable, "__call__", awaitable)
+            )
+        ):
             # Python does not have very reliable detection of async functions
             # (lots of false negatives) so this is just a warning.
-            warnings.warn("async_to_sync was passed a non-async-marked 
callable")
+            warnings.warn(
+                "async_to_sync was passed a non-async-marked callable", 
stacklevel=2
+            )
         self.awaitable = awaitable
         try:
             self.__self__ = self.awaitable.__self__
@@ -133,7 +128,7 @@
             self.main_event_loop = None
         else:
             try:
-                self.main_event_loop = get_running_loop()
+                self.main_event_loop = asyncio.get_running_loop()
             except RuntimeError:
                 # There's no event loop in this thread. Look for the 
threadlocal if
                 # we're inside SyncToAsync
@@ -152,7 +147,7 @@
     def __call__(self, *args, **kwargs):
         # You can't call AsyncToSync from a thread with a running event loop
         try:
-            event_loop = get_running_loop()
+            event_loop = asyncio.get_running_loop()
         except RuntimeError:
             pass
         else:
@@ -162,12 +157,9 @@
                     "just await the async function directly."
                 )
 
-        if contextvars is not None:
-            # Wrapping context in list so it can be reassigned from within
-            # `main_wrap`.
-            context = [contextvars.copy_context()]
-        else:
-            context = None
+        # Wrapping context in list so it can be reassigned from within
+        # `main_wrap`.
+        context = [contextvars.copy_context()]
 
         # Make a future for the return information
         call_result = Future()
@@ -182,6 +174,7 @@
             old_current_executor = None
         current_executor = CurrentThreadExecutor()
         self.executors.current = current_executor
+        loop = None
         # Use call_soon_threadsafe to schedule a synchronous callback on the
         # main event loop's thread if it's there, otherwise make a new loop
         # in this thread.
@@ -193,6 +186,7 @@
             if not (self.main_event_loop and 
self.main_event_loop.is_running()):
                 # Make our own event loop - in a new thread - and run inside 
that.
                 loop = asyncio.new_event_loop()
+                self.loop_thread_executors[loop] = current_executor
                 loop_executor = ThreadPoolExecutor(max_workers=1)
                 loop_future = loop_executor.submit(
                     self._run_event_loop, loop, awaitable
@@ -212,12 +206,13 @@
                     current_executor.run_until_future(call_result)
         finally:
             # Clean up any executor we were running
+            if loop is not None:
+                del self.loop_thread_executors[loop]
             if hasattr(self.executors, "current"):
                 del self.executors.current
             if old_current_executor:
                 self.executors.current = old_current_executor
-            if contextvars is not None:
-                _restore_context(context[0])
+            _restore_context(context[0])
 
         # Wait for results from the future.
         return call_result.result()
@@ -233,10 +228,7 @@
             try:
                 # mimic asyncio.run() behavior
                 # cancel unexhausted async generators
-                if sys.version_info >= (3, 7, 0):
-                    tasks = asyncio.all_tasks(loop)
-                else:
-                    tasks = asyncio.Task.all_tasks(loop)
+                tasks = asyncio.all_tasks(loop)
                 for task in tasks:
                     task.cancel()
 
@@ -297,8 +289,7 @@
         finally:
             del self.launch_map[current_task]
 
-            if context is not None:
-                context[0] = contextvars.copy_context()
+            context[0] = contextvars.copy_context()
 
 
 class SyncToAsync:
@@ -325,7 +316,9 @@
 
     # If they've set ASGI_THREADS, update the default asyncio executor for now
     if "ASGI_THREADS" in os.environ:
-        loop = get_running_loop()
+        # We use get_event_loop here - not get_running_loop - as this will
+        # be run at import time, and we want to update the main thread's loop.
+        loop = asyncio.get_event_loop()
         loop.set_default_executor(
             ThreadPoolExecutor(max_workers=int(os.environ["ASGI_THREADS"]))
         )
@@ -341,21 +334,15 @@
 
     # Maintain a contextvar for the current execution context. Optionally used
     # for thread sensitive mode.
-    if sys.version_info >= (3, 7):
-        thread_sensitive_context: "contextvars.ContextVar[str]" = (
-            contextvars.ContextVar("thread_sensitive_context")
-        )
-    else:
-        thread_sensitive_context: None = None
+    thread_sensitive_context: "contextvars.ContextVar[str]" = 
contextvars.ContextVar(
+        "thread_sensitive_context"
+    )
 
     # Contextvar that is used to detect if the single thread executor
     # would be awaited on while already being used in the same context
-    if sys.version_info >= (3, 7):
-        deadlock_context: "contextvars.ContextVar[bool]" = 
contextvars.ContextVar(
-            "deadlock_context"
-        )
-    else:
-        deadlock_context: None = None
+    deadlock_context: "contextvars.ContextVar[bool]" = contextvars.ContextVar(
+        "deadlock_context"
+    )
 
     # Maintaining a weak reference to the context ensures that thread pools are
     # erased once the context goes out of scope. This terminates the thread 
pool.
@@ -369,7 +356,11 @@
         thread_sensitive: bool = True,
         executor: Optional["ThreadPoolExecutor"] = None,
     ) -> None:
-        if not callable(func) or _iscoroutinefunction_or_partial(func):
+        if (
+            not callable(func)
+            or _iscoroutinefunction_or_partial(func)
+            or _iscoroutinefunction_or_partial(getattr(func, "__call__", func))
+        ):
             raise TypeError("sync_to_async can only be applied to sync 
functions.")
         self.func = func
         functools.update_wrapper(self, func)
@@ -384,7 +375,7 @@
             pass
 
     async def __call__(self, *args, **kwargs):
-        loop = get_running_loop()
+        loop = asyncio.get_running_loop()
 
         # Work out what thread to run the code in
         if self._thread_sensitive:
@@ -405,6 +396,9 @@
                     # Create new thread executor in current context
                     executor = ThreadPoolExecutor(max_workers=1)
                     self.context_to_thread_executor[thread_sensitive_context] 
= executor
+            elif loop in AsyncToSync.loop_thread_executors:
+                # Re-use thread executor for running loop
+                executor = AsyncToSync.loop_thread_executors[loop]
             elif self.deadlock_context and self.deadlock_context.get(False):
                 raise RuntimeError(
                     "Single thread executor already being used, would deadlock"
@@ -418,14 +412,11 @@
             # Use the passed in executor, or the loop's default if it is None
             executor = self._executor
 
-        if contextvars is not None:
-            context = contextvars.copy_context()
-            child = functools.partial(self.func, *args, **kwargs)
-            func = context.run
-            args = (child,)
-            kwargs = {}
-        else:
-            func = self.func
+        context = contextvars.copy_context()
+        child = functools.partial(self.func, *args, **kwargs)
+        func = context.run
+        args = (child,)
+        kwargs = {}
 
         try:
             # Run the code in the right thread
@@ -444,8 +435,7 @@
             ret = await asyncio.wait_for(future, timeout=None)
 
         finally:
-            if contextvars is not None:
-                _restore_context(context)
+            _restore_context(context)
             if self.deadlock_context:
                 self.deadlock_context.set(False)
 
@@ -493,17 +483,11 @@
     @staticmethod
     def get_current_task():
         """
-        Cross-version implementation of asyncio.current_task()
-
-        Returns None if there is no task.
+        Implementation of asyncio.current_task()
+        that returns None if there is no task.
         """
         try:
-            if hasattr(asyncio, "current_task"):
-                # Python 3.7 and up
-                return asyncio.current_task()
-            else:
-                # Python 3.6
-                return asyncio.Task.current_task()
+            return asyncio.current_task()
         except RuntimeError:
             return None
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/asgiref-3.4.1/asgiref/timeout.py 
new/asgiref-3.5.2/asgiref/timeout.py
--- old/asgiref-3.4.1/asgiref/timeout.py        2021-04-05 18:48:44.000000000 
+0200
+++ new/asgiref-3.5.2/asgiref/timeout.py        2022-01-22 17:43:01.000000000 
+0100
@@ -7,7 +7,6 @@
 
 
 import asyncio
-import sys
 from types import TracebackType
 from typing import Any, Optional, Type
 
@@ -82,7 +81,7 @@
         if self._timeout is None:
             return self
 
-        self._task = current_task(self._loop)
+        self._task = asyncio.current_task(self._loop)
         if self._task is None:
             raise RuntimeError(
                 "Timeout context manager should be used " "inside a task"
@@ -111,17 +110,3 @@
         if self._task is not None:
             self._task.cancel()
             self._cancelled = True
-
-
-def current_task(loop: asyncio.AbstractEventLoop) -> 
"Optional[asyncio.Task[Any]]":
-    if sys.version_info >= (3, 7):
-        task = asyncio.current_task(loop=loop)
-    else:
-        task = asyncio.Task.current_task(loop=loop)
-    if task is None:
-        # this should be removed, tokio must use register_task and family API
-        fn = getattr(loop, "current_task", None)
-        if fn is not None:
-            task = fn()
-
-    return task
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/asgiref-3.4.1/asgiref/typing.py 
new/asgiref-3.5.2/asgiref/typing.py
--- old/asgiref-3.4.1/asgiref/typing.py 2021-06-27 22:24:56.000000000 +0200
+++ new/asgiref-3.5.2/asgiref/typing.py 2022-01-22 17:53:53.000000000 +0100
@@ -1,19 +1,5 @@
 import sys
-import warnings
-from typing import (
-    Any,
-    Awaitable,
-    Callable,
-    Dict,
-    Iterable,
-    List,
-    Optional,
-    Tuple,
-    Type,
-    Union,
-)
-
-from asgiref._pep562 import pep562
+from typing import Awaitable, Callable, Dict, Iterable, Optional, Tuple, Type, 
Union
 
 if sys.version_info >= (3, 8):
     from typing import Literal, Protocol, TypedDict
@@ -254,34 +240,3 @@
     Awaitable[None],
 ]
 ASGIApplication = Union[ASGI2Application, ASGI3Application]
-
-__deprecated__ = {
-    "WebsocketConnectEvent": WebSocketConnectEvent,
-    "WebsocketAcceptEvent": WebSocketAcceptEvent,
-    "WebsocketReceiveEvent": WebSocketReceiveEvent,
-    "WebsocketSendEvent": WebSocketSendEvent,
-    "WebsocketResponseStartEvent": WebSocketResponseStartEvent,
-    "WebsocketResponseBodyEvent": WebSocketResponseBodyEvent,
-    "WebsocketDisconnectEvent": WebSocketDisconnectEvent,
-    "WebsocketCloseEvent": WebSocketCloseEvent,
-}
-
-
-def __getattr__(name: str) -> Any:
-    deprecated = __deprecated__.get(name)
-    if deprecated:
-        stacklevel = 3 if sys.version_info >= (3, 7) else 4
-        warnings.warn(
-            f"'{name}' is deprecated. Use '{deprecated.__name__}' instead.",
-            category=DeprecationWarning,
-            stacklevel=stacklevel,
-        )
-        return deprecated
-    raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
-
-
-def __dir__() -> List[str]:
-    return sorted(list(__all__) + list(__deprecated__.keys()))
-
-
-pep562(__name__)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/asgiref-3.4.1/asgiref.egg-info/PKG-INFO 
new/asgiref-3.5.2/asgiref.egg-info/PKG-INFO
--- old/asgiref-3.4.1/asgiref.egg-info/PKG-INFO 2021-07-01 18:17:25.000000000 
+0200
+++ new/asgiref-3.5.2/asgiref.egg-info/PKG-INFO 2022-05-16 22:39:02.000000000 
+0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: asgiref
-Version: 3.4.1
+Version: 3.5.2
 Summary: ASGI specs, helper code, and adapters
 Home-page: https://github.com/django/asgiref/
 Author: Django Software Foundation
@@ -9,7 +9,6 @@
 Project-URL: Documentation, https://asgi.readthedocs.io/
 Project-URL: Further Documentation, 
https://docs.djangoproject.com/en/stable/topics/async/#async-adapter-functions
 Project-URL: Changelog, 
https://github.com/django/asgiref/blob/master/CHANGELOG.txt
-Platform: UNKNOWN
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Environment :: Web Environment
 Classifier: Intended Audience :: Developers
@@ -18,12 +17,12 @@
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 3
 Classifier: Programming Language :: Python :: 3 :: Only
-Classifier: Programming Language :: Python :: 3.6
 Classifier: Programming Language :: Python :: 3.7
 Classifier: Programming Language :: Python :: 3.8
 Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
 Classifier: Topic :: Internet :: WWW/HTTP
-Requires-Python: >=3.6
+Requires-Python: >=3.7
 Provides-Extra: tests
 License-File: LICENSE
 
@@ -125,7 +124,7 @@
 Dependencies
 ------------
 
-``asgiref`` requires Python 3.6 or higher.
+``asgiref`` requires Python 3.7 or higher.
 
 
 Contributing
@@ -240,5 +239,3 @@
 
 This repository is part of the Channels project. For the shepherd and 
maintenance team, please see the
 `main Channels readme 
<https://github.com/django/channels/blob/master/README.rst>`_.
-
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/asgiref-3.4.1/asgiref.egg-info/SOURCES.txt 
new/asgiref-3.5.2/asgiref.egg-info/SOURCES.txt
--- old/asgiref-3.4.1/asgiref.egg-info/SOURCES.txt      2021-07-01 
18:17:25.000000000 +0200
+++ new/asgiref-3.5.2/asgiref.egg-info/SOURCES.txt      2022-05-16 
22:39:02.000000000 +0200
@@ -4,7 +4,6 @@
 setup.cfg
 setup.py
 asgiref/__init__.py
-asgiref/_pep562.py
 asgiref/compatibility.py
 asgiref/current_thread_executor.py
 asgiref/local.py
@@ -22,7 +21,6 @@
 asgiref.egg-info/requires.txt
 asgiref.egg-info/top_level.txt
 tests/test_compatibility.py
-tests/test_deprecated_types.py
 tests/test_local.py
 tests/test_server.py
 tests/test_sync.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/asgiref-3.4.1/setup.cfg new/asgiref-3.5.2/setup.cfg
--- old/asgiref-3.4.1/setup.cfg 2021-07-01 18:17:25.980000000 +0200
+++ new/asgiref-3.5.2/setup.cfg 2022-05-16 22:39:02.462978600 +0200
@@ -16,10 +16,10 @@
        Programming Language :: Python
        Programming Language :: Python :: 3
        Programming Language :: Python :: 3 :: Only
-       Programming Language :: Python :: 3.6
        Programming Language :: Python :: 3.7
        Programming Language :: Python :: 3.8
        Programming Language :: Python :: 3.9
+       Programming Language :: Python :: 3.10
        Topic :: Internet :: WWW/HTTP
 project_urls = 
        Documentation = https://asgi.readthedocs.io/
@@ -27,7 +27,7 @@
        Changelog = https://github.com/django/asgiref/blob/master/CHANGELOG.txt
 
 [options]
-python_requires = >=3.6
+python_requires = >=3.7
 packages = find:
 include_package_data = true
 install_requires = 
@@ -42,6 +42,7 @@
 
 [tool:pytest]
 testpaths = tests
+asyncio_mode = strict
 
 [flake8]
 exclude = venv/*,tox/*,specs/*
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/asgiref-3.4.1/tests/test_deprecated_types.py 
new/asgiref-3.5.2/tests/test_deprecated_types.py
--- old/asgiref-3.4.1/tests/test_deprecated_types.py    2021-06-27 
22:22:20.000000000 +0200
+++ new/asgiref-3.5.2/tests/test_deprecated_types.py    1970-01-01 
01:00:00.000000000 +0100
@@ -1,20 +0,0 @@
-import importlib
-
-import pytest
-
-from asgiref import typing
-
-
[email protected]("deprecated_type", typing.__deprecated__.keys())
-def test_deprecated_types(deprecated_type: str) -> None:
-    with pytest.warns(DeprecationWarning) as record:
-        getattr(importlib.import_module("asgiref.typing"), deprecated_type)
-        assert len(record) == 1
-        assert deprecated_type in str(record.list[0])
-
-
[email protected]("available_type", typing.__all__)
-def test_available_types(available_type: str) -> None:
-    with pytest.warns(None) as record:
-        getattr(importlib.import_module("asgiref.typing"), available_type)
-        assert len(record) == 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/asgiref-3.4.1/tests/test_server.py 
new/asgiref-3.5.2/tests/test_server.py
--- old/asgiref-3.4.1/tests/test_server.py      2021-04-05 18:48:44.000000000 
+0200
+++ new/asgiref-3.5.2/tests/test_server.py      2022-05-16 22:28:11.000000000 
+0200
@@ -1,11 +1,153 @@
+import asyncio
+import socket as sock
+from functools import partial
+
+import pytest
+
 from asgiref.server import StatelessServer
 
 
-def test_stateless_server():
-    """StatelessServer can be instantiated with an ASGI 3 application."""
+async def sock_recvfrom(sock, n):
+    while True:
+        try:
+            return sock.recvfrom(n)
+        except BlockingIOError:
+            await asyncio.sleep(0)
+
+
+class Server(StatelessServer):
+    def __init__(self, application, max_applications=1000):
+        super().__init__(
+            application,
+            max_applications=max_applications,
+        )
+        self._sock = sock.socket(sock.AF_INET, sock.SOCK_DGRAM)
+        self._sock.setblocking(False)
+        self._sock.bind(("127.0.0.1", 0))
+
+    @property
+    def address(self):
+        return self._sock.getsockname()
+
+    async def handle(self):
+        while True:
+            data, addr = await sock_recvfrom(self._sock, 4096)
+            data = data.decode("utf-8")
+
+            if data.startswith("Register"):
+                _, usr_name = data.split(" ")
+                input_quene = 
self.get_or_create_application_instance(usr_name, addr)
+                input_quene.put_nowait(b"Welcome")
+
+            elif data.startswith("To"):
+                _, usr_name, msg = data.split(" ", 2)
+                input_quene = 
self.get_or_create_application_instance(usr_name, addr)
+                input_quene.put_nowait(msg.encode("utf-8"))
+
+    async def application_send(self, scope, message):
+        self._sock.sendto(message, scope)
 
+    def close(self):
+        self._sock.close()
+        for details in self.application_instances.values():
+            details["future"].cancel()
+
+
+class Client:
+    def __init__(self, name):
+        self._sock = sock.socket(sock.AF_INET, sock.SOCK_DGRAM)
+        self._sock.setblocking(False)
+        self.name = name
+
+    async def register(self, server_addr, name=None):
+        name = name or self.name
+        self._sock.sendto(f"Register {name}".encode("utf-8"), server_addr)
+
+    async def send(self, server_addr, to, msg):
+        self._sock.sendto(f"To {to} {msg}".encode("utf-8"), server_addr)
+
+    async def get_msg(self):
+        msg, server_addr = await sock_recvfrom(self._sock, 4096)
+        return msg, server_addr
+
+    def close(self):
+        self._sock.close()
+
+
[email protected](scope="function")
+def server():
     async def app(scope, receive, send):
-        pass
+        while True:
+            msg = await receive()
+            await send(msg)
+
+    server = Server(app, 10)
+    yield server
+    server.close()
+
+
+async def check_client_msg(client, expected_address, expected_msg):
+    msg, server_addr = await asyncio.wait_for(client.get_msg(), timeout=1.0)
+    assert msg == expected_msg
+    assert server_addr == expected_address
+
+
+async def server_auto_close(fut, timeout):
+    """Server run based on run_until_complete. It will block forever with 
handle
+    function because it is a while True loop without break.  Use this method 
to close
+    server automatically."""
+    loop = asyncio.get_running_loop()
+    task = asyncio.ensure_future(fut, loop=loop)
+    await asyncio.sleep(timeout)
+    task.cancel()
+
+
+def test_stateless_server(server):
+    """StatelessServer can be instantiated with an ASGI 3 application."""
+    """Create a UDP Server can register instance based on name from message of 
client.
+    Clients can communicate to other client by name through server"""
+
+    loop = asyncio.new_event_loop()
+    asyncio.set_event_loop(loop)
+    server.handle = partial(server_auto_close, fut=server.handle(), 
timeout=1.0)
+
+    client1 = Client(name="client1")
+    client2 = Client(name="client2")
+
+    async def check_client1_behavior():
+        await client1.register(server.address)
+        await check_client_msg(client1, server.address, b"Welcome")
+        await client1.send(server.address, "client2", "Hello")
+
+    async def check_client2_behavior():
+        await client2.register(server.address)
+        await check_client_msg(client2, server.address, b"Welcome")
+        await check_client_msg(client2, server.address, b"Hello")
+
+    task1 = loop.create_task(check_client1_behavior())
+    task2 = loop.create_task(check_client2_behavior())
+
+    server.run()
+
+    assert task1.done()
+    assert task2.done()
+
+
+def test_server_delete_instance(server):
+    """The max_applications of Server is 10. After 20 times register, 
application number should be 10."""
+    loop = asyncio.new_event_loop()
+    asyncio.set_event_loop(loop)
+    server.handle = partial(server_auto_close, fut=server.handle(), 
timeout=1.0)
+
+    client1 = Client(name="client1")
+
+    async def client1_multiple_register():
+        for i in range(20):
+            await client1.register(server.address, name=f"client{i}")
+            print(f"client{i}")
+            await check_client_msg(client1, server.address, b"Welcome")
+
+    task = loop.create_task(client1_multiple_register())
+    server.run()
 
-    server = StatelessServer(app)
-    server.get_or_create_application_instance("scope_id", {})
+    assert task.done()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/asgiref-3.4.1/tests/test_sync.py 
new/asgiref-3.5.2/tests/test_sync.py
--- old/asgiref-3.4.1/tests/test_sync.py        2021-06-30 21:01:55.000000000 
+0200
+++ new/asgiref-3.5.2/tests/test_sync.py        2022-05-16 22:28:11.000000000 
+0200
@@ -1,17 +1,17 @@
 import asyncio
 import functools
 import multiprocessing
-import sys
 import threading
 import time
+import warnings
 from concurrent.futures import ThreadPoolExecutor
 from functools import wraps
 from unittest import TestCase
 
 import pytest
 
-from asgiref.compatibility import create_task, get_running_loop
 from asgiref.sync import ThreadSensitiveContext, async_to_sync, sync_to_async
+from asgiref.timeout import timeout
 
 
 @pytest.mark.asyncio
@@ -35,15 +35,15 @@
     assert result == 42
     assert end - start >= 1
     # Set workers to 1, call it twice and make sure that works right
-    loop = get_running_loop()
+    loop = asyncio.get_running_loop()
     old_executor = loop._default_executor or ThreadPoolExecutor()
     loop.set_default_executor(ThreadPoolExecutor(max_workers=1))
     try:
         start = time.monotonic()
         await asyncio.wait(
             [
-                create_task(async_function()),
-                create_task(async_function()),
+                asyncio.create_task(async_function()),
+                asyncio.create_task(async_function()),
             ]
         )
         end = time.monotonic()
@@ -100,6 +100,18 @@
 
 
 @pytest.mark.asyncio
+async def test_sync_to_async_raises_typeerror_for_async_callable_instance():
+    class CallableClass:
+        async def __call__(self):
+            return None
+
+    with pytest.raises(
+        TypeError, match="sync_to_async can only be applied to sync functions."
+    ):
+        sync_to_async(CallableClass())
+
+
[email protected]
 async def test_sync_to_async_decorator():
     """
     Tests sync_to_async as a decorator
@@ -366,9 +378,32 @@
     assert result["worked"]
 
 
-def test_async_to_async_method_self_attribute():
+def test_async_to_sync_on_callable_object():
+    """
+    Tests async_to_sync on a callable class instance
+    """
+
+    result = {}
+
+    class CallableClass:
+        async def __call__(self, value):
+            await asyncio.sleep(0)
+            result["worked"] = True
+            return value
+
+    # Run it (without warnings)
+    with warnings.catch_warnings():
+        warnings.simplefilter("error")
+        sync_function = async_to_sync(CallableClass())
+        out = sync_function(42)
+
+    assert out == 42
+    assert result["worked"] is True
+
+
+def test_async_to_sync_method_self_attribute():
     """
-    Tests async_to_async on a method copies __self__.
+    Tests async_to_sync on a method copies __self__.
     """
     # Define async function.
     class TestClass:
@@ -398,15 +433,21 @@
     @async_to_sync
     async def middle():
         await inner()
+        await asyncio.create_task(inner_task())
 
-    # Inner sync function
+    # Inner sync functions
     @sync_to_async
     def inner():
         result["thread"] = threading.current_thread()
 
+    @sync_to_async
+    def inner_task():
+        result["thread2"] = threading.current_thread()
+
     # Run it
     middle()
     assert result["thread"] == threading.current_thread()
+    assert result["thread2"] == threading.current_thread()
 
 
 @pytest.mark.asyncio
@@ -435,7 +476,9 @@
         result["thread"] = threading.current_thread()
 
     # Run it (in supposed parallel!)
-    await asyncio.wait([create_task(outer(result_1)), 
create_task(inner(result_2))])
+    await asyncio.wait(
+        [asyncio.create_task(outer(result_1)), 
asyncio.create_task(inner(result_2))]
+    )
 
     # They should not have run in the main thread, but in the same thread
     assert result_1["thread"] != threading.current_thread()
@@ -457,8 +500,8 @@
             # Run it (in supposed parallel!)
             await asyncio.wait(
                 [
-                    create_task(store_thread_async(result_1)),
-                    create_task(store_thread_async(result_2)),
+                    asyncio.create_task(store_thread_async(result_1)),
+                    asyncio.create_task(store_thread_async(result_2)),
                 ]
             )
 
@@ -606,32 +649,33 @@
     assert asyncio.iscoroutinefunction(sync_to_async(sync_func))
 
 
+async def async_process(queue):
+    queue.put(42)
+
+
+def sync_process(queue):
+    """Runs async_process synchronously"""
+    async_to_sync(async_process)(queue)
+
+
+def fork_first():
+    """Forks process before running sync_process"""
+    queue = multiprocessing.Queue()
+    fork = multiprocessing.Process(target=sync_process, args=[queue])
+    fork.start()
+    fork.join(3)
+    # Force cleanup in failed test case
+    if fork.is_alive():
+        fork.terminate()
+    return queue.get(True, 1)
+
+
 @pytest.mark.asyncio
 async def test_multiprocessing():
     """
     Tests that a forked process can use async_to_sync without it looking for
     the event loop from the parent process.
     """
-
-    test_queue = multiprocessing.Queue()
-
-    async def async_process():
-        test_queue.put(42)
-
-    def sync_process():
-        """Runs async_process synchronously"""
-        async_to_sync(async_process)()
-
-    def fork_first():
-        """Forks process before running sync_process"""
-        fork = multiprocessing.Process(target=sync_process)
-        fork.start()
-        fork.join(3)
-        # Force cleanup in failed test case
-        if fork.is_alive():
-            fork.terminate()
-        return test_queue.get(True, 1)
-
     assert await sync_to_async(fork_first)() == 42
 
 
@@ -672,7 +716,6 @@
     )
 
 
[email protected](sys.version_info < (3, 7), reason="Issue persists with 
3.6")
 def test_sync_to_async_deadlock_raises():
     def db_write():
         pass
@@ -696,7 +739,6 @@
         asyncio.run(server_entry())
 
 
[email protected](sys.version_info < (3, 7), reason="Issue persists with 
3.6")
 def test_sync_to_async_deadlock_ignored_with_exception():
     """
     Ensures that throwing an exception from inside a deadlock-protected block
@@ -717,3 +759,75 @@
             pass
 
     asyncio.run(server_entry())
+
+
[email protected]
[email protected]
+async def test_sync_to_async_with_blocker_thread_sensitive():
+    """
+    Tests sync_to_async running on a long-time blocker in a thread_sensitive 
context.
+    Expected to fail at the moment.
+    """
+
+    delay = 1  # second
+    event = multiprocessing.Event()
+
+    async def async_process_waiting_on_event():
+        """Wait for the event to be set."""
+        await sync_to_async(event.wait)()
+        return 42
+
+    async def async_process_that_triggers_event():
+        """Sleep, then set the event."""
+        await asyncio.sleep(delay)
+        await sync_to_async(event.set)()
+
+    # Run the event setter as a task.
+    trigger_task = asyncio.ensure_future(async_process_that_triggers_event())
+
+    try:
+        # wait on the event waiter, which is now blocking the event setter.
+        async with timeout(delay + 1):
+            assert await async_process_waiting_on_event() == 42
+    except asyncio.TimeoutError:
+        # In case of timeout, set the event to unblock things, else
+        # downstream tests will get fouled up.
+        event.set()
+        raise
+    finally:
+        await trigger_task
+
+
[email protected]
+async def test_sync_to_async_with_blocker_non_thread_sensitive():
+    """
+    Tests sync_to_async running on a long-time blocker in a 
non_thread_sensitive context.
+    """
+
+    delay = 1  # second
+    event = multiprocessing.Event()
+
+    async def async_process_waiting_on_event():
+        """Wait for the event to be set."""
+        await sync_to_async(event.wait, thread_sensitive=False)()
+        return 42
+
+    async def async_process_that_triggers_event():
+        """Sleep, then set the event."""
+        await asyncio.sleep(1)
+        await sync_to_async(event.set)()
+
+    # Run the event setter as a task.
+    trigger_task = asyncio.ensure_future(async_process_that_triggers_event())
+
+    try:
+        # wait on the event waiter, which is now blocking the event setter.
+        async with timeout(delay + 1):
+            assert await async_process_waiting_on_event() == 42
+    except asyncio.TimeoutError:
+        # In case of timeout, set the event to unblock things, else
+        # downstream tests will get fouled up.
+        event.set()
+        raise
+    finally:
+        await trigger_task
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/asgiref-3.4.1/tests/test_sync_contextvars.py 
new/asgiref-3.5.2/tests/test_sync_contextvars.py
--- old/asgiref-3.4.1/tests/test_sync_contextvars.py    2021-06-27 
22:11:39.000000000 +0200
+++ new/asgiref-3.5.2/tests/test_sync_contextvars.py    2022-01-22 
17:43:01.000000000 +0100
@@ -4,7 +4,6 @@
 
 import pytest
 
-from asgiref.compatibility import create_task
 from asgiref.sync import ThreadSensitiveContext, async_to_sync, sync_to_async
 
 contextvars = pytest.importorskip("contextvars")
@@ -26,7 +25,9 @@
             await store_thread(result)
 
     # Run it (in true parallel!)
-    await asyncio.wait([create_task(fn(result_1)), create_task(fn(result_2))])
+    await asyncio.wait(
+        [asyncio.create_task(fn(result_1)), asyncio.create_task(fn(result_2))]
+    )
 
     # They should not have run in the main thread, and on different threads
     assert result_1["thread"] != threading.current_thread()

Reply via email to