Skip to content

Commit f232e07

Browse files
authored
Reject unreachable partial varargs after keyword binding (#2537)
1 parent d85857e commit f232e07

2 files changed

Lines changed: 42 additions & 4 deletions

File tree

‎returns/contrib/mypy/_typeops/transform_callable.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,8 @@ def diff(self, applied_args: list[FuncArg]) -> CallableType:
161161
# longer be filled positionally, so every remaining positional
162162
# parameter after it must become keyword-only. Otherwise the partial
163163
# would accept positional arguments that raise a ``TypeError`` at
164-
# runtime. See issue #2191.
164+
# runtime. Variadic positional parameters become unreachable too.
165+
# See issue #2191.
165166
keyword_hole_at = self._keyword_hole_at(
166167
original_args,
167168
applied_args,
@@ -171,6 +172,7 @@ def diff(self, applied_args: list[FuncArg]) -> CallableType:
171172
self._remaining_arg(arg, keyword_only=index > keyword_hole_at)
172173
for index, arg in enumerate(original_args)
173174
if not self._was_applied(arg, index, intermediate_names)
175+
and (arg.kind != ARG_STAR or index < keyword_hole_at)
174176
])
175177

176178
def _keyword_hole_at(

‎typesafety/test_curry/test_partial/test_partial_arguments.yml‎

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@
2828
main:14: note: Revealed type is "def (a: int, b: int, c: int =, *args: float, d: str, e: bool =, **kwargs: str) -> str"
2929
main:15: note: Revealed type is "def (b: int, c: int =, *args: float, d: str, e: bool =, **kwargs: str) -> str"
3030
main:16: note: Revealed type is "def (c: int =, *args: float, d: str, e: bool =, **kwargs: str) -> str"
31-
main:17: note: Revealed type is "def (*args: float, d: str, e: bool =, **kwargs: str) -> str"
32-
main:18: note: Revealed type is "def (*args: float, e: bool =, **kwargs: str) -> str"
33-
main:19: note: Revealed type is "def (*args: float, d: str, **kwargs: str) -> str"
31+
main:17: note: Revealed type is "def (*, d: str, e: bool =, **kwargs: str) -> str"
32+
main:18: note: Revealed type is "def (*, e: bool =, **kwargs: str) -> str"
33+
main:19: note: Revealed type is "def (*, d: str, **kwargs: str) -> str"
3434
main:20: note: Revealed type is "def (c: int =, *args: float, e: bool =, **kwargs: str) -> str"
3535
main:21: note: Revealed type is "def (c: int =, *args: float, d: str, **kwargs: str) -> str"
3636
main:22: note: Revealed type is "def (*args: float, d: str, e: bool =, **kwargs: str) -> str"
@@ -211,3 +211,39 @@
211211
from returns.curry import partial
212212
213213
reveal_type(partial((lambda x, y: str(x + y)), 1)) # N: Revealed type is "def (y: Any) -> str"
214+
215+
216+
- case: partial_variadic_argument_binding
217+
disable_cache: false
218+
main: |
219+
from returns.curry import partial
220+
221+
def multiple(first: int, *args: int, flag: bool = False) -> str:
222+
...
223+
224+
bound = partial(multiple, first=1)
225+
reveal_type(bound) # N: Revealed type is "def (*, flag: bool =) -> str"
226+
reveal_type(bound(flag=True)) # N: Revealed type is "str"
227+
bound(True) # E: Too many positional arguments [call-arg]
228+
229+
keyword_bound = partial(multiple, flag=True)
230+
reveal_type(keyword_bound) # N: Revealed type is "def (first: int, *args: int) -> str"
231+
reveal_type(keyword_bound(1, 2, 3)) # N: Revealed type is "str"
232+
233+
positional_bound = partial(multiple, 1, 2)
234+
reveal_type(positional_bound) # N: Revealed type is "def (*args: int, flag: bool =) -> str"
235+
reveal_type(positional_bound(3, 4, flag=True)) # N: Revealed type is "str"
236+
237+
238+
- case: partial_keyword_hole_before_varargs_keeps_prefix
239+
disable_cache: false
240+
main: |
241+
from returns.curry import partial
242+
243+
def multiple(first: int, second: int, *args: int, flag: bool = False) -> str:
244+
...
245+
246+
bound = partial(multiple, second=2)
247+
reveal_type(bound) # N: Revealed type is "def (first: int, *, flag: bool =) -> str"
248+
bound(1, flag=True)
249+
bound(1, True) # E: Too many positional arguments [call-arg]

0 commit comments

Comments
 (0)