Merge pull request #6537 from tk0miya/refactor_domain

refactor: Add data accessors to CDomain, ChangesetDomain and JavaScriptDomain
This commit is contained in:
Takeshi KOMIYA 2019-06-30 15:34:40 +09:00 committed by GitHub
commit cb7e2a9dea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 93 additions and 57 deletions

View File

@ -11,6 +11,7 @@
import re import re
import string import string
from typing import Any, Dict, Iterator, List, Tuple from typing import Any, Dict, Iterator, List, Tuple
from typing import cast
from docutils import nodes from docutils import nodes
from docutils.nodes import Element from docutils.nodes import Element
@ -22,12 +23,15 @@ from sphinx.builders import Builder
from sphinx.directives import ObjectDescription from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, ObjType from sphinx.domains import Domain, ObjType
from sphinx.environment import BuildEnvironment from sphinx.environment import BuildEnvironment
from sphinx.locale import _ from sphinx.locale import _, __
from sphinx.roles import XRefRole from sphinx.roles import XRefRole
from sphinx.util import logging
from sphinx.util.docfields import Field, TypedField from sphinx.util.docfields import Field, TypedField
from sphinx.util.nodes import make_refnode from sphinx.util.nodes import make_refnode
logger = logging.getLogger(__name__)
# RE to split at word boundaries # RE to split at word boundaries
wsplit_re = re.compile(r'(\W+)') wsplit_re = re.compile(r'(\W+)')
@ -201,13 +205,9 @@ class CObject(ObjectDescription):
signode['ids'].append(targetname) signode['ids'].append(targetname)
signode['first'] = (not self.names) signode['first'] = (not self.names)
self.state.document.note_explicit_target(signode) self.state.document.note_explicit_target(signode)
inv = self.env.domaindata['c']['objects']
if name in inv: domain = cast(CDomain, self.env.get_domain('c'))
self.state_machine.reporter.warning( domain.note_object(name, self.objtype)
'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) indextext = self.get_index_text(name)
if indextext: if indextext:
@ -271,10 +271,22 @@ class CDomain(Domain):
'objects': {}, # fullname -> docname, objtype 'objects': {}, # fullname -> docname, objtype
} # type: Dict[str, Dict[str, Tuple[str, Any]]] } # type: Dict[str, Dict[str, Tuple[str, Any]]]
@property
def objects(self) -> Dict[str, Tuple[str, str]]:
return self.data.setdefault('objects', {}) # fullname -> docname, objtype
def note_object(self, name: str, objtype: str, location: Any = None) -> None:
if name in self.objects:
docname = self.objects[name][0]
logger.warning(__('duplicate C object description of %s, '
'other instance in %s, use :noindex: for one of them'),
name, docname, location=location)
self.objects[name] = (self.env.docname, objtype)
def clear_doc(self, docname: str) -> None: def clear_doc(self, docname: str) -> None:
for fullname, (fn, _l) in list(self.data['objects'].items()): for fullname, (fn, _l) in list(self.objects.items()):
if fn == docname: if fn == docname:
del self.data['objects'][fullname] del self.objects[fullname]
def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None: def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
# XXX check duplicates # XXX check duplicates
@ -290,9 +302,9 @@ class CDomain(Domain):
# becase TypedField can generate xrefs # becase TypedField can generate xrefs
if target in CObject.stopwords: if target in CObject.stopwords:
return contnode return contnode
if target not in self.data['objects']: if target not in self.objects:
return None return None
obj = self.data['objects'][target] obj = self.objects[target]
return make_refnode(builder, fromdocname, obj[0], 'c.' + target, return make_refnode(builder, fromdocname, obj[0], 'c.' + target,
contnode, target) contnode, target)
@ -301,15 +313,15 @@ class CDomain(Domain):
) -> List[Tuple[str, Element]]: ) -> List[Tuple[str, Element]]:
# strip pointer asterisk # strip pointer asterisk
target = target.rstrip(' *') target = target.rstrip(' *')
if target not in self.data['objects']: if target not in self.objects:
return [] return []
obj = self.data['objects'][target] obj = self.objects[target]
return [('c:' + self.role_for_objtype(obj[1]), return [('c:' + self.role_for_objtype(obj[1]),
make_refnode(builder, fromdocname, obj[0], 'c.' + target, make_refnode(builder, fromdocname, obj[0], 'c.' + target,
contnode, target))] contnode, target))]
def get_objects(self) -> Iterator[Tuple[str, str, str, str, str, int]]: def get_objects(self) -> Iterator[Tuple[str, str, str, str, str, int]]:
for refname, (docname, type) in list(self.data['objects'].items()): for refname, (docname, type) in list(self.objects.items()):
yield (refname, refname, type, docname, 'c.' + refname, 1) yield (refname, refname, type, docname, 'c.' + refname, 1)

View File

@ -117,22 +117,9 @@ class ChangeSetDomain(Domain):
'changes': {}, # version -> list of ChangeSet 'changes': {}, # version -> list of ChangeSet
} # type: Dict } # type: Dict
def clear_doc(self, docname: str) -> None: @property
for version, changes in self.data['changes'].items(): def changesets(self) -> Dict[str, List[ChangeSet]]:
for changeset in changes[:]: return self.data.setdefault('changes', {}) # version -> list of ChangeSet
if changeset.docname == docname:
changes.remove(changeset)
def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
# XXX duplicates?
for version, otherchanges in otherdata['changes'].items():
changes = self.data['changes'].setdefault(version, [])
for changeset in otherchanges:
if changeset.docname in docnames:
changes.append(changeset)
def process_doc(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA
pass # nothing to do here. All changesets are registered on calling directive.
def note_changeset(self, node: addnodes.versionmodified) -> None: def note_changeset(self, node: addnodes.versionmodified) -> None:
version = node['version'] version = node['version']
@ -140,10 +127,27 @@ class ChangeSetDomain(Domain):
objname = self.env.temp_data.get('object') objname = self.env.temp_data.get('object')
changeset = ChangeSet(node['type'], self.env.docname, node.line, changeset = ChangeSet(node['type'], self.env.docname, node.line,
module, objname, node.astext()) module, objname, node.astext())
self.data['changes'].setdefault(version, []).append(changeset) self.changesets.setdefault(version, []).append(changeset)
def clear_doc(self, docname: str) -> None:
for version, changes in self.changesets.items():
for changeset in changes[:]:
if changeset.docname == docname:
changes.remove(changeset)
def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
# XXX duplicates?
for version, otherchanges in otherdata['changes'].items():
changes = self.changesets.setdefault(version, [])
for changeset in otherchanges:
if changeset.docname in docnames:
changes.append(changeset)
def process_doc(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA
pass # nothing to do here. All changesets are registered on calling directive.
def get_changesets_for(self, version: str) -> List[ChangeSet]: def get_changesets_for(self, version: str) -> List[ChangeSet]:
return self.data['changes'].get(version, []) return self.changesets.get(version, [])
def setup(app: "Sphinx") -> Dict[str, Any]: def setup(app: "Sphinx") -> Dict[str, Any]:

View File

@ -9,6 +9,7 @@
""" """
from typing import Any, Dict, Iterator, List, Tuple from typing import Any, Dict, Iterator, List, Tuple
from typing import cast
from docutils import nodes from docutils import nodes
from docutils.nodes import Element, Node from docutils.nodes import Element, Node
@ -22,13 +23,17 @@ from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, ObjType from sphinx.domains import Domain, ObjType
from sphinx.domains.python import _pseudo_parse_arglist from sphinx.domains.python import _pseudo_parse_arglist
from sphinx.environment import BuildEnvironment from sphinx.environment import BuildEnvironment
from sphinx.locale import _ from sphinx.locale import _, __
from sphinx.roles import XRefRole from sphinx.roles import XRefRole
from sphinx.util import logging
from sphinx.util.docfields import Field, GroupedField, TypedField from sphinx.util.docfields import Field, GroupedField, TypedField
from sphinx.util.docutils import SphinxDirective from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import make_refnode from sphinx.util.nodes import make_refnode
logger = logging.getLogger(__name__)
class JSObject(ObjectDescription): class JSObject(ObjectDescription):
""" """
Description of a JavaScript object. Description of a JavaScript object.
@ -106,14 +111,10 @@ class JSObject(ObjectDescription):
signode['ids'].append(fullname.replace('$', '_S_')) signode['ids'].append(fullname.replace('$', '_S_'))
signode['first'] = not self.names signode['first'] = not self.names
self.state.document.note_explicit_target(signode) self.state.document.note_explicit_target(signode)
objects = self.env.domaindata['js']['objects']
if fullname in objects: domain = cast(JavaScriptDomain, self.env.get_domain('js'))
self.state_machine.reporter.warning( domain.note_object(fullname, self.objtype,
'duplicate object description of %s, ' % fullname + location=(self.env.docname, self.lineno))
'other instance in ' +
self.env.doc2path(objects[fullname][0]),
line=self.lineno)
objects[fullname] = self.env.docname, self.objtype
indextext = self.get_index_text(mod_name, name_obj) indextext = self.get_index_text(mod_name, name_obj)
if indextext: if indextext:
@ -248,10 +249,13 @@ class JSModule(SphinxDirective):
noindex = 'noindex' in self.options noindex = 'noindex' in self.options
ret = [] # type: List[Node] ret = [] # type: List[Node]
if not noindex: if not noindex:
self.env.domaindata['js']['modules'][mod_name] = self.env.docname domain = cast(JavaScriptDomain, self.env.get_domain('js'))
domain.note_module(mod_name)
# Make a duplicate entry in 'objects' to facilitate searching for # Make a duplicate entry in 'objects' to facilitate searching for
# the module in JavaScriptDomain.find_obj() # the module in JavaScriptDomain.find_obj()
self.env.domaindata['js']['objects'][mod_name] = (self.env.docname, 'module') domain.note_object(mod_name, 'module', location=(self.env.docname, self.lineno))
targetnode = nodes.target('', '', ids=['module-' + mod_name], targetnode = nodes.target('', '', ids=['module-' + mod_name],
ismod=True) ismod=True)
self.state.document.note_explicit_target(targetnode) self.state.document.note_explicit_target(targetnode)
@ -314,31 +318,48 @@ class JavaScriptDomain(Domain):
} }
initial_data = { initial_data = {
'objects': {}, # fullname -> docname, objtype 'objects': {}, # fullname -> docname, objtype
'modules': {}, # mod_name -> docname 'modules': {}, # modname -> docname
} # type: Dict[str, Dict[str, Tuple[str, str]]] } # type: Dict[str, Dict[str, Tuple[str, str]]]
@property
def objects(self) -> Dict[str, Tuple[str, str]]:
return self.data.setdefault('objects', {}) # fullname -> docname, objtype
def note_object(self, fullname: str, objtype: str, location: Any = None) -> None:
if fullname in self.objects:
docname = self.objects[fullname][0]
logger.warning(__('duplicate object description of %s, other instance in %s'),
fullname, docname, location=location)
self.objects[fullname] = (self.env.docname, objtype)
@property
def modules(self) -> Dict[str, str]:
return self.data.setdefault('modules', {}) # modname -> docname
def note_module(self, modname: str) -> None:
self.modules[modname] = self.env.docname
def clear_doc(self, docname: str) -> None: def clear_doc(self, docname: str) -> None:
for fullname, (pkg_docname, _l) in list(self.data['objects'].items()): for fullname, (pkg_docname, _l) in list(self.objects.items()):
if pkg_docname == docname: if pkg_docname == docname:
del self.data['objects'][fullname] del self.objects[fullname]
for mod_name, pkg_docname in list(self.data['modules'].items()): for modname, pkg_docname in list(self.modules.items()):
if pkg_docname == docname: if pkg_docname == docname:
del self.data['modules'][mod_name] del self.modules[modname]
def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None: def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
# XXX check duplicates # XXX check duplicates
for fullname, (fn, objtype) in otherdata['objects'].items(): for fullname, (fn, objtype) in otherdata['objects'].items():
if fn in docnames: if fn in docnames:
self.data['objects'][fullname] = (fn, objtype) self.objects[fullname] = (fn, objtype)
for mod_name, pkg_docname in otherdata['modules'].items(): for mod_name, pkg_docname in otherdata['modules'].items():
if pkg_docname in docnames: if pkg_docname in docnames:
self.data['modules'][mod_name] = pkg_docname self.modules[mod_name] = pkg_docname
def find_obj(self, env: BuildEnvironment, mod_name: str, prefix: str, name: str, def find_obj(self, env: BuildEnvironment, mod_name: str, prefix: str, name: str,
typ: str, searchorder: int = 0) -> Tuple[str, Tuple[str, str]]: typ: str, searchorder: int = 0) -> Tuple[str, Tuple[str, str]]:
if name[-2:] == '()': if name[-2:] == '()':
name = name[:-2] name = name[:-2]
objects = self.data['objects']
searches = [] searches = []
if mod_name and prefix: if mod_name and prefix:
@ -354,10 +375,10 @@ class JavaScriptDomain(Domain):
newname = None newname = None
for search_name in searches: for search_name in searches:
if search_name in objects: if search_name in self.objects:
newname = search_name newname = search_name
return newname, objects.get(newname) return newname, self.objects.get(newname)
def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder, def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
typ: str, target: str, node: pending_xref, contnode: Element typ: str, target: str, node: pending_xref, contnode: Element
@ -384,9 +405,8 @@ class JavaScriptDomain(Domain):
name.replace('$', '_S_'), contnode, name))] name.replace('$', '_S_'), contnode, name))]
def get_objects(self) -> Iterator[Tuple[str, str, str, str, str, int]]: def get_objects(self) -> Iterator[Tuple[str, str, str, str, str, int]]:
for refname, (docname, type) in list(self.data['objects'].items()): for refname, (docname, type) in list(self.objects.items()):
yield refname, refname, type, docname, \ yield refname, refname, type, docname, refname.replace('$', '_S_'), 1
refname.replace('$', '_S_'), 1
def get_full_qualified_name(self, node: Element) -> str: def get_full_qualified_name(self, node: Element) -> str:
modname = node.get('js:module') modname = node.get('js:module')