def process_usecase_nodes(app, doctree, fromdocname): if not app.config.usecases_include_usecases: for node in doctree.traverse(usecase): node.parent.remove(node) # Replace all usecaselist nodes with a list of the collected usecases. # Argument each usecase with a backlink to the original location. env = app.builder.env if not hasattr(env, "usecase_all_usecases"): return for node in doctree.traverse(usecaselist): if not app.config.usecases_include_usecases: node.replace_self([]) continue content = [] for usecase_info in env.usecase_all_usecases: para = nodes.paragraph() # Create a reference if app.config.usecases_simple_usecaseslist: para += nodes.strong("UseCase: ", "UseCase: ") # description = ( # _('%s ') % # (usecase_info['usecase-name'])) # para += nodes.Text(description, description) newnode = nodes.reference("", "") innernode = nodes.emphasis(_(usecase_info["usecase-name"]), _(usecase_info["usecase-name"])) newnode["refdocname"] = usecase_info["docname"] newnode["refuri"] = app.builder.get_relative_uri(fromdocname, usecase_info["docname"]) newnode["refuri"] += "#" + usecase_info["target"]["refid"] newnode.append(innernode) para += newnode content.append(para) else: filename = env.doc2path(usecase_info["docname"], base=None) description = _("(The original entry is located in %s, line %d and can be found ") % ( filename, usecase_info["lineno"], ) para += nodes.Text(description, description) newnode = nodes.reference("", "") innernode = nodes.emphasis(_("here"), _("here")) newnode["refdocname"] = usecase_info["docname"] newnode["refuri"] = app.builder.get_relative_uri(fromdocname, usecase_info["docname"]) # newnode['refuri'] += '#' + usecase_info['target']['refid'] newnode.append(innernode) para += newnode para += nodes.Text(".)", ".)") # Insert into the usecaselist content.append(usecase_info["usecase"]) content.append(para) node.replace_self(content)
def get_index_text(self, name): if self.objtype == 'option': return _('%s (option)') % name elif self.objtype == 'variable': return _('%s (variable)') % name else: return ''
def depart_caption(self, node): self.body.append('</span>') # append permalink if available if isinstance(node.parent, nodes.container) \ and node.parent.get('literal_block'): self.add_permalink_ref( node.parent, _('Permalink to this code') ) elif isinstance(node.parent, nodes.figure): image_nodes = node.parent.traverse(nodes.image) target_node = image_nodes and image_nodes[0] or node.parent self.add_permalink_ref( target_node, _('Permalink to this image') ) elif node.parent.get('toctree'): self.add_permalink_ref( node.parent.parent, _('Permalink to this toctree') ) if isinstance(node.parent, nodes.container) \ and node.parent.get('literal_block'): self.body.append('</div>\n') else: SmartyPantsHTMLTranslator.__bases__[0].__bases__[0] \ .depart_caption(self, node)
def run(self): env = self.state.document.settings.env modname = self.arguments[0].strip() noindex = "noindex" in self.options env.temp_data["py:module"] = modname env.domaindata["py"]["modules"][modname] = ( env.docname, self.options.get("synopsis", ""), self.options.get("platform", ""), "deprecated" in self.options, ) # make a duplicate entry in 'objects' to facilitate searching for the # module in PythonDomain.find_obj() env.domaindata["py"]["objects"][modname] = (env.docname, "module") targetnode = nodes.target("", "", ids=["module-" + modname], ismod=True) self.state.document.note_explicit_target(targetnode) ret = [targetnode] # XXX this behavior of the module directive is a mess... if "platform" in self.options: platform = self.options["platform"] node = nodes.paragraph() node += nodes.emphasis("", _("Platforms: ")) node += nodes.Text(platform, platform) ret.append(node) # the synopsis isn't printed; in fact, it is only used in the # modindex currently if not noindex: indextext = _("%s (module)") % modname inode = addnodes.index(entries=[("single", indextext, "module-" + modname, modname)]) ret.append(inode) return ret
def text_visit_graphviz(self, node): # type: (nodes.NodeVisitor, graphviz) -> None if 'alt' in node.attributes: self.add_text(_('[graph: %s]') % node['alt']) else: self.add_text(_('[graph]')) raise nodes.SkipNode
def setup(app): from sphinx.domains.python import PyField from sphinx.util.docfields import Field app.add_object_type( 'confval', 'confval', objname='configuration value', indextemplate='pair: %s; configuration value', doc_field_types=[ PyField( 'type', label=_('Type'), has_arg=False, names=('type',), bodyrolename='class' ), Field( 'default', label=_('Default'), has_arg=False, names=('default',), ), ] )
def build(self, force_all=False, filenames=None): # type: (bool, List[unicode]) -> None try: if force_all: self.builder.compile_all_catalogs() self.builder.build_all() elif filenames: self.builder.compile_specific_catalogs(filenames) self.builder.build_specific(filenames) else: self.builder.compile_update_catalogs() self.builder.build_update() status = (self.statuscode == 0 and _('succeeded') or _('finished with problems')) if self._warncount: logger.info(bold(_('build %s, %s warning%s.') % (status, self._warncount, self._warncount != 1 and 's' or ''))) else: logger.info(bold(_('build %s.') % status)) except Exception as err: # delete the saved env to force a fresh build next time envfile = path.join(self.doctreedir, ENV_PICKLE_FILENAME) if path.isfile(envfile): os.unlink(envfile) self.emit('build-finished', err) raise else: self.emit('build-finished', None) self.builder.cleanup()
def depart_title(self, node): # XXX Because we want to inject our link into the title, this is # largely copy-pasta'd from sphinx.html.writers.HtmlTranslator. close_tag = self.context[-1] if (self.permalink_text and self.builder.add_permalinks and node.parent.hasattr('ids') and node.parent['ids']): aname = node.parent['ids'][0] if close_tag.startswith('</a></h'): self.body.append('</a>') self.body.append(u'<a class="headerlink" href="#%s" ' % aname + u'title="%s">%s</a>' % ( _('Permalink to this headline'), self.permalink_text)) self.body.append(u'<a class="headerlink" href="%s#%s" ' % ( html.slide_path(self.builder), aname,) + u'title="%s">%s' % ( _('Slides'), self.builder.app.config.slide_html_slide_link_symbol)) if not close_tag.startswith('</a></h'): self.body.append('</a>') BaseTranslator.depart_title(self, node)
def generic_visit_altair_plot(self, node): # TODO: generate PNGs and insert them here if 'alt' in node.attributes: self.body.append(_('[ graph: %s ]') % node['alt']) else: self.body.append(_('[ graph ]')) raise nodes.SkipNode
def run(self): env = self.state.document.settings.env modname = self.arguments[0].strip() noindex = 'noindex' in self.options env.temp_data['tcpip:module'] = modname env.domaindata['tcpip']['modules'][modname] = \ (env.docname, self.options.get('synopsis', ''), self.options.get('platform', ''), 'deprecated' in self.options) targetnode = nodes.target('', '', ids=['module-' + modname], ismod=True) self.state.document.note_explicit_target(targetnode) ret = [targetnode] # XXX this behavior of the module directive is a mess... if 'platform' in self.options: platform = self.options['platform'] node = nodes.paragraph() node += nodes.emphasis('', _('Platforms: ')) node += nodes.Text(platform, platform) ret.append(node) # the synopsis isn't printed; in fact, it is only used in the # modindex currently if not noindex: indextext = _('%s (module)') % modname inode = addnodes.index(entries=[('single', indextext, 'module-' + modname, modname)]) ret.append(inode) return ret
def convert_overrides(self, name, value): # type: (unicode, Any) -> Any if not isinstance(value, string_types): return value else: defvalue = self.values[name][0] if isinstance(defvalue, dict): raise ValueError(_('cannot override dictionary config setting %r, ' 'ignoring (use %r to set individual elements)') % (name, name + '.key=value')) elif isinstance(defvalue, list): return value.split(',') elif isinstance(defvalue, integer_types): try: return int(value) except ValueError: raise ValueError(_('invalid number %r for config value %r, ignoring') % (value, name)) elif hasattr(defvalue, '__call__'): return value elif defvalue is not None and not isinstance(defvalue, string_types): raise ValueError(_('cannot override config setting %r with unsupported ' 'type, ignoring') % name) else: return value
def process_see_nodes(app, doctree, fromdocname): for node in doctree.traverse(see): content = [] para = nodes.paragraph() para += nodes.Text("See also:", "See also:") for name in node.refs: join_str = " " if name != node.refs[0]: join_str = ", " link_txt = join_str + name; if name not in app.env.domaindata['bro']['idtypes']: # Just create the text and issue warning app.env.warn(fromdocname, 'unknown target for ".. bro:see:: %s"' % (name)) para += nodes.Text(link_txt, link_txt) else: # Create a reference typ = app.env.domaindata['bro']['idtypes'][name] todocname = app.env.domaindata['bro']['objects'][(typ, name)] newnode = nodes.reference('', '') innernode = nodes.literal(_(name), _(name)) newnode['refdocname'] = todocname newnode['refuri'] = app.builder.get_relative_uri( fromdocname, todocname) newnode['refuri'] += '#' + typ + '-' + name newnode.append(innernode) para += nodes.Text(join_str, join_str) para += newnode content.append(para) node.replace_self(content)
def get_index_text(self, objectname, name): # type: (str, str) -> str if self.objtype == 'directive': return _('%s (directive)') % name elif self.objtype == 'role': return _('%s (role)') % name return ''
def assemble_doctree(self, indexfile): docnames = set([indexfile]) self.info(darkgreen(indexfile) + " ", nonl=1) tree = self.env.get_doctree(indexfile) tree['docname'] = indexfile # extract toctree nodes from the tree and put them in a fresh document new_tree = docutils.utils.new_document('<rinoh output>') for node in tree.traverse(addnodes.toctree): new_tree += node largetree = inline_all_toctrees(self, docnames, indexfile, new_tree, darkgreen) largetree['docname'] = indexfile self.info() self.info("resolving references...") self.env.resolve_references(largetree, indexfile, self) # resolve :ref:s to distant tex files -- we can't add a cross-reference, # but append the document name for pendingnode in largetree.traverse(addnodes.pending_xref): docname = pendingnode['refdocname'] sectname = pendingnode['refsectname'] newnodes = [nodes.emphasis(sectname, sectname)] for subdir, title in self.titles: if docname.startswith(subdir): newnodes.append(nodes.Text(_(' (in '), _(' (in '))) newnodes.append(nodes.emphasis(title, title)) newnodes.append(nodes.Text(')', ')')) break else: pass pendingnode.replace_self(newnodes) return largetree
def man_visit_graphviz(self, node): warn_for_deprecated_option(self, node) if 'alt' in node.attributes: self.body.append(_('[graph: %s]') % node['alt']) else: self.body.append(_('[graph]')) raise nodes.SkipNode
def get_index_text(self, objectname, name_obj): name = name_obj[0].split('->')[-1] if objectname or name_obj[1]: objectname = name_obj[0].split('->')[0] return _('%s() (%s member)') % (name, objectname) else: return _('%s() (method)') % name
def text_visit_graphviz(self, node): warn_for_deprecated_option(self, node) if 'alt' in node.attributes: self.add_text(_('[graph: %s]') % node['alt']) else: self.add_text(_('[graph]')) raise nodes.SkipNode
def get_index_text(self, modname, name_cls): if self.objtype == 'function': if not modname: return _('%s() (global function)') % name_cls[0] return _('%s() (module function in %s)') % (name_cls[0], modname) else: return ''
def run(self): env = self.state.document.settings.env targetid = "question-%d" % env.new_serialno('question') targetnode = nodes.target('', '', ids=[targetid]) ad = Question('\n'.join(self.content)) ad += nodes.title(_('Question'), _('Question')) #ad += nodes.class(_('Question'), _('Question')) self.state.nested_parse(self.content, self.content_offset, ad) ad.attributes["classes"] = ["question"] #ad.options["class"] = "question" #ad = Question() #ad.name = self.name #ad.content_offset = self.content_offset #ad.options = self.options #ad.content = self.content #ad.block_text = self.block_text #ad.state = self.state #ad.state_machine = self.state_machine #ad = make_admonition(Question, self.name, [_('Question')], self.options, # self.content, self.lineno, self.content_offset, # self.block_text, self.state, self.state_machine) return [targetnode,ad]
def get_index_text(self, objectname, name): # type: (unicode, unicode) -> unicode if self.objtype == 'directive': return _('%s (directive)') % name elif self.objtype == 'role': return _('%s (role)') % name return ''
def __init__(self, name, theme_path, factory): # type: (unicode, unicode, HTMLThemeFactory) -> None self.name = name self.base = None self.rootdir = None if path.isdir(theme_path): # already a directory, do nothing self.rootdir = None self.themedir = theme_path else: # extract the theme to a temp directory self.rootdir = tempfile.mkdtemp('sxt') self.themedir = path.join(self.rootdir, name) extract_zip(theme_path, self.themedir) self.config = configparser.RawConfigParser() self.config.read(path.join(self.themedir, THEMECONF)) # type: ignore try: inherit = self.config.get('theme', 'inherit') except configparser.NoSectionError: raise ThemeError(_('theme %r doesn\'t have "theme" setting') % name) except configparser.NoOptionError: raise ThemeError(_('theme %r doesn\'t have "inherit" setting') % name) if inherit != 'none': try: self.base = factory.create(inherit) except ThemeError: raise ThemeError(_('no theme named %r found, inherited by %r') % (inherit, name))
def run(self): pkgname = self.arguments[0].strip() env = self.state.document.settings.env env.ref_context['qbs:package'] = pkgname if 'noindex' in self.options: return [] sdkname = self.options.get('sdk', env.ref_context.get('qbs:sdk', None)) if sdkname: pkgname = sdkname + '.' + pkgname env.domaindata['qbs']['packages'][pkgname] = (env.docname, self.options.get('synopsis', '')) env.domaindata['qbs']['objects'][pkgname] = (env.docname, 'package') # target targetname = 'package-' + pkgname targetnode = nodes.target('', '', ids=[targetname], ismod=True) self.state.document.note_explicit_target(targetnode) # index if sdkname: itext = _('%s (Qbs Package in %s)') % (pkgname[len(sdkname) + 1:], sdkname) else: itext = _('%s (Qbs Package') % (pkgname[len(sdkname) + 1:]) inode = addnodes.index(entries=[('single', itext, targetname, '', None)]) return [targetnode, inode]
def convert(self, _from, _to): # type: (unicode, unicode) -> bool """Converts the image to expected one.""" try: args = ([self.config.image_converter] + self.config.image_converter_args + [_from, _to]) logger.debug('Invoking %r ...', args) p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE) except OSError as err: if err.errno != ENOENT: # No such file or directory raise logger.warning(_('convert command %r cannot be run.' 'check the image_converter setting'), self.config.image_converter) return False try: stdout, stderr = p.communicate() except (OSError, IOError) as err: if err.errno not in (EPIPE, EINVAL): raise stdout, stderr = p.stdout.read(), p.stderr.read() p.wait() if p.returncode != 0: raise ExtensionError(_('convert exited with error:\n' '[stderr]\n%s\n[stdout]\n%s') % (stderr, stdout)) return True
def man_visit_graphviz(self, node): # type: (nodes.NodeVisitor, graphviz) -> None if 'alt' in node.attributes: self.body.append(_('[graph: %s]') % node['alt']) else: self.body.append(_('[graph]')) raise nodes.SkipNode
def run(self): if not self.content: self.content = docutils.statemachine.StringList(['This driver supports :ref:`virtual I/O operations (/vsimem/, etc.) <virtual_file_systems>`']) node = supports_virtualio('\n'.join(self.content)) node += nodes.title(_('Supports VirtualIO'), _('Supports VirtualIO')) return finish_directive(self, 'supports_virtualio', node)
def get_index_text(self, modname, name): """Return the text for the index entry of the object.""" if self.objtype == 'function': return _('%s()') % name[0] elif self.objtype == 'data': return _('%s') % name[0] else: return ''
def run(self): if not self.content: self.content = docutils.statemachine.StringList(['This driver supports the :cpp:func:`GDALDriver::CreateCopy` operation']) node = supports_createcopy('\n'.join(self.content)) node += nodes.title(_('Supports CreateCopy()'), _('Supports CreateCopy()')) return finish_directive(self, 'supports_createcopy', node)
def run(self): if not self.content: self.content = docutils.statemachine.StringList(['This driver supports georeferencing']) node = supports_georeferencing('\n'.join(self.content)) node += nodes.title(_('Supports Georeferencing'), _('Supports Georeferencing')) return finish_directive(self, 'supports_georeferencing', node)
def add_builder(self, builder): # type: (Type[Builder]) -> None if not hasattr(builder, 'name'): raise ExtensionError(_('Builder class %s has no "name" attribute') % builder) if builder.name in self.builders: raise ExtensionError(_('Builder %r already exists (in module %s)') % (builder.name, self.builders[builder.name].__module__)) self.builders[builder.name] = builder
def override_domain(self, domain): # type: (Type[Domain]) -> None if domain.name not in self.domains: raise ExtensionError(_('domain %s not yet registered') % domain.name) if not issubclass(domain, self.domains[domain.name]): raise ExtensionError(_('new domain not a subclass of registered %s ' 'domain') % domain.name) self.domains[domain.name] = domain
def __init__(self, srcdir, confdir, outdir, doctreedir, buildername, confoverrides=None, status=sys.stdout, warning=sys.stderr, freshenv=False, warningiserror=False, tags=None, verbosity=0, parallel=0): # type: (unicode, unicode, unicode, unicode, unicode, Dict, IO, IO, bool, bool, List[unicode], int, int) -> None # NOQA self.verbosity = verbosity self.extensions = {} # type: Dict[unicode, Extension] self._setting_up_extension = ['?'] # type: List[unicode] self.builder = None # type: Builder self.env = None # type: BuildEnvironment self.registry = SphinxComponentRegistry() self.enumerable_nodes = { } # type: Dict[nodes.Node, Tuple[unicode, Callable]] # NOQA self.post_transforms = [] # type: List[Transform] self.html_themes = {} # type: Dict[unicode, unicode] self.srcdir = srcdir self.confdir = confdir self.outdir = outdir self.doctreedir = doctreedir self.parallel = parallel if status is None: self._status = cStringIO() # type: IO self.quiet = True else: self._status = status self.quiet = False if warning is None: self._warning = cStringIO() # type: IO else: self._warning = warning self._warncount = 0 self.warningiserror = warningiserror logging.setup(self, self._status, self._warning) self.events = EventManager() # keep last few messages for traceback # This will be filled by sphinx.util.logging.LastMessagesWriter self.messagelog = deque(maxlen=10) # type: deque # say hello to the world logger.info(bold('Running Sphinx v%s' % sphinx.__display_version__)) # status code for command-line application self.statuscode = 0 if not path.isdir(outdir): logger.info('making output directory...') os.makedirs(outdir) # read config self.tags = Tags(tags) self.config = Config(confdir, CONFIG_FILENAME, confoverrides or {}, self.tags) self.config.check_unicode() # defer checking types until i18n has been initialized # initialize some limited config variables before initialize i18n and loading # extensions self.config.pre_init_values() # set up translation infrastructure self._init_i18n() # check the Sphinx version if requested if self.config.needs_sphinx and self.config.needs_sphinx > sphinx.__display_version__: raise VersionRequirementError( _('This project needs at least Sphinx v%s and therefore cannot ' 'be built with this version.') % self.config.needs_sphinx) # set confdir to srcdir if -C given (!= no confdir); a few pieces # of code expect a confdir to be set if self.confdir is None: self.confdir = self.srcdir # load all built-in extension modules for extension in builtin_extensions: self.setup_extension(extension) # load all user-given extension modules for extension in self.config.extensions: self.setup_extension(extension) # preload builder module (before init config values) self.preload_builder(buildername) # the config file itself can be an extension if self.config.setup: self._setting_up_extension = ['conf.py'] # py31 doesn't have 'callable' function for below check if hasattr(self.config.setup, '__call__'): self.config.setup(self) else: raise ConfigError( _("'setup' as currently defined in conf.py isn't a Python callable. " "Please modify its definition to make it a callable function. This is " "needed for conf.py to behave as a Sphinx extension.")) # now that we know all config values, collect them from conf.py self.config.init_values() # check extension versions if requested verify_required_extensions(self, self.config.needs_extensions) # check primary_domain if requested primary_domain = self.config.primary_domain if primary_domain and not self.registry.has_domain(primary_domain): logger.warning(_('primary_domain %r not found, ignored.'), primary_domain) # create the builder self.builder = self.create_builder(buildername) # check all configuration values for permissible types self.config.check_types() # set up source_parsers self._init_source_parsers() # set up the build environment self._init_env(freshenv) # set up the builder self._init_builder() # set up the enumerable nodes self._init_enumerable_nodes()
def run(self) -> List[Node]: node = addnodes.glossary() node.document = self.state.document # This directive implements a custom format of the reST definition list # that allows multiple lines of terms before the definition. This is # easy to parse since we know that the contents of the glossary *must # be* a definition list. # first, collect single entries entries = [] # type: List[Tuple[List[Tuple[str, str, int]], StringList]] in_definition = True in_comment = False was_empty = True messages = [] # type: List[Node] for line, (source, lineno) in zip(self.content, self.content.items): # empty line -> add to last definition if not line: if in_definition and entries: entries[-1][1].append('', source, lineno) was_empty = True continue # unindented line -> a term if line and not line[0].isspace(): # enable comments if line.startswith('.. '): in_comment = True continue else: in_comment = False # first term of definition if in_definition: if not was_empty: messages.append(self.state.reporter.warning( _('glossary term must be preceded by empty line'), source=source, line=lineno)) entries.append(([(line, source, lineno)], StringList())) in_definition = False # second term and following else: if was_empty: messages.append(self.state.reporter.warning( _('glossary terms must not be separated by empty lines'), source=source, line=lineno)) if entries: entries[-1][0].append((line, source, lineno)) else: messages.append(self.state.reporter.warning( _('glossary seems to be misformatted, check indentation'), source=source, line=lineno)) elif in_comment: pass else: if not in_definition: # first line of definition, determines indentation in_definition = True indent_len = len(line) - len(line.lstrip()) if entries: entries[-1][1].append(line[indent_len:], source, lineno) else: messages.append(self.state.reporter.warning( _('glossary seems to be misformatted, check indentation'), source=source, line=lineno)) was_empty = False # now, parse all the entries into a big definition list items = [] for terms, definition in entries: termtexts = [] # type: List[str] termnodes = [] # type: List[Node] system_messages = [] # type: List[Node] for line, source, lineno in terms: parts = split_term_classifiers(line) # parse the term with inline markup # classifiers (parts[1:]) will not be shown on doctree textnodes, sysmsg = self.state.inline_text(parts[0], lineno) # use first classifier as a index key term = make_glossary_term(self.env, textnodes, parts[1], source, lineno, document=self.state.document) term.rawsource = line system_messages.extend(sysmsg) termtexts.append(term.astext()) termnodes.append(term) termnodes.extend(system_messages) defnode = nodes.definition() if definition: self.state.nested_parse(definition, definition.items[0][1], defnode) termnodes.append(defnode) items.append((termtexts, nodes.definition_list_item('', *termnodes))) if 'sorted' in self.options: items.sort(key=lambda x: unicodedata.normalize('NFD', x[0][0].lower())) dlist = nodes.definition_list() dlist['classes'].append('glossary') dlist.extend(item[1] for item in items) node += dlist return messages + [node]
class EnvVar(GenericObject): indextemplate = _('environment variable; %s')
def text_visit_graphviz(self, node): if 'alt' in node.attributes: self.add_text(_('[graph: %s]') % node['alt']) else: self.add_text(_('[graph]')) raise nodes.SkipNode
def get_type_name(self, type, primary=False): # type: (ObjType, bool) -> unicode """Return full name for given ObjType.""" if primary: return type.lname return _('%s %s') % (self.label, type.lname)
# 3rd party import sphinx.domains.changeset from docutils import nodes from docutils.nodes import Node from sphinx import addnodes from sphinx.application import Sphinx from sphinx.locale import _ # this package from sphinx_toolbox.utils import SphinxExtMetadata, metadata_add_version __all__ = ["VersionChange", "setup"] versionlabels = { "versionremoved": _("Removed in version %s"), **sphinx.domains.changeset.versionlabels, } versionlabel_classes = { "versionremoved": "removed", **sphinx.domains.changeset.versionlabel_classes, } class VersionChange(sphinx.domains.changeset.VersionChange): """ Directive to describe a addition/change/deprecation/removal in a specific version. """ def run(self) -> List[Node]: """
def get_index_text(self, modname, name_cls): # type: (str, str) -> str name, cls = name_cls add_modules = self.env.config.add_module_names if self.objtype == 'method': try: clsname, methname = name.rsplit('.', 1) except ValueError: if modname: return _('%s() (in module %s)') % (name, modname) else: return '%s()' % name if modname and add_modules: return _('%s() (%s.%s method)') % (methname, modname, clsname) else: return _('%s() (%s method)') % (methname, clsname) elif self.objtype == 'staticmethod': try: clsname, methname = name.rsplit('.', 1) except ValueError: if modname: return _('%s() (in module %s)') % (name, modname) else: return '%s()' % name if modname and add_modules: return _('%s() (%s.%s static method)') % (methname, modname, clsname) else: return _('%s() (%s static method)') % (methname, clsname) elif self.objtype == 'classmethod': try: clsname, methname = name.rsplit('.', 1) except ValueError: if modname: return _('%s() (in module %s)') % (name, modname) else: return '%s()' % name if modname: return _('%s() (%s.%s class method)') % (methname, modname, clsname) else: return _('%s() (%s class method)') % (methname, clsname) elif self.objtype == 'attribute': try: clsname, attrname = name.rsplit('.', 1) except ValueError: if modname: return _('%s (in module %s)') % (name, modname) else: return name if modname and add_modules: return _('%s (%s.%s attribute)') % (attrname, modname, clsname) else: return _('%s (%s attribute)') % (attrname, clsname) else: return ''
def generate(self, docnames=None): # type: (Iterable[str]) -> Tuple[List[Tuple[str, List[IndexEntry]]], bool] content = {} # type: Dict[str, List[IndexEntry]] # list of prefixes to ignore ignores = None # type: List[str] ignores = self.domain.env.config[ 'modindex_common_prefix'] # type: ignore ignores = sorted(ignores, key=len, reverse=True) # list of all modules, sorted by module name modules = sorted(self.domain.data['modules'].items(), key=lambda x: x[0].lower()) # sort out collapsable modules prev_modname = '' num_toplevels = 0 for modname, (docname, synopsis, platforms, deprecated) in modules: if docnames and docname not in docnames: continue for ignore in ignores: if modname.startswith(ignore): modname = modname[len(ignore):] stripped = ignore break else: stripped = '' # we stripped the whole module name? if not modname: modname, stripped = stripped, '' entries = content.setdefault(modname[0].lower(), []) package = modname.split('.')[0] if package != modname: # it's a submodule if prev_modname == package: # first submodule - make parent a group head if entries: last = entries[-1] entries[-1] = IndexEntry(last[0], 1, last[2], last[3], last[4], last[5], last[6]) entries.append( IndexEntry(stripped + package, 1, '', '', '', '', '')) elif not prev_modname.startswith(package): # submodule without parent in list, add dummy entry entries.append( IndexEntry(stripped + package, 1, '', '', '', '', '')) subtype = 2 else: num_toplevels += 1 subtype = 0 qualifier = deprecated and _('Deprecated') or '' entries.append( IndexEntry(stripped + modname, subtype, docname, 'module-' + stripped + modname, platforms, qualifier, synopsis)) prev_modname = modname # apply heuristics when to collapse modindex at page load: # only collapse if number of toplevel modules is larger than # number of submodules collapse = len(modules) - num_toplevels < num_toplevels # sort by first letter sorted_content = sorted(content.items()) return sorted_content, collapse
class PythonDomain(Domain): """Python language domain.""" name = 'py' label = 'Python' object_types = { 'function': ObjType(_('function'), 'func', 'obj'), 'data': ObjType(_('data'), 'data', 'obj'), 'class': ObjType(_('class'), 'class', 'exc', 'obj'), 'exception': ObjType(_('exception'), 'exc', 'class', 'obj'), 'method': ObjType(_('method'), 'meth', 'obj'), 'classmethod': ObjType(_('class method'), 'meth', 'obj'), 'staticmethod': ObjType(_('static method'), 'meth', 'obj'), 'attribute': ObjType(_('attribute'), 'attr', 'obj'), 'module': ObjType(_('module'), 'mod', 'obj'), } # type: Dict[str, ObjType] directives = { 'function': PyModulelevel, 'data': PyModulelevel, 'class': PyClasslike, 'exception': PyClasslike, 'method': PyClassmember, 'classmethod': PyClassmember, 'staticmethod': PyClassmember, 'attribute': PyClassmember, 'module': PyModule, 'currentmodule': PyCurrentModule, 'decorator': PyDecoratorFunction, 'decoratormethod': PyDecoratorMethod, } roles = { 'data': PyXRefRole(), 'exc': PyXRefRole(), 'func': PyXRefRole(fix_parens=True), 'class': PyXRefRole(), 'const': PyXRefRole(), 'attr': PyXRefRole(), 'meth': PyXRefRole(fix_parens=True), 'mod': PyXRefRole(), 'obj': PyXRefRole(), } initial_data = { 'objects': {}, # fullname -> docname, objtype 'modules': {}, # modname -> docname, synopsis, platform, deprecated } # type: Dict[str, Dict[str, Tuple[Any]]] indices = [ PythonModuleIndex, ] def clear_doc(self, docname): # type: (str) -> None for fullname, (fn, _l) in list(self.data['objects'].items()): if fn == docname: del self.data['objects'][fullname] for modname, (fn, _x, _x, _x) in list(self.data['modules'].items()): if fn == docname: del self.data['modules'][modname] def merge_domaindata(self, docnames, otherdata): # type: (List[str], Dict) -> None # XXX check duplicates? for fullname, (fn, objtype) in otherdata['objects'].items(): if fn in docnames: self.data['objects'][fullname] = (fn, objtype) for modname, data in otherdata['modules'].items(): if data[0] in docnames: self.data['modules'][modname] = data def find_obj(self, env, modname, classname, name, type, searchmode=0): # type: (BuildEnvironment, str, str, str, str, int) -> List[Tuple[str, Any]] """Find a Python object for "name", perhaps using the given module and/or classname. Returns a list of (name, object entry) tuples. """ # skip parens if name[-2:] == '()': name = name[:-2] if not name: return [] objects = self.data['objects'] matches = [] # type: List[Tuple[str, Any]] newname = None if searchmode == 1: if type is None: objtypes = list(self.object_types) else: objtypes = self.objtypes_for_role(type) if objtypes is not None: if modname and classname: fullname = modname + '.' + classname + '.' + name if fullname in objects and objects[fullname][1] in objtypes: newname = fullname if not newname: if modname and modname + '.' + name in objects and \ objects[modname + '.' + name][1] in objtypes: newname = modname + '.' + name elif name in objects and objects[name][1] in objtypes: newname = name else: # "fuzzy" searching mode searchname = '.' + name matches = [(oname, objects[oname]) for oname in objects if oname.endswith(searchname) and objects[oname][1] in objtypes] else: # NOTE: searching for exact match, object type is not considered if name in objects: newname = name elif type == 'mod': # only exact matches allowed for modules return [] elif classname and classname + '.' + name in objects: newname = classname + '.' + name elif modname and modname + '.' + name in objects: newname = modname + '.' + name elif modname and classname and \ modname + '.' + classname + '.' + name in objects: newname = modname + '.' + classname + '.' + name # special case: builtin exceptions have module "exceptions" set elif type == 'exc' and '.' not in name and \ 'exceptions.' + name in objects: newname = 'exceptions.' + name # special case: object methods elif type in ('func', 'meth') and '.' not in name and \ 'object.' + name in objects: newname = 'object.' + name if newname is not None: matches.append((newname, objects[newname])) return matches def resolve_xref(self, env, fromdocname, builder, type, target, node, contnode): # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA modname = node.get('py:module') clsname = node.get('py:class') searchmode = node.hasattr('refspecific') and 1 or 0 matches = self.find_obj(env, modname, clsname, target, type, searchmode) if not matches: return None elif len(matches) > 1: logger.warning( __('more than one target found for cross-reference %r: %s'), target, ', '.join(match[0] for match in matches), type='ref', subtype='python', location=node) name, obj = matches[0] if obj[1] == 'module': return self._make_module_refnode(builder, fromdocname, name, contnode) else: return make_refnode(builder, fromdocname, obj[0], name, contnode, name) def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode): # type: (BuildEnvironment, str, Builder, str, addnodes.pending_xref, nodes.Element) -> List[Tuple[str, nodes.Element]] # NOQA modname = node.get('py:module') clsname = node.get('py:class') results = [] # type: List[Tuple[str, nodes.Element]] # always search in "refspecific" mode with the :any: role matches = self.find_obj(env, modname, clsname, target, None, 1) for name, obj in matches: if obj[1] == 'module': results.append( ('py:mod', self._make_module_refnode(builder, fromdocname, name, contnode))) else: results.append(('py:' + self.role_for_objtype(obj[1]), make_refnode(builder, fromdocname, obj[0], name, contnode, name))) return results def _make_module_refnode(self, builder, fromdocname, name, contnode): # type: (Builder, str, str, nodes.Node) -> nodes.Element # get additional info for modules docname, synopsis, platform, deprecated = self.data['modules'][name] title = name if synopsis: title += ': ' + synopsis if deprecated: title += _(' (deprecated)') if platform: title += ' (' + platform + ')' return make_refnode(builder, fromdocname, docname, 'module-' + name, contnode, title) def get_objects(self): # type: () -> Iterator[Tuple[str, str, str, str, str, int]] for modname, info in self.data['modules'].items(): yield (modname, modname, 'module', info[0], 'module-' + modname, 0) for refname, (docname, type) in self.data['objects'].items(): if type != 'module': # modules are already handled yield (refname, refname, type, docname, refname, 1) def get_full_qualified_name(self, node): # type: (nodes.Element) -> str modname = node.get('py:module') clsname = node.get('py:class') target = node.get('reftarget') if target is None: return None else: return '.'.join(filter(None, [modname, clsname, target]))
def get_index_text(self, name, type): return _('%s (Lisp %s)') % (name, type)
def depart_desc_signature_line(self, node): if node.get('add_permalink'): # the permalink info is on the parent desc_signature node self.add_permalink_ref(node.parent, _('Permalink to this definition')) self.body.append('<br />')
def collect_pages(app): # type: (Sphinx) -> Iterator[Tuple[unicode, Dict[unicode, Any], unicode]] env = app.builder.env if not hasattr(env, '_viewcode_modules'): return highlighter = app.builder.highlighter # type: ignore urito = app.builder.get_relative_uri modnames = set(env._viewcode_modules) # type: ignore # app.builder.info(' (%d module code pages)' % # len(env._viewcode_modules), nonl=1) for modname, entry in status_iterator( iteritems(env._viewcode_modules), # type: ignore 'highlighting module code... ', "blue", len(env._viewcode_modules), # type: ignore app.verbosity, lambda x: x[0]): if not entry: continue code, tags, used, refname = entry # construct a page name for the highlighted source pagename = '_modules/' + modname.replace('.', '/') # highlight the source using the builder's highlighter if env.config.highlight_language in ('python3', 'default', 'none'): lexer = env.config.highlight_language else: lexer = 'python' highlighted = highlighter.highlight_block(code, lexer, linenos=False) # split the code into lines lines = highlighted.splitlines() # split off wrap markup from the first line of the actual code before, after = lines[0].split('<pre>') lines[0:1] = [before + '<pre>', after] # nothing to do for the last line; it always starts with </pre> anyway # now that we have code lines (starting at index 1), insert anchors for # the collected tags (HACK: this only works if the tag boundaries are # properly nested!) maxindex = len(lines) - 1 for name, docname in iteritems(used): type, start, end = tags[name] backlink = urito(pagename, docname) + '#' + refname + '.' + name lines[start] = ( '<div class="viewcode-block" id="%s"><a class="viewcode-back" ' 'href="%s">%s</a>' % (name, backlink, _('[docs]')) + lines[start]) lines[min(end - 1, maxindex)] += '</div>' # try to find parents (for submodules) parents = [] parent = modname while '.' in parent: parent = parent.rsplit('.', 1)[0] if parent in modnames: parents.append({ 'link': urito(pagename, '_modules/' + parent.replace('.', '/')), 'title': parent }) parents.append({ 'link': urito(pagename, '_modules/index'), 'title': _('Module code') }) parents.reverse() # putting it all together context = { 'parents': parents, 'title': modname, 'body': (_('<h1>Source code for %s</h1>') % modname + '\n'.join(lines)), } # type: Dict[unicode, Any] yield (pagename, context, 'page.html') if not modnames: return html = ['\n'] # the stack logic is needed for using nested lists for submodules stack = [''] for modname in sorted(modnames): if modname.startswith(stack[-1]): stack.append(modname + '.') html.append('<ul>') else: stack.pop() while not modname.startswith(stack[-1]): stack.pop() html.append('</ul>') stack.append(modname + '.') html.append('<li><a href="%s">%s</a></li>\n' % (urito('_modules/index', '_modules/' + modname.replace('.', '/')), modname)) html.append('</ul>' * (len(stack) - 1)) context = { 'title': _('Overview: module code'), 'body': (_('<h1>All modules for which code is available</h1>') + ''.join(html)), } yield ('_modules/index', context, 'page.html')
class PyObject(ObjectDescription): """ Description of a general Python object. :cvar allow_nesting: Class is an object that allows for nested namespaces :vartype allow_nesting: bool """ option_spec = { 'noindex': directives.flag, 'module': directives.unchanged, 'annotation': directives.unchanged, } doc_field_types = [ PyTypedField('parameter', label=_('Parameters'), names=('param', 'parameter', 'arg', 'argument', 'keyword', 'kwarg', 'kwparam'), typerolename='class', typenames=('paramtype', 'type'), can_collapse=True), PyTypedField('variable', label=_('Variables'), rolename='obj', names=('var', 'ivar', 'cvar'), typerolename='class', typenames=('vartype', ), can_collapse=True), PyGroupedField('exceptions', label=_('Raises'), rolename='exc', names=('raises', 'raise', 'exception', 'except'), can_collapse=True), Field('returnvalue', label=_('Returns'), has_arg=False, names=('returns', 'return')), PyField('returntype', label=_('Return type'), has_arg=False, names=('rtype', ), bodyrolename='class'), ] allow_nesting = False def get_signature_prefix(self, sig): # type: (str) -> str """May return a prefix to put before the object name in the signature. """ return '' def needs_arglist(self): # type: () -> bool """May return true if an empty argument list is to be generated even if the document contains none. """ return False def handle_signature(self, sig, signode): # type: (str, addnodes.desc_signature) -> Tuple[str, str] """Transform a Python signature into RST nodes. Return (fully qualified name of the thing, classname if any). If inside a class, the current class name is handled intelligently: * it is stripped from the displayed name if present * it is added to the full name (return value) if not present """ m = py_sig_re.match(sig) if m is None: raise ValueError name_prefix, name, arglist, retann = m.groups() # determine module and class name (if applicable), as well as full name modname = self.options.get('module', self.env.ref_context.get('py:module')) classname = self.env.ref_context.get('py:class') if classname: add_module = False if name_prefix and (name_prefix == classname or name_prefix.startswith(classname + ".")): fullname = name_prefix + name # class name is given again in the signature name_prefix = name_prefix[len(classname):].lstrip('.') elif name_prefix: # class name is given in the signature, but different # (shouldn't happen) fullname = classname + '.' + name_prefix + name else: # class name is not given in the signature fullname = classname + '.' + name else: add_module = True if name_prefix: classname = name_prefix.rstrip('.') fullname = name_prefix + name else: classname = '' fullname = name signode['module'] = modname signode['class'] = classname signode['fullname'] = fullname sig_prefix = self.get_signature_prefix(sig) if sig_prefix: signode += addnodes.desc_annotation(sig_prefix, sig_prefix) if name_prefix: signode += addnodes.desc_addname(name_prefix, name_prefix) # exceptions are a special case, since they are documented in the # 'exceptions' module. elif add_module and self.env.config.add_module_names: modname = self.options.get('module', self.env.ref_context.get('py:module')) if modname and modname != 'exceptions': nodetext = modname + '.' signode += addnodes.desc_addname(nodetext, nodetext) anno = self.options.get('annotation') signode += addnodes.desc_name(name, name) if not arglist: if self.needs_arglist(): # for callables, add an empty parameter list signode += addnodes.desc_parameterlist() if retann: signode += addnodes.desc_returns(retann, retann) if anno: signode += addnodes.desc_annotation(' ' + anno, ' ' + anno) return fullname, name_prefix _pseudo_parse_arglist(signode, arglist) if retann: signode += addnodes.desc_returns(retann, retann) if anno: signode += addnodes.desc_annotation(' ' + anno, ' ' + anno) return fullname, name_prefix def get_index_text(self, modname, name): # type: (str, str) -> str """Return the text for the index entry of the object.""" raise NotImplementedError('must be implemented in subclasses') def add_target_and_index(self, name_cls, sig, signode): # type: (str, str, addnodes.desc_signature) -> None modname = self.options.get('module', self.env.ref_context.get('py:module')) fullname = (modname and modname + '.' or '') + name_cls[0] # note target if fullname not in self.state.document.ids: signode['names'].append(fullname) signode['ids'].append(fullname) signode['first'] = (not self.names) self.state.document.note_explicit_target(signode) objects = self.env.domaindata['py']['objects'] if fullname in objects: self.state_machine.reporter.warning( 'duplicate object description of %s, ' % fullname + 'other instance in ' + self.env.doc2path(objects[fullname][0]) + ', use :noindex: for one of them', line=self.lineno) objects[fullname] = (self.env.docname, self.objtype) indextext = self.get_index_text(modname, name_cls) if indextext: self.indexnode['entries'].append( ('single', indextext, fullname, '', None)) def before_content(self): # type: () -> None """Handle object nesting before content :py:class:`PyObject` represents Python language constructs. For constructs that are nestable, such as a Python classes, this method will build up a stack of the nesting heirarchy so that it can be later de-nested correctly, in :py:meth:`after_content`. For constructs that aren't nestable, the stack is bypassed, and instead only the most recent object is tracked. This object prefix name will be removed with :py:meth:`after_content`. """ prefix = None if self.names: # fullname and name_prefix come from the `handle_signature` method. # fullname represents the full object name that is constructed using # object nesting and explicit prefixes. `name_prefix` is the # explicit prefix given in a signature (fullname, name_prefix) = self.names[-1] if self.allow_nesting: prefix = fullname elif name_prefix: prefix = name_prefix.strip('.') if prefix: self.env.ref_context['py:class'] = prefix if self.allow_nesting: classes = self.env.ref_context.setdefault('py:classes', []) classes.append(prefix) if 'module' in self.options: modules = self.env.ref_context.setdefault('py:modules', []) modules.append(self.env.ref_context.get('py:module')) self.env.ref_context['py:module'] = self.options['module'] def after_content(self): # type: () -> None """Handle object de-nesting after content If this class is a nestable object, removing the last nested class prefix ends further nesting in the object. If this class is not a nestable object, the list of classes should not be altered as we didn't affect the nesting levels in :py:meth:`before_content`. """ classes = self.env.ref_context.setdefault('py:classes', []) if self.allow_nesting: try: classes.pop() except IndexError: pass self.env.ref_context['py:class'] = (classes[-1] if len(classes) > 0 else None) if 'module' in self.options: modules = self.env.ref_context.setdefault('py:modules', []) if modules: self.env.ref_context['py:module'] = modules.pop() else: self.env.ref_context.pop('py:module')
class CObject(ObjectDescription): """ Description of a C language object. """ doc_field_types = [ TypedField('parameter', label=_('Parameters'), names=('param', 'parameter', 'arg', 'argument'), typerolename='type', typenames=('type', )), Field('returnvalue', label=_('Returns'), has_arg=False, names=('returns', 'return')), Field('returntype', label=_('Return type'), has_arg=False, names=('rtype', )), ] # These C types aren't described anywhere, so don't try to create # a cross-reference to them stopwords = set(( 'const', 'void', 'char', 'wchar_t', 'int', 'short', 'long', 'float', 'double', 'unsigned', 'signed', 'FILE', 'clock_t', 'time_t', 'ptrdiff_t', 'size_t', 'ssize_t', 'struct', '_Bool', )) def _parse_type(self, node, ctype): # type: (nodes.Element, str) -> None # add cross-ref nodes for all words for part in [_f for _f in wsplit_re.split(ctype) if _f]: tnode = nodes.Text(part, part) if part[0] in string.ascii_letters + '_' and \ part not in self.stopwords: pnode = addnodes.pending_xref('', refdomain='c', reftype='type', reftarget=part, modname=None, classname=None) pnode += tnode node += pnode else: node += tnode def _parse_arglist(self, arglist): # type: (str) -> Iterator[str] while True: m = c_funcptr_arg_sig_re.match(arglist) if m: yield m.group() arglist = c_funcptr_arg_sig_re.sub('', arglist) if ',' in arglist: _, arglist = arglist.split(',', 1) else: break else: if ',' in arglist: arg, arglist = arglist.split(',', 1) yield arg else: yield arglist break def handle_signature(self, sig, signode): # type: (str, addnodes.desc_signature) -> str """Transform a C signature into RST nodes.""" # first try the function pointer signature regex, it's more specific m = c_funcptr_sig_re.match(sig) if m is None: m = c_sig_re.match(sig) if m is None: raise ValueError('no match') rettype, name, arglist, const = m.groups() desc_type = addnodes.desc_type('', '') signode += desc_type self._parse_type(desc_type, rettype) try: classname, funcname = name.split('::', 1) classname += '::' signode += addnodes.desc_addname(classname, classname) signode += addnodes.desc_name(funcname, funcname) # name (the full name) is still both parts except ValueError: signode += addnodes.desc_name(name, name) # clean up parentheses from canonical name m = c_funcptr_name_re.match(name) if m: name = m.group(1) typename = self.env.ref_context.get('c:type') if self.name == 'c:member' and typename: fullname = typename + '.' + name else: fullname = name if not arglist: if self.objtype == 'function' or \ self.objtype == 'macro' and sig.rstrip().endswith('()'): # for functions, add an empty parameter list signode += addnodes.desc_parameterlist() if const: signode += addnodes.desc_addname(const, const) return fullname paramlist = addnodes.desc_parameterlist() arglist = arglist.replace('`', '').replace('\\ ', '') # remove markup # this messes up function pointer types, but not too badly ;) for arg in self._parse_arglist(arglist): arg = arg.strip() param = addnodes.desc_parameter('', '', noemph=True) try: m = c_funcptr_arg_sig_re.match(arg) if m: self._parse_type(param, m.group(1) + '(') param += nodes.emphasis(m.group(2), m.group(2)) self._parse_type(param, ')(' + m.group(3) + ')') if m.group(4): param += addnodes.desc_addname(m.group(4), m.group(4)) else: ctype, argname = arg.rsplit(' ', 1) self._parse_type(param, ctype) # separate by non-breaking space in the output param += nodes.emphasis(' ' + argname, u'\xa0' + argname) except ValueError: # no argument name given, only the type self._parse_type(param, arg) paramlist += param signode += paramlist if const: signode += addnodes.desc_addname(const, const) return fullname def get_index_text(self, name): # type: (str) -> str if self.objtype == 'function': return _('%s (C function)') % name elif self.objtype == 'member': return _('%s (C member)') % name elif self.objtype == 'macro': return _('%s (C macro)') % name elif self.objtype == 'type': return _('%s (C type)') % name elif self.objtype == 'var': return _('%s (C variable)') % name else: return '' def add_target_and_index(self, name, sig, signode): # type: (str, str, addnodes.desc_signature) -> None # for C API items we add a prefix since names are usually not qualified # by a module name and so easily clash with e.g. section titles targetname = 'c.' + name if targetname not in self.state.document.ids: signode['names'].append(targetname) signode['ids'].append(targetname) signode['first'] = (not self.names) self.state.document.note_explicit_target(signode) inv = self.env.domaindata['c']['objects'] if name in inv: self.state_machine.reporter.warning( 'duplicate C object description of %s, ' % name + 'other instance in ' + self.env.doc2path(inv[name][0]), line=self.lineno) inv[name] = (self.env.docname, self.objtype) indextext = self.get_index_text(name) if indextext: self.indexnode['entries'].append( ('single', indextext, targetname, '', None)) def before_content(self): # type: () -> None self.typename_set = False if self.name == 'c:type': if self.names: self.env.ref_context['c:type'] = self.names[0] self.typename_set = True def after_content(self): # type: () -> None if self.typename_set: self.env.ref_context.pop('c:type', None)
def config_initiated(app, config): theme_options = config.html_theme_options or {} if theme_options.get("canonical_url"): logger.warning( _("The canonical_url option is deprecated, use the html_baseurl option from Sphinx instead.") )
def depart_desc_signature(self, node): # type: (addnodes.desc_signature) -> None if not node.get('is_multiline'): self.add_permalink_ref(node, _('Permalink to this definition')) self.body.append('</dt>\n')
def get_index_text(self, objectname, name): if self.objtype == 'directive': return _('%s (directive)') % name elif self.objtype == 'role': return _('%s (role)') % name return ''
def set_translator(self, name, translator_class): # type: (unicode, Type[nodes.NodeVisitor]) -> None logger.info(bold(_('Change of translator for the %s builder.') % name)) self.registry.add_translator(name, translator_class)
class CDomain(Domain): """C language domain.""" name = 'c' label = 'C' object_types = { 'function': ObjType(_('function'), 'func'), 'member': ObjType(_('member'), 'member'), 'macro': ObjType(_('macro'), 'macro'), 'type': ObjType(_('type'), 'type'), 'var': ObjType(_('variable'), 'data'), } directives = { 'function': CObject, 'member': CObject, 'macro': CObject, 'type': CObject, 'var': CObject, } roles = { 'func': CXRefRole(fix_parens=True), 'member': CXRefRole(), 'macro': CXRefRole(), 'data': CXRefRole(), 'type': CXRefRole(), } initial_data = { 'objects': {}, # fullname -> docname, objtype } # type: Dict[str, Dict[str, Tuple[str, Any]]] def clear_doc(self, docname): # type: (str) -> None for fullname, (fn, _l) in list(self.data['objects'].items()): if fn == docname: del self.data['objects'][fullname] def merge_domaindata(self, docnames, otherdata): # type: (List[str], Dict) -> None # XXX check duplicates for fullname, (fn, objtype) in otherdata['objects'].items(): if fn in docnames: self.data['objects'][fullname] = (fn, objtype) def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA # strip pointer asterisk target = target.rstrip(' *') # becase TypedField can generate xrefs if target in CObject.stopwords: return contnode if target not in self.data['objects']: return None obj = self.data['objects'][target] return make_refnode(builder, fromdocname, obj[0], 'c.' + target, contnode, target) def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode): # type: (BuildEnvironment, str, Builder, str, addnodes.pending_xref, nodes.Element) -> List[Tuple[str, nodes.Element]] # NOQA # strip pointer asterisk target = target.rstrip(' *') if target not in self.data['objects']: return [] obj = self.data['objects'][target] return [('c:' + self.role_for_objtype(obj[1]), make_refnode(builder, fromdocname, obj[0], 'c.' + target, contnode, target))] def get_objects(self): # type: () -> Iterator[Tuple[str, str, str, str, str, int]] for refname, (docname, type) in list(self.data['objects'].items()): yield (refname, refname, type, docname, 'c.' + refname, 1)
from sphinx.environment import BuildEnvironment # NOQA from sphinx.util.typing import TextlikeNode # NOQA logger = logging.getLogger(__name__) # REs for Python signatures py_sig_re = re.compile( r'''^ ([\w.]*\.)? # class name(s) (\w+) \s* # thing name (?: \(\s*(.*)\s*\) # optional: arguments (?:\s* -> \s* (.*))? # return annotation )? $ # and nothing more ''', re.VERBOSE) pairindextypes = { 'module': _('module'), 'keyword': _('keyword'), 'operator': _('operator'), 'object': _('object'), 'exception': _('exception'), 'statement': _('statement'), 'builtin': _('built-in function'), } locale.pairindextypes = DeprecatedDict( pairindextypes, 'sphinx.locale.pairindextypes is deprecated. ' 'Please use sphinx.domains.python.pairindextypes instead.', RemovedInSphinx30Warning) def _pseudo_parse_arglist(signode, arglist):
def indexmarkup_role(typ, rawtext, etext, lineno, inliner, options={}, content=[]): """Role for PEP/RFC references that generate an index entry.""" env = inliner.document.settings.env if not typ: typ = env.config.default_role else: typ = typ.lower() text = utils.unescape(etext) targetid = 'index-%s' % env.new_serialno('index') indexnode = addnodes.index() targetnode = nodes.target('', '', ids=[targetid]) inliner.document.note_explicit_target(targetnode) if typ == 'pep': indexnode['entries'] = [ ('single', _('Python Enhancement Proposals; PEP %s') % text, targetid, '') ] anchor = '' anchorindex = text.find('#') if anchorindex > 0: text, anchor = text[:anchorindex], text[anchorindex:] try: pepnum = int(text) except ValueError: msg = inliner.reporter.error('invalid PEP number %s' % text, line=lineno) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] ref = inliner.document.settings.pep_base_url + 'pep-%04d' % pepnum sn = nodes.strong('PEP ' + text, 'PEP ' + text) rn = nodes.reference('', '', internal=False, refuri=ref + anchor, classes=[typ]) rn += sn return [indexnode, targetnode, rn], [] elif typ == 'rfc': indexnode['entries'] = [('single', 'RFC; RFC %s' % text, targetid, '')] anchor = '' anchorindex = text.find('#') if anchorindex > 0: text, anchor = text[:anchorindex], text[anchorindex:] try: rfcnum = int(text) except ValueError: msg = inliner.reporter.error('invalid RFC number %s' % text, line=lineno) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] ref = inliner.document.settings.rfc_base_url + inliner.rfc_url % rfcnum sn = nodes.strong('RFC ' + text, 'RFC ' + text) rn = nodes.reference('', '', internal=False, refuri=ref + anchor, classes=[typ]) rn += sn return [indexnode, targetnode, rn], []
def process_todo_nodes(app, doctree, fromdocname): # type: (Sphinx, nodes.document, str) -> None node = None # type: nodes.Element if not app.config['todo_include_todos']: for node in doctree.traverse(todo_node): node.parent.remove(node) # Replace all todolist nodes with a list of the collected todos. # Augment each todo with a backlink to the original location. env = app.builder.env if not hasattr(env, 'todo_all_todos'): env.todo_all_todos = [] # type: ignore for node in doctree.traverse(todolist): if node.get('ids'): content = [nodes.target()] # type: List[nodes.Element] else: content = [] if not app.config['todo_include_todos']: node.replace_self(content) continue for todo_info in env.todo_all_todos: # type: ignore para = nodes.paragraph(classes=['todo-source']) if app.config['todo_link_only']: description = _('<<original entry>>') else: description = ( _('(The <<original entry>> is located in %s, line %d.)') % (todo_info['source'], todo_info['lineno'])) desc1 = description[:description.find('<<')] desc2 = description[description.find('>>') + 2:] para += nodes.Text(desc1, desc1) # Create a reference newnode = nodes.reference('', '', internal=True) innernode = nodes.emphasis(_('original entry'), _('original entry')) try: newnode['refuri'] = app.builder.get_relative_uri( fromdocname, todo_info['docname']) try: newnode['refuri'] += '#' + todo_info['target']['refid'] except KeyError: # presently, there's no refid for a todo that appears # inside a param, possibly other places. avoid crashing # and leave a warning so we can track it down. logger.warning(__("TODO target missing refid: %s"), todo_info['source'], location=node) except NoUri: # ignore if no URI can be determined, e.g. for LaTeX output pass newnode.append(innernode) para += newnode para += nodes.Text(desc2, desc2) todo_entry = todo_info['todo'] # Remove targetref from the (copied) node to avoid emitting a # duplicate label of the original entry when we walk this node. if 'targetref' in todo_entry: del todo_entry['targetref'] # (Recursively) resolve references in the todo content env.resolve_references(todo_entry, todo_info['docname'], app.builder) # Insert into the todolist content.append(todo_entry) content.append(para) node.replace_self(content)
class StandardDomain(Domain): """ Domain for all objects that don't fit into another domain or are added via the application interface. """ name = 'std' label = 'Default' object_types = { 'term': ObjType(_('glossary term'), 'term', searchprio=-1), 'token': ObjType(_('grammar token'), 'token', searchprio=-1), 'label': ObjType(_('reference label'), 'ref', 'keyword', searchprio=-1), 'envvar': ObjType(_('environment variable'), 'envvar'), 'cmdoption': ObjType(_('program option'), 'option'), 'doc': ObjType(_('document'), 'doc', searchprio=-1) } # type: Dict[str, ObjType] directives = { 'program': Program, 'cmdoption': Cmdoption, # old name for backwards compatibility 'option': Cmdoption, 'envvar': EnvVar, 'glossary': Glossary, 'productionlist': ProductionList, } # type: Dict[str, Type[Directive]] roles = { 'option': OptionXRefRole(warn_dangling=True), 'envvar': EnvVarXRefRole(), # links to tokens in grammar productions 'token': TokenXRefRole(), # links to terms in glossary 'term': XRefRole(lowercase=True, innernodeclass=nodes.inline, warn_dangling=True), # links to headings or arbitrary labels 'ref': XRefRole(lowercase=True, innernodeclass=nodes.inline, warn_dangling=True), # links to labels of numbered figures, tables and code-blocks 'numref': XRefRole(lowercase=True, warn_dangling=True), # links to labels, without a different title 'keyword': XRefRole(warn_dangling=True), # links to documents 'doc': XRefRole(warn_dangling=True, innernodeclass=nodes.inline), } # type: Dict[str, Union[RoleFunction, XRefRole]] initial_data = { 'progoptions': {}, # (program, name) -> docname, labelid 'objects': {}, # (type, name) -> docname, labelid 'labels': { # labelname -> docname, labelid, sectionname 'genindex': ('genindex', '', _('Index')), 'modindex': ('py-modindex', '', _('Module Index')), 'search': ('search', '', _('Search Page')), }, 'anonlabels': { # labelname -> docname, labelid 'genindex': ('genindex', ''), 'modindex': ('py-modindex', ''), 'search': ('search', ''), }, } dangling_warnings = { 'term': 'term not in glossary: %(target)s', 'ref': 'undefined label: %(target)s (if the link has no caption ' 'the label must precede a section header)', 'numref': 'undefined label: %(target)s', 'keyword': 'unknown keyword: %(target)s', 'doc': 'unknown document: %(target)s', 'option': 'unknown option: %(target)s', } enumerable_nodes = { # node_class -> (figtype, title_getter) nodes.figure: ('figure', None), nodes.table: ('table', None), nodes.container: ('code-block', None), } # type: Dict[Type[Node], Tuple[str, Callable]] def __init__(self, env: "BuildEnvironment") -> None: super().__init__(env) # set up enumerable nodes self.enumerable_nodes = copy(self.enumerable_nodes) # create a copy for this instance for node, settings in env.app.registry.enumerable_nodes.items(): self.enumerable_nodes[node] = settings def note_hyperlink_target(self, name: str, docname: str, node_id: str, title: str = '') -> None: """Add a hyperlink target for cross reference. .. warning:: This is only for internal use. Please don't use this from your extension. ``document.note_explicit_target()`` or ``note_implicit_target()`` are recommended to add a hyperlink target to the document. This only adds a hyperlink target to the StandardDomain. And this does not add a node_id to node. Therefore, it is very fragile to calling this without understanding hyperlink target framework in both docutils and Sphinx. .. versionadded:: 3.0 """ if name in self.anonlabels and self.anonlabels[name] != (docname, node_id): logger.warning(__('duplicate label %s, other instance in %s'), name, self.env.doc2path(self.anonlabels[name][0])) self.anonlabels[name] = (docname, node_id) if title: self.labels[name] = (docname, node_id, title) @property def objects(self) -> Dict[Tuple[str, str], Tuple[str, str]]: return self.data.setdefault('objects', {}) # (objtype, name) -> docname, labelid def note_object(self, objtype: str, name: str, labelid: str, location: Any = None ) -> None: """Note a generic object for cross reference. .. versionadded:: 3.0 """ if (objtype, name) in self.objects: docname = self.objects[objtype, name][0] logger.warning(__('duplicate %s description of %s, other instance in %s'), objtype, name, docname, location=location) self.objects[objtype, name] = (self.env.docname, labelid) def add_object(self, objtype: str, name: str, docname: str, labelid: str) -> None: warnings.warn('StandardDomain.add_object() is deprecated.', RemovedInSphinx50Warning) self.objects[objtype, name] = (docname, labelid) @property def progoptions(self) -> Dict[Tuple[str, str], Tuple[str, str]]: return self.data.setdefault('progoptions', {}) # (program, name) -> docname, labelid @property def labels(self) -> Dict[str, Tuple[str, str, str]]: return self.data.setdefault('labels', {}) # labelname -> docname, labelid, sectionname @property def anonlabels(self) -> Dict[str, Tuple[str, str]]: return self.data.setdefault('anonlabels', {}) # labelname -> docname, labelid def clear_doc(self, docname: str) -> None: key = None # type: Any for key, (fn, _l) in list(self.progoptions.items()): if fn == docname: del self.progoptions[key] for key, (fn, _l) in list(self.objects.items()): if fn == docname: del self.objects[key] for key, (fn, _l, _l) in list(self.labels.items()): if fn == docname: del self.labels[key] for key, (fn, _l) in list(self.anonlabels.items()): if fn == docname: del self.anonlabels[key] def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None: # XXX duplicates? for key, data in otherdata['progoptions'].items(): if data[0] in docnames: self.progoptions[key] = data for key, data in otherdata['objects'].items(): if data[0] in docnames: self.objects[key] = data for key, data in otherdata['labels'].items(): if data[0] in docnames: self.labels[key] = data for key, data in otherdata['anonlabels'].items(): if data[0] in docnames: self.anonlabels[key] = data def process_doc(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA for name, explicit in document.nametypes.items(): if not explicit: continue labelid = document.nameids[name] if labelid is None: continue node = document.ids[labelid] if isinstance(node, nodes.target) and 'refid' in node: # indirect hyperlink targets node = document.ids.get(node['refid']) labelid = node['names'][0] if (node.tagname == 'footnote' or 'refuri' in node or node.tagname.startswith('desc_')): # ignore footnote labels, labels automatically generated from a # link and object descriptions continue if name in self.labels: logger.warning(__('duplicate label %s, other instance in %s'), name, env.doc2path(self.labels[name][0]), location=node) self.anonlabels[name] = docname, labelid if node.tagname in ('section', 'rubric'): title = cast(nodes.title, node[0]) sectname = clean_astext(title) elif self.is_enumerable_node(node): sectname = self.get_numfig_title(node) if not sectname: continue else: toctree = next(iter(node.traverse(addnodes.toctree)), None) if toctree and toctree.get('caption'): sectname = toctree.get('caption') else: # anonymous-only labels continue self.labels[name] = docname, labelid, sectname def add_program_option(self, program: str, name: str, docname: str, labelid: str) -> None: self.progoptions[program, name] = (docname, labelid) def build_reference_node(self, fromdocname: str, builder: "Builder", docname: str, labelid: str, sectname: str, rolename: str, **options: Any ) -> Element: nodeclass = options.pop('nodeclass', nodes.reference) newnode = nodeclass('', '', internal=True, **options) innernode = nodes.inline(sectname, sectname) if innernode.get('classes') is not None: innernode['classes'].append('std') innernode['classes'].append('std-' + rolename) if docname == fromdocname: newnode['refid'] = labelid else: # set more info in contnode; in case the # get_relative_uri call raises NoUri, # the builder will then have to resolve these contnode = pending_xref('') contnode['refdocname'] = docname contnode['refsectname'] = sectname newnode['refuri'] = builder.get_relative_uri( fromdocname, docname) if labelid: newnode['refuri'] += '#' + labelid newnode.append(innernode) return newnode def resolve_xref(self, env: "BuildEnvironment", fromdocname: str, builder: "Builder", typ: str, target: str, node: pending_xref, contnode: Element) -> Element: if typ == 'ref': resolver = self._resolve_ref_xref elif typ == 'numref': resolver = self._resolve_numref_xref elif typ == 'keyword': resolver = self._resolve_keyword_xref elif typ == 'doc': resolver = self._resolve_doc_xref elif typ == 'option': resolver = self._resolve_option_xref elif typ == 'citation': warnings.warn('pending_xref(domain=std, type=citation) is deprecated: %r' % node, RemovedInSphinx40Warning) domain = env.get_domain('citation') return domain.resolve_xref(env, fromdocname, builder, typ, target, node, contnode) else: resolver = self._resolve_obj_xref return resolver(env, fromdocname, builder, typ, target, node, contnode) def _resolve_ref_xref(self, env: "BuildEnvironment", fromdocname: str, builder: "Builder", typ: str, target: str, node: pending_xref, contnode: Element) -> Element: if node['refexplicit']: # reference to anonymous label; the reference uses # the supplied link caption docname, labelid = self.anonlabels.get(target, ('', '')) sectname = node.astext() else: # reference to named label; the final node will # contain the section name after the label docname, labelid, sectname = self.labels.get(target, ('', '', '')) if not docname: return None return self.build_reference_node(fromdocname, builder, docname, labelid, sectname, 'ref') def _resolve_numref_xref(self, env: "BuildEnvironment", fromdocname: str, builder: "Builder", typ: str, target: str, node: pending_xref, contnode: Element) -> Element: if target in self.labels: docname, labelid, figname = self.labels.get(target, ('', '', '')) else: docname, labelid = self.anonlabels.get(target, ('', '')) figname = None if not docname: return None target_node = env.get_doctree(docname).ids.get(labelid) figtype = self.get_enumerable_node_type(target_node) if figtype is None: return None if figtype != 'section' and env.config.numfig is False: logger.warning(__('numfig is disabled. :numref: is ignored.'), location=node) return contnode try: fignumber = self.get_fignumber(env, builder, figtype, docname, target_node) if fignumber is None: return contnode except ValueError: logger.warning(__("no number is assigned for %s: %s"), figtype, labelid, location=node) return contnode try: if node['refexplicit']: title = contnode.astext() else: title = env.config.numfig_format.get(figtype, '') if figname is None and '{name}' in title: logger.warning(__('the link has no caption: %s'), title, location=node) return contnode else: fignum = '.'.join(map(str, fignumber)) if '{name}' in title or 'number' in title: # new style format (cf. "Fig.{number}") if figname: newtitle = title.format(name=figname, number=fignum) else: newtitle = title.format(number=fignum) else: # old style format (cf. "Fig.%s") newtitle = title % fignum except KeyError as exc: logger.warning(__('invalid numfig_format: %s (%r)'), title, exc, location=node) return contnode except TypeError: logger.warning(__('invalid numfig_format: %s'), title, location=node) return contnode return self.build_reference_node(fromdocname, builder, docname, labelid, newtitle, 'numref', nodeclass=addnodes.number_reference, title=title) def _resolve_keyword_xref(self, env: "BuildEnvironment", fromdocname: str, builder: "Builder", typ: str, target: str, node: pending_xref, contnode: Element) -> Element: # keywords are oddballs: they are referenced by named labels docname, labelid, _ = self.labels.get(target, ('', '', '')) if not docname: return None return make_refnode(builder, fromdocname, docname, labelid, contnode) def _resolve_doc_xref(self, env: "BuildEnvironment", fromdocname: str, builder: "Builder", typ: str, target: str, node: pending_xref, contnode: Element) -> Element: # directly reference to document by source name; can be absolute or relative refdoc = node.get('refdoc', fromdocname) docname = docname_join(refdoc, node['reftarget']) if docname not in env.all_docs: return None else: if node['refexplicit']: # reference with explicit title caption = node.astext() else: caption = clean_astext(env.titles[docname]) innernode = nodes.inline(caption, caption, classes=['doc']) return make_refnode(builder, fromdocname, docname, None, innernode) def _resolve_option_xref(self, env: "BuildEnvironment", fromdocname: str, builder: "Builder", typ: str, target: str, node: pending_xref, contnode: Element) -> Element: progname = node.get('std:program') target = target.strip() docname, labelid = self.progoptions.get((progname, target), ('', '')) if not docname: commands = [] while ws_re.search(target): subcommand, target = ws_re.split(target, 1) commands.append(subcommand) progname = "-".join(commands) docname, labelid = self.progoptions.get((progname, target), ('', '')) if docname: break else: return None return make_refnode(builder, fromdocname, docname, labelid, contnode) def _resolve_obj_xref(self, env: "BuildEnvironment", fromdocname: str, builder: "Builder", typ: str, target: str, node: pending_xref, contnode: Element) -> Element: objtypes = self.objtypes_for_role(typ) or [] for objtype in objtypes: if (objtype, target) in self.objects: docname, labelid = self.objects[objtype, target] break else: docname, labelid = '', '' if not docname: return None return make_refnode(builder, fromdocname, docname, labelid, contnode) def resolve_any_xref(self, env: "BuildEnvironment", fromdocname: str, builder: "Builder", target: str, node: pending_xref, contnode: Element) -> List[Tuple[str, Element]]: results = [] # type: List[Tuple[str, Element]] ltarget = target.lower() # :ref: lowercases its target automatically for role in ('ref', 'option'): # do not try "keyword" res = self.resolve_xref(env, fromdocname, builder, role, ltarget if role == 'ref' else target, node, contnode) if res: results.append(('std:' + role, res)) # all others for objtype in self.object_types: key = (objtype, target) if objtype == 'term': key = (objtype, ltarget) if key in self.objects: docname, labelid = self.objects[key] results.append(('std:' + self.role_for_objtype(objtype), make_refnode(builder, fromdocname, docname, labelid, contnode))) return results def get_objects(self) -> Iterator[Tuple[str, str, str, str, str, int]]: # handle the special 'doc' reference here for doc in self.env.all_docs: yield (doc, clean_astext(self.env.titles[doc]), 'doc', doc, '', -1) for (prog, option), info in self.progoptions.items(): if prog: fullname = ".".join([prog, option]) yield (fullname, fullname, 'cmdoption', info[0], info[1], 1) else: yield (option, option, 'cmdoption', info[0], info[1], 1) for (type, name), info in self.objects.items(): yield (name, name, type, info[0], info[1], self.object_types[type].attrs['searchprio']) for name, (docname, labelid, sectionname) in self.labels.items(): yield (name, sectionname, 'label', docname, labelid, -1) # add anonymous-only labels as well non_anon_labels = set(self.labels) for name, (docname, labelid) in self.anonlabels.items(): if name not in non_anon_labels: yield (name, name, 'label', docname, labelid, -1) def get_type_name(self, type: ObjType, primary: bool = False) -> str: # never prepend "Default" return type.lname def is_enumerable_node(self, node: Node) -> bool: return node.__class__ in self.enumerable_nodes def get_numfig_title(self, node: Node) -> str: """Get the title of enumerable nodes to refer them using its title""" if self.is_enumerable_node(node): elem = cast(Element, node) _, title_getter = self.enumerable_nodes.get(elem.__class__, (None, None)) if title_getter: return title_getter(elem) else: for subnode in elem: if isinstance(subnode, (nodes.caption, nodes.title)): return clean_astext(subnode) return None def get_enumerable_node_type(self, node: Node) -> str: """Get type of enumerable nodes.""" def has_child(node: Element, cls: "Type") -> bool: return any(isinstance(child, cls) for child in node) if isinstance(node, nodes.section): return 'section' elif (isinstance(node, nodes.container) and 'literal_block' in node and has_child(node, nodes.literal_block)): # given node is a code-block having caption return 'code-block' else: figtype, _ = self.enumerable_nodes.get(node.__class__, (None, None)) return figtype def get_fignumber(self, env: "BuildEnvironment", builder: "Builder", figtype: str, docname: str, target_node: Element) -> Tuple[int, ...]: if figtype == 'section': if builder.name == 'latex': return tuple() elif docname not in env.toc_secnumbers: raise ValueError # no number assigned else: anchorname = '#' + target_node['ids'][0] if anchorname not in env.toc_secnumbers[docname]: # try first heading which has no anchor return env.toc_secnumbers[docname].get('') else: return env.toc_secnumbers[docname].get(anchorname) else: try: figure_id = target_node['ids'][0] return env.toc_fignumbers[docname][figtype][figure_id] except (KeyError, IndexError): # target_node is found, but fignumber is not assigned. # Maybe it is defined in orphaned document. raise ValueError def get_full_qualified_name(self, node: Element) -> str: if node.get('reftype') == 'option': progname = node.get('std:program') command = ws_re.split(node.get('reftarget')) if progname: command.insert(0, progname) option = command.pop() if command: return '.'.join(['-'.join(command), option]) else: return None else: return None def note_citations(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA warnings.warn('StandardDomain.note_citations() is deprecated.', RemovedInSphinx40Warning) def note_citation_refs(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA warnings.warn('StandardDomain.note_citation_refs() is deprecated.', RemovedInSphinx40Warning) def note_labels(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA warnings.warn('StandardDomain.note_labels() is deprecated.', RemovedInSphinx40Warning)
def write(self, *ignored): # type: (Any) -> None version = self.config.version domain = cast(ChangeSetDomain, self.env.get_domain('changeset')) libchanges = {} # type: Dict[str, List[Tuple[str, str, int]]] apichanges = [] # type: List[Tuple[str, str, int]] otherchanges = { } # type: Dict[Tuple[str, str], List[Tuple[str, str, int]]] if version not in self.env.versionchanges: logger.info(bold(__('no changes in version %s.') % version)) return logger.info(bold('writing summary file...')) for changeset in domain.get_changesets_for(version): if isinstance(changeset.descname, tuple): descname = changeset.descname[0] else: descname = changeset.descname ttext = self.typemap[changeset.type] context = changeset.content.replace('\n', ' ') if descname and changeset.docname.startswith('c-api'): if context: entry = '<b>%s</b>: <i>%s:</i> %s' % (descname, ttext, context) else: entry = '<b>%s</b>: <i>%s</i>.' % (descname, ttext) apichanges.append((entry, changeset.docname, changeset.lineno)) elif descname or changeset.module: if not changeset.module: module = _('Builtins') if not descname: descname = _('Module level') if context: entry = '<b>%s</b>: <i>%s:</i> %s' % (descname, ttext, context) else: entry = '<b>%s</b>: <i>%s</i>.' % (descname, ttext) libchanges.setdefault(module, []).append( (entry, changeset.docname, changeset.lineno)) else: if not context: continue entry = '<i>%s:</i> %s' % (ttext.capitalize(), context) title = self.env.titles[changeset.docname].astext() otherchanges.setdefault((changeset.docname, title), []).append( (entry, changeset.docname, changeset.lineno)) ctx = { 'project': self.config.project, 'version': version, 'docstitle': self.config.html_title, 'shorttitle': self.config.html_short_title, 'libchanges': sorted(libchanges.items()), 'apichanges': sorted(apichanges), 'otherchanges': sorted(otherchanges.items()), 'show_copyright': self.config.html_show_copyright, 'show_sphinx': self.config.html_show_sphinx, } with open(path.join(self.outdir, 'index.html'), 'w', encoding='utf8') as f: f.write(self.templates.render('changes/frameset.html', ctx)) with open(path.join(self.outdir, 'changes.html'), 'w', encoding='utf8') as f: f.write(self.templates.render('changes/versionchanges.html', ctx)) hltext = [ '.. versionadded:: %s' % version, '.. versionchanged:: %s' % version, '.. deprecated:: %s' % version ] def hl(no, line): # type: (int, str) -> str line = '<a name="L%s"> </a>' % no + html.escape(line) for x in hltext: if x in line: line = '<span class="hl">%s</span>' % line break return line logger.info(bold(__('copying source files...'))) for docname in self.env.all_docs: with open(self.env.doc2path(docname), encoding=self.env.config.source_encoding) as f: try: lines = f.readlines() except UnicodeDecodeError: logger.warning( __('could not read %r for changelog creation'), docname) continue targetfn = path.join(self.outdir, 'rst', os_path(docname)) + '.html' ensuredir(path.dirname(targetfn)) with open(targetfn, 'w', encoding='utf-8') as f: text = ''.join( hl(i + 1, line) for (i, line) in enumerate(lines)) ctx = { 'filename': self.env.doc2path(docname, None), 'text': text } f.write(self.templates.render('changes/rstsource.html', ctx)) themectx = dict(('theme_' + key, val) for (key, val) in self.theme.get_options({}).items()) copy_asset_file(path.join(package_dir, 'themes', 'default', 'static', 'default.css_t'), self.outdir, context=themectx, renderer=self.templates) copy_asset_file( path.join(package_dir, 'themes', 'basic', 'static', 'basic.css'), self.outdir)
def man_visit_mermaid(self, node): if "alt" in node.attributes: self.body.append(_("[graph: %s]") % node["alt"]) else: self.body.append(_("[graph]")) raise nodes.SkipNode
def text_visit_mermaid(self, node): if "alt" in node.attributes: self.add_text(_("[graph: %s]") % node["alt"]) else: self.add_text(_("[graph]")) raise nodes.SkipNode
def man_visit_graphviz(self, node): if 'alt' in node.attributes: self.body.append(_('[graph: %s]') % node['alt']) else: self.body.append(_('[graph]')) raise nodes.SkipNode
def doctree_read(app, doctree): # type: (Sphinx, nodes.Node) -> None env = app.builder.env if not hasattr(env, '_viewcode_modules'): env._viewcode_modules = {} # type: ignore if app.builder.name == "singlehtml": return if app.builder.name.startswith( "epub") and not env.config.viewcode_enable_epub: return def has_tag(modname, fullname, docname, refname): entry = env._viewcode_modules.get(modname, None) # type: ignore try: analyzer = ModuleAnalyzer.for_module(modname) except Exception: env._viewcode_modules[modname] = False # type: ignore return if not isinstance(analyzer.code, text_type): code = analyzer.code.decode(analyzer.encoding) else: code = analyzer.code if entry is None or entry[0] != code: analyzer.find_tags() entry = code, analyzer.tags, {}, refname env._viewcode_modules[modname] = entry # type: ignore elif entry is False: return _, tags, used, _ = entry if fullname in tags: used[fullname] = docname return True for objnode in doctree.traverse(addnodes.desc): if objnode.get('domain') != 'py': continue names = set() # type: Set[unicode] for signode in objnode: if not isinstance(signode, addnodes.desc_signature): continue modname = signode.get('module') fullname = signode.get('fullname') refname = modname if env.config.viewcode_import: modname = _get_full_modname(app, modname, fullname) if not modname: continue fullname = signode.get('fullname') if not has_tag(modname, fullname, env.docname, refname): continue if fullname in names: # only one link per name, please continue names.add(fullname) pagename = '_modules/' + modname.replace('.', '/') onlynode = addnodes.only(expr='html') onlynode += addnodes.pending_xref('', reftype='viewcode', refdomain='std', refexplicit=False, reftarget=pagename, refid=fullname, refdoc=env.docname) onlynode[0] += nodes.inline('', _('[source]'), classes=['viewcode-link']) signode += onlynode
def missing_reference(app, env, node, contnode): # type: (Sphinx, BuildEnvironment, nodes.Element, nodes.TextElement) -> None """Attempt to resolve a missing reference via intersphinx references.""" target = node['reftarget'] inventories = InventoryAdapter(env) objtypes = None # type: List[unicode] if node['reftype'] == 'any': # we search anything! objtypes = [ '%s:%s' % (domain.name, objtype) for domain in env.domains.values() for objtype in domain.object_types ] domain = None else: domain = node.get('refdomain') if not domain: # only objects in domains are in the inventory return objtypes = env.get_domain(domain).objtypes_for_role(node['reftype']) if not objtypes: return objtypes = ['%s:%s' % (domain, objtype) for objtype in objtypes] if 'std:cmdoption' in objtypes: # until Sphinx-1.6, cmdoptions are stored as std:option objtypes.append('std:option') to_try = [(inventories.main_inventory, target)] if domain: full_qualified_name = env.get_domain(domain).get_full_qualified_name( node) if full_qualified_name: to_try.append((inventories.main_inventory, full_qualified_name)) in_set = None if ':' in target: # first part may be the foreign doc set name setname, newtarget = target.split(':', 1) if setname in inventories.named_inventory: in_set = setname to_try.append((inventories.named_inventory[setname], newtarget)) if domain: node['reftarget'] = newtarget full_qualified_name = env.get_domain( domain).get_full_qualified_name(node) if full_qualified_name: to_try.append((inventories.named_inventory[setname], full_qualified_name)) for inventory, target in to_try: for objtype in objtypes: if objtype not in inventory or target not in inventory[objtype]: continue proj, version, uri, dispname = inventory[objtype][target] if '://' not in uri and node.get('refdoc'): # get correct path in case of subdirectories uri = path.join(relative_path(node['refdoc'], '.'), uri) if version: reftitle = _('(in %s v%s)') % (proj, version) else: reftitle = _('(in %s)') % (proj, ) newnode = nodes.reference('', '', internal=False, refuri=uri, reftitle=reftitle) if node.get('refexplicit'): # use whatever title was given newnode.append(contnode) elif dispname == '-' or \ (domain == 'std' and node['reftype'] == 'keyword'): # use whatever title was given, but strip prefix title = contnode.astext() if in_set and title.startswith(in_set + ':'): newnode.append( contnode.__class__(title[len(in_set) + 1:], title[len(in_set) + 1:])) else: newnode.append(contnode) else: # else use the given display name (used for :ref:) newnode.append(contnode.__class__(dispname, dispname)) return newnode # at least get rid of the ':' in the target if no explicit title given if in_set is not None and not node.get('refexplicit', True): if len(contnode) and isinstance(contnode[0], nodes.Text): contnode[0] = nodes.Text(newtarget, contnode[0].rawsource)
def missing_reference(app, env, node, contnode): """Attempt to resolve a missing reference via intersphinx references.""" target = node['reftarget'] if node['reftype'] == 'any': # we search anything! objtypes = [ '%s:%s' % (domain.name, objtype) for domain in env.domains.values() for objtype in domain.object_types ] domain = None elif node['reftype'] == 'doc': domain = 'std' # special case objtypes = ['std:doc'] else: domain = node.get('refdomain') if not domain: # only objects in domains are in the inventory return objtypes = env.domains[domain].objtypes_for_role(node['reftype']) if not objtypes: return objtypes = ['%s:%s' % (domain, objtype) for objtype in objtypes] to_try = [(env.intersphinx_inventory, target)] in_set = None if ':' in target: # first part may be the foreign doc set name setname, newtarget = target.split(':', 1) if setname in env.intersphinx_named_inventory: in_set = setname to_try.append( (env.intersphinx_named_inventory[setname], newtarget)) for inventory, target in to_try: for objtype in objtypes: if objtype not in inventory or target not in inventory[objtype]: continue proj, version, uri, dispname = inventory[objtype][target] if '://' not in uri and node.get('refdoc'): # get correct path in case of subdirectories uri = path.join(relative_path(node['refdoc'], '.'), uri) newnode = nodes.reference('', '', internal=False, refuri=uri, reftitle=_('(in %s v%s)') % (proj, version)) if node.get('refexplicit'): # use whatever title was given newnode.append(contnode) elif dispname == '-' or \ (domain == 'std' and node['reftype'] == 'keyword'): # use whatever title was given, but strip prefix title = contnode.astext() if in_set and title.startswith(in_set + ':'): newnode.append( contnode.__class__(title[len(in_set) + 1:], title[len(in_set) + 1:])) else: newnode.append(contnode) else: # else use the given display name (used for :ref:) newnode.append(contnode.__class__(dispname, dispname)) return newnode # at least get rid of the ':' in the target if no explicit title given if in_set is not None and not node.get('refexplicit', True): if len(contnode) and isinstance(contnode[0], nodes.Text): contnode[0] = nodes.Text(newtarget, contnode[0].rawsource)