# -*- coding: utf-8 -*- """ sphinx.directives ~~~~~~~~~~~~~~~~~ Handlers for additional ReST directives. :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ import re from docutils.parsers.rst import Directive, directives from docutils.parsers.rst.directives import images from sphinx import addnodes from sphinx.util.docfields import DocFieldTransformer # import and register directives from sphinx.directives.code import * from sphinx.directives.other import * # allow units for the figure's "figwidth" try: images.Figure.option_spec['figwidth'] = \ directives.length_or_percentage_or_unitless except AttributeError: images.figure.options['figwidth'] = \ directives.length_or_percentage_or_unitless # RE to strip backslash escapes nl_escape_re = re.compile(r'\\\n') strip_backslash_re = re.compile(r'\\(.)') class ObjectDescription(Directive): """ Directive to describe a class, function or similar object. Not used directly, but subclassed (in domain-specific directives) to add custom behavior. """ has_content = True required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True option_spec = { 'noindex': directives.flag, } # types of doc fields that this directive handles, see sphinx.util.docfields doc_field_types = [] def get_signatures(self): """ Retrieve the signatures to document from the directive arguments. By default, signatures are given as arguments, one per line. Backslash-escaping of newlines is supported. """ lines = nl_escape_re.sub('', self.arguments[0]).split('\n') # remove backslashes to support (dummy) escapes; helps Vim highlighting return [strip_backslash_re.sub(r'\1', line.strip()) for line in lines] def handle_signature(self, sig, signode): """ Parse the signature *sig* into individual nodes and append them to *signode*. If ValueError is raised, parsing is aborted and the whole *sig* is put into a single desc_name node. The return value should be a value that identifies the object. It is passed to :meth:`add_target_and_index()` unchanged, and otherwise only used to skip duplicates. """ raise ValueError def add_target_and_index(self, name, sig, signode): """ Add cross-reference IDs and entries to self.indexnode, if applicable. *name* is whatever :meth:`handle_signature()` returned. """ return # do nothing by default def before_content(self): """ Called before parsing content. Used to set information about the current directive context on the build environment. """ pass def after_content(self): """ Called after parsing content. Used to reset information about the current directive context on the build environment. """ pass def run(self): """ Main directive entry function, called by docutils upon encountering the directive. This directive is meant to be quite easily subclassable, so it delegates to several additional methods. What it does: * find out if called as a domain-specific directive, set self.domain * create a `desc` node to fit all description inside * parse standard options, currently `noindex` * create an index node if needed as self.indexnode * parse all given signatures (as returned by self.get_signatures()) using self.handle_signature(), which should either return a name or raise ValueError * add index entries using self.add_target_and_index() * parse the content and handle doc fields in it """ if ':' in self.name: self.domain, self.objtype = self.name.split(':', 1) else: self.domain, self.objtype = '', self.name self.env = self.state.document.settings.env self.indexnode = addnodes.index(entries=[]) node = addnodes.desc() node.document = self.state.document node['domain'] = self.domain # 'desctype' is a backwards compatible attribute node['objtype'] = node['desctype'] = self.objtype node['noindex'] = noindex = ('noindex' in self.options) self.names = [] signatures = self.get_signatures() for i, sig in enumerate(signatures): # add a signature node for each signature in the current unit # and add a reference target for it signode = addnodes.desc_signature(sig, '') signode['first'] = False node.append(signode) try: # name can also be a tuple, e.g. (classname, objname); # this is strictly domain-specific (i.e. no assumptions may # be made in this base class) name = self.handle_signature(sig, signode) except ValueError: # signature parsing failed signode.clear() signode += addnodes.desc_name(sig, sig) continue # we don't want an index entry here if not noindex and name not in self.names: # only add target and index entry if this is the first # description of the object with this name in this desc block self.names.append(name) self.add_target_and_index(name, sig, signode) contentnode = addnodes.desc_content() node.append(contentnode) if self.names: # needed for association of version{added,changed} directives self.env.temp_data['object'] = self.names[0] self.before_content() self.state.nested_parse(self.content, self.content_offset, contentnode) DocFieldTransformer(self).transform_all(contentnode) self.env.temp_data['object'] = None self.after_content() return [self.indexnode, node] # backwards compatible old name DescDirective = ObjectDescription class DefaultDomain(Directive): """ Directive to (re-)set the default domain for this source file. """ has_content = False required_arguments = 1 optional_arguments = 0 final_argument_whitespace = False option_spec = {} def run(self): env = self.state.document.settings.env domain_name = self.arguments[0].lower() # if domain_name not in env.domains: # # try searching by label # for domain in env.domains.itervalues(): # if domain.label.lower() == domain_name: # domain_name = domain.name # break env.temp_data['default_domain'] = env.domains.get(domain_name) return [] directives.register_directive('default-domain', DefaultDomain) directives.register_directive('describe', ObjectDescription) # new, more consistent, name directives.register_directive('object', ObjectDescription)