-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Detect impossible unpacking? #18783
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
This is clearly a bug, unpacking should be checked consistently. It already happens if the callable is "good", but plain from typing import Any
def fn1(*args: Any, **kwargs: Any) -> None: ...
fn2: Any
fn3: int
def baz(x: int) -> None:
fn1(*x, **x) # E: Expected iterable as variadic argument [misc] \
# E: Argument after ** must be a mapping, not "int" [arg-type]
fn2(*x, **x)
# Note there's an error, but an unrelated one
fn3(*x, **x) # E: "int" not callable [operator] |
See related https://github.com/python/mypy/pull/18207/files and |
Hey, I'm really not fluent in Is it a good starting point to look around here? Lines 2484 to 2492 in e37d92d
I'm unsure because it seems that this would confront an actual call to a desired signature, whereas we only need to analyze the call here. Also I could not really understand quickly how |
@ego-thales hi! Thanks for your interest. Here's a little prototype to help you working on this feature: diff --git mypy/checkexpr.py mypy/checkexpr.py
index 1017009ce..eac3a759d 100644
--- mypy/checkexpr.py
+++ mypy/checkexpr.py
@@ -1583,7 +1583,7 @@ class ExpressionChecker(ExpressionVisitor[Type]):
callee, args, arg_kinds, arg_names, callable_name, object_type, context
)
elif isinstance(callee, AnyType) or not self.chk.in_checked_function():
- return self.check_any_type_call(args, callee)
+ return self.check_any_type_call(args, callee, arg_kinds, context)
elif isinstance(callee, UnionType):
return self.check_union_call(callee, args, arg_kinds, arg_names, context)
elif isinstance(callee, Instance):
@@ -2481,15 +2481,7 @@ class ExpressionChecker(ExpressionVisitor[Type]):
# Keep track of consumed tuple *arg items.
mapper = ArgTypeExpander(self.argument_infer_context())
- for arg_type, arg_kind in zip(arg_types, arg_kinds):
- arg_type = get_proper_type(arg_type)
- if arg_kind == nodes.ARG_STAR and not self.is_valid_var_arg(arg_type):
- self.msg.invalid_var_arg(arg_type, context)
- if arg_kind == nodes.ARG_STAR2 and not self.is_valid_keyword_var_arg(arg_type):
- is_mapping = is_subtype(
- arg_type, self.chk.named_type("_typeshed.SupportsKeysAndGetItem")
- )
- self.msg.invalid_keyword_var_arg(arg_type, is_mapping, context)
+ self.check_args_unpacking(arg_types, arg_kinds, context)
for i, actuals in enumerate(formal_to_actual):
orig_callee_arg_type = get_proper_type(callee.arg_types[i])
@@ -3292,8 +3284,17 @@ class ExpressionChecker(ExpressionVisitor[Type]):
skip_unsatisfied=skip_unsatisfied,
)
- def check_any_type_call(self, args: list[Expression], callee: Type) -> tuple[Type, Type]:
- self.infer_arg_types_in_empty_context(args)
+ def check_any_type_call(
+ self,
+ args: list[Expression],
+ callee: Type,
+ arg_kinds: list[ArgKind],
+ context: Context,
+ ) -> tuple[Type, Type]:
+ arg_types = self.infer_arg_types_in_empty_context(args)
+
+ self.check_args_unpacking(arg_types, arg_kinds, context)
+
callee = get_proper_type(callee)
if isinstance(callee, AnyType):
return (
@@ -3303,6 +3304,17 @@ class ExpressionChecker(ExpressionVisitor[Type]):
else:
return AnyType(TypeOfAny.special_form), AnyType(TypeOfAny.special_form)
+ def check_args_unpacking(self, arg_types: list[Type], arg_kinds: list[ArgKind], context: Context) -> None:
+ for arg_type, arg_kind in zip(arg_types, arg_kinds):
+ arg_type = get_proper_type(arg_type)
+ if arg_kind == nodes.ARG_STAR and not self.is_valid_var_arg(arg_type):
+ self.msg.invalid_var_arg(arg_type, context)
+ if arg_kind == nodes.ARG_STAR2 and not self.is_valid_keyword_var_arg(arg_type):
+ is_mapping = is_subtype(
+ arg_type, self.chk.named_type("_typeshed.SupportsKeysAndGetItem")
+ )
+ self.msg.invalid_keyword_var_arg(arg_type, is_mapping, context)
+
def check_union_call(
self,
callee: UnionType,
I didn't test this, but it should probably work :) |
I'd like to work on this, too. |
@Jdwashin9 go ahead! My diff above can be a nice starting point. |
Oh thank you so much, because despite the really helpful contribution from @sobolevn, I could not find enough time to go further with this, requiring time to understand the core machanisms of the project. I had not forgotten, it still sat in my todo list, but it was not realistically going to happen before summer from my side. Thanks in advance and good luck @Jdwashin9! |
Hi,
Consider the following code, which passes with no error.
I think it would be neat to have
mypy
raising error on this, sinceint
cannot be unpacked in any way.Is it something to consider or am I missing something?
Thanks in advance.
Élie
The text was updated successfully, but these errors were encountered: