Target factories
Target factories^1 are functions that generate targets programatically. Without target factories, every target must be specified individually in your _markmeld.yaml
config file. With target factories, you can add multiple targets with a single definition. These targets are added programatically using a function call . For example, a target factory could build targets from an external data source, or a list of files in a directory. Target factories provide a powerful way to produce a lot of targets with very little effort.
Example use case
Say I have a folder with a bunch of .md
files and I'd like to build a separate PDF for each one. If I want a single target that builds all the PDFs at once, I could use a multi-output target. But if I want to build each one independently, I need a separate target for each file. I could add each target to the _markmeld.yaml
file and that would work, but wouldn't it be nice if I could somehow just say, "I want every markdown file in this folder to be its own target", and then leave it at that? That's what target factories do. With a target factory, I could add a new target to the project by just adding a new .md
file to the folder -- no change would be required in _markmeld.yaml
.
How to use target factories
There are built-in target factories and custom target factories. You can use built-in factories with no further prerequisites.
1. Built-in target factories
Right now there is 1 built-in factory, called the glob factory.
Glob factory
The glob
factory solves the above use case. You have a bunch of .md
files and you want a target for each one. You can invoke the glob factory in your _markmeld.yaml
config file like this:
target_factories:
- glob:
path: "*.md"
- glob:
name_levels: 2
path: "*/*.md"
glob_variables:
jinja_template: ...
Parameters for glob
factory:
path
: A Pythonglob
(regular expression) for paths to markdown files.name_levels
: How many levels down do want to go for target names, and output file names? You use this to have nested targets. For example, for.md
files in the same folder, you'd leave this at the default (1
). If you have folders, and each.md
file is in a subfolder, you'd usename_levels: 2
. So, it's the number of folders deep you want to use for your target names and output files.glob_variables
: Any additional variables you want to pass to the targets generated by this particular factory.
2. Custom target factories
You can also write your own target factories, which we call custom target factories. Custom target factories are Python packages. Once you've installed the package, you can use the target factory just like a built-in factory. To create one, you just have to follow 2 guidelines:
2.1. Add entry_points to setup.py
The setup.py
file uses entry_points
to map target factory name to function.
entry_points={
'markmeld.factories': 'factory_name=packagename:my_function',
}
The format is: 'markmeld.factories': 'FACTORY_NAME=FACTORY_PACKAGE_NAME:FUNCTION_NAME'.
- "FACTORY_NAME" can be any unique identifier for your factory
- "FACTORY_PACKAGE_NAME" must be the name of python package the holds your factory.
- "FUNCTION_NAME" must match the name of the function in your package
2.2. Write functions to call
The factory function name must correspond to what you specify in setup.py
in the entry points above. These functions must take exactly 2 arguments. The first is a Python dict
object representing variables configuring this factory, the second is the parent config file object. The function must return a targets
object. The dict
object provided will be any additional variables given by the user in _markmeld.yaml
, which is how users can parameterize the factory. For example:
target_factories:
- glob:
path: "*/*.md"
name: "folder"
Markmeld will pass this object to the registered function for the glob
factory:
{ path: "*/*.md"
name: "folder": }
The function is expected to return a targets
object, that will be used to update
the targets
object specified in _markmeld.yaml
.
[^1]: Name borrowed from the excellent targets R package.