Question

I have defined a number of fabric tasks by subclassing Task (as described here: http://docs.fabfile.org/en/latest/usage/tasks.html#task-subclasses). I'd like to have a docstring in order to describe what each task subclass does which will be displayed on fab -l just like when definining tasks with the @task decorator. I tried putting the docstring in a number of places (after class, after def run) but it didn't work.

Is it possible to have a fab -l visible description for tasks defined as subclasses of Task ?

For instance, here's a portion of my fabfile:

class BaseConfig(Task):
    def run(self):
        env.env = self.name
        env.user = 'serafeim'
        env.role = self.name
        puts("Using {0} configuration with user {1} at {2}".format(env.env, env.user, env.roledefs[env.env]))

class UatConfig(BaseConfig):
    """
    UAT settings
    """
    name = "uat"
uat_cfg_instance = UatConfig()


class ProdConfig(BaseConfig):
    """
    Prod settings
    """
    name = "prod"
prod_cfg_instance = ProdConfig()

When I run fab -l I only see

uat
dev

without any description of what these do.

Was it helpful?

Solution

Using Fabric==1.8.3:

Your code with added imports and managing docstring for tasks: fabfile.py:

from fabric.api import *
from fabric.tasks import Task

class BaseConfig(Task):
    """BaseConfig task docstring"""
    def run(self):
        """BaseConfig task docstring for `run` method"""
        env.env = self.name
        env.user = 'serafeim'
        env.role = self.name
        puts("Using {0} configuration with user {1} at {2}".format(env.env, env.user, env.roledefs[env.env]))

class UatConfig(BaseConfig):
    """
    UAT settings
    """
    name = "uat"
uat_cfg_instance = UatConfig()


class ProdConfig(BaseConfig):
    """
    Prod settings

    talking a bit more about our classy concept, not yet diving into `run` details
    """
    name = "prod"
    def run(self):
        """ProdConfig run docstring

        on multiple lines
        we are now in prod"""
        super(ProdConfig, self).run()

prod_cfg_instance = ProdConfig()

Listing available tasks

$ fab -l
Available commands:

    prod  Prod settings
    uat   UAT settings

Here we see, that docstring from class derived from Task is used - but just the first line.

Show detailed docstring for uat - docstring from super class

Detailed description for uat illustrates the problem you have described:

$ fab -d uat
Displaying detailed information for task 'uat':

    BaseConfig task docstring for `run` method
    Arguments: self

As uat does not redefine run method, parent run method is used and this applies to presenting docstring for task details.

Show detailed docstring for prod - docstring from run method

For prod we redefine run method and this gives us us a chance to modify docstring for the task:

$ fab -d prod
Displaying detailed information for task 'prod':

    ProdConfig run docstring

            on multiple lines
            we are now in prod
    Arguments: self

Conclusions

  • brief listing (via $ fab -l) shows first line of class docstring
  • detailed listing (via $ fab -d <taskname>) shows docstring from used run method
  • if you want to change task detailed docstring, you have to define run method in your inherited class and specify the docstring there. Do not forget to call run from super class (if it is relevant).

OTHER TIPS

In order to display the docstring of the subclass when fab -d is called, the following works for me in Fabric 1.13.1:

 class BaseConfig(Task):
        def run(self):
            env.env = self.name
            env.user = 'serafeim'
            env.role = self.name
            puts("Using {0} configuration with user {1} at {2}".format(env.env, env.user, env.roledefs[env.env]))

        def __details__(self):
            return self.__doc__

And voila:

$ fab -d provision.prod
Displaying detailed information for task 'provision.prod':

    Prod settings

$ fab -d provision.uat
Displaying detailed information for task 'provision.uat':

    UAT settings

If you want to use the same docstring for the Task class and run() method, use the following way:

from fabric.api import *
from fabric.tasks import Task


class BaseConfig(Task):
    def __init__(self, *args, **kwargs):
        super(BaseConfig, self).__init__(*args, **kwargs)
        # Update docstring of run() so `fab -d <task>` will show the same
        # docstring of the class
        if self.__doc__:
            # http://stackoverflow.com/a/4835557/2293304
            self.run.__func__.__doc__ = self.__doc__


class FooConfig(BaseConfig):
    """
    Foo docstring

    This is the foo description.
    """
    name = 'foo'

    def run(self):
        # This method will automatically use the docstring of the class
        puts('This run foo...')


foo_instance = FooConfig()
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top