Movatterモバイル変換


[0]ホーム

URL:


Docutils |Overview |About |Users |Reference |Developers

CreatingreStructuredText Directives

Authors:

Dethe Elza

David Goodger

Lea Wiemann

Contact:
docutils-develop@lists.sourceforge.net
Date:
2024-08-15
Revision:
9906
Copyright:
This document has been placed in the public domain.

Directives are the primary extension mechanism of reStructuredText.This document aims to make the creation of new directives as easy andunderstandable as possible. There are only a couple ofreStructuredText-specific features the developer needs to know tocreate a basic directive.

The syntax of directives is detailed in thereStructuredText MarkupSpecification, and standard directives are described inreStructuredText Directives.

Directives are a reStructuredText markup/parser concept. There is no"directive" document tree element, no single element that correspondsexactly to the concept of directives. Instead, choose the mostappropriate elements from the existing Docutils elements. Directivesbuild structures using the existing building blocks. SeeTheDocutils Document Tree and thedocutils.nodes module for moreabout the building blocks of Docutils documents.

The Directive Class

Directives are created by defining a directive class that inheritsfromdocutils.parsers.rst.Directive:

from docutils.parsers import rstclass MyDirective(rst.Directive):    ...

To understand how to implement the directive, let's have a look at thedocstring of theDirective base class:

>>> from docutils.parsers import rst>>> print rst.Directive.__doc__    Base class for reStructuredText directives.    The following attributes may be set by subclasses.  They are    interpreted by the directive parser (which runs the directive    class):    - `required_arguments`: The number of required arguments (default:      0).    - `optional_arguments`: The number of optional arguments (default:      0).    - `final_argument_whitespace`: A boolean, indicating if the final      argument may contain whitespace (default: False).    - `option_spec`: A dictionary, mapping known option names to      conversion functions such as `int` or `float` (default: {}, no      options).  Several conversion functions are defined in the      directives/__init__.py module.      Option conversion functions take a single parameter, the option      argument (a string or ``None``), validate it and/or convert it      to the appropriate form.  Conversion functions may raise      `ValueError` and `TypeError` exceptions.    - `has_content`: A boolean; True if content is allowed.  Client      code must handle the case where content is required but not      supplied (an empty content list will be supplied).    Arguments are normally single whitespace-separated words.  The    final argument may contain whitespace and/or newlines if    `final_argument_whitespace` is True.    If the form of the arguments is more complex, specify only one    argument (either required or optional) and set    `final_argument_whitespace` to True; the client code must do any    context-sensitive parsing.    When a directive implementation is being run, the directive class    is instantiated, and the `run()` method is executed.  During    instantiation, the following instance variables are set:    - ``name`` is the directive type or name (string).    - ``arguments`` is the list of positional arguments (strings).    - ``options`` is a dictionary mapping option names (strings) to      values (type depends on option conversion functions; see      `option_spec` above).    - ``content`` is a list of strings, the directive content line by line.    - ``lineno`` is the line number of the first line of the directive.    - ``content_offset`` is the line offset of the first line of the content from      the beginning of the current input.  Used when initiating a nested parse.    - ``block_text`` is a string containing the entire directive.    - ``state`` is the state which called the directive function.    - ``state_machine`` is the state machine which controls the state which called      the directive function.    Directive functions return a list of nodes which will be inserted    into the document tree at the point where the directive was    encountered.  This can be an empty list if there is nothing to    insert.    For ordinary directives, the list must contain body elements or    structural elements.  Some directives are intended specifically    for substitution definitions, and must return a list of `Text`    nodes and/or inline elements (suitable for inline insertion, in    place of the substitution reference).  Such directives must verify    substitution definition context, typically using code like this::        if not isinstance(state, states.SubstitutionDef):            error = state_machine.reporter.error(                'Invalid context: the "%s" directive can only be used '                'within a substitution definition.' % (name),                nodes.literal_block(block_text, block_text), line=lineno)            return [error]>>>

Option Conversion Functions

An option specification (Directive.option_spec) must be defineddetailing the options available to the directive. An option spec is amapping of option name to conversion function; conversion functionsare applied to each option value to check validity and convert them tothe expected type. Python's built-in conversion functions are oftenusable for this, such asint,float. Other useful conversionfunctions are included in thedocutils.parsers.rst.directivespackage (in the__init__.py module):

A further utility function,choice, is supplied to enableoptions whose argument must be a member of a finite set of possiblevalues. A custom conversion function must be written to use it.For example:

from docutils.parsers.rst import directivesdef yesno(argument):    return directives.choice(argument, ('yes', 'no'))

For example, here is an option spec for a directive which allows twooptions, "name" and "value", each with an option argument:

option_spec = {'name': unchanged, 'value': int}

Error Handling

If your directive implementation encounters an error duringprocessing, you should callself.error() inside therun()method:

if error_condition:    raise self.error('Error message.')

Theself.error() method will immediately raise an exception thatwill be caught by the reStructuredText directive handler. Thedirective handler will then insert an error-level system message inthe document at the place where the directive occurred.

Instead ofself.error, you can also useself.severe andself.warning for more or less severe problems.

If you want to return a system messageand document contents, you need tocreate the system message yourself instead of using theself.errorconvenience method:

def run(self):    # Create node(s).    node = nodes.paragraph(...)    # Node list to return.    node_list = [node]    if error_condition:         # Create system message.         error = self.reporter.error(             'Error in "%s" directive: Your error message.' % self.name,             nodes.literal_block(block_text, block_text), line=lineno)         node_list.append(error)    return node_list

Register the Directive

Examples

For the most direct and accurate information, "Use the Source, Luke!".All standard directives are documented inreStructuredTextDirectives, and the source code implementing them is located in thedocutils/parsers/rst/directives package. The__init__.pymodule contains a mapping of directive name to module and functionname. Several representative directives are described below.

Admonitions

Admonition directives, such as "note" and "caution", are quitesimple. They have no directive arguments or options. Admonitiondirective content is interpreted as ordinary reStructuredText.

The resulting document tree for a simple reStructuredText line".. note:: This is a note." looks as follows:

<note>
<paragraph>

This is a note.

The directive class for the "note" directive simply derives from ageneric admonition directive class:

class Note(BaseAdmonition):    node_class = nodes.note

Note that the only thing distinguishing the various admonitiondirectives is the element (node class) generated. In the code above,the node class is set as a class attribute and is read by therun() method ofBaseAdmonition, where the actual processingtakes place:

# Import Docutils document tree nodes module.from docutils import nodes# Import Directive base class.from docutils.parsers.rst import Directiveclass BaseAdmonition(Directive):    required_arguments = 0    optional_arguments = 0    final_argument_whitespace = True    option_spec = {}    has_content = True    node_class = None    """Subclasses must set this to the appropriate admonition node class."""    def run(self):        # Raise an error if the directive does not have contents.        self.assert_has_content()        text = '\n'.join(self.content)        # Create the admonition node, to be populated by `nested_parse`.        admonition_node = self.node_class(rawsource=text)        # Parse the directive contents.        self.state.nested_parse(self.content, self.content_offset,                                admonition_node)        return [admonition_node]

Three things are noteworthy in therun() method above:

  • Theadmonition_node = self.node_class(text) line creates thewrapper element, using the class set by the specific admonitionsubclasses (as in note,node_class = nodes.note).

  • The call tostate.nested_parse() is what does the actualprocessing. It parses the directive content and adds any generatedelements as child elements ofadmonition_node.

  • If there was no directive content, theassert_has_content()convenience method raises an error exception by callingself.error() (seeError Handling above).

"image"

The "image" directive is used to insert a picture into a document.This directive has one argument, the path to the image file, andsupports several options. There is no directive content. Here's anearly version of the image directive class:

# Import Docutils document tree nodes module.from docutils import nodes# Import ``directives`` module (contains conversion functions).from docutils.parsers.rst import directives# Import Directive base class.from docutils.parsers.rst import Directivedef align(argument):    """Conversion function for the "align" option."""    return directives.choice(argument, ('left', 'center', 'right'))class Image(Directive):    required_arguments = 1    optional_arguments = 0    final_argument_whitespace = True    option_spec = {'alt': directives.unchanged,                   'height': directives.nonnegative_int,                   'width': directives.nonnegative_int,                   'scale': directives.nonnegative_int,                   'align': align,                   }    has_content = False    def run(self):        reference = directives.uri(self.arguments[0])        self.options['uri'] = reference        image_node = nodes.image(rawsource=self.block_text,                                 **self.options)        return [image_node]

Several things are noteworthy in the code above:

  • The "image" directive requires a single argument, which is allowedto contain whitespace (final_argument_whitespace = True). Thisis to allow for long URLs which may span multiple lines. The firstline of therun() method joins the URL, discarding any embeddedwhitespace.

  • The reference is added to theoptions dictionary under the"uri" key; this becomes an attribute of thenodes.image elementobject. Any other attributes have already been set explicitly inthe reStructuredText source text.

The Pending Element

Directives that cause actions to be performedafter the completedocument tree has been generated can be implemented using apending node. Thepending node causes atransform to be runafter the document has been parsed.

For an example usage of thepending node, see the implementationof thecontents directive indocutils.parsers.rst.directives.parts.


[8]ページ先頭

©2009-2025 Movatter.jp