/documentor/libraries/Sphinx-1.1.3-py3.2/sphinx/util/nodes.py

https://github.com/tictactatic/Superdesk · Python · 206 lines · 136 code · 33 blank · 37 comment · 34 complexity · c4ec161cd49bd5b403cf0dbd9a482cfa MD5 · raw file

  1. # -*- coding: utf-8 -*-
  2. """
  3. sphinx.util.nodes
  4. ~~~~~~~~~~~~~~~~~
  5. Docutils node-related utility functions for Sphinx.
  6. :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
  7. :license: BSD, see LICENSE for details.
  8. """
  9. import re
  10. from docutils import nodes
  11. from sphinx import addnodes
  12. from sphinx.locale import pairindextypes
  13. class WarningStream(object):
  14. def __init__(self, warnfunc):
  15. self.warnfunc = warnfunc
  16. self._re = re.compile(r'\((DEBUG|INFO|WARNING|ERROR|SEVERE)/[0-4]\)')
  17. def write(self, text):
  18. text = text.strip()
  19. if text:
  20. self.warnfunc(self._re.sub(r'\1:', text), None, '')
  21. # \x00 means the "<" was backslash-escaped
  22. explicit_title_re = re.compile(r'^(.+?)\s*(?<!\x00)<(.*?)>$', re.DOTALL)
  23. caption_ref_re = explicit_title_re # b/w compat alias
  24. IGNORED_NODES = (
  25. nodes.Invisible,
  26. nodes.Inline,
  27. nodes.literal_block,
  28. nodes.doctest_block,
  29. #XXX there are probably more
  30. )
  31. def extract_messages(doctree):
  32. """Extract translatable messages from a document tree."""
  33. for node in doctree.traverse(nodes.TextElement):
  34. if not node.source:
  35. continue # built-in message
  36. if isinstance(node, IGNORED_NODES):
  37. continue
  38. # <field_name>orphan</field_name>
  39. # XXX ignore all metadata (== docinfo)
  40. if isinstance(node, nodes.field_name) and node.children[0] == 'orphan':
  41. continue
  42. msg = node.rawsource.replace('\n', ' ').strip()
  43. # XXX nodes rendering empty are likely a bug in sphinx.addnodes
  44. if msg:
  45. yield node, msg
  46. def nested_parse_with_titles(state, content, node):
  47. """Version of state.nested_parse() that allows titles and does not require
  48. titles to have the same decoration as the calling document.
  49. This is useful when the parsed content comes from a completely different
  50. context, such as docstrings.
  51. """
  52. # hack around title style bookkeeping
  53. surrounding_title_styles = state.memo.title_styles
  54. surrounding_section_level = state.memo.section_level
  55. state.memo.title_styles = []
  56. state.memo.section_level = 0
  57. try:
  58. return state.nested_parse(content, 0, node, match_titles=1)
  59. finally:
  60. state.memo.title_styles = surrounding_title_styles
  61. state.memo.section_level = surrounding_section_level
  62. def clean_astext(node):
  63. """Like node.astext(), but ignore images."""
  64. node = node.deepcopy()
  65. for img in node.traverse(nodes.image):
  66. img['alt'] = ''
  67. return node.astext()
  68. def split_explicit_title(text):
  69. """Split role content into title and target, if given."""
  70. match = explicit_title_re.match(text)
  71. if match:
  72. return True, match.group(1), match.group(2)
  73. return False, text, text
  74. indextypes = [
  75. 'single', 'pair', 'double', 'triple', 'see', 'seealso',
  76. ]
  77. def process_index_entry(entry, targetid):
  78. indexentries = []
  79. entry = entry.strip()
  80. oentry = entry
  81. main = ''
  82. if entry.startswith('!'):
  83. main = 'main'
  84. entry = entry[1:].lstrip()
  85. for type in pairindextypes:
  86. if entry.startswith(type+':'):
  87. value = entry[len(type)+1:].strip()
  88. value = pairindextypes[type] + '; ' + value
  89. indexentries.append(('pair', value, targetid, main))
  90. break
  91. else:
  92. for type in indextypes:
  93. if entry.startswith(type+':'):
  94. value = entry[len(type)+1:].strip()
  95. if type == 'double':
  96. type = 'pair'
  97. indexentries.append((type, value, targetid, main))
  98. break
  99. # shorthand notation for single entries
  100. else:
  101. for value in oentry.split(','):
  102. value = value.strip()
  103. main = ''
  104. if value.startswith('!'):
  105. main = 'main'
  106. value = value[1:].lstrip()
  107. if not value:
  108. continue
  109. indexentries.append(('single', value, targetid, main))
  110. return indexentries
  111. def inline_all_toctrees(builder, docnameset, docname, tree, colorfunc):
  112. """Inline all toctrees in the *tree*.
  113. Record all docnames in *docnameset*, and output docnames with *colorfunc*.
  114. """
  115. tree = tree.deepcopy()
  116. for toctreenode in tree.traverse(addnodes.toctree):
  117. newnodes = []
  118. includefiles = list(map(str, toctreenode['includefiles']))
  119. for includefile in includefiles:
  120. try:
  121. builder.info(colorfunc(includefile) + " ", nonl=1)
  122. subtree = inline_all_toctrees(builder, docnameset, includefile,
  123. builder.env.get_doctree(includefile), colorfunc)
  124. docnameset.add(includefile)
  125. except Exception:
  126. builder.warn('toctree contains ref to nonexisting '
  127. 'file %r' % includefile,
  128. builder.env.doc2path(docname))
  129. else:
  130. sof = addnodes.start_of_file(docname=includefile)
  131. sof.children = subtree.children
  132. newnodes.append(sof)
  133. toctreenode.parent.replace(toctreenode, newnodes)
  134. return tree
  135. def make_refnode(builder, fromdocname, todocname, targetid, child, title=None):
  136. """Shortcut to create a reference node."""
  137. node = nodes.reference('', '', internal=True)
  138. if fromdocname == todocname:
  139. node['refid'] = targetid
  140. else:
  141. node['refuri'] = (builder.get_relative_uri(fromdocname, todocname)
  142. + '#' + targetid)
  143. if title:
  144. node['reftitle'] = title
  145. node.append(child)
  146. return node
  147. def set_source_info(directive, node):
  148. node.source, node.line = \
  149. directive.state_machine.get_source_and_line(directive.lineno)
  150. def set_role_source_info(inliner, lineno, node):
  151. try:
  152. node.source, node.line = \
  153. inliner.reporter.locator(lineno)
  154. except AttributeError:
  155. # docutils 0.9+
  156. node.source, node.line = inliner.reporter.get_source_and_line(lineno)
  157. # monkey-patch Node.__contains__ to get consistent "in" operator behavior
  158. # across docutils versions
  159. def _new_contains(self, key):
  160. # support both membership test for children and attributes
  161. # (has_key is translated to "in" by 2to3)
  162. if isinstance(key, str):
  163. return key in self.attributes
  164. return key in self.children
  165. nodes.Node.__contains__ = _new_contains
  166. # monkey-patch Element.copy to copy the rawsource
  167. def _new_copy(self):
  168. return self.__class__(self.rawsource, **self.attributes)
  169. nodes.Element.copy = _new_copy