Trees

Reconfigure operates with three types of data:

  • Raw config text
  • Syntax tree
  • Data tree

Config text

This is a raw content, as read from the config file. It is fed to Parsers to produce the Syntax trees.

Syntax trees

Syntax tree is an object tree built from reconfigure.nodes.Node objects, representing the syntax structure of the file. This is very similar to Abstract Syntax Trees.

Syntax trees are produced by Parsers classes.

Example:

>>> text = open('/etc/samba/smb.conf').read()
>>> text
'#\n# Sample configuration file for the Samba suite for Debian GNU/Linux.\
...
>>> from reconfigure.parsers import IniFileParser
>>> parser = IniFileParser()
>>> node_tree = parser.parse(text)
>>> print node_tree
(None)
        (global)
                workgroup = WORKGROUP
                server string = %h server (Samba, Ubuntu)
                dns proxy = no
                log file = /var/log/samba/log.%m
                max log size = 1000
                syslog = 0
                panic action = /usr/share/samba/panic-action %d
                encrypt passwords = true
                passdb backend = tdbsam
                obey pam restrictions = yes
                unix password sync = yes
                passwd program = /usr/bin/passwd %u
                passwd chat = *Enter\snew\s*\spassword:* %n\n *Retype\snew\s*\spassword:* %n\n *password\supdated\ssuccessfully* .
                pam password change = yes
                map to guest = bad user
                usershare allow guests = yes
        (printers)
                comment = All Printers
                browseable = no
                path = /var/spool/samba
                printable = yes
                guest ok = no
                read only = yes
                create mask = 0700
>>> node_tree
<reconfigure.nodes.RootNode object at 0x219a150>
>>> node_tree.children[0]
<reconfigure.nodes.Node object at 0x219a950>
>>> node_tree.children[0].name
'global'
>>> node_tree.children[0].children[0]
<reconfigure.nodes.PropertyNode object at 0x219aa10>
>>> node_tree.children[0].children[0].name
'workgroup'
>>> node_tree.children[0].children[0].value
'WORKGROUP'

reconfigure.nodes.Node reference page contains more information on how to manipulate node trees.

Parsers work both ways - you can call stringify() and get the text representation back. Even more, you can feed the node tree to another parser and get the config in other format:

>>> from reconfigure.parsers import JsonParser
>>> json_parser = JsonParser()
>>> json_parser.stringify(node_tree)
>>> print json_parser.stringify(node_tree)
{
    "global": {
        "encrypt passwords": "true",
        "pam password change": "yes",
        "passdb backend": "tdbsam",
        "passwd program": "/usr/bin/passwd %u",
        ...
    },
    "print$": {
        "comment": "Printer Drivers",
        "path": "/var/lib/samba/printers",
        "read only": "yes",
        ...

Syntax trees might look useful to you, but they are not nearly as cool as Data trees

Data trees

Data tree represents the actual, meaningful ideas stored in the config. Straight to example:

>>> from reconfigure.builders import BoundBuilder
>>> from reconfigure.items.samba import SambaData
>>> builder = BoundBuilder(SambaData)
>>> data_tree = builder.build(node_tree)
>>> data_tree
{
    "global": {
        "server_string": "%h server (Samba, Ubuntu)",
        "workgroup": "WORKGROUP",
        "interfaces": "",
        "bind_interfaces_only": true,
        "security": "user",
        "log_file": "/var/log/samba/log.%m"
    },
    "shares": [
        {
            "comment": "All Printers",
            "browseable": false,
            "create_mask": "0700",
            "name": "printers",
            "directory_mask": "0755",
            "read_only": true,
            "guest_ok": false,
            "path": "/var/spool/samba"
        },
        {
            "comment": "Printer Drivers",
            "browseable": true,
            "create_mask": "0744",
            "name": "print$",
            "directory_mask": "0755",
            "read_only": true,
            "guest_ok": false,
            "path": "/var/lib/samba/printers"
        }
    ]
}

>>> data_tree.shares
<reconfigure.items.bound.BoundCollection object at 0x23d0610>
>>> [_.path for _ in data_tree.shares]
['/var/spool/samba', '/var/lib/samba/printers']

Data trees may consist of any Python objects, but the common approach is to use Bound Data

Data trees can be manipulated as you wish:

>>> from reconfigure.items.samba import ShareData
>>> share = ShareData()
>>> share.path = '/home/user'
>>> share.comment = 'New share'
>>> data_tree.shares.append(share)
>>> data_tree
{
    ....
    "shares": [
    {
        "comment": "All Printers",
        "browseable": false,
        "create_mask": "0700",
        "name": "printers",
        "directory_mask": "0755",
        "read_only": true,
        "guest_ok": false,
        "path": "/var/spool/samba"
    },
    {
        "comment": "Printer Drivers",
        "browseable": true,
        "create_mask": "0744",
        "name": "print$",
        "directory_mask": "0755",
        "read_only": true,
        "guest_ok": false,
        "path": "/var/lib/samba/printers"
    },
    {
        "comment": "New share",
        "browseable": true,
        "create_mask": "0744",
        "name": "share",
        "directory_mask": "0755",
        "read_only": true,
        "guest_ok": false,
        "path": "/home/user"
    }
]

After you’re done with the modifications, the data tree must be converted back to the node tree:

>>> node_tree = builder.unbuild(data_tree)