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.

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 are MyProject and A Great Project, respectively, the citation title will be MyProject: 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.