With a few disabled features (see XXX), the test suite runs again.

This commit is contained in:
Georg Brandl
2009-07-13 21:28:01 +02:00
parent a1dd4695f3
commit 1c030b415e
9 changed files with 179 additions and 145 deletions

View File

@@ -14,6 +14,7 @@
import sys import sys
import types import types
import posixpath import posixpath
from os import path
from cStringIO import StringIO from cStringIO import StringIO
from docutils import nodes from docutils import nodes

View File

@@ -240,7 +240,9 @@ class StandaloneHTMLBuilder(Builder):
rellinks = [] rellinks = []
if self.config.html_use_index: if self.config.html_use_index:
rellinks.append(('genindex', _('General Index'), 'I', _('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'), rellinks.append(('modindex', _('Global Module Index'),
'M', _('modules'))) 'M', _('modules')))
@@ -405,12 +407,13 @@ class StandaloneHTMLBuilder(Builder):
# the global module index # 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 # the sorted list of all modules, for the global module index
modules = sorted(((mn, (self.get_relative_uri('modindex', fn) + modules = sorted(((mn, (self.get_relative_uri('modindex', fn) +
'#module-' + mn, sy, pl, dep)) '#module-' + mn, sy, pl, dep))
for (mn, (fn, sy, pl, dep)) in for (mn, (fn, sy, pl, dep)) in
self.env.modules.iteritems()), moduleindex.iteritems()),
key=lambda x: x[0].lower()) key=lambda x: x[0].lower())
# collect all platforms # collect all platforms
platforms = set() platforms = set()
@@ -710,14 +713,15 @@ class StandaloneHTMLBuilder(Builder):
self.info(bold('dumping object inventory... '), nonl=True) self.info(bold('dumping object inventory... '), nonl=True)
f = open(path.join(self.outdir, INVENTORY_FILENAME), 'w') f = open(path.join(self.outdir, INVENTORY_FILENAME), 'w')
try: try:
# XXX inventory version 2
f.write('# Sphinx inventory version 1\n') f.write('# Sphinx inventory version 1\n')
f.write('# Project: %s\n' % self.config.project.encode('utf-8')) f.write('# Project: %s\n' % self.config.project.encode('utf-8'))
f.write('# Version: %s\n' % self.config.version) f.write('# Version: %s\n' % self.config.version)
for modname, info in self.env.modules.iteritems(): #for modname, info in self.env.modules.iteritems():
f.write('%s mod %s\n' % (modname, self.get_target_uri(info[0]))) # f.write('%s mod %s\n' % (modname, self.get_target_uri(info[0])))
for refname, (docname, desctype) in self.env.descrefs.iteritems(): #for refname, (docname, desctype) in self.env.descrefs.iteritems():
f.write('%s %s %s\n' % (refname, desctype, # f.write('%s %s %s\n' % (refname, desctype,
self.get_target_uri(docname))) # self.get_target_uri(docname)))
finally: finally:
f.close() f.close()
self.info('done') self.info('done')

View File

@@ -204,7 +204,10 @@ class DescDirective(Directive):
pass pass
def run(self): 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.env = self.state.document.settings.env
self.indexnode = addnodes.index(entries=[]) self.indexnode = addnodes.index(entries=[])
@@ -366,7 +369,6 @@ class DefaultDomain(Directive):
def run(self): def run(self):
env = self.state.document.settings.env env = self.state.document.settings.env
domain_name = arguments[0] domain_name = arguments[0]
# XXX won't work
env.default_domain = env.domains.get(domain_name) env.default_domain = env.domains.get(domain_name)

View File

@@ -103,75 +103,6 @@ class TocTree(Directive):
return ret 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): class Author(Directive):
""" """
Directive to give the name of the author of the current document 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('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('sectionauthor', directive_dwim(Author))
directives.register_directive('moduleauthor', directive_dwim(Author)) directives.register_directive('moduleauthor', directive_dwim(Author))
directives.register_directive('program', directive_dwim(Program)) directives.register_directive('program', directive_dwim(Program))

View File

@@ -14,11 +14,13 @@ import re
import string import string
from docutils import nodes from docutils import nodes
from docutils.parsers.rst import directives
from sphinx import addnodes from sphinx import addnodes
from sphinx.roles import XRefRole from sphinx.roles import XRefRole
from sphinx.directives import DescDirective from sphinx.directives import DescDirective
from sphinx.util import make_refnode from sphinx.util import make_refnode
from sphinx.util.compat import Directive
class Domain(object): class Domain(object):
@@ -28,28 +30,28 @@ class Domain(object):
label = '' label = ''
# data value for a fresh environment # data value for a fresh environment
initial_data = { initial_data = {}
}
# data version # data version
data_version = 0 data_version = 0
def __init__(self, env): def __init__(self, env):
self.env = env self.env = env
self.data = env.domaindata.get(self.name, self.initial_data) if self.name not in env.domaindata:
if 'version' in self.data and self.data['version'] < self.data_version: new_data = self.initial_data.copy()
raise IOError('data of %r domain out of date' % self.label) 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._role_cache = {}
self._directive_cache = {} self._directive_cache = {}
def __getstate__(self): def clear_doc(self, docname):
# can't pickle the adapter caches """
state = self.__dict__.copy() Remove traces of a document in the domain-specific inventories.
state['_role_cache'] = {} """
state['_directive_cache'] = {} pass
return state
#def clear_doc
def role(self, name): def role(self, name):
""" """
@@ -212,7 +214,15 @@ class PythonDesc(DescDirective):
signode['ids'].append(fullname) signode['ids'].append(fullname)
signode['first'] = (not self.names) signode['first'] = (not self.names)
self.state.document.note_explicit_target(signode) 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) indextext = self.get_index_text(modname, name_cls)
if indextext: if indextext:
@@ -352,6 +362,75 @@ class ClassmemberDesc(PythonDesc):
self.clsname_set = True 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): class PyXRefRole(XRefRole):
def process_link(self, env, pnode, has_explicit_title, title, target): def process_link(self, env, pnode, has_explicit_title, title, target):
pnode['modname'] = env.currmodule pnode['modname'] = env.currmodule
@@ -387,6 +466,8 @@ class PythonDomain(Domain):
'classmethod': ClassmemberDesc, 'classmethod': ClassmemberDesc,
'staticmethod': ClassmemberDesc, 'staticmethod': ClassmemberDesc,
'attribute': ClassmemberDesc, 'attribute': ClassmemberDesc,
'module': PyModule,
'currentmodule': PyCurrentModule,
} }
roles = { roles = {
'data': PyXRefRole(), 'data': PyXRefRole(),
@@ -399,6 +480,18 @@ class PythonDomain(Domain):
'mod': PyXRefRole(), 'mod': PyXRefRole(),
'obj': 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): def find_desc(self, env, modname, classname, name, type, searchorder=0):
""" """
@@ -412,41 +505,43 @@ class PythonDomain(Domain):
if not name: if not name:
return None, None return None, None
objects = self.data['objects']
newname = None newname = None
if searchorder == 1: if searchorder == 1:
if modname and classname and \ if modname and classname and \
modname + '.' + classname + '.' + name in env.descrefs: modname + '.' + classname + '.' + name in objects:
newname = modname + '.' + classname + '.' + name newname = modname + '.' + classname + '.' + name
elif modname and modname + '.' + name in env.descrefs: elif modname and modname + '.' + name in objects:
newname = modname + '.' + name newname = modname + '.' + name
elif name in env.descrefs: elif name in objects:
newname = name newname = name
else: else:
if name in env.descrefs: if name in objects:
newname = name newname = name
elif modname and modname + '.' + name in env.descrefs: elif modname and modname + '.' + name in objects:
newname = modname + '.' + name newname = modname + '.' + name
elif modname and classname and \ elif modname and classname and \
modname + '.' + classname + '.' + name in env.descrefs: modname + '.' + classname + '.' + name in objects:
newname = modname + '.' + classname + '.' + name newname = modname + '.' + classname + '.' + name
# special case: builtin exceptions have module "exceptions" set # special case: builtin exceptions have module "exceptions" set
elif type == 'exc' and '.' not in name and \ elif type == 'exc' and '.' not in name and \
'exceptions.' + name in env.descrefs: 'exceptions.' + name in objects:
newname = 'exceptions.' + name newname = 'exceptions.' + name
# special case: object methods # special case: object methods
elif type in ('func', 'meth') and '.' not in name and \ elif type in ('func', 'meth') and '.' not in name and \
'object.' + name in env.descrefs: 'object.' + name in objects:
newname = 'object.' + name newname = 'object.' + name
if newname is None: if newname is None:
return None, None return None, None
return newname, env.descrefs[newname] return newname, objects[newname]
def resolve_xref(self, env, fromdocname, builder, def resolve_xref(self, env, fromdocname, builder,
typ, target, node, contnode): typ, target, node, contnode):
if (typ == 'mod' or if (typ == 'mod' or
typ == 'obj' and target in env.modules): typ == 'obj' and target in self.data['modules']):
docname, synopsis, platform, deprecated = \ docname, synopsis, platform, deprecated = \
env.modules.get(target, ('','','', '')) self.data['modules'].get(target, ('','','', ''))
if not docname: if not docname:
return None return None
elif docname == fromdocname: elif docname == fromdocname:
@@ -459,7 +554,6 @@ class PythonDomain(Domain):
return make_refnode(builder, fromdocname, docname, return make_refnode(builder, fromdocname, docname,
'module-' + target, contnode, title) 'module-' + target, contnode, title)
else: else:
# "descrefs"
modname = node['modname'] modname = node['modname']
clsname = node['classname'] clsname = node['classname']
searchorder = node.hasattr('refspecific') and 1 or 0 searchorder = node.hasattr('refspecific') and 1 or 0
@@ -589,7 +683,14 @@ class CDesc(DescDirective):
signode['ids'].append(name) signode['ids'].append(name)
signode['first'] = (not self.names) signode['first'] = (not self.names)
self.state.document.note_explicit_target(signode) 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) indextext = self.get_index_text(name)
if indextext: if indextext:
@@ -614,15 +715,22 @@ class CDomain(Domain):
'data': XRefRole(), 'data': XRefRole(),
'type': 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, def resolve_xref(self, env, fromdocname, builder,
typ, target, node, contnode): typ, target, node, contnode):
# strip pointer asterisk # strip pointer asterisk
target = target.rstrip(' *') target = target.rstrip(' *')
# XXX descrefs if target not in self.data:
if target not in env.descrefs:
return None return None
desc = env.descrefs[target] desc = self.data[target]
return make_refnode(builder, fromdocname, desc[0], contnode, target) return make_refnode(builder, fromdocname, desc[0], contnode, target)

View File

@@ -297,13 +297,10 @@ class BuildEnvironment:
self.glob_toctrees = set() # docnames that have :glob: toctrees self.glob_toctrees = set() # docnames that have :glob: toctrees
self.numbered_toctrees = set() # docnames that have :numbered: toctrees self.numbered_toctrees = set() # docnames that have :numbered: toctrees
# domain-specific inventories # domain-specific inventories, here to be pickled
self.domaindata = {} # domainname -> object self.domaindata = {} # domainname -> domain-specific object
# X-ref target inventory # X-ref target inventory
self.descrefs = {} # fullname -> docname, desctype
self.modules = {} # modname -> docname, synopsis,
# platform, deprecated
self.labels = {} # labelname -> docname, labelid, sectionname self.labels = {} # labelname -> docname, labelid, sectionname
self.anonlabels = {} # labelname -> docname, labelid self.anonlabels = {} # labelname -> docname, labelid
self.progoptions = {} # (program, name) -> docname, labelid self.progoptions = {} # (program, name) -> docname, labelid
@@ -371,12 +368,6 @@ class BuildEnvironment:
fnset.discard(docname) fnset.discard(docname)
if not fnset: if not fnset:
del self.files_to_rebuild[subfn] 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(): for labelname, (fn, _, _) in self.labels.items():
if fn == docname: if fn == docname:
del self.labels[labelname] del self.labels[labelname]
@@ -390,6 +381,10 @@ class BuildEnvironment:
new = [change for change in changes if change[1] != docname] new = [change for change in changes if change[1] != docname]
changes[:] = new 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): def doc2path(self, docname, base=True, suffix=None):
""" """
Return the filename for the document name. Return the filename for the document name.
@@ -1001,18 +996,6 @@ class BuildEnvironment:
# ------- # -------
# these are called from docutils directives and therefore use self.docname # 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): def note_progoption(self, optname, labelid):
self.progoptions[self.currprogram, optname] = (self.docname, labelid) self.progoptions[self.currprogram, optname] = (self.docname, labelid)

View File

@@ -79,6 +79,7 @@ class CoverageBuilder(Builder):
def build_c_coverage(self): def build_c_coverage(self):
# Fetch all the info from the header files # Fetch all the info from the header files
c_objects = self.env.domains['c'].data['objects']
for filename in self.c_sourcefiles: for filename in self.c_sourcefiles:
undoc = [] undoc = []
f = open(filename, 'r') f = open(filename, 'r')
@@ -88,7 +89,7 @@ class CoverageBuilder(Builder):
match = regex.match(line) match = regex.match(line)
if match: if match:
name = match.groups()[0] 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, ()): for exp in self.c_ignorexps.get(key, ()):
if exp.match(name): if exp.match(name):
break break
@@ -116,7 +117,10 @@ class CoverageBuilder(Builder):
op.close() op.close()
def build_py_coverage(self): 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 ignore = False
for exp in self.mod_ignorexps: for exp in self.mod_ignorexps:
if exp.match(mod_name): if exp.match(mod_name):
@@ -151,7 +155,7 @@ class CoverageBuilder(Builder):
full_name = '%s.%s' % (mod_name, name) full_name = '%s.%s' % (mod_name, name)
if inspect.isfunction(obj): if inspect.isfunction(obj):
if full_name not in self.env.descrefs: if full_name not in objects:
for exp in self.fun_ignorexps: for exp in self.fun_ignorexps:
if exp.match(name): if exp.match(name):
break break
@@ -162,7 +166,7 @@ class CoverageBuilder(Builder):
if exp.match(name): if exp.match(name):
break break
else: else:
if full_name not in self.env.descrefs: if full_name not in objects:
# not documented at all # not documented at all
classes[name] = [] classes[name] = []
continue continue
@@ -176,7 +180,7 @@ class CoverageBuilder(Builder):
continue continue
full_attr_name = '%s.%s' % (full_name, attr_name) 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) attrs.append(attr_name)
if attrs: if attrs:

View File

@@ -149,6 +149,8 @@ class IndexBuilder(object):
def get_modules(self, fn2index): def get_modules(self, fn2index):
rv = {} rv = {}
# XXX implement search capability
return rv
for name, (doc, _, _, _) in self.env.modules.iteritems(): for name, (doc, _, _, _) in self.env.modules.iteritems():
if doc in fn2index: if doc in fn2index:
rv[name] = fn2index[doc] rv[name] = fn2index[doc]
@@ -157,6 +159,8 @@ class IndexBuilder(object):
def get_descrefs(self, fn2index): def get_descrefs(self, fn2index):
rv = {} rv = {}
dt = self._desctypes dt = self._desctypes
# XXX implement search capability
return rv
for fullname, (doc, desctype) in self.env.descrefs.iteritems(): for fullname, (doc, desctype) in self.env.descrefs.iteritems():
if doc not in fn2index: if doc not in fn2index:
continue continue

View File

@@ -20,9 +20,8 @@ warnings = []
def setup_module(): def setup_module():
global app, env global app, env
app = TestApp(srcdir='(temp)') app = TestApp(srcdir='(temp)', freshenv=True)
env = app.env env = app.env
#env = BuildEnvironment(app.srcdir, app.doctreedir, app.config)
env.set_warnfunc(lambda *args: warnings.append(args)) env.set_warnfunc(lambda *args: warnings.append(args))
def teardown_module(): def teardown_module():
@@ -93,7 +92,7 @@ def test_second_update():
assert 'autodoc' not in env.found_docs assert 'autodoc' not in env.found_docs
def test_object_inventory(): def test_object_inventory():
refs = env.descrefs refs = env.domains['py'].data['objects']
assert 'func_without_module' in refs assert 'func_without_module' in refs
assert refs['func_without_module'] == ('desc', 'function') assert refs['func_without_module'] == ('desc', 'function')
@@ -110,5 +109,5 @@ def test_object_inventory():
assert 'func_in_module' not in refs assert 'func_in_module' not in refs
assert 'func_noindex' not in refs assert 'func_noindex' not in refs
assert 'mod' in env.modules assert env.domains['py'].data['modules']['mod'] == \
assert env.modules['mod'] == ('desc', 'Module synopsis.', 'UNIX', False) ('desc', 'Module synopsis.', 'UNIX', False)