Script (Single Case)¶
StepUp Core implements a simple script protocol for defining scripts that combine planning and execution in a single source file.
This can be more convenient than putting a lot of detail in the plan.py
file.
Script Protocol¶
The script()
protocol itself is rather simple.
The following line in plan.py
:
is roughly equivalent to:
Note that the use of a subdirectory is not required.
The ./executable plan
step is expected to define additional steps to actually run something useful with the executable.
A common scenario is to plan a single ./executable run
step with appropriate inputs and outputs.
When the optional=True
keyword argument is given to the script()
function,
it executes ./executable plan --optional
.
The script protocol requires that all run steps created by this planning step should then receive the optional=True
keyword argument.
Note that the plan step itself is never an optional step:
It is always executed.
Script driver¶
StepUp implements a driver
function in the module stepup.core.script
that greatly facilitates
writing Python scripts that adhere to the script protocol.
It can be used in two ways:
-
To run the executable for just one specific case of inputs and outputs (this tutorial).
-
To run the same script with multiple combinations of inputs and outputs (next tutorial).
Single Case Script Driver¶
A Python script using the driver for a single case has the following structure.
#!/usr/bin/env python
from stepup.core.script import driver
def info():
return {
"inp": ..., # a single input path or a list of input paths
"out": ..., # a single output path or a list of input paths
"static": ..., # declare a static file or a list of static files
"stdout": ..., # redirect the standard output to a file (StepUp 1.3.0)
"stderr": ..., # redirect the standard error to a file (StepUp 1.3.0)
"just_any": "argument that you want to add",
}
def run(inp, out, just_any):
...
if __name__ == "__main__":
driver()
-
The
info
function provides the data necessary to plan the execution of the script. It is executed when calling the script as./script.py plan
.Note
All dictionary items are optional. The
info
function can even return an empty dictionary. The keysinp
,out
,static
,stdout
andstderr
affect the planning the run part, as explained in the comments above. -
The
run
function is called to perform the useful work and is executed when the script is executed as./script.py run
.Note
The
run
function can have any argument defined in the dictionary returned byinfo
, but it does not have to specify all of them. The argument list ofrun
can contain fewer arguments (or even none at all).
Example¶
Example source files: getting_started/script_single/
Consider a script that has parameters defined in a config file config.json
,
which may be used by multiple script, e.g. for reasons of consistency.
For this example, the configuration contains a number of steps and a frequency in arbitrary units,
serialized in a JSON file:
This file is used in a script generate.py
as follows:
#!/usr/bin/env python
import json
import numpy as np
from stepup.core.script import driver
def info():
return {
"inp": "config.json",
"out": ["cos.npy", "sin.npy"],
}
def run(inp, out):
with open("config.json") as fh:
config = json.load(fh)
nstep = config["nstep"]
freq = config["freq"]
np.save(out[0], np.cos(2 * np.pi * freq * np.arange(nstep)))
np.save(out[1], np.sin(2 * np.pi * freq * np.arange(nstep)))
if __name__ == "__main__":
driver()
Also, add the following plan.py
file:
#!/usr/bin/env python
from stepup.core.api import script, static
static("generate.py", "config.json")
script("generate.py")
Finally, make the Python scripts executable and give StepUp a spin:
You should see the following output on screen:
DIRECTOR │ Listening on /tmp/stepup-########/director
DIRECTOR │ Launched worker 0
PHASE │ run
START │ ./plan.py
SUCCESS │ ./plan.py
START │ ./generate.py plan
SUCCESS │ ./generate.py plan
START │ ./generate.py run
SUCCESS │ ./generate.py run
WORKFLOW │ Dumped to .stepup/workflow.mpk.xz
DIRECTOR │ Stopping workers.
DIRECTOR │ See you!
As expected, this creates two files: cos.npy
and sin.npy
.
Try the Following:¶
-
Modify the file
config.json
and re-run StepUp. The planning is skipped because the script itself did not change. Only the run function is called to work with the updatedconfig.json
. -
Delete one of the outputs and rerun StepUp. Again, the planning is skipped and the computation is repeated to recreate the missing output.
-
Create a new module
utils.py
with acompute
function to calculate the cosine and sine arrays with parametersnstep
andfreq
. Import this module intogenerate.py
, use it inrun
and re-run StepUp. This will automatically makeutils.py
an input for the planning and running ofgenerate.py
. Test this by making a small change toutils.py
and re-running it. (Note that local imports inside therun
function will not be identified automatically and are therefore not recommended.)