Skip to content

type alias to union is invalid in runtime context #5354

Closed

Description

@glyph

As of MyPy 0.620:

# aliases.py
from typing import Union
x = Union[int, str]
def y() -> None:
    print(x)

This produces the error:

aliases.py:5: error: The type alias to Union is invalid in runtime context

However, type variables are quite useful in "runtime context" in order to implement things like serialization; for example, https://github.com/Tinche/cattrs makes heavy use of introspecting Union objects to determine which types are valid.

Those of us hacking around code like this have known that the current API for enumerating the members of a Union was private & internal, and not for public consumption, so we've been keeping pace with implementation details while waiting for a public API to get this information to emerge. These errors are super annoying because they imply the only future-looking direction for this type of code will involve manually re-typing the same list of types for every union alias defined anywhere.

Can this change be backed out? If not, is there any guidance for how to define a Union such that it is introspectable?

Activity

  1. glyph commented on Jul 13, 2018

    @glyph
    Author

    I guess this was a result of #5221 ?

  2. ilevkivskyi commented on Jul 14, 2018

    @ilevkivskyi
    Member

    From mypy's point of view it is hard to distinguish whether this is intentional or a mistake. The point is that this change discovered actual errors in real code, so I am not so keen to revert this change even though it was unintentional.

    On the other hand, such aliases will likely fail at runtime in most scenarios where it is an error quite soon. So the value of discovering them statically is not so big.

    One possible way is to turn the type aliases into variables by giving them explicit types:

    X: Type[Any] = Union[A, B, C]
    print(X)  # OK

    but then X can't be used in annotations. I would like to see more input here before making a decision here.

  3. added a commit that references this issue on Jul 16, 2018
  4. glyph commented on Jul 16, 2018

    @glyph
    Author

    I can see your point as far as this being a useful thing to check most of the time. And given that we're relying on private implementation details of typing I know it's my own responsibility to keep up with such changes :). Mainly what I'm looking for here is a non-horrible way forward that translates to something that will be close to a one-day public API. For example, I'm very curious what you mean by

    such aliases will likely fail at runtime in most scenarios where it is an error quite soon

    what will make this the case?

    (previous complaint Type[Any] approach deleted, that was an error that was fixed in 0.620)

  5. glyph commented on Jul 16, 2018

    @glyph
    Author

    OK, now that I'm actually testing on the correct version:

    x = Union[int, str]
    z: Type[Any] = x
    

    still says "The type alias to Union is invalid in runtime context", which means any type I want to use in this way needs to be fully typed out twice.

  6. glyph commented on Jul 16, 2018

    @glyph
    Author

    Is this a workable way to express this for the time being?

    x = Union[int, str]
    x_runtime: Type[Any]
    if not TYPE_CHECKING:
        x_runtime = x
  7. JelleZijlstra commented on Jul 16, 2018

    @JelleZijlstra
    Member

    What if you do x: Type[Any] = Union[int, str]?

  8. ilevkivskyi commented on Jul 17, 2018

    @ilevkivskyi
    Member

    After a discussion with @JukkaL I think it is OK to allow the original example. This is however relatively low priority since there are workarounds like using Type[Any] and/or # type: ignore and/or if TYPE_CHECKING: ....

  9. 15 remaining items

  10. bwo commented on Aug 6, 2019

    @bwo
    Contributor

    More basically, if either of these works, shouldn't both?

    def spam(u: Type[Union]) -> bytes:
        return b''
    
    
    spam(convenient_union)
    spam(Union[int, str])

    We have codegen that creates functions that return Union[…] (for generative testing purposes). I suspect this is similar to the use @glyph has.

  11. removed their assignment
    on Aug 12, 2019
  12. Tishka17 commented on Sep 8, 2019

    @Tishka17

    Hello,

    I'm working on library which helps to create dataclass instances from dict (a bit like pydantic or marshmallow). The main idea is to have funciton like parse(data: Any, type_: Type[T]) -> T. It analyses what actual type as provied and does parsing based on detected infomation.

    I am working on new feature polymrophic parsing and the use case is just to call parse(data, Union[ClassA, ClassB]). It works ok, but mypy detects an error The type alias to Union is invalid in runtime context. It is very strange to me that i cannot provide such a type explicitely, as at a same time i can detect it from annotations (and i do it for dataclass fields)

    Minimal exmaple is:

    from typing import Union, TypeVar, Type, Any
    
    mytype = Union[str, int]
    
    T = TypeVar("T")
    
    
    def parse(value: T, type_: Type[T]) -> None:
        print(type_, value)
    
    
    parse(1, mytype)  # error: The type alias to Union is invalid in runtime context

    Some code from my library (reagento/adaptix@adc6f53)

    What can i do with this?

  13. ilevkivskyi commented on Sep 9, 2019

    @ilevkivskyi
    Member

    @Tishka17 The simplest way is to # type: ignore this for now and wait for the issue to be resolved.

  14. Tishka17 commented on Sep 17, 2019

    @Tishka17

    @Tishka17 The simplest way is to # type: ignore this for now and wait for the issue to be resolved.

    Any ideas when it can be fixed?

  15. ilevkivskyi commented on Sep 17, 2019

    @ilevkivskyi
    Member

    Any ideas when it can be fixed?

    As soon as someone submits a PR (i.e. no estimate).

  16. wsanchez commented on Oct 28, 2019

    @wsanchez

    Any chance we can give this message its own error code instead of [misc]?

  17. JukkaL commented on Oct 28, 2019

    @JukkaL
    Collaborator

    Any chance we can give this message its own error code instead of [misc]?

    Can you explain your use where this would be helpful?

  18. wsanchez commented on Oct 29, 2019

    @wsanchez

    Well [misc] lumps a lot of things into one bucket, which sort of defeats the point of having error codes.

    If you look at this diff: https://github.com/twisted/klein/pull/301/files, you'll see I added a ton of #typedef ignore[misc] in the code, and I'd much rather have simply disable this warning globally in the config file, but I don't want to disable everything associated with [misc].

    This is due to a hack @glyph added here to help mypy associate Zope interfaces with the classes that implement them.

  19. chadrik commented on Oct 29, 2019

    @chadrik
    Contributor

    @JukkaL IIRC, there were plans to be able to configure which error codes to ignore -- globally and per module -- so further classification of errors in the "misc" bucket will become even more significant after that feature arrives, since ignoring the entire "misc" error code would be ill advised.

    There are a handful of errors like this one that fall into the "you used a type object incorrectly at runtime" grouping, which could perhaps share the same error code.

  20. wsanchez commented on Oct 29, 2019

    @wsanchez

    @chadrik Heh I assumed that feature already existed. :-)

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions