BoaConstructor

URI:http://pypi.python.org/pypi/boaconstructor

What is it?

The boacontructor is a templating library for data. It allows you to construct Python dictionaries from other Template instances, dictionaries or object instances.

How does it work?

The library uses dictionary string values in a particular format. Each format indicates what should be recovered and assigned. The top level data template must be a dictionary. The operations can be nested meaning an operation string can resolve to point at another operation.

There are three types of operation strings which are recognised. A reference-attribute, all-inclusion or a derivefrom string. The power and flexibilty of the template system comes from how and where these are used.

Reference-Attribute

I usually refer to this as a ‘refatt’ string. It is defined as follows:

<reference string>.$.<attribute string>

The ‘.$.’ is the deliminator used to work out which part is the reference and attribute. There must be something either side of the refatt deliminator or the string is ignored.

Example:

# reference "common" data:
common = {"timeout": 10}

# Template
{
  "timeout": "common.$.timeout",
}

# Render time result:
{
  "timeout": 10,
}

The timeout string value is interpreted as find the value of “timeout”, which is an attribute of the reference “common”. When the dict is “rendered” timeout will contain the value 10. The type of the resolved value is preserved.

All-Inclusion

I usually refer to this as a ‘allinc’ string. It is defined as follows:

<inclusion string>.*

The ‘.*’ is the deliminator at the end of a string. Nothing must appear after the allinc ‘.*’ or the string will be ignored.

For Example:

machine = {"address": '127.0.0.1', "port": 8080}

# Template
{
  "host": "machine.*",
}

# Render time result
{
  "host": {"address": '127.0.0.1', "port": 8080}
}

The value of “host” becomes whatever machine resolves as.

There is currently no restriction on machine being a dict. Anything machine points at will be placed as-is. This could be another data type for example string, number, list.

Where the value pointed at is a dictionary, it will be processed until all its references are resolved. The result of this processing will then become the value.

For example:

common = {"timeout": 10}

machine = {"address": '127.0.0.1', "port": 8080, "wait": 'common.$.timeout'}

# Template
{
  "machine": "machine.*",
}

# Render time result
{
  "machine": {"address": '127.0.0.1', "port": 8080, "wait": 10}
}

Derive-From

It is defined as follows:

derivefrom.[<reference string>]

The reference string between the [] brackets referes to the data which will be derived from.

For example:

common = {"timeout": 10}

# The "base" data:
machine = {"address": 'localhost', "port": 8080, "wait": 'common.$.timeout'}

# Template (child of parent base):
{
  "": "derivefrom.[machine]",
  "address": "example.com"
}

# Render time result
{
  "address": 'example',
  "port": 8080,
  "wait": 10
}

The template derives from machine and then overrides the address. Multiple derivefrom operations are not supported. Only one can be used. By convention the key of a derivefrom is left empty or used as a comment.

Quickstart

Two quick examples should give a feel for how boaconstructor works.

Basic Example

I’ll let the code and comments do the talking.

from boaconstructor import Template

# Some shared information in a dict. This could also be a class instance
# or something else that supports getattr and hasattr.
#
common = dict(
    timeout = 30,
    email = "admin@example.com"
)

# This is a template created in a module. You need one of these. I pass in
# references that are available at this stage. The 'host.$.name' I will pass
# in at render time.
#
webserver_data = Template('webserver',
    dict(
        interface = 'host.$.name',
        port = 32189,
        timeout = 'common.$.timeout',
        alert_email = 'common.$.email',
    ),
    # This is uses common as an 'internal' reference
    references={'common':common}
)

# At run time I can pass 'external' references to resolve the hostnames.
# Maybe I got these from a database or some other source.
#
machine_1 = webserver_data.render({'host': {'name': 'myserver1'}}),
# {'alert_email': 'admin@example.com', 'interface': 'myserver1', 'port': 32189, 'timeout': 30}

machine_2 = webserver_data.render({'host': {'name': 'myserver2'}}),
# {'alert_email': 'admin@example.com', 'interface': 'myserver2', 'port': 32189, 'timeout': 30}


# Now I can pass these to Cheetah/Mako/etc to render a specific type of
# XML/INI/Text configuration files.

Complex Example

This shows the construction of a dictionary which makes use of reference-attributes, all inclusion and derive from.

import pprint

from boaconstructor import Template

common = dict(keep='yes', buffer=4096, timeout=30)

peter = dict(username='pstoppard', secret='11ed394')
graham = dict(username='gturner', secret='54jsl31')

# "base" data test1 will derive from an provide an
# alternative name
search = dict(
    name = '<over written in rendered output>',
    search = 'google.com',
    timeout = 'common.$.timeout'
)

test1 = Template(
    'test1',
    {
        "": "derivefrom.[search]",
        "name": 'production',
        "options": 'common.*',
        "usernames": ['peter.$.username','graham.$.username'],
        "users": ['peter.*', 'graham.*'],
    },
)

result = test1.render(
    references={
        'common': common,
        'peter': peter,
        'graham': graham,
        'search': search,
    },
)

pprint.pprint(result)
{'name': 'production',
 'options': {'buffer': 4096, 'timeout': 30, 'keep': 'yes'},
 'search': 'google.com',
 'timeout': 30,
 'usernames': ['pstoppard', 'gturner'],
 'users': [{'username': 'pstoppard', 'secret': '11ed394'},
           {'username': 'gturner', 'secret': '54jsl31'}]}

Project Documentation

The Template class

This is the high level class most commonly used.

class boaconstructor.Template(name, content, references={})

Template represents a dict which may or may not refer to data from other dicts.

Example:

# Data to be used in host1 and host2
common = Template('common', {
    "timeout": 42
})

# Uses data from common:
host1 = Template('host1', {
        "host": "1.2.3.4",
        "flag": False,
        "timeout": 'common.$.timeout'
    },
    references = {'common':common}
)

# Uses data from common and host1
host2 = Template('host2', {
        "host": "4.3.2.1",
        "flag": 'host.$.flag',
        "timeout": 'common.$.timeout',
    },
)

# Render the 'host1' dict:
>> host1.render()
{"host":"1.2.3.4","flag":True,"timeout":42}

# Render the 'host2' dict:
>> host2.render(
    references = {
        'common': common,
        'host': host1,
    }
)
{"host":"1.2.3.4","flag":True,"timeout":42}

Notes:

  • In host2.render(...) above the reference ‘host’ was used as an alias to ‘host1’.
items()

Used in an all-inclusion / render to return our contained content dict.

render(references={})

Generate a data dict from this template and any it references.

Parameters:references – this is a dict of string to template mappings.

This is used to resolve references to other templates. If this is empty self.references will be used instead.

Returns:This returns a ‘rendered’ dict.

All references will have been replaced with the value the point at.

The utils module

The Template class wraps the functionality hiding most of the details of this module.

Exceptions

class boaconstructor.utils.ReferenceError(msg, ref, *args)

Raised when a reference name could not found in references given.

class boaconstructor.utils.AttributeError(msg, attr, *args)

Raised when an attribute was not found for the references given.

class boaconstructor.utils.DeriveFromError(msg, found, *args)

Raised for problems with derivefrom.[<value>] recovered item for the value.

class boaconstructor.utils.MultipleDeriveFromError

Raised while attempting to have multiple derivefroms in the same template.

render

boaconstructor.utils.render(state)

Construct the final dictionary after resolving all references to get their actual values.

Parameters:state – The is an instance of RenderState set up ready from the render process to begin.
Returns:A single dict representing the combination of all parts after references have been resolved.

parse_value

boaconstructor.utils.parse_value(value)

Recover the ref-attr, all-inclusion or derive from if present.

Returns:The results of parsing the given value string.

This is a dict in the form:

dict(
    found='refatt', 'all', 'derivefrom' or None,
    reference='' or '<reference string recovered>',
    attribute='' or '<attribute string recovered>',
    allfrom='' or '<all inclusion string recovered>',
    derivefrom='' or 'derivefrom string recovered',
)

resolve_references

boaconstructor.utils.resolve_references(reference, attribute, int_references, ext_references={})

Work out the attribute value for a reference from the internal or external references.

This is usually call by the Template.resolve method.

Parameters:
  • reference – This is the <reference> string recovered from a call to parse_value.
  • attribute – This is the dict key we must look up in the references.

This is the <attribute> recovered from a call to parse_value. If this is None then only the reference will be resolved. What it points at will then be returned.

Parameters:
  • int_references – This the reference dict usually representing the reference stored as a member of the Template class.
  • ext_references – This the reference dict usually representing the reference given as an argument to render.

The ext_references is given priority over the int_references.

If a reference-attribute is found, then int_references will not be consulted.

If the reference is not found in int_references or ext_references then ReferenceError will be raised.

If the attribute is not found in in the any of the reference dicts, then AttributeError will be raised.

Returns:The value or item pointed at by the reference and / or attribute.

build_ref_cache

boaconstructor.utils.build_ref_cache(int_refs, ext_refs)

Work out all the references and child references from the internal and externally given references.

This in effect flattens the references and making lookup faster for hunt_n_resolve.

Parameters:
  • int_refs – a dict of ‘dicts and/or Template’ instances.
  • ext_refs – a dict of ‘dicts and/or Template’ instances.
Returns:

a dict with the results in the form:

results = {
    'int':{ ... },
    'ext':{ ... }
}

For an example of this see tests/testboacontructor.py:DataTemplate and ‘testBuildRefCache()’.

RenderState

class boaconstructor.utils.RenderState(template, int_refs={}, ext_refs={}, name='', parent=None, reference_cache=None)

This is used to track what is going on where in the render process.

hunt_n_resolve

boaconstructor.utils.hunt_n_resolve(value, state)

Resolve a single attribute using the given reference_cache.

Parameters:
  • value
  • state – An instance of RenderState contain
Returns:

The value the attribute points at.

If the value is not an attribute it is passed through unprocessed.

what_is_required

boaconstructor.utils.what_is_required(template)

Recover the ‘top-level’ references the given template requires.

Para template:This is a dict / templatewhich provides the items() method.

This should returning a list of (key, value) pairs.

Aliases are not resolved. This is purley the references mentioned in the dict structure.

This does not scan internal / external references the template may provides. It only goes through the dict values checking for ref-attr or allinc. If the value found is a list, it will recurse look through it too.

Returns:This is a dict whose keys are the references found.

For example:

>>> from boaconstructor import Template
>>> from boaconstructor.utils import what_is_required
>>>
>>> test2 = Template(
...     'test2',
...     dict(
...         x='derivefrom.[trucks]',
...         items=[
...             dict(abc='derivefrom.[cars]'),
...         ],
...         host='test1.*',
...         stuff=[
...             'com.$.keep',
...             ["frank.*",]
...         ]
...     ),
... )
>>>
>>> result = what_is_required(test2)
>>>
>>> print result
{'test1': 1, 'cars': 1, 'com': 1, 'trucks': 1, 'frank': 1}

has

boaconstructor.utils.has(reference, attribute)

Check if the dict, instance or Template instance has a given attribute.

Parameters:
  • reference – A dict, Template instance or a object instance.
  • attribute – A string representing the key/member variable to recover.
Returns:

True yes, False no.

get

boaconstructor.utils.get(reference, attribute)

Get the attribute value from the given dict, object instance or Template instance.

Parameters:
  • reference – A dict, Template instance or a object instance.
  • attribute – A string representing the key/member variable to recover.
Returns:

The value found. If nothing could be recovered then AttributeError will be raised.

License

Copyright 2011 Oisin Mulvihill

Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.