Skip to content

AbstractContextManager.__enter__ not properly annotated #13950

Closed as not planned

Description

@mharding-hpe

Per the Python docs, AbstractContextManager provides a default __enter__ implementation which just returns self. But this does not appear to be properly type annotated (using Self):

from typing import reveal_type, Optional, Self
from types import TracebackType
from contextlib import AbstractContextManager

class A1(AbstractContextManager):
    def __exit__(  # pylint: disable=useless-return
            self, exc_type: Optional[type[BaseException]],
            exc_val: Optional[BaseException],
            exc_tb: Optional[TracebackType]) -> Optional[bool]:
        return None

class A2(AbstractContextManager):
    def __exit__(  # pylint: disable=useless-return
            self, exc_type: Optional[type[BaseException]],
            exc_val: Optional[BaseException],
            exc_tb: Optional[TracebackType]) -> Optional[bool]:
        return None

    def __enter__(self) -> Self:
        return self

class B1(A1):
    pass

class B2(A2):
    pass

with B1() as d1:
    reveal_type(d1)

with B2() as d2:
    reveal_type(d2)

Running this with mypy, only the second case properly reveals the type as B2. The first one just says Any.

At runtime, the behavior of both is identical.

I have recreated this on both Python 3.12 and Python 3.13.

Activity

  1. transferred this issue frompython/typingon May 6, 2025
  2. srittau commented on May 6, 2025

    @srittau
    Collaborator

    This is due to the duality of AbstractContextManager being both an ABC and a protocol. As a protocol it makes sense that it is generic over the return type of __enter__. Ideally we'd give the type variable a default of Self, but I'm not sure that that's possible.

  3. srittau commented on May 6, 2025

    @srittau
    Collaborator

    I've opened #13951 as a example. I've also opened a discussion on discuss.python.org.

  4. mharding-hpe commented on May 6, 2025

    @mharding-hpe
    ContributorAuthor

    Yeah, I tried using AbstractContextManager[Self], but that gave an error saying Self could only be used inside the class body itself.

    If it isn't possible to have it default to Self, at least the "workaround" I used in A2 isn't too painful.

  5. added
    reason: inexpressibleClosed, because this can't be expressed within the current type system
    on May 11, 2025
  6. srittau commented on May 11, 2025

    @srittau
    Collaborator

    Considering that Self can't be used as a default, and the discussion doesn't look encouraging, I'm going to close this as "can't fix" for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    reason: inexpressibleClosed, because this can't be expressed within the current type systemstubs: false negativeType checkers do not report an error, but should

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions