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)])
예제 #2
0
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
예제 #3
0
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"])
예제 #4
0
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
예제 #5
0
    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
예제 #6
0
    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
예제 #7
0
    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
예제 #8
0
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
예제 #9
0
    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
예제 #10
0
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
예제 #11
0
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'])
예제 #12
0
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)
        ]
예제 #13
0
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
예제 #14
0
    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"])
예제 #15
0
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
예제 #16
0
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))
예제 #17
0
def block_replace_elem(elem, doc):
    if isinstance(elem, pf.Para):
        return pf.CodeBlock("b")
예제 #18
0
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)
예제 #19
0
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