如何将布尔参数传递给结构命令



目前我正在使用fab -f check_remote.py func:"arg1","arg2"...来远程运行fab。

现在我需要发送一个布尔参数,但是 True/False 变成了一个字符串参数,如何将其设置为布尔类型?

我正在使用这个:

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 转换为真/假布尔值的原因。

为了完整起见,以下是 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)
如果所讨论的 func 使用 "if argN:"

而不是 "if argN is True:"来测试布尔值是否为真,则可以使用 " 表示 False,使用 "anything" 表示 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",Craig 和 Ari 的答案将产生 True 值(Ari 的答案对此更清楚(

如果使用 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

当然,这意味着任何不在 TRUTHY 中的值都是有效的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 传递类似,它将在 python 中转换为 0 作为 int(False( == 0

以下是通过带有不良参数的 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

如结构文档中所述,所有参数最终都以字符串形式结束。这里最简单的事情就是检查参数:

def myfunc(arg1, arg2):
  arg1 = (arg1 == 'True')

括号不是必需的,但有助于提高可读性。

编辑:显然我实际上并没有尝试我以前的答案; 更新。(两年后。

最新更新