|
3 | 3 | [](https://github.com/tool-spec/tool_template_python/actions/workflows/docker-image.yml) |
4 | 4 | [](https://zenodo.org/badge/latestdoi/558416591) |
5 | 5 |
|
6 | | -This is the template for a generic containerized Python tool following the [Tool Specification](https://tool-spec.github.io/tool-specs/) for reusable research software using Docker. |
| 6 | +Template repository for building a Python tool that follows the [Tool Specification](https://tool-spec.github.io/tool-specs/) container contract. |
7 | 7 |
|
8 | | -This template can be used to generate new Github repositories from it. |
| 8 | +## How `gotap` works here |
9 | 9 |
|
| 10 | +This template ships with [`gotap`](https://github.com/tool-spec/gotap) inside the image. The default container command is: |
10 | 11 |
|
11 | | -## How generic? |
| 12 | +```Dockerfile |
| 13 | +CMD ["gotap", "run", "foobar", "--input-file", "/in/input.json"] |
| 14 | +``` |
12 | 15 |
|
13 | | -Tools using this template can be run by the [toolbox-runner](https://github.com/tool-spec/tool-runner). |
14 | | -That is only convenience, the tools implemented using this template are independent of any framework. |
| 16 | +At build time, `gotap generate` creates `parameters.py` from `src/tool.yml`. At runtime, `run.py` uses the generated bindings to: |
15 | 17 |
|
16 | | -The main idea is to implement a common file structure inside container to load inputs and outputs of the |
17 | | -tool. The template shares this structures with the [R template](https://github.com/tool-spec/tool_template_r), |
18 | | -[NodeJS template](https://github.com/tool-spec/tool_template_node) and [Octave template](https://github.com/tool-spec/tool_template_octave), |
19 | | -but can be mimiced in any container. |
| 18 | +- validate `/in/input.json` |
| 19 | +- load typed parameters and data paths |
| 20 | +- emit structured run logs |
20 | 21 |
|
21 | | -Each container needs at least the following structure: |
| 22 | +## Required file structure |
22 | 23 |
|
23 | | -``` |
| 24 | +```text |
24 | 25 | / |
25 | 26 | |- in/ |
26 | | -| |- parameters.json |
| 27 | +| |- input.json |
27 | 28 | |- out/ |
28 | 29 | | |- ... |
29 | 30 | |- src/ |
30 | 31 | | |- tool.yml |
31 | 32 | | |- run.py |
| 33 | +| |- parameters.py (generated at build time) |
| 34 | +| |- CITATION.cff |
32 | 35 | ``` |
33 | 36 |
|
34 | | -* `parameters.json` are parameters. Whichever framework runs the container, this is how parameters are passed. |
35 | | -* `tool.yml` is the tool specification. It contains metadata about the scope of the tool, the number of endpoints (functions) and their parameters |
36 | | -* `run.py` is the tool itself, or a Python script that handles the execution. It has to capture all outputs and either `print` them to console or create files in `/out` |
37 | | - |
38 | | -## How to build the image? |
39 | | - |
40 | | -You can build the image from within the root of this repo by |
41 | | -``` |
42 | | -docker build -t tbr_python_tempate . |
43 | | -``` |
| 37 | +- `/in/input.json` contains parameter values and data references |
| 38 | +- `/out/` receives generated files plus `gotap` metadata such as `_metadata.json` |
| 39 | +- `/src/tool.yml` defines the tool metadata and command |
| 40 | +- `/src/run.py` is the tool entrypoint referenced by `tool.yml` |
44 | 41 |
|
45 | | -Use any tag you like. If you want to run and manage the container with [toolbox-runner](https://github.com/tool-spec/tool-runner) |
46 | | -they should be prefixed by `tbr_` to be recognized. |
| 42 | +## Build and run |
47 | 43 |
|
48 | | -Alternatively, the contained `.github/workflows/docker-image.yml` will build the image for you |
49 | | -on new releases on Github. You need to change the target repository in the aforementioned yaml. |
| 44 | +Build the image from the template root: |
50 | 45 |
|
51 | | -## How to run? |
| 46 | +```bash |
| 47 | +docker build -t tbr_python_template . |
| 48 | +``` |
52 | 49 |
|
53 | | -This template installs the json2args python package to parse the parameters in the `/in/parameters.json`. This assumes that |
54 | | -the files are not renamed and not moved and there is actually only one tool in the container. For any other case, the environment variables |
55 | | -`PARAM_FILE` can be used to specify a new location for the `parameters.json` and `TOOL_RUN` can be used to specify the tool to be executed. |
56 | | -The `run.py` has to take care of that. |
| 50 | +Run the sample tool with the bundled input and output folders: |
57 | 51 |
|
58 | | -To invoke the docker container directly run something similar to: |
59 | | -``` |
60 | | -docker run --rm -it -v /path/to/local/in:/in -v /path/to/local/out:/out -e TOOL_RUN=foobar tbr_python_template |
| 52 | +```bash |
| 53 | +docker run --rm -it \ |
| 54 | + -v "$(pwd)/in:/in" \ |
| 55 | + -v "$(pwd)/out:/out" \ |
| 56 | + -e TOOL_RUN=foobar \ |
| 57 | + tbr_python_template |
61 | 58 | ``` |
62 | 59 |
|
63 | | -Then, the output will be in your local out and based on your local input folder. Stdout and Stderr are also connected to the host. |
| 60 | +`TOOL_RUN` is only needed when the image contains more than one tool entry. The primary runtime contract is still `/in/input.json` plus `gotap run`. |
64 | 61 |
|
65 | | -With the [toolbox runner](https://github.com/tool-spec/tool-runner), this is simplyfied: |
| 62 | +## Customize |
66 | 63 |
|
67 | | -```python |
68 | | -from toolbox_runner import list_tools |
69 | | -tools = list_tools() # dict with tool names as keys |
70 | | - |
71 | | -foobar = tools.get('foobar') # it has to be present there... |
72 | | -foobar.run(result_path='./', foo_int=1337, foo_string="Please change me") |
73 | | -``` |
74 | | -The example above will create a temporary file structure to be mounted into the container and then create a `.tar.gz` on termination of all |
75 | | -inputs, outputs, specifications and some metadata, including the image sha256 used to create the output in the current working directory. |
| 64 | +1. Update `src/tool.yml` to describe your real tool. |
| 65 | +2. Add Python or system dependencies in `Dockerfile`. |
| 66 | +3. Implement the tool logic in `src/run.py`. |
| 67 | +4. Rebuild the image so `gotap generate` refreshes `parameters.py`. |
76 | 68 |
|
77 | | -## What about real tools, no foobar? |
| 69 | +## Generated bindings and logging |
78 | 70 |
|
79 | | -Yeah. |
| 71 | +The generated `parameters.py` file is not edited by hand. It exposes: |
80 | 72 |
|
81 | | -1. change the `tool.yml` to describe your actual tool |
82 | | -2. add any `pip install` or `apt-get install` needed to the dockerfile |
83 | | -3. add additional source code to `/src` |
84 | | -4. change the `run.py` to consume parameters and data from `/in` and useful output in `out` |
85 | | -5. build, run, rock! |
| 73 | +- `get_parameters()` |
| 74 | +- `get_data()` |
| 75 | +- `get_run_context()` |
| 76 | +- `get_logger()` |
86 | 77 |
|
| 78 | +The starter code uses `get_logger()` to write structured JSON Lines logs to the file chosen by `gotap`. |
0 commit comments