Edit Front Matter Module

Synopsis

A thread safe class that uses Jinja2 templating to edit yaml front matter within text files. This class is intended to be used for batch processing.

Platform

Unix, Windows, python >=v3.5.3

Dependencies
Jinja2>=2.10.1
MarkupSafe>=1.1.1
oyaml>=0.9
PyYAML>=5.1
License

MIT

Module Author

Karl N. Redman

Current Release

version: 0.0.1

New in version 0.0.1: Initial Version

exception EditFrontMatter_Exception(msg, exc, *args, **kwargs)[source]

Bases: Exception

Custom exception handler for EditFrontMatter Project

__init__(msg, exc, *args, **kwargs)[source]
Description

A custom exception handler for the module. Provides a simplified output message for debugging.

Parameters
  • msg (str) – A custom messsage for the exception caught in the code

  • exc (Exception) – Original exception object from try block

Returns

Exception obj

class EditFrontMatter(*, file_path=None, jinja2_env=<jinja2.environment.Environment object>, template_str='', yaml_delim='---', keys_toDelete=[], do_readFile=True)[source]

Bases: object

Main Class for module

__init__(*, file_path=None, jinja2_env=<jinja2.environment.Environment object>, template_str='', yaml_delim='---', keys_toDelete=[], do_readFile=True)[source]

Main class for the module. Programmatically Adds / Updates / Deletes yaml front matter elements embedded in text/markdown files.

Hint

This class uses keyword only arguments. Inheriting this class would look something like the following:

class Derived_EditFrontMatter (EditFrontMatter):
    def __init__(self,**kwargs):
        EditFrontMatter.__init__(self, **kwargs)
Parameters
  • yaml_delim (str) – yaml file section delimiter (i.e. “—”)used to locate the source file’s embedded yaml section.

  • do_readFile (bool) – allow instantiation without implicit readFile() call

Variables
  • self.file_lines (list) – Lines from the data source file. Must be managed after creating the class object if __init__(do_readFile=False)

  • self.file_path (str) – Path of source data file. Superfluous if readFile() is never called

  • self.fmatter (yaml) – [default: empty dict if yaml not found] Front matter as a yaml object. Set in readFile().

  • self.yaml_delim (str) – [default:”—”] Front matter delimiter. Can be used to change front matter delimter between reading the source file into file_lines and executing dumpFrontMatter()/writeFile().

  • self.yamlSeperator_pattern (re.compile()) – Regex patten for the yaml line delimiter. only used if readFile() is called

  • self.yaml_start (int) – Beginning of the yaml blob in the original source file. Set in readFile()

  • self.yaml_end (int) – End of the yaml blob in the origional source file. Set in readFile()

  • self.template_str (str) – Contains the jinja2 template. Can be manipulated between class instantiation and executing run()

  • self.jinja2_env (jinja2.Environment) – This object can be specified during class instantiation if greater control is required

  • self.keys_toDelete (list) – keys to be deleted from fmatter object. Utilized at the end of the run() method

Throws:

EditFrontMatter_Exception

set_yaml_delim(delim, *args, **kwargs) → None[source]

Set the yaml delimiter and compile it.

Parameters

delim (str) – A string to use as a delimiter for finding and editing frontmatter in a file.

readFile(file_path=None, *args, **kwargs) → None[source]
Read a file into file_lines list (if applicable) and

seperate the front matter into fmatter yaml object. This function resets fmatter.

Parameters

file_path (str) – optional file path

Hint

If local file_path:None and file_path:None, file_lines should be populated before calling this function.

In the example below, the initialization would fail if do_readFile:True:

proc = EditFrontMatter(do_readFile=False)
proc.file_lines = ''.join(open(RUN_PATH + "example.md", "r").readlines())
# initialize proc.fmatter and record data position
proc.readFile()
...

Note

If the file source content is empty file_empty is set to True. This affects dumpFileData() and writeFile() behavior

writeFile(file_path=None, *args, **kwargs) → bool[source]

Write to arg file_path, attr file_path

Note

If the original file source data was empty after a call to readFile(), no attempt is maid to write to the file.

Parameters

file_path (str) – optional file path

Returns

  • True if file was written

  • ’False` if file was not written

add_JinjaFilter(name, func, *args, **kwargs) → None[source]
Add a Jinja filter

for setting a jinja2 template variable programmatically through callback.

Parameters
  • name (str) – Jinja template variable name

  • func (object) – callback function that will set name

Example of implementing a filter for callback

Programatically change the value of draft field in the source document example1.md using a Jinja2 filter

jinja2 template

{% set toc = "true" %}

toc: {{ toc }}
draft: {{ false | canPublish }}
hasMath: {{ hasMath }}
stuff: {{ addedVariable }}
mardown file

---
title: "EditFrontMatter Class Example 1"
description: "Edit some fields in this front matter"
catagories: [programming, python, markdown]

deleteme: this will be deleted

tags: [front matter, administration, testing]

# comments and spaces will be eliminated (see docs)

author: "Karl N. Redman"
creatordisplayname: "Karl N. Redman"
creatoremail: "karl.redman@example.com"
date: 2019-05-23T17:43:45-05:00
lastmodifierdisplayname: "Karl N. Redman"
lastmodifieremail: "karl.redman@gmail.com"
lastmod: 2019-05-23T17:43:45-05:00
toc: false
type: "page"
hasMath: false
draft: false
weight: 5
---

# EditFontMatter Class Example 1

Edit several fields of front matter.

## Fields affected in this example:

* toc
  * note: uses local template variable
  * pre: false
  * post: true
* draft:
  * note: uses jinja2 filter (callback)
  * pre: false
  * post: true
* hasMath
  * note: uses program variable
  * pre: true
  * post: false
* stuff:
  * note: uses program variable to create field
  * pre: did not exist
  * post: (list) ['one', 'two', 'three']
* deleteme:
  * note: removed from final result
  * pre: this will be deleted
  * post: N/A
Example code

from editfrontmatter import EditFrontMatter
import os

# a jinja2 filter callback function
def canPublish_func(val):
    # do some processing....
    return True

# stringify the template file
template_str = ''.join(open(os.environ.get("TEST_DATA_DIR") +
    "template1.j2", "r").readlines())

# creating object
obj = EditFrontMatter(file_path = os.environ.get("TEST_DATA_DIR") + "example1.md",
        template_str = template_str)

# set `canPublish_func` function for our `draft` field callback using
# `canPublish` template variable.
obj.add_JinjaFilter('canPublish', canPublish_func)

# process the front matter from 'example1.md'. `draft` in the front
# matter will be set to `true`
obj.run()

# print the new file contents (uncomment to see dump)
# print(obj.dumpFileData())
del_JinjaFilter(name, *args, **kwargs) → bool[source]

Delete a Jinja filter.

Parameters

name (str) – filter key

Returns

  • True if filter found and deleted

  • False if filter not found

dumpFrontMatter(*args, **kwargs) → str[source]

Dump fmatter as a string

Returns

fmatter content as a string

dumpFileData(*args, **kwargs) → str[source]

Concatenate frontmatter with original content and return as a string.

Returns

  • A string formed by the concatination of fmatter and file_lines (if file souce content is not empty* and yaml was present).

  • An empty string if file source content is empty or original yaml was not present.

Hint

fmatter and file_lines are still available if needed beyond this method. One way of managing this data would be to manually prepend yaml front matter to file_lines and rerun readFile() before calling this function again.

has_source_yaml() → bool[source]

Checks if the yaml is empty after a call to readFile()

Note

This return value does not rely on fmatter since that attribute is subject to change once readFile() is called.

Returns

  • True if the original source file data did have a yaml section

  • False if the original source file data did not have a yaml section

has_source_data() → bool[source]

Checks if the source file data is empty

Returns

  • True if the source data content is empty

  • False if the source data is not empty

run(extraVars_dict={}, *args, **kwargs) → None[source]

Process the front matter changes and delete previously specified yaml keys.

Variables to change yaml data are passed as a dictionary argument as extraVars_dict.

Parameters

extraVars_dict (dict) – key,value pairs to be set or added in the fmatter object.

Example

Call EditFrontMatter.run() with variables to change in fmatter:

proc = EditFrontMatter()
...
proc.run({'hasMath': True, 'addedVariable': ['one', 'two', 'three']})
...