GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||
GeezerVison is showing github.com — `contextlib.contextmanager` exit before the start of a decorated function if we use it on async functions · Issue #114033 · python/cpython Open Labels stdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytype-featureA feature request or enhancementA feature request or enhancement DescriptionBug reportBug description:IssueI'm using a simple script to test how After some investigations, it seems that if we missmatch the sync or async context manager with the sync or async function, the context manager is not called well.
import asyncio import contextlib @contextlib.contextmanager def sync_cm(): print("sync open") yield print("sync close") @contextlib.asynccontextmanager async def async_cm(): print("async open") yield print("async close") @sync_cm() def sync_sync(): print("sync_sync") @sync_cm() async def sync_async(): print("sync_async") @async_cm() def async_sync(): print("async_sync") @async_cm() async def async_async(): print("async_async") async def main(): sync_sync() print("\n---\n") await async_async() print("\n---\n") async_sync() print("\n---\n") await sync_async() asyncio.run(main()) The expected output is: But for the two last cases, the output is: For the first one that means that the function decorated with Potential reasonBut for the last one we have a more concerning output: This means that if you decorate an async function with As it doesn't raise any error, it's hard to understand why it's not working... SolutionAfer some investigations, I have found that we can use the Here is the code: import asyncio import contextlib import functools class AgnosticContextManager(contextlib._GeneratorContextManager): def __call__(self, func): if asyncio.iscoroutinefunction(func): @functools.wraps(func) async def async_wrapper(*args, **kwargs): with self._recreate_cm(): return await func(*args, **kwargs) return async_wrapper else: @functools.wraps(func) def wrapper(*args, **kwargs): with self._recreate_cm(): return func(*args, **kwargs) return wrapper def agnosticcontextmanager(func): @functools.wraps(func) def helper(*args, **kwds): return AgnosticContextManager(func, args, kwds) return helper @agnosticcontextmanager def agno_cm(): print("agno open") yield print("agno close") @agno_cm() def sync(): print("sync") @agno_cm() async def async_(): print("async") async def main(): sync() print("\n---\n") await async_() asyncio.run(main()) That gives the following results : I'm wondering if it can be implemented in the standard library to, by default close the resource after the function is awaited, even if the function is async.
CPython versions tested on:3.8, 3.10, 3.11, 3.12, 3.13, Cpython main branch Operating systems tested on:Linux Reacted by ento Activity
MetadataMetadataAssigneesLabelsstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytype-featureA feature request or enhancementA feature request or enhancement ProjectsMilestoneRelationshipsDevelopmentIssue actions | |||||||||||||||||||||
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |