class KOSAttribute(KOSObject): doc_field_types = [ Field('access', label=l_('Access'), has_arg=False), Field('type' , label=l_('Type' ), has_arg=False), ] def handle_signature(self, sig, signode): m = ks_sig_re.match(sig) name = m.group('object') struct = None #might override further down current_struct = self.env.temp_data.get('ks:structure') if m.group('prefix') is None: if current_struct is not None: struct = current_struct fullname = current_struct + ':' + name else: raise Exception("Attribute name lacks a prefix and isn't " + "indented inside a structure section. Problem " + "occurred at " + self.env.docname + ", line " + str(self.lineno) + ".") else: struct = m.group('prefix').split(':')[-1] fullname = struct + ':' + name if struct is not None: if struct != '': signode += addnodes.desc_type(struct,struct+':') signode += addnodes.desc_name(fullname, name) return fullname def get_index_text(self, objectname, name): return _('{}'.format(name))
class JSCallable(JSObject): """Description of a JavaScript function, method or constructor.""" has_arguments = True doc_field_types = [ TypedField('arguments', label=_('Arguments'), names=('argument', 'arg', 'parameter', 'param'), typerolename='func', typenames=('paramtype', 'type')), GroupedField('errors', label=_('Throws'), rolename='err', names=('throws', ), can_collapse=True), Field('returnvalue', label=_('Returns'), has_arg=False, names=('returns', 'return')), Field('returntype', label=_('Return type'), has_arg=False, names=('rtype', )), ]
class LuaFunction(LuaObject): typename = "function" doc_field_types = [ TypedField('parameter', label=l_('Parameters'), names=('param', 'parameter', 'arg', 'argument'), typerolename='obj', typenames=('paramtype', 'type', 'ptype')), TypedField('returnvalues', label=l_('Returns'), names=('return', 'ret'), typerolename='obj', typenames=('rtype', 'type')), Field('returnvalue', label=l_('Returns'), has_arg=False, names=('returns')), Field('returntype', label=l_('Return type'), has_arg=False, names=('returntype',)), ] def build_objtype(self): return self.options.get('objtype') or "" def needs_arglist(self): return True def get_index_name(self, names): return '%s()' % (names['fullname'])
def setup(app): from sphinx.util.docfields import Field, TypedField increase_python_stack() # the :ghci-cmd: directive used in ghci.rst app.add_object_type('ghci-cmd', 'ghci-cmd', parse_node=parse_ghci_cmd, objname='GHCi command', indextemplate='pair: %s; GHCi command') # Haddock references app.add_role('th-ref', haddock_role('template-haskell')) app.add_role('base-ref', haddock_role('base')) app.add_role('cabal-ref', haddock_role('Cabal')) app.add_role('ghc-compact-ref', haddock_role('ghc-compact')) app.add_role('ghc-prim-ref', haddock_role('ghc-prim')) app.add_role('parallel-ref', haddock_role('parallel')) app.add_role('array-ref', haddock_role('array')) app.add_object_type('rts-flag', 'rts-flag', objname='runtime system command-line option', parse_node=parse_flag, indextemplate='pair: %s; RTS option', doc_field_types=[ Field('since', label='Introduced in GHC version', names=['since']), ]) app.add_object_type('pragma', 'pragma', objname='Haskell pragma', parse_node=parse_pragma, indextemplate='pair: %s; pragma', doc_field_types=[ Field('since', label='Introduced in GHC version', names=['since']), Field('where', label='Allowed contexts', names=['where']) ])
class AlloyMacro(AlloyCallable): """Description of an Alloy macro.""" has_arguments = False display_prefix = "let " doc_field_types = [ TypedField('arguments', label=_('Arguments'), names=('argument', 'arg', 'parameter', 'param'), typerolename='func', typenames=('paramtype', 'type')), Field('expandsto', label=_('Expands to'), has_arg=False, names=('expand', 'expansion')), ]
class ClifInst(ClifObject): """A Cranelift IR instruction.""" doc_field_types = [ TypedField('argument', label=l_('Arguments'), names=('in', 'arg'), typerolename='type', typenames=('type',)), TypedField('result', label=l_('Results'), names=('out', 'result'), typerolename='type', typenames=('type',)), GroupedField( 'typevar', names=('typevar',), label=l_('Type Variables')), GroupedField('flag', names=('flag',), label=l_('Flags')), Field('resulttype', label=l_('Result type'), has_arg=False, names=('rtype',)), ] def handle_signature(self, sig, signode): # Look for signatures like # # v0, v1 = foo op0, op1 # v0 = foo # foo op0 parts = re.split(sep_equal, sig, 1) if len(parts) == 2: # Outgoing parameters. parse_params(parts[0], signode) signode += nodes.Text(' = ') name = parts[1] else: name = parts[0] # Parse 'name arg, arg' parts = name.split(None, 1) name = parts[0] signode += addnodes.desc_name(name, name) if len(parts) == 2: # Incoming parameters. signode += nodes.Text(' ') parse_params(parts[1], signode) return name def get_index_text(self, name): return name
def setup(app): from sphinx.util.docfields import Field app.add_object_type( directivename="conf", rolename="conf", objname="configuration value", indextemplate="pair: %s; configuration value", doc_field_types=[ Field( 'type', label='Type', has_arg=False, names=('type',), bodyrolename='class' ), ] )
class LuaData(LuaObject): typename = "data" option_spec = dict( readonly=directives.flag, **LuaObject.option_spec ) doc_field_types = [ Field('type', label=l_('Type'), has_arg=False, names=('type',)), ] def build_objtype(self): if 'readonly' in self.options: return "const %s" % (self.options.get('objtype') or "") else: return self.options.get('objtype') or ""
class JSAttribute(JSObject): doc_field_types = [ AnnotateField('type', label=l_('Type'), names=('type',), bodyrolename='class', annotate_type=ANNOTATE_TYPE, has_arg=False), Field('default', label=l_('Default'), names=('default',), has_arg=False), BoolField('required', label=l_('Required'), has_arg=False, names=('require', 'required')), BoolField('static', label=l_('Static'), names=('static',), has_arg=False), AnnotateField('readonly', label=l_('Read-only'), names=('readonly', 'ro'), annotate_type=ANNOTATE_READ_ONLY, has_arg=False), AnnotateField('extends', label=l_('Extends'), names=('extend', 'extends', 'inherits', 'inherit'), annotate_type=ANNOTATE_EXTENDS, bodyrolename='class', has_arg=False), AnnotateField('inheritedFrom', label=l_('inheritedFrom'), names=('inheritedFrom', 'inheritedFrom'), annotate_type=ANNOTATE_INHERITEDFROM, has_arg=False), ]
class DmlProperty(DmlObject): """Description of a function.""" option_spec: OptionSpec = DmlObject.option_spec.copy() option_spec.update({ 'const': directives.flag, 'readonly': directives.flag, 'type': directives.unchanged, }) doc_field_types = [ Field('default', label=_('Default'), names=('default')), GroupedField('throws', label=_('Throws'), names=('throws', 'throw', 'error', 'err'), can_collapse=True) ] def split_signature(self, sig: str) -> Tuple[str, str, List[str], List[str]]: # return tuple: # - type (class, property, event, function) -> str # - name -> str # - arglist -> List[str] # - annotations -> List[str] if not "type" in self.options: raise Exception("DML property always needs type assigned") typ = "property " + self.options["type"] name = sig arglist = None annotations = [] if 'const' in self.options: annotations.append("constant") if 'readonly' in self.options: annotations.append("readonly") return (typ, name, arglist, annotations) def get_index_text(self, name_cls: Tuple[str, str]) -> str: # add index in own add_target_and_index() instead. return "PropertyName " + name_cls[0]
class LuaClass(LuaObject): doc_field_types = [ Field('extend', label=l_('Extends'), has_arg=False, names=('extend',)), ] typename = "object" def get_index_type(self): return "%s" % (self.__class__.typename) def before_content(self): LuaObject.before_content(self) if self.names: self.env.temp_data['lua:class'] = self.names[0]['fullname'] def after_content(self): LuaObject.after_content(self) if self.names: self.env.temp_data['lua:class'] = None
class PSTable(PSschemaObject): doc_field_types = [ TypedField('column', label=l_('Columns'), rolename='obj', names=('col', 'column', 'cols'), typerolename='obj', typenames=('paramtype', 'type')), Field('inpatch', label=l_('Included in Patch'), has_arg=False, names=('inpatch')), TypedField('versioninfo', label=l_('Version Info'), rolename='obj', names=('version', 'versioninfo'), typerolename='obj', typenames=('paramtype', 'type')), ]
def setup(app: Sphinx) -> None: """Register the ``confval`` role and directive. This allows to declare theme options as their own object for styling and cross-referencing. """ app.add_object_type( "confval", "confval", objname="configuration parameter", doc_field_types=[ Field( "default", label="default", has_arg=True, names=("default", ), bodyrolename="class", ) ], )
def setup(app: Sphinx) -> None: """Register the :dir: role. This is just a shortcut to curb the cognitive dissonance when saying :file:`directory/`. """ app.add_role("dir", DirRole()) app.add_object_type( "confval", "confval", objname="configuration parameter", doc_field_types=[ Field( "default", label="default", has_arg=True, names=("default", ), bodyrolename="class", ) ], )
def setup(app): from sphinx.domains.python import PyField from sphinx.util.docfields import Field app.add_object_type("confval", "confval", objname="configuration value", indextemplate="pair: %s; configuration value", doc_field_types=[ PyField("type", label=_("Type"), has_arg=False, names=("type", ), bodyrolename="class"), Field( "default", label=_("Default"), has_arg=False, names=("default", ), ), ])
def setup(app): from sphinx.domains.python import PyField from sphinx.util.docfields import Field app.add_object_type('confval', 'confval', objname='configuration value', indextemplate='pair: %s; configuration value', doc_field_types=[ PyField('type', label=_('Type'), has_arg=False, names=('type', ), bodyrolename='class'), Field( 'default', label=_('Default'), has_arg=False, names=('default', ), ), ])
class ReFORMStatement(ReFORMMarkup): """ Description of a reFORM statement. """ doc_field_types = [ TypedField('parameter', label=l_('Parameters'), names=('param', 'parameter', 'arg', 'argument'), typerolename='type', typenames=('type', )), Field('returnvalue', label=l_('Returns'), has_arg=False, names=('returns', 'return')), ] def handle_signature(self, sig, signode): name = sig.strip() nameid = name.split()[0] desc_name = name signode += addnodes.desc_name(nameid, desc_name) return nameid
def setup(app) -> Dict[str, Any]: app.add_config_value('ceph_confval_imports', default=[], rebuild='html', types=[str]) app.add_config_value('ceph_confval_mgr_module_path', default=[], rebuild='html', types=[str]) app.add_config_value('ceph_confval_mgr_python_path', default=[], rebuild='', types=[str]) app.add_object_type( 'confsec', 'confsec', objname='configuration section', indextemplate='pair: %s; configuration section', doc_field_types=[ Field( 'example', label=_('Example'), has_arg=False, )] ) app.add_object_type( 'confval', 'confval', objname='configuration option', ) app.add_directive_to_domain('std', 'mgr_module', CephModule) app.add_directive_to_domain('std', 'confval', CephOption, override=True) app.connect('env-purge-doc', _reset_ref_context) return { 'version': 'builtin', 'parallel_read_safe': True, 'parallel_write_safe': True, }
class CabalField(CabalObject): ''' Base for fields in *.cabal files ''' option_spec = { 'noindex': directives.flag, 'deprecated': parse_deprecated, 'since': StrictVersion, 'synopsis': lambda x: x } doc_field_types = [ Field('default', label='Default value', names=['default'], has_arg=False) ] def handle_signature(self, sig, signode): ''' As in sphinx.directives.ObjectDescription By default make an object description from name and adding either deprecated or since as annotation. ''' env = self.state.document.settings.env sig = sig.strip() parts = sig.split(':', 1) name = parts[0] signode += addnodes.desc_name(name, name) signode += addnodes.desc_addname(': ', ': ') if len(parts) > 1: rest = parts[1].strip() signode += addnodes.desc_annotation(rest, rest) return name
def setup(app): from sphinx.domains.python import PyField from sphinx.util.docfields import Field app.add_object_type('confval', 'confval', objname='configuration value', indextemplate='pair: %s; configuration value', doc_field_types=[ PyField('type', label=_('Type'), has_arg=False, names=('type', ), bodyrolename='class'), Field( 'default', label=_('Default'), has_arg=False, names=('default', ), ), ]) app.add_stylesheet("css/theme_overrides.css") app.add_javascript("js/version_switch.js")
def make_confval(app): # Copy-pasted from https://github.com/rtfd/sphinx_rtd_theme from sphinx.locale import _ from sphinx.domains.python import PyField from sphinx.util.docfields import Field app.add_object_type('confval', 'confval', objname='configuration value', indextemplate='pair: %s; configuration value', doc_field_types=[ PyField('type', label=_('Type'), has_arg=False, names=('type', ), bodyrolename='class'), Field( 'default', label=_('Default'), has_arg=False, names=('default', ), ), ])
def setup(app): from sphinx.domains.python import PyField from sphinx.util.docfields import Field app.add_css_file('/source/_static/custom.css') app.add_stylesheet('/source/_static/theme_override.css') app.add_object_type('confval', 'confval', objname='configuration value', indextemplate='pair: %s; configuration value', doc_field_types=[ PyField('type', label=_('Type'), has_arg=False, names=('type', ), bodyrolename='class'), Field( 'default', label=_('Default'), has_arg=False, names=('default', ), ), ]) # Patch the MATLAB lexer to correct wrong highlighting from sphinx.highlighting import lexers from pygments.lexers import MatlabLexer from pygments.token import Name from pygments.filters import NameHighlightFilter matlab_lexer = MatlabLexer() matlab_lexer.add_filter( NameHighlightFilter( names=[ 'function', 'end', 'if', 'else', 'elseif', 'switch', 'case', 'return', 'otherwise' ], tokentype=Name.Function, )) app.add_lexer('matlab', matlab_lexer)
class DotNetProperty(DotNetObject): option_spec = { 'name': directives.unchanged, 'prefix': directives.unchanged, 'type': directives.unchanged, } doc_field_types = [ Field('value', label=l_('Value'), has_arg=False, names=('value', 'value')), ] def handle_signature(self, sig, signode): name = self.options['name'] prefix = self.options['prefix'] type_v = json.loads(self.options['type']) if prefix != 'readwrite': signode += nodes.Text(prefix, prefix) signode += nodes.Text(' ', u'\xa0') xref = addnodes.pending_xref(':ref:`' + type_v[1] + '`', refdomain='std', reftype='ref', reftarget=ws_re.sub( ' ', type_v[1].lower()), refexplicit=False) xref += nodes.Text(type_v[0], type_v[0]) signode += xref signode += nodes.Text(' ', u'\xa0') signode += addnodes.desc_name(name, name) return name, ""
class JSONDefinition(JSONObject): doc_field_types = [ Field("type", label=l_("Type"), names=("type")), TypedField( "props", label=l_("Properties"), names=("property", "prop"), typenames=("array", "boolean", "integer", "null", "number", "object", "string"), ) ] def handle_signature(self, sig, signode): import ipdb ipdb.set_trace() signode += addnodes.desc_name(sig, sig) fullname = self.objtype.capitalize() + " " + sig signode["fullname"] = fullname return (fullname, sig) def add_target_and_index(self, name_cls, sig, signode): signode["ids"].append("{0}-{1}".format(self.objtype, sig)) self.env.domaindata["jsschema"]["objects"][sig] = (self.env.docname, self.objtype, "")
class CObject(ObjectDescription): """ Description of a C language object. """ doc_field_types = [ TypedField('parameter', label=l_('Parameters'), names=('param', 'parameter', 'arg', 'argument'), typerolename='type', typenames=('type', )), Field('returnvalue', label=l_('Returns'), has_arg=False, names=('returns', 'return')), Field('returntype', label=l_('Return type'), has_arg=False, names=('rtype', )), ] # These C types aren't described anywhere, so don't try to create # a cross-reference to them stopwords = set(( 'const', 'void', 'char', 'wchar_t', 'int', 'short', 'long', 'float', 'double', 'unsigned', 'signed', 'FILE', 'clock_t', 'time_t', 'ptrdiff_t', 'size_t', 'ssize_t', 'struct', '_Bool', )) def _parse_type(self, node, ctype): # type: (nodes.Node, unicode) -> None # add cross-ref nodes for all words for part in [_f for _f in wsplit_re.split(ctype) if _f]: # type: ignore tnode = nodes.Text(part, part) if part[0] in string.ascii_letters + '_' and \ part not in self.stopwords: pnode = addnodes.pending_xref('', refdomain='c', reftype='type', reftarget=part, modname=None, classname=None) pnode += tnode node += pnode else: node += tnode def _parse_arglist(self, arglist): # type: (unicode) -> Iterator[unicode] while True: m = c_funcptr_arg_sig_re.match(arglist) # type: ignore if m: yield m.group() arglist = c_funcptr_arg_sig_re.sub('', arglist) # type: ignore if ',' in arglist: _, arglist = arglist.split(',', 1) else: break else: if ',' in arglist: arg, arglist = arglist.split(',', 1) yield arg else: yield arglist break def handle_signature(self, sig, signode): # type: (unicode, addnodes.desc_signature) -> unicode """Transform a C signature into RST nodes.""" # first try the function pointer signature regex, it's more specific m = c_funcptr_sig_re.match(sig) # type: ignore if m is None: m = c_sig_re.match(sig) # type: ignore if m is None: raise ValueError('no match') rettype, name, arglist, const = m.groups() signode += addnodes.desc_type('', '') self._parse_type(signode[-1], rettype) try: classname, funcname = name.split('::', 1) classname += '::' signode += addnodes.desc_addname(classname, classname) signode += addnodes.desc_name(funcname, funcname) # name (the full name) is still both parts except ValueError: signode += addnodes.desc_name(name, name) # clean up parentheses from canonical name m = c_funcptr_name_re.match(name) if m: name = m.group(1) typename = self.env.ref_context.get('c:type') if self.name == 'c:member' and typename: fullname = typename + '.' + name else: fullname = name if not arglist: if self.objtype == 'function': # for functions, add an empty parameter list signode += addnodes.desc_parameterlist() if const: signode += addnodes.desc_addname(const, const) return fullname paramlist = addnodes.desc_parameterlist() arglist = arglist.replace('`', '').replace('\\ ', '') # remove markup # this messes up function pointer types, but not too badly ;) for arg in self._parse_arglist(arglist): arg = arg.strip() param = addnodes.desc_parameter('', '', noemph=True) try: m = c_funcptr_arg_sig_re.match(arg) # type: ignore if m: self._parse_type(param, m.group(1) + '(') param += nodes.emphasis(m.group(2), m.group(2)) self._parse_type(param, ')(' + m.group(3) + ')') if m.group(4): param += addnodes.desc_addname(m.group(4), m.group(4)) else: ctype, argname = arg.rsplit(' ', 1) self._parse_type(param, ctype) # separate by non-breaking space in the output param += nodes.emphasis(' ' + argname, u'\xa0' + argname) except ValueError: # no argument name given, only the type self._parse_type(param, arg) paramlist += param signode += paramlist if const: signode += addnodes.desc_addname(const, const) return fullname def get_index_text(self, name): # type: (unicode) -> unicode if self.objtype == 'function': return _('%s (C function)') % name elif self.objtype == 'member': return _('%s (C member)') % name elif self.objtype == 'macro': return _('%s (C macro)') % name elif self.objtype == 'type': return _('%s (C type)') % name elif self.objtype == 'var': return _('%s (C variable)') % name else: return '' def add_target_and_index(self, name, sig, signode): # type: (unicode, unicode, addnodes.desc_signature) -> None # for C API items we add a prefix since names are usually not qualified # by a module name and so easily clash with e.g. section titles targetname = 'c.' + name if targetname not in self.state.document.ids: signode['names'].append(targetname) signode['ids'].append(targetname) signode['first'] = (not self.names) self.state.document.note_explicit_target(signode) inv = self.env.domaindata['c']['objects'] if name in inv: self.state_machine.reporter.warning( '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) if indextext: self.indexnode['entries'].append( ('single', indextext, targetname, '', None)) def before_content(self): # type: () -> None self.typename_set = False if self.name == 'c:type': if self.names: self.env.ref_context['c:type'] = self.names[0] self.typename_set = True def after_content(self): # type: () -> None if self.typename_set: self.env.ref_context.pop('c:type', None)
class PyObject(ObjectDescription): """ Description of a general Python object. :cvar allow_nesting: Class is an object that allows for nested namespaces :vartype allow_nesting: bool """ option_spec = { 'noindex': directives.flag, 'module': directives.unchanged, 'annotation': directives.unchanged, } doc_field_types = [ PyTypedField('parameter', label=_('Parameters'), names=('param', 'parameter', 'arg', 'argument', 'keyword', 'kwarg', 'kwparam'), typerolename='class', typenames=('paramtype', 'type'), can_collapse=True), PyTypedField('variable', label=_('Variables'), rolename='obj', names=('var', 'ivar', 'cvar'), typerolename='class', typenames=('vartype',), can_collapse=True), PyGroupedField('exceptions', label=_('Raises'), rolename='exc', names=('raises', 'raise', 'exception', 'except'), can_collapse=True), Field('returnvalue', label=_('Returns'), has_arg=False, names=('returns', 'return')), PyField('returntype', label=_('Return type'), has_arg=False, names=('rtype',), bodyrolename='class'), ] allow_nesting = False def get_signature_prefix(self, sig: str) -> str: """May return a prefix to put before the object name in the signature. """ return '' def needs_arglist(self) -> bool: """May return true if an empty argument list is to be generated even if the document contains none. """ return False def handle_signature(self, sig: str, signode: desc_signature) -> Tuple[str, str]: """Transform a Python signature into RST nodes. Return (fully qualified name of the thing, classname if any). If inside a class, the current class name is handled intelligently: * it is stripped from the displayed name if present * it is added to the full name (return value) if not present """ m = py_sig_re.match(sig) if m is None: raise ValueError prefix, name, arglist, retann = m.groups() # determine module and class name (if applicable), as well as full name modname = self.options.get('module', self.env.ref_context.get('py:module')) classname = self.env.ref_context.get('py:class') if classname: add_module = False if prefix and (prefix == classname or prefix.startswith(classname + ".")): fullname = prefix + name # class name is given again in the signature prefix = prefix[len(classname):].lstrip('.') elif prefix: # class name is given in the signature, but different # (shouldn't happen) fullname = classname + '.' + prefix + name else: # class name is not given in the signature fullname = classname + '.' + name else: add_module = True if prefix: classname = prefix.rstrip('.') fullname = prefix + name else: classname = '' fullname = name signode['module'] = modname signode['class'] = classname signode['fullname'] = fullname sig_prefix = self.get_signature_prefix(sig) if sig_prefix: signode += addnodes.desc_annotation(sig_prefix, sig_prefix) if prefix: signode += addnodes.desc_addname(prefix, prefix) elif add_module and self.env.config.add_module_names: if modname and modname != 'exceptions': # exceptions are a special case, since they are documented in the # 'exceptions' module. nodetext = modname + '.' signode += addnodes.desc_addname(nodetext, nodetext) signode += addnodes.desc_name(name, name) if arglist: try: signode += _parse_arglist(arglist) except SyntaxError: # fallback to parse arglist original parser. # it supports to represent optional arguments (ex. "func(foo [, bar])") _pseudo_parse_arglist(signode, arglist) except NotImplementedError as exc: logger.warning("could not parse arglist (%r): %s", arglist, exc) _pseudo_parse_arglist(signode, arglist) else: if self.needs_arglist(): # for callables, add an empty parameter list signode += addnodes.desc_parameterlist() if retann: signode += addnodes.desc_returns(retann, retann) anno = self.options.get('annotation') if anno: signode += addnodes.desc_annotation(' ' + anno, ' ' + anno) return fullname, prefix def get_index_text(self, modname: str, name: Tuple[str, str]) -> str: """Return the text for the index entry of the object.""" raise NotImplementedError('must be implemented in subclasses') def add_target_and_index(self, name_cls: Tuple[str, str], sig: str, signode: desc_signature) -> None: modname = self.options.get('module', self.env.ref_context.get('py:module')) fullname = (modname + '.' if modname else '') + name_cls[0] node_id = make_id(self.env, self.state.document, modname or '', name_cls[0]) signode['ids'].append(node_id) # Assign old styled node_id(fullname) not to break old hyperlinks (if possible) # Note: Will removed in Sphinx-5.0 (RemovedInSphinx50Warning) if node_id != fullname and fullname not in self.state.document.ids: signode['ids'].append(fullname) self.state.document.note_explicit_target(signode) domain = cast(PythonDomain, self.env.get_domain('py')) domain.note_object(fullname, self.objtype, node_id, location=signode) indextext = self.get_index_text(modname, name_cls) if indextext: self.indexnode['entries'].append(('single', indextext, node_id, '', None)) def before_content(self) -> None: """Handle object nesting before content :py:class:`PyObject` represents Python language constructs. For constructs that are nestable, such as a Python classes, this method will build up a stack of the nesting heirarchy so that it can be later de-nested correctly, in :py:meth:`after_content`. For constructs that aren't nestable, the stack is bypassed, and instead only the most recent object is tracked. This object prefix name will be removed with :py:meth:`after_content`. """ prefix = None if self.names: # fullname and name_prefix come from the `handle_signature` method. # fullname represents the full object name that is constructed using # object nesting and explicit prefixes. `name_prefix` is the # explicit prefix given in a signature (fullname, name_prefix) = self.names[-1] if self.allow_nesting: prefix = fullname elif name_prefix: prefix = name_prefix.strip('.') if prefix: self.env.ref_context['py:class'] = prefix if self.allow_nesting: classes = self.env.ref_context.setdefault('py:classes', []) classes.append(prefix) if 'module' in self.options: modules = self.env.ref_context.setdefault('py:modules', []) modules.append(self.env.ref_context.get('py:module')) self.env.ref_context['py:module'] = self.options['module'] def after_content(self) -> None: """Handle object de-nesting after content If this class is a nestable object, removing the last nested class prefix ends further nesting in the object. If this class is not a nestable object, the list of classes should not be altered as we didn't affect the nesting levels in :py:meth:`before_content`. """ classes = self.env.ref_context.setdefault('py:classes', []) if self.allow_nesting: try: classes.pop() except IndexError: pass self.env.ref_context['py:class'] = (classes[-1] if len(classes) > 0 else None) if 'module' in self.options: modules = self.env.ref_context.setdefault('py:modules', []) if modules: self.env.ref_context['py:module'] = modules.pop() else: self.env.ref_context.pop('py:module')
class PyObject(ObjectDescription): """ Description of a general Python object. :cvar allow_nesting: Class is an object that allows for nested namespaces :vartype allow_nesting: bool """ option_spec = { 'noindex': directives.flag, 'module': directives.unchanged, 'annotation': directives.unchanged, } doc_field_types = [ PyTypedField('parameter', label=_('Parameters'), names=('param', 'parameter', 'arg', 'argument', 'keyword', 'kwarg', 'kwparam'), typerolename='class', typenames=('paramtype', 'type'), can_collapse=True), PyTypedField('variable', label=_('Variables'), rolename='obj', names=('var', 'ivar', 'cvar'), typerolename='class', typenames=('vartype',), can_collapse=True), PyGroupedField('exceptions', label=_('Raises'), rolename='exc', names=('raises', 'raise', 'exception', 'except'), can_collapse=True), Field('returnvalue', label=_('Returns'), has_arg=False, names=('returns', 'return')), PyField('returntype', label=_('Return type'), has_arg=False, names=('rtype',), bodyrolename='class'), ] allow_nesting = False def get_signature_prefix(self, sig): # type: (unicode) -> unicode """May return a prefix to put before the object name in the signature. """ return '' def needs_arglist(self): # type: () -> bool """May return true if an empty argument list is to be generated even if the document contains none. """ return False def handle_signature(self, sig, signode): # type: (unicode, addnodes.desc_signature) -> Tuple[unicode, unicode] """Transform a Python signature into RST nodes. Return (fully qualified name of the thing, classname if any). If inside a class, the current class name is handled intelligently: * it is stripped from the displayed name if present * it is added to the full name (return value) if not present """ m = py_sig_re.match(sig) # type: ignore if m is None: raise ValueError name_prefix, name, arglist, retann = m.groups() # determine module and class name (if applicable), as well as full name modname = self.options.get( 'module', self.env.ref_context.get('py:module')) classname = self.env.ref_context.get('py:class') if classname: add_module = False if name_prefix and name_prefix.startswith(classname): fullname = name_prefix + name # class name is given again in the signature name_prefix = name_prefix[len(classname):].lstrip('.') elif name_prefix: # class name is given in the signature, but different # (shouldn't happen) fullname = classname + '.' + name_prefix + name else: # class name is not given in the signature fullname = classname + '.' + name else: add_module = True if name_prefix: classname = name_prefix.rstrip('.') fullname = name_prefix + name else: classname = '' fullname = name signode['module'] = modname signode['class'] = classname signode['fullname'] = fullname sig_prefix = self.get_signature_prefix(sig) if sig_prefix: signode += addnodes.desc_annotation(sig_prefix, sig_prefix) if name_prefix: signode += addnodes.desc_addname(name_prefix, name_prefix) # exceptions are a special case, since they are documented in the # 'exceptions' module. elif add_module and self.env.config.add_module_names: modname = self.options.get( 'module', self.env.ref_context.get('py:module')) if modname and modname != 'exceptions': nodetext = modname + '.' signode += addnodes.desc_addname(nodetext, nodetext) anno = self.options.get('annotation') signode += addnodes.desc_name(name, name) if not arglist: if self.needs_arglist(): # for callables, add an empty parameter list signode += addnodes.desc_parameterlist() if retann: signode += addnodes.desc_returns(retann, retann) if anno: signode += addnodes.desc_annotation(' ' + anno, ' ' + anno) return fullname, name_prefix _pseudo_parse_arglist(signode, arglist) if retann: signode += addnodes.desc_returns(retann, retann) if anno: signode += addnodes.desc_annotation(' ' + anno, ' ' + anno) return fullname, name_prefix def get_index_text(self, modname, name): # type: (unicode, unicode) -> unicode """Return the text for the index entry of the object.""" raise NotImplementedError('must be implemented in subclasses') def add_target_and_index(self, name_cls, sig, signode): # type: (unicode, unicode, addnodes.desc_signature) -> None modname = self.options.get( 'module', self.env.ref_context.get('py:module')) fullname = (modname and modname + '.' or '') + name_cls[0] # note target if fullname not in self.state.document.ids: signode['names'].append(fullname) signode['ids'].append(fullname) signode['first'] = (not self.names) self.state.document.note_explicit_target(signode) objects = self.env.domaindata['py']['objects'] if fullname in objects: self.state_machine.reporter.warning( 'duplicate object description of %s, ' % fullname + 'other instance in ' + self.env.doc2path(objects[fullname][0]) + ', use :noindex: for one of them', line=self.lineno) objects[fullname] = (self.env.docname, self.objtype) indextext = self.get_index_text(modname, name_cls) if indextext: self.indexnode['entries'].append(('single', indextext, fullname, '', None)) def before_content(self): # type: () -> None """Handle object nesting before content :py:class:`PyObject` represents Python language constructs. For constructs that are nestable, such as a Python classes, this method will build up a stack of the nesting heirarchy so that it can be later de-nested correctly, in :py:meth:`after_content`. For constructs that aren't nestable, the stack is bypassed, and instead only the most recent object is tracked. This object prefix name will be removed with :py:meth:`after_content`. """ prefix = None if self.names: # fullname and name_prefix come from the `handle_signature` method. # fullname represents the full object name that is constructed using # object nesting and explicit prefixes. `name_prefix` is the # explicit prefix given in a signature (fullname, name_prefix) = self.names[-1] if self.allow_nesting: prefix = fullname elif name_prefix: prefix = name_prefix.strip('.') if prefix: self.env.ref_context['py:class'] = prefix if self.allow_nesting: classes = self.env.ref_context.setdefault('py:classes', []) classes.append(prefix) if 'module' in self.options: modules = self.env.ref_context.setdefault('py:modules', []) modules.append(self.env.ref_context.get('py:module')) self.env.ref_context['py:module'] = self.options['module'] def after_content(self): # type: () -> None """Handle object de-nesting after content If this class is a nestable object, removing the last nested class prefix ends further nesting in the object. If this class is not a nestable object, the list of classes should not be altered as we didn't affect the nesting levels in :py:meth:`before_content`. """ classes = self.env.ref_context.setdefault('py:classes', []) if self.allow_nesting: try: classes.pop() except IndexError: pass self.env.ref_context['py:class'] = (classes[-1] if len(classes) > 0 else None) if 'module' in self.options: modules = self.env.ref_context.setdefault('py:modules', []) if modules: self.env.ref_context['py:module'] = modules.pop() else: self.env.ref_context.pop('py:module')
class PyObject(ObjectDescription): """ Description of a general Python object. """ option_spec = { 'noindex': directives.flag, 'module': directives.unchanged, 'annotation': directives.unchanged, } doc_field_types = [ PyTypedField('parameter', label=l_('Parameters'), names=('param', 'parameter', 'arg', 'argument', 'keyword', 'kwarg', 'kwparam'), typerolename='obj', typenames=('paramtype', 'type'), can_collapse=True), PyTypedField('variable', label=l_('Variables'), rolename='obj', names=('var', 'ivar', 'cvar'), typerolename='obj', typenames=('vartype', ), can_collapse=True), PyGroupedField('exceptions', label=l_('Raises'), rolename='exc', names=('raises', 'raise', 'exception', 'except'), can_collapse=True), Field('returnvalue', label=l_('Returns'), has_arg=False, names=('returns', 'return')), PyField('returntype', label=l_('Return type'), has_arg=False, names=('rtype', ), bodyrolename='obj'), ] def get_signature_prefix(self, sig): # type: (unicode) -> unicode """May return a prefix to put before the object name in the signature. """ return '' def needs_arglist(self): # type: () -> bool """May return true if an empty argument list is to be generated even if the document contains none. """ return False def handle_signature(self, sig, signode): # type: ignore # type: (unicode, addnodes.desc_signature) -> Tuple[unicode, unicode] """Transform a Python signature into RST nodes. Return (fully qualified name of the thing, classname if any). If inside a class, the current class name is handled intelligently: * it is stripped from the displayed name if present * it is added to the full name (return value) if not present """ m = py_sig_re.match(sig) # type: ignore if m is None: raise ValueError name_prefix, name, arglist, retann = m.groups() # determine module and class name (if applicable), as well as full name modname = self.options.get('module', self.env.ref_context.get('py:module')) classname = self.env.ref_context.get('py:class') if classname: add_module = False if name_prefix and name_prefix.startswith(classname): fullname = name_prefix + name # class name is given again in the signature name_prefix = name_prefix[len(classname):].lstrip('.') elif name_prefix: # class name is given in the signature, but different # (shouldn't happen) fullname = classname + '.' + name_prefix + name else: # class name is not given in the signature fullname = classname + '.' + name else: add_module = True if name_prefix: classname = name_prefix.rstrip('.') fullname = name_prefix + name else: classname = '' fullname = name signode['module'] = modname signode['class'] = classname signode['fullname'] = fullname sig_prefix = self.get_signature_prefix(sig) if sig_prefix: signode += addnodes.desc_annotation(sig_prefix, sig_prefix) if name_prefix: signode += addnodes.desc_addname(name_prefix, name_prefix) # exceptions are a special case, since they are documented in the # 'exceptions' module. elif add_module and self.env.config.add_module_names: modname = self.options.get('module', self.env.ref_context.get('py:module')) if modname and modname != 'exceptions': nodetext = modname + '.' signode += addnodes.desc_addname(nodetext, nodetext) anno = self.options.get('annotation') signode += addnodes.desc_name(name, name) if not arglist: if self.needs_arglist(): # for callables, add an empty parameter list signode += addnodes.desc_parameterlist() if retann: signode += addnodes.desc_returns(retann, retann) if anno: signode += addnodes.desc_annotation(' ' + anno, ' ' + anno) return fullname, name_prefix _pseudo_parse_arglist(signode, arglist) if retann: signode += addnodes.desc_returns(retann, retann) if anno: signode += addnodes.desc_annotation(' ' + anno, ' ' + anno) return fullname, name_prefix def get_index_text(self, modname, name): # type: (unicode, unicode) -> unicode """Return the text for the index entry of the object.""" raise NotImplementedError('must be implemented in subclasses') def add_target_and_index(self, name_cls, sig, signode): # type: (unicode, unicode, addnodes.desc_signature) -> None modname = self.options.get('module', self.env.ref_context.get('py:module')) fullname = (modname and modname + '.' or '') + name_cls[0] # note target if fullname not in self.state.document.ids: signode['names'].append(fullname) signode['ids'].append(fullname) signode['first'] = (not self.names) self.state.document.note_explicit_target(signode) objects = self.env.domaindata['py']['objects'] if fullname in objects: self.state_machine.reporter.warning( 'duplicate object description of %s, ' % fullname + 'other instance in ' + self.env.doc2path(objects[fullname][0]) + ', use :noindex: for one of them', line=self.lineno) objects[fullname] = (self.env.docname, self.objtype) indextext = self.get_index_text(modname, name_cls) if indextext: self.indexnode['entries'].append( ('single', indextext, fullname, '', None)) def before_content(self): # type: () -> None # needed for automatic qualification of members (reset in subclasses) self.clsname_set = False def after_content(self): # type: () -> None if self.clsname_set: self.env.ref_context.pop('py:class', None)
class CephOption(ObjectDescription): """ emit option loaded from given command/options/<name>.yaml.in file """ has_content = True required_arguments = 1 optional_arguments = 0 final_argument_whitespace = False option_spec = { 'module': directives.unchanged, 'default': directives.unchanged } doc_field_types = [ Field('default', label=_('Default'), has_arg=False, names=('default', )), Field('type', label=_('Type'), has_arg=False, names=('type', ), bodyrolename='class'), ] template = jinja_template() opts: Dict[str, Dict[str, FieldValueT]] = {} mgr_opts: Dict[str, # module name Dict[str, # option name Dict[str, # field_name FieldValueT]]] = {} def _load_yaml(self) -> Dict[str, Dict[str, FieldValueT]]: if CephOption.opts: return CephOption.opts opts = [] for fn in status_iterator(self.config.ceph_confval_imports, 'loading options...', 'red', len(self.config.ceph_confval_imports), self.env.app.verbosity): self.env.note_dependency(fn) try: with open(fn, 'r') as f: yaml_in = io.StringIO() for line in f: if '@' not in line: yaml_in.write(line) yaml_in.seek(0) opts += yaml.safe_load(yaml_in)['options'] except OSError as e: message = f'Unable to open option file "{fn}": {e}' raise self.error(message) CephOption.opts = dict((opt['name'], opt) for opt in opts) return CephOption.opts def _normalize_path(self, dirname): my_dir = os.path.dirname(os.path.realpath(__file__)) src_dir = os.path.abspath(os.path.join(my_dir, '../..')) return os.path.join(src_dir, dirname) def _is_mgr_module(self, dirname, name): if not os.path.isdir(os.path.join(dirname, name)): return False if not os.path.isfile(os.path.join(dirname, name, '__init__.py')): return False return name not in ['tests'] @contextlib.contextmanager def mocked_modules(self): # src/pybind/mgr/tests from tests import mock mock_imports = [ 'rados', 'rbd', 'cephfs', 'dateutil', 'dateutil.parser' ] # make dashboard happy mock_imports += [ 'OpenSSL', 'jwt', 'bcrypt', 'jsonpatch', 'rook.rook_client', 'rook.rook_client.ceph', 'rook.rook_client._helper', 'cherrypy=3.2.3' ] # make diskprediction_local happy mock_imports += ['numpy', 'scipy'] # make restful happy mock_imports += [ 'pecan', 'pecan.rest', 'pecan.hooks', 'werkzeug', 'werkzeug.serving' ] for m in mock_imports: args = {} parts = m.split('=', 1) mocked = parts[0] if len(parts) > 1: args['__version__'] = parts[1] sys.modules[mocked] = mock.Mock(**args) try: yield finally: for m in mock_imports: mocked = m.split('=', 1)[0] sys.modules.pop(mocked) def _collect_options_from_module(self, name): with self.mocked_modules(): mgr_mod = __import__(name, globals(), locals(), [], 0) # import 'M' from src/pybind/mgr/tests from tests import M def subclass(x): try: return issubclass(x, M) except TypeError: return False ms = [ c for c in mgr_mod.__dict__.values() if subclass(c) and 'Standby' not in c.__name__ ] [m] = ms assert isinstance(m.MODULE_OPTIONS, list) return m.MODULE_OPTIONS def _load_module(self, module) -> Dict[str, Dict[str, FieldValueT]]: mgr_opts = CephOption.mgr_opts.get(module) if mgr_opts is not None: return mgr_opts python_path = self.config.ceph_confval_mgr_python_path for path in python_path.split(':'): sys.path.insert(0, self._normalize_path(path)) module_path = self.env.config.ceph_confval_mgr_module_path module_path = self._normalize_path(module_path) sys.path.insert(0, module_path) if not self._is_mgr_module(module_path, module): raise self.error( f'module "{module}" not found under {module_path}') fn = os.path.join(module_path, module, 'module.py') if os.path.exists(fn): self.env.note_dependency(fn) os.environ['UNITTEST'] = 'true' opts = self._collect_options_from_module(module) CephOption.mgr_opts[module] = dict((opt['name'], opt) for opt in opts) return CephOption.mgr_opts[module] def _current_module(self) -> str: return self.options.get('module', self.env.ref_context.get('ceph:module')) def _render_option(self, name) -> str: cur_module = self._current_module() if cur_module: opt = self._load_module(cur_module).get(name) else: opt = self._load_yaml().get(name) if opt is None: raise self.error(f'Option "{name}" not found!') if cur_module and 'type' not in opt: # the type of module option defaults to 'str' opt['type'] = 'str' desc = opt.get('fmt_desc') or opt.get('long_desc') or opt.get('desc') opt_default = opt.get('default') default = self.options.get('default', opt_default) try: return self.template.render(opt=opt, desc=desc, default=default) except Exception as e: message = (f'Unable to render option "{name}": {e}. ', f'opt={opt}, desc={desc}, default={default}') raise self.error(message) def handle_signature(self, sig: str, signode: addnodes.desc_signature) -> str: signode.clear() signode += addnodes.desc_name(sig, sig) # normalize whitespace like XRefRole does name = ws_re.sub(' ', sig) cur_module = self._current_module() if cur_module: return '/'.join(['mgr', cur_module, name]) else: return name def transform_content(self, contentnode: addnodes.desc_content) -> None: name = self.arguments[0] source, lineno = self.get_source_info() source = f'{source}:{lineno}:<confval>' fields = StringList(self._render_option(name).splitlines() + [''], source=source, parent_offset=lineno) with switch_source_input(self.state, fields): self.state.nested_parse(fields, 0, contentnode) def add_target_and_index(self, name: str, sig: str, signode: addnodes.desc_signature) -> None: node_id = make_id(self.env, self.state.document, self.objtype, name) signode['ids'].append(node_id) self.state.document.note_explicit_target(signode) entry = f'{name}; configuration option' self.indexnode['entries'].append(('pair', entry, node_id, '', None)) std = self.env.get_domain('std') std.note_object(self.objtype, name, node_id, location=signode)
class AdaObject(ObjectDescription): """ Description of an Ada language object. """ doc_field_types = [ TypedField('parameter', label=l_('Parameters'), names=('param', 'parameter', 'arg', 'argument'), typerolename='type', typenames=('type', )), Field('returnvalue', label=l_('Returns'), has_arg=False, names=('returns', 'return')), Field('returntype', label=l_('Return type'), has_arg=False, names=('rtype', )), ] def needs_arglist(self): return self.objtype == 'function' or self.objtype == 'procedure' def _resolve_module_name(self, signode, modname, name): env_modname = self.options.get( 'module', self.env.temp_data.get('ada:module', 'ada')) if modname: fullname = modname + name signode['module'] = modname[:-1] else: fullname = env_modname + '.' + name signode['module'] = env_modname signode['fullname'] = fullname name_prefix = signode['module'] + '.' signode += addnodes.desc_addname(name_prefix, name_prefix) signode += addnodes.desc_name(name, name) return fullname def _handle_function_signature(self, sig, signode): m = ada_func_sig_re.match(sig) if m is None: print("m did not match the function") raise ValueError modname, name, dummy, arglist, returntype, abstract = m.groups() fullname = self._resolve_module_name(signode, modname, name) if not arglist: if self.needs_arglist(): # for functions and procedures, add an empty parameter list new_node = addnodes.desc_parameterlist() new_node.child_text_separator = '; ' signode += new_node if returntype: signode += addnodes.desc_returns(returntype, returntype) return fullname signode += nodes.Text(' ') new_node = addnodes.desc_parameterlist() new_node.child_text_separator = '; ' signode += new_node stack = [signode[-1]] counters = [0, 0] for token in string.split(arglist, ';'): pieces = string.split(token, ':') name = pieces[0].strip() stack[-1] += addnodes.desc_parameter( name, name + " : " + pieces[1].strip()) if len(stack) == 1: counters[0] += 1 else: counters[1] += 1 if len(stack) != 1: raise ValueError if not counters[1]: fullname = '%s/%d' % (fullname, counters[0]) else: fullname = '%s/%d..%d' % (fullname, counters[0], sum(counters)) if returntype: signode += addnodes.desc_returns(returntype, returntype) return fullname def _handle_procedure_signature(self, sig, signode): m = ada_proc_sig_re.match(sig) if m is None: print("m did not match") raise ValueError modname, name, dummy, arglist, abstract = m.groups() fullname = self._resolve_module_name(signode, modname, name) if not arglist: if self.needs_arglist(): # for functions and procedures, add an empty parameter list newnode = addnodes.desc_parameterlist() newnode.child_text_separator = '; ' signode += newnode signode += nodes.Text(' ') newnode = addnodes.desc_parameterlist() newnode.child_text_separator = '; ' signode += newnode stack = [signode[-1]] counters = [0, 0] for token in string.split(arglist, ';'): pieces = string.split(token, ':') name = pieces[0].strip() stack[-1] += addnodes.desc_parameter( name, name + " : " + pieces[1].strip()) if len(stack) == 1: counters[0] += 1 else: counters[1] += 1 if len(stack) != 1: raise ValueError if not counters[1]: fullname = '%s/%d' % (fullname, counters[0]) else: fullname = '%s/%d..%d' % (fullname, counters[0], sum(counters)) return fullname def _handle_type_signature(self, sig, signode): m = ada_type_sig_re.match(sig) if m is None: print("m did not match") raise ValueError name, value = m.groups() fullname = self._resolve_module_name(signode, '', name) # signode += addnodes.desc_parameterlist() # stack = [signode[-1]] # signode += addnodes.desc_type(name, name + " is " + value) signode += addnodes.desc_type(name, '') return fullname def handle_signature(self, sig, signode): if sig.startswith('function'): return self._handle_function_signature(sig, signode) elif sig.startswith('type'): return self._handle_type_signature(sig, signode) else: # sig.startswith('procedure'): return self._handle_procedure_signature(sig, signode) def _get_index_text(self, name): if self.objtype == 'function': return _('%s (Ada function)') % name elif self.objtype == 'procedure': return _('%s (Ada procedure)') % name elif self.objtype == 'type': return _('%s (Ada type)') % name else: return '' def add_target_and_index(self, name, sig, signode): pieces = string.split(name, '.') if name not in self.state.document.ids: signode['names'].append(name) signode['ids'].append(name) signode['first'] = (not self.names) self.state.document.note_explicit_target(signode) if self.objtype == 'function': finv = self.env.domaindata['ada']['functions'] fname, arity = name.split('/') if '..' in arity: first, last = map(int, arity.split('..')) else: first = last = int(arity) for arity_index in range(first, last + 1): if fname in finv and arity_index in finv[fname]: self.env.warn( self.env.docname, ('duplicate Ada function description' 'of %s, ') % name + 'other instance in ' + self.env.doc2path(finv[fname][arity_index][0]), self.lineno) arities = finv.setdefault(fname, {}) arities[arity_index] = (self.env.docname, name) if self.objtype == 'procedure': finv = self.env.domaindata['ada']['procedures'] fname, arity = name.split('/') if '..' in arity: first, last = map(int, arity.split('..')) else: first = last = int(arity) for arity_index in range(first, last + 1): if fname in finv and arity_index in finv[fname]: self.env.warn( self.env.docname, ('duplicate Ada procedure description' 'of %s, ') % name + 'other instance in ' + self.env.doc2path(finv[fname][arity_index][0]), self.lineno) arities = finv.setdefault(fname, {}) arities[arity_index] = (self.env.docname, name) else: oinv = self.env.domaindata['ada']['objects'] if name in oinv: self.env.warn( self.env.docname, 'duplicate Ada object description of %s, ' % name + 'other instance in ' + self.env.doc2path(oinv[name][0]), self.lineno) oinv[name] = (self.env.docname, self.objtype) indextext = self._get_index_text(name) if indextext: self.indexnode['entries'].append(('single', indextext, name, name)) plain_name = pieces[-1] indextext = self._get_index_text(plain_name) if indextext: self.indexnode['entries'].append( ('single', indextext, name, plain_name))