def action(elem, doc): if isinstance(elem, pf.Para) and is_include_line(elem): raw_path = pf.stringify(elem, newlines=False).split(maxsplit=1)[1].strip() if raw_path[0:2] == '@/': raw_path = './' + raw_path[2:] else: raise ValueError(f'[code import] {raw_path} should begin with @/') rawPathRegexp = r'^(.+(?:\.([a-z]+)))(?:#([\w-]+))?(?: ?({\d+(?:[,-]\d+)?}))?$' search = re.search(rawPathRegexp, raw_path) if search is None: raise ValueError(f'[code import] invalid parameter {raw_path}') (filepath, extension, region_name, meta) = search.groups() if not path.isfile(filepath): raise ValueError(f'[code import] file not found: {filepath}') basename = path.basename(filepath).split('.') extension = basename[-1] subextension = '' if len(basename) > 2: subextension = basename[-2] with open(filepath) as f: raw = f.read() region = extract_region(raw, region_name, filepath) return pf.CodeBlock(dedent(region), '', [get_code_type(extension, subextension)])
def action(elem, doc): if isinstance(elem, pf.CodeBlock) and u'include' in elem.attributes: fromLine = toLine = 0 data = open(elem.attributes[u'include'], 'r').readlines() if u'from' in elem.attributes: fromLine = int(elem.attributes[u'from']) - 1 if u'to' in elem.attributes: toLine = int(elem.attributes[u'to']) if u'length' in elem.attributes: toLine = fromLine + int(elem.attributes[u'length']) attr = elem.attributes attr[u'startFrom'] = str(fromLine + 1) if toLine == 0: rawCode = ''.join(data[fromLine:]) else: rawCode = ''.join(data[fromLine:toLine]) code = pf.CodeBlock(rawCode, identifier=elem.identifier, classes=elem.classes[:], attributes=elem.attributes) return code
def action(elem, doc): # is comment if (isinstance(elem, panflute.RawBlock)): matchResult = doc.commentRegex.match(panflute.stringify(elem)) if matchResult: insertType = matchResult.group("type") path = f"./{matchResult.group('path')}" content = stripCodeFile(doc, insertType, path) return panflute.CodeBlock(content, classes=["cs"])
def fenced_action(options, data, element, doc): attrs = get_attributes(options) content = render(attrs.filename) code = pf.CodeBlock(content, classes=attrs.classes, attributes={"caption": attrs.caption}, identifier=attrs.ident) return code
def _unknown_environment_debug(env_name, elem): """Handle unknown latex environment. Output visual feedback about the unknown environment. """ classes = ELEMENT_CLASSES["DEBUG_UNKNOWN_ENV"] + [slugify(env_name)] div = pf.Div(classes=classes) div.content.extend([ pf.Para(pf.Strong(*destringify("Unhandled environment:"))), pf.CodeBlock(elem.text), ]) return div
def _unknown_command_debug(cmd_name, elem): """Handle unknown latex commands. Output visual feedback about the unknown command. """ classes = ELEMENT_CLASSES["DEBUG_UNKNOWN_CMD"] + [slugify(cmd_name)] msg_prefix = pf.Strong(*destringify("Unhandled command:")) if isinstance(elem, pf.Block): div = pf.Div(classes=classes) div.content.extend([pf.Para(msg_prefix), pf.CodeBlock(elem.text)]) return div # RawInline span = pf.Span(classes=classes) span.content.extend([msg_prefix, pf.Space(), pf.Code(elem.text)]) return span
def listingtable(self, filename, idn, caption, options): if self.doc.format in ["latex"]: for c in caption: if isinstance(c, (pf.Str)): c.text = c.text.replace("_", r"\textunderscore ") # pf.debug(c.text) basename = os.path.basename(filename) # /path/to/file.txt -> file.txt file_type = options.get("type", "plain") types = [file_type, "numberLines"] startFrom = options.get("startFrom", "1") numbers = options.get("numbers", "left") attr = {"startFrom": startFrom, "numbers": numbers} linefrom = options.get("from") lineto = options.get("to") linefrom = None if not linefrom else (int(linefrom) - 1) lineto = None if not lineto else (int(lineto)) if self.doc.format in ["latex"]: file_title = basename.replace("_", r"\textunderscore") else: file_title = basename temp_caption = [pf.Str("%s" % (file_title))] caption = temp_caption if not len(caption) else caption with open(filename, "r", encoding="utf-8") as f: lines = list(f) if (not linefrom) and (not lineto): raw = "".join(lines) elif linefrom and (not lineto): raw = "".join(lines[linefrom:]) elif not linefrom and lineto: raw = "".join(lines[:lineto]) else: raw = "".join(lines[linefrom:lineto]) # pf.debug(linefrom, lineto, raw) label = basename.lower().replace(".", "_").replace("/", "_") + str( self.counter) idn = idn if idn else "lst:{label:s}".format(label=label) read = pf.CodeBlock(raw, classes=types, identifier=idn, attributes=attr) ret = [pf.Para(pf.Str("Listing:"), pf.Space(), *caption), read] return ret
def table2csv(elem, doc): """ find Table element and return a csv table in code-block with class "table" """ if isinstance(elem, panflute.Table): # get options as a dictionary options = {} # options: caption: panflute ast to markdown if elem.caption: options['caption'] = ast2markdown(panflute.Para(*elem.caption)) # options: alignment align_dict = { "AlignLeft": 'L', "AlignCenter": 'C', "AlignRight": 'R', "AlignDefault": 'D' } parsed_alignment = [align_dict[i] for i in elem.alignment] options['alignment'] = "".join(parsed_alignment) # options: width options['width'] = elem.width # options: table-width from width options['table-width'] = sum(options['width']) # options: header: False if empty header row, else True options['header'] = bool(panflute.stringify( elem.header)) if elem.header else False # options: markdown options['markdown'] = True # option in YAML yaml_metadata = yaml.safe_dump(options) # table in panflute AST table_body = elem.content if options['header']: table_body.insert(0, elem.header) # table in list table_list = [[ast2markdown(cell.content) for cell in row.content] for row in table_body] # table in CSV with io.StringIO() as file: writer = csv.writer(file) writer.writerows(table_list) csv_table = file.getvalue() code_block = "{delimiter}{yaml}{delimiter}{csv}".format( yaml=yaml_metadata, csv=csv_table, delimiter='---\n') return panflute.CodeBlock(code_block, classes=["table"]) return None
def handle_mtikzauto(self, cmd_args, elem): r"""Handle ``\MTikzAuto`` command. Create a ``CodeBlock`` with TikZ code. """ if isinstance(elem, pf.Inline): raise ValueError( r"\MTikzAuto should be block element!: {}".format(cmd_args)) tikz_code = REGEX_PATTERNS["STRIP_HASH_LINE"].sub("", cmd_args[0]) for repl in TIKZ_SUBSTITUTIONS: tikz_code = re.sub(repl[0], repl[1], tikz_code) # remove empty lines tikz_code = linesep.join([s for s in tikz_code.splitlines() if s]) codeblock = pf.CodeBlock(tikz_code) codeblock.classes = ELEMENT_CLASSES["MTIKZAUTO"] ret = pf.Div(codeblock, classes=["figure"]) return ret
def fenced_action(options, data, element, doc): # We'll only run this for CodeBlock elements of class 'include' file = options.get('file') classes = options.get('classes', []) caption = options.get('caption', "") ident = options.get('id', "") if not os.path.isfile(file): raise FileNotFoundError(file) with open(file, 'r', encoding="utf-8") as f: content = f.read() code = pf.CodeBlock(content, classes=classes, attributes={"caption": caption}, identifier=ident) return code
def fenced_html(options, data, element, doc): identifier = options.get('identifier', '') element.identifier = identifier caption = options.get('caption', '') caption = pf.convert_text(caption, extra_args=['--biblatex'], input_format='markdown', output_format='html') caption_span = pf.Plain( pf.Span(pf.RawInline(caption), classes=['fencedSourceCodeCaption'])) code_block = pf.CodeBlock(data, classes=element.classes, attributes=element.attributes) return pf.Div(code_block, caption_span, identifier=identifier, classes=['fencedSourceCode'])
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 table_to_csv(elem, doc): """convert Table element and to csv table in code-block with class "table" in panflute AST""" if isinstance(elem, panflute.Table): # get options as a dictionary options = {} # options: caption: panflute ast to markdown if elem.caption: options['caption'] = ast_to_markdown(panflute.Para(*elem.caption)) # options: alignment parsed_alignment = [ALIGN_TO_LETTER[i] for i in elem.alignment] options['alignment'] = "".join(parsed_alignment) # options: width options['width'] = elem.width # options: table-width from width options['table-width'] = sum(options['width']) # options: header: False if empty header row, else True options['header'] = bool(panflute.stringify( elem.header)) if elem.header else False # options: markdown options['markdown'] = True # option in YAML yaml_metadata = yaml.safe_dump(options) # table in panflute AST table_body = elem.content if options['header']: table_body.insert(0, elem.header) # table in list table_list = [[ast_to_markdown(cell.content) for cell in row.content] for row in table_body] # table in CSV with io.StringIO() as file: writer = csv.writer(file) writer.writerows(table_list) csv_table = file.getvalue() code_block = f"""--- {yaml_metadata}--- {csv_table}""" return panflute.CodeBlock(code_block, classes=["table"]) return None
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 execute_code_block(elem, doc): ''' makes a block out of the executed code ''' code = elem.text.strip() script = elem.attributes.get("script") wd = elem.attributes.get("wd") linelength = int(elem.attributes.get("linelength", 51)) if script: output = execute_script(script, wd) with open(wd+"/"+script) as python_script: code = python_script.read() elem.text = code else: output = execute_code(code, wd) output = '\n'.join([ '\n'.join(textwrap.wrap( line, linelength, break_long_words=False, replace_whitespace=False)) for line in output.splitlines()]) output_element = pf.CodeBlock(output, classes=['output']) # output_element = pf.CodeBlock(output, classes=['python']) return output_element
def latexblock(code, file_found=True): """LaTeX block""" if file_found: return pf.RawBlock('\n'.join(code), format='latex') else: return pf.CodeBlock('\n'.join(code))
def block_replace_elem(elem, doc): if isinstance(elem, pf.Para): return pf.CodeBlock("b")
def test_block_elem(): in_doc = pf.Doc(pf.Para(pf.Str("a"))) in_doc.walk(block_replace_elem) expected_doc = pf.Doc(pf.CodeBlock("b")) assert compare_docs(in_doc, expected_doc)
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