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