Skip to content

ktbarrett/kuddl

Repository files navigation

regressions codecov

Kaleb's (otherwise) Untitled Data Description Language - KUDDL

Combines the cleanliness of YAML, the power of Python, and some ingenuity.

It's like a hug — Paul U.

How It Works

KUDDL uses YAML constructors to turn purely declarative values into dynamic values.

This is accomplished by evaluating Python expressions in strings. The !Eval constructor evaluates a single Python expression using Python's eval. BlockEval offers support for multiple statements; the value returned becomes the value of the document node.

four: !Eval "2 * 2"
fibs: !BlockEval |
  c = [1, 1]
  for i in range(5):
    c.append(c[-1] + c[-2])
  return c
doc = kuddl.load(example_str)
assert doc["four"] == 4
assert doc["fibs"] == [1, 1, 2, 3, 5, 8, 13]

KUDDL allows you to reference other values in the hierarchy in a lexical way. There is also the special variable 'global' which allows you to override lexical scoping; and 'args' which allows the user to pass arguments to the YAML "script".

a: 2
b:
    value: !Eval "a * 3"
c:
- more: !Eval "str(b['value'])"
- !Eval "global['c'][0]['more'] * 3"
doc = kuddl.load(example_str)
assert doc["b"] == 6
assert doc["c"][0]["more"] == "6"
assert doc["c"][1] == "666"

You can describe string templates in KUDDL using the !Template constructor. Variables are reference in the same way as in the rest of KUDDL. Python formatting options that are supported by f-strings are supported in !Templates as well.

a:
  b: 5
c: !Template 1{a['b']}
doc = kuddl.load(example_str)
assert doc["c"] == "15"

It's worth noting that all other KUDDL constructors are also templates. This allows you to do some basic string-based metaprogramming in KUDDL.

Import Python classes, functions, values, etc. using !Import. Import strings follow the convention: package.module:object.value. Combine this with variables for more powerful expressions.

ceil: !Import math:ceil
log2: !Import math:log2
value: !Eval ceil(log2(8))
value2: !Eval ceil(log2(9))
doc = kuddl.load(example_str)
assert doc["value"] == 3
assert doc["value2"] == 4

Split your YAMLs into multiple files and use !Include to join the documents together. Include paths can be relative (to the main process), or absolute.

other_doc: !Include "./other.yaml"
myvar: !Eval other_doc.special_var

Releases

No releases published

Languages