|
10 | 10 |
|
11 | 11 | from __future__ import annotations |
12 | 12 |
|
| 13 | +import sys |
13 | 14 | from functools import wraps |
14 | 15 | from inspect import Parameter, signature |
15 | | -from typing import TYPE_CHECKING, Callable, TypeVar |
| 16 | +from typing import TYPE_CHECKING |
16 | 17 | from warnings import warn |
17 | 18 |
|
18 | 19 | if TYPE_CHECKING: |
19 | | - from typing import ParamSpec |
| 20 | + from collections.abc import Callable |
| 21 | + from typing import ParamSpec, TypeVar |
20 | 22 |
|
21 | 23 | P = ParamSpec("P") |
22 | 24 | R = TypeVar("R") |
|
25 | 27 | POS_TYPES = {Parameter.POSITIONAL_ONLY, Parameter.POSITIONAL_OR_KEYWORD} |
26 | 28 |
|
27 | 29 |
|
| 30 | +# The actual returned Callable of course accepts more positional parameters, |
| 31 | +# but we want the type to lie so end users don’t rely on the deprecated API. |
28 | 32 | def legacy_api(*old_positionals: str) -> Callable[[Callable[P, R]], Callable[P, R]]: |
29 | 33 | """Legacy API wrapper. |
30 | 34 |
|
@@ -57,30 +61,33 @@ def wrapper(fn: Callable[P, R]) -> Callable[P, R]: |
57 | 61 | par_types = [p.kind for p in sig.parameters.values()] |
58 | 62 | has_var = Parameter.VAR_POSITIONAL in par_types |
59 | 63 | n_required = sum(1 for p in sig.parameters.values() if p.default is Parameter.empty) |
60 | | - n_positional = INF if has_var else sum(1 for p in par_types if p in POS_TYPES) |
| 64 | + n_positional = sys.maxsize if has_var else sum(1 for p in par_types if p in POS_TYPES) |
61 | 65 |
|
62 | 66 | @wraps(fn) |
63 | | - def fn_compatible(*args: P.args, **kw: P.kwargs) -> R: |
64 | | - if len(args) > n_positional: |
65 | | - args, args_rest = args[:n_positional], args[n_positional:] |
66 | | - if args_rest: |
67 | | - if len(args_rest) > len(old_positionals): |
68 | | - n_max = n_positional + len(old_positionals) |
69 | | - msg = ( |
70 | | - f"{fn.__name__}() takes from {n_required} to {n_max} parameters, " |
71 | | - f"but {len(args) + len(args_rest)} were given." |
72 | | - ) |
73 | | - raise TypeError(msg) |
74 | | - warn( |
75 | | - f"The specified parameters {old_positionals[:len(args_rest)]!r} are " |
76 | | - "no longer positional. " |
77 | | - f"Please specify them like `{old_positionals[0]}={args_rest[0]!r}`", |
78 | | - DeprecationWarning, |
79 | | - stacklevel=2, |
80 | | - ) |
81 | | - kw = {**kw, **dict(zip(old_positionals, args_rest))} |
82 | | - |
83 | | - return fn(*args, **kw) |
| 67 | + def fn_compatible(*args_all: P.args, **kw: P.kwargs) -> R: |
| 68 | + if len(args_all) <= n_positional: |
| 69 | + return fn(*args_all, **kw) |
| 70 | + |
| 71 | + args_pos: P.args |
| 72 | + args_pos, args_rest = args_all[:n_positional], args_all[n_positional:] |
| 73 | + |
| 74 | + if len(args_rest) > len(old_positionals): |
| 75 | + n_max = n_positional + len(old_positionals) |
| 76 | + msg = ( |
| 77 | + f"{fn.__name__}() takes from {n_required} to {n_max} parameters, " |
| 78 | + f"but {len(args_pos) + len(args_rest)} were given." |
| 79 | + ) |
| 80 | + raise TypeError(msg) |
| 81 | + warn( |
| 82 | + f"The specified parameters {old_positionals[:len(args_rest)]!r} are " |
| 83 | + "no longer positional. " |
| 84 | + f"Please specify them like `{old_positionals[0]}={args_rest[0]!r}`", |
| 85 | + DeprecationWarning, |
| 86 | + stacklevel=2, |
| 87 | + ) |
| 88 | + kw_new: P.kwargs = {**kw, **dict(zip(old_positionals, args_rest))} |
| 89 | + |
| 90 | + return fn(*args_pos, **kw_new) |
84 | 91 |
|
85 | 92 | return fn_compatible |
86 | 93 |
|
|
0 commit comments