Как не передавать аргумент в функцию если она его не ждёт?
Как-то раз я делал модуль с функциями, которые вызывались как фоновые задачи. В основном они принимали чёткий список позиционных аргументов. И вот, в разгар разработки, пришла новая фича - в каждую такую функцию теперь передаётся Lock-объект. Он позволяет сделать выполнение этой функции синхронным на разных воркерах (как и положено любому локеру).
Но вот проблема, в новых функциях, где нужен локер, я его, конечно же, принимаю как аргумент. Но в старых функциях он не предусмотрен. Часто функции вообще без аргументов.
Какие варианты решения?
▫️ Добавить во всех функциях в аргументы **kwargs. Это решит все проблемы. Строго говоря, это надо было сделать сразу. Теперь таски не будут падать из-за неизвестного аргумента. И теперь следует не забывать добавлять **kwargs в новых функциях. Но что, если нет возможности изменять код? Тогда...
▫️Проверить, может ли функция принять аргумент с определённым именем. И если не может то не передавать. Это можно сделать с помощью стандартной функции inspect.signature
from inspect import signature
def func(x, y, z=True):
pass
sig = signature(func)
print(sig)
# <Signature (x, y, z=True)>
print(sig.parameters)
mappingproxy(OrderedDict([('x', <Parameter "x">), ('y', <Parameter "y">), ('z', <Parameter "z=True">)]))
Теперь можно проверить, ожидает ли функция параметр с определённым именем
print('lock' in sig.parameters)
# False
Финальный псевдокод
from tasks import my_task, LockClass
from inspect import signature
task_kwargs = {}
lock = LockClass()
if 'lock' in signature(my_task).parameters:
task_kwargs['lock'] = lock
my_task(**task_kwargs)
Конечно же, наличие этого имени не гарантирует что функция ожидает именно этот тип. Но это уже нюансы реализации 😼
#tricks