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 Python glob (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 use name_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.