commit python-asgiref for openSUSE:Factory
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 2023-11-30 21:59:42 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-asgiref (Old) and /work/SRC/openSUSE:Factory/.python-asgiref.new.25432 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "python-asgiref" Thu Nov 30 21:59:42 2023 rev:10 rq:1129812 version:3.7.2 Changes: -------- --- /work/SRC/openSUSE:Factory/python-asgiref/python-asgiref.changes 2023-04-22 22:03:06.386165616 +0200 +++ /work/SRC/openSUSE:Factory/.python-asgiref.new.25432/python-asgiref.changes 2023-11-30 22:00:40.927647124 +0100 @@ -1,0 +2,21 @@ +Wed Nov 29 13:04:33 UTC 2023 - Dirk Müller <dmueller@suse.com> + +- update to 3.7.2: + * The type annotations for SyncToAsync and AsyncToSync have been + changed to more accurately reflect the kind of callables they + return. + * On Python 3.10 and below, the version of the "typing_extensions" + package is now constrained to be at least version 4 (as we depend + on functionality in that version and above) + * Contextvars are now required for the implementation of `sync` + as Python 3.6 is now no longer a supported version. + * sync_to_async and async_to_sync now pass-through + * Debug and Lifespan State extensions have resulted in a typing + change for some request and response types. This change should + be backwards-compatible. + * ``asgiref`` frames will now be hidden in Django tracebacks + by default. + * Raw performance and garbage collection improvements in Local, + SyncToAsync, and AsyncToSync. + +------------------------------------------------------------------- Old: ---- asgiref-3.6.0.tar.gz New: ---- asgiref-3.7.2.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-asgiref.spec ++++++ --- /var/tmp/diff_new_pack.TyK56N/_old 2023-11-30 22:00:41.431665691 +0100 +++ /var/tmp/diff_new_pack.TyK56N/_new 2023-11-30 22:00:41.435665839 +0100 @@ -16,32 +16,25 @@ # -%define skip_python2 1 -%{?!python_module:%define python_module() python-%{**} python3-%{**}} %{?sle15_python_module_pythons} Name: python-asgiref -Version: 3.6.0 +Version: 3.7.2 Release: 0 Summary: ASGI specs, helper code, and adapters License: BSD-3-Clause Group: Development/Languages/Python URL: https://github.com/django/asgiref/ Source: https://files.pythonhosted.org/packages/source/a/asgiref/asgiref-%{version}.tar.gz -BuildRequires: %{python_module base >= 3.6} +BuildRequires: %{python_module base >= 3.7} +BuildRequires: %{python_module pip} BuildRequires: %{python_module pytest-asyncio} BuildRequires: %{python_module pytest} -BuildRequires: %{python_module setuptools} +BuildRequires: %{python_module wheel} BuildRequires: fdupes BuildRequires: python-rpm-macros BuildArch: noarch -%if %{?suse_version} < 1540 -BuildRequires: %{python_module typing_extensions} -%else -BuildRequires: %{python_module typing_extensions if %python-base < 3.8} -%endif -%if 0%{python_version_nodots} < 38 -Requires: python-typing_extensions -%endif +BuildRequires: %{python_module typing-extensions > 4} +Requires: python-typing-extensions > 4 %python_subpackages %description @@ -53,10 +46,10 @@ %setup -q -n asgiref-%{version} %build -%python_build +%pyproject_wheel %install -%python_install +%pyproject_install %python_expand %fdupes %{buildroot}%{$python_sitelib} %check @@ -65,5 +58,6 @@ %files %{python_files} %license LICENSE %doc README.rst -%{python_sitelib}/* +%{python_sitelib}/asgiref +%{python_sitelib}/asgiref-%{version}.dist-info ++++++ asgiref-3.6.0.tar.gz -> asgiref-3.7.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asgiref-3.6.0/PKG-INFO new/asgiref-3.7.2/PKG-INFO --- old/asgiref-3.6.0/PKG-INFO 2022-12-20 10:06:25.459167200 +0100 +++ new/asgiref-3.7.2/PKG-INFO 2023-05-27 19:21:19.747680700 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: asgiref -Version: 3.6.0 +Version: 3.7.2 Summary: ASGI specs, helper code, and adapters Home-page: https://github.com/django/asgiref/ Author: Django Software Foundation diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asgiref-3.6.0/asgiref/__init__.py new/asgiref-3.7.2/asgiref/__init__.py --- old/asgiref-3.6.0/asgiref/__init__.py 2022-12-20 09:57:26.000000000 +0100 +++ new/asgiref-3.7.2/asgiref/__init__.py 2023-05-27 19:20:44.000000000 +0200 @@ -1 +1 @@ -__version__ = "3.6.0" +__version__ = "3.7.2" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asgiref-3.6.0/asgiref/current_thread_executor.py new/asgiref-3.7.2/asgiref/current_thread_executor.py --- old/asgiref-3.6.0/asgiref/current_thread_executor.py 2022-04-19 09:47:42.000000000 +0200 +++ new/asgiref-3.7.2/asgiref/current_thread_executor.py 2023-05-23 18:35:51.000000000 +0200 @@ -1,6 +1,17 @@ import queue +import sys import threading from concurrent.futures import Executor, Future +from typing import TYPE_CHECKING, Any, Callable, TypeVar, Union + +if sys.version_info >= (3, 10): + from typing import ParamSpec +else: + from typing_extensions import ParamSpec + +_T = TypeVar("_T") +_P = ParamSpec("_P") +_R = TypeVar("_R") class _WorkItem: @@ -9,13 +20,20 @@ Copied from ThreadPoolExecutor (but it's private, so we're not going to rely on importing it) """ - def __init__(self, future, fn, args, kwargs): + def __init__( + self, + future: "Future[_R]", + fn: Callable[_P, _R], + *args: _P.args, + **kwargs: _P.kwargs, + ): self.future = future self.fn = fn self.args = args self.kwargs = kwargs - def run(self): + def run(self) -> None: + __traceback_hide__ = True # noqa: F841 if not self.future.set_running_or_notify_cancel(): return try: @@ -23,7 +41,7 @@ except BaseException as exc: self.future.set_exception(exc) # Break a reference cycle with the exception 'exc' - self = None + self = None # type: ignore[assignment] else: self.future.set_result(result) @@ -35,12 +53,12 @@ the thread they came from. """ - def __init__(self): + def __init__(self) -> None: self._work_thread = threading.current_thread() - self._work_queue = queue.Queue() + self._work_queue: queue.Queue[Union[_WorkItem, "Future[Any]"]] = queue.Queue() self._broken = False - def run_until_future(self, future): + def run_until_future(self, future: "Future[Any]") -> None: """ Runs the code in the work queue until a result is available from the future. Should be run from the thread the executor is initialised in. @@ -59,12 +77,18 @@ work_item = self._work_queue.get() if work_item is future: return + assert isinstance(work_item, _WorkItem) work_item.run() del work_item finally: self._broken = True - def submit(self, fn, *args, **kwargs): + def _submit( + self, + fn: Callable[_P, _R], + *args: _P.args, + **kwargs: _P.kwargs, + ) -> "Future[_R]": # Check they're not submitting from the same thread if threading.current_thread() == self._work_thread: raise RuntimeError( @@ -74,8 +98,18 @@ if self._broken: raise RuntimeError("CurrentThreadExecutor already quit or is broken") # Add to work queue - f = Future() - work_item = _WorkItem(f, fn, args, kwargs) + f: "Future[_R]" = Future() + work_item = _WorkItem(f, fn, *args, **kwargs) self._work_queue.put(work_item) # Return the future return f + + # Python 3.9+ has a new signature for submit with a "/" after `fn`, to enforce + # it to be a positional argument. If we ignore[override] mypy on 3.9+ will be + # happy but 3.7/3.8 will say that the ignore comment is unused, even when + # defining them differently based on sys.version_info. + # We should be able to remove this when we drop support for 3.7/3.8. + if not TYPE_CHECKING: + + def submit(self, fn, *args, **kwargs): + return self._submit(fn, *args, **kwargs) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asgiref-3.6.0/asgiref/sync.py new/asgiref-3.7.2/asgiref/sync.py --- old/asgiref-3.6.0/asgiref/sync.py 2022-12-15 10:04:51.000000000 +0100 +++ new/asgiref-3.7.2/asgiref/sync.py 2023-05-27 19:19:57.000000000 +0200 @@ -9,21 +9,48 @@ import warnings import weakref from concurrent.futures import Future, ThreadPoolExecutor -from typing import Any, Callable, Dict, Optional, overload +from typing import ( + TYPE_CHECKING, + Any, + Awaitable, + Callable, + Coroutine, + Dict, + Generic, + List, + Optional, + TypeVar, + Union, + overload, +) from .current_thread_executor import CurrentThreadExecutor from .local import Local +if sys.version_info >= (3, 10): + from typing import ParamSpec +else: + from typing_extensions import ParamSpec -def _restore_context(context): +if TYPE_CHECKING: + # This is not available to import at runtime + from _typeshed import OptExcInfo + +_F = TypeVar("_F", bound=Callable[..., Any]) +_P = ParamSpec("_P") +_R = TypeVar("_R") + + +def _restore_context(context: contextvars.Context) -> None: # Check for changes in contextvars, and set them to the current # context for downstream consumers for cvar in context: + cvalue = context.get(cvar) try: - if cvar.get() != context.get(cvar): - cvar.set(context.get(cvar)) + if cvar.get() != cvalue: + cvar.set(cvalue) except LookupError: - cvar.set(context.get(cvar)) + cvar.set(cvalue) # Python 3.12 deprecates asyncio.iscoroutinefunction() as an alias for @@ -32,29 +59,25 @@ # Until 3.12 is the minimum supported Python version, provide a shim. # Django 4.0 only supports 3.8+, so don't concern with the _or_partial backport. -# Type hint: should be generic: whatever T it takes it returns. (Same id) -def markcoroutinefunction(func: Any) -> Any: - if hasattr(inspect, "markcoroutinefunction"): - return inspect.markcoroutinefunction(func) - else: +if hasattr(inspect, "markcoroutinefunction"): + iscoroutinefunction = inspect.iscoroutinefunction + markcoroutinefunction: Callable[[_F], _F] = inspect.markcoroutinefunction +else: + iscoroutinefunction = asyncio.iscoroutinefunction # type: ignore[assignment] + + def markcoroutinefunction(func: _F) -> _F: func._is_coroutine = asyncio.coroutines._is_coroutine # type: ignore return func -def iscoroutinefunction(func: Any) -> bool: - if hasattr(inspect, "markcoroutinefunction"): - return inspect.iscoroutinefunction(func) - else: - return asyncio.iscoroutinefunction(func) - - -def _iscoroutinefunction_or_partial(func: Any) -> bool: - # Python < 3.8 does not correctly determine partially wrapped - # coroutine functions are coroutine functions, hence the need for - # this to exist. Code taken from CPython. - if sys.version_info >= (3, 8): - return iscoroutinefunction(func) - else: +if sys.version_info >= (3, 8): + _iscoroutinefunction_or_partial = iscoroutinefunction +else: + + def _iscoroutinefunction_or_partial(func: Any) -> bool: + # Python < 3.8 does not correctly determine partially wrapped + # coroutine functions are coroutine functions, hence the need for + # this to exist. Code taken from CPython. while inspect.ismethod(func): func = func.__func__ while isinstance(func, functools.partial): @@ -104,7 +127,7 @@ SyncToAsync.thread_sensitive_context.reset(self.token) -class AsyncToSync: +class AsyncToSync(Generic[_P, _R]): """ Utility class which turns an awaitable that only works on the thread with the event loop into a synchronous callable that works in a subthread. @@ -128,7 +151,14 @@ # 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): + def __init__( + self, + awaitable: Union[ + Callable[_P, Coroutine[Any, Any, _R]], + Callable[_P, Awaitable[_R]], + ], + force_new_loop: bool = False, + ): if not callable(awaitable) or ( not _iscoroutinefunction_or_partial(awaitable) and not _iscoroutinefunction_or_partial( @@ -142,7 +172,7 @@ ) self.awaitable = awaitable try: - self.__self__ = self.awaitable.__self__ + self.__self__ = self.awaitable.__self__ # type: ignore[union-attr] except AttributeError: pass if force_new_loop: @@ -166,7 +196,9 @@ else: self.main_event_loop = None - def __call__(self, *args, **kwargs): + def __call__(self, *args: _P.args, **kwargs: _P.kwargs) -> _R: + __traceback_hide__ = True # noqa: F841 + # You can't call AsyncToSync from a thread with a running event loop try: event_loop = asyncio.get_running_loop() @@ -184,7 +216,7 @@ context = [contextvars.copy_context()] # Make a future for the return information - call_result = Future() + call_result: "Future[_R]" = Future() # Get the source thread source_thread = threading.current_thread() # Make a CurrentThreadExecutor we'll use to idle in this thread - we @@ -202,7 +234,12 @@ # in this thread. try: awaitable = self.main_wrap( - args, kwargs, call_result, source_thread, sys.exc_info(), context + call_result, + source_thread, + sys.exc_info(), + context, + *args, + **kwargs, ) if not (self.main_event_loop and self.main_event_loop.is_running()): @@ -275,7 +312,7 @@ loop.close() asyncio.set_event_loop(self.main_event_loop) - def __get__(self, parent, objtype): + def __get__(self, parent: Any, objtype: Any) -> Callable[_P, _R]: """ Include self for methods """ @@ -283,16 +320,26 @@ return functools.update_wrapper(func, self.awaitable) async def main_wrap( - self, args, kwargs, call_result, source_thread, exc_info, context - ): + self, + call_result: "Future[_R]", + source_thread: threading.Thread, + exc_info: "OptExcInfo", + context: List[contextvars.Context], + *args: _P.args, + **kwargs: _P.kwargs, + ) -> None: """ Wraps the awaitable with something that puts the result into the result/exception future. """ + + __traceback_hide__ = True # noqa: F841 + if context is not None: _restore_context(context[0]) current_task = SyncToAsync.get_current_task() + assert current_task is not None self.launch_map[current_task] = source_thread try: # If we have an exception, run the function inside the except block @@ -314,7 +361,7 @@ context[0] = contextvars.copy_context() -class SyncToAsync: +class SyncToAsync(Generic[_P, _R]): """ Utility class which turns a synchronous callable into an awaitable that runs in a threadpool. It also sets a threadlocal inside the thread so @@ -347,8 +394,8 @@ # Maintain a contextvar for the current execution context. Optionally used # for thread sensitive mode. - thread_sensitive_context: "contextvars.ContextVar[str]" = contextvars.ContextVar( - "thread_sensitive_context" + thread_sensitive_context: "contextvars.ContextVar[ThreadSensitiveContext]" = ( + contextvars.ContextVar("thread_sensitive_context") ) # Contextvar that is used to detect if the single thread executor @@ -359,13 +406,13 @@ # 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. - context_to_thread_executor: "weakref.WeakKeyDictionary[object, ThreadPoolExecutor]" = ( + context_to_thread_executor: "weakref.WeakKeyDictionary[ThreadSensitiveContext, ThreadPoolExecutor]" = ( weakref.WeakKeyDictionary() ) def __init__( self, - func: Callable[..., Any], + func: Callable[_P, _R], thread_sensitive: bool = True, executor: Optional["ThreadPoolExecutor"] = None, ) -> None: @@ -387,7 +434,8 @@ except AttributeError: pass - async def __call__(self, *args, **kwargs): + async def __call__(self, *args: _P.args, **kwargs: _P.kwargs) -> _R: + __traceback_hide__ = True # noqa: F841 loop = asyncio.get_running_loop() # Work out what thread to run the code in @@ -395,9 +443,7 @@ if hasattr(AsyncToSync.executors, "current"): # If we have a parent sync thread above somewhere, use that executor = AsyncToSync.executors.current - elif self.thread_sensitive_context and self.thread_sensitive_context.get( - None - ): + elif self.thread_sensitive_context.get(None): # If we have a way of retrieving the current context, attempt # to use a per-context thread pool executor thread_sensitive_context = self.thread_sensitive_context.get() @@ -412,15 +458,14 @@ 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): + elif self.deadlock_context.get(False): raise RuntimeError( "Single thread executor already being used, would deadlock" ) else: # Otherwise, we run it in a fixed single thread executor = self.single_thread_executor - if self.deadlock_context: - self.deadlock_context.set(True) + self.deadlock_context.set(True) else: # Use the passed in executor, or the loop's default if it is None executor = self._executor @@ -428,12 +473,10 @@ 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 - future = loop.run_in_executor( + ret: _R = await loop.run_in_executor( executor, functools.partial( self.thread_handler, @@ -441,20 +484,19 @@ self.get_current_task(), sys.exc_info(), func, - *args, - **kwargs, + child, ), ) - ret = await asyncio.wait_for(future, timeout=None) finally: _restore_context(context) - if self.deadlock_context: - self.deadlock_context.set(False) + self.deadlock_context.set(False) return ret - def __get__(self, parent, objtype): + def __get__( + self, parent: Any, objtype: Any + ) -> Callable[_P, Coroutine[Any, Any, _R]]: """ Include self for methods """ @@ -465,6 +507,9 @@ """ Wraps the sync application with exception handling. """ + + __traceback_hide__ = True # noqa: F841 + # Set the threadlocal for AsyncToSync self.threadlocal.main_event_loop = loop self.threadlocal.main_event_loop_pid = os.getpid() @@ -477,6 +522,9 @@ else: self.launch_map[current_thread] = source_task parent_set = True + source_task = ( + None # allow the task to be garbage-collected in case of exceptions + ) # Run the function try: # If we have an exception, run the function inside the except block @@ -495,7 +543,7 @@ del self.launch_map[current_thread] @staticmethod - def get_current_task(): + def get_current_task() -> Optional["asyncio.Task[Any]"]: """ Implementation of asyncio.current_task() that returns None if there is no task. @@ -506,33 +554,84 @@ return None -# Lowercase aliases (and decorator friendliness) -async_to_sync = AsyncToSync +@overload +def async_to_sync( + *, + force_new_loop: bool = False, +) -> Callable[ + [Union[Callable[_P, Coroutine[Any, Any, _R]], Callable[_P, Awaitable[_R]]]], + Callable[_P, _R], +]: + ... + + +@overload +def async_to_sync( + awaitable: Union[ + Callable[_P, Coroutine[Any, Any, _R]], + Callable[_P, Awaitable[_R]], + ], + *, + force_new_loop: bool = False, +) -> Callable[_P, _R]: + ... + + +def async_to_sync( + awaitable: Optional[ + Union[ + Callable[_P, Coroutine[Any, Any, _R]], + Callable[_P, Awaitable[_R]], + ] + ] = None, + *, + force_new_loop: bool = False, +) -> Union[ + Callable[ + [Union[Callable[_P, Coroutine[Any, Any, _R]], Callable[_P, Awaitable[_R]]]], + Callable[_P, _R], + ], + Callable[_P, _R], +]: + if awaitable is None: + return lambda f: AsyncToSync( + f, + force_new_loop=force_new_loop, + ) + return AsyncToSync( + awaitable, + force_new_loop=force_new_loop, + ) @overload def sync_to_async( - func: None = None, + *, thread_sensitive: bool = True, executor: Optional["ThreadPoolExecutor"] = None, -) -> Callable[[Callable[..., Any]], SyncToAsync]: +) -> Callable[[Callable[_P, _R]], Callable[_P, Coroutine[Any, Any, _R]]]: ... @overload def sync_to_async( - func: Callable[..., Any], + func: Callable[_P, _R], + *, thread_sensitive: bool = True, executor: Optional["ThreadPoolExecutor"] = None, -) -> SyncToAsync: +) -> Callable[_P, Coroutine[Any, Any, _R]]: ... def sync_to_async( - func=None, - thread_sensitive=True, - executor=None, -): + func: Optional[Callable[_P, _R]] = None, + *, + thread_sensitive: bool = True, + executor: Optional["ThreadPoolExecutor"] = None, +) -> Union[ + Callable[[Callable[_P, _R]], Callable[_P, Coroutine[Any, Any, _R]]], + Callable[_P, Coroutine[Any, Any, _R]], +]: if func is None: return lambda f: SyncToAsync( f, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asgiref-3.6.0/asgiref/typing.py new/asgiref-3.7.2/asgiref/typing.py --- old/asgiref-3.6.0/asgiref/typing.py 2022-12-15 10:04:51.000000000 +0100 +++ new/asgiref-3.7.2/asgiref/typing.py 2023-05-23 18:35:51.000000000 +0200 @@ -1,11 +1,26 @@ import sys -from typing import Awaitable, Callable, Dict, Iterable, Optional, Tuple, Type, Union +from typing import ( + Any, + Awaitable, + Callable, + Dict, + Iterable, + Optional, + Tuple, + Type, + Union, +) if sys.version_info >= (3, 8): from typing import Literal, Protocol, TypedDict else: from typing_extensions import Literal, Protocol, TypedDict +if sys.version_info >= (3, 11): + from typing import NotRequired +else: + from typing_extensions import NotRequired + __all__ = ( "ASGIVersions", "HTTPScope", @@ -62,6 +77,7 @@ headers: Iterable[Tuple[bytes, bytes]] client: Optional[Tuple[str, int]] server: Optional[Tuple[str, Optional[int]]] + state: NotRequired[Dict[str, Any]] extensions: Optional[Dict[str, Dict[object, object]]] @@ -78,12 +94,14 @@ client: Optional[Tuple[str, int]] server: Optional[Tuple[str, Optional[int]]] subprotocols: Iterable[str] + state: NotRequired[Dict[str, Any]] extensions: Optional[Dict[str, Dict[object, object]]] class LifespanScope(TypedDict): type: Literal["lifespan"] asgi: ASGIVersions + state: NotRequired[Dict[str, Any]] WWWScope = Union[HTTPScope, WebSocketScope] @@ -96,6 +114,11 @@ more_body: bool +class HTTPResponseDebugEvent(TypedDict): + type: Literal["http.response.debug"] + info: Dict[str, object] + + class HTTPResponseStartEvent(TypedDict): type: Literal["http.response.start"] status: int diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asgiref-3.6.0/asgiref.egg-info/PKG-INFO new/asgiref-3.7.2/asgiref.egg-info/PKG-INFO --- old/asgiref-3.6.0/asgiref.egg-info/PKG-INFO 2022-12-20 10:06:25.000000000 +0100 +++ new/asgiref-3.7.2/asgiref.egg-info/PKG-INFO 2023-05-27 19:21:19.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: asgiref -Version: 3.6.0 +Version: 3.7.2 Summary: ASGI specs, helper code, and adapters Home-page: https://github.com/django/asgiref/ Author: Django Software Foundation diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asgiref-3.6.0/asgiref.egg-info/requires.txt new/asgiref-3.7.2/asgiref.egg-info/requires.txt --- old/asgiref-3.6.0/asgiref.egg-info/requires.txt 2022-12-20 10:06:25.000000000 +0100 +++ new/asgiref-3.7.2/asgiref.egg-info/requires.txt 2023-05-27 19:21:19.000000000 +0200 @@ -1,6 +1,6 @@ -[:python_version < "3.8"] -typing_extensions +[:python_version < "3.11"] +typing_extensions>=4 [tests] pytest diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asgiref-3.6.0/setup.cfg new/asgiref-3.7.2/setup.cfg --- old/asgiref-3.6.0/setup.cfg 2022-12-20 10:06:25.459771600 +0100 +++ new/asgiref-3.7.2/setup.cfg 2023-05-27 19:21:19.747680700 +0200 @@ -32,7 +32,7 @@ packages = find: include_package_data = true install_requires = - typing_extensions; python_version < "3.8" + typing_extensions>=4; python_version < "3.11" zip_safe = false [options.extras_require] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/asgiref-3.6.0/tests/test_sync_contextvars.py new/asgiref-3.7.2/tests/test_sync_contextvars.py --- old/asgiref-3.6.0/tests/test_sync_contextvars.py 2022-07-06 16:22:11.000000000 +0200 +++ new/asgiref-3.7.2/tests/test_sync_contextvars.py 2023-05-23 18:35:51.000000000 +0200 @@ -1,4 +1,5 @@ import asyncio +import contextvars import threading import time @@ -6,9 +7,7 @@ from asgiref.sync import ThreadSensitiveContext, async_to_sync, sync_to_async -contextvars = pytest.importorskip("contextvars") - -foo = contextvars.ContextVar("foo") +foo: "contextvars.ContextVar[str]" = contextvars.ContextVar("foo") @pytest.mark.asyncio
participants (1)
-
Source-Sync