PageRenderTime 187ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/documentor/libraries/Sphinx-1.1.3-py3.2/sphinx/roles.py

https://github.com/tictactatic/Superdesk
Python | 324 lines | 276 code | 12 blank | 36 comment | 12 complexity | e4b8a9e1983c6da081be737466ab98b5 MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-3.0, GPL-2.0
  1. # -*- coding: utf-8 -*-
  2. """
  3. sphinx.roles
  4. ~~~~~~~~~~~~
  5. Handlers for additional ReST roles.
  6. :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
  7. :license: BSD, see LICENSE for details.
  8. """
  9. import re
  10. import warnings
  11. from docutils import nodes, utils
  12. from docutils.parsers.rst import roles
  13. from sphinx import addnodes
  14. from sphinx.locale import _
  15. from sphinx.util import ws_re
  16. from sphinx.util.nodes import split_explicit_title, process_index_entry, \
  17. set_role_source_info
  18. generic_docroles = {
  19. 'command' : nodes.strong,
  20. 'dfn' : nodes.emphasis,
  21. 'kbd' : nodes.literal,
  22. 'mailheader' : addnodes.literal_emphasis,
  23. 'makevar' : nodes.strong,
  24. 'manpage' : addnodes.literal_emphasis,
  25. 'mimetype' : addnodes.literal_emphasis,
  26. 'newsgroup' : addnodes.literal_emphasis,
  27. 'program' : nodes.strong, # XXX should be an x-ref
  28. 'regexp' : nodes.literal,
  29. }
  30. for rolename, nodeclass in generic_docroles.items():
  31. generic = roles.GenericRole(rolename, nodeclass)
  32. role = roles.CustomRole(rolename, generic, {'classes': [rolename]})
  33. roles.register_local_role(rolename, role)
  34. # -- generic cross-reference role ----------------------------------------------
  35. class XRefRole(object):
  36. """
  37. A generic cross-referencing role. To create a callable that can be used as
  38. a role function, create an instance of this class.
  39. The general features of this role are:
  40. * Automatic creation of a reference and a content node.
  41. * Optional separation of title and target with `title <target>`.
  42. * The implementation is a class rather than a function to make
  43. customization easier.
  44. Customization can be done in two ways:
  45. * Supplying constructor parameters:
  46. * `fix_parens` to normalize parentheses (strip from target, and add to
  47. title if configured)
  48. * `lowercase` to lowercase the target
  49. * `nodeclass` and `innernodeclass` select the node classes for
  50. the reference and the content node
  51. * Subclassing and overwriting `process_link()` and/or `result_nodes()`.
  52. """
  53. nodeclass = addnodes.pending_xref
  54. innernodeclass = nodes.literal
  55. def __init__(self, fix_parens=False, lowercase=False,
  56. nodeclass=None, innernodeclass=None, warn_dangling=False):
  57. self.fix_parens = fix_parens
  58. self.lowercase = lowercase
  59. self.warn_dangling = warn_dangling
  60. if nodeclass is not None:
  61. self.nodeclass = nodeclass
  62. if innernodeclass is not None:
  63. self.innernodeclass = innernodeclass
  64. def _fix_parens(self, env, has_explicit_title, title, target):
  65. if not has_explicit_title:
  66. if title.endswith('()'):
  67. # remove parentheses
  68. title = title[:-2]
  69. if env.config.add_function_parentheses:
  70. # add them back to all occurrences if configured
  71. title += '()'
  72. # remove parentheses from the target too
  73. if target.endswith('()'):
  74. target = target[:-2]
  75. return title, target
  76. def __call__(self, typ, rawtext, text, lineno, inliner,
  77. options={}, content=[]):
  78. env = inliner.document.settings.env
  79. if not typ:
  80. typ = env.config.default_role
  81. else:
  82. typ = typ.lower()
  83. if ':' not in typ:
  84. domain, role = '', typ
  85. classes = ['xref', role]
  86. else:
  87. domain, role = typ.split(':', 1)
  88. classes = ['xref', domain, '%s-%s' % (domain, role)]
  89. # if the first character is a bang, don't cross-reference at all
  90. if text[0:1] == '!':
  91. text = utils.unescape(text)[1:]
  92. if self.fix_parens:
  93. text, tgt = self._fix_parens(env, False, text, "")
  94. innernode = self.innernodeclass(rawtext, text, classes=classes)
  95. return self.result_nodes(inliner.document, env, innernode,
  96. is_ref=False)
  97. # split title and target in role content
  98. has_explicit_title, title, target = split_explicit_title(text)
  99. title = utils.unescape(title)
  100. target = utils.unescape(target)
  101. # fix-up title and target
  102. if self.lowercase:
  103. target = target.lower()
  104. if self.fix_parens:
  105. title, target = self._fix_parens(
  106. env, has_explicit_title, title, target)
  107. # create the reference node
  108. refnode = self.nodeclass(rawtext, reftype=role, refdomain=domain,
  109. refexplicit=has_explicit_title)
  110. # we may need the line number for warnings
  111. set_role_source_info(inliner, lineno, refnode)
  112. title, target = self.process_link(
  113. env, refnode, has_explicit_title, title, target)
  114. # now that the target and title are finally determined, set them
  115. refnode['reftarget'] = target
  116. refnode += self.innernodeclass(rawtext, title, classes=classes)
  117. # we also need the source document
  118. refnode['refdoc'] = env.docname
  119. refnode['refwarn'] = self.warn_dangling
  120. # result_nodes allow further modification of return values
  121. return self.result_nodes(inliner.document, env, refnode, is_ref=True)
  122. # methods that can be overwritten
  123. def process_link(self, env, refnode, has_explicit_title, title, target):
  124. """Called after parsing title and target text, and creating the
  125. reference node (given in *refnode*). This method can alter the
  126. reference node and must return a new (or the same) ``(title, target)``
  127. tuple.
  128. """
  129. return title, ws_re.sub(' ', target)
  130. def result_nodes(self, document, env, node, is_ref):
  131. """Called before returning the finished nodes. *node* is the reference
  132. node if one was created (*is_ref* is then true), else the content node.
  133. This method can add other nodes and must return a ``(nodes, messages)``
  134. tuple (the usual return value of a role function).
  135. """
  136. return [node], []
  137. def indexmarkup_role(typ, rawtext, etext, lineno, inliner,
  138. options={}, content=[]):
  139. """Role for PEP/RFC references that generate an index entry."""
  140. env = inliner.document.settings.env
  141. if not typ:
  142. typ = env.config.default_role
  143. else:
  144. typ = typ.lower()
  145. text = utils.unescape(etext)
  146. targetid = 'index-%s' % env.new_serialno('index')
  147. indexnode = addnodes.index()
  148. targetnode = nodes.target('', '', ids=[targetid])
  149. inliner.document.note_explicit_target(targetnode)
  150. if typ == 'pep':
  151. indexnode['entries'] = [
  152. ('single', _('Python Enhancement Proposals; PEP %s') % text,
  153. targetid, '')]
  154. anchor = ''
  155. anchorindex = text.find('#')
  156. if anchorindex > 0:
  157. text, anchor = text[:anchorindex], text[anchorindex:]
  158. try:
  159. pepnum = int(text)
  160. except ValueError:
  161. msg = inliner.reporter.error('invalid PEP number %s' % text,
  162. line=lineno)
  163. prb = inliner.problematic(rawtext, rawtext, msg)
  164. return [prb], [msg]
  165. ref = inliner.document.settings.pep_base_url + 'pep-%04d' % pepnum
  166. sn = nodes.strong('PEP '+text, 'PEP '+text)
  167. rn = nodes.reference('', '', internal=False, refuri=ref+anchor,
  168. classes=[typ])
  169. rn += sn
  170. return [indexnode, targetnode, rn], []
  171. elif typ == 'rfc':
  172. indexnode['entries'] = [('single', 'RFC; RFC %s' % text, targetid, '')]
  173. anchor = ''
  174. anchorindex = text.find('#')
  175. if anchorindex > 0:
  176. text, anchor = text[:anchorindex], text[anchorindex:]
  177. try:
  178. rfcnum = int(text)
  179. except ValueError:
  180. msg = inliner.reporter.error('invalid RFC number %s' % text,
  181. line=lineno)
  182. prb = inliner.problematic(rawtext, rawtext, msg)
  183. return [prb], [msg]
  184. ref = inliner.document.settings.rfc_base_url + inliner.rfc_url % rfcnum
  185. sn = nodes.strong('RFC '+text, 'RFC '+text)
  186. rn = nodes.reference('', '', internal=False, refuri=ref+anchor,
  187. classes=[typ])
  188. rn += sn
  189. return [indexnode, targetnode, rn], []
  190. _amp_re = re.compile(r'(?<!&)&(?![&\s])')
  191. def menusel_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
  192. text = utils.unescape(text)
  193. if typ == 'menuselection':
  194. text = text.replace('-->', '\N{TRIANGULAR BULLET}')
  195. spans = _amp_re.split(text)
  196. node = nodes.emphasis(rawtext=rawtext)
  197. for i, span in enumerate(spans):
  198. span = span.replace('&&', '&')
  199. if i == 0:
  200. if len(span) > 0:
  201. textnode = nodes.Text(span)
  202. node += textnode
  203. continue
  204. accel_node = nodes.inline()
  205. letter_node = nodes.Text(span[0])
  206. accel_node += letter_node
  207. accel_node['classes'].append('accelerator')
  208. node += accel_node
  209. textnode = nodes.Text(span[1:])
  210. node += textnode
  211. node['classes'].append(typ)
  212. return [node], []
  213. _litvar_re = re.compile('{([^}]+)}')
  214. def emph_literal_role(typ, rawtext, text, lineno, inliner,
  215. options={}, content=[]):
  216. text = utils.unescape(text)
  217. pos = 0
  218. retnode = nodes.literal(role=typ.lower(), classes=[typ])
  219. for m in _litvar_re.finditer(text):
  220. if m.start() > pos:
  221. txt = text[pos:m.start()]
  222. retnode += nodes.Text(txt, txt)
  223. retnode += nodes.emphasis(m.group(1), m.group(1))
  224. pos = m.end()
  225. if pos < len(text):
  226. retnode += nodes.Text(text[pos:], text[pos:])
  227. return [retnode], []
  228. _abbr_re = re.compile('\((.*)\)$', re.S)
  229. def abbr_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
  230. text = utils.unescape(text)
  231. m = _abbr_re.search(text)
  232. if m is None:
  233. return [addnodes.abbreviation(text, text)], []
  234. abbr = text[:m.start()].strip()
  235. expl = m.group(1)
  236. return [addnodes.abbreviation(abbr, abbr, explanation=expl)], []
  237. def index_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
  238. # create new reference target
  239. env = inliner.document.settings.env
  240. targetid = 'index-%s' % env.new_serialno('index')
  241. targetnode = nodes.target('', '', ids=[targetid])
  242. # split text and target in role content
  243. has_explicit_title, title, target = split_explicit_title(text)
  244. title = utils.unescape(title)
  245. target = utils.unescape(target)
  246. # if an explicit target is given, we can process it as a full entry
  247. if has_explicit_title:
  248. entries = process_index_entry(target, targetid)
  249. # otherwise we just create a "single" entry
  250. else:
  251. # but allow giving main entry
  252. main = ''
  253. if target.startswith('!'):
  254. target = target[1:]
  255. title = title[1:]
  256. main = 'main'
  257. entries = [('single', target, targetid, main)]
  258. indexnode = addnodes.index()
  259. indexnode['entries'] = entries
  260. textnode = nodes.Text(title, title)
  261. return [indexnode, targetnode, textnode], []
  262. specific_docroles = {
  263. # links to download references
  264. 'download': XRefRole(nodeclass=addnodes.download_reference),
  265. # links to documents
  266. 'doc': XRefRole(warn_dangling=True),
  267. 'pep': indexmarkup_role,
  268. 'rfc': indexmarkup_role,
  269. 'guilabel': menusel_role,
  270. 'menuselection': menusel_role,
  271. 'file': emph_literal_role,
  272. 'samp': emph_literal_role,
  273. 'abbr': abbr_role,
  274. 'index': index_role,
  275. }
  276. for rolename, func in specific_docroles.items():
  277. roles.register_local_role(rolename, func)
  278. # backwards compatibility alias
  279. def xfileref_role(*args, **kwds):
  280. warnings.warn('xfileref_role is deprecated, use XRefRole',
  281. DeprecationWarning, stacklevel=2)
  282. return XRefRole()(*args, **kwds)