Bug report
Bug description:
I noticed a behavioral difference between the (async) with statement and (Async)ExitStack. The with statement correctly recognizes context manager methods (__enter__, __exit__, __aenter__, and __aexit__) even when they are defined via __slots__, whereas (Async)ExitStack does not.
class SlotsCM: __slots__ = ("__enter__", "__exit__", ) def __init__(self): self.__enter__ = self.__exit__ = lambda *__: None # This works with SlotsCM(): pass # This doesn't. from contextllib import ExitStack with ExitStack() as stack: stack.enter_context(SlotsCM())
Traceback (most recent call last):
File "xxxx.py", line 16, in <module>
stack.enter_context(SlotsCM())
~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
File "......../contextlib.py", line 530, in enter_context
result = _enter(cm)
TypeError: 'member_descriptor' object is not callable
Furthermore, both (async) with-statement and (Async)ExitStack don't suppot those special dunder methods stored in __dict__. You can verify from this repository:
https://github.com/gottadiveintopython/invest-context-manager
CPython versions tested on:
3.13
Operating systems tested on:
Linux
Linked PRs
- gh-144386: Add support for descriptors in ExitStack and AsyncExitStack #144420
- gh-144386: Update equivalent code for "with", "async with" and "async for" #144472
- [3.14] gh-144386: Update equivalent code for "with", "async with" and "async for" (GH-144472) #144945
- [3.13] gh-144386: Update equivalent code for "with", "async with" and "async for" (GH-144472) #144946