Как передать аргумент bool команде fabric
В настоящее время я использую fab -f check_remote.py func:"arg1","arg2"...
для запуска fab remote.
Теперь мне нужно отправить bool arg, но True / False станет строковым arg, как установить его в качестве типа bool?
10 ответов:
Как упоминалось в fabric docs, все аргументы заканчиваются строками. Самое простое, что можно сделать здесь, это просто проверить Аргумент:
def myfunc(arg1, arg2): arg1 = (arg1 == 'True')
Скобки не требуются, но помогают с удобочитаемостью.
Edit : видимо, я на самом деле не пробовал свой предыдущий ответ; обновлено. (Два года спустя.)
Я использую это:
from distutils.util import strtobool def func(arg1="default", arg2=False): if arg2: arg2 = bool(strtobool(arg2))
Пока работает на меня. он будет анализировать значения (игнорируя регистр):
'y', 'yes', 't', 'true', 'on', '1' 'n', 'no', 'f', 'false', 'off', '0'
Strtobool возвращает 0 или 1, поэтому bool необходимо преобразовать в True/False boolean.
Для полноты, вот реализация
strtobool
:def strtobool (val): """Convert a string representation of truth to true (1) or false (0). True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if 'val' is anything else. """ val = val.lower() if val in ('y', 'yes', 't', 'true', 'on', '1'): return 1 elif val in ('n', 'no', 'f', 'false', 'off', '0'): return 0 else: raise ValueError("invalid truth value %r" % (val,))
Чуть лучшая версия (Спасибо за комментарии mVChr)
from distutils.util import strtobool def _prep_bool_arg(arg): return bool(strtobool(str(arg))) def func(arg1="default", arg2=False): arg2 = _prep_bool_arg(arg2)
Я бы использовал функцию:
def booleanize(value): """Return value as a boolean.""" true_values = ("yes", "true", "1") false_values = ("no", "false", "0") if isinstance(value, bool): return value if value.lower() in true_values: return True elif value.lower() in false_values: return False raise TypeError("Cannot booleanize ambiguous value '%s'" % value)
Тогда в задаче:
@task def mytask(arg): arg = booleanize(arg)
Если рассматриваемая функция использует "if argN: "вместо" if argN is True: "для проверки истинности логического значения, вы можете использовать ""для False и" что угодно " для True.
См. также: http://docs.python.org/library/stdtypes.html#truth-value-testing
Если вы используете шаблон последовательно ('false','true' - логический) для всех ваших задач, вы можете просто обернуть задачу тканью и применить ее ко всем
Вы можете использовать этот пакет (написанный мной): https://pypi.python.org/pypi/boolfab/
Вот (по существу) источник:
from fabric.api import task as _task def fix_boolean(f): def fix_bool(value): if isinstance(value, basestring): if value.lower() == 'false': return False if value.lower() == 'true': return True return value @wraps(f) def wrapper(*args, **kwargs): args_ = [fix_bool(arg) for arg in args] kwargs_ = {k: fix_bool(v) for k,v in kwargs.iteritems()} return f(*args_, **kwargs_) return wrapper def task(f): return _task(fix_boolean(f))
Так что оно становится:
@task def my_task(flag_a, flag_b, flag_c) if flag_a: ....
Не загрязняя каждую задачу "булеизирующими" аргами.
Лучшим способом было бы использовать
ast.literal_eval
:from ast import literal_eval def my_task(flag): if isinstance(flag, basestring): # also supports calling from other tasks flag = literal_eval(flag)
Хотя это не принимает во внимание такие значения, как " да " или "нет", это немного чище и безопаснее, чем
eval
...
Ответы Крейга и Ари приведут к истинному значению, если пользователь передаст " False "(ответ Ари яснее об этом)
Если вы используете eval (), строки "True" и "False" будут вычислены до их правильных логических значений, но если вы используете значения по умолчанию, вам нужно будет убедиться, что это строки, а не логические значения.
def myfunc(arg1="True", arg2=False): arg1 = eval(arg1) arg2 = eval(arg2) #error
В моих fabfiles я просто делаю:
TRUTHY = [True, 1, '1', 'true', 't', 'yes', 'y'] @task def my_task(my_arg=True): if my_arg in TRUTHY: # do stuff else: # do other stuff
Конечно, это означает, что любая ценность не в истине эффективно
False
, но до сих пор мне не нужно было ничего более сложного.
Я решил эту проблему с помощью декораторов. Мне нравится гибкость и эксплицитность, которые вы получаете от использования декоратора.
Вот мясо кода:
import ast from fabric import utils from fabric.api import task from functools import wraps def params(*types, **kwtypes): def decorator(function): @wraps(function) def wrapper(*args, **kwargs): new_args = () for index, arg in enumerate(args): new_args += __cast(arg, types[index]), for kwarg in kwargs: kwargs[kwarg] = __cast(kwargs[kwarg], kwtypes[kwarg]) return function(*new_args, **kwargs) return wrapper return decorator def __evaluate(arg): try: return ast.literal_eval(arg) except: return str(arg) def __cast(arg, arg_type): try: return arg_type(__evaluate(arg)) except: utils.abort("Unable to cast '{}' to {}".format(arg, arg_type))
Вот как это выглядит, чтобы использовать его в коде:
@task @params(int, bool, arg1=int, arg2=bool) def test(arg1, arg2): print type(arg1), arg1 print type(arg2), arg2
Вот как это выглядит, чтобы назвать его через fab с хорошими парами:
fab test:0.1,1 <type 'int'> 0 <type 'bool'> True fab test:5,arg2=False <type 'int'> 5 <type 'bool'> False fab test:arg1=0,arg2=false <type 'int'> 5 <type 'bool'> True
Примечание: в последнем примере "false" - это True, это ожидаемое поведение в python, однако оно может быть естественным образом контринтуитивным. Аналогично с передачей False как int, он будет приведен к 0 как int (False) == 0 в python
Вот как это выглядит, чтобы назвать его через fab с плохими парами:
fab test:Test,False Fatal error: Unable to cast 'Test' to <type 'int'> Aborting.
Это рабочая версия, основанная на https://gist.github.com/mgedmin/f832eed2ac0f3ce31edf . В отличие от старой версии, это фактически соблюдает все возможные параметры декоратора и псевдоним задачи:
from functools import wraps from fabric import tasks def fix_boolean(f): true_values = ("yes", "true", "1") false_values = ("no", "false", "0") def fix_bool(value): if isinstance(value, basestring): if value.lower() in false_values: return False if value.lower() in true_values: return True return value @wraps(f) def wrapper(*args, **kwargs): args_ = [fix_bool(arg) for arg in args] kwargs_ = {k: fix_bool(v) for k,v in kwargs.iteritems()} return f(*args_, **kwargs_) return wrapper def task(*args, **kwargs): """ The fabric.decorators.task decorator which automatically converts command line task arguments to a boolean representation if applicable. :param args: :param kwargs: :return: wrapped """ invoked = bool(not args or kwargs) task_class = kwargs.pop("task_class", tasks.WrappedCallableTask) def wrapper(f): return task_class(fix_boolean(f), *args, **kwargs) return wrapper if invoked else wrapper(args[0])
Суть: https://gist.github.com/eltismerino/a8ec8584034c8a7d087e