PageRenderTime 50ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/documentation/doctools/tags/0.4.1/sphinx/directives/desc.py

https://github.com/creasyw/IMTAphy
Python | 560 lines | 513 code | 22 blank | 25 comment | 75 complexity | 7c2add32d43eda8b7f13c8181dbe3a25 MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-2.0, LGPL-2.1
  1. # -*- coding: utf-8 -*-
  2. """
  3. sphinx.directives.desc
  4. ~~~~~~~~~~~~~~~~~~~~~~
  5. :copyright: 2007-2008 by Georg Brandl.
  6. :license: BSD.
  7. """
  8. import re
  9. import string
  10. from docutils import nodes
  11. from docutils.parsers.rst import directives
  12. from sphinx import addnodes
  13. ws_re = re.compile(r'\s+')
  14. # ------ information units ---------------------------------------------------------
  15. def desc_index_text(desctype, module, name):
  16. if desctype == 'function':
  17. if not module:
  18. return '%s() (built-in function)' % name
  19. return '%s() (in module %s)' % (name, module)
  20. elif desctype == 'data':
  21. if not module:
  22. return '%s (built-in variable)' % name
  23. return '%s (in module %s)' % (name, module)
  24. elif desctype == 'class':
  25. return '%s (class in %s)' % (name, module)
  26. elif desctype == 'exception':
  27. return name
  28. elif desctype == 'method':
  29. try:
  30. clsname, methname = name.rsplit('.', 1)
  31. except ValueError:
  32. if module:
  33. return '%s() (in module %s)' % (name, module)
  34. else:
  35. return '%s()' % name
  36. if module:
  37. return '%s() (%s.%s method)' % (methname, module, clsname)
  38. else:
  39. return '%s() (%s method)' % (methname, clsname)
  40. elif desctype == 'staticmethod':
  41. try:
  42. clsname, methname = name.rsplit('.', 1)
  43. except ValueError:
  44. if module:
  45. return '%s() (in module %s)' % (name, module)
  46. else:
  47. return '%s()' % name
  48. if module:
  49. return '%s() (%s.%s static method)' % (methname, module, clsname)
  50. else:
  51. return '%s() (%s static method)' % (methname, clsname)
  52. elif desctype == 'attribute':
  53. try:
  54. clsname, attrname = name.rsplit('.', 1)
  55. except ValueError:
  56. if module:
  57. return '%s (in module %s)' % (name, module)
  58. else:
  59. return name
  60. if module:
  61. return '%s (%s.%s attribute)' % (attrname, module, clsname)
  62. else:
  63. return '%s (%s attribute)' % (attrname, clsname)
  64. elif desctype == 'opcode':
  65. return '%s (opcode)' % name
  66. elif desctype == 'cfunction':
  67. return '%s (C function)' % name
  68. elif desctype == 'cmember':
  69. return '%s (C member)' % name
  70. elif desctype == 'cmacro':
  71. return '%s (C macro)' % name
  72. elif desctype == 'ctype':
  73. return '%s (C type)' % name
  74. elif desctype == 'cvar':
  75. return '%s (C variable)' % name
  76. else:
  77. raise ValueError("unhandled descenv: %s" % desctype)
  78. # ------ make field lists (like :param foo:) in desc bodies prettier
  79. doc_fields_with_arg = {
  80. 'param': 'param',
  81. 'parameter': 'param',
  82. 'arg': 'param',
  83. 'argument': 'param',
  84. 'keyword': 'param',
  85. 'kwarg': 'param',
  86. 'kwparam': 'param',
  87. 'type': 'type',
  88. 'raises': 'Raises',
  89. 'raise': 'Raises',
  90. 'exception': 'Raises',
  91. 'except': 'Raises',
  92. 'var': 'Variable',
  93. 'ivar': 'Variable',
  94. 'cvar': 'Variable',
  95. 'returns': 'Returns',
  96. 'return': 'Returns',
  97. }
  98. doc_fields_without_arg = {
  99. 'returns': 'Returns',
  100. 'return': 'Returns',
  101. 'rtype': 'Return type',
  102. }
  103. def handle_doc_fields(node):
  104. # don't traverse, only handle field lists that are immediate children
  105. for child in node.children:
  106. if not isinstance(child, nodes.field_list):
  107. continue
  108. params = None
  109. param_nodes = {}
  110. param_types = {}
  111. new_list = nodes.field_list()
  112. for field in child:
  113. fname, fbody = field
  114. try:
  115. typ, obj = fname.astext().split(None, 1)
  116. typ = doc_fields_with_arg[typ]
  117. if len(fbody.children) == 1 and \
  118. isinstance(fbody.children[0], nodes.paragraph):
  119. children = fbody.children[0].children
  120. else:
  121. children = fbody.children
  122. if typ == 'param':
  123. if not params:
  124. pfield = nodes.field()
  125. pfield += nodes.field_name('Parameters', 'Parameters')
  126. pfield += nodes.field_body()
  127. params = nodes.bullet_list()
  128. pfield[1] += params
  129. new_list += pfield
  130. dlitem = nodes.list_item()
  131. dlpar = nodes.paragraph()
  132. dlpar += nodes.emphasis(obj, obj)
  133. dlpar += nodes.Text(' -- ', ' -- ')
  134. dlpar += children
  135. param_nodes[obj] = dlpar
  136. dlitem += dlpar
  137. params += dlitem
  138. elif typ == 'type':
  139. param_types[obj] = fbody.astext()
  140. else:
  141. fieldname = typ + ' ' + obj
  142. nfield = nodes.field()
  143. nfield += nodes.field_name(fieldname, fieldname)
  144. nfield += nodes.field_body()
  145. nfield[1] += fbody.children
  146. new_list += nfield
  147. except (KeyError, ValueError):
  148. fnametext = fname.astext()
  149. try:
  150. typ = doc_fields_without_arg[fnametext]
  151. except KeyError:
  152. # at least capitalize the field name
  153. typ = fnametext.capitalize()
  154. fname[0] = nodes.Text(typ)
  155. new_list += field
  156. for param, type in param_types.iteritems():
  157. if param in param_nodes:
  158. param_nodes[param].insert(1, nodes.Text(' (%s)' % type))
  159. child.replace_self(new_list)
  160. # ------ functions to parse a Python or C signature and create desc_* nodes.
  161. py_sig_re = re.compile(
  162. r'''^ ([\w.]*\.)? # class name(s)
  163. (\w+) \s* # thing name
  164. (?: \((.*)\) # optional arguments
  165. (\s* -> \s* .*)? )? $ # optional return annotation
  166. ''', re.VERBOSE)
  167. py_paramlist_re = re.compile(r'([\[\],])') # split at '[', ']' and ','
  168. def parse_py_signature(signode, sig, desctype, module, env):
  169. """
  170. Transform a python signature into RST nodes.
  171. Return (fully qualified name of the thing, classname if any).
  172. If inside a class, the current class name is handled intelligently:
  173. * it is stripped from the displayed name if present
  174. * it is added to the full name (return value) if not present
  175. """
  176. m = py_sig_re.match(sig)
  177. if m is None:
  178. raise ValueError
  179. classname, name, arglist, retann = m.groups()
  180. if env.currclass:
  181. add_module = False
  182. if classname and classname.startswith(env.currclass):
  183. fullname = classname + name
  184. # class name is given again in the signature
  185. classname = classname[len(env.currclass):].lstrip('.')
  186. elif classname:
  187. # class name is given in the signature, but different
  188. # (shouldn't happen)
  189. fullname = env.currclass + '.' + classname + name
  190. else:
  191. # class name is not given in the signature
  192. fullname = env.currclass + '.' + name
  193. else:
  194. add_module = True
  195. fullname = classname and classname + name or name
  196. if desctype == 'staticmethod':
  197. signode += addnodes.desc_annotation('static ', 'static ')
  198. if classname:
  199. signode += addnodes.desc_addname(classname, classname)
  200. # exceptions are a special case, since they are documented in the
  201. # 'exceptions' module.
  202. elif add_module and env.config.add_module_names and \
  203. module and module != 'exceptions':
  204. nodetext = module + '.'
  205. signode += addnodes.desc_addname(nodetext, nodetext)
  206. signode += addnodes.desc_name(name, name)
  207. if not arglist:
  208. if desctype in ('function', 'method', 'staticmethod'):
  209. # for callables, add an empty parameter list
  210. signode += addnodes.desc_parameterlist()
  211. return fullname, classname
  212. signode += addnodes.desc_parameterlist()
  213. stack = [signode[-1]]
  214. for token in py_paramlist_re.split(arglist):
  215. if token == '[':
  216. opt = addnodes.desc_optional()
  217. stack[-1] += opt
  218. stack.append(opt)
  219. elif token == ']':
  220. try:
  221. stack.pop()
  222. except IndexError:
  223. raise ValueError
  224. elif not token or token == ',' or token.isspace():
  225. pass
  226. else:
  227. token = token.strip()
  228. stack[-1] += addnodes.desc_parameter(token, token)
  229. if len(stack) != 1:
  230. raise ValueError
  231. if retann:
  232. retann = u' \N{RIGHTWARDS ARROW} ' + retann.strip()[2:]
  233. signode += addnodes.desc_type(retann, retann)
  234. return fullname, classname
  235. c_sig_re = re.compile(
  236. r'''^([^(]*?) # return type
  237. ([\w:]+) \s* # thing name (colon allowed for C++ class names)
  238. (?: \((.*)\) )? # optionally arguments
  239. (\s+const)? $ # const specifier
  240. ''', re.VERBOSE)
  241. c_funcptr_sig_re = re.compile(
  242. r'''^([^(]+?) # return type
  243. (\( [^()]+ \)) \s* # name in parentheses
  244. \( (.*) \) # arguments
  245. (\s+const)? $ # const specifier
  246. ''', re.VERBOSE)
  247. c_funcptr_name_re = re.compile(r'^\(\s*\*\s*(.*?)\s*\)$')
  248. # RE to split at word boundaries
  249. wsplit_re = re.compile(r'(\W+)')
  250. # These C types aren't described in the reference, so don't try to create
  251. # a cross-reference to them
  252. stopwords = set(('const', 'void', 'char', 'int', 'long', 'FILE', 'struct'))
  253. def parse_c_type(node, ctype):
  254. # add cross-ref nodes for all words
  255. for part in filter(None, wsplit_re.split(ctype)):
  256. tnode = nodes.Text(part, part)
  257. if part[0] in string.letters+'_' and part not in stopwords:
  258. pnode = addnodes.pending_xref(
  259. '', reftype='ctype', reftarget=part, modname=None, classname=None)
  260. pnode += tnode
  261. node += pnode
  262. else:
  263. node += tnode
  264. def parse_c_signature(signode, sig, desctype):
  265. """Transform a C (or C++) signature into RST nodes."""
  266. # first try the function pointer signature regex, it's more specific
  267. m = c_funcptr_sig_re.match(sig)
  268. if m is None:
  269. m = c_sig_re.match(sig)
  270. if m is None:
  271. raise ValueError('no match')
  272. rettype, name, arglist, const = m.groups()
  273. signode += addnodes.desc_type("", "")
  274. parse_c_type(signode[-1], rettype)
  275. try:
  276. classname, funcname = name.split('::', 1)
  277. classname += '::'
  278. signode += addnodes.desc_addname(classname, classname)
  279. signode += addnodes.desc_name(funcname, funcname)
  280. # name (the full name) is still both parts
  281. except ValueError:
  282. signode += addnodes.desc_name(name, name)
  283. # clean up parentheses from canonical name
  284. m = c_funcptr_name_re.match(name)
  285. if m:
  286. name = m.group(1)
  287. if not arglist:
  288. if desctype == 'cfunction':
  289. # for functions, add an empty parameter list
  290. signode += addnodes.desc_parameterlist()
  291. return name
  292. paramlist = addnodes.desc_parameterlist()
  293. arglist = arglist.replace('`', '').replace('\\ ', '') # remove markup
  294. # this messes up function pointer types, but not too badly ;)
  295. args = arglist.split(',')
  296. for arg in args:
  297. arg = arg.strip()
  298. param = addnodes.desc_parameter('', '', noemph=True)
  299. try:
  300. ctype, argname = arg.rsplit(' ', 1)
  301. except ValueError:
  302. # no argument name given, only the type
  303. parse_c_type(param, arg)
  304. else:
  305. parse_c_type(param, ctype)
  306. param += nodes.emphasis(' '+argname, ' '+argname)
  307. paramlist += param
  308. signode += paramlist
  309. if const:
  310. signode += addnodes.desc_addname(const, const)
  311. return name
  312. opcode_sig_re = re.compile(r'(\w+(?:\+\d)?)\s*\((.*)\)')
  313. def parse_opcode_signature(signode, sig):
  314. """Transform an opcode signature into RST nodes."""
  315. m = opcode_sig_re.match(sig)
  316. if m is None:
  317. raise ValueError
  318. opname, arglist = m.groups()
  319. signode += addnodes.desc_name(opname, opname)
  320. paramlist = addnodes.desc_parameterlist()
  321. signode += paramlist
  322. paramlist += addnodes.desc_parameter(arglist, arglist)
  323. return opname.strip()
  324. option_desc_re = re.compile(
  325. r'(/|-|--)([-_a-zA-Z0-9]+)(\s*.*?)(?=,\s+(?:/|-|--)|$)')
  326. def parse_option_desc(signode, sig):
  327. """Transform an option description into RST nodes."""
  328. count = 0
  329. firstname = ''
  330. for m in option_desc_re.finditer(sig):
  331. prefix, optname, args = m.groups()
  332. if count:
  333. signode += addnodes.desc_addname(', ', ', ')
  334. signode += addnodes.desc_name(prefix+optname, prefix+optname)
  335. signode += addnodes.desc_addname(args, args)
  336. if not count:
  337. firstname = optname
  338. count += 1
  339. if not firstname:
  340. raise ValueError
  341. return firstname
  342. def desc_directive(desctype, arguments, options, content, lineno,
  343. content_offset, block_text, state, state_machine):
  344. env = state.document.settings.env
  345. node = addnodes.desc()
  346. node['desctype'] = desctype
  347. noindex = ('noindex' in options)
  348. node['noindex'] = noindex
  349. # remove backslashes to support (dummy) escapes; helps Vim's highlighting
  350. signatures = map(lambda s: s.strip().replace('\\', ''), arguments[0].split('\n'))
  351. names = []
  352. clsname = None
  353. module = options.get('module', env.currmodule)
  354. for i, sig in enumerate(signatures):
  355. # add a signature node for each signature in the current unit
  356. # and add a reference target for it
  357. sig = sig.strip()
  358. signode = addnodes.desc_signature(sig, '')
  359. signode['first'] = False
  360. node.append(signode)
  361. try:
  362. if desctype in ('function', 'data', 'class', 'exception',
  363. 'method', 'staticmethod', 'attribute'):
  364. name, clsname = parse_py_signature(signode, sig, desctype, module, env)
  365. elif desctype in ('cfunction', 'cmember', 'cmacro', 'ctype', 'cvar'):
  366. name = parse_c_signature(signode, sig, desctype)
  367. elif desctype == 'opcode':
  368. name = parse_opcode_signature(signode, sig)
  369. elif desctype == 'cmdoption':
  370. optname = parse_option_desc(signode, sig)
  371. if not noindex:
  372. targetname = 'cmdoption-' + optname
  373. signode['ids'].append(targetname)
  374. state.document.note_explicit_target(signode)
  375. env.note_index_entry('pair', 'command line option; %s' % sig,
  376. targetname, targetname)
  377. env.note_reftarget('option', optname, targetname)
  378. continue
  379. elif desctype == 'describe':
  380. signode.clear()
  381. signode += addnodes.desc_name(sig, sig)
  382. continue
  383. else:
  384. # another registered generic x-ref directive
  385. rolename, indextemplate, parse_node = additional_xref_types[desctype]
  386. if parse_node:
  387. fullname = parse_node(env, sig, signode)
  388. else:
  389. signode.clear()
  390. signode += addnodes.desc_name(sig, sig)
  391. # normalize whitespace like xfileref_role does
  392. fullname = ws_re.sub('', sig)
  393. if not noindex:
  394. targetname = '%s-%s' % (rolename, fullname)
  395. signode['ids'].append(targetname)
  396. state.document.note_explicit_target(signode)
  397. if indextemplate:
  398. indexentry = indextemplate % (fullname,)
  399. indextype = 'single'
  400. colon = indexentry.find(':')
  401. if colon != -1:
  402. indextype = indexentry[:colon].strip()
  403. indexentry = indexentry[colon+1:].strip()
  404. env.note_index_entry(indextype, indexentry,
  405. targetname, targetname)
  406. env.note_reftarget(rolename, fullname, targetname)
  407. # don't use object indexing below
  408. continue
  409. except ValueError, err:
  410. # signature parsing failed
  411. signode.clear()
  412. signode += addnodes.desc_name(sig, sig)
  413. continue # we don't want an index entry here
  414. # only add target and index entry if this is the first description of the
  415. # function name in this desc block
  416. if not noindex and name not in names:
  417. fullname = (module and module + '.' or '') + name
  418. # note target
  419. if fullname not in state.document.ids:
  420. signode['names'].append(fullname)
  421. signode['ids'].append(fullname)
  422. signode['first'] = (not names)
  423. state.document.note_explicit_target(signode)
  424. env.note_descref(fullname, desctype, lineno)
  425. names.append(name)
  426. env.note_index_entry('single',
  427. desc_index_text(desctype, module, name),
  428. fullname, fullname)
  429. subnode = addnodes.desc_content()
  430. # needed for automatic qualification of members
  431. clsname_set = False
  432. if desctype in ('class', 'exception') and names:
  433. env.currclass = names[0]
  434. clsname_set = True
  435. elif desctype in ('method', 'staticmethod', 'attribute') and \
  436. clsname and not env.currclass:
  437. env.currclass = clsname.strip('.')
  438. clsname_set = True
  439. # needed for association of version{added,changed} directives
  440. if names:
  441. env.currdesc = names[0]
  442. state.nested_parse(content, content_offset, subnode)
  443. handle_doc_fields(subnode)
  444. if clsname_set:
  445. env.currclass = None
  446. env.currdesc = None
  447. node.append(subnode)
  448. return [node]
  449. desc_directive.content = 1
  450. desc_directive.arguments = (1, 0, 1)
  451. desc_directive.options = {'noindex': directives.flag,
  452. 'module': directives.unchanged}
  453. desctypes = [
  454. # the Python ones
  455. 'function',
  456. 'data',
  457. 'class',
  458. 'method',
  459. 'staticmethod',
  460. 'attribute',
  461. 'exception',
  462. # the C ones
  463. 'cfunction',
  464. 'cmember',
  465. 'cmacro',
  466. 'ctype',
  467. 'cvar',
  468. # the odd one
  469. 'opcode',
  470. # for command line options
  471. 'cmdoption',
  472. # the generic one
  473. 'describe',
  474. 'envvar',
  475. ]
  476. for _name in desctypes:
  477. directives.register_directive(_name, desc_directive)
  478. # Generic cross-reference types; they can be registered in the application;
  479. # the directives are either desc_directive or target_directive
  480. additional_xref_types = {
  481. # directive name: (role name, index text, function to parse the desc node)
  482. 'envvar': ('envvar', 'environment variable; %s', None),
  483. }
  484. # ------ target --------------------------------------------------------------------
  485. def target_directive(targettype, arguments, options, content, lineno,
  486. content_offset, block_text, state, state_machine):
  487. """Generic target for user-defined cross-reference types."""
  488. env = state.document.settings.env
  489. rolename, indextemplate, _ = additional_xref_types[targettype]
  490. # normalize whitespace in fullname like xfileref_role does
  491. fullname = ws_re.sub('', arguments[0].strip())
  492. targetname = '%s-%s' % (rolename, fullname)
  493. node = nodes.target('', '', ids=[targetname])
  494. state.document.note_explicit_target(node)
  495. if indextemplate:
  496. indexentry = indextemplate % (fullname,)
  497. indextype = 'single'
  498. colon = indexentry.find(':')
  499. if colon != -1:
  500. indextype = indexentry[:colon].strip()
  501. indexentry = indexentry[colon+1:].strip()
  502. env.note_index_entry(indextype, indexentry, targetname, targetname)
  503. env.note_reftarget(rolename, fullname, targetname)
  504. return [node]
  505. target_directive.content = 0
  506. target_directive.arguments = (1, 0, 1)
  507. # note, the target directive is not registered here, it is used by the application
  508. # when registering additional xref types