Dynamic documentation of Django commands

Dynamic documentation of Django commands

Published Aug. 7, 2016 in Development, Documentation, Web - Last update on Aug. 7, 2016.

I'm updating Django-DBBackup's documentation and don't want to copy/paste piece of code in docs, especialy commands' usages text. Fortunately optparse and argparse have the helpful parser.print_usage() method. Unfortunately there's no Sphinx plugin to simply get these outputs and include in docs. It's time to get on the job.

What I want to do ?

Have a simple RST markup for get the --help of a Django command. I want to be able to do:

.. djcommand:: myapp.management.commands.mycmd

And Bim! I have:

Usage: manage.py mycmd [options] 

Options:
  --version             show program's version number and exit
  -h, --help            show this help message and exit

The requirements are:

  • Understand how to create a Sphinx plugin
  • Know how to print usage

We'll begin by the 2nd, less boring...

It is not hard to have pretty nice help message, Django uses argparse (optparse for old versions) and it has a print_help() method:

>>> argparse.ArgumentParser.print_help??
Signature: argparse.ArgumentParser.print_help(self, file=None)
Source:
    def print_help(self, file=None):
        if file is None:
            file = _sys.stdout
        self._print_message(self.format_help(), file)
File:      /usr/lib/python2.7/argparse.py
Type:      instancemethod

This method takes an optional argument file, if passed output will be written on that file object. Next thing, Django can print_help, but it can only on stdout (the default output file of argparse.ArgumentParser.print_help if you follow everything):

>>> django.core.management.base.BaseCommand.print_help??
Signature: django.core.management.base.BaseCommand.print_help(self, prog_name, subcommand)
Source:
    def print_help(self, prog_name, subcommand):
        """
        Print the help message for this command, derived from
        ``self.usage()``.

        """
        parser = self.create_parser(prog_name, subcommand)
        parser.print_help()

We just have to create the parser and print help in a BytesIO.

And the bridge between RST and Python

Sphinx has already a tons of plugin, just look at sphinx-contrib to have an overview. There is a quite great tutorial to understand how to create your plugin but I think it is too complex for get a first approach of how to do. What I am doing is pretty simple and a 6 pages tutorial is too much, I prefer to look at source code and I found sphinxcontrib/examplecode. From this reference I created the following code:

from importlib import import_module
from docutils import nodes
from docutils.parsers.rst import Directive
from io import BytesIO


class djcommand(nodes.Element):
    pass


def visit_djcommand_node(self, node):
    self.visit_admonition(node)
    self.body.append(self.starttag(node, "pre"))

    command_name = node.rawsource.split('.')[-1]
    command_module = import_module(node.rawsource)
    command = command_module.Command()
    parser = command.create_parser('manage.py', command_name)
    command_help = BytesIO()
    parser.print_help(command_help)
    command_help.seek(0)
    self.body.append(command_help.read())

    self.body.append("</pre>")


def depart_djcommand_node(self, node):
    self.depart_admonition(node)


class DjCommandDirective(Directive):
    has_content = True
    node_class = djcommand

    def run(self):
        self.assert_has_content()
        text = '\n'.join(self.content)
        node = self.node_class(text)
        return [node]

Do not copy/paste, it is packaged

I created this repository: sphinx-django-command, as said it is a Sphinx plugin, you won't have to pull your hairs like me. Just pip install sphinx-django-command.

References

Comments

No comments yet.

Post your comment

Comment as . Log out.