def parse(self, file_content, package_name): u""" """ all_fields = [] fields = [] pre_comments = StringList() for item in file_content.xitems(): # (source, offset, value) line = item[2].strip() if line and not [c for c in line if not c == '-']: all_fields.append(fields) fields = [] elif line == '' or line[0] == '#': if line: line = line[1:] if fields: fields[-1].post_comments.append(line, source=item[0], offset=item[1]) pre_comments.append(line, source=item[0], offset=item[1]) else: new_field = ROSField(line, source=item[0], offset=item[1], pre_comments=pre_comments, package_name=package_name) # if sucessfully parsed if new_field.name: fields.append(new_field) pre_comments = StringList() else: # todo print("?? <%s>" % line) all_fields.append(fields) return all_fields
def make_docfields(self, field_group, field_comment_option): docfields = StringList([u'']) for field in field_group.fields: field_type = self.constant_name if field.value else self.field_name name = field.name + field.size desc = field.get_description(field_comment_option) if len(desc) == 0: docfields.append(u':{0} {1}:'.format(field_type, name), source=field.source, offset=field.offset) elif len(desc) == 1: docfields.append(u':{0} {1}: {2}'.format(field_type, name, desc[0].strip()), source=desc.source(0), offset=desc.offset(0)) elif len(desc) > 1: if 'quote' in field_comment_option: align_strings(desc, ' | ') else: align_strings(desc, ' ') docfields.append(u':{0} {1}: {2}'.format(field_type, name, desc[0]), source=desc.source(0), offset=desc.offset(0)) docfields.extend(desc[1:]) docfields.append(u':{0}-{1} {2}: {3}'.format(field_type, TYPE_SUFFIX, name, field.type), source=field.source, offset=field.offset) if field.value: docfields.append(u':{0}-{1} {2}: {3}'.format(field_type, VALUE_SUFFIX, name, field.value), source=field.source, offset=field.offset) return docfields
class ROSFieldGroup(object): u"""A group of fields and constants. """ def __init__(self, package_name): self.package_name = package_name self.description = StringList() self.fields = [] def append(self, line, source, offset): if line == '' or line[0] == '#': if line: line = line[1:] if self.fields: self.fields[-1].post_comments.append(line, source=source, offset=offset) else: self.description.append(line, source=source, offset=offset) else: if self.fields: pre_comments = self.fields[-1].post_comments else: pre_comments = self.description new_field = ROSField(line, source=source, offset=offset, pre_comments=pre_comments, package_name=self.package_name) # if sucessfully parsed if new_field.name: self.fields.append(new_field) self.pre_comments = StringList() else: # todo print("?? <%s>" % line)
def read(self) -> StringList: # type: ignore inputstring = super().read() lines = string2lines(inputstring, convert_whitespace=True) content = StringList() for lineno, line in enumerate(lines): content.append(line, self.source_path, lineno) prepend_prolog(content, self.env.config.rst_prolog) append_epilog(content, self.env.config.rst_epilog) return content
def append_epilog(content: StringList, epilog: str) -> None: """Append a string to content body as epilog.""" if epilog: if 0 < len(content): source, lineno = content.info(-1) else: source = '<generated>' lineno = 0 content.append('', source, lineno + 1) for lineno, line in enumerate(epilog.splitlines()): content.append(line, '<rst_epilog>', lineno)
def read(self): # type: ignore # type: () -> StringList inputstring = super().read() lines = string2lines(inputstring, convert_whitespace=True) content = StringList() for lineno, line in enumerate(lines): content.append(line, self.source_path, lineno) prepend_prolog(content, self.env.config.rst_prolog) append_epilog(content, self.env.config.rst_epilog) return content
def read(self): # type: () -> StringList inputstring = SphinxBaseFileInput.read(self) lines = string2lines(inputstring, convert_whitespace=True) content = StringList() for lineno, line in enumerate(lines): content.append(line, self.source_path, lineno) if self.env.config.rst_prolog: self.prepend_prolog(content, self.env.config.rst_prolog) if self.env.config.rst_epilog: self.append_epilog(content, self.env.config.rst_epilog) return content
def aux(name): module = importlib.import_module(name) contents = StringList() contents.append(f'.. automodule:: {name}', sourcename) if hasattr(module, '__all__'): module_attrs = [ attr_name for attr_name in module.__all__ if getattr(module, attr_name).__module__ == name ] if module_attrs: contents.append(f" :members: {', '.join(module_attrs)}", sourcename) else: contents.append(' :members:', sourcename) contents.append('', sourcename) node = docutils.nodes.section() nested_parse_with_titles(self.state, contents, node) # If this module defines any sections, then submodules should go # inside of the last one. section = node for child in node.children: if isinstance(child, docutils.nodes.section): section = child if hasattr(module, '__path__'): submodules = sorted(module_info.name for module_info in pkgutil.iter_modules( module.__path__, prefix=name + '.')) for submodule in submodules: section.extend(aux(submodule)) return node.children
def read(self): # type: ignore # type: () -> StringList warnings.warn('SphinxRSTFileInput is deprecated.', RemovedInSphinx30Warning, stacklevel=2) inputstring = super(SphinxRSTFileInput, self).read() lines = string2lines(inputstring, convert_whitespace=True) content = StringList() for lineno, line in enumerate(lines): content.append(line, self.source_path, lineno) prepend_prolog(content, self.env.config.rst_prolog) append_epilog(content, self.env.config.rst_epilog) return content
def append_row(*column_texts: str) -> None: row = nodes.row('') source, line = self.state_machine.get_source_and_line() for text in column_texts: node = nodes.paragraph('') vl = StringList() vl.append(text, '%s:%d:<autosummary>' % (source, line)) with switch_source_input(self.state, vl): self.state.nested_parse(vl, 0, node) try: if isinstance(node[0], nodes.paragraph): node = node[0] except IndexError: pass row.append(nodes.entry('', node)) body.append(row)
def read(self): # type: () -> StringList warnings.warn('SphinxRSTFileInput is deprecated.', RemovedInSphinx30Warning, stacklevel=2) inputstring = super(SphinxRSTFileInput, self).read() lines = string2lines(inputstring, convert_whitespace=True) content = StringList() for lineno, line in enumerate(lines): content.append(line, self.source_path, lineno) prepend_prolog(content, self.env.config.rst_prolog) append_epilog(content, self.env.config.rst_epilog) return content
def append_row(*column_texts): # type: (str) -> None row = nodes.row('') source, line = self.state_machine.get_source_and_line() for text in column_texts: node = nodes.paragraph('') vl = StringList() vl.append(text, '%s:%d:<autosummary>' % (source, line)) with switch_source_input(self.state, vl): self.state.nested_parse(vl, 0, node) try: if isinstance(node[0], nodes.paragraph): node = node[0] except IndexError: pass row.append(nodes.entry('', node)) body.append(row)
def to_content(self): # type: () -> StringList content = StringList() content.append(ViewList([u".. change:: {}".format(self.category)])) content.append(ViewList([u" :tags: {}".format(",".join(self.tags)) ])) content.append(ViewList([u""])) content.append(ViewList([u" {}".format(self.message)])) return content
def _build_functions(self): """Imports the dict and builds the output for the functions. This is what determines aliases and performs sorting. Calls :func:`build_function_directive` for each function, then renders the list of reStructuredText to nodes. The list of sorted names is stored for use by :meth:`_build_table`. :return: A list of rendered nodes. """ map_name = self.arguments[0] mapping = import_object(map_name) grouped = {} # reverse the mapping to get a list of aliases for each function for key, value in mapping.items(): grouped.setdefault(value, []).append(key) # store the function names for use by _build_table self.funcs = funcs = [] compare_ops = {"eq", "ge", "gt", "le", "lt", "ne"} for func, names in grouped.items(): # use the longest alias as the canonical name names.sort(key=len) # adjust for special cases names.sort(key=lambda x: x in compare_ops) name = names.pop() funcs.append((name, names, func)) funcs.sort() result = StringList() # generate and collect markup for name, aliases, func in funcs: for item in build_function_directive(name, aliases, func): result.append(item, "<jinja>") # parse the generated markup into nodes node = nodes.Element() self.state.nested_parse(result, self.content_offset, node) return node.children
def _parse_string(self, s: str): """Adapted from https://github.com/sphinx-doc/sphinx/blob/5559e5af1ff6f5fc2dc706 79bdd6dc089cfff388/sphinx/ext/autosummary/__init__.py#L425.""" node = nodes.paragraph("") vl = StringList() source, line = self.state_machine.get_source_and_line() vl.append(s, f"{source}:{line}:<probnum-config-options>") with switch_source_input(self.state, vl): self.state.nested_parse(vl, 0, node) try: if isinstance(node[0], nodes.paragraph): node = node[0] except IndexError: pass return node
def update_content(self): package_name = self.arguments[0] package = self.find_package(package_name) if not package: return None self.env.note_dependency(self.env.relfn2path(package.filename)[0]) content = StringList() for attr in self.env.config.ros_package_attrs: if attr in self.env.config.ros_package_attrs_formatter: formatter = self.env.config.ros_package_attrs_formatter[attr] elif attr.endswith('_depends'): formatter = 'depend_formatter' else: formatter = self.attr_formatters.get(attr, 'default_formatter') field = format_attr(package, attr, formatter) if field: content.extend(field) content.items = [(source, 0) for source, line in content.items] if len(content) > 0: content.append(StringList([u''])) return content + self.content
def run(self): # Create and error-check the range. try: _range = range(*[int(arg) for arg in self.arguments]) except Exception as e: raise self.error("Invalid arguments to range: {}".format(e)) # Run the for loop over all this directive's content. Docutils expects a StringList, so use that. loop_content = StringList() for loop_index in _range: # Loop over each line of the content, only replacing lines in the content of this directive. for source, offset, value in self.content.xitems(): loop_content.append( value.format(loop_index, *self.arguments), source, offset ) # Add an additional newline between loop iterations. loop_content.append("\n", "for-loop", 0) # Parse the resulting content and return it. node = nodes.container() self.state.nested_parse(loop_content, self.content_offset, node) return [node]
def get_table(self, items): """Return a definition list rather than a table.""" # See original implementation: # https://github.com/sphinx-doc/sphinx/blob/master/sphinx/ext/autosummary/__init__.py source, line = self.state_machine.get_source_and_line() src = "{source}:{line}:<dlistsummary>".format(source=source, line=line) # We're going to build out a StringList by formatting a definition list and then # parsing it at the end. Definition list syntax: # # **First Item** # First item description, indented by four spaces. # **Second Item** # Second item description, indented by four spaces. s_list = StringList() for name, signature, summary_string, real_name in items: # Add the definition item. s_list.append("**{name}**\n".format(name=name), src) # Add the autosummary description for this demo, including a link to the # full demonstration. This is the definition of the item. summary_string += " :any:`Go to demo ↱ <{real_name}>`\n".format( real_name=real_name) s_list.append(" " + summary_string, src) # Now that we have a fully populated StringList, let Sphinx handle the dirty # work of evaluating the rst as actual nodes. node = definition_list("") self.state.nested_parse(s_list, 0, node) try: if isinstance(node[0], definition_list): node = node[0] except IndexError: pass return [node]
def run(self): sl = StringList(["{% if session.get('print_mode', True) %}"]) sl.append(StringList([""])) sl += self.content sl.append(StringList([""])) sl.append(StringList(["{% endif %}"])) return Container(content=sl, arguments=[], lineno=self.lineno, block_text=self.block_text, content_offset=self.content_offset, name="container", options=self.options, state=self.state, state_machine=self.state_machine).run()
def get_table(self, items: List[Tuple[str, str, str, str, str]]) -> List[Node]: """Generate a proper list of table nodes for autosummary:: directive. *items* is a list produced by :meth:`get_items`. """ has_config_type = any([item[-1] is not None for item in items]) if has_config_type: n_cols = 3 else: n_cols = 2 table_spec = addnodes.tabular_col_spec() table_spec["spec"] = r"\X{1}{2}\X{1}{2}" table = autosummary_table("") real_table = nodes.table("", classes=["longtable"]) table.append(real_table) group = nodes.tgroup("", cols=n_cols) real_table.append(group) group.append(nodes.colspec("", colwidth=10)) if has_config_type: group.append(nodes.colspec("", colwidth=10)) group.append(nodes.colspec("", colwidth=90)) head = nodes.thead("") cols = ["Class/method name", "type", "Summary"] if not has_config_type: del cols[1] row = nodes.row("") source, line = self.state_machine.get_source_and_line() for text in cols: node = nodes.paragraph("") vl = StringList() vl.append(text, "%s:%d:<autosummary>" % (source, line)) with switch_source_input(self.state, vl): self.state.nested_parse(vl, 0, node) try: if isinstance(node[0], nodes.paragraph): node = node[0] except IndexError: pass row.append(nodes.entry("", node)) head.append(row) group.append(head) body = nodes.tbody("") group.append(body) def append_row(*column_texts: str) -> None: row = nodes.row("") source, line = self.state_machine.get_source_and_line() for text in column_texts: node = nodes.paragraph("") vl = StringList() vl.append(text, "%s:%d:<autosummary>" % (source, line)) with switch_source_input(self.state, vl): self.state.nested_parse(vl, 0, node) try: if isinstance(node[0], nodes.paragraph): node = node[0] except IndexError: pass row.append(nodes.entry("", node)) body.append(row) for name, sig, summary, real_name, config_type in items: qualifier = "obj" if "nosignatures" not in self.options: col1 = ":%s:`%s <%s>`\\ %s" % ( qualifier, name, real_name, rst.escape(sig), ) else: col1 = ":%s:`%s <%s>`" % (qualifier, name, real_name) col2 = summary if has_config_type: col3 = config_type if config_type else "" append_row(col1, col3, col2) else: append_row(col1, col2) return [table_spec, table]
def append_epilog(content: StringList, epilog: str) -> None: """Append a string to content body as epilog.""" if epilog: content.append('', '<generated>', 0) for lineno, line in enumerate(epilog.splitlines()): content.append(line, '<rst_epilog>', lineno)
class DBusDoc: def __init__(self, sphinx_directive, dbusfile): self._cur_doc = None self._sphinx_directive = sphinx_directive self._dbusfile = dbusfile self._top_node = nodes.section() self.result = StringList() self.indent = "" def add_line(self, line: str, *lineno: int) -> None: """Append one line of generated reST to the output.""" if line.strip(): # not a blank line self.result.append(self.indent + line, self._dbusfile, *lineno) else: self.result.append("", self._dbusfile, *lineno) def add_method(self, method): self.add_line(f".. dbus:method:: {method.name}") self.add_line("") self.indent += " " for arg in method.in_args: self.add_line(f":arg {arg.signature} {arg.name}: {arg.doc_string}") for arg in method.out_args: self.add_line(f":ret {arg.signature} {arg.name}: {arg.doc_string}") self.add_line("") for line in prepare_docstring("\n" + method.doc_string): self.add_line(line) self.indent = self.indent[:-3] def add_signal(self, signal): self.add_line(f".. dbus:signal:: {signal.name}") self.add_line("") self.indent += " " for arg in signal.args: self.add_line(f":arg {arg.signature} {arg.name}: {arg.doc_string}") self.add_line("") for line in prepare_docstring("\n" + signal.doc_string): self.add_line(line) self.indent = self.indent[:-3] def add_property(self, prop): self.add_line(f".. dbus:property:: {prop.name}") self.indent += " " self.add_line(f":type: {prop.signature}") access = { "read": "readonly", "write": "writeonly", "readwrite": "readwrite" }[prop.access] self.add_line(f":{access}:") if prop.emits_changed_signal: self.add_line(f":emits-changed: yes") self.add_line("") for line in prepare_docstring("\n" + prop.doc_string): self.add_line(line) self.indent = self.indent[:-3] def add_interface(self, iface): self.add_line(f".. dbus:interface:: {iface.name}") self.add_line("") self.indent += " " for line in prepare_docstring("\n" + iface.doc_string): self.add_line(line) for method in iface.methods: self.add_method(method) for sig in iface.signals: self.add_signal(sig) for prop in iface.properties: self.add_property(prop) self.indent = self.indent[:-3]
def run(self): config = self.state.document.settings.env.config # Read enabled builders; Defaults to None if self.arguments: chosen_builders = choose_builders(self.arguments) else: chosen_builders = [] # Enable 'http' language for http part self.arguments = ['http'] # split the request and optional response in the content. # The separator is two empty lines followed by a line starting with # 'HTTP/' request_content = StringList() response_content = None emptylines_count = 0 in_response = False for i, line in enumerate(self.content): source = self.content.source(i) if in_response: response_content.append(line, source) else: if emptylines_count >= 2 and line.startswith('HTTP/'): in_response = True response_content = StringList() response_content.append(line, source) elif line == '': emptylines_count += 1 else: request_content.extend( StringList([''] * emptylines_count, source)) emptylines_count = 0 request_content.append(line, source) # Load optional external request cwd = os.path.dirname(self.state.document.current_source) if 'request' in self.options: request = utils.resolve_path(self.options['request'], cwd) with open(request) as fp: request_content = StringList( list(map(str.rstrip, fp.readlines())), request) # Load optional external response if 'response' in self.options: response = utils.resolve_path(self.options['response'], cwd) with open(response) as fp: response_content = StringList( list(map(str.rstrip, fp.readlines())), response) # reset the content to just the request self.content = request_content # Wrap and render main directive as 'http-example-http' klass = 'http-example-http' container = nodes.container('', classes=[klass]) container.append(nodes.caption('', 'http')) container.extend(super(HTTPExample, self).run()) # Init result node list result = [container] # Append builder responses for name in chosen_builders: raw = ('\r\n'.join(request_content)).encode('utf-8') request = parsers.parse_request(raw, config.httpexmpl_scheme) builder_, language = AVAILABLE_BUILDERS[name] command = builder_(request) content = StringList([command], request_content.source(0)) options = self.options.copy() options.pop('name', None) options.pop('caption', None) block = CodeBlock( 'code-block', [language], options, content, self.lineno, self.content_offset, self.block_text, self.state, self.state_machine ) # Wrap and render main directive as 'http-example-{name}' klass = 'http-example-{}'.format(name) container = nodes.container('', classes=[klass]) container.append(nodes.caption('', name)) container.extend(block.run()) # Append to result nodes result.append(container) # Append optional response if response_content: options = self.options.copy() options.pop('name', None) options.pop('caption', None) block = CodeBlock( 'code-block', ['http'], options, response_content, self.lineno, self.content_offset, self.block_text, self.state, self.state_machine ) # Wrap and render main directive as 'http-example-response' klass = 'http-example-response' container = nodes.container('', classes=[klass]) container.append(nodes.caption('', 'response')) container.extend(block.run()) # Append to result nodes result.append(container) # Final wrap container_node = nodes.container('', classes=['http-example']) container_node.extend(result) return [container_node]
def run(self): config = self.state.document.settings.env.config # Read enabled builders; Defaults to None chosen_builders = choose_builders(self.arguments) # Enable 'http' language for http part self.arguments = ['http'] # process 'query' reST fields if self.content: raw = ('\r\n'.join(self.content)).encode('utf-8') request = parsers.parse_request(raw) params, _ = request.extract_fields('query') params = [(p[1], p[2]) for p in params] new_path = utils.add_url_params(request.path, params) self.content[0] = ' '.join( [request.command, new_path, request.request_version]) # split the request and optional response in the content. # The separator is two empty lines followed by a line starting with # 'HTTP/' or 'HTTP ' request_content = StringList() request_content_no_fields = StringList() response_content = None emptylines_count = 0 in_response = False is_field = r':({}) (.+): (.+)'.format('|'.join(AVAILABLE_FIELDS)) for i, line in enumerate(self.content): source = self.content.source(i) if in_response: response_content.append(line, source) else: if emptylines_count >= 2 and \ (line.startswith('HTTP/') or line.startswith('HTTP ')): in_response = True response_content = StringList() response_content.append(line, source) elif line == '': emptylines_count += 1 else: request_content.extend( StringList([''] * emptylines_count, source)) request_content.append(line, source) if not re.match(is_field, line): request_content_no_fields.extend( StringList([''] * emptylines_count, source)) request_content_no_fields.append(line, source) emptylines_count = 0 # Load optional external request cwd = os.path.dirname(self.state.document.current_source) if 'request' in self.options: request = utils.resolve_path(self.options['request'], cwd) with open(request) as fp: request_content = request_content_no_fields = StringList( list(map(str.rstrip, fp.readlines())), request) # Load optional external response if 'response' in self.options: response = utils.resolve_path(self.options['response'], cwd) with open(response) as fp: response_content = StringList( list(map(str.rstrip, fp.readlines())), response) # reset the content to the request, stripped of the reST fields self.content = request_content_no_fields # Wrap and render main directive as 'http-example-http' klass = 'http-example-http' container = nodes.container('', classes=[klass]) container.append(nodes.caption('', 'http')) container.extend(super(HTTPExample, self).run()) # Init result node list result = [container] # reset the content to just the request self.content = request_content # Append builder responses if request_content_no_fields: raw = ('\r\n'.join(request_content_no_fields)).encode('utf-8') for name in chosen_builders: request = parsers.parse_request(raw, config.httpexample_scheme) builder_, language = AVAILABLE_BUILDERS[name] command = builder_(request) content = StringList([command], request_content_no_fields.source(0)) options = self.options.copy() options.pop('name', None) options.pop('caption', None) block = CodeBlock('code-block', [language], options, content, self.lineno, self.content_offset, self.block_text, self.state, self.state_machine) # Wrap and render main directive as 'http-example-{name}' klass = 'http-example-{}'.format(name) container = nodes.container('', classes=[klass]) container.append(nodes.caption('', name)) container.extend(block.run()) # Append to result nodes result.append(container) # Append optional response if response_content: options = self.options.copy() options.pop('name', None) options.pop('caption', None) block = CodeBlock('code-block', ['http'], options, response_content, self.lineno, self.content_offset, self.block_text, self.state, self.state_machine) # Wrap and render main directive as 'http-example-response' klass = 'http-example-response' container = nodes.container('', classes=[klass]) container.append(nodes.caption('', 'response')) container.extend(block.run()) # Append to result nodes result.append(container) # Final wrap container_node = nodes.container('', classes=['http-example']) container_node.extend(result) return [container_node]
def append_epilog(self, text: StringList, epilog: str) -> None: # append a blank line and rst_epilog text.append('', '<generated>', 0) for lineno, line in enumerate(epilog.splitlines()): text.append(line, '<rst_epilog>', lineno)
def run(self): if self.content: sl = StringList([ "{% if ((logged_in is not none) and (logged_in['right'] == 'admin')) %}" ]) sl.append(StringList([""])) sl.append(StringList([".. container:: framed"])) sl.append(StringList([""])) new_content = StringList([]) for item in self.content: new_content.append(StringList([" %s" % item])) sl.append(new_content) sl.append(StringList([""])) sl.append(StringList(["{% endif %}"])) self.content = sl return Container(content=self.content, arguments=[], lineno=self.lineno, block_text=self.block_text, content_offset=self.content_offset, name="container", options=self.options, state=self.state, state_machine=self.state_machine).run()