def page_role(_role: str, rawtext: str, text: str, lineno: int, inliner: Inliner, options: dict = None, _content: dict = None): if options is None: options = {} set_classes(options) if "/" in text: parts = [escape(x) for x in text.rsplit("/", 1)] else: msg = inliner.reporter.error( "Page specification must be in the form <page_slug>/<text>", line=lineno) prb = inliner.problematic(text, rawtext, msg) return [prb], [msg] try: url = url_for("wiki.page", page=parts[0]) name = parts[1] html = f"""<a href="{url}">{name}</a>""" node = nodes.raw(html, html, format="html", **options) return [node], [] except Exception as e: msg = inliner.reporter.error(str(e), line=lineno) prb = inliner.problematic(text, rawtext, msg) return [prb], [msg]
def _parse(self, line): """ Parses a single line/string for inline rst statements, like strong, emphasis, literal, ... :param line: string to parse :return: nodes """ inline_parser = Inliner() inline_parser.init_customizations(self.doc_settings) result, message = inline_parser.parse(line, 0, self.doc_memo, self.dummy_doc) if message: raise SphinxNeedLayoutException(message) return result
def sphinx_state(local_app): """ Fixture which will provide a sphinx state for use in testing sphinx directives. Yields: :class:`docutils.parsers.rst.states.State`: A state for use in testing directive functionality. """ # Get the environment and decorate it with what sphinx may need for the # parsing. env = local_app.env env.temp_data["docname"] = "test" # A fake document name # Create a document and inliner object, to be perfectly honest not sure # exactly what these are or do, but needed to get the directive to run. document = new_document(__file__) document.settings.pep_references = 1 document.settings.rfc_references = 1 document.settings.env = env document.settings.tab_width = 4 inliner = Inliner() inliner.init_customizations(document.settings) # Create a state machine so that we can get a state to pass back. statemachine = RSTStateMachine(state_classes=state_classes, initial_state="Body") statemachine.input_lines = StringList([""] * 40) state = statemachine.get_state() state.document = document state.memo = Struct( inliner=inliner, language=en, title_styles=[], reporter=document.reporter, document=document, section_level=0, section_bubble_up_kludge=False, ) state.memo.reporter.get_source_and_line = statemachine.get_source_and_line # The environemnt isn't normally available on the state in sphinx, but it's # done here to make testing easier. state.env = env # Sphinx monkeypatches docutils when run. This is how it get's # monkeypatched so that the python directives and roles can be found with sphinx_domains(env): # Provide the state back to the test. yield state
def icon_role(_role: str, rawtext: str, text: str, lineno: int, inliner: Inliner, options: dict = None, _content: dict = None): if options is None: options = {} set_classes(options) if "/" in text: parts = [escape(x) for x in text.split("/")] else: msg = inliner.reporter.error( "Icon specification must be in the form <type>/<name>", line=lineno) prb = inliner.problematic(text, rawtext, msg) return [prb], [msg] if len(parts) != 2: msg = inliner.reporter.error( "Icon specification must be in the form <type>/<name>", line=lineno) prb = inliner.problematic(text, rawtext, msg) return [prb], [msg] else: if parts[0] == "light": weight = "fal" elif parts[0] == "regular": weight = "far" elif parts[0] == "solid": weight = "fas" elif parts[0] == "branding": weight = "fab" else: msg = inliner.reporter.error( "Icon type must be one of light, regular, solid or branding", line=lineno) prb = inliner.problematic(text, rawtext, msg) return [prb], [msg] html = f"""<i class="uk-icon fa-fw {weight} fa-{parts[1]}"></i>""" node = nodes.raw(html, html, format="html", **options) return [node], []
def parse_message_to_doctree(po, message): inliner = Inliner() settings = AttrDict({ 'character_level_inline_markup': False, 'pep_references': None, 'rfc_references': None }) inliner.init_customizations(settings) document = new_document(None) document.settings.syntax_highlight = 'long' stream = StringIO() reporter = Reporter(po.file, report_level=Reporter.WARNING_LEVEL, halt_level=Reporter.SEVERE_LEVEL, stream=stream) memo = Struct(document=document, reporter=reporter, language=None, inliner=inliner) doctree, errors = inliner.parse(message, po.current_index, memo, None) warning = stream.getvalue() return doctree, errors, warning
def fcicon_role(name: str, rawtext: str, text: str, lineno: int, inliner: Inliner, options: dict = {}, content: List[str] = []): """FreeCAD Icon role function. Returns 2 part tuple containing list of nodes to insert into the document and a list of system messages. Both are allowed to be empty. For additional information on role functions, see: * https://docutils.readthedocs.io/en/sphinx-docs/howto/rst-roles.html * https://doughellmann.com/blog/2010/05/09/defining-custom-roles-in-sphinx/ :param name: The role name used in the document. :param rawtext: The entire markup snippet, with role. :param text: The text marked with the role. :param lineno: The line number where rawtext appears in the input. :param inliner: The inliner instance that called us. :param options: Directive options for customization. :param content: The directive content for customization. """ try: pattern = re.compile('([\w\s]+) \((sm|md|lg)\) \<(.*\.\w+)\>') result = pattern.search(text) if not result or len(result.groups()) != 3: raise ValueError alt, size, filename = result.groups() except ValueError: msg = inliner.reporter.error( 'FreeCAD Icon must include alt, size (sm, md, or lg), and filename (e.g. :fcicon:`My Icon Alt (md) <MyIcon.svg>`); ' '"%s" is invalid.' % text, line=lineno) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] app = inliner.document.settings.env.app try: freecad_icon_directory = app.config.freecad_icon_directory if not freecad_icon_directory: raise AttributeError except AttributeError: raise ValueError( 'freecad_icon_directory configuration value is not set') image = make_image_node(freecad_icon_directory, alt, size, filename) return [image], []
def nxt_hint_role_fn(_: str, rawtext: str, text: str, lineno: int, inliner: Inliner, *args: Any) -> Tuple[List[Node], List[system_message]]: """The nxt_hint role handler for inline text outside code blocks.""" node = nxt_hint() groups = re.search(NXT_HINT_REGEX, rawtext.replace('\n', ' ').replace('\r', '')) try: node.term, node.tip = groups.group(1), groups.group(2) except IndexError: msg = inliner.reporter.error( f'Inline term "{text}" is invalid.', line=lineno) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] return [node], []
def rolefunc( role_name: str, # e.g. "role" rawtext: str, # e.g. ":role:`text`" text: str, # e.g. "text" lineno: int, inliner: Inliner, options: Dict[str, Any] = None, content: List[str] = None, ) -> Tuple[List[Node], List[str]]: """ Attempt to implemented substitutions inside inline markup. This is not directly supported: https://sourceforge.net/p/docutils/feature-requests/53/ Role functions: see http://docutils.sourceforge.net/docs/howto/rst-roles.html See also: https://github.com/sphinx-doc/sphinx/issues/2173 Returns the tuple (nodes, messages). Search docutils for "role_fn" to see how this function will be called. """ options = options or {} # type: Dict[str, Any] content = content or [] # type: List[str] log.debug("rolefunc() called with role_name={rn!r}, rawtext={rt!r}, " "text={t!r}, lineno={ln}, inliner={i!r}, " "options={o!r}, content={c!r}".format( rn=role_name, rt=rawtext, t=text, ln=lineno, i=inliner, o=options, c=content, )) parsed_nodes, parsed_msgs = inliner.parse( text=text, lineno=0, memo=inliner, parent=None) # type: Tuple[List[Node], List[str]] top_node = nodes.inline( # was nodes.inline text="", refid=css_class, **options) # type: Element top_node["classes"].append( css_class) # see deprecated Element.set_class # noqa top_node += parsed_nodes # adds children to this_node; see Element return [top_node], []
def _role_annot( name: str, rawtext: str, text: str, lineno: int, inliner: Inliner, options: Dict[str, Any] = {}, content: Sequence[str] = (), # *, # https://github.com/ambv/black/issues/613 additional_classes: Iterable[str] = (), ) -> Tuple[List[Node], List[SystemMessage]]: options = options.copy() set_classes(options) if additional_classes: options["classes"] = options.get("classes", []).copy() options["classes"].extend(additional_classes) memo = Struct(document=inliner.document, reporter=inliner.reporter, language=inliner.language) node = nodes.inline(unescape(rawtext), "", **options) children, messages = inliner.parse(_unescape(text), lineno, memo, node) node.extend(children) return [node], messages
def reset(self, document, parent, level): """Reset the state of state machine. After reset, self and self.state can be used to passed to docutils.parsers.rst.Directive.run Parameters ---------- document: docutils document Current document of the node. parent: parent node Parent node that will be used to interpret role and directives. level: int Current section level. """ self.language = languages.get_language( document.settings.language_code) # setup memo self.memo.document = document self.memo.reporter = document.reporter self.memo.language = self.language self.memo.section_level = level # setup inliner if self.memo.inliner is None: self.memo.inliner = Inliner() self.memo.inliner.init_customizations(document.settings) inliner = self.memo.inliner inliner.reporter = document.reporter inliner.document = document inliner.language = self.language inliner.parent = parent # setup self self.document = document self.reporter = self.memo.reporter self.node = parent self.state.runtime_init() self.input_lines = document['source']
def indexmarkup_role( typ: str, rawtext: str, text: str, lineno: int, inliner: Inliner, options: Dict = {}, content: List[str] = []) -> Tuple[List[Node], List[system_message]]: """Role for PEP/RFC references that generate an index entry.""" warnings.warn( 'indexmarkup_role() is deprecated. Please use PEP or RFC class instead.', RemovedInSphinx40Warning, stacklevel=2) env = inliner.document.settings.env if not typ: assert env.temp_data['default_role'] typ = env.temp_data['default_role'].lower() else: typ = typ.lower() has_explicit_title, title, target = split_explicit_title(text) title = utils.unescape(title) target = utils.unescape(target) targetid = 'index-%s' % env.new_serialno('index') indexnode = addnodes.index() targetnode = nodes.target('', '', ids=[targetid]) inliner.document.note_explicit_target(targetnode) if typ == 'pep': indexnode['entries'] = [ ('single', _('Python Enhancement Proposals; PEP %s') % target, targetid, '', None) ] anchor = '' anchorindex = target.find('#') if anchorindex > 0: target, anchor = target[:anchorindex], target[anchorindex:] if not has_explicit_title: title = "PEP " + utils.unescape(title) try: pepnum = int(target) except ValueError: msg = inliner.reporter.error('invalid PEP number %s' % target, line=lineno) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] ref = inliner.document.settings.pep_base_url + 'pep-%04d' % pepnum sn = nodes.strong(title, title) rn = nodes.reference('', '', internal=False, refuri=ref + anchor, classes=[typ]) rn += sn return [indexnode, targetnode, rn], [] elif typ == 'rfc': indexnode['entries'] = [('single', 'RFC; RFC %s' % target, targetid, '', None)] anchor = '' anchorindex = target.find('#') if anchorindex > 0: target, anchor = target[:anchorindex], target[anchorindex:] if not has_explicit_title: title = "RFC " + utils.unescape(title) try: rfcnum = int(target) except ValueError: msg = inliner.reporter.error('invalid RFC number %s' % target, line=lineno) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] ref = inliner.document.settings.rfc_base_url + inliner.rfc_url % rfcnum sn = nodes.strong(title, title) rn = nodes.reference('', '', internal=False, refuri=ref + anchor, classes=[typ]) rn += sn return [indexnode, targetnode, rn], [] else: raise ValueError('unknown role type: %s' % typ)