Skip to content

Revert namedtuple member rules #10461

Closed as not planned
Closed as not planned

Description

@WSH032

If you have a question about a behavior that you’re seeing in pyright, consider posting to the Pyright discussion forum.

Is your feature request related to a problem? Please describe.

#10351 disallows namedtuple members whose names start with an underscore (_), but consider the following case:

from collections import namedtuple
from typing import TYPE_CHECKING, NamedTuple

if not TYPE_CHECKING:
    # Import from extension module
    Foo = namedtuple("Foo", ["_0", "_1", "_2"], rename=True)

else:
    # Provide type annotations (i.e., `.pyi` files)
    class Foo(NamedTuple):
        _0: int
        _1: int
        _2: int


# work normally
foo = Foo(_0=0, _1=1, _2=2)
print(foo[0])
print(foo._0)

I know that for typing.NamedTuple, member names starting with an underscore are not allowed anyway. However, collections.namedtuple does allow this (with rename=True).

In my use case, I do have a namedtuple with members prefixed by _ (for example, imported from an extension module), and I want to provide type annotations for it (as you would in a .pyi file). This used to work fine, but after #10351, pyright rejects this pattern.

I know I can use pyright: ignore to suppress these errors, but #10351 also affects other IDE features:

# works, but pyright complains: No parameter named "_0"
foo = Foo(_0=0, _1=1, _2=2)

# `x, y, z` should be `int`, but pyright says they are `Never`
match foo:
    case (x, y, z):
        pass

Describe the solution you’d like

Considering that python/typing#1979 is still just a draft, and that members starting with an underscore are valid at runtime, I request that the changes in #10351 be reverted.

Alternatively, please downgrade this to a warning so that it does not affect other IDE features.

Activity

  1. erictraut commented on May 18, 2025

    @erictraut
    Collaborator

    The recent change aims to better align pyright's behavior with runtime behavior. Regardless of whether this behavior is mandated by the spec (as it likely will be in the near future), it's generally a good idea to model runtime behaviors accurately.

    I don't think your use case is good justification for modeling NamedTuple incorrectly. In your code above, you're effectively lying to the type checker through the use of a TYPE_CHECKING conditional. Your runtime code differs from the code you're asking the type checker to analyze. I recommend against doing this because it leads to problems like the one you're seeing here.

    If you have the option of changing your code to avoid using fields that start with underscores, I recommend doing that.

  2. WSH032 commented on May 18, 2025

    @WSH032
    Author

    Okay. However, is it possible to make this rule not affect other IDE features? For example, as I mentioned above:

    # works, but pyright complains: No parameter named "_0"
    foo = Foo(_0=0, _1=1, _2=2)
    
    # `x, y, z` should be `int`, but pyright says they are `Never`
    match foo:
        case (x, y, z):
            pass

    This used to work as expected.

    I'm fine with manually adding pyright: ignore for these fields, but I hope it won't affect other IDE features (as it did before). I guess it's just a matter of removing these return statements?


    BTW, typing#1979 allows collections.namedtuple to have members starting with _ (with rename=True), but pyright currently rejects this.

  3. WSH032 commented on May 18, 2025

    @WSH032
    Author

    This might be a bit off-topic—perhaps I should ask this in python/typing#1979 instead?

    If type checker refuses to analyze members like _0, but still allows rename=True (which renames invalid members to _{index}), how can I provide type annotations for them in a type stub (.pyi file) when I cannot modify the code?

  4. WSH032 commented on May 18, 2025

    @WSH032
    Author

    Okay. However, is it possible to make this rule not affect other IDE features?

    As evidence, after mypy rejects underscore-named members, it still analyzes those members correctly:

    Image

    But pyright refuses to analyze them:

    Image

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions