I'm updating Django-DBBackup's documentation and don't want to copy/paste piece of code in docs, especialy commands' usages text. Fortunately
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
optparse for old versions) and it has a
>>> 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
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.