python / python/typing

Allow Self or Self-like type arguments for generics

Ouverte
#2,276 2 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

topic: feature
Langage dominant
Python
Étoiles
1.8k
Forks
302
Merge moyen
23 h
PR mergées (30 j)
8

Description

from contextlib import AbstractContextManager

class ManagerA(AbstractContextManager):
    def __init__(self, x: int) -> None:
        self._x = x
    def __enter__(self) -> int:
        return self._x

This can raise type check errors because AbstractContextManager is a generic type, and this code omits the type argument. Fair enough, and easy to fix:

from contextlib import AbstractContextManager

class ManagerA(AbstractContextManager[int]):
    def __init__(self, x: int) -> None:
        self._x = x
    def __enter__(self) -> int:
        return self._x

But what if we are not overriding the __enter__ method? Consider this example:

from contextlib import AbstractContextManager

class ManagerB(AbstractContextManager):
    def __init__(self, x: int) -> None:
        self._x = x
    # Other methods are defined, but __enter__ is not overridden

In this case, there is no great way to accurate type annotate it in order to reflect the fact that the __enter__ method always returns the object that it was called on (which is the default implementation of the __enter__ method in AbstractContextManager.

If we were willing to mark this class as final, then we could annotate it as:

class ManagerB(AbstractContextManager["ManagerB"]):

And that would work. But if we want to be able to subclass it, then this would no longer be accurate. Consider:

from contextlib import AbstractContextManager
from types import TracebackType
from typing import Type

class ManagerB(AbstractContextManager["ManagerB"]):
    def __init__(self, x: int) -> None:
        self._x = x

    # Other methods are defined, but __enter__ is not overridden

    def __exit__(
            self, exc_type: Type[BaseException] | None,
            exc_val: BaseException | None,
            exc_tb: TracebackType | None) -> bool | None:
        return None

class SubManager(ManagerB):
    def harf(self) -> None:
        print("harf")

with SubManager(1) as subman:
    subman.harf()

This runs fine, but the type checker will rightly complain. The annotations indicate that subman should be of type ManagerB, but ManagerB has no harf attribute.

It feels like it would be appropriate to use:

class ManagerB(AbstractContextManager[Self]):

But Self is not allowed to be used in this context.

The feature I would like to see is some way to accurately handle non-final subclasses of AbstractContextManager that do not overwrite the __enter__ method (or that overwrite it but still return self).

Obviously this issue applies generally to generics -- this is just an obvious example since the Python implementation of that class makes it impossible to type annotate many of its subclasses. But ideally any solution would apply generally to generics -- essentially giving a way to use Self (or something Self-like) as the type argument for a generic.

Guide de contribution

Aucun guide de contribution indexé pour ce dépôt

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Piste de recherche

Commencez par les exemples de AbstractContextManager et la restriction indiquée concernant l’utilisation de Self comme argument de type générique. Comparez le comportement souhaité pour ManagerB et SubManager avec les règles actuelles concernant Self, l’héritage et les arguments de type générique. La tâche est terminée lorsqu’une solution générale infère correctement la sous-classe concrète tout en préservant le comportement correct pour les méthodes enter redéfinies ou héritées.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
python
Domaine
tooling
Type d'issue
Fonctionnalité
Difficulté
5/5
Temps estimé
Plus d'une semaine
Activité
Calme
Clarté
Plutôt claire
Accessibilité débutants
35/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.