def get_admonition(self, token, name, classes, title): line = token.map[0] if name == "admonition": # parse title node = nodes.admonition(title, classes=classes[1:].split(",")) state_machine = MockStateMachine(self, line) state = MockState(self, state_machine, line) textnodes, messages = state.inline_text(title, line) title_node = nodes.title(title, "", *textnodes) self.add_line_and_source_path(title_node, token) node += title_node node += messages return node node_cls = STD_ADMONITIONS.get(name, None) if node_cls is None: self.current_node.append( self.reporter.warning( f"admonition name not recognised, defaulting to note: {name}", line=line, )) node_cls = nodes.note return node_cls(title, classes=classes[1:].split(","))
def get_admonition(self, token, name, classes, title): """Create an admonition node. """ line = token.map[0] if name == "admonition": # parse title node = nodes.admonition(title, classes=classes[1:].split(",")) state_machine = MockStateMachine(self, line) state = MockState(self, state_machine, line) textnodes, messages = state.inline_text(title, line) title_node = nodes.title(title, "", *textnodes) self.add_line_and_source_path(title_node, token) node += title_node node += messages return node node_cls = STD_ADMONITIONS.get(name) return node_cls(title, classes=classes[1:].split(","))
def dict_to_fm_field_list(self, data: Dict[str, Any], language_code: str, line: int = 0) -> nodes.field_list: """Render each key/val pair as a docutils ``field_node``. Bibliographic keys below will be parsed as Markdown, all others will be left as literal text. The field list should be at the start of the document, and will then be converted to a `docinfo` node during the `docutils.docutils.transforms.frontmatter.DocInfo` transform (priority 340), and bibliographic keys (or their translation) will be converted to nodes:: {'author': docutils.nodes.author, 'authors': docutils.nodes.authors, 'organization': docutils.nodes.organization, 'address': docutils.nodes.address, 'contact': docutils.nodes.contact, 'version': docutils.nodes.version, 'revision': docutils.nodes.revision, 'status': docutils.nodes.status, 'date': docutils.nodes.date, 'copyright': docutils.nodes.copyright, 'dedication': docutils.nodes.topic, 'abstract': docutils.nodes.topic} Also, the 'dedication' and 'abstract' will be placed outside the `docinfo`, and so will always be shown in the document. If using sphinx, this `docinfo` node will later be extracted from the AST, by the `DoctreeReadEvent` transform (priority 880), calling `MetadataCollector.process_doc`. In this case keys and values will be converted to strings and stored in `app.env.metadata[app.env.docname]` See https://www.sphinx-doc.org/en/master/usage/restructuredtext/field-lists.html for docinfo fields used by sphinx. """ field_list = nodes.field_list() bibliofields = get_language(language_code).bibliographic_fields state_machine = MockStateMachine(self, line) state = MockState(self, state_machine, line) for key, value in data.items(): if not isinstance(value, (str, int, float, date, datetime)): value = json.dumps(value) value = str(value) if key in bibliofields: para_nodes, _ = state.inline_text(value, line) body_children = [nodes.paragraph("", "", *para_nodes)] else: body_children = [nodes.Text(value, value)] field_node = nodes.field() field_node.source = value field_node += nodes.field_name(key, "", nodes.Text(key, key)) field_node += nodes.field_body(value, *body_children) field_list += field_node return field_list
def render_substitution(self, token, inline: bool): """Substitutions are rendered by: 1. Combining global substitutions with front-matter substitutions to create a variable context (front-matter takes priority) 2. Add the sphinx `env` to the variable context (if available) 3. Create the string content with Jinja2 (passing it the variable context) 4. If the substitution is inline and not a directive, parse to nodes ignoring block syntaxes (like lists or block-quotes), otherwise parse to nodes with all syntax rules. """ position = token.map[0] # front-matter substitutions take priority over config ones variable_context = { **self.config.get("myst_substitutions", {}), **getattr(self.document, "fm_substitutions", {}), } try: variable_context["env"] = self.document.settings.env except AttributeError: pass # if not sphinx renderer # fail on undefined variables env = jinja2.Environment(undefined=jinja2.StrictUndefined) # try rendering try: rendered = env.from_string(f"{{{{{token.content}}}}}").render( variable_context) except Exception as error: error_msg = self.reporter.error( f"Substitution error:{error.__class__.__name__}: {error}", line=position, ) self.current_node += [error_msg] return # handle circular references ast = env.parse(f"{{{{{token.content}}}}}") references = { n.name for n in ast.find_all(jinja2.nodes.Name) if n.name != "env" } self.document.sub_references = getattr(self.document, "sub_references", set()) cyclic = references.intersection(self.document.sub_references) if cyclic: error_msg = self.reporter.error( f"circular substitution reference: {cyclic}", line=position, ) self.current_node += [error_msg] return # parse rendered text state_machine = MockStateMachine(self, position) state = MockState(self, state_machine, position) # TODO improve error reporting; # at present, for a multi-line substitution, # an error may point to a line lower than the substitution # should it point to the source of the substitution? # or the error message should at least indicate that its a substitution # we record used references before nested parsing, then remove them after self.document.sub_references.update(references) try: if inline and not REGEX_DIRECTIVE_START.match(rendered): sub_nodes, _ = state.inline_text(rendered, position) else: base_node = nodes.Element() state.nested_parse( StringList(rendered.splitlines(), self.document["source"]), 0, base_node, ) sub_nodes = base_node.children finally: self.document.sub_references.difference_update(references) self.current_node.extend(sub_nodes)