Skip to content

Commit c4de474

Browse files
authored
Merge pull request #1228 from davesque/merge-args-kwargs
Improvements to `merge_args_and_kwargs`
2 parents 83d4b3a + 575cb66 commit c4de474

File tree

1 file changed

+28
-16
lines changed

1 file changed

+28
-16
lines changed

web3/_utils/abi.py

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,15 @@ def check_if_arguments_can_be_encoded(function_abi, args, kwargs):
275275

276276

277277
def merge_args_and_kwargs(function_abi, args, kwargs):
278+
"""
279+
Takes a list of positional args (``args``) and a dict of keyword args
280+
(``kwargs``) defining values to be passed to a call to the contract function
281+
described by ``function_abi``. Checks to ensure that the correct number of
282+
args were given, no duplicate args were given, and no unknown args were
283+
given. Returns a list of argument values aligned to the order of inputs
284+
defined in ``function_abi``.
285+
"""
286+
# Ensure the function is being applied to the correct number of args
278287
if len(args) + len(kwargs) != len(function_abi.get('inputs', [])):
279288
raise TypeError(
280289
"Incorrect argument count. Expected '{0}'. Got '{1}'".format(
@@ -283,47 +292,50 @@ def merge_args_and_kwargs(function_abi, args, kwargs):
283292
)
284293
)
285294

295+
# If no keyword args were given, we don't need to align them
286296
if not kwargs:
287297
return args
288298

289-
args_as_kwargs = {
290-
arg_abi['name']: arg
291-
for arg_abi, arg in zip(function_abi['inputs'], args)
292-
}
293-
duplicate_keys = set(args_as_kwargs).intersection(kwargs.keys())
294-
if duplicate_keys:
299+
kwarg_names = set(kwargs.keys())
300+
sorted_arg_names = tuple(arg_abi['name'] for arg_abi in function_abi['inputs'])
301+
args_as_kwargs = dict(zip(sorted_arg_names, args))
302+
303+
# Check for duplicate args
304+
duplicate_args = kwarg_names.intersection(args_as_kwargs.keys())
305+
if duplicate_args:
295306
raise TypeError(
296307
"{fn_name}() got multiple values for argument(s) '{dups}'".format(
297308
fn_name=function_abi['name'],
298-
dups=', '.join(duplicate_keys),
309+
dups=', '.join(duplicate_args),
299310
)
300311
)
301312

302-
sorted_arg_names = [arg_abi['name'] for arg_abi in function_abi['inputs']]
303-
304-
unknown_kwargs = {key for key in kwargs.keys() if key not in sorted_arg_names}
305-
if unknown_kwargs:
313+
# Check for unknown args
314+
unknown_args = kwarg_names.difference(sorted_arg_names)
315+
if unknown_args:
306316
if function_abi.get('name'):
307317
raise TypeError(
308318
"{fn_name}() got unexpected keyword argument(s) '{dups}'".format(
309319
fn_name=function_abi.get('name'),
310-
dups=', '.join(unknown_kwargs),
320+
dups=', '.join(unknown_args),
311321
)
312322
)
313-
# show type instead of name in the error message incase key 'name' is missing.
314323
raise TypeError(
315324
"Type: '{_type}' got unexpected keyword argument(s) '{dups}'".format(
316325
_type=function_abi.get('type'),
317-
dups=', '.join(unknown_kwargs),
326+
dups=', '.join(unknown_args),
318327
)
319328
)
320329

321-
sorted_args = list(zip(
330+
# Sort args according to their position in the ABI and unzip them from their
331+
# names
332+
sorted_args = tuple(zip(
322333
*sorted(
323334
itertools.chain(kwargs.items(), args_as_kwargs.items()),
324-
key=lambda kv: sorted_arg_names.index(kv[0])
335+
key=lambda kv: sorted_arg_names.index(kv[0]),
325336
)
326337
))
338+
327339
if sorted_args:
328340
return sorted_args[1]
329341
else:

0 commit comments

Comments
 (0)