目前我正在使用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来将其转换为True/False布尔值。
为了完整起见,这里是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,))
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)
如果你在所有任务中始终使用一致的模式('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))
So that it becomes:
@task
def my_task(flag_a, flag_b, flag_c)
if flag_a:
....
避免用“布尔化”参数来污染每个任务。
@task(name='foo-bar')
或 @task(default=True)
。 - Marius Gedminasarg1
等于"False"
时,它将被评估为True
,这将是相当不直观的。 - charlaxast.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
稍微更简洁、更安全...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 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作为整数传递,它将转换为0,因为在Python中int(False) == 0。fab test:Test,False
Fatal error: Unable to cast 'Test' to <type 'int'>
Aborting.
def myfunc(arg1="True", arg2=False):
arg1 = eval(arg1)
arg2 = eval(arg2) #error
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
,但到目前为止,我没有需要更复杂的内容。这是一个基于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])
如fabric文档中所述,所有参数最终都会变成字符串。在这里最简单的做法就是检查参数:
def myfunc(arg1, arg2):
arg1 = (arg1 == 'True')
括号不是必需的,但有助于提高可读性。
编辑:显然我之前的答案并没有真正尝试;已更新。(两年后。)
arg2 = True if arg2 == 'arg2' else bool(strtobool(str(arg2)))
- 然后你可以使用fab func:arg2=True
、fab func:arg2=1
或者简单地使用fab func:arg2
来打开它,而使用fab func:arg2=False
、fab func:arg2=0
或者fab func
来关闭它。最内层的str()
表示你可以避免if arg2:
的检查,并真正实现一行代码(因为它将默认值False
转换为字符串"False"
,再传递给strtobool()
函数)。 - gimbolandif
检查了:_prep_arg(arg): return bool(strtobool(str(arg)))
,然后只需要arg2 = _prep_arg(arg2)
。 - mVChr