Как передать аргумент bool команде fabric


В настоящее время я использую fab -f check_remote.py func:"arg1","arg2"... для запуска fab remote.

Теперь мне нужно отправить bool arg, но True / False станет строковым arg, как установить его в качестве типа bool?

10 22

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