ckdl Python Package

Download and install the ckdl Python Package using pip:

% pip install ckdl

You can also build and install from source using pip:

% pip install path/to/ckdl/source

or some other PEP 517 compatible tool.

Note

As always, it is recommended to work in a venv

The Python ckdl API

A Brief Tour

Reading

>>> import ckdl
>>> kdl_txt = """
... node1 1 2 "three" (f64)4.0 {
...     (some-type)"first child"
...     child-2 prop="val"
... }
... """
>>> doc = ckdl.parse(kdl_txt)
>>> doc
<Document; 1 nodes>
>>> doc.nodes
[<Node node1; 4 args; 2 children>]
>>> doc.nodes[0]
<Node node1; 4 args; 2 children>
>>> doc[0].type_annotation
>>> doc[0].args
[1, 2, 'three', <Value (f64)4>]
>>> type(doc[0].args[0])
<class 'int'>
>>> type(doc[0].args[3])
<class 'ckdl._ckdl.Value'>
>>> doc[0].args[3]
<Value (f64)4>
>>> doc[0].args[3].type_annotation
'f64'
>>> doc[0].children
[<Node (some-type)"first child">, <Node child-2; 1 property>]
>>> doc[0].children[0].type_annotation
'some-type'
>>> doc[0].children[1].properties
{'prop': 'val'}
>>>
>>> # KDL v2 can be enabled like this
>>>
>>> doc2 = ckdl.parse("one 2 three #inf", version="detect")
>>> doc2.nodes
[<Node one; 3 args>]
>>> doc2.nodes[0].args
[2, 'three', inf]
>>>

Writing

>>> mydoc = ckdl.Document(
...     ckdl.Node("node1", args=["argument 1", 2, None],
...                        properties={"node1-prop": 0xff_ff},
...                        children=[
...                            ckdl.Node("child1"),
...                            ckdl.Node("child2-type", "child2", child2_prop=True)
...                        ]),
...     ckdl.Node(None, "node2", "arg1", "arg2", ckdl.Node("child3"), some_property="foo")
... )
>>> print(str(mydoc))
node1 "argument 1" 2 null node1-prop=65535 {
    child1
    (child2-type)child2 child2_prop=true
}
node2 "arg1" "arg2" some_property="foo" {
    child3
}
>>>
>>> # KDL v2 output
>>>
>>> print(mydoc.dump(ckdl.EmitterOptions(version=2)))
node1 "argument 1" 2 #null node1-prop=65535 {
    child1
    (child2-type)child2 child2_prop=#true
}
node2 arg1 arg2 some_property=foo {
    child3
}

API

The ckdl package is relatively simple. It provides one function to parse KDL, three classes to represent data, and some classes to optionally configure the emitter.

Parsing

ckdl.parse(kdl_doc, *, version=1)

Parse a KDL document

Parameters:
  • kdl_doc (str) – The KDL document to parse

  • version – Which version(s) to accept: 1 for KDLv1 only, 2 for KDLv2 only, or either None or "detect" to support both.

Return type:

Document

Raises:

ParseError

exception ckdl.ParseError

Thrown by parse() when the CKDL parser cannot parse the document (generally because it’s ill-formed).

Data types

class ckdl.Value(type_annotation: str, value)

A KDL value with a type annotation.

Values without a type annotation are represented as NoneType, bool, int, float, or str.

type_annotation

The type annotation of the value

Type:

str

value

The actual value

class ckdl.Node

A KDL node, with its arguments, properties and children

type_annotation

Type annotation as str or NoneType

name

Node name - str

args

Node args - list

properties

Node properties - dict

children

Child nodes - list of Node

The Node constructor supports a number of different signatures.

If the first two arguments are strings, or None and a string, they are interpreted as the type annotation and the node tag name. Then, either:

  • Node([type_annotation,] name, *args, *children, **properties)
    the remaining positional arguments are all the node arguments, followed by the child nodes, and the keyword arguments are the properties, or
  • Node([type_annotation,] name, [args, [children, ]] *, **properties)
    the next positional arguments are lists of all the arguments and children, and the keyword arguments are the properties, or
  • Node([type_annotation,] name, [args=..., [children=..., ]] *, [properties=...])
    the properties are passed as a dict in the properties keyword argument, the arguments are passed as a list either in the args keyword argument, or the positional argument after the tag name, and the children are similarly passed as a list, either in the children keyword argument, or in the positional argument following the node arguments.

Note that when the node arguments are given as positional arguments, and the first argument is a string, the type annotation cannot be omitted (Node("name", "arg", 1) is (name)arg 1, and Node(None, "name", "arg", 1) is name "arg" 1, but Node("name", 1, 2) is name 1 2).

class ckdl.Document(nodes)

A KDL document, consisting of zero or more nodes.

nodes

The top-level nodes in the document - list of Node

dump(self[, opts : EmitterOptions])

Serialize the document to KDL

Parameters:

opts – (optional) Options for the ckdl emitter

__str__(self)

See dump()

Emitter configuration

class ckdl.EmitterOptions(*, indent=None, escape_mode=None, identifier_mode=None, float_mode=None, version=None)
indent

Number of spaces to indent child nodes by (default: 4)

Type:

int

escape_mode

Which characters should be escaped in regular strings?

Type:

EscapeMode

identifier_mode

How should identifiers (i.e., node names, type annotations and property keys) be rendered?

Type:

IdentifierMode

float_mode

How exactly should doubles be formatted?

Type:

FloatMode

version

Which KDL version to emit? The constructor accepts KdlVersion and int.

Type:

KdlVersion

class ckdl.EscapeMode

Enum

minimal
control
newline
tab
ascii_mode
default
class ckdl.IdentifierMode

Enum

prefer_bare_identifiers
quote_all_identifiers
ascii_identifiers
class ckdl.FloatMode(*, always_write_decimal_point=None, always_write_decimal_point_or_exponent=None, capital_e=None, exponent_plus=None, plus=None, min_exponent=None)
always_write_decimal_point
Type:

bool

always_write_decimal_point_or_exponent
Type:

bool

capital_e
Type:

bool

exponent_plus
Type:

bool

plus
Type:

bool

min_exponent
Type:

int

class ckdl.KdlVersion

Enum

kdl_1
kdl_2