Skip to content
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

Annotating functions which don't raise exceptions #604

Open
pfalcon opened this issue Jan 7, 2019 · 7 comments
Open

Annotating functions which don't raise exceptions #604

pfalcon opened this issue Jan 7, 2019 · 7 comments
Labels
topic: feature Discussions about new features for Python's type annotations

Comments

@pfalcon
Copy link

pfalcon commented Jan 7, 2019

I would find it interesting to be able to annotate functions which never throw exceptions. "Never" can be either in the absolute sense, or in the sense of "checked exceptions", with some subset of exception designated as "unchecked" (details of this designation are perhaps up to the actual tool to process annotations).

Example of a function which absolutely never raises exceptions (well, at least per language semantics, particular implementations might still have means to break with some implementation-related exceptions):

def foo(l):
    if isinstance(l, list) and len(l) > 0:
        return l[0]

Example of a function which may throw (unchecked) exception if API contract is violated:

def foo(l: list):
    if len(l) > 0:
        return l[0]

So, hopefully these examples show that the notion does exist in Python.

Now the question how to annotate it. Following the existing NoReturn, it would be called NoRaise or NoThrow. "nothrow" terminology if familiar from C++ (and it seems to be replaced even there), so perhaps sticking with native Python terminology makes sense (but "throw" is native to Python too, re: exception handling with generators).

More interesting question is where to put that annotation. Taking the example above, a natural annotation would be:

def foo(l) -> Optional(Any), NoRaise:

And I was quite surprised that the usual "implicit tuple" syntax rule doesn't apply here, and the above is SyntaxError as of CPy3.7:

Python 3.7.1 (default, Oct 22 2018, 11:21:55) 
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def foo() -> list, int:
  File "<stdin>", line 1
    def foo() -> list, int:
                     ^
SyntaxError: invalid syntax

I wonder if that warrants a separate issue report. And whether it was a cunning design choice mere mortals need to decipher, because that seems too obvious thing to overlook with an implicit tuple syntax, again (neither problematic from forma grammar perspective, even in LL it's "->" expr ("," expr)* ":", which is trivial, Python has more complex grammar rules in many places.

So, all in all, now it would need to be written as:

def foo(l) -> (Optional(Any), NoRaise):

Which is of course not as pretty as without parens.

@pfalcon
Copy link
Author

pfalcon commented Jan 7, 2019

This is related to #71 . In a sense, it's complement of it: there, people want to annotate which exceptions are thrown. Supposedly, when these annotations are full and complete, functions which aren't annotated to raise any exception, are those which don't raise them. In reality of course, that level of coverage is not realistically achievable, so instead of explicitly marking all exceptions raised, thin RFC proposes to allow to explicitly mark those functions which don't raise exceptions.
More discussion of this approach: #71 (comment)

@pfalcon
Copy link
Author

pfalcon commented Jan 7, 2019

I had this idea in mind for some time, and the discussion in https://mail.python.org/pipermail/python-dev/2019-January/155998.html only gives an example that "nothrow" property is important to the Python language, whether those in powers of it admit, or not.

To recap, the talk in that thread (and with reference to the official docs at https://docs.python.org/3/reference/compound_stmts.html#try) is that

except E as N:
    body

is compiled as

except E as N:
    try:
        body
    finally:
        N = None
        del N

The reason why there're 2 statements "N = None; del N", is exactly for that (compiler-generated) code to have no-throw property - even if "body" (containing arbitrary user code) has del N statement itself.

But the reason the exception handler body needs to be wrapped into extra try-finally in the first place, is from the reservations that it may throw exceptions! If we'd know that it doesn't, we could avoid that overhead. And note that it doesn't even apply to "native code generation", but to bytecode generation, as done e.g. by CPython.

Of course, I don't call for CPython itself to do such an optimization. But there're many Python language implementations which try to advance performance state of the start for the language, and setting a common ground for them to annotate nothrow/NoRaise functions is worthy a task IMHO. With that idea I submit this ticket.

@ilevkivskyi
Copy link
Member

I would find it interesting to be able to annotate functions which never throw exceptions.

What exactly do you want? Do you just want to have a syntax to attach some extra info to (return) types that would be ignored by type checkers but may be used by some other tools? If yes, then this is essentially a duplicate of #600. With that you can write:

def func() -> Annotated[int, NoRaise]:
    ...

@pfalcon
Copy link
Author

pfalcon commented Jan 9, 2019

Thanks for reference to #600. I guess I could crash there with alternative syntax ideas ;-).

Beyond syntax for multiple annotations, it's also about choosing a specific annotation symbol for "no-throw" case. (I understand it unlikely to be added to "typing" module any type soon, so question is "if it would be added, what would it be", and python/typing is a kind of crossroads among different annotations projects, that's why I posted it here).

@ilevkivskyi
Copy link
Member

The choice of name is up to the team/tool that is going to support it, other type checkers will just ignore it. We can keep this open for a while to see who else is interested in this feature.

@SemMulder
Copy link

+1

@rmzr7
Copy link

rmzr7 commented Aug 9, 2021

Perhaps an approach to consider interesting approach is similar to what Swift does

def can_throw_errors(): ThrowsError[String]

@srittau srittau added the topic: feature Discussions about new features for Python's type annotations label Nov 4, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic: feature Discussions about new features for Python's type annotations
Projects
None yet
Development

No branches or pull requests

5 participants