Creating, Configuring, and Packaging the Initial Template
Prerequisites
It is assumed the VSCode Plugin and uip-cli version 2.0.0 are installed.
The development environment will be WSL (Windows Subsystem for Linux), however, it could be any of the supported platforms. The tutorial assumes a folder called demo_template
has been opened in VSCode.
Introduction
The custom, starter template that we will create is a contrived example, but it sufficiently covers all the features and its usefulness.
Step 1 - Create the Initial Template
To create a custom starter template, we will first make use of the built-in ue-task
template. Open up the terminal to the demo_template
folder and run
uip init -t ue-task -e "extension_name=my_custom_ext" -e "universal_template_name=my_custom_template"
The directory structure should be as follows (not showing the Python *.pyc
/__pycache__
files):
demo_template/ ├── .uip │ └── config │ └── uip.yml ├── __init__.py ├── requirements.txt ├── setup.cfg ├── setup.py └── src ├── __init__.py ├── extension.py ├── extension.yml └── templates └── template.json
The basic structure of the custom template is almost finished! Go ahead and create a file called template_config.yml
at the same level as the src/
folder. The resulting directory tree is as follows:
demo_template/ ├── .uip │ └── config │ └── uip.yml ├── __init__.py ├── requirements.txt ├── setup.cfg ├── setup.py ├── src │ ├── __init__.py │ ├── extension.py │ ├── extension.yml │ └── templates │ └── template.json └── template_config.yml
Step 2 - Configuring the Initial Template
To make the template configurable, add the following content below to template_config.yml
:
name: example-template version: 1.0.0 description: this is the description for example template files_to_template: - src/extension.py - src/templates/template.json variables: msg: default: test_message description: message to print to STDOUT and STDERR log_level: default: Info description: Universal Template Log Level
template_config.yml details
Each template_config.yml
must contain:
name
A string that identifies the name of the template (this is NOT referring to the Universal Template name). It can be anything other than the name of the built-in templates:
ue-task
andue-publisher
.
version
A string that identifies the template version. Not restricted to
x.y.z
(SemVer); it could be anything (e.g.v1
).
description
A string describing the template.
Although not required, it can also contain:
files_to_template
An array containing paths to files that will be “templated” using
Jinja2
. All files must be specified relative totemplate_config.yml
.
variables
A mapping/dictionary of variables that will be substituted in the relevant files specified by
files_to_template
. The value of each key/variable is another mapping/dictionary that must containdefault
anddescription
.
Now, open src/extension.py
and replace extension_start()
with the code below:
def extension_start(self, fields): """Required method that serves as the starting point for work performed for a task instance. Parameters ---------- fields : dict populated with field values from the associated task instance launched in the Controller Returns ------- ExtensionResult once the work is done, an instance of ExtensionResult must be returned. See the documentation for a full list of parameters that can be passed to the ExtensionResult class constructor """ my_msg = "{{ msg }}" # Get the value of the 'action' field action_field = fields.get('action', []) if len(action_field) != 1: # 'action' field was not specified or is invalid action = '' else: action = action_field[0] if action.lower() == 'print': # Print to standard output... print(my_msg) else: # Log to standard error... logger.info(my_msg) # Return the result with a payload containing a Hello message... return ExtensionResult( unv_output='Hello Extension!' )
On line 19, a variable called my_msg
is defined with the value of "{{ msg }}"
. This is Jinja2 syntax that will eventually be replaced with the specified value for msg
(or the default, if they don’t specify anything) during initialization time.
On lines 31 and 34, the message was changed to print the value of my_msg
.
Now, open src/templates/template.json
and replace the value of logLevel
key (should be around line 86) with the change shown below:
"sysId": "535c354ed083489cb10413019d71a57c", "textType": "Plain" } ], "logLevel": "{{ log_level | title }}", "minReleaseLevel": "7.0.0.0", "name": "my_custom_template",
On line 86 of the snippet above, the value of logLevel
was changed from Inherited
to {{ log_level | title }}
. Once again, this is Jinja2 syntax that will eventually be replaced with the specified value of log_level
(or the default, if nothing is specified). Additionally, the built-in title
filter is applied to the value of log_level
. This will ensure values like trACe
end up as Trace
(first letter capitalized and everything else lowercase) as required by template.json
.
Step 3 - Packaging the Initial Template
Although the example is a bit contrived, we have managed to create a fully-functional customized template.
To package the template, simply zip up everything into a file called example_template.zip
(or whatever you want to call it). Its structure should be as follows (if *.pyc
and __pycache__
files are there, it’s fine):
Archive: example_template.zip Length Date Time Name --------- ---------- ----- ---- 0 2023-04-04 15:37 __init__.py 306 2023-04-04 15:37 requirements.txt 43 2023-04-05 11:03 setup.cfg 12088 2023-04-05 11:03 setup.py 0 2023-04-05 11:03 src/ 1723 2023-04-05 11:03 src/extension.py 221 2023-04-05 11:03 src/extension.yml 0 2023-04-05 11:03 src/templates/ 3278 2023-04-05 11:09 src/templates/template.json 0 2023-04-04 15:37 src/__init__.py 349 2023-04-05 11:05 template_config.yml --------- ------- 18008 11 files