def test_ast_find(): text = ( '<div class="a"><div class="c"><x/><y>z</y><div class="a b"></div></div></div>' ) ast = tokenize_html(text) found = list(ast.find("div", classes=["a"])) assert [e.attrs.classes for e in found] == [["a"], ["a", "b"]]
def test_render_overrides(): text = "<div><abc></abc></div>" ast = tokenize_html(text) def _render_abc(element, *args, **kwargs): return "hallo" output = ast.render(tag_overrides={"abc": _render_abc}) assert output == "<div>hallo</div>"
def html_to_nodes( text: str, line_number: int, renderer: DocutilsRenderer ) -> list[nodes.Element]: """Convert HTML to docutils nodes.""" if renderer.md_config.gfm_only: text, _ = RE_FLOW.subn(lambda s: s.group(0).replace("<", "<"), text) enable_html_img = "html_image" in renderer.md_config.enable_extensions enable_html_admonition = "html_admonition" in renderer.md_config.enable_extensions if not (enable_html_img or enable_html_admonition): return default_html(text, renderer.document["source"], line_number) # parse the HTML to AST try: root = tokenize_html(text).strip(inplace=True, recurse=False) except Exception: msg_node = renderer.create_warning( "HTML could not be parsed", line=line_number, subtype="html" ) return ([msg_node] if msg_node else []) + default_html( text, renderer.document["source"], line_number ) if len(root) < 1: # if empty return default_html(text, renderer.document["source"], line_number) if not all( (enable_html_img and child.name == "img") or ( enable_html_admonition and child.name == "div" and "admonition" in child.attrs.classes ) for child in root ): return default_html(text, renderer.document["source"], line_number) nodes_list = [] for child in root: if child.name == "img": if "src" not in child.attrs: return [ renderer.reporter.error( "<img> missing 'src' attribute", line=line_number ) ] content = "\n".join( f":{k}: {v}" for k, v in sorted(child.attrs.items()) if k in OPTION_KEYS_IMAGE ) nodes_list.extend( renderer.run_directive( "image", child.attrs["src"], content, line_number ) ) else: children = child.strip().children if ( children and children[0].name in ("div", "p") and ( "title" in children[0].attrs.classes or "admonition-title" in children[0].attrs.classes ) ): title = "".join(child.render() for child in children.pop(0)) else: title = "Note" options = "\n".join( f":{k}: {v}" for k, v in sorted(child.attrs.items()) if k in OPTION_KEYS_ADMONITION ).rstrip() new_children = [] for child in children: if child.name == "p": new_children.extend(child.children) new_children.append(Data("\n\n")) else: new_children.append(child) content = ( options + ("\n\n" if options else "") + "".join(child.render() for child in new_children).lstrip() ) nodes_list.extend( renderer.run_directive("admonition", title, content, line_number) ) return nodes_list
def test_html_round_trip(file_params): ast = tokenize_html(file_params.content) file_params.assert_expected(str(ast), rstrip=True)
def test_html_ast(file_params): tokens = "\n".join( repr(t) for t in tokenize_html(file_params.content).walk(include_self=True)) file_params.assert_expected(tokens, rstrip=True)