Troubleshooting

During development, this parser provides a few diagnostic tools that can help you understand how your configuration is being parsed and interpreted. These tools are especially useful when you are debugging configuration issues or verifying new features.

Displaying the Parsed Document as a Value Tree

Sometimes it’s useful to see the entire document as a value tree. You can do this for the whole document or for a specific section using the to_test_value_tree() method.

import erbsland.conf as elcl

try:
    doc = elcl.load("configuration.elcl")

    print(doc.to_test_value_tree(
        elcl.TestOutput.MINIMAL_ESC |
        elcl.TestOutput.POSITION |
        elcl.TestOutput.SOURCE_ID |
        elcl.TestOutput.ALIGN_VALUES))

except elcl.Error as e:
    print(f"Failed to load configuration: {e}")

Running this example will produce output like the following:

<Document>               => Document()[A:1:1]
├───main                 => SectionWithNames()[A:1:1]
│   ├───listener         => Boolean(true)[A:2:22]
│   ├───polling_interval => TimeDelta(1000,millisecond)[A:3:22]
│   └───server           => SectionWithNames()[A:5:1]
│       ├───name         => Text("host01.example.com")[A:6:22]
│       └───port         => Integer(8080)[A:7:22]
└───client               => SectionList()[A:9:1]
    ├───[0]              => SectionWithNames()[A:9:1]
    │   ├───name         => Text("client01")[A:10:22]
    │   └───ip           => Text("192.168.1.10")[A:11:22]
    ├───[1]              => SectionWithNames()[A:13:1]
    │   ├───name         => Text("client02")[A:14:22]
    │   ├───ip           => Text("192.168.1.11")[A:15:22]
    │   ├───port         => Integer(9100)[A:16:22]
    │   └───filter       => SectionWithNames()[A:18:1]
    │       └───keywords => ValueList()[A:20:22]
    │           ├───[0]  => Text("tree")[A:20:24]
    │           ├───[1]  => Text("leaf")[A:21:24]
    │           ├───[2]  => Text("branch")[A:22:24]
    │           └───[3]  => Text("flower")[A:23:24]
    └───[2]              => SectionWithNames()[A:25:1]
        ├───name         => Text("client03")[A:26:22]
        ├───ip           => Text("192.168.1.12")[A:27:22]
        └───port         => Integer(9170)[A:28:22]
A: file:(...)/configuration.elcl

In this display:

  • The left side shows the hierarchical tree of names and sections.

  • The right side shows the parsed value assigned to each entry.

This view is very handy for confirming that your configuration is parsed into the structure you expect. You can also adjust the formatting with flags from TestOutput to match your debugging needs.

Retrieving All Values as a Flat Dictionary

Another way to inspect the parsed document is by converting it into a flat dictionary of key-value pairs. This can be done using the to_flat_dict() method (available only on the root document).

import erbsland.conf as elcl

try:
    doc = elcl.load("configuration.elcl")
    for key, value in doc.to_flat_dict().items():
        print(f"{key}: {value}")

except elcl.Error as e:
    print(f"Failed to load configuration: {e}")

Running this code produces output like the following:

client: SectionList(name=client, size=3)
client[0]: SectionWithNames(name=[0], size=2)
client[0].ip: Text(name=ip, data="192.168.1.10")
client[0].name: Text(name=name, data="client01")
client[1]: SectionWithNames(name=[1], size=4)
client[1].filter: SectionWithNames(name=filter, size=1)
client[1].filter.keywords: ValueList(name=keywords, size=4)
client[1].filter.keywords[0]: Text(name=[0], data="tree")
client[1].filter.keywords[1]: Text(name=[1], data="leaf")
client[1].filter.keywords[2]: Text(name=[2], data="branch")
client[1].filter.keywords[3]: Text(name=[3], data="flower")
client[1].ip: Text(name=ip, data="192.168.1.11")
client[1].name: Text(name=name, data="client02")
client[1].port: Integer(name=port, data=9100)
client[2]: SectionWithNames(name=[2], size=3)
client[2].ip: Text(name=ip, data="192.168.1.12")
client[2].name: Text(name=name, data="client03")
client[2].port: Integer(name=port, data=9170)
main: SectionWithNames(name=main, size=3)
main.listener: Boolean(name=listener, data=True)
main.polling_interval: TimeDelta(name=polling_interval, data=1000 milliseconds)
main.server: SectionWithNames(name=server, size=2)
main.server.name: Text(name=name, data="host01.example.com")
main.server.port: Integer(name=port, data=8080)

This representation is especially useful if you want a quick overview of all resolved values without navigating the hierarchy.

Error Handling →