Skip to content

Mypy doesn't handle namedtuples with dynamic fields #848

Closed

Description

@agentydragon

Given:

A = ['x']
T = namedtuple('T', A)

We get: error: List literal expected as the second argument to namedtuple(). Mypy should be able to handle this somehow, e.g., by allowing any operation on T instances if the second argument to namedtuple is not a list literal.

Activity

  1. JukkaL commented on Aug 23, 2015

    @JukkaL
    Collaborator

    It would be reasonable to fall back to a "namedtuple with unknown item names" in a case like above in a weak type checking mode. I'm still unsure about how this weak mode could be enabled for particular files.

    In the normal type checking mode we'd probably still want to give a warning about the above code.

  2. o11c commented on Aug 24, 2015

    @o11c
    Contributor

    I actually think the long-term correct approach will be to track the values of constants.

  3. elazarg commented on Sep 3, 2016

    @elazarg
    Contributor

    @JukkaL shouldn't there be basic constant propagation, just before the semantic analysis? It will be most helpful, with ints and strings. And it shouldn't do anything tricky - no fixpoint or anything; just a single pass. At least it can be another optional feature.

  4. JukkaL commented on Sep 5, 2016

    @JukkaL
    Collaborator

    Yeah, it seems that this should be possible for ints, strings and booleans and maybe tuples and lists, at least within a single module. So these could be okay:

    x = ['x']
    X = namedtuple('X', x)
    y = 'a b c'
    Y = namedtuple('Y', y)
    

    We know with certainty the values of x and y, because nothing can change them after the initialization.

    However, this is trickier:

    # a.py
    y = 'a b c'
    
    # b.py
    from a import y
    Y = namedtuple('Y', y)   # but maybe another module has modified y?
    

    And what about this:

    from m import f
    y = 'a b c'
    f()  # hmm could this modify y if it imports the current module?
    Y = namedtuple('Y', y)
    

    Constant propagation could work somewhat like this:

    • On each assignment, keep track of the constant value of a variable, if the right hand side is a constant.
    • If we encounter a destructive operation, change (some) constants back to variables. Examples:
      • Referring to a variable using global should mark a variable as not constant.
      • The second assignment to a constant marks it as a variable (except perhaps if this replaces the original value on all code paths).
      • Calling a user-defined function should likely mark at least all currently known constants that refer to mutable objects such as lists as not constants.
    • Outside a module mark everything in the module as not constant, because we can't easily know whether another module might have touched something.

    Before spending more time on this I'd like to see more use cases where this would be useful. Named tuples are clearly a potential example, but that is a pretty narrow use case.

  5. elazarg commented on Sep 5, 2016

    @elazarg
    Contributor

    Tracking types is a form of constant propagation, and the questions you raise apply to class names, function definitions etc. We already assume the code behaves nicely.

    (If I understand your description correctly, you want the join operation on different constants to result in Top. That's the sensible thing to do; but maybe there's a place for a plugin here too?).

  6. gvanrossum commented on Sep 5, 2016

    @gvanrossum
    Member

    An example like the OP's here is usually a proxy for a more dynamic source of field names, which won't work even if we implement constant propagation.

  7. elazarg commented on Sep 5, 2016

    @elazarg
    Contributor

    I used code similar to the example just to separate the fields part, which is very noisy, from the inheriting class, without introducing a temporary type name.

  8. sjaensch commented on Oct 19, 2016

    @sjaensch

    Note that this error is also triggered when passing a tuple instead of a list literal:

    T = namedtuple('T', ('a', 'b'))
    

    Is this within the scope of this issue or should I open a separate one?

  9. elazarg commented on Oct 19, 2016

    @elazarg
    Contributor

    This has been fixed in #2262, in the repo version. It is unrelated to this issue.

  10. gvanrossum commented on Oct 20, 2016

    @gvanrossum
    Member

    This just never will happen (the dynamic version).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions