コード例 #1
0
class NovaDomain(domains.Domain):
    """nova domain."""
    name = 'nova'
    label = 'nova'
    object_types = {
        'configoption': domains.ObjType(
            'extra spec',
            'spec',
        ),
    }
    directives = {
        'extra-spec': ExtraSpecDirective,
    }
    roles = {
        'extra-spec': ExtraSpecXRefRole(),
    }
    initial_data = {
        'extra_specs': {},
    }

    def resolve_xref(
        self,
        env,
        fromdocname,
        builder,
        typ,
        target,
        node,
        contnode,
    ):
        """Resolve cross-references"""
        if typ == 'extra-spec':
            return sphinx_nodes.make_refnode(
                builder,
                fromdocname,
                env.domaindata['nova']['extra_specs'][target],
                target,
                contnode,
                target,
            )
        return None

    def merge_domaindata(self, docnames, otherdata):
        for target, docname in otherdata['extra_specs'].items():
            if docname in docnames:
                self.data['extra_specs'][target] = docname
コード例 #2
0
class JSONDomain(domains.Domain):
    """
    Implementation of the JSON domain.

    The majority of the work is done by :class:`JSONObject` and the
    existing Sphinx/docutils classes.  The domain instance is responsible
    for tying everything together and providing somewhere to store data
    as the documents are processed.  The ``data`` dictionary contains
    three entries:

    :objects: mapping from normalized object name to
        :class:`PropertyDefinition` instance.  This mapping is cleared for
        each document in :meth:`.clear_doc`.

    :examples: list of "object key", "format", "content node" tuples
        that is processed in :meth:`.process_doc` to generate example
        nodes in the document.  See the :meth:`.generate_examples` method
        for details.

    :all_objects: mapping from normalized object name to
        :class:`PropertyDefinition` instance.  This mapping persists
        for as long as the Domain object.  It is used to keep object
        descriptions around for processing examples.

    """

    name = 'json'
    label = 'JSON'
    data_version = 1
    object_types = {
        'object': domains.ObjType('object', 'object', 'obj'),
    }
    directives = {
        'object': JSONObject,
    }
    roles = {
        'object': JSONXRef(),
    }
    initial_data = {
        'objects': {},  # name -> PropertyDefinition
        'all_objects': {},  # name -> PropertyDefinition
        'examples': [],  # tuple(obj-key, format, content-parent)
    }
    indicies = []

    REF_TYPES = {  # type-name -> (URL, tool tip)
        'uri': ('https://tools.ietf.org/html/rfc3986',
                'URI as described in RFC3986'),
        'boolean': (
            'https://docs.python.org/library/stdtypes.html#boolean-values',
            'Python Boolean'),
        'string': ('https://docs.python.org/library/stdtypes.html#str',
                   'Python String'),
        'integer': ('https://docs.python.org/library/stdtypes.html#int',
                    'Python Integer'),
        'float': ('https://docs.python.org/library/stdtypes.html#float',
                  'Python Float'),
        'null': ('https://docs.python.org/library/constants.html#None',
                 'Python None'),
        'email': ('https://tools.ietf.org/html/rfc2822#section-3.4.1',
                  'Email Address'),
        'iso8601': ('https://tools.ietf.org/html/rfc3339#section-5.6',
                    'ISO8601 Date/Time'),
        'uuid4': ('https://tools.ietf.org/html/rfc4122#section-4.4',
                  'UUIDv4 in canonical syntax'),
        'md5': ('https://tools.ietf.org/html/rfc1321', 'MD5 checksum'),
        'sha1': ('https://tools.ietf.org/html/rfc3174', 'SHA1 checksum'),
        'sha256': ('https://tools.ietf.org/html/rfc6234', 'SHA256 checksum'),
    }
    for alias, target in [('url', 'uri'), ('int', 'integer'),
                          ('str', 'string'), ('user_name', 'string'),
                          ('number', 'float'), ('bool', 'boolean')]:
        REF_TYPES[alias] = REF_TYPES[target]

    def clear_doc(self, docname):
        names = [
            k for k, v in self.data['objects'].items() if v.docname == docname
        ]
        for name in names:
            del self.data['objects'][name]
        del self.data['examples'][:]

    def process_doc(self, env, docname, document):
        super(JSONDomain, self).process_doc(env, docname, document)
        self.generate_examples(docname)

    def get_objects(self):
        for objdef in self.data['objects'].values():
            yield (objdef.name, objdef.name, 'object', objdef.docname,
                   objdef.key, 1)

    def resolve_xref(self, env, fromdocname, builder, typ, target, node,
                     contnode):
        """
        Generate a cross-reference node.

        :param str fromdocname: document name that contains the reference
        :param sphinx.builders.Builder builder: active builder
        :param str typ: identifies the type of cross reference
        :param str target: cross-reference target.  In the case of a
            property, this will be the property type.
        :param docutils.nodes.Element node: unresolved cross-reference
            node.  If this has a ``json:name`` attribute then it was
            processed by :class:`JSONXRef`
        :param docutils.nodes.Element contnode: content node.  This is
            what the new reference node should wrap
        :return: a cross-reference node or :data:`None`
        :rtype: docutils.nodes.reference|NoneType

        This method is also used to make property types ``clickable``.
        In this case the `typ` will be ``jsonprop`` and `target` will
        be the property type.  :data:`.REF_TYPES` is consulted to
        generate an appropriate link and tool tip -- for example, if
        the type is ``uri``, then a link to the RFC is generated.

        """
        if node.get('json:name'):
            objdef = self.get_object(node['json:name'])
            if objdef:
                return node_utils.make_refnode(builder, fromdocname,
                                               objdef.docname, objdef.key,
                                               contnode)
        if typ == 'jsonprop':
            try:
                ref = nodes.reference(internal=False)
                ref['refuri'], ref['reftitle'] = self.REF_TYPES[target]
                ref.append(contnode)
                return ref
            except KeyError:
                pass

    def get_object(self, name):
        """
        Retrieve an existing object.

        :param str name: object name

        :return: object defintion or :data:`None`
        :rtype: PropertyDefinition|NoneType

        """
        try:
            return self.data['objects'][normalize_object_name(name)]
        except KeyError:
            return None

    def add_object(self, name, env, contentnode):
        """
        Create a new object.

        :param str name:
        :param env:
        :param docutils.nodes.Element contentnode:

        :return: a new :class:`PropertyDefinition` instance
        :rtype: PropertyDefinition

        """
        props = PropertyDefinition(name, env.docname)
        props.gather(contentnode)
        self.data['objects'][props.key] = props
        self.data['all_objects'][props.key] = props
        return props

    def generate_examples(self, docname):
        """
        Generate example snippets after the document has been processed.

        :param str docname: documentat that is being processed.  This is
            used to report warnings.

        This is called from within :meth:`.process_doc` after our superclass
        has done all of the work.  When an object definition that wants an
        example generated is encountered by :meth:`JSONObject.run`, a new
        empty content node is inserted into the tree and a entry is added
        to our list of examples.

        Once we have processed *all* of the object descriptions, we can
        safely populate examples for objects that refer to each other.
        That is what this method is doing.

        """
        fake_factory = faker.Factory.create()
        for name, language, parent in self.data['examples']:
            props = self.get_object(name)
            sample_data = props.generate_sample_data(self.data['all_objects'],
                                                     fake_factory)
            if language == 'yaml' and yaml is not None:
                title = 'YAML Example'
                code_text = yaml.safe_dump(sample_data,
                                           indent=4,
                                           default_flow_style=False,
                                           explicit_start=True,
                                           version=(1, 2))
            else:
                if language == 'yaml':
                    self.env.warn(
                        docname, 'YAML support is disabled, pip install yaml '
                        'to enable.')
                title = 'JSON Example'
                language = 'json'
                code_text = json.dumps(sample_data,
                                       indent=4,
                                       ensure_ascii=False)

            example = nodes.literal_block(code_text, code_text)
            example['language'] = language
            parent.append(nodes.strong(title, title))
            parent.append(example)
コード例 #3
0
ファイル: eql.py プロジェクト: zhutony/edgedb
class EdgeQLDomain(s_domains.Domain):

    name = "eql"
    label = "EdgeQL"

    object_types = {
        'function': s_domains.ObjType('function', 'func', 'func-desc'),
        'constraint': s_domains.ObjType('constraint', 'constraint'),
        'type': s_domains.ObjType('type', 'type'),
        'keyword': s_domains.ObjType('keyword', 'kw'),
        'operator': s_domains.ObjType('operator', 'op', 'op-desc'),
        'statement': s_domains.ObjType('statement', 'stmt'),
    }

    _role_to_object_type = {
        role: tn
        for tn, td in object_types.items() for role in td.roles
    }

    directives = {
        'function': EQLFunctionDirective,
        'constraint': EQLConstraintDirective,
        'type': EQLTypeDirective,
        'keyword': EQLKeywordDirective,
        'operator': EQLOperatorDirective,
        'synopsis': EQLSynopsisDirective,
        'react-element': EQLReactElement,
        'struct': EQLStructElement,
    }

    roles = {
        'func': EQLFunctionXRef(),
        'func-desc': EQLFunctionDescXRef(),
        'constraint': EQLConstraintXRef(),
        'type': EQLTypeXRef(),
        'kw': s_roles.XRefRole(),
        'op': s_roles.XRefRole(),
        'op-desc': EQLOperatorDescXRef(),
        'stmt': s_roles.XRefRole(),
        'gh': GitHubLinkRole(),
    }

    desc_roles = {
        'func-desc',
        'op-desc',
    }

    initial_data = {
        'objects': {}  # fullname -> docname, objtype, description
    }

    def resolve_xref(self, env, fromdocname, builder,
                     type, target, node, contnode):

        objects = self.data['objects']
        expected_type = self._role_to_object_type[type]

        target = target.replace(' ', '-')
        if expected_type == 'keyword':
            targets = [f'keyword::{target}']
        elif expected_type == 'operator':
            targets = [f'operator::{target}']
        elif expected_type == 'statement':
            targets = [f'statement::{target}']
        elif expected_type in {'type', 'function', 'constraint'}:
            targets = [f'{expected_type}::{target}']
            if '::' not in target:
                targets.append(f'{expected_type}::std::{target}')
        else:
            targets = [target]

        docname = None
        obj_type = None
        obj_desc = None
        for target in targets:
            try:
                docname, obj_type, obj_desc = objects[target]
            except KeyError:
                continue

        if docname is None:
            if not node.get('eql-auto-link'):
                raise shared.DomainError(
                    f'cannot resolve :eql:{type}: targeting {target!r}')
            else:
                return

        if obj_type != expected_type:
            raise shared.DomainError(
                f'cannot resolve :eql:{type}: targeting {target!r}: '
                f'the type of referred object {expected_type!r} '
                f'does not match the reftype')

        if node['reftype'] in self.desc_roles:
            node = d_nodes.Text(obj_desc)
        else:
            node = s_nodes_utils.make_refnode(
                builder, fromdocname, docname, target, contnode, None)
            node['eql-type'] = obj_type

        return node

    def clear_doc(self, docname):
        for fullname, (fn, _l, _d) in list(self.data['objects'].items()):
            if fn == docname:
                del self.data['objects'][fullname]

    def merge_domaindata(self, docnames, otherdata):
        for fullname, (fn, objtype, desc) in otherdata['objects'].items():
            if fn in docnames:
                self.data['objects'][fullname] = (fn, objtype, desc)

    def get_objects(self):
        for refname, (docname, type, _desc) in self.data['objects'].items():
            yield (refname, refname, type, docname, refname, 1)

    def get_full_qualified_name(self, node):
        fn = node.get('eql-fullname')
        if not fn:
            raise shared.DirectiveParseError(
                self, 'no eql-fullname attribute')
        return fn
コード例 #4
0
class EdgeQLDomain(s_domains.Domain):

    name = "eql"
    label = "EdgeQL"

    object_types = {
        'function': s_domains.ObjType('function', 'func', 'func-desc'),
        'constraint': s_domains.ObjType('constraint', 'constraint'),
        'type': s_domains.ObjType('type', 'type'),
        'keyword': s_domains.ObjType('keyword', 'kw'),
        'operator': s_domains.ObjType('operator', 'op', 'op-desc'),
        'statement': s_domains.ObjType('statement', 'stmt'),
    }

    _role_to_object_type = {
        role: tn
        for tn, td in object_types.items() for role in td.roles
    }

    directives = {
        'function': EQLFunctionDirective,
        'constraint': EQLConstraintDirective,
        'type': EQLTypeDirective,
        'keyword': EQLKeywordDirective,
        'operator': EQLOperatorDirective,
        'synopsis': EQLSynopsisDirective,
        'react-element': EQLReactElement,
        'section-intro-page': EQLSectionIntroPage,
        'struct': EQLStructElement,
    }

    roles = {
        'func': EQLFunctionXRef(),
        'func-desc': EQLFunctionDescXRef(),
        'constraint': EQLConstraintXRef(),
        'type': EQLTypeXRef(),
        'kw': s_roles.XRefRole(),
        'op': s_roles.XRefRole(),
        'op-desc': EQLOperatorDescXRef(),
        'stmt': s_roles.XRefRole(),
        'gh': GitHubLinkRole(),
    }

    desc_roles = {
        'func-desc',
        'op-desc',
    }

    initial_data: Dict[str, Dict[str, Any]] = {
        'objects': {}  # fullname -> docname, objtype, description
    }

    def resolve_xref(self, env, fromdocname, builder, type, target, node,
                     contnode):

        objects = self.data['objects']
        expected_type = self._role_to_object_type[type]

        target = target.replace(' ', '-')
        if expected_type == 'keyword':
            targets = [f'keyword::{target}']
        elif expected_type == 'operator':
            targets = [f'operator::{target}']
        elif expected_type == 'statement':
            targets = [f'statement::{target}']
        elif expected_type in {'type', 'function', 'constraint'}:
            targets = [f'{expected_type}::{target}']
            if '::' not in target:
                targets.append(f'{expected_type}::std::{target}')
        else:
            targets = [target]

        docname = None
        obj_type = None
        obj_desc = None
        for target in targets:
            try:
                docname, obj_type, obj_desc = objects[target]
            except KeyError:
                continue

        if docname is None:
            if not node.get('eql-auto-link'):
                # if ref was not found, the :eql: xref may be being used
                # outside of the docs, so try resolving ref from intersphinx
                # inventories
                inventories = InventoryAdapter(env)

                for target in targets:
                    obj_type, name = target.split('::', 1)
                    if ':' not in name:
                        continue
                    docset_name, name = name.split(':', 1)

                    docset = inventories.named_inventory.get(docset_name)
                    if docset is None:
                        continue
                    refs = docset.get('eql:' + obj_type)
                    if refs is None:
                        continue
                    ref = refs.get(obj_type + '::' + name)
                    if ref is None:
                        continue

                    newnode = d_nodes.reference(
                        '',
                        '',
                        internal=False,
                        refuri=ref[2],
                    )
                    if node.get('refexplicit'):
                        newnode.append(d_nodes.Text(contnode.astext()))
                    else:
                        title = contnode.astext()
                        newnode.append(
                            contnode.__class__(title[len(docset_name) + 1:],
                                               title[len(docset_name) + 1:]))
                    return newnode

                raise shared.DomainError(
                    f'cannot resolve :eql:{type}: targeting {target!r}')
            else:
                return

        if obj_type != expected_type:
            raise shared.DomainError(
                f'cannot resolve :eql:{type}: targeting {target!r}: '
                f'the type of referred object {expected_type!r} '
                f'does not match the reftype')

        if node['reftype'] in self.desc_roles:
            node = d_nodes.Text(obj_desc)
        else:
            node = s_nodes_utils.make_refnode(builder, fromdocname, docname,
                                              target, contnode, None)
            node['eql-type'] = obj_type

        return node

    def resolve_any_xref(self, env, fromdocname, builder, target, node,
                         contnode):
        # 'myst-parser' resolves all markdown links as :any: xrefs, so return
        # empty list to prevent sphinx trying to resolve these as :eql: refs
        return []

    def clear_doc(self, docname):
        for fullname, (fn, _l, _d) in list(self.data['objects'].items()):
            if fn == docname:
                del self.data['objects'][fullname]

    def merge_domaindata(self, docnames, otherdata):
        for fullname, (fn, objtype, desc) in otherdata['objects'].items():
            if fn in docnames:
                self.data['objects'][fullname] = (fn, objtype, desc)

    def get_objects(self):
        for refname, (docname, type, _desc) in self.data['objects'].items():
            yield (refname, refname, type, docname, refname, 1)

    def get_full_qualified_name(self, node):
        fn = node.get('eql-fullname')
        if not fn:
            raise self.error('no eql-fullname attribute')
        return fn