-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
type refinement in function call #5206
Comments
This could perhaps be done with literal types in the future, something like this:
|
That's pretty verbose though..
…On Tue, Jun 12, 2018, 08:08 Jelle Zijlstra ***@***.***> wrote:
This could perhaps be done with literal types in the future, something
like this:
@overload
def check(x: int) -> Literal[True]: ...
@overload
def check(x: str) -> Literal[False]: ...
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#5206 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/ACwrMvKHVxoXmkkO8mpf8aeuMzX0ZZmTks5t79lfgaJpZM4UkjNs>
.
|
I've had this issue recently too. I definitely think Mypy would benefit from some way to refine types by following conditional paths. Unfortunately implementing that kind of logic is well beyond my expertise. AFAIK you would still have the problem if you wrote |
Here I copy the relevant link from one of the above issues I just closed microsoft/TypeScript#1007 |
I've found the TypeScript type guards to be a very useful concept, especially when dealing with structured data coming in over the network (e.g. a JSON API). This is the TS syntax: function isNumber(x: any): x is number {
return typeof x === "number";
}
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
} That syntax is probably not feasible in Python (I don't think Maybe a placeholder type ( Maybe |
Grammatically type annotations are just expressions, so something like this:
is grammatically valid. However, it has some possible problems:
In contrast, your In any case, the bigger difficulty for getting something like this into mypy is going to be first convincing people that it's a good idea and second getting an implementation written and merged into mypy. Syntax is easy. :) |
What would be a reason against this (besides implementation complexity)? It considerably helps when dealing with external data, because it combines runtime validation and static type checks. |
@JelleZijlstra would implementation be easy too? Could you not typecheck a call
by just virtually (or literally) replacing it with |
How about a bundled do-nothing decorator for the syntax?
|
@DustinWehr a literal |
@lubieowoce I would've guessed the static analyzer's use of |
I see that this was already suggested in #7870 by @gantsevdenis. Good idea imo.
|
@DustinWehr i guess i was being a bit pedantic :) btw if you want to take a look, the logic for isinstance checks seems to start in Lines 3798 to 3814 in 9101707
i think that's indeed the case if i'm understanding you correctly. in general
seems to be handled with two different typings of variables, one for the Lines 3953 to 3971 in 9101707
i imagine that a hacky proof of concept impl of type guards could simply add an |
Repeating the name
On a related note, a still-open typescript feature request for is to extend type guards to multiple arguments. It would be nice to cover that use case as well, e.g.
|
Why do we need a boolean return? A direct solution to this issue would be to implement type-guard functions to return the checked argument, like a checked cast in fact: def as_integer(x) -> int:
if not isinstance(x, int):
raise ValueError("not an int")
return x If-else logic can simply be achieved using try-catch-else blocks. try:
checked_x = as_integer(x)
except ValueError:
...
else:
... # use checked_x as an integer in this block This checked-cast functions can also be used as expression, which is convenient for case like Checked-cast functions could also return an def as_integer(x) -> Optional[int]:
return x if isinstance(x, int) else None
if (checked_x := as_integer(x)) is not None :
...
else:
... Moreover, if several parameters needs to be type-checked, a tuple returns can do the job: def check_int_and_str(x, y) -> tuple[int, str]:
if not isinstance(x, int) or not isinstance(y, str):
raise ValueError("bad types")
return x, y
checked_x, checked_y = check_int_and_str(x, y) But yes, this solution imply to assign an additional variable (with an additional name), so it's heavier, but it already works out of the box and do the job, no PEP required. |
That's being said, if boolean type-guard functions have to be implemented in the language (and I would be happy to use them to replace my heavier checked-cast), why not using PEP 593 from typing import Annotated, TypeGuard
def is_integer(x) -> Annotated[bool, TypeGuard(int, "x")]: # map the type-guard to the corresponding parameter
return isinstance(x, int) That could allow type-guarding of several parameters: def check_int_and_str(x, y) -> Annotated[bool, TypeGuard(int, "x"), TypeGuard(str, "y")]:
return isinstance(x, int) and isinstance(y, str) Using a PEP 593 type annotation instead of a whole new type has the advantage of not impacting every tools using type annotation (as metadata can just be ignored).
The type-guard is indeed a metadata of the function result, but the function still returns a But yes, this solution seems to be a little bit heavier than @vbraun proposal or PEP 647, but nothing prevent the specification and implementation of my proposal to provide the following shortcuts:
A simple implementation would be: class TypeGuard:
def __init__(self, tp, param=None):
self.tp = tp
if param is not None and not isinstance(param, str):
raise TypeError("Type guard parameter mapping must be a string")
self.param = param
def __getitem__(self, item):
return Annotated[bool, TypeGuard(item)] It would then possible to write def is_integer(x) -> TypeGuard[int]: ...
# which would give in fact `def is_integer(x) -> Annotated[bool, TypeGuard(int)]`
# which would thus be equivalent to `def is_integer(x) -> Annotated[bool, TypeGuard(int, "x")]` As easy, but more powerful (support arbitrary parameters), and again, less complexity (no additional @gvanrossum I've discovered PEP 647 while writing this comment; is it too late to add this proposal using PEP 593 to it ? (or to debate if the checked casts of my previous comment are enough or not) |
Let’s keep the discussion in one place, typing-sig. |
Mypy now supports |
Hi everyone! I've created a pretty simple library for refinement types, by making use of PEP 647 (Type guards) and PEP 593 (Annotated types). Currently, it only works for functions with a @refined decorator, but I'd love to get it to support mypy and other type checkers. Contributions are more than welcome! 😊 🍻 |
@espetro You might be interested in phantom-types, which works with pre- * end of shameless self-plug, sorry for off topic * |
This is ok for mypy:
but how can I make it work if the
isinstance
test is in a function call?I get:
The text was updated successfully, but these errors were encountered: