text: """
      sphinx.domains
      ~~~~~~~~~~~~~~
  
      Support for domains, which are groupings of description directives
      and roles describing e.g. constructs of one programming language.
  
      :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
      :license: BSD, see LICENSE for details.
  """
  
  import copy
  from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterable, List, NamedTuple, Tuple,
                      Type, Union, cast)
  
  from docutils import nodes
  from docutils.nodes import Element, Node, system_message
  from docutils.parsers.rst.states import Inliner
  
  from sphinx.addnodes import pending_xref
  from sphinx.errors import SphinxError
  from sphinx.locale import _
  from sphinx.roles import XRefRole
  from sphinx.util.typing import RoleFunction
  
  if TYPE_CHECKING:
      from sphinx.builders import Builder
      from sphinx.environment import BuildEnvironment
  
  
  class ObjType:
      """
      An ObjType is the description for a type of object that a domain can
      document.  In the object_types attribute of Domain subclasses, object type
      names are mapped to instances of this class.
  
      Constructor arguments:
  
      - *lname*: localized name of the type (do not include domain name)
      - *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 :meth:`Domain.get_objects()`.
      """
  
      known_attrs = {
          'searchprio': 1,
      }
  
      def __init__(self, lname: str, *roles: Any, **attrs: Any) -> None:
          self.lname = lname
          self.roles = roles                      # type: Tuple
          self.attrs = self.known_attrs.copy()    # type: Dict
          self.attrs.update(attrs)
  
  
  class IndexEntry(NamedTuple):
      name: str
      subtype: int
      docname: str
      anchor: str
      extra: str
      qualifier: str
      descr: str
  
  
  class Index:
      """
      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.
        It is also used for a hyperlink target for the index. Therefore, users can
        refer the index page using ``ref`` role and a string which is combined
        domain name and ``name`` attribute (ex. ``:ref:`py-modindex```).
      * `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()`.
  
      .. versionchanged:: 3.0
  
         Index pages can be referred by domain name and index name via
         :rst:role:`ref` role.
      """
  
      name = None  # type: str
      localname = None  # type: str
      shortname = None  # type: str
  
      def __init__(self, domain: "Domain") -> None:
          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: Iterable[str] = None
                   ) -> Tuple[List[Tuple[str, List[IndexEntry]]], bool]:
          """Get entries for the index.
  
          If ``docnames`` is given, restrict to entries referring to these
          docnames.
  
          The return value is a tuple of ``(content, collapse)``:
  
          ``collapse``
            A boolean that determines if sub-entries should start collapsed (for
            output formats that support collapsing sub-entries).
  
          ``content``:
            A sequence of ``(letter, entries)`` tuples, where ``letter`` is the
            "heading" for the given ``entries``, usually the starting letter, and
            ``entries`` is a sequence of single entries. Each 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``
              The sub-entry related type. One of:
  
              ``0``
                A normal entry.
              ``1``
                An entry with sub-entries.
              ``2``
                A 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 for some output formats such
          as LaTeX.
          """
          raise NotImplementedError
  
  
  class Domain:
      """
      A Domain is meant to be a group of "object" description directives for
      objects of a similar nature, and corresponding roles to create references to
      them.  Examples would be Python modules, classes, functions etc., elements
      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 `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
      stored on a BuildEnvironment instance, the `domain.data` object is also
      stored in the `env.domaindata` dict under the key `domain.name`.  Before the
      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'
      :attr:`data_version` attribute.  Otherwise, `OSError` is raised and the
      pickled environment is discarded.
      """
  
      #: domain name: should be short, but unique
      name = ''
      #: domain label: longer, more descriptive (used in messages)
      label = ''
      #: type (usually directive) name -> ObjType instance
      object_types = {}       # type: Dict[str, ObjType]
      #: directive name -> directive class
      directives = {}         # type: Dict[str, Any]
      #: role name -> role callable
      roles = {}              # type: Dict[str, Union[RoleFunction, XRefRole]]
      #: a list of Index subclasses
      indices = []            # type: List[Type[Index]]
      #: role name -> a warning message if reference is missing
      dangling_warnings = {}  # type: Dict[str, str]
      #: node_class -> (enum_node_type, title_getter)
      enumerable_nodes = {}   # type: Dict[Type[Node], Tuple[str, Callable]]
  
      #: data value for a fresh environment
      initial_data = {}       # type: Dict
      #: data value
      data = None             # type: Dict
      #: data version, bump this when the format of `self.data` changes
      data_version = 0
  
      def __init__(self, env: "BuildEnvironment") -> None:
          self.env = env              # type: BuildEnvironment
          self._role_cache = {}       # type: Dict[str, Callable]
          self._directive_cache = {}  # type: Dict[str, Callable]
          self._role2type = {}        # type: Dict[str, List[str]]
          self._type2role = {}        # type: Dict[str, str]
  
          # convert class variables to instance one (to enhance through API)
          self.object_types = dict(self.object_types)
          self.directives = dict(self.directives)
          self.roles = dict(self.roles)
          self.indices = list(self.indices)
  
          if self.name not in env.domaindata:
              assert isinstance(self.initial_data, dict)
              new_data = copy.deepcopy(self.initial_data)
              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 OSError('data of %r domain out of date' % self.label)
          for name, obj in self.object_types.items():
              for rolename in obj.roles:
                  self._role2type.setdefault(rolename, []).append(name)
              self._type2role[name] = obj.roles[0] if obj.roles else ''
          self.objtypes_for_role = self._role2type.get    # type: Callable[[str], List[str]]
          self.role_for_objtype = self._type2role.get     # type: Callable[[str], str]
  
      def setup(self) -> None:
          """Set up domain object."""
          from sphinx.domains.std import StandardDomain
  
          # Add special hyperlink target for index pages (ex. py-modindex)
          std = cast(StandardDomain, self.env.get_domain('std'))
          for index in self.indices:
              if index.name and index.localname:
                  docname = "%s-%s" % (self.name, index.name)
                  std.note_hyperlink_target(docname, docname, '', index.localname)
  
      def add_object_type(self, name: str, objtype: ObjType) -> None:
          """Add an object type."""
          self.object_types[name] = objtype
          if objtype.roles:
              self._type2role[name] = objtype.roles[0]
          else:
              self._type2role[name] = ''
  
          for role in objtype.roles:
              self._role2type.setdefault(role, []).append(name)
  
      def role(self, name: str) -> RoleFunction:
          """Return a role adapter function that always gives the registered
          role its full name ('domain:name') as the first argument.
          """
          if name in self._role_cache:
              return self._role_cache[name]
          if name not in self.roles:
              return None
          fullname = '%s:%s' % (self.name, name)
  
          def role_adapter(typ: str, rawtext: str, text: str, lineno: int,
                           inliner: Inliner, options: Dict = {}, content: List[str] = []
                           ) -> Tuple[List[Node], List[system_message]]:
              return self.roles[name](fullname, rawtext, text, lineno,
                                      inliner, options, content)
          self._role_cache[name] = role_adapter
          return role_adapter
  
      def directive(self, name: str) -> Callable:
          """Return a directive adapter class that always gives the registered
          directive its full name ('domain:name') as ``self.name``.
          """
          if name in self._directive_cache:
              return self._directive_cache[name]
          if name not in self.directives:
              return None
          fullname = '%s:%s' % (self.name, name)
          BaseDirective = self.directives[name]
  
          class DirectiveAdapter(BaseDirective):  # type: ignore
              def run(self) -> List[Node]:
                  self.name = fullname
                  return super().run()
          self._directive_cache[name] = DirectiveAdapter
          return DirectiveAdapter
  
      # methods that should be overwritten
  
      def clear_doc(self, docname: str) -> None:
          """Remove traces of a document in the domain-specific inventories."""
          pass
  
      def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
          """Merge in data regarding *docnames* from a different domaindata
          inventory (coming from a subprocess in parallel builds).
          """
          raise NotImplementedError('merge_domaindata must be implemented in %s '
                                    'to be able to do parallel builds!' %
                                    self.__class__)
  
      def process_doc(self, env: "BuildEnvironment", docname: str,
                      document: nodes.document) -> None:
          """Process a document after it is read by the environment."""
          pass
  
      def check_consistency(self) -> None:
          """Do consistency checks (**experimental**)."""
          pass
  
      def process_field_xref(self, pnode: pending_xref) -> None:
          """Process a pending xref created in a doc field.
          For example, attach information about the current scope.
          """
          pass
  
      def resolve_xref(self, env: "BuildEnvironment", fromdocname: str, builder: "Builder",
                       typ: str, target: str, node: pending_xref, contnode: Element
                       ) -> Element:
          """Resolve the pending_xref *node* with the given *typ* and *target*.
  
          This method should return a new node, to replace the xref node,
          containing the *contnode* which is the markup content of the
          cross-reference.
  
          If no resolution can be found, None can be returned; the xref node will
          then given to the :event:`missing-reference` event, and if that yields no
          resolution, replaced by *contnode*.
  
          The method can also raise :exc:`sphinx.environment.NoUri` to suppress
          the :event:`missing-reference` event being emitted.
          """
          pass
  
      def resolve_any_xref(self, env: "BuildEnvironment", fromdocname: str, builder: "Builder",
                           target: str, node: pending_xref, contnode: Element
                           ) -> List[Tuple[str, Element]]:
          """Resolve the pending_xref *node* with the given *target*.
  
          The reference comes from an "any" or similar role, which means that we
          don't know the type.  Otherwise, the arguments are the same as for
          :meth:`resolve_xref`.
  
          The method must return a list (potentially empty) of tuples
          ``('domain:role', newnode)``, where ``'domain:role'`` is the name of a
          role that could have created the same reference, e.g. ``'py:func'``.
          ``newnode`` is what :meth:`resolve_xref` would return.
  
          .. versionadded:: 1.3
          """
          raise NotImplementedError
  
      def get_objects(self) -> Iterable[Tuple[str, str, str, str, str, int]]:
          """Return an iterable of "object descriptions".
  
          Object descriptions are tuples with six items:
  
          ``name``
            Fully qualified name.
  
          ``dispname``
            Name to display when searching/linking.
  
          ``type``
            Object type, a key in ``self.object_types``.
  
          ``docname``
            The document where it is to be found.
  
          ``anchor``
            The anchor name for the object.
  
          ``priority``
            How "important" the object is (determines placement in search
            results). One of:
  
            ``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 get_type_name(self, type: ObjType, primary: bool = False) -> str:
          """Return full name for given ObjType."""
          if primary:
              return type.lname
          return _('%s %s') % (self.label, type.lname)
  
      def get_enumerable_node_type(self, node: Node) -> str:
          """Get type of enumerable nodes (experimental)."""
          enum_node_type, _ = self.enumerable_nodes.get(node.__class__, (None, None))
          return enum_node_type
  
      def get_full_qualified_name(self, node: Element) -> str:
          """Return full qualified name for given node."""
          return None
  