Skip to content

Commit 38f2ed5

Browse files
committed
fix: align positional argument parsing with parameter positions
1 parent 9fa2a1c commit 38f2ed5

1 file changed

Lines changed: 80 additions & 39 deletions

File tree

taskiq/receiver/params_parser.py

Lines changed: 80 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,62 @@
88
logger = getLogger(__name__)
99

1010

11+
def _parse_arg(
12+
param_name: str,
13+
annot: Any,
14+
argnum: int,
15+
message: TaskiqMessage,
16+
) -> None:
17+
"""
18+
Parse a positional argument by its annotation, in place.
19+
20+
:param param_name: name of the parameter.
21+
:param annot: type annotation of the parameter.
22+
:param argnum: index of the argument in message.args.
23+
:param message: incoming message.
24+
"""
25+
value = message.args[argnum]
26+
if value is None:
27+
return
28+
logger.debug("Trying to parse %s as %s", param_name, annot)
29+
try:
30+
# trying to parse found value as in type annotation.
31+
message.args[argnum] = parse_obj_as(annot, value)
32+
except (ValueError, RuntimeError) as exc:
33+
logger.warning(
34+
"Can't parse argument %d for task %s. Reason: %s",
35+
argnum,
36+
message.task_name,
37+
exc,
38+
exc_info=True,
39+
)
40+
41+
42+
def _parse_kwarg(param_name: str, annot: Any, message: TaskiqMessage) -> None:
43+
"""
44+
Parse a keyword argument by its annotation, in place.
45+
46+
:param param_name: name of the parameter.
47+
:param annot: type annotation of the parameter.
48+
:param message: incoming message.
49+
"""
50+
value = message.kwargs.get(param_name)
51+
if value is None:
52+
return
53+
logger.debug("Trying to parse %s as %s", param_name, annot)
54+
try:
55+
# trying to parse found value as in type annotation.
56+
message.kwargs[param_name] = parse_obj_as(annot, value)
57+
except (ValueError, RuntimeError) as exc:
58+
logger.warning(
59+
"Can't parse argument %s for task %s. Reason: %s",
60+
param_name,
61+
message.task_name,
62+
exc,
63+
exc_info=True,
64+
)
65+
66+
1167
def parse_params(
1268
signature: inspect.Signature | None,
1369
type_hints: dict[str, Any],
@@ -55,47 +111,32 @@ def parse_params(
55111
return
56112
argnum = -1
57113
# Iterate over function's params.
58-
for param_name in signature.parameters:
114+
for param_name, param in signature.parameters.items():
59115
# If parameter doesn't have an annotation.
60116
annot = type_hints.get(param_name)
61-
if annot is None:
62-
continue
63-
# Increment argument numbers. This is
64-
# for positional arguments.
65-
argnum += 1
66-
# Value from incoming message.
67-
value = None
68-
logger.debug("Trying to parse %s as %s", param_name, annot)
69-
# Check if we have positional arguments in passed message.
70-
if argnum < len(message.args):
71-
# Get positional argument.
72-
value = message.args[argnum]
73-
if value is None:
117+
if param.kind in (
118+
inspect.Parameter.POSITIONAL_ONLY,
119+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
120+
):
121+
# Every positional-capable parameter occupies a slot in
122+
# message.args, even if it has no type annotation.
123+
argnum += 1
124+
if annot is None:
125+
continue
126+
if argnum < len(message.args):
127+
# This parameter was passed positionally.
128+
_parse_arg(param_name, annot, argnum, message)
129+
else:
130+
# The parameter was passed as a kwarg or not at all.
131+
_parse_kwarg(param_name, annot, message)
132+
elif param.kind == inspect.Parameter.VAR_POSITIONAL:
133+
# All remaining positional arguments belong to *args.
134+
if annot is None:
74135
continue
75-
try:
76-
# trying to parse found value as in type annotation.
77-
message.args[argnum] = parse_obj_as(annot, value)
78-
except (ValueError, RuntimeError) as exc:
79-
logger.warning(
80-
"Can't parse argument %d for task %s. Reason: %s",
81-
argnum,
82-
message.task_name,
83-
exc,
84-
exc_info=True,
85-
)
136+
for i in range(argnum + 1, len(message.args)):
137+
_parse_arg(param_name, annot, i, message)
86138
else:
87-
# We try to get this parameter from kwargs.
88-
value = message.kwargs.get(param_name)
89-
if value is None:
139+
# KEYWORD_ONLY and VAR_KEYWORD parameters are matched by name.
140+
if annot is None or param.kind == inspect.Parameter.VAR_KEYWORD:
90141
continue
91-
try:
92-
# trying to parse found value as in type annotation.
93-
message.kwargs[param_name] = parse_obj_as(annot, value)
94-
except (ValueError, RuntimeError) as exc:
95-
logger.warning(
96-
"Can't parse argument %s for task %s. Reason: %s",
97-
param_name,
98-
message.task_name,
99-
exc,
100-
exc_info=True,
101-
)
142+
_parse_kwarg(param_name, annot, message)

0 commit comments

Comments
 (0)