Hello community, here is the log from the commit of package python-asgiref for openSUSE:Factory checked in at 2020-07-02 23:54:44 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-asgiref (Old) and /work/SRC/openSUSE:Factory/.python-asgiref.new.3060 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-asgiref" Thu Jul 2 23:54:44 2020 rev:2 rq:817963 version:3.2.10 Changes: -------- --- /work/SRC/openSUSE:Factory/python-asgiref/python-asgiref.changes 2020-04-25 20:34:37.830802287 +0200 +++ /work/SRC/openSUSE:Factory/.python-asgiref.new.3060/python-asgiref.changes 2020-07-02 23:54:49.800572868 +0200 @@ -1,0 +2,10 @@ +Wed Jul 1 04:59:38 UTC 2020 - Steve Kowalik <steven.kowa...@suse.com> + +- Update to 3.2.10: + * Fixed bugs due to bad WeakRef handling introduced in 3.2.8 + * Fixed regression with exception handling in 3.2.8 related to the + contextvars fix. + * Fixed small memory leak in local.Local + * contextvars are now persisted through AsyncToSync + +------------------------------------------------------------------- Old: ---- asgiref-3.2.7.tar.gz New: ---- asgiref-3.2.10.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-asgiref.spec ++++++ --- /var/tmp/diff_new_pack.KrYhBi/_old 2020-07-02 23:54:50.480575122 +0200 +++ /var/tmp/diff_new_pack.KrYhBi/_new 2020-07-02 23:54:50.484575135 +0200 @@ -19,7 +19,7 @@ %define skip_python2 1 %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-asgiref -Version: 3.2.7 +Version: 3.2.10 Release: 0 Summary: ASGI specs, helper code, and adapters License: BSD-3-Clause ++++++ asgiref-3.2.7.tar.gz -> asgiref-3.2.10.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asgiref-3.2.7/PKG-INFO new/asgiref-3.2.10/PKG-INFO --- old/asgiref-3.2.7/PKG-INFO 2020-03-24 18:57:19.607391400 +0100 +++ new/asgiref-3.2.10/PKG-INFO 2020-06-18 20:49:06.533756500 +0200 @@ -1,11 +1,14 @@ Metadata-Version: 2.1 Name: asgiref -Version: 3.2.7 +Version: 3.2.10 Summary: ASGI specs, helper code, and adapters -Home-page: http://github.com/django/asgiref/ +Home-page: https://github.com/django/asgiref/ Author: Django Software Foundation Author-email: foundat...@djangoproject.com License: BSD +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 Description: asgiref ======= @@ -212,11 +215,11 @@ Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Topic :: Internet :: WWW/HTTP Requires-Python: >=3.5 -Description-Content-Type: text/x-rst Provides-Extra: tests diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asgiref-3.2.7/asgiref/__init__.py new/asgiref-3.2.10/asgiref/__init__.py --- old/asgiref-3.2.7/asgiref/__init__.py 2020-03-24 18:57:04.000000000 +0100 +++ new/asgiref-3.2.10/asgiref/__init__.py 2020-06-18 20:48:49.000000000 +0200 @@ -1 +1 @@ -__version__ = "3.2.7" +__version__ = "3.2.10" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asgiref-3.2.7/asgiref/local.py new/asgiref-3.2.10/asgiref/local.py --- old/asgiref-3.2.7/asgiref/local.py 2020-03-24 18:57:04.000000000 +0100 +++ new/asgiref-3.2.10/asgiref/local.py 2020-06-18 20:48:49.000000000 +0200 @@ -35,7 +35,7 @@ def __init__(self, thread_critical=False): self._thread_critical = thread_critical self._thread_lock = threading.RLock() - self._context_refs = [] + self._context_refs = weakref.WeakSet() # Random suffixes stop accidental reuse between different Locals, # though we try to force deletion as well. self._attr_name = "_asgiref_local_impl_%s_%s" % ( @@ -83,17 +83,20 @@ context_obj = self._get_context_id() if not hasattr(context_obj, self._attr_name): setattr(context_obj, self._attr_name, {}) - self._context_refs.append(weakref.ref(context_obj)) + self._context_refs.add(context_obj) return getattr(context_obj, self._attr_name) def __del__(self): - for ref in self._context_refs: - context_obj = ref() - if context_obj: + try: + for context_obj in self._context_refs: try: delattr(context_obj, self._attr_name) except AttributeError: pass + except TypeError: + # WeakSet.__iter__ can crash when interpreter is shutting down due + # to _IterationGuard being None. + pass def __getattr__(self, key): with self._thread_lock: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asgiref-3.2.7/asgiref/sync.py new/asgiref-3.2.10/asgiref/sync.py --- old/asgiref-3.2.7/asgiref/sync.py 2020-03-24 18:57:04.000000000 +0100 +++ new/asgiref-3.2.10/asgiref/sync.py 2020-06-18 20:48:49.000000000 +0200 @@ -15,6 +15,17 @@ contextvars = None +def _restore_context(context): + # Check for changes in contextvars, and set them to the current + # context for downstream consumers + for cvar in context: + try: + if cvar.get() != context.get(cvar): + cvar.set(context.get(cvar)) + except LookupError: + cvar.set(context.get(cvar)) + + class AsyncToSync: """ Utility class which turns an awaitable that only works on the thread with @@ -66,6 +77,14 @@ "You cannot use AsyncToSync in the same thread as an async event loop - " "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 + # Make a future for the return information call_result = Future() # Get the source thread @@ -83,16 +102,16 @@ # main event loop's thread if it's there, otherwise make a new loop # in this thread. try: + awaitable = self.main_wrap( + args, kwargs, call_result, source_thread, sys.exc_info(), context + ) + 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() loop_executor = ThreadPoolExecutor(max_workers=1) loop_future = loop_executor.submit( - self._run_event_loop, - loop, - self.main_wrap( - args, kwargs, call_result, source_thread, sys.exc_info() - ), + self._run_event_loop, loop, awaitable ) if current_executor: # Run the CurrentThreadExecutor until the future is done @@ -102,10 +121,7 @@ else: # Call it inside the existing loop self.main_event_loop.call_soon_threadsafe( - self.main_event_loop.create_task, - self.main_wrap( - args, kwargs, call_result, source_thread, sys.exc_info() - ), + self.main_event_loop.create_task, awaitable ) if current_executor: # Run the CurrentThreadExecutor until the future is done @@ -116,6 +132,9 @@ del self.executors.current if old_current_executor: self.executors.current = old_current_executor + if contextvars is not None: + _restore_context(context[0]) + # Wait for results from the future. return call_result.result() @@ -161,11 +180,16 @@ func = functools.partial(self.__call__, parent) return functools.update_wrapper(func, self.awaitable) - async def main_wrap(self, args, kwargs, call_result, source_thread, exc_info): + async def main_wrap( + self, args, kwargs, call_result, source_thread, exc_info, context + ): """ Wraps the awaitable with something that puts the result into the result/exception future. """ + if context is not None: + _restore_context(context[0]) + current_task = SyncToAsync.get_current_task() self.launch_map[current_task] = source_thread try: @@ -185,6 +209,9 @@ finally: del self.launch_map[current_task] + if context is not None: + context[0] = contextvars.copy_context() + class SyncToAsync: """ @@ -269,14 +296,7 @@ ret = await asyncio.wait_for(future, timeout=None) if contextvars is not None: - # Check for changes in contextvars, and set them to the current - # context for downstream consumers - for cvar in context: - try: - if cvar.get() != context.get(cvar): - cvar.set(context.get(cvar)) - except LookupError: - cvar.set(context.get(cvar)) + _restore_context(context) return ret diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asgiref-3.2.7/asgiref.egg-info/PKG-INFO new/asgiref-3.2.10/asgiref.egg-info/PKG-INFO --- old/asgiref-3.2.7/asgiref.egg-info/PKG-INFO 2020-03-24 18:57:19.000000000 +0100 +++ new/asgiref-3.2.10/asgiref.egg-info/PKG-INFO 2020-06-18 20:49:06.000000000 +0200 @@ -1,11 +1,14 @@ Metadata-Version: 2.1 Name: asgiref -Version: 3.2.7 +Version: 3.2.10 Summary: ASGI specs, helper code, and adapters -Home-page: http://github.com/django/asgiref/ +Home-page: https://github.com/django/asgiref/ Author: Django Software Foundation Author-email: foundat...@djangoproject.com License: BSD +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 Description: asgiref ======= @@ -212,11 +215,11 @@ Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Topic :: Internet :: WWW/HTTP Requires-Python: >=3.5 -Description-Content-Type: text/x-rst Provides-Extra: tests diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asgiref-3.2.7/asgiref.egg-info/requires.txt new/asgiref-3.2.10/asgiref.egg-info/requires.txt --- old/asgiref-3.2.7/asgiref.egg-info/requires.txt 2020-03-24 18:57:19.000000000 +0100 +++ new/asgiref-3.2.10/asgiref.egg-info/requires.txt 2020-06-18 20:49:06.000000000 +0200 @@ -1,4 +1,4 @@ [tests] -pytest~=4.3.0 -pytest-asyncio~=0.10.0 +pytest +pytest-asyncio diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asgiref-3.2.7/setup.cfg new/asgiref-3.2.10/setup.cfg --- old/asgiref-3.2.7/setup.cfg 2020-03-24 18:57:19.607391400 +0100 +++ new/asgiref-3.2.10/setup.cfg 2020-06-18 20:49:06.537758600 +0200 @@ -1,5 +1,41 @@ -[bdist_wheel] -universal = 1 +[metadata] +name = asgiref +version = attr: asgiref.__version__ +url = https://github.com/django/asgiref/ +author = Django Software Foundation +author_email = foundat...@djangoproject.com +description = ASGI specs, helper code, and adapters +long_description = file: README.rst +license = BSD +classifiers = + Development Status :: 5 - Production/Stable + Environment :: Web Environment + Intended Audience :: Developers + License :: OSI Approved :: BSD License + Operating System :: OS Independent + Programming Language :: Python + Programming Language :: Python :: 3 + Programming Language :: Python :: 3 :: Only + Programming Language :: Python :: 3.5 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Topic :: Internet :: WWW/HTTP +project_urls = + Documentation = https://asgi.readthedocs.io/ + Further Documentation = https://docs.djangoproject.com/en/stable/topics/async/#async-adapter-functions + Changelog = https://github.com/django/asgiref/blob/master/CHANGELOG.txt + +[options] +python_requires = >=3.5 +packages = find: +include_package_data = true +zip_safe = false + +[options.extras_require] +tests = + pytest + pytest-asyncio [tool:pytest] testpaths = tests diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asgiref-3.2.7/setup.py new/asgiref-3.2.10/setup.py --- old/asgiref-3.2.7/setup.py 2020-03-24 18:57:04.000000000 +0100 +++ new/asgiref-3.2.10/setup.py 2020-06-18 20:48:49.000000000 +0200 @@ -1,44 +1,3 @@ -import os -from setuptools import find_packages, setup -from asgiref import __version__ +from setuptools import setup - -# We use the README as the long_description -readme_path = os.path.join(os.path.dirname(__file__), "README.rst") - - -setup( - name='asgiref', - version=__version__, - url='http://github.com/django/asgiref/', - author='Django Software Foundation', - author_email='foundat...@djangoproject.com', - description='ASGI specs, helper code, and adapters', - long_description=open(readme_path).read(), - long_description_content_type='text/x-rst', - license='BSD', - zip_safe=False, - packages=find_packages(exclude=['tests']), - include_package_data=True, - python_requires=">=3.5", - extras_require={ - "tests": [ - "pytest~=4.3.0", - "pytest-asyncio~=0.10.0", - ], - }, - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Environment :: Web Environment', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Topic :: Internet :: WWW/HTTP', - ], -) +setup(name='asgiref') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asgiref-3.2.7/tests/test_local.py new/asgiref-3.2.10/tests/test_local.py --- old/asgiref-3.2.7/tests/test_local.py 2020-03-24 18:57:04.000000000 +0100 +++ new/asgiref-3.2.10/tests/test_local.py 2020-06-18 20:48:49.000000000 +0200 @@ -298,3 +298,20 @@ await sync_to_async(sync_function)(5) assert test_local.counter == 6 + + +def test_local_del_swallows_type_error(monkeypatch): + test_local = Local() + + blow_up_calls = 0 + + def blow_up(self): + nonlocal blow_up_calls + blow_up_calls += 1 + raise TypeError() + + monkeypatch.setattr("weakref.WeakSet.__iter__", blow_up) + + test_local.__del__() + + assert blow_up_calls == 1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asgiref-3.2.7/tests/test_sync_contextvars.py new/asgiref-3.2.10/tests/test_sync_contextvars.py --- old/asgiref-3.2.7/tests/test_sync_contextvars.py 2020-03-24 18:57:04.000000000 +0100 +++ new/asgiref-3.2.10/tests/test_sync_contextvars.py 2020-06-18 20:48:49.000000000 +0200 @@ -1,8 +1,9 @@ +import asyncio import time import pytest -from asgiref.sync import sync_to_async +from asgiref.sync import async_to_sync, sync_to_async contextvars = pytest.importorskip("contextvars") @@ -27,5 +28,26 @@ # Wrap it foo.set("bar") async_function = sync_to_async(sync_function) - await async_function() + assert await async_function() == 42 + assert foo.get() == "baz" + + +def test_async_to_sync_contextvars(): + """ + Tests to make sure that contextvars from the calling context are + present in the called context, and that any changes in the called context + are then propagated back to the calling context. + """ + # Define sync function + async def async_function(): + await asyncio.sleep(1) + assert foo.get() == "bar" + foo.set("baz") + return 42 + + # Ensure outermost detection works + # Wrap it + foo.set("bar") + sync_function = async_to_sync(async_function) + assert sync_function() == 42 assert foo.get() == "baz"