3.2. Templating#
PyPackIT’s templating mechanism enables the use of all control center contents throughout the project. Within the control center configuration files you can use any control center content by referencing its path, which allows you to reuse values for different configurations, or to dynamically construct more complex values. Since you can use the control center to generate any file in your repository, this effectively renders your entire project highly dynamic, flexible, and customizable, while eliminating data redundancy and ensuring consistency across all project resources. Similarly, for website files, Jinja templating is enabled and the entire control center content is passed to the Sphinx environment during builds. This also enables you to create sophisticated webpages that are automatically updated with any changes to the control center.
3.2.1. Configuration Files#
Within the control center YAML files,
you can reuse any value by referencing its path using the ${{ PATH }}
substitution syntax:
${{
denotes the start of the substitution.PATH
is a JSONPath expression pointing to the value you want to substitute, only without the leading$.
.}}
denotes the end of the substitution.Any whitespaces between
PATH
and the curly braces are optional and ignored.
Example
In the control center, you can set a title for your project at $.title
.
By default, this is reused in several other places,
such as the description of your GitHub repository,
which can be set at $.repo.description
.
Looking at the default configuration file .control/vcs.yaml
,
you can see the following segment,
which sets the value of $.repo.description
to the value of $.title
:
repo:
description: ${{ title }}
This sounds similar to YAML anchors and references, but it has several key advantages over them:
Multi File Support: Anchors and references are limited to the same file, while substitutions can reference any part of the control center.
Complex Expressions: Substitutions allow the use of JSONPath expressions, which can be used for complex queries, e.g., to dynamically build an array of values from different parts of the control center. For example, if you need a list of all team members’ full names, you can use the following JSONPath expression:
${{ team[*].name.full }}
.Recursive Substitutions: Substitutions are evaluated recursively, meaning that you can use substitutions to reference a content that itself contains substitutions. You only need to make sure not to create any circular references, otherwise you will get an error.
String Substitution: Substitutions can be used within strings, enabling the dynamic construction of complex values from multiple parts of the control center. For example, the title used in your project’s citation, configurable at
$.citation.title
, is set to${{ name }}: ${{ title }}
, so, assuming your project’s$.name
and$.title
areMyProject
andA Great Project
, respectively, the citation title will beMyProject: A Great Project
. Note that if you reference a value that is not a string, it will be converted to a string before being substituted. In contrast, when the entire value is being substituted (i.e., the entire string is only the substitution syntax), the substituted value will have the same type as the referenced value.Relative Paths: Substitutions can use relative paths, which are resolved relative to the path of the value using the substitution. This is particularly useful when using substitutions at locations that do not have a fixed path. For example, assume you have a sequence of mappings where some value in each mapping depends on other values in the same mapping. You can of course use absolute paths and reference each mapping by its sequence index, but then you would need to update all references every time you add or remove a mapping, since indices would change.
To simplify such cases, PyPackIT extends the JSONPath syntax to enable using relative paths, as follows:
When a path starts with one or more periods (
.
), it is considered a relative path.One period refers to the path of the complex data structure (i.e., mapping or sequence) containing the value using the substitution. That is, if the substitution syntax is used in the value of a key in a mapping,
.
refers to the path of that mapping. Similarly, if the substitution syntax is used in the value of an element in a sequence,.
refers to the path of that sequence.Each additional period refers to the path of the parent complex data structure of the previous path, following the same logic.
Example
This feature is used to dynamically set default values for new pieces of data that are added to the control center. For example, the control center has configurations for entering the
email
and other social accounts of each team member. Each of these are a mapping including the keysid
andurl
. By default, theurl
key of each mapping is built using string substitution with relative paths. For example,email.url
is set tomailto:${{ .id }}
, andlinkedin.url
is set tohttps://linkedin.com/in/${{ .id }}
.