mypy treats Enum's __call__ as calling __init__
Nadie ha tomado este issue todavía.
- Lenguaje dominante
- Python
- Estrellas
- 20.6k
- Forks
- 3.3k
- Merge medio
- 1 d 18 h
- PR fusionados (30 d)
- 54
Descripción
Bug Report
The standard library's enum.Enum has a class method† __call__ that can take a member of the enumeration, or a member's value, and returns that member in either case.
It is also possible to override the enumeration's __init__. The Python docs show an example that uses this to attach additional data to the members.
If this is done, mypy reports an error when calling the class, expecting the call to follow the signature of __init__.
To Reproduce
This is a stripped-down example of the one from the Python docs. It runs successfully (the asserts pass).
from enum import Enum
class Planet(Enum):
MERCURY = (3.303e+23, 2.4397e6)
def __init__(self, mass: float, radius: float) -> None:
self.mass = mass
self.radius = radius
mercury = Planet(Planet.MERCURY)
assert mercury is Planet.MERCURY
mercury = Planet((3.303e+23, 2.4397e6))
assert mercury is Planet.MERCURY
Expected Behavior
One possibility, of course, would be for mypy to produce no errors. But this is a somewhat odd situation, so failing that, there should be a way‡ to signal to mypy that this is what's happening.
I couldn't find any such way, short of # type: ignore. Overriding and annotating __call__ didn't work:
@classmethod
def __call__(cls, value_or_member: Planet | tuple[float, float]) -> Planet:
return type(Enum).__call__(cls, value_or_member)
This definition type-checks fine, but it doesn't affect the previous errors. (It's worth noting that this doesn't seem to actually override anything; the calls bypass it and go for the metaclass.)
It is possibly to go the other way and adapt the code to suit mypy, by making the use of __call__ explicit. This type-checks fine:
mercury = Planet.__call__(Planet.MERCURY)
assert mercury is Planet.MERCURY
mercury = Planet.__call__((3.303e+23, 2.4397e6))
assert mercury is Planet.MERCURY
But that seems backwards to me.
Searching for existing issues or Q&As didn't turn up anything that looked like it matched. There are some issues related to the functional API (e.g. #10469, a dupe of #6037), which also uses __call__, but mypy specifically supports that. There's also #15024, but that appears to be about __call__ on an instance.
Actual Behavior
main.py:10: error: Missing positional argument "radius" in call to "Planet" [call-arg]
main.py:10: error: Argument 1 to "Planet" has incompatible type "Planet"; expected "float" [arg-type]
main.py:13: error: Missing positional argument "radius" in call to "Planet" [call-arg]
main.py:13: error: Argument 1 to "Planet" has incompatible type "tuple[float, float]"; expected "float" [arg-type]
Found 4 errors in 1 file (checked 1 source file)
Your Environment
- Mypy version used: 0.982 locally, latest on playground
- Mypy command-line flags and config options: none
- Python version used: 3.11.6 locally, 3.12 on playground
† Technically it's implemented on the metaclass rather than by @classmethod.
‡ Preferably something obvious, to be PEP 20-compliant. 😉 ("There should be one—and preferably only one—obvious way to do it. Although that way may not be obvious at first unless you're Dutch." Maybe the problem is that I'm not Dutch?)
Guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Línea de trabajo
Comienza con el reproductor de Playground enlazado y las llamadas a Enum reportadas que implican un init sobrescrito. Rastrea cómo mypy deriva la firma invocable para una clase Enum y compárala con el comportamiento de call de la metaclase descrito en el issue. Se considera terminado cuando el reproductor pasa la comprobación de tipos sin un ignore y se conserva la comprobación existente de los argumentos de init.
Escrito por el modelo de indexación a partir del texto del issue.
Evaluación
- Stack tecnológico
- python
- Área
- compilers, devtools
- Tipo de issue
- Error
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Estado de actividad
- Estancado
- Claridad
- Bastante claro
- Aptitud para principiantes
- 45/100