Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enum and NamedTuple should not allow extra keywords #16521

Open
sobolevn opened this issue Nov 18, 2023 · 5 comments
Open

Enum and NamedTuple should not allow extra keywords #16521

sobolevn opened this issue Nov 18, 2023 · 5 comments
Assignees
Labels

Comments

@sobolevn
Copy link
Member

This code produces no error:

from typing import NamedTuple

class A(type): ...

class Foo(NamedTuple, metaclass=A, x=1):
   x: int
   
   
from enum import Enum

class E(Enum, x=1):
    a = 1

It is much easier with the NamedTuple, but, I think that enum might allow some keywords like boundary=STRICT.

So, I think that I will split the PRs for these two problems.

Refs #16432

@sobolevn sobolevn added bug mypy got something wrong topic-named-tuple topic-enum labels Nov 18, 2023
@sobolevn sobolevn self-assigned this Nov 18, 2023
@erictraut
Copy link

Do you happen to know why subclasses of NamedTuple aren't allowed to use a custom metaclass? I don't think NamedTuple itself uses a custom metaclass, so I don't see why there would be a metaclass conflict.

class Foo(NamedTuple):
    x: int
print(type(Foo)) # <class 'type'>

As for Enum, the typeshed stub enum.pyi defines EnumMeta.__new__ as follows:

class EnumMeta(type):
    if sys.version_info >= (3, 11):
        def __new__(
            metacls: type[_typeshed.Self],
            cls: str,
            bases: tuple[type, ...],
            classdict: _EnumDict,
            *,
            boundary: FlagBoundary | None = None,
            _simple: bool = False,
            **kwds: Any,
        ) -> _typeshed.Self: ...
    elif sys.version_info >= (3, 9):
        def __new__(
            metacls: type[_typeshed.Self], cls: str, bases: tuple[type, ...], classdict: _EnumDict, **kwds: Any
        ) -> _typeshed.Self: ...
    else:
        def __new__(metacls: type[_typeshed.Self], cls: str, bases: tuple[type, ...], classdict: _EnumDict) -> _typeshed.Self: ...

The **kwds: Any parameter implies that it accepts arbitrary keyword arguments. Any idea why? Perhaps this is just a bug in typeshed.

@AlexWaygood
Copy link
Member

I don't think NamedTuple itself uses a custom metaclass

That's incorrect; it does: https://github.com/python/cpython/blob/1a969b4f55f92a17bec82ce0366021a53afdb2c3/Lib/typing.py#L2715

@erictraut
Copy link

erictraut commented Nov 19, 2023

Ah, that explains it. Then I'd argue that the typeshed definition of the NamedTuple class should reflect that it has a custom metaclass.

@type_check_only
class _NamedTupleMeta(type): ...

class NamedTuple(tuple[Any, ...], metaclass=_NamedTupleMeta):
    ...

This would more correctly reflect the runtime behavior, and then type checkers wouldn't need to hardcode that knowledge. Their normal metaclass conflict rules would apply.

@AlexWaygood
Copy link
Member

Then I'd argue that the typeshed definition of the NamedTuple class should reflect that it has a custom metaclass.

That sounds reasonable to me — though the full semantics of NamedTuple are inexpressible in typeshed's stubs. (At runtime, NamedTuple is a function, not a class — although, unlike nearly all other functions, you can inherit from it, it never appears in a class's __bases__ tuple.) So there's no getting around the fact that NamedTuple will always have to be heavily special-cased by type checkers — this is mostly why we haven't bothered trying to precisely reflect all details of NamedTuple's runtime implementation in typeshed's stubs.

@sobolevn
Copy link
Member Author

sobolevn commented Nov 19, 2023

This issue has some mypy internal specifics, because we have semanal_enum and semanal_namedtuple, which are a bit different and do not follow the same path as regular classes. Maybe they should (at least for this specific step)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants