如何将参数传递给python fabric自定义任务

时间:2014-03-03 16:46:33

标签: python task subclass fabric

我无法弄清楚如何将参数实际传递给结构自定义任务。

我有一堆任务都需要做同样的设置,所以我希望子类化任务并让基类进行设置,然后运行特定的子任务。设置代码和子任务都需要访问从命令行传递到任务的一些参数。我还需要能够为参数设置默认值。

原始尝试

我的原始尝试显示了我在没有任何子类的情况下尝试做的事情。 此代码正常工作。 以下代码位于文件tmp1.py:

from fabric.api import task

def do_setup(myarg):
    ''' common setup for all tasks '''
    print "in do_setup(myarg=%s)" % myarg
    # do setup using myarg for something important

@task
def actual_task1(myarg='default_value', alias='at'):
    print "In actual_task1(myarg=%s)" % myarg
    do_setup(myarg)
    # do rest of work ...

@task
def actual_task2(myarg='default_value', alias='at'):
    print "In actual_task2(myarg=%s)" % myarg
    do_setup(myarg)
    # do rest of work ...

我从命令行运行它而没有任何args,并且正确地看到了'default_value'的myarg的默认值

fab -f ./tmp1.py actual_task1

打印:

In actual_task1(myarg=default_value)
in do_setup(myarg=default_value)
Done.

然后我用myarg ='hello'调用它并看到'hello'正确传递

fab -f ./tmp1.py actual_task1:myarg='hello'

输出:

In actual_task1(myarg=hello)
in do_setup(myarg=hello)
Done.

尝试使用自定义任务

我的下一个尝试是做一个通用的任务来封装设置部分。 这是从http://docs.fabfile.org/en/1.5/usage/tasks.html复制的 以下代码位于文件tmp2.py:

from fabric.api import task
from fabric.tasks import Task

def do_setup(myarg):
    ''' common setup for all tasks '''
    print "in do_setup(myarg=%s)" % myarg
    # do setup using myarg for something important

'''
Attempt to make a common task to encapsulate the setup part
copied from http://docs.fabfile.org/en/1.5/usage/tasks.html
'''
class CustomTask(Task):
    def init(self, func, myarg, args, *kwargs):
        super(CustomTask, self).init(args, *kwargs)
        print("=> init(myarg=%s, args=%s, kwargs=%s" % (myarg, args, kwargs))
        self.func = func
        self.myarg = myarg
        print "in init: self.func=",self.func,"self.myarg=",self.myarg

    def run(self, *args, **kwargs):
        return self.func(self.myarg, *args, **kwargs)

@task(task_class=CustomTask, myarg='default_value', alias='at')
def actual_task1():
    print "In actual_task1(myarg=%s)" % myarg
    # do rest of work ...

运行时,有两个问题:

  1. __ init__获取“default_value”而不是“Hello”

  2. 它抱怨actual_task1()需要0个参数

  3. 我这样运行:

    fab -f ./tmp2.py actual_task1:myarg="Hello"
    

    打印:

    => init(myarg=default_value, args=(), kwargs={'alias': 'at'}
    in init: self.func= self.myarg= default_value
    Traceback (most recent call last):
    File "/home/xxx/Documents/pyenvs/xxx/local/lib/python2.7/site-packages/fabric/main.py", line 743,      in main args, *kwargs
    File "/home/xxx/Documents/pyenvs/xxx/local/lib/python2.7/site-packages/fabric/tasks.py", line 405,  in execute results[''] = task.run(args, *new_kwargs)
    File "/home/xxx/test_fab/tmp2.py", line 21, in run
    return self.func(self.myarg, args, *kwargs)
    TypeError: actual_task1() takes no arguments (1 given)
    

    我花了不少时间尝试完成这项工作,但我似乎无法解决default_value问题。我肯定错过了什么?

    我很感激帮助找出如何运行此示例程序。具有自定义任务的第二个版本需要表现得像我展示的原始版本。

    感谢您对此问题的任何帮助。

3 个答案:

答案 0 :(得分:2)

使用设置修复示例:

from fabric.api import task
from fabric.tasks import Task

def do_setup(foo, verbose):
    ''' common setup for all tasks '''
    print "IN do_setup(foo=%s, verbose=%s)" % (foo, verbose)
    # do setup using foo and verbose...

class CustomTask(Task):
    def __init__(self, func, *args, **kwargs):
        ''' 
        The special args like hosts and roles do not show up in
        args, and kwargs, they are stripped already.

        args and kwargs may contain task specific special arguments
        (e.g. aliases, alias, default, and name) to customize the
        task. They are set in the @task decorator and cannot be passed
        on the command-line. Note also that these special task
        arguments are not passed to the run method.

        Non-special arguments (there are none in this example) are
        set in the task decorator. These other arguments are not
        passed to the run method and cannot be overridden from the
        command-line.

        Note that if you pass any "task specific special arguments" or
        "non-special arguments declared in the task decorator" from the
        command-line, they are treated as different arguments and the
        command-line values are passed to the run method but not to
        this method.
        '''
        super(CustomTask, self).__init__(*args, **kwargs)
        print "IN __init__(args=%s, kwargs=%s)" % (args, kwargs)
        self.func = func

    def run(self, foo='foo_default_val', verbose='verbose_default_val', 
            *args, **kwargs):
        ''' 
        The arguments to this method will be:
        1) arguments from the actual task (e.g. foo and verbose). This method
        is where you set a default value for the arguments from the
        actual_task, not on the actual_task.
        2) task specific arguments from the command-line
        (e.g. actual_task:bar='xxx'). This example is not expecting any,
        so it strips them and does not pass them to the
        actual_function one (e.g. it calls self.func with only foo
        and verbose and does not pass args and kwargs)
        '''
        print "IN run(foo=%s, verbose=%s, args=%s, kwargs=%s)" % \
                  (foo, verbose, args, kwargs)
        do_setup(foo, verbose)
        return self.func(foo, verbose)

@task(task_class=CustomTask, alias="RUNME")
def actual_task(foo, verbose):
    print 'IN task actual_task(foo=%s, verbose=%s)' % (foo, verbose)

仅使用命令行中指定的主机运行:

fab -f ./example_with_setup.py actual_task:host='hhh'

IN __init__(args=(), kwargs={'alias': 'RUNME'})
[hhh] Executing task 'actual_task'
IN run(foo=foo_default_val, verbose=verbose_default_val, args=(), kwargs={})
IN do_setup(foo=foo_default_val, verbose=verbose_default_val)
IN task actual_task(foo=foo_default_val, verbose=verbose_default_val)

在命令行上运行指定foo:

fab -f ./example_with_setup.py actual_task:host='hhh',foo='bar'

IN __init__(args=(), kwargs={'alias': 'RUNME'})
[hhh] Executing task 'actual_task'
IN run(foo=bar, verbose=verbose_default_val, args=(), kwargs={})
IN do_setup(foo=bar, verbose=verbose_default_val)
IN task actual_task(foo=bar, verbose=verbose_default_val)

在命令行上运行指定foo和verbose:

fab -f ./example_with_setup.py actual_task:host='hhh',foo='bar',verbose=True

IN __init__(args=(), kwargs={'alias': 'RUNME'})
[hhh] Executing task 'actual_task'
IN run(foo=bar, verbose=True, args=(), kwargs={})
IN do_setup(foo=bar, verbose=True)
IN task actual_task(foo=bar, verbose=True)

答案 1 :(得分:1)

在自定义类部分中,函数actual_task1实际上不接受参数,因此调用结构文件的唯一有效方法是:

fab -f ./tmp2.py actual_task1

此外,我认为您实际上并未在do_setupCustomTask

中调用actual_task1

答案 2 :(得分:1)

这是固定的例子。

# fixed the example from http://docs.fabfile.org/en/1.8/usage/tasks.html
from fabric.api import task
from fabric.tasks import Task

class CustomTask(Task):
    def __init__(self, func, myarg1, *args, **kwargs):
        ''' 
        The special args like hosts and roles do not show up in
        args, and kwargs, they are stripped already.

        args and kwargs may contain task specific special arguments
        (e.g. aliases, alias, default, and name) to customize the
        task. They are set in the @task decorator and cannot be passed
        on the command-line. Note also that these special task
        arguments are not passed to the run method.

        Non-special arguments (in this example myarg1) are set in the task
        decorator. These other arguments are not passed to the run
        method and cannot be overridden from the command-line. 

        Note that if you pass any "task specific special arguments" or
        "non-special arguments declared in the task decorator" from the
        command-line, they are treated as different arguments and the
        command-line values are passed to the run method but not to
        this method.
        '''
        super(CustomTask, self).__init__(*args, **kwargs)
        print "IN __init__(myarg1=%s, args=%s, kwargs=%s)" % \
            (myarg1, args, kwargs)
        self.func = func
        self.myarg1 = myarg1

    def run(self, myarg2='default_value2', *args, **kwargs):
        ''' 
        The arguments to this method will be:
        1) arguments from the actual task (e.g. myarg2). This method
        is where you set a default value for the arguments from the
        actual_task, not on the actual_task.
        2) task specific arguments from the command-line
        (e.g. actual_host:foo='foo'). This example is not expecting
        any, so it strips them and does not pass them to the
        actual_function (e.g. it calls self.func with only myarg2 and
        does not pass args and kwargs)
        '''
        print "IN run(myarg2=%s, args=%s, kwargs=%s)" % \
            (myarg2, args, kwargs)
        return self.func(myarg2)

@task(task_class=CustomTask, myarg1='special_value', alias='RUNME')
def actual_task(myarg2):
    print "IN actual_task(myarg2=%s)" % myarg2

仅使用命令行中指定的主机运行:

fab -f ./fixed_example actual_task:hosts="hhh"

IN __init__(myarg1=special_value, args=(), kwargs={'alias': 'RUNME'})
[hhh] Executing task 'actual_task'
IN run(myarg2=default_value2, args=(), kwargs={})
IN actual_task(myarg2=default_value2)

在命令行上运行指定myarg2:

fab -f ./fixed_example actual_task:hosts="hhh",myarg2="good_value"

IN __init__(myarg1=special_value, args=(), kwargs={'alias': 'RUNME'})
[hhh] Executing task 'actual_task'
IN run(myarg2=good_value, args=(), kwargs={})
IN actual_task(myarg2=good_value)

在命令行上指定myarg1和别名的错误运行。请注意, init 获取任务装饰器中指定的值,而不是命令行中的值。请注意,run现在将myarg1和alias作为参数。

fab -f ./fixed_example actual_task:hosts="hhh",myarg1="myarg1_from_commandline",alias="alias_from_commandline"

IN __init__(myarg1=special_value, args=(), kwargs={'alias': 'RUNME'})
[hhh] Executing task 'actual_task'
IN run(myarg2=default_value2, args=(), kwargs={'alias': 'alias_from_commandline', 'myarg1': 'myarg1_from_commandline'})
IN actual_task(myarg2=default_value2)