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 types
import posixpath
from os import path
from cStringIO import StringIO
from docutils import nodes

View File

@@ -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')

View File

@@ -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)

View File

@@ -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))

View File

@@ -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)

View File

@@ -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)

View File

@@ -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:

View File

@@ -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

View File

@@ -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)