python - Cómo pasar el argumento bool al comando fabric
boolean (10)
Como se menciona en los documentos de tejido , todos los argumentos terminan como cadenas. Lo más sencillo de hacer aquí sería verificar el argumento:
def myfunc(arg1, arg2):
arg1 = (arg1 == ''True'')
Los paréntesis no son necesarios, pero ayudan con la legibilidad.
Edit : Aparentemente no probé mi respuesta anterior; actualizado. (Dos años después.)
Actualmente estoy usando fab -f check_remote.py func:"arg1","arg2"...
para ejecutar el control remoto fab.
Ahora necesito enviar un argumento bool, pero True / False se convierte en una cadena arg, ¿cómo configurarlo como tipo bool?
En mis fabfiles acabo de hacer:
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
Por supuesto, esto significa que cualquier valor que no esté en la VERDAD es efectivamente False
, pero hasta ahora no he necesitado nada más complicado.
Esta es una versión de trabajo basada en gist.github.com/mgedmin/f832eed2ac0f3ce31edf . A diferencia de la versión anterior, en realidad esto respeta todos los parámetros posibles de decorador y los alias de la tarea:
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])
Gist: https://gist.github.com/eltismerino/a8ec8584034c8a7d087e
Estoy usando esto:
from distutils.util import strtobool
def func(arg1="default", arg2=False):
if arg2:
arg2 = bool(strtobool(arg2))
Hasta ahora funciona para mí. analizará los valores (ignorando el caso):
''y'', ''yes'', ''t'', ''true'', ''on'', ''1''
''n'', ''no'', ''f'', ''false'', ''off'', ''0''
strtobool devuelve 0 o 1, por eso se necesita bool para convertir a True / False boolean.
Para completar, aquí está la strtobool
de 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,))
Versión ligeramente mejor (gracias por los comentarios 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)
He resuelto esto utilizando decoradores. Me gusta la flexibilidad y la experiencia que se obtiene al usar un decorador.
Aquí está la carne del código:
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))
Aquí está lo que parece usarlo en el código:
@task
@params(int, bool, arg1=int, arg2=bool)
def test(arg1, arg2):
print type(arg1), arg1
print type(arg2), arg2
Esto es lo que parece llamarlo vía fab con buenos parámetros:
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
NOTA: En el último ejemplo, "falso" es Verdadero, este es el comportamiento esperado en Python, sin embargo, puede ser naturalmente contraintuitivo. Similar a pasar False como un int, se convertirá a 0 como int (False) == 0 en python
Esto es lo que parece llamarlo a través de fab con malos parámetros:
fab test:Test,False
Fatal error: Unable to cast ''Test'' to <type ''int''>
Aborting.
Las respuestas de Craig y Ari resultarán en un valor Verdadero si el usuario pasa "Falso" (la respuesta de Ari es más clara al respecto)
Si usa eval (), las cadenas "Verdadero" y "Falso" se evaluarán según sus valores booleanos correctos, pero si está usando valores predeterminados, deberá asegurarse de que sean cadenas y no booleanos.
def myfunc(arg1="True", arg2=False):
arg1 = eval(arg1)
arg2 = eval(arg2) #error
Si la función en cuestión usa "si argN:" en lugar de "si argN es Verdadero:" para probar si un valor booleano es verdadero, podría usar "" para Falso y "cualquier cosa" para Verdadero.
Véase también: http://docs.python.org/library/stdtypes.html#truth-value-testing
Si utiliza el patrón de forma coherente (''falso'', ''verdadero'' es booleano) en todas sus tareas, puede simplemente envolver la tarea de estructura y aplicarla en todos
Puede usar este paquete (escrito por mí): https://pypi.python.org/pypi/boolfab/
Aquí está (esencialmente) la fuente:
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))
Para que se convierta en:
@task
def my_task(flag_a, flag_b, flag_c)
if flag_a:
....
sin contaminar cada tarea con args ''booleanizing''.
Una mejor manera sería usar 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)
Aunque esto no toma en cuenta valores como ''sí'' o ''no'', es un poco más limpio y seguro que eval
...
Yo usaría una función:
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)
Luego en la tarea:
@task
def mytask(arg):
arg = booleanize(arg)