Skip to content

Disallow type[NoReturn] #11291

Open
Open

Description

@DetachHead
from typing import NoReturn

# Incompatible types in assignment (expression has type "object", variable has type "Type[NoReturn]")  [assignment]
foo: type[NoReturn] = NoReturn 

https://mypy-play.net/?mypy=latest&python=3.10&gist=ae2c337cc5fdfe2a3022aaae54903012

Activity

  1. KotlinIsland commented on Oct 7, 2021

    @KotlinIsland
    Contributor

    NoReturn is a type realm type, not an instance of the runtime type. It's actually an instance of _SpecialForm.

  2. JelleZijlstra commented on Oct 9, 2021

    @JelleZijlstra
    Member

    We should perhaps reject type[NoReturn]. I'm not sure what that would even mean.

  3. DetachHead commented on Oct 9, 2021

    @DetachHead
    ContributorAuthor

    i don't see why it'd be any different to something like this, which works

    foo: type[int] = int

    my use case is a class with reified generics at runtime. something like this:

    T = TypeVar("T")
    
    class Foo(Generic[T]):
        t: type[T]
        def foo(self) -> T:
            if isinstance(self.t, type(NoReturn)):
                raise TypeError("type is NoReturn")
            return self.t()
  4. erictraut commented on Oct 9, 2021

    @erictraut

    NoReturn isn't a valid runtime type. You should think of it as equivalent to a Union with no subtypes. It doesn't make sense to use it within a runtime isinstance check, just like it would make sense to use Union, Any, or TypeGuard within an isinstance check.

  5. DetachHead commented on Oct 9, 2021

    @DetachHead
    ContributorAuthor

    you can use Union in an isinstance check though (as of 3.10 i believe)

    >>> isinstance(1, Union[int,str])
    True
    
  6. erictraut commented on Oct 9, 2021

    @erictraut

    That change was added in 3.10 to support isinstance(1, int | str), which is the equivalent of isinstance(1, (int, str)). Those both have valid meanings. But isinstance(1, Union) and isinstance(1, NoReturn) do not.

  7. erictraut commented on Oct 9, 2021

    @erictraut

    For what it's worth, the runtime type of the expression type(NoReturn) is actually typing._SpecialForm.

    >>> type(NoReturn)
    <class 'typing._SpecialForm'>
    
    >>> type(Union) == type(NoReturn)
    True
    
    >>> type(ClassVar) == type(NoReturn)
    True
    
    >>> type(Final) == type(NoReturn)
    True
  8. changed the title [-]`NoReturn` type incorrectly widened to `object`[/-] [+]Disallow `type[NoReturn]`[/+] on Oct 9, 2021
  9. JelleZijlstra commented on Oct 9, 2021

    @JelleZijlstra
    Member

    I am going to repurpose this issue to track warning against usage of type[NoReturn].

    For what it's worth, pyright doesn't warn against this either (or perhaps I don't have the right option on).

  10. DetachHead commented on Oct 9, 2021

    @DetachHead
    ContributorAuthor

    That change was added in 3.10 to support isinstance(1, int | str), which is the equivalent of isinstance(1, (int, str)). Those both have valid meanings. But isinstance(1, Union) and isinstance(1, NoReturn) do not.

    true, though i would expect isinstance(1, NoReturn) to be equivalent to isinstance(1, ()), but obviously that's out of mypy's control

  11. erictraut commented on Oct 9, 2021

    @erictraut

    You're attempting to use NoReturn well outside of the use case for which it was designed, so it's not surprising that it doesn't work in these cases. NoReturn is a construct that is meant to be used at type checking time to designate the return type of a function that never returns — e.g., executes an infinite loop, raises exceptions on all code paths, or terminates the process. If you use it for its intended purpose, it works fine.

  12. DetachHead commented on Oct 9, 2021

    @DetachHead
    ContributorAuthor

    as you said on microsoft/pyright#2299 (comment) there are more meanings to NoReturn, such as empty unions. do you think that Never type you mentioned, if added, would support use cases like mine?

  13. erictraut commented on Oct 9, 2021

    @erictraut

    I can't say because I don't understand what you're trying to do, and I can't tell from the sample above. Could you provide more context? The best I can discern is that you're trying to use NoReturn as a runtime sentinel of sorts, but I'm not sure why None wouldn't be a logical choice.

    In the previous discussions about Never, the thought was that it would simply be an alias for NoReturn, so the two would work in the same manner.

  14. DetachHead commented on Oct 9, 2021

    @DetachHead
    ContributorAuthor

    not that big a deal i guess. in my case i can easily just use None, it's just that i felt NoReturn was more suitable given the context - a class with a method that instantiates a T or throws an error

  15. KotlinIsland commented on Oct 9, 2021

    @KotlinIsland
    Contributor

    also disallow type[Union] type[Generic] type[ClassVar] etc.

  16. randolf-scholz commented on Jul 29, 2023

    @randolf-scholz
    Contributor

    Maybe this should be reconsidered, as Never, is now officially both considered the bottom type and an alias of NoReturn.

    The bottom type itself is supposed to have no members, so, one might consider raising an error whenever an instance of Never is produced, but using the type in and of itself should just work like any other type, no? x: type[Any] = ... is also allowed, after all.

    Never might also result from incompatible intersections, as is currently discussed (e.g. int & str=Never). So possibly in the future, isinstance(x, A & B) and issubclass(x, A & B) will be allowed, just like isinstance(x, A | B) and issubclass(x, A | B) are today.

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