0

I have a YAML file

tests:
  - is-gt: val, 2
    info: "Test Succeeded!!, val is greater than 2, it is <{{post['val']}}>"
    err: "Test Failed!!, val is not greater than 2, it is <{{post['val']}}>"

in this case, instead of hard coding value of val(i.e. 2 ), it should directly take value specified with operation "is-gt". If I change test operation to " is-gt: val, 3" , it should automatically take this value inside info and error message.

How do I do this? I am using this YAML file further in some Python script to read it.

dreftymac
  • 31,404
  • 26
  • 119
  • 182
Yash Awasthi
  • 31
  • 1
  • 6
  • I don't think it's possible with just yaml. Tools that let you do that actually post-process the values using, for instance Jinja or `template.Template`. Here for instance you could make the value you test against available as a template variable, under name "value" for instance, and just put `{{value}}` in your string. – spectras Nov 08 '16 at 07:22
  • Possible duplicate of [Is it possible to do string substitution in Yaml?](http://stackoverflow.com/questions/30407488/is-it-possible-to-do-string-substitution-in-yaml) – Anthon Nov 08 '16 at 08:41
  • @spectras can you tell me how is it possible using Jinja ? I am new to this if you can elaborate with an example – Yash Awasthi Nov 08 '16 at 08:49
  • **See also:** https://stackoverflow.com/questions/41620674/use-placeholders-in-yaml – dreftymac Apr 04 '21 at 09:53

2 Answers2

1

Your is-gt is just a scalar string to YAML, that functions as a key for a mapping. There is nothing special about that string, and it is certainly not an "operation".

You can post-process your loaded YAML (e.g. if you use safe_load from ruamel.yaml/PyYAML). What is cleaner YAML, is that you tag your sequence items and, based on that tag, load your mapping key/values into a specific object that interprets is-gt and updates the scalar strings of the other values that become its attributes.

Such an object would best have a map-like interface and you would have to decide to do the resolving at load time, or using lazy evaluation when retrieving the values by its key.

Anthon
  • 69,918
  • 32
  • 186
  • 246
1

YAML is a data serialization language, not a data processing language. Templating capabilities are beyond its scope (although there exist a few optional features in that direction, which are not applicable here).

Postprocessing with other templating languages has already been proposed. I'd like to add the suggestion to use pystache because it is a minimal language which might be more appropriate here than using Jinja.

Furthermore, I'd suggest to put the value spec into a map. And since you already use {{ for something else in the string, I'd suggest to tell pystache to use another means of escaping. Example:

tests:
  - is-gt: {val: 2}
    info: "Test Succeeded!!, val is greater than `val`, it is <{{post['val']}}>"
    err: "Test Failed!!, val is not greater than `val`, it is <{{post['val']}}>"

You can now parse it like this:

import yaml, pystache

raw = yaml.safe_load("""
tests:
  - is-gt: {val: 2}
    info: "Test Succeeded!!, val is greater than `val`, it is <{{post['val']}}>"
    err: "Test Failed!!, val is not greater than `val`, it is <{{post['val']}}>"
""")

def dynString(inputs, template):
    return pystache.render("{{=` `=}}" + template, inputs)

tests = []
for rawtest in raw["tests"]:
    test = {}
    inputs = rawtest["is-gt"]
    for msg in ["info", "err"]:
        test[msg] = dynString(inputs, rawtest[msg])
    tests.append(test)

print(tests)

Which outputs:

[{'info': "Test Succeeded!!, val is greater than 2, it is <{{post['val']}}>",
  'err': "Test Failed!!, val is not greater than 2, it is <{{post['val']}}>"}]
Anthon
  • 69,918
  • 32
  • 186
  • 246
flyx
  • 35,506
  • 7
  • 89
  • 126
  • You still have `post['val']` in your output. On first glance I think you have to populate `post` instead of `inputs`, but I haven't used `pystache` (so I might be wrong). – Anthon Nov 08 '16 at 08:59
  • 1
    Anthon: I believe that this is what is wanted, because `post['val']` accesses the actual value that is compared, while `{val: 2}` specifies the value to be compared against. But I might be wrong, it is not very clear from the question. It was asked how to replace the `2` with the value given in the YAML, which I did. – flyx Nov 08 '16 at 09:04
  • 2
    I missed the substitution of `2` by `val` in your separate input, probably because you still use the hardcoded 2 in the multiline string that you actually `safe_load()` instead of using the backquoted `val`. If you change `2` → `3` in `is-gt`'s value nothing happened. – Anthon Nov 08 '16 at 11:47
  • Oh yes, I oversaw that. Thanks for fixing. – flyx Nov 08 '16 at 11:54
  • @flyx thanks for solution but what if {val: 2} is {//val :2} and as remaining `val` – Yash Awasthi Nov 08 '16 at 14:14
  • @YashAwasthi I do not really get what you're asking. `{//val :2}` does parse as `{"//val :2": ""}` because of missing space after `:`, so that cannot work. But if you have `{//val: 2}`, you can use `//val` as template variable. – flyx Nov 08 '16 at 15:09