1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 | Doc/tools/extensions/pyspecific.py
# -*- coding: utf-8 -*- """ pyspecific.py ~~~~~~~~~~~~~ Sphinx extension with Python doc-specific markup. :copyright: 2008-2014 by Georg Brandl. :license: Python license. """ ISSUE_URI = 'https://bugs.python.org/issue%s' SOURCE_URI = 'https://hg.python.org/cpython/file/2.7/%s' from docutils import nodes, utils from sphinx.util.nodes import split_explicit_title from sphinx.util.compat import Directive from sphinx.writers.html import HTMLTranslator from sphinx.writers.latex import LaTeXTranslator # monkey-patch reST parser to disable alphabetic and roman enumerated lists from docutils.parsers.rst.states import Body Body.enum.converters['loweralpha'] = \ Body.enum.converters['upperalpha'] = \ Body.enum.converters['lowerroman'] = \ Body.enum.converters['upperroman'] = lambda x: None # monkey-patch HTML and LaTeX translators to keep doctest blocks in the # doctest docs themselves orig_visit_literal_block = HTMLTranslator.visit_literal_block def new_visit_literal_block(self, node): meta = self.builder.env.metadata[self.builder.current_docname] old_trim_doctest_flags = self.highlighter.trim_doctest_flags if 'keepdoctest' in meta: self.highlighter.trim_doctest_flags = False try: orig_visit_literal_block(self, node) finally: self.highlighter.trim_doctest_flags = old_trim_doctest_flags HTMLTranslator.visit_literal_block = new_visit_literal_block orig_depart_literal_block = LaTeXTranslator.depart_literal_block def new_depart_literal_block(self, node): meta = self.builder.env.metadata[self.curfilestack[-1]] old_trim_doctest_flags = self.highlighter.trim_doctest_flags if 'keepdoctest' in meta: self.highlighter.trim_doctest_flags = False try: orig_depart_literal_block(self, node) finally: self.highlighter.trim_doctest_flags = old_trim_doctest_flags LaTeXTranslator.depart_literal_block = new_depart_literal_block # Support for marking up and linking to bugs.python.org issues def issue_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): issue = utils.unescape(text) text = 'issue ' + issue refnode = nodes.reference(text, text, refuri=ISSUE_URI % issue) return [refnode], [] # Support for linking to Python source files easily def source_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): has_t, title, target = split_explicit_title(text) title = utils.unescape(title) target = utils.unescape(target) refnode = nodes.reference(title, title, refuri=SOURCE_URI % target) return [refnode], [] # Support for marking up implementation details class ImplementationDetail(Directive): has_content = True required_arguments = 0 optional_arguments = 1 final_argument_whitespace = True def run(self): pnode = nodes.compound(classes=['impl-detail']) content = self.content add_text = nodes.strong('CPython implementation detail:', 'CPython implementation detail:') if self.arguments: n, m = self.state.inline_text(self.arguments[0], self.lineno) pnode.append(nodes.paragraph('', '', *(n + m))) self.state.nested_parse(content, self.content_offset, pnode) if pnode.children and isinstance(pnode[0], nodes.paragraph): pnode[0].insert(0, add_text) pnode[0].insert(1, nodes.Text(' ')) else: pnode.insert(0, nodes.paragraph('', '', add_text)) return [pnode] # Support for documenting decorators from sphinx import addnodes from sphinx.domains.python import PyModulelevel, PyClassmember class PyDecoratorMixin(object): def handle_signature(self, sig, signode): ret = super(PyDecoratorMixin, self).handle_signature(sig, signode) signode.insert(0, addnodes.desc_addname('@', '@')) return ret def needs_arglist(self): return False class PyDecoratorFunction(PyDecoratorMixin, PyModulelevel): def run(self): # a decorator function is a function after all self.name = 'py:function' return PyModulelevel.run(self) class PyDecoratorMethod(PyDecoratorMixin, PyClassmember): def run(self): self.name = 'py:method' return PyClassmember.run(self) # Support for building "topic help" for pydoc pydoc_topic_labels = [ 'assert', 'assignment', 'atom-identifiers', 'atom-literals', 'attribute-access', 'attribute-references', 'augassign', 'binary', 'bitwise', 'bltin-code-objects', 'bltin-ellipsis-object', 'bltin-null-object', 'bltin-type-objects', 'booleans', 'break', 'callable-types', 'calls', 'class', 'comparisons', 'compound', 'context-managers', 'continue', 'conversions', 'customization', 'debugger', 'del', 'dict', 'dynamic-features', 'else', 'exceptions', 'exec', 'execmodel', 'exprlists', 'floating', 'for', 'formatstrings', 'function', 'global', 'id-classes', 'identifiers', 'if', 'imaginary', 'import', 'in', 'integers', 'lambda', 'lists', 'naming', 'numbers', 'numeric-types', 'objects', 'operator-summary', 'pass', 'power', 'print', 'raise', 'return', 'sequence-types', 'shifting', 'slicings', 'specialattrs', 'specialnames', 'string-methods', 'strings', 'subscriptions', 'truth', 'try', 'types', 'typesfunctions', 'typesmapping', 'typesmethods', 'typesmodules', 'typesseq', 'typesseq-mutable', 'unary', 'while', 'with', 'yield' ] from os import path from time import asctime from pprint import pformat from docutils.io import StringOutput from docutils.utils import new_document from sphinx.builders import Builder from sphinx.writers.text import TextWriter class PydocTopicsBuilder(Builder): name = 'pydoc-topics' def init(self): self.topics = {} def get_outdated_docs(self): return 'all pydoc topics' def get_target_uri(self, docname, typ=None): return '' # no URIs def write(self, *ignored): writer = TextWriter(self) for label in self.status_iterator(pydoc_topic_labels, 'building topics... ', length=len(pydoc_topic_labels)): if label not in self.env.domaindata['std']['labels']: self.warn('label %r not in documentation' % label) continue docname, labelid, sectname = self.env.domaindata['std']['labels'][label] doctree = self.env.get_and_resolve_doctree(docname, self) document = new_document('<section node>') document.append(doctree.ids[labelid]) destination = StringOutput(encoding='utf-8') writer.write(document, destination) self.topics[label] = writer.output def finish(self): f = open(path.join(self.outdir, 'topics.py'), 'wb') try: f.write('# -*- coding: utf-8 -*-\n'.encode('utf-8')) f.write(('# Autogenerated by Sphinx on %s\n' % asctime()).encode('utf-8')) f.write(('topics = ' + pformat(self.topics) + '\n').encode('utf-8')) finally: f.close() # Support for checking for suspicious markup import suspicious # Support for documenting Opcodes import re opcode_sig_re = re.compile(r'(\w+(?:\+\d)?)(?:\s*\((.*)\))?') def parse_opcode_signature(env, sig, signode): """Transform an opcode signature into RST nodes.""" m = opcode_sig_re.match(sig) if m is None: raise ValueError opname, arglist = m.groups() signode += addnodes.desc_name(opname, opname) if arglist is not None: paramlist = addnodes.desc_parameterlist() signode += paramlist paramlist += addnodes.desc_parameter(arglist, arglist) return opname.strip() # Support for documenting pdb commands pdbcmd_sig_re = re.compile(r'([a-z()!]+)\s*(.*)') # later... #pdbargs_tokens_re = re.compile(r'''[a-zA-Z]+ | # identifiers # [.,:]+ | # punctuation # [\[\]()] | # parens # \s+ # whitespace # ''', re.X) def parse_pdb_command(env, sig, signode): """Transform a pdb command signature into RST nodes.""" m = pdbcmd_sig_re.match(sig) if m is None: raise ValueError name, args = m.groups() fullname = name.replace('(', '').replace(')', '') signode += addnodes.desc_name(name, name) if args: signode += addnodes.desc_addname(' '+args, ' '+args) return fullname def setup(app): app.add_role('issue', issue_role) app.add_role('source', source_role) app.add_directive('impl-detail', ImplementationDetail) app.add_builder(PydocTopicsBuilder) app.add_builder(suspicious.CheckSuspiciousMarkupBuilder) app.add_description_unit('opcode', 'opcode', '%s (opcode)', parse_opcode_signature) app.add_description_unit('pdbcommand', 'pdbcmd', '%s (pdb command)', parse_pdb_command) app.add_description_unit('2to3fixer', '2to3fixer', '%s (2to3 fixer)') app.add_directive_to_domain('py', 'decorator', PyDecoratorFunction) app.add_directive_to_domain('py', 'decoratormethod', PyDecoratorMethod) return {'version': '1.0', 'parallel_read_safe': True} |