Clarify namedtuple member rules
This PR clarifies the typing spec around named tuple fields and adds a few conformance test cases:
- fields are not being allowed to begin with underscore (and its interaction with
rename) - which members are counted/not counted as fields
Analogous restrictions are outlined in the spec for enums, but they are not present in the spec for named tuples.
Since this is adding new rules to the spec, should I open a discussion in the forum about it?
Yes, please start a thread in the discussion forum. I don't think these changes will be controversial, but we should let the community weigh in.
Posted https://discuss.python.org/t/adding-namedtuple-member-naming-rules-to-the-typing-spec/88882
Hi, I'm not sure if this is the right place for this discussion—if not, please let me know (and sorry!).
I originally raised this issue in pyright, but I think here might be a more appropriate place to discuss it.
Suppose I do have members that start with "_":
NT5 = namedtuple("NT5", ["abc", "_1"], rename=True) # OK
NT5(abc="", _1=1) # OK
How can I provide type annotations for them in a .pyi file?
class NT5(NamedTuple):
abc: str
_1: int # <- HOW ?
-
Can we allow an exemption for field names matching the pattern
_{index}(e.g.,_1)? -
Or, could the spec recommend that even if a type checker rejects a field name like
_1, it should still provide type analysis for that field:from collections import namedtuple from typing_extensions import TYPE_CHECKING, NamedTuple, assert_type if not TYPE_CHECKING: NT5 = namedtuple("NT5", ["abc", "_1"], rename=True) # OK else: class NT5(NamedTuple): abc: str _1: int # type: ignore # # ^ suppress false positive nt5 = NT5(abc="", _1=1) # should be ok assert_type(nt5[0], str) assert_type(nt5[1], int) # should be ok assert_type(nt5.abc, str) assert_type(nt5._1, int) # should be okCurrently, mypy and pyright behave differently on this point.
I'm with Eric on the linked issue that this doesn't make sense to support. If you use different types on both arms of if TYPE_CHECKING, you'll see things like this happen.
Can we allow an exemption for field names matching the pattern
_{index}(e.g.,_1)?
No, unless the runtime has that exemption. We should match the runtime behavior.
could the spec recommend that even if a type checker rejects a field name like
_1, it should still provide type analysis for that field
Type checkers could do that but I don't think it should be the spec's business to decide how type checkers should proceed once they've detected a type error.
And to give a constructive suggestion for your use case: Maybe just don't use namedtuple? If you require names that start with an underscore, it's not the right tool for the job.
Fair enough! Thanks for your quick response! 👍
No, unless the runtime has that exemption
It would be nice if typing.NamedTuple also supported a rename=True option at runtime (I think that's outside the scope of this issue discussion 😂).
And to give a constructive suggestion for your use case: Maybe just don't use namedtuple? If you require names that start with an underscore, it's not the right tool for the job.
I can't control the code on the other end; I can only provide type annotations for it (like in a .pyi file).
The workaround I've come up with for now is:
class NT5(tuple[str, int]):
abc: str
_1: int
__match_args__ = ("abc", "_1")
def __new__(cls, abc: str, _1: int) -> Self: ...
But this isn't as straightforward as typing.NamedTuple.
Anyway, thanks for your help! 🙏
The change looks good to me, but you'll need to update the branch, resolve merge conflicts, and re-run the tests with the latest versions of the type checkers.
Thanks for the contribution!
Ok, I've rebased and updated the results.
The PR looks good to me, but we need to wait for @carljm and @JukkaL to review and sign off on the change here.