Skip to content

Commit

Permalink
Use Jinja2 template in interactive app command
Browse files Browse the repository at this point in the history
  • Loading branch information
sverhoeven committed Nov 13, 2023
1 parent 0fc8183 commit 02c142d
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 19 deletions.
2 changes: 1 addition & 1 deletion config-example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ job_root_dir: /tmp/jobs
destination_picker: bartender.picker:pick_first
applications:
wc:
command: wc $config
command: wc config
config: README.md
destinations:
local:
Expand Down
26 changes: 15 additions & 11 deletions config-haddock3.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ interactive_applications:
rescore:
command: >
haddock3-re score \
--w_elec $w_elec --w_vdw $w_vdw --w_desolv $w_desolv --w_bsa $w_bsa --w_air $w_air \
$capri_dir
--w_elec {{w_elec|q}} --w_vdw {{w_vdw|q}} --w_desolv {{w_desolv|q}} --w_bsa {{w_bsa|q}} --w_air {{w_air|q}} \
{{ capri_dir }}
description: Rescore a HADDOCK run with different weights.
input:
$schema: https://json-schema.org/draft/2020-12/schema
Expand Down Expand Up @@ -60,9 +60,15 @@ interactive_applications:
reclustrmsd:
command: >
haddock3-re clustrmsd
--criterion $criterion
--n_clusters $n_clusters --clust_cutoff $clust_cutoff --min_population $min_population
$clustrmsd_dir
{% if criterion == 'maxclust' -%}
--n_clusters {{n_clusters|q}}
{% else -%}
--clust_cutoff {{clust_cutoff|q}}
{% endif -%}
{% if min_population -%}
--min_population {{min_population|q}}
{% endif -%}
{{clustrmsd_dir|q}}
description: Recluster a HADDOCK run with RSMD and different parameters.
input:
$schema: https://json-schema.org/draft/2020-12/schema
Expand All @@ -72,24 +78,22 @@ interactive_applications:
type: string
criterion:
type: string
enum: [maxclust, distance]
clust_cutoff:
type: number
n_clusters:
type: number
min_population:
type: number
required:
- clustrmsd_dir
- criterion
- clust_cutoff
- n_clusters
- min_population
- clustrmsd_dir
type: object
reclustfcc:
command: >
haddock3-re clustfcc
--clust_cutoff $clust_cutoff --strictness $strictness --min_population $min_population
$clustfcc_dir
--clust_cutoff {{clust_cutoff|q}} --strictness {{strictness|q}} --min_population {{min_population|q}}
{{clustfcc_dir|q}}
description: Recluster a HADDOCK run with FCC and different parameters.
input:
$schema: https://json-schema.org/draft/2020-12/schema
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ python-jose = {extras = ["cryptography"], version = "^3.3.0"}
# poetry has upper limit for jsonschema, so lowering ourselves to it
# unreleased poetry > 1.6.1 will use latest fastjsonschema
jsonschema = "<4.18.0"
jinja2 = "^3.1.2"

[tool.poetry.group.dev.dependencies]
pytest = "^7.0"
Expand Down
15 changes: 9 additions & 6 deletions src/bartender/web/api/job/interactive_apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
from asyncio.subprocess import PIPE
from pathlib import Path
from shlex import quote
from string import Template
from typing import Any

from jinja2 import Environment
from jsonschema import Draft202012Validator
from pydantic import BaseModel

Expand Down Expand Up @@ -50,6 +50,12 @@ async def _shell(job_dir: Path, command: str, timeout: float) -> InteractiveAppR
)


template_environment = (
Environment() # noqa: S701 -- used to generate shell commands not HTML
)
template_environment.filters["q"] = lambda variable: quote(str(variable))


def build_command(
payload: dict[Any, Any],
app: InteractiveApplicationConfiguration,
Expand All @@ -68,11 +74,8 @@ def build_command(
validator = Draft202012Validator(app.input)
validator.validate(payload)

# TODO to allow nested payload we could use a Jinja2 template
# but need to be careful with newlines
return Template(app.command).substitute(
{key: quote(str(value)) for key, value in payload.items()},
)
template = template_environment.from_string(app.command)
return template.render(**payload)


async def run(
Expand Down
34 changes: 33 additions & 1 deletion tests/web/test_interactive_apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
@pytest.fixture
def app_config() -> InteractiveApplicationConfiguration:
return InteractiveApplicationConfiguration(
command="cat ${input_file} | wc -m > ${output_file}",
command="cat {{ input_file|q }} | wc -m > {{ output_file|q }}",
input={
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
Expand Down Expand Up @@ -77,3 +77,35 @@ def test_build_command(
) -> None:
command = build_command(payload, app_config)
assert command == expected


@pytest.mark.parametrize(
"payload, expected",
[
(
{},
"ls",
),
(
{"recursive": True},
"ls --recursive",
),
(
{"recursive": False},
"ls",
),
],
)
def test_build_command_optional_field(payload: dict[Any, Any], expected: str) -> None:
config = InteractiveApplicationConfiguration(
command="ls{% if recursive %} --recursive{% endif %}",
input={
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"recursive": {"type": "boolean"},
},
},
)
command = build_command(payload, config)
assert command == expected

0 comments on commit 02c142d

Please sign in to comment.