=======================
``ckdl`` Python Package
=======================
.. highlight:: shell-session
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`_
.. _PEP 517: https://peps.python.org/pep-0517/
.. _venv: https://docs.python.org/3/library/venv.html
The Python ``ckdl`` API
-----------------------
A Brief Tour
^^^^^^^^^^^^
Reading
"""""""
.. code-block:: pycon
>>> 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
>>> doc.nodes
[]
>>> doc.nodes[0]
>>> doc[0].type_annotation
>>> doc[0].args
[1, 2, 'three', ]
>>> type(doc[0].args[0])
>>> type(doc[0].args[3])
>>> doc[0].args[3]
>>> doc[0].args[3].type_annotation
'f64'
>>> doc[0].children
[, ]
>>> 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
[]
>>> doc2.nodes[0].args
[2, 'three', inf]
>>>
Writing
"""""""
.. code-block:: pycon
>>> 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
^^^
.. highlight:: python3
.. py:module:: ckdl
.. py:currentmodule:: ckdl
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
"""""""
.. py:function:: parse(kdl_doc, *, version=1)
Parse a KDL document
:param kdl_doc: The KDL document to parse
:type kdl_doc: str
:param version: Which version(s) to accept: ``1`` for KDLv1 only, ``2`` for KDLv2 only,
or either ``None`` or ``"detect"`` to support both.
:rtype: Document
:raises: :py:exc:`ParseError`
.. py:exception:: ParseError
Thrown by :py:func:`parse` when the CKDL parser cannot parse the document (generally
because it's ill-formed).
Data types
""""""""""
.. py:class:: 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.
.. py:attribute:: type_annotation
The type annotation of the value
:type: str
.. py:attribute:: value
The actual value
.. py:class:: Node
A KDL node, with its arguments, properties and children
.. py:attribute:: type_annotation
Type annotation as str or NoneType
.. py:attribute:: name
Node name - str
.. py:attribute:: args
Node args - list
.. py:attribute:: properties
Node properties - dict
.. py:attribute:: children
Child nodes - list of :py:class:`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``).
.. py:class:: Document(nodes)
A KDL document, consisting of zero or more nodes.\
.. py:attribute:: nodes
The top-level nodes in the document - list of :py:class:`Node`
.. py:method:: dump(self[, opts : EmitterOptions])
Serialize the document to KDL
:param opts: (optional) Options for the ckdl emitter
.. py:method:: __str__(self)
See dump()
Emitter configuration
"""""""""""""""""""""
.. py:class:: EmitterOptions(*, indent=None, escape_mode=None, identifier_mode=None, float_mode=None, version=None)
.. py:attribute:: indent
Number of spaces to indent child nodes by (default: 4)
:type: int
.. py:attribute:: escape_mode
Which characters should be escaped in regular strings?
:type: EscapeMode
.. py:attribute:: identifier_mode
How should identifiers (i.e., node names, type annotations and property keys) be rendered?
:type: IdentifierMode
.. py:attribute:: float_mode
How exactly should doubles be formatted?
:type: FloatMode
.. py:attribute:: version
Which KDL version to emit? The constructor accepts :py:class:`KdlVersion` and :py:class:`int`.
:type: KdlVersion
.. py:class:: EscapeMode
Enum
.. py:attribute:: minimal
.. py:attribute:: control
.. py:attribute:: newline
.. py:attribute:: tab
.. py:attribute:: ascii_mode
.. py:attribute:: default
.. py:class:: IdentifierMode
Enum
.. py:attribute:: prefer_bare_identifiers
.. py:attribute:: quote_all_identifiers
.. py:attribute:: ascii_identifiers
.. py:class:: FloatMode(*, always_write_decimal_point=None, always_write_decimal_point_or_exponent=None, capital_e=None, exponent_plus=None, plus=None, min_exponent=None)
.. py:attribute:: always_write_decimal_point
:type: bool
.. py:attribute:: always_write_decimal_point_or_exponent
:type: bool
.. py:attribute:: capital_e
:type: bool
.. py:attribute:: exponent_plus
:type: bool
.. py:attribute:: plus
:type: bool
.. py:attribute:: min_exponent
:type: int
.. py:class:: KdlVersion
Enum
.. py:attribute:: kdl_1
.. py:attribute:: kdl_2