From 19984ab9e8eb0b68cc18672d03c433f9bc256de1 Mon Sep 17 00:00:00 2001 From: Daniel Porter Date: Thu, 19 Sep 2024 17:36:59 +0100 Subject: [PATCH 1/5] Fix metadata handling for py38 and py39 venv environments importlib.metadata in Python 3.10 had improved handling of certain virtual environments. This change addresses the issue with some venv environments not returning a version string for AnyIO. --- asyncer/_compat.py | 10 +++++++--- pyproject.toml | 3 ++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/asyncer/_compat.py b/asyncer/_compat.py index ade175e8..546c5b08 100644 --- a/asyncer/_compat.py +++ b/asyncer/_compat.py @@ -1,6 +1,5 @@ # AnyIO 4.1.0 renamed cancellable to abandon_on_cancel -import importlib -import importlib.metadata +import sys from typing import Callable, TypeVar, Union import anyio @@ -8,7 +7,12 @@ from anyio import CapacityLimiter from typing_extensions import TypeVarTuple, Unpack -ANYIO_VERSION = importlib.metadata.version("anyio") +if sys.version_info < (3, 10): + from importlib_metadata import version as get_version +else: + from importlib.metadata import version as get_version + +ANYIO_VERSION = get_version("anyio") T_Retval = TypeVar("T_Retval") PosArgsT = TypeVarTuple("PosArgsT") diff --git a/pyproject.toml b/pyproject.toml index e3fd80bb..3b7d7297 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,8 @@ classifiers = [ ] dependencies = [ "anyio >=3.4.0,<5.0", - "typing_extensions >=4.8.0; python_version < '3.10'" + "importlib-metadata >=4.6; python_version < '3.10'", + "typing_extensions >=4.8.0; python_version < '3.10'", ] [project.urls] From d4483687875ab9d8e5942997db7ef9687f1aea09 Mon Sep 17 00:00:00 2001 From: Daniel Porter Date: Thu, 19 Sep 2024 17:53:21 +0100 Subject: [PATCH 2/5] Properly calculate version of AnyIO --- asyncer/_compat.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/asyncer/_compat.py b/asyncer/_compat.py index 546c5b08..c65227bc 100644 --- a/asyncer/_compat.py +++ b/asyncer/_compat.py @@ -1,6 +1,5 @@ -# AnyIO 4.1.0 renamed cancellable to abandon_on_cancel import sys -from typing import Callable, TypeVar, Union +from typing import Callable, Tuple, TypeVar, Union import anyio import anyio.to_thread @@ -12,12 +11,16 @@ else: from importlib.metadata import version as get_version -ANYIO_VERSION = get_version("anyio") +ANYIO_VERSION: Tuple[int, ...] = tuple( + int(num) for num in get_version("anyio").split(".")[:3] +) T_Retval = TypeVar("T_Retval") PosArgsT = TypeVarTuple("PosArgsT") -if ANYIO_VERSION >= "4.1.0": + +# AnyIO 4.1.0 renamed cancellable to abandon_on_cancel +if ANYIO_VERSION >= (4, 1): async def run_sync( func: Callable[[Unpack[PosArgsT]], T_Retval], From bf9672cce73353ace0cb1f3155b1206785a21905 Mon Sep 17 00:00:00 2001 From: Daniel Porter Date: Thu, 19 Sep 2024 17:59:36 +0100 Subject: [PATCH 3/5] Use inspect to determine anyio.to_thread.run_sync kwargs --- asyncer/_compat.py | 15 +++------------ pyproject.toml | 3 +-- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/asyncer/_compat.py b/asyncer/_compat.py index c65227bc..6f575bee 100644 --- a/asyncer/_compat.py +++ b/asyncer/_compat.py @@ -1,26 +1,17 @@ -import sys -from typing import Callable, Tuple, TypeVar, Union +import inspect +from typing import Callable, TypeVar, Union import anyio import anyio.to_thread from anyio import CapacityLimiter from typing_extensions import TypeVarTuple, Unpack -if sys.version_info < (3, 10): - from importlib_metadata import version as get_version -else: - from importlib.metadata import version as get_version - -ANYIO_VERSION: Tuple[int, ...] = tuple( - int(num) for num in get_version("anyio").split(".")[:3] -) - T_Retval = TypeVar("T_Retval") PosArgsT = TypeVarTuple("PosArgsT") # AnyIO 4.1.0 renamed cancellable to abandon_on_cancel -if ANYIO_VERSION >= (4, 1): +if "abandon_on_cancel" in inspect.getfullargspec(anyio.to_thread.run_sync).kwonlyargs: async def run_sync( func: Callable[[Unpack[PosArgsT]], T_Retval], diff --git a/pyproject.toml b/pyproject.toml index 3b7d7297..e3fd80bb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,8 +33,7 @@ classifiers = [ ] dependencies = [ "anyio >=3.4.0,<5.0", - "importlib-metadata >=4.6; python_version < '3.10'", - "typing_extensions >=4.8.0; python_version < '3.10'", + "typing_extensions >=4.8.0; python_version < '3.10'" ] [project.urls] From 78626dbc9c09c57fb4d4e3d6c05ce15bde5b5693 Mon Sep 17 00:00:00 2001 From: Daniel Porter Date: Thu, 30 Jan 2025 19:59:36 +0000 Subject: [PATCH 4/5] Move anyio version conditional to function This should keep type checkers happy that aren't running code. --- asyncer/_compat.py | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/asyncer/_compat.py b/asyncer/_compat.py index 6f575bee..5ff2eca2 100644 --- a/asyncer/_compat.py +++ b/asyncer/_compat.py @@ -10,26 +10,21 @@ PosArgsT = TypeVarTuple("PosArgsT") -# AnyIO 4.1.0 renamed cancellable to abandon_on_cancel -if "abandon_on_cancel" in inspect.getfullargspec(anyio.to_thread.run_sync).kwonlyargs: - - async def run_sync( - func: Callable[[Unpack[PosArgsT]], T_Retval], - *args: Unpack[PosArgsT], - abandon_on_cancel: bool = False, - limiter: Union[CapacityLimiter, None] = None, - ) -> T_Retval: +async def run_sync( + func: Callable[[Unpack[PosArgsT]], T_Retval], + *args: Unpack[PosArgsT], + abandon_on_cancel: bool = False, + limiter: Union[CapacityLimiter, None] = None, +) -> T_Retval: + # AnyIO 4.1.0 renamed cancellable to abandon_on_cancel + if ( + "abandon_on_cancel" + in inspect.getfullargspec(anyio.to_thread.run_sync).kwonlyargs + ): return await anyio.to_thread.run_sync( func, *args, abandon_on_cancel=abandon_on_cancel, limiter=limiter ) -else: - - async def run_sync( - func: Callable[[Unpack[PosArgsT]], T_Retval], - *args: Unpack[PosArgsT], - abandon_on_cancel: bool = False, - limiter: Union[CapacityLimiter, None] = None, - ) -> T_Retval: + else: return await anyio.to_thread.run_sync( func, *args, cancellable=abandon_on_cancel, limiter=limiter ) From 022707704b01b95f6ffe1323357f2ff376d72f53 Mon Sep 17 00:00:00 2001 From: Daniel Porter Date: Sat, 1 Mar 2025 02:04:07 +0000 Subject: [PATCH 5/5] Move anyio kwarg check to constant This ensures the check is only performed once on import, whilst retaining a static function definition. --- asyncer/_compat.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/asyncer/_compat.py b/asyncer/_compat.py index 5ff2eca2..805b5036 100644 --- a/asyncer/_compat.py +++ b/asyncer/_compat.py @@ -9,6 +9,11 @@ T_Retval = TypeVar("T_Retval") PosArgsT = TypeVarTuple("PosArgsT") +# Use abandon_on_cancel if available +RUN_SYNC_AOC = ( + "abandon_on_cancel" in inspect.getfullargspec(anyio.to_thread.run_sync).kwonlyargs +) + async def run_sync( func: Callable[[Unpack[PosArgsT]], T_Retval], @@ -17,10 +22,7 @@ async def run_sync( limiter: Union[CapacityLimiter, None] = None, ) -> T_Retval: # AnyIO 4.1.0 renamed cancellable to abandon_on_cancel - if ( - "abandon_on_cancel" - in inspect.getfullargspec(anyio.to_thread.run_sync).kwonlyargs - ): + if RUN_SYNC_AOC: return await anyio.to_thread.run_sync( func, *args, abandon_on_cancel=abandon_on_cancel, limiter=limiter )