-
Notifications
You must be signed in to change notification settings - Fork 258
A NoReturn
type is needed
#165
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
A simpler approach would be to just teach tools about common things that don't return such as
This has the benefit of making things more explicit for the reader and getting a runtime error if the expectation doesn't hold. Functions that don't return are special beasts anyway, and maybe explicit is better than implicit in this case. |
FWIW, I've never liked functions that don't return. Other tools are also On Thu, Oct 29, 2015 at 9:49 PM, Jukka Lehtosalo [email protected]
--Guido van Rossum (python.org/~guido) |
Requiring @overload
def foo(arg: int) -> int: ...
@overload
def foo(arg: str) -> NoReturn: ...
# other overloads with different return types
def bar(arg: Union[int, str]):
x = foo(arg) # x has type `int`
Needing to special-case like that is a sign of bad design. And auto-dedent is never going to be perfect anyway, it's more important to have completeness. |
(I apologize for repeating things I've said on related discussions): |
We're moving ahead with Regardless of whether it's the same as |
I don't think I have suggested to spell it Unless there is a difference, in which case it's interesting to know about it (for example, if |
I think there's a decent argument to be made that |
What is the point of this? The former can be easily done with def exit(code=1):
raise SystemExit(1) I don't really see the point of the latter. |
It is needed to specify that a function never returns, so that local code paths involving such a call can be analyzed correctly. We considered the |
That's unfortunate. Why not distinguish between "may raise" and "will raise"? def may_raise(...):
if random():
raise ...
def will_raise():
raise ... I do worry that a lot of useful information that could be in the stub files is being thrown away. |
I think "no return" and "will raise" are different guarantees, and def foo() -> NoReturn:
do_some_stuff() is very clear, and we can also verify that What is the problem with this addition? |
There are three ways to not return.
def non_terminate():
while True:
pass Should the above be marked as "What is the problem with this addition?" |
FTR, we (Semmle) already do sophisticated control flow analysis, including pruning edges following calls to functions that do not return. No annotation is needed. Whether a function returns or not is purely a function of its control flow graph and the call graph. No type information is needed. |
It's the empty type. I think we know pretty well how it works.
|
Given it is unnecessary, adding it merely adds extra work for those of us implementing checkers. |
There are two answers for this:
1. No matter how you spell it, if you allow expressing this specification
then you run into the same questions.
2. I don't see what's wrong with the straight forward type-theoretical
answers (only empty lists are allowed, NoReturn is a subtype of everything,
etc.), plus whatever user friendly special casing you deem appropriate. In
particular, NoReturn can be treated uniformly as the empty Union.
I agree that a proof of concept will be helpful.
|
IMO, pruning edges in the CFG is not really a type checking operation, and so NoReturn shouldn't really be a type. That way the typing rules are kept simpler (CFG pruning should precede type checking) and the distinction between stubs and normal code is diminished, which again simplifies things. |
Since the only use of this type is as a return type, perhaps it can be defined not to work elsewhere; then you can treat it in the same way you'd treat |
I think NoReturn should be a type. So far we've maintained the invariant (in mypy at least -- not sure if this is discussed in the PEP) that everything you need to know about a function is contained in its type signature. This is important for the ability to do local-only inference and for specifying the behavior of too-dynamic-to-understand pieces of code. I think this is an important invariant to maintain. From another lens, it seems reasonable to me that a user would want to be able to talk about functions that don't return as first-class citizens, and you can't do that unless you have some sort of NoReturn type. |
Since |
Well, we haven't even had a single mypy release supporting it. So I don't
know how "non-experimental" it is.
In any case, the reason it's in mypy_extensions is mainly that we want to
support it even on places that can't upgrade typing.py easily, e.g.
existing Python 3.5.3 installations -- it'll be a long time before 3.5.4
will be released. (And ditto for 3.6.1 actually, I think it's too late to
add it given that release schedule.)
|
I am referring here to what I have heard from @ddfisher in python/mypy#2920 (comment)
Yes, this is a good reason. |
I don't actually know what definition of "experimental" Jukka and David are using. Maybe it means a mypy feature that can be changed incompatibly or withdrawn in any future release? In any case, it's right that a mypy extension is not always a proposal for a typing feature (even though the current two, TypedDict and NoReturn, both are). I'm guessing also that the design of NoReturn at this point is uncontroversial so we can start updating the PEP and the next version of typing.py; but IIUC there are still many details for TypedDict that haven't been settled yet. |
NoReturn is not experimental in the sense that it seems pretty uncontroversial and it has no major open issues. I still consider TypedDict quite experimental, however. |
As discussed in python/typing#165.
So we also need changes to mypy that allow |
Here is a simple PR to allow this python/mypy#3041 (one line plus tests). |
In order to perform branch analysis, it is necessary to know which calls will never return normally. Examples are
sys.exit
(which always returns via exception) andos.exit
(which never returns).NoReturn
should drop out ofUnion
and dominateIntersection
.To manually mark a branch as dead, use
cast(NoReturn, expression)
in an expression context, orraise
in statement context.This can also be used as the return type of
__init__
or__new__
or the metaclass's__call__
to indicate types that can't be normally instantiated.The text was updated successfully, but these errors were encountered: