panflute

repository·master·Indexed 20 days ago

https://github.com/sergiocorreia/panflute

A Python package designed to make creating Pandoc filters more intuitive and 'Pythonic'. It allows developers to manipulate Pandoc Abstract Syntax Trees (ASTs) using Python, providing tools for traversing documents, modifying elements, and implementing YAML-based filters.

Tokens
13.1K
Snippets
47
Records
75
Agent score
68%

What's inside panflute

  1. Use global variables and backmatter via the `doc` object

    master
    To perform tasks that require document-wide knowledge—such as generating a Table of Contents or moving elements to a specific location (e.g., finding a placeholder string like $tables)—you can track state using global variables stored as attributes of the doc object.
  2. Panflute 'Batteries Included' features

    master

    Panflute includes several high-level utilities for common filter tasks:

    • Text Conversion: Use convert_text(text, input_format, output_format) to convert formatted strings (like Markdown) into Panflute objects via an internal Pandoc call.
    • YAML/Data in Code Blocks: Use yaml_filter(element, doc, tag, function) to process data stored in code blocks.
    • External Commands: Execute external programs and fetch results using shell().
    • Document Lifecycle: Use the prepare and finalize arguments in run_filter to perform actions before or after the main filter pass (e.g., moving all figures to the end of a document).
    • Metadata Access: Access metadata as a standard Python dictionary of built-in values using doc.get_metadata() instead of interacting with Panflute objects.
    • Keyword Replacement: Use replace_keyword for easy text substitution.
    • Self-Running Filters: Panflute can act as a filter runner itself; it will automatically execute all filters listed in the Pandoc metadata field panflute-filters.
  3. Navigate the document tree using parent and sibling attributes

    master

    Every element in panflute has attributes that allow you to navigate the document structure relative to the current element:

    • .parent: The parent element.
    • .next: The next sibling.
    • .prev: The previous sibling.
    • .ancestor(n): The n-th ancestor.
    • .index: The index of the element among its siblings.
    • .offset(n): The offset relative to the current element.
  4. Core concepts of panflute: Pythonic element manipulation

    master

    Panflute provides a Pythonic interface for interacting with Pandoc elements, making them significantly easier to manipulate than the raw JSON structures used by pandocfilters.

    Key Advantages:

    • Easy Modification: Access attributes directly. For example, use header.level += 1 to change a header level or header.identifier = 'spam' to change an identifier.
    • Simple Creation: Construct elements using standard Python constructors. Example: Header(Str(The), Space, Str(Title), level=1, identifier=foo).
    • Navigation: Traverse the document tree using attributes like elem.parent, elem.next, or by checking types with isinstance(elem.parent, Inline).
  5. Rules for writing action functions in filters

    master

    Action functions are the core of panflute filters. They are called with the signature action(element, doc). When writing these functions, follow these rules:

    • Arguments: They must accept at least two arguments: element and doc. Additional arguments can be passed via **kwargs when using toJSONFilter or toJSONFilters.
    • Return Values:
      • None: The element remains in the document as is (though it may have been modified in place).
      • Element: The returned element replaces the original element in the document.
      • [] (empty list): The element is deleted from the document. (Note: You can delete table rows or list items, but you cannot delete a table's caption; you can only make it empty).
      • List[Element]: If the input is a block or inline element, you can return a list of elements of the same base class to replace it.
  6. Create a simple element modification filter

    master

    To modify existing elements in a document (e.g., changing header levels), write a Python script that defines a function to handle specific element types and then calls panflute.run_filter().

    import panflute as pf
    
    def action(elem, doc):
        if isinstance(elem, pf.Header):
            elem.level = 1
        return elem
    
    if __name__ == '__main__':
        pf.run_filter(action)
  7. Install panflute and Pandoc via Conda

    master

    Using Conda is recommended if you want panflute and a matching version of Pandoc to be managed and installed together automatically. You can use conda or mamba (a faster drop-in replacement).

    # Install both pandoc and panflute (version >= 2.0.5)
    conda install -c conda-forge pandoc 'panflute>=2.0.5'
    
    # Install pandoc, panflute, and extra dependencies (yamlloader)
    conda install -c conda-forge pandoc 'panflute>=2.0.5' yamlloader
    
    # Upgrade both
    conda update pandoc panflute
    
    # Remove both
    conda remove pandoc panflute
  8. Build PDF documentation

    master

    To build the PDF version of the documentation, you must have miktex or a similar LaTeX distribution installed. The process is slower than building HTML.

    On Windows, you can run the latex target via make.bat or manually run pdflatex on the generated .tex file.

    cd docs && make.bat latex && cd build && cd latex && Makefile && cd
  9. Build and update documentation and website

    master

    To rebuild the HTML documentation and update the hosted website, execute the following sequence of commands. This involves generating HTML via make.bat, building the Jekyll site, and pushing to S3.

    Note: This assumes make.bat, jekyll, and s3_website are available in your environment.

    cd docs && make.bat html && cd .. && cd ../website && jekyll build && s3_website push && cd ../panflute
  10. How to write and run a panflute filter

    master

    To create a panflute filter, write a function that operates on Pandoc elements and call it using run_filter. The function should accept an element (elem) and the document (doc). If you want to modify an element, return the modified element (or a list of elements); to delete an element, return an empty list [].

    from panflute import *
    
    def increase_header_level(elem, doc):
        if type(elem) == Header:
            if elem.level < 6:
                elem.level += 1
            else:
                return [] # Delete headers already in level 6
    
    def main(doc=None):
        return run_filter(increase_header_level, doc=doc)
    
    if __name__ == "__main__":
        main()