def register_spec_with_docutils(spec: specparser.Spec, default_domain: Optional[str]) -> Registry: """Register all of the definitions in the spec with docutils, overwriting the previous call to this function. This function should only be called once in the process lifecycle.""" from .legacy_guides import LegacyGuideDirective, LegacyGuideIndexDirective SPECIAL_DIRECTIVE_HANDLERS["guide"] = LegacyGuideDirective SPECIAL_DIRECTIVE_HANDLERS["guide-index"] = LegacyGuideIndexDirective builder = Registry.Builder() directives = list(spec.directive.items()) roles = list(spec.role.items()) # Define rstobjects for name, rst_object in spec.rstobject.items(): directive = rst_object.create_directive() directives.append((name, directive)) role = rst_object.create_role() roles.append((name, role)) for name, directive in directives: # Skip abstract base directives if name.startswith("_"): continue options: Dict[str, object] = { option_name: spec.get_validator(option) for option_name, option in directive.options.items() } base_class: Any = BaseDocutilsDirective # Tabs have special handling because of the need to support legacy syntax if name == "tabs": base_class = BaseTabsDirective elif name in SPECIAL_DIRECTIVE_HANDLERS: base_class = SPECIAL_DIRECTIVE_HANDLERS[name] DocutilsDirective = make_docutils_directive_handler( directive, base_class, name, options) builder.add_directive(name, DocutilsDirective) # reference tabs directive declaration as first step in registering tabs-* with docutils tabs_directive = spec.directive["tabs"] # Define tabsets for name in spec.tabs: tabs_base_class: Any = BaseTabsDirective tabs_name = "tabs-" + name # copy and modify the tabs directive to update its name to match the deprecated tabs-* naming convention modified_tabs_directive = dataclasses.replace(tabs_directive, name=tabs_name) tabs_options: Dict[str, object] = { option_name: spec.get_validator(option) for option_name, option in tabs_directive.options.items() } DocutilsDirective = make_docutils_directive_handler( modified_tabs_directive, tabs_base_class, "tabs", tabs_options) builder.add_directive(tabs_name, DocutilsDirective) # Docutils builtins builder.add_directive("unicode", docutils.parsers.rst.directives.misc.Unicode) builder.add_directive("replace", docutils.parsers.rst.directives.misc.Replace) # Define roles builder.add_role("", handle_role_null) for name, role_spec in roles: handler: Optional[RoleHandlerType] = None domain = role_spec.domain or "" if not role_spec.type or role_spec.type == specparser.PrimitiveRoleType.text: handler = TextRoleHandler(domain) elif isinstance(role_spec.type, specparser.LinkRoleType): handler = LinkRoleHandler(role_spec.type.link, role_spec.type.format) elif isinstance(role_spec.type, specparser.RefRoleType): handler = RefRoleHandler( role_spec.type.domain or domain, role_spec.type.name, role_spec.type.tag, role_spec.rstobject.type if role_spec.rstobject else specparser.TargetType.plain, role_spec.type.format, ) elif role_spec.type == specparser.PrimitiveRoleType.explicit_title: handler = ExplicitTitleRoleHandler(domain) if not handler: raise ValueError('Unknown role type "{}"'.format(role_spec.type)) builder.add_role(name, handler) return builder.build(default_domain)
def register_spec_with_docutils(spec: specparser.Spec) -> None: """Register all of the definitions in the spec with docutils.""" from .legacy_guides import LegacyGuideDirective, LegacyGuideIndexDirective directives = list(spec.directive.items()) roles = list(spec.role.items()) # Define rstobjects for name, rst_object in spec.rstobject.items(): directive = rst_object.create_directive() role = rst_object.create_role() directives.append((name, directive)) roles.append((name, role)) for name, directive in directives: # Skip abstract base directives if name.startswith("_"): continue # Tabs have special handling because of the need to support legacy syntax if name == "tabs" or name.startswith("tabs-"): docutils.parsers.rst.directives.register_directive(name, TabsDirective) continue options: Dict[str, object] = { option_name: spec.get_validator(option) for option_name, option in directive.options.items() } class DocutilsDirective(BaseDocutilsDirective): has_content = bool(directive.content_type) optional_arguments = 1 if directive.argument_type else 0 final_argument_whitespace = True option_spec = options new_name = ( "".join(e for e in name.title() if e.isalnum() or e == "_") + "Directive" ) DocutilsDirective.__name__ = DocutilsDirective.__qualname__ = new_name docutils.parsers.rst.directives.register_directive(name, DocutilsDirective) # Some directives currently have special handling docutils.parsers.rst.directives.register_directive("code-block", CodeDirective) docutils.parsers.rst.directives.register_directive("code", CodeDirective) docutils.parsers.rst.directives.register_directive("sourcecode", CodeDirective) docutils.parsers.rst.directives.register_directive("deprecated", VersionDirective) docutils.parsers.rst.directives.register_directive("versionadded", VersionDirective) docutils.parsers.rst.directives.register_directive( "versionchanged", VersionDirective ) docutils.parsers.rst.directives.register_directive("guide", LegacyGuideDirective) docutils.parsers.rst.directives.register_directive("card-group", CardGroupDirective) docutils.parsers.rst.directives.register_directive( "guide-index", LegacyGuideIndexDirective ) docutils.parsers.rst.directives.register_directive("toctree", TocTreeDirective) # Define roles for name, role_spec in roles: handler = None if not role_spec.type or role_spec.type == specparser.PrimitiveRoleType.text: handler = handle_role_text elif isinstance(role_spec.type, specparser.LinkRoleType): handler = LinkRoleHandler(role_spec.type.link) elif role_spec.type == specparser.PrimitiveRoleType.explicit_title: handler = handle_role_explicit_title if not handler: raise ValueError('Unknown role type "{}"'.format(role_spec.type)) docutils.parsers.rst.roles.register_local_role(name, handler)