A Proposal for User Plugins for Docutils Readers/Writers
- Address:
dkuhlman@rexx.com http://www.rexx.com/~dkuhlman
- Date:
- March 21, 2007
Abstract
This proposal describes a standard for support of user directives for Docutils readers/writers. It is intended that Docutils writers (for example, rst2html.py, rst2latex.py, rst2odt.py, etc.) would attempt to load and register directives as described by this proposal.
Rationale
Directives are a Docutils extension mechanism. In particular, directives enable us to implement a class that adds nodes to the document node tree as that tree is being constructed and before the tree is processed by a writer.
This proposal describes a way to make directive implementations automatically loadable. It is intended that this mechanism would enable users to add their own directives and even to provide different directive implementations for different documents.
The Proposal
The following items describe this proposal:
Each writer will attempt to import a directive module containing directive implementation classes.
The default directive module name is docutils_directives.py. Each writer will also implement a command line flag --plugins-module-name that will enable users to override the default directive module name. But see note on problems with using a command line flag below.
All classes in the directive module which contain the class variable directive_name will be treated as directive implementation classes. Each such class will be registered as a directive using the value of directive_name as the directive name.
Each directive implementation class must follow the directive class implementation guidelines given at Creating reStructuredText Directives.
Notes:
I'd like users to be able to pass in the name of the plugins module on the command line. However, this seems difficult, because the writer class, which has access to configuration variables, does not get control until after the node tree has been constructed, and it is during construction of the node tree that plugins (directive classes) are run.
The mechanism described above registers all classes in a module, if the class has the class variable named directive_name. Therefore, that module could "include" directive implementations from other modules using the following:
from my_other_module import *
or:
from my_other_module import MyDirectiveClass1, MyDirectiveClass2
This proposal supports the implementation of directives by classes but not by functions. But, I can't find any documentation on implementing a directive as a function, anyway. See: Creating reStructuredText Directives.
Alternatives
Lea Wiemann also has a proposal for extensions. That proposal seems more oriented toward adding extensions to an installation of Docutils rather than, as in this proposal, enabling users to load and register directives automatically, on the fly. So, for example, under Lea's plugins proposal, in order to change the implementation of a directive, the user must perform some sort of install operation. Am I right about that?
Lea's proposal also appears to be much more general and powerful than this one. It covers the ability to add other types of extensions besides directives. A quote from Lea's proposal:
"Specifically, this document covers the addition of Docutils components (readers, parsers, and writers), and the addition of reStructuredText roles and directives."
See:
http://svn.berlios.de/viewcvs/docutils/branches/plugins/docs/howto/
"How To Create Extensions to Docutils" -- http://svn.berlios.de/viewcvs/docutils/branches/plugins/docs/howto/extensions.txt?view=markup
A Sample Implementation
The following code could be added near the top of a Docutils writer in order to implement this proposal. This code is provided for discussion. Note that it does not support a command line flag that can pass in the directive module name.
import inspect # # Register directives defined in a module named "odtwriter_plugins". # def load_plugins(): plugin_mod = None count = 0 try: import odtwriter_plugins plugin_mod = odtwriter_plugins except ImportError, e: pass if plugin_mod is None: return count klasses = inspect.getmembers(plugin_mod, inspect.isclass) for klass in klasses: if register_plugin(*klass): count += 1 return count def register_plugin(name, klass): plugin_name = getattr(klass, 'plugin_name', None) if plugin_name is not None: rst.directives.register_directive(plugin_name, klass) load_plugins()
Copyright
This document has been placed in the public domain.