def _nonnormative(name): _wrap( pf.Span(pf.Str('[ '), pf.Emph(pf.Str('{}:'.format(name.title()))), pf.Space), pf.Span(pf.Str(' — '), pf.Emph(pf.Str('end {}'.format(name.lower()))), pf.Str(' ]')))
def format_names(names): first = pf.Emph(pf.Str(split_name(names[0])[0])) if len(names) == 0: return [first] elif len(names) == 1: second = pf.Emph(pf.Str(split_name(names[0])[0])) return [first, pf.Str("and"), second] elif len(names) > 1: return [first, pf.Str("et al.")]
def action(elem, doc): """The panflute filter main method, called once per element.""" global shift, workaround_level_overflow, workaround_level_underflow if isinstance(elem, pf.Header): level_old = elem.level level_new = level_old + shift if level_new > MAX_LEVEL: eprint( "After shifting header levels by %d, '%s' would be on level %d, " "which is above the max level %d." % (shift, elem.identifier, level_new, MAX_LEVEL)) if workaround_level_overflow: eprint( "Thus we convert it to an emphazised text paragraph instead." ) if level_new == (MAX_LEVEL + 1): elem = pf.Para(pf.Strong(*elem.content)) else: elem = pf.Para(pf.Emph(*elem.content)) else: raise OverflowError() elif level_new < MIN_LEVEL: eprint( "After shifting header levels by %d, '%s' would be on level %d, " "which is below the min level %d." % (shift, elem.identifier, level_new, MIN_LEVEL)) if workaround_level_underflow: eprint("Thus we leave it at the min level.") else: raise OverflowError() else: elem.level = level_new return elem
def action(elem, doc): if isinstance(elem, pf.RawInline): if elem.format == 'html': if elem.text.startswith('<!--') and elem.text.endswith('-->'): comment = elem.text.replace('<!--', 'question: ').replace('-->', '?') token = [pf.Emph(pf.Inline(text=comment))] elem = token
def action(elem, doc): if isinstance(elem, pf.Doc): version = pkg_resources.get_distribution("panflute").version json_serializer = lambda elem: elem.to_json() raw = json.dumps(elem, default=json_serializer) raw = json.loads(raw) raw = json.dumps(raw, check_circular=False, indent=4, separators=(',', ': ')) disclaimer = pf.Para(pf.Emph(pf.Str('Note: sort order not preserved'))) elem.content = [ pf.Header(pf.Str('Python version:'), level=2), pf.Para(pf.Str(sys.version)), pf.Header(pf.Str('Panflute version:'), level=2), pf.Para(pf.Str(version)), pf.Header(pf.Str('sys.argv:'), level=2), pf.Plain(pf.Str(str(sys.argv))), pf.Header(pf.Str('JSON Input:'), level=2), disclaimer, pf.CodeBlock(raw) ]
def test_to_inline(self): """It should convert different elements correctly to inline""" content1 = pf.Para(pf.Strong(pf.Str("just some text"))) transformed1 = content1.content[0] content2 = pf.Div( pf.Para(pf.Strong(pf.Str("again")), pf.Space, pf.Emph(pf.Str("normal")))) content3 = pf.Div( pf.Para( pf.Span(pf.Str("foo"), classes=["1st-span-class"]), pf.Span( pf.Strong(pf.Str("Unhandled"), pf.Space, pf.Str("command:")), classes=["2nd-span-class"], ), ), pf.CodeBlock(r"\MLFunctionQuestion{10}{sin(x)}{5}{x}{5}{DS2}"), classes=["div-class"], ) self.assertEqual(to_inline(content1), transformed1) # test if nested inlining works il_content2 = to_inline(content2) self.assertIsInstance(il_content2.content[0], pf.Strong) self.assertEqual(il_content2.content[0].content[0].text, "again") self.assertIsInstance(il_content2.content[2], pf.Emph) # test if class conservation works and advanced nesting il_content3 = to_inline(content3) self.assertEqual(len(il_content3.content), 2) self.assertEqual(len(il_content3.content[0].content), 2) self.assertEqual(il_content3.classes, ["div-class"]) self.assertEqual(il_content3.content[0].content[0].classes, ["1st-span-class"]) self.assertEqual(il_content3.content[0].content[1].classes, ["2nd-span-class"])
def comment(elem, doc): text = pf.stringify(elem).strip() is_relevant = isinstance(elem, pf.Str) and text.startswith("::comment-") if is_relevant and text.endswith("-begin"): doc.ignore += 1 # comment text wont be displayed if doc.show_comments and doc.ignore == 1: logger.debug("Seems like show_comments is set to true?") return pf.Emph(pf.Str("Comment:")) return [] if doc.ignore > 0: if is_relevant and text.endswith("-end"): doc.ignore -= 1 elif doc.show_comments: return None return []
def test_all(): md = 'Some *markdown* **text** ~xyz~' c_md = pf.convert_text(md) b_md = [ pf.Para(pf.Str("Some"), pf.Space, pf.Emph(pf.Str("markdown")), pf.Space, pf.Strong(pf.Str("text")), pf.Space, pf.Subscript(pf.Str("xyz"))) ] print("Benchmark MD:") print(b_md) print("Converted MD:") print(c_md) assert repr(c_md) == repr(b_md) with io.StringIO() as f: doc = pf.Doc(*c_md) pf.dump(doc, f) c_md_dump = f.getvalue() with io.StringIO() as f: doc = pf.Doc(*b_md) pf.dump(doc, f) b_md_dump = f.getvalue() assert c_md_dump == b_md_dump # ---------------------- print() tex = r'Some $x^y$ or $x_n = \sqrt{a + b}$ \textit{a}' c_tex = pf.convert_text(tex) b_tex = [ pf.Para(pf.Str("Some"), pf.Space, pf.Math("x^y", format='InlineMath'), pf.Space, pf.Str("or"), pf.Space, pf.Math(r"x_n = \sqrt{a + b}", format='InlineMath'), pf.Space, pf.RawInline(r"\textit{a}", format='tex')) ] print("Benchmark TEX:") print(b_tex) print("Converted TEX:") print(c_tex) assert repr(c_tex) == repr(b_tex) with io.StringIO() as f: doc = pf.Doc(*c_tex) pf.dump(doc, f) c_tex_dump = f.getvalue() with io.StringIO() as f: doc = pf.Doc(*b_tex) pf.dump(doc, f) b_tex_dump = f.getvalue() assert c_tex_dump == b_tex_dump print("\nBack and forth conversions... md->json->md") md = 'Some *markdown* **text** ~xyz~' print("[MD]", md) md2json = pf.convert_text(md, input_format='markdown', output_format='json') print("[JSON]", md2json) md2json2md = pf.convert_text(md2json, input_format='json', output_format='markdown') print("[MD]", md2json2md) assert md == md2json2md print("\nBack and forth conversions... md->panflute->md") md = 'Some *markdown* **text** ~xyz~' print("[MD]", md) md2panflute = pf.convert_text(md, input_format='markdown', output_format='panflute') print("[PANFLUTE]", md2panflute) md2panflute2md = pf.convert_text(md2panflute, input_format='panflute', output_format='markdown') print("[MD]", md2panflute2md) assert md == md2panflute2md print("\nBack and forth conversions... md->panflute(standalone)->md") md = 'Some *markdown* **text** ~xyz~' print("[MD]", md) md2panflute = pf.convert_text(md, input_format='markdown', output_format='panflute', standalone=True) print("[PANFLUTE]", md2panflute) md2panflute2md = pf.convert_text(md2panflute, input_format='panflute', output_format='markdown') print("[MD]", md2panflute2md) assert md == md2panflute2md print( "\nBack and forth conversions... md table -> json(standalone) -> md table" ) md = """lorem --- --- x y --- --- ipsum""" print("[MD]", repr(md)) md2json = pf.convert_text(md, input_format='markdown', output_format='json', standalone=True) print("[json]", md2json) md2json2md = pf.convert_text(md2json, input_format='json', output_format='markdown') print("[MD]", repr(md2json2md)) assert md == md2json2md print( "\nBack and forth conversions... md table -> panflute(standalone) -> md table" ) print("[MD]", repr(md)) md2panflute = pf.convert_text(md, input_format='markdown', output_format='panflute', standalone=True) print("[PANFLUTE]", md2panflute) md2panflute2md = pf.convert_text(md2panflute, input_format='panflute', output_format='markdown') print("[MD]", repr(md2panflute2md)) assert md == md2panflute2md print( "\nBack and forth conversions... gfm table (empty) -> json(standalone) -> gfm table (empty)" ) md = """lorem | x | y | | - | - | ipsum""" print("[MD]", repr(md)) md2json = pf.convert_text(md, input_format='gfm', output_format='json', standalone=True) print("[json]", md2json) md2json2md = pf.convert_text(md2json, input_format='json', output_format='gfm') print("[MD]", repr(md2json2md)) assert md == md2json2md print( "\nBack and forth conversions... gfm table (empty) -> panflute(standalone) -> gfm table (empty)" ) print("[MD]", repr(md)) md2panflute = pf.convert_text(md, input_format='gfm', output_format='panflute', standalone=True) print("[PANFLUTE]", md2panflute) md2panflute2md = pf.convert_text(md2panflute, input_format='panflute', output_format='gfm') print("[MD]", repr(md2panflute2md)) assert md == md2panflute2md
def make_emph(elem, doc): if isinstance(elem, pf.Str): return pf.Emph(elem)
def convert(self): doc = panflute.Doc( api_version=(1, 17, 5), metadata={ 'pagetitle': self.title, }, ) doc.content.append(panflute.Header(panflute.Str(self.title))) lists = {} tables = {} table_rows = {} table_cells = {} for chunk in self._attr_chunks(): self.logger.debug(chunk) container = panflute.Para() cdiv = panflute.Div(container) # Handle lists if 'list_class' in chunk[0]['attrs']: lc = chunk[0]['attrs']['list_class'] check_state = None if lc in ['checked', 'unchecked']: check_state = lc lc = 'checklist' ld = chunk[0]['attrs']['list_depth'] # prune any lists that are lower than us, they're finished for i in list(lists.keys()): if i > ld: lists.pop(i) # non-homogenous list types can be immediately adjacent without # ending up merged if ld in lists and lists[ld]['class'] != lc: lists.pop(ld) # checklists are a special case, they can't contain other lists if lc != 'checklist' and lists and lists[1][ 'class'] == 'checklist': lists = {} # make sure any intermediate lists were created, including # the top level because boxnotes for i in range(1, ld + 1): if i not in lists: lists[i] = self._list(lc, i) if i != ld: lists[i]['pf'].content.append(panflute.ListItem()) lp = lists[i]['pf'] if lc == 'checklist': lp = panflute.Div(lp, classes=['checklist']) if i == 1: doc.content.append(lp) else: lists[i - 1]['pf'].content[-1].content.append(lp) # set the container for the other subchunks container = panflute.Plain() cdiv.content = [container] cdiv.classes.append(lc) if check_state: cdiv.classes.append(check_state) lists[ld]['pf'].content.append(panflute.ListItem(cdiv)) if check_state == 'checked': container.content.append(panflute.Str(CHECKED)) elif check_state == 'unchecked': container.content.append(panflute.Str(UNCHECKED)) elif 'table_id' in chunk[-1]['attrs']: table_id = chunk[-1]['attrs']['table_id'] row_id = chunk[-1]['attrs']['table_row'] cell_id = row_id + chunk[-1]['attrs']['table_col'] if table_id not in tables: # There's some magic in the constructor for panflute tables # that isn't exposed in any other way, so we can't create # the table until we've finished populating the rows. # Instead, use a placeholder div to locate it within the # document. tables[table_id] = { 'div': panflute.Div(), 'rows': [], } doc.content.append(tables[table_id]['div']) if row_id not in table_rows: table_rows[row_id] = panflute.TableRow() tables[table_id]['rows'].append(table_rows[row_id]) if cell_id not in table_cells: cdiv = panflute.Div(panflute.Plain()) table_cells[cell_id] = panflute.TableCell(cdiv) table_rows[row_id].content.append(table_cells[cell_id]) container = table_cells[cell_id].content[0].content[0] else: lists = {} doc.content.append(cdiv) if 'align' in chunk[0]['attrs']: cdiv.attributes['style'] = 'text-align: ' + chunk[0]['attrs'][ 'align'] + ';' for subchunk in chunk: if subchunk['newlines'] > 1: # we've had an extra linebreak, no more adding on to lists lists = {} # don't do anything with markers if subchunk['text'] == '*' and 'lmkr' in subchunk['attrs']: continue scont = container if 'href' in subchunk['attrs']: scont = panflute.Link(url=subchunk['attrs']['href']) container.content.append(scont) if 'image' in subchunk['attrs']: scont.content.append( panflute.Image( url=self._image(subchunk['attrs']['author'], subchunk['attrs']['image']))) continue span = panflute.Span() lines = subchunk['text'].splitlines() while lines: subtext = lines.pop(0) span.content.append(panflute.Str(subtext)) if lines: span.content.append(panflute.LineBreak()) if 'font' in subchunk['attrs']: color = subchunk['attrs']['font'].get('color', '000000') size = subchunk['attrs']['font'].get('size', 'medium') span.classes.append('font-size-' + size) span.classes.append('font-color-' + color) # I don't actually know what the possible colors are and I # don't feel like finding out, so just inject it as an # inline style. if color != '000000': span.attributes['style'] = 'color: #' + color + ';' if subchunk['attrs'].get('underline'): span.classes.append('underline') if subchunk['attrs'].get('bold'): span = panflute.Strong(span) if subchunk['attrs'].get('italic'): span = panflute.Emph(span) if subchunk['attrs'].get('strikethrough'): span = panflute.Strikeout(span) scont.content.append(span) # Actually create the tables for x in tables: tables[x]['div'].content.append(panflute.Table(*tables[x]['rows'])) with io.StringIO() as f: panflute.dump(doc, f) return f.getvalue()
def action(elem, doc): # noqa """Processes pf.CodeBlocks. For details and a specification of how each command should behave, check the example files (especially the md and pdf)! Args: elem: The element to process. doc: The document. Returns: A changed element or None. """ if isinstance(elem, pf.CodeBlock): doc.listings_counter += 1 elems = [elem] if 'hide' not in elem.classes else [] if 'file' in elem.attributes: elem.text = read_file(elem.attributes['file']) filename = trimpath(elem.attributes) prefix = pf.Emph(pf.Str('File:')) if 'exec' in elem.classes: if 'interactive' in elem.classes or elem.text[:4] == '>>> ': elem.text = execute_interactive_code(elem, doc) else: result = execute_code_block(elem, doc) if 'hideimports' in elem.classes: elem.text = remove_import_statements(elem.text) if 'plt' in elem.attributes or 'plt' in elem.classes: doc.plot_found = True result = maybe_center_plot(result) block = pf.RawBlock(result, format='latex') else: block = pf.CodeBlock(result, classes=['changelog']) elems += [pf.Para(pf.Emph(pf.Str('Output:'))), block] if 'lines' in elem.attributes: elem.text = filter_lines(elem.text, elem.attributes['lines']) label = elem.attributes.get('label', f'cl:{doc.listings_counter}') if 'caption' in elem.attributes.keys(): doc.caption_found = True cap = pf.convert_text(elem.attributes['caption'], output_format='latex') # noqa if 'shortcaption' in elem.attributes.keys(): shortcap = pf.convert_text(elem.attributes['shortcaption'], output_format='latex') # noqa else: shortcap = cap if 'file' in elem.attributes.keys(): cap += pf.convert_text(f' (`{filename}`)', output_format='latex') # noqa elems = make_codelisting(elems, cap, label, shortcaption=shortcap, above='capbelow' not in elem.classes) elif 'caption' in elem.classes: doc.caption_found = True cap = '' if 'file' in elem.attributes.keys(): cap = pf.convert_text(f'`{filename}`', output_format='latex') elems = make_codelisting(elems, cap, label, above='capbelow' not in elem.classes) else: if 'file' in elem.attributes.keys(): elems.insert(0, pf.Para(prefix, pf.Space, pf.Code(filename))) return elems
def handle_modsemph(self, cmd_args, elem): r"""Handle \modsemph command.""" return pf.Emph(*parse_fragment( cmd_args[0], elem.doc.metadata["lang"].text)[0].content)