summaryrefslogtreecommitdiff
path: root/sphinx/domains
diff options
context:
space:
mode:
authorGeorg Brandl <georg@python.org>2010-02-28 14:45:43 +0100
committerGeorg Brandl <georg@python.org>2010-02-28 14:45:43 +0100
commit4d77d3d6fb8859f859ecd46a7aa3a03923b9cfa2 (patch)
treea7053d260603179f7c9447a43a82243ab70112e3 /sphinx/domains
parent06fd1678117bc2a1c61b4d2de130d2480160e91d (diff)
downloadsphinx-4d77d3d6fb8859f859ecd46a7aa3a03923b9cfa2.tar.gz
Change domain-index API: introduce a class.
Diffstat (limited to 'sphinx/domains')
-rw-r--r--sphinx/domains/__init__.py137
-rw-r--r--sphinx/domains/python.py141
2 files changed, 144 insertions, 134 deletions
diff --git a/sphinx/domains/__init__.py b/sphinx/domains/__init__.py
index a03a74db..f1e6e8af 100644
--- a/sphinx/domains/__init__.py
+++ b/sphinx/domains/__init__.py
@@ -10,6 +10,8 @@
:license: BSD, see LICENSE for details.
"""
+from sphinx.errors import SphinxError
+
class ObjType(object):
"""
@@ -23,7 +25,7 @@ class ObjType(object):
- *roles*: all the roles that can refer to an object of this type
- *attrs*: object attributes -- currently only "searchprio" is known,
which defines the object's priority in the full-text search index,
- see `Domain.get_objects`.
+ see :meth:`Domain.get_objects()`.
"""
known_attrs = {
@@ -37,6 +39,63 @@ class ObjType(object):
self.attrs.update(attrs)
+class Index(object):
+ """
+ An Index is the description for a domain-specific index. To add an index to
+ a domain, subclass Index, overriding the three name attributes:
+
+ * `name` is an identifier used for generating file names.
+ * `localname` is the section title for the index.
+ * `shortname` is a short name for the index, for use in the relation bar in
+ HTML output. Can be empty to disable entries in the relation bar.
+
+ and providing a :meth:`generate()` method. Then, add the index class to
+ your domain's `indices` list. Extensions can add indices to existing
+ domains using :meth:`~sphinx.application.Sphinx.add_index_to_domain()`.
+ """
+
+ name = None
+ localname = None
+ shortname = None
+
+ def __init__(self, domain):
+ if self.name is None or self.localname is None:
+ raise SphinxError('Index subclass %s has no valid name or localname'
+ % self.__class__.__name__)
+ self.domain = domain
+
+ def generate(self, docnames=None):
+ """
+ Return entries for the index given by *name*. If *docnames* is given,
+ restrict to entries referring to these docnames.
+
+ The return value is a tuple of ``(content, collapse)``, where *collapse*
+ is a boolean that determines if sub-entries should start collapsed (for
+ output formats that support collapsing sub-entries).
+
+ *content* is a sequence of ``(letter, entries)`` tuples, where *letter*
+ is the "heading" for the given *entries*, usually the starting letter.
+
+ *entries* is a sequence of single entries, where a single entry is a
+ sequence ``[name, subtype, docname, anchor, extra, qualifier, descr]``.
+ The items in this sequence have the following meaning:
+
+ - `name` -- the name of the index entry to be displayed
+ - `subtype` -- sub-entry related type:
+ 0 -- normal entry
+ 1 -- entry with sub-entries
+ 2 -- sub-entry
+ - `docname` -- docname where the entry is located
+ - `anchor` -- anchor for the entry within `docname`
+ - `extra` -- extra info for the entry
+ - `qualifier` -- qualifier for the description
+ - `descr` -- description for the entry
+
+ Qualifier and description are not rendered e.g. in LaTeX output.
+ """
+ return []
+
+
class Domain(object):
"""
A Domain is meant to be a group of "object" description directives for
@@ -45,9 +104,9 @@ class Domain(object):
of a templating language, Sphinx roles and directives, etc.
Each domain has a separate storage for information about existing objects
- and how to reference them in `data`, which must be a dictionary. It also
- must implement several functions that expose the object information in a
- uniform way to parts of Sphinx that allow the user to reference or search
+ and how to reference them in `self.data`, which must be a dictionary. It
+ also must implement several functions that expose the object information in
+ a uniform way to parts of Sphinx that allow the user to reference or search
for objects in a domain-agnostic way.
About `self.data`: since all object and cross-referencing information is
@@ -56,8 +115,8 @@ class Domain(object):
build process starts, every active domain is instantiated and given the
environment object; the `domaindata` dict must then either be nonexistent or
a dictionary whose 'version' key is equal to the domain class'
- `data_version` attribute. Otherwise, `IOError` is raised and the pickled
- environment is discarded.
+ :attr:`data_version` attribute. Otherwise, `IOError` is raised and the
+ pickled environment is discarded.
"""
#: domain name: should be short, but unique
@@ -70,12 +129,12 @@ class Domain(object):
directives = {}
#: role name -> role callable
roles = {}
- #: (index identifier, localized index name, localized short name) tuples
+ #: a list of Index subclasses
indices = []
#: data value for a fresh environment
initial_data = {}
- #: data version
+ #: data version, bump this when the format of `self.data` changes
data_version = 0
def __init__(self, env):
@@ -153,8 +212,8 @@ class Domain(object):
then given to the 'missing-reference' event, and if that yields no
resolution, replaced by *contnode*.
- The method can also raise `sphinx.environment.NoUri` to suppress the
- 'missing-reference' event being emitted.
+ The method can also raise :exc:`sphinx.environment.NoUri` to suppress
+ the 'missing-reference' event being emitted.
"""
pass
@@ -170,63 +229,13 @@ class Domain(object):
* `priority` -- how "important" the object is (determines placement
in search results)
- 1: default priority (placed before full-text matches)
- 0: object is important (placed before default-priority objects)
- 2: object is unimportant (placed after full-text matches)
- -1: object should not show up in search at all
+ - 1: default priority (placed before full-text matches)
+ - 0: object is important (placed before default-priority objects)
+ - 2: object is unimportant (placed after full-text matches)
+ - -1: object should not show up in search at all
"""
return []
- def has_index_entries(self, name, docnames=None):
- """
- Return True if there are entries for the index given by *name*. If
- *docnames* is given, restrict to entries referring to these docnames.
-
- Do not overwrite this method, add a method ``has_<name>_entries(self,
- docnames=None)`` method for every index.
- """
- func = getattr(self, 'has_%s_entries' % name, None)
- if not func:
- return bool(self.get_index(name, docnames))
- return func(docnames)
-
- def get_index(self, name, docnames=None):
- """
- Return entries for the index given by *name*. If *docnames* is given,
- restrict to entries referring to these docnames.
-
- The return value is a tuple of ``(content, collapse)``, where *collapse*
- is a boolean that determines if sub-entries should start collapsed (for
- output formats that support collapsing sub-entries).
-
- *content* is a sequence of ``(letter, entries)`` tuples, where *letter*
- is the "heading" for the given *entries*, usually the starting letter.
-
- *entries* is a sequence of single entries, where a single entry is a
- sequence ``[name, subtype, docname, anchor, extra, qualifier, descr]``.
- The items in this sequence have the following meaning:
-
- - `name` -- the name of the index entry to be displayed
- - `subtype` -- sub-entry related type:
- 0 -- normal entry
- 1 -- entry with sub-entries
- 2 -- sub-entry
- - `docname` -- docname where the entry is located
- - `anchor` -- anchor for the entry within `docname`
- - `extra` -- extra info for the entry
- - `qualifier` -- qualifier for the description
- - `descr` -- description for the entry
-
- Qualifier and description are not rendered e.g. in LaTeX output.
-
- Do not overwrite this method, add a method ``get_<name>_index(self,
- docnames=None)`` method for every index.
- """
- func = getattr(self, 'get_%s_index' % name, None)
- if not func:
- return []
- return func(docnames)
-
from sphinx.domains.c import CDomain
from sphinx.domains.std import StandardDomain
diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py
index af1392a6..af8b2eda 100644
--- a/sphinx/domains/python.py
+++ b/sphinx/domains/python.py
@@ -17,7 +17,7 @@ from docutils.parsers.rst import directives
from sphinx import addnodes
from sphinx.roles import XRefRole
from sphinx.locale import l_, _
-from sphinx.domains import Domain, ObjType
+from sphinx.domains import Domain, ObjType, Index
from sphinx.directives import ObjectDescription
from sphinx.util.nodes import make_refnode
from sphinx.util.compat import Directive
@@ -419,6 +419,75 @@ class PyXRefRole(XRefRole):
return title, target
+class PythonModuleIndex(Index):
+ """
+ Index subclass to provide the Python module index.
+ """
+
+ name = 'modindex'
+ localname = l_('Python Module Index')
+ shortname = l_('modules')
+
+ def generate(self, docnames=None):
+ content = {}
+ # list of prefixes to ignore
+ ignores = self.domain.env.config['modindex_common_prefix']
+ ignores = sorted(ignores, key=len, reverse=True)
+ # list of all modules, sorted by module name
+ modules = sorted(self.domain.data['modules'].iteritems(),
+ 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
+ entries[-1][1] = 1
+ elif not prev_modname.startswith(package):
+ # submodule without parent in list, add dummy entry
+ entries.append([stripped + package, 1, '', '', '', '', ''])
+ subtype = 2
+ else:
+ num_toplevels += 1
+ subtype = 0
+
+ qualifier = deprecated and _('Deprecated') or ''
+ entries.append([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
+ content = sorted(content.iteritems())
+
+ return content, collapse
+
+
class PythonDomain(Domain):
"""Python language domain."""
name = 'py'
@@ -463,7 +532,7 @@ class PythonDomain(Domain):
'modules': {}, # modname -> docname, synopsis, platform, deprecated
}
indices = [
- ('modindex', l_('Python Module Index'), l_('modules')),
+ PythonModuleIndex,
]
def clear_doc(self, docname):
@@ -548,71 +617,3 @@ class PythonDomain(Domain):
yield (modname, 'module', info[0], 'module-' + modname, 0)
for refname, (docname, type) in self.data['objects'].iteritems():
yield (refname, type, docname, refname, 1)
-
- def has_modindex_entries(self, docnames=None):
- if not docnames:
- return bool(self.data['modules'])
- else:
- for modname, info in self.data['modules'].iteritems():
- if info[0] in docnames:
- return True
- return False
-
- def get_modindex_index(self, docnames=None):
- content = {}
- # list of prefixes to ignore
- ignores = self.env.config['modindex_common_prefix']
- ignores = sorted(ignores, key=len, reverse=True)
- # list of all modules, sorted by module name
- modules = sorted(self.data['modules'].iteritems(),
- 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
- entries[-1][1] = 1
- elif not prev_modname.startswith(package):
- # submodule without parent in list, add dummy entry
- entries.append([stripped + package, 1, '', '', '', '', ''])
- subtype = 2
- else:
- num_toplevels += 1
- subtype = 0
-
- qualifier = deprecated and _('Deprecated') or ''
- entries.append([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
- content = sorted(content.iteritems())
-
- return content, collapse