diff options
| -rw-r--r-- | sphinx/application.py | 1 | ||||
| -rw-r--r-- | sphinx/builders/html.py | 20 | ||||
| -rw-r--r-- | sphinx/directives/desc.py | 6 | ||||
| -rw-r--r-- | sphinx/directives/other.py | 71 | ||||
| -rw-r--r-- | sphinx/domains.py | 170 | ||||
| -rw-r--r-- | sphinx/environment.py | 29 | ||||
| -rw-r--r-- | sphinx/ext/coverage.py | 14 | ||||
| -rw-r--r-- | sphinx/search.py | 4 | ||||
| -rw-r--r-- | tests/test_env.py | 9 |
9 files changed, 179 insertions, 145 deletions
diff --git a/sphinx/application.py b/sphinx/application.py index 3566bc50..7d9ee49d 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -14,6 +14,7 @@ import sys import types import posixpath +from os import path from cStringIO import StringIO from docutils import nodes diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 20e84b41..de4c9a39 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -240,7 +240,9 @@ class StandaloneHTMLBuilder(Builder): rellinks = [] if self.config.html_use_index: rellinks.append(('genindex', _('General Index'), 'I', _('index'))) - if self.config.html_use_modindex and self.env.modules: + # XXX generalization of modindex? + if self.config.html_use_modindex and \ + self.env.domains['py'].data['modules']: rellinks.append(('modindex', _('Global Module Index'), 'M', _('modules'))) @@ -405,12 +407,13 @@ class StandaloneHTMLBuilder(Builder): # the global module index - if self.config.html_use_modindex and self.env.modules: + moduleindex = self.env.domains['py'].data['modules'] + if self.config.html_use_modindex and moduleindex: # the sorted list of all modules, for the global module index modules = sorted(((mn, (self.get_relative_uri('modindex', fn) + '#module-' + mn, sy, pl, dep)) for (mn, (fn, sy, pl, dep)) in - self.env.modules.iteritems()), + moduleindex.iteritems()), key=lambda x: x[0].lower()) # collect all platforms platforms = set() @@ -710,14 +713,15 @@ class StandaloneHTMLBuilder(Builder): self.info(bold('dumping object inventory... '), nonl=True) f = open(path.join(self.outdir, INVENTORY_FILENAME), 'w') try: + # XXX inventory version 2 f.write('# Sphinx inventory version 1\n') f.write('# Project: %s\n' % self.config.project.encode('utf-8')) f.write('# Version: %s\n' % self.config.version) - for modname, info in self.env.modules.iteritems(): - f.write('%s mod %s\n' % (modname, self.get_target_uri(info[0]))) - for refname, (docname, desctype) in self.env.descrefs.iteritems(): - f.write('%s %s %s\n' % (refname, desctype, - self.get_target_uri(docname))) + #for modname, info in self.env.modules.iteritems(): + # f.write('%s mod %s\n' % (modname, self.get_target_uri(info[0]))) + #for refname, (docname, desctype) in self.env.descrefs.iteritems(): + # f.write('%s %s %s\n' % (refname, desctype, + # self.get_target_uri(docname))) finally: f.close() self.info('done') diff --git a/sphinx/directives/desc.py b/sphinx/directives/desc.py index e3018100..06fc4c98 100644 --- a/sphinx/directives/desc.py +++ b/sphinx/directives/desc.py @@ -204,7 +204,10 @@ class DescDirective(Directive): pass def run(self): - self.desctype = self.name + if ':' in self.name: + self.domain, self.desctype = self.name.split(':', 1) + else: + self.domain, self.desctype = '', self.name self.env = self.state.document.settings.env self.indexnode = addnodes.index(entries=[]) @@ -366,7 +369,6 @@ class DefaultDomain(Directive): def run(self): env = self.state.document.settings.env domain_name = arguments[0] - # XXX won't work env.default_domain = env.domains.get(domain_name) diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py index d056f3dc..ee938cfd 100644 --- a/sphinx/directives/other.py +++ b/sphinx/directives/other.py @@ -103,75 +103,6 @@ class TocTree(Directive): return ret -class Module(Directive): - """ - Directive to mark description of a new module. - """ - - has_content = False - required_arguments = 1 - optional_arguments = 0 - final_argument_whitespace = False - option_spec = { - 'platform': lambda x: x, - 'synopsis': lambda x: x, - 'noindex': directives.flag, - 'deprecated': directives.flag, - } - - def run(self): - env = self.state.document.settings.env - modname = self.arguments[0].strip() - noindex = 'noindex' in self.options - env.currmodule = modname - env.note_module(modname, self.options.get('synopsis', ''), - self.options.get('platform', ''), - 'deprecated' in self.options) - modulenode = addnodes.module() - modulenode['modname'] = modname - modulenode['synopsis'] = self.options.get('synopsis', '') - targetnode = nodes.target('', '', ids=['module-' + modname], ismod=True) - self.state.document.note_explicit_target(targetnode) - ret = [modulenode, targetnode] - if 'platform' in self.options: - platform = self.options['platform'] - modulenode['platform'] = 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.insert(0, inode) - return ret - - -class CurrentModule(Directive): - """ - This directive is just to tell Sphinx that we're documenting - stuff in module foo, but links to module foo won't lead here. - """ - - has_content = False - required_arguments = 1 - optional_arguments = 0 - final_argument_whitespace = False - option_spec = {} - - def run(self): - env = self.state.document.settings.env - modname = self.arguments[0].strip() - if modname == 'None': - env.currmodule = None - else: - env.currmodule = modname - return [] - - class Author(Directive): """ Directive to give the name of the author of the current document @@ -559,8 +490,6 @@ class Only(Directive): directives.register_directive('toctree', directive_dwim(TocTree)) -directives.register_directive('module', directive_dwim(Module)) -directives.register_directive('currentmodule', directive_dwim(CurrentModule)) directives.register_directive('sectionauthor', directive_dwim(Author)) directives.register_directive('moduleauthor', directive_dwim(Author)) directives.register_directive('program', directive_dwim(Program)) diff --git a/sphinx/domains.py b/sphinx/domains.py index 66448111..3e5b22e8 100644 --- a/sphinx/domains.py +++ b/sphinx/domains.py @@ -14,11 +14,13 @@ import re import string from docutils import nodes +from docutils.parsers.rst import directives from sphinx import addnodes from sphinx.roles import XRefRole from sphinx.directives import DescDirective from sphinx.util import make_refnode +from sphinx.util.compat import Directive class Domain(object): @@ -28,28 +30,28 @@ class Domain(object): label = '' # data value for a fresh environment - initial_data = { - - } + initial_data = {} # data version data_version = 0 def __init__(self, env): self.env = env - self.data = env.domaindata.get(self.name, self.initial_data) - if 'version' in self.data and self.data['version'] < self.data_version: - raise IOError('data of %r domain out of date' % self.label) + if self.name not in env.domaindata: + new_data = self.initial_data.copy() + new_data['version'] = self.data_version + self.data = env.domaindata[self.name] = new_data + else: + self.data = env.domaindata[self.name] + if self.data['version'] < self.data_version: + raise IOError('data of %r domain out of date' % self.label) self._role_cache = {} self._directive_cache = {} - def __getstate__(self): - # can't pickle the adapter caches - state = self.__dict__.copy() - state['_role_cache'] = {} - state['_directive_cache'] = {} - return state - - #def clear_doc + def clear_doc(self, docname): + """ + Remove traces of a document in the domain-specific inventories. + """ + pass def role(self, name): """ @@ -212,7 +214,15 @@ class PythonDesc(DescDirective): signode['ids'].append(fullname) signode['first'] = (not self.names) self.state.document.note_explicit_target(signode) - self.env.note_descref(fullname, self.desctype, self.lineno) + objects = self.env.domains['py'].data['objects'] + if fullname in objects: + self.env.warn( + self.env.docname, + 'duplicate object description of %s, ' % fullname + + 'other instance in ' + + self.env.doc2path(objects[fullname][0]), + self.lineno) + objects[fullname] = (self.env.docname, self.desctype) indextext = self.get_index_text(modname, name_cls) if indextext: @@ -352,6 +362,75 @@ class ClassmemberDesc(PythonDesc): self.clsname_set = True +class PyModule(Directive): + """ + Directive to mark description of a new module. + """ + + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = { + 'platform': lambda x: x, + 'synopsis': lambda x: x, + 'noindex': directives.flag, + 'deprecated': directives.flag, + } + + def run(self): + env = self.state.document.settings.env + modname = self.arguments[0].strip() + noindex = 'noindex' in self.options + env.currmodule = modname + env.domains['py'].data['modules'][modname] = \ + (env.docname, self.options.get('synopsis', ''), + self.options.get('platform', ''), 'deprecated' in self.options) + modulenode = addnodes.module() + modulenode['modname'] = modname + modulenode['synopsis'] = self.options.get('synopsis', '') + targetnode = nodes.target('', '', ids=['module-' + modname], ismod=True) + self.state.document.note_explicit_target(targetnode) + ret = [modulenode, targetnode] + if 'platform' in self.options: + platform = self.options['platform'] + modulenode['platform'] = 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.insert(0, inode) + return ret + + +class PyCurrentModule(Directive): + """ + This directive is just to tell Sphinx that we're documenting + stuff in module foo, but links to module foo won't lead here. + """ + + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = {} + + def run(self): + env = self.state.document.settings.env + modname = self.arguments[0].strip() + if modname == 'None': + env.currmodule = None + else: + env.currmodule = modname + return [] + + class PyXRefRole(XRefRole): def process_link(self, env, pnode, has_explicit_title, title, target): pnode['modname'] = env.currmodule @@ -387,6 +466,8 @@ class PythonDomain(Domain): 'classmethod': ClassmemberDesc, 'staticmethod': ClassmemberDesc, 'attribute': ClassmemberDesc, + 'module': PyModule, + 'currentmodule': PyCurrentModule, } roles = { 'data': PyXRefRole(), @@ -399,6 +480,18 @@ class PythonDomain(Domain): 'mod': PyXRefRole(), 'obj': PyXRefRole(), } + initial_data = { + 'objects': {}, # fullname -> docname, desctype + 'modules': {}, # modname -> docname, synopsis, platform, deprecated + } + + def clear_doc(self, docname): + for fullname, (fn, _) in self.data['objects'].items(): + if fn == docname: + del self.data['objects'][fullname] + for modname, (fn, _, _, _) in self.data['modules'].items(): + if fn == docname: + del self.data['modules'][modname] def find_desc(self, env, modname, classname, name, type, searchorder=0): """ @@ -412,41 +505,43 @@ class PythonDomain(Domain): if not name: return None, None + objects = self.data['objects'] + newname = None if searchorder == 1: if modname and classname and \ - modname + '.' + classname + '.' + name in env.descrefs: + modname + '.' + classname + '.' + name in objects: newname = modname + '.' + classname + '.' + name - elif modname and modname + '.' + name in env.descrefs: + elif modname and modname + '.' + name in objects: newname = modname + '.' + name - elif name in env.descrefs: + elif name in objects: newname = name else: - if name in env.descrefs: + if name in objects: newname = name - elif modname and modname + '.' + name in env.descrefs: + elif modname and modname + '.' + name in objects: newname = modname + '.' + name elif modname and classname and \ - modname + '.' + classname + '.' + name in env.descrefs: + 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 env.descrefs: + 'exceptions.' + name in objects: newname = 'exceptions.' + name # special case: object methods elif type in ('func', 'meth') and '.' not in name and \ - 'object.' + name in env.descrefs: + 'object.' + name in objects: newname = 'object.' + name if newname is None: return None, None - return newname, env.descrefs[newname] + return newname, objects[newname] def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): if (typ == 'mod' or - typ == 'obj' and target in env.modules): + typ == 'obj' and target in self.data['modules']): docname, synopsis, platform, deprecated = \ - env.modules.get(target, ('','','', '')) + self.data['modules'].get(target, ('','','', '')) if not docname: return None elif docname == fromdocname: @@ -459,7 +554,6 @@ class PythonDomain(Domain): return make_refnode(builder, fromdocname, docname, 'module-' + target, contnode, title) else: - # "descrefs" modname = node['modname'] clsname = node['classname'] searchorder = node.hasattr('refspecific') and 1 or 0 @@ -589,7 +683,14 @@ class CDesc(DescDirective): signode['ids'].append(name) signode['first'] = (not self.names) self.state.document.note_explicit_target(signode) - self.env.note_descref(name, self.desctype, self.lineno) + inv = self.env.domains['c'].data['objects'] + if name in inv: + self.env.warn( + self.env.docname, + 'duplicate C object description of %s, ' % name + + 'other instance in ' + self.env.doc2path(inv[name][0]), + self.lineno) + inv[name] = (self.env.docname, self.desctype) indextext = self.get_index_text(name) if indextext: @@ -614,15 +715,22 @@ class CDomain(Domain): 'data': XRefRole(), 'type': XRefRole(), } + initial_data = { + 'objects': {}, # fullname -> docname, desctype + } + + def clear_doc(self, docname): + for fullname, (fn, _) in self.data['objects'].items(): + if fn == docname: + del self.data['objects'][fullname] def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): # strip pointer asterisk target = target.rstrip(' *') - # XXX descrefs - if target not in env.descrefs: + if target not in self.data: return None - desc = env.descrefs[target] + desc = self.data[target] return make_refnode(builder, fromdocname, desc[0], contnode, target) diff --git a/sphinx/environment.py b/sphinx/environment.py index 62d3875b..2f64b147 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -297,13 +297,10 @@ class BuildEnvironment: self.glob_toctrees = set() # docnames that have :glob: toctrees self.numbered_toctrees = set() # docnames that have :numbered: toctrees - # domain-specific inventories - self.domaindata = {} # domainname -> object + # domain-specific inventories, here to be pickled + self.domaindata = {} # domainname -> domain-specific object # X-ref target inventory - self.descrefs = {} # fullname -> docname, desctype - self.modules = {} # modname -> docname, synopsis, - # platform, deprecated self.labels = {} # labelname -> docname, labelid, sectionname self.anonlabels = {} # labelname -> docname, labelid self.progoptions = {} # (program, name) -> docname, labelid @@ -371,12 +368,6 @@ class BuildEnvironment: fnset.discard(docname) if not fnset: del self.files_to_rebuild[subfn] - for fullname, (fn, _) in self.descrefs.items(): - if fn == docname: - del self.descrefs[fullname] - for modname, (fn, _, _, _) in self.modules.items(): - if fn == docname: - del self.modules[modname] for labelname, (fn, _, _) in self.labels.items(): if fn == docname: del self.labels[labelname] @@ -390,6 +381,10 @@ class BuildEnvironment: new = [change for change in changes if change[1] != docname] changes[:] = new + # XXX why does this not work inside the if? + for domain in self.domains.values(): + domain.clear_doc(docname) + def doc2path(self, docname, base=True, suffix=None): """ Return the filename for the document name. @@ -1001,18 +996,6 @@ class BuildEnvironment: # ------- # these are called from docutils directives and therefore use self.docname # - def note_descref(self, fullname, desctype, line): - if fullname in self.descrefs: - self.warn(self.docname, - 'duplicate canonical description name %s, ' % fullname + - 'other instance in ' + - self.doc2path(self.descrefs[fullname][0]), - line) - self.descrefs[fullname] = (self.docname, desctype) - - def note_module(self, modname, synopsis, platform, deprecated): - self.modules[modname] = (self.docname, synopsis, platform, deprecated) - def note_progoption(self, optname, labelid): self.progoptions[self.currprogram, optname] = (self.docname, labelid) diff --git a/sphinx/ext/coverage.py b/sphinx/ext/coverage.py index 964e58ee..6eb9bc8b 100644 --- a/sphinx/ext/coverage.py +++ b/sphinx/ext/coverage.py @@ -79,6 +79,7 @@ class CoverageBuilder(Builder): def build_c_coverage(self): # Fetch all the info from the header files + c_objects = self.env.domains['c'].data['objects'] for filename in self.c_sourcefiles: undoc = [] f = open(filename, 'r') @@ -88,7 +89,7 @@ class CoverageBuilder(Builder): match = regex.match(line) if match: name = match.groups()[0] - if name not in self.env.descrefs: + if name not in c_objects: for exp in self.c_ignorexps.get(key, ()): if exp.match(name): break @@ -116,7 +117,10 @@ class CoverageBuilder(Builder): op.close() def build_py_coverage(self): - for mod_name in self.env.modules: + objects = self.env.domains['py'].data['objects'] + modules = self.env.domains['py'].data['modules'] + + for mod_name in modules: ignore = False for exp in self.mod_ignorexps: if exp.match(mod_name): @@ -151,7 +155,7 @@ class CoverageBuilder(Builder): full_name = '%s.%s' % (mod_name, name) if inspect.isfunction(obj): - if full_name not in self.env.descrefs: + if full_name not in objects: for exp in self.fun_ignorexps: if exp.match(name): break @@ -162,7 +166,7 @@ class CoverageBuilder(Builder): if exp.match(name): break else: - if full_name not in self.env.descrefs: + if full_name not in objects: # not documented at all classes[name] = [] continue @@ -176,7 +180,7 @@ class CoverageBuilder(Builder): continue full_attr_name = '%s.%s' % (full_name, attr_name) - if full_attr_name not in self.env.descrefs: + if full_attr_name not in objects: attrs.append(attr_name) if attrs: diff --git a/sphinx/search.py b/sphinx/search.py index 499c4aa9..f283881f 100644 --- a/sphinx/search.py +++ b/sphinx/search.py @@ -149,6 +149,8 @@ class IndexBuilder(object): def get_modules(self, fn2index): rv = {} + # XXX implement search capability + return rv for name, (doc, _, _, _) in self.env.modules.iteritems(): if doc in fn2index: rv[name] = fn2index[doc] @@ -157,6 +159,8 @@ class IndexBuilder(object): def get_descrefs(self, fn2index): rv = {} dt = self._desctypes + # XXX implement search capability + return rv for fullname, (doc, desctype) in self.env.descrefs.iteritems(): if doc not in fn2index: continue diff --git a/tests/test_env.py b/tests/test_env.py index d5f40f8c..921d893c 100644 --- a/tests/test_env.py +++ b/tests/test_env.py @@ -20,9 +20,8 @@ warnings = [] def setup_module(): global app, env - app = TestApp(srcdir='(temp)') + app = TestApp(srcdir='(temp)', freshenv=True) env = app.env - #env = BuildEnvironment(app.srcdir, app.doctreedir, app.config) env.set_warnfunc(lambda *args: warnings.append(args)) def teardown_module(): @@ -93,7 +92,7 @@ def test_second_update(): assert 'autodoc' not in env.found_docs def test_object_inventory(): - refs = env.descrefs + refs = env.domains['py'].data['objects'] assert 'func_without_module' in refs assert refs['func_without_module'] == ('desc', 'function') @@ -110,5 +109,5 @@ def test_object_inventory(): assert 'func_in_module' not in refs assert 'func_noindex' not in refs - assert 'mod' in env.modules - assert env.modules['mod'] == ('desc', 'Module synopsis.', 'UNIX', False) + assert env.domains['py'].data['modules']['mod'] == \ + ('desc', 'Module synopsis.', 'UNIX', False) |
