consolidate settings into an object that can be passed around
diff --git a/reclass/adapters/ansible.py b/reclass/adapters/ansible.py
index 6794a0d..1bf509d 100755
--- a/reclass/adapters/ansible.py
+++ b/reclass/adapters/ansible.py
@@ -10,6 +10,9 @@
# Copyright © 2007–14 martin f. krafft <madduck@madduck.net>
# Released under the terms of the Artistic Licence 2.0
#
+# 2017.08.08 Andew Pickford <anpickford@googlemail.com>
+# The ansible adapter has received little testing and may not work at all now.
+
import os, sys, posix, optparse
@@ -19,6 +22,7 @@
from reclass.config import find_and_read_configfile, get_options
from reclass.version import *
from reclass.constants import MODE_NODEINFO
+from reclass.settings import Settings
def cli():
try:
@@ -57,7 +61,8 @@
storage = get_storage(options.storage_type, options.nodes_uri, options.classes_uri)
class_mappings = defaults.get('class_mappings')
- reclass = Core(storage, class_mappings, default_environment=None)
+ settings = Settings(defaults)
+ reclass = Core(storage, class_mappings, settings)
if options.mode == MODE_NODEINFO:
data = reclass.nodeinfo(options.hostname)
diff --git a/reclass/adapters/salt.py b/reclass/adapters/salt.py
index beca9e5..4d6cccc 100755
--- a/reclass/adapters/salt.py
+++ b/reclass/adapters/salt.py
@@ -15,6 +15,7 @@
from reclass.config import find_and_read_configfile, get_options
from reclass.constants import MODE_NODEINFO
from reclass.defaults import *
+from reclass.settings import Settings
from reclass.version import *
def ext_pillar(minion_id, pillar,
@@ -23,7 +24,8 @@
nodes_uri=OPT_NODES_URI,
classes_uri=OPT_CLASSES_URI,
class_mappings=None,
- propagate_pillar_data_to_reclass=False):
+ propagate_pillar_data_to_reclass=False,
+ **kwargs):
path_mangler = get_path_mangler(storage_type)
nodes_uri, classes_uri = path_mangler(inventory_base_uri, nodes_uri, classes_uri)
@@ -31,7 +33,8 @@
input_data = None
if propagate_pillar_data_to_reclass:
input_data = pillar
- reclass = Core(storage, class_mappings, input_data=input_data, default_environment='base')
+ settings = Settings(kwargs)
+ reclass = Core(storage, class_mappings, settings, input_data=input_data)
data = reclass.nodeinfo(minion_id)
params = data.get('parameters', {})
diff --git a/reclass/cli.py b/reclass/cli.py
index 15df811..6023bfa 100644
--- a/reclass/cli.py
+++ b/reclass/cli.py
@@ -11,9 +11,10 @@
from reclass import get_storage, output
from reclass.core import Core
+from reclass.settings import Settings
from reclass.config import find_and_read_configfile, get_options
-from reclass.errors import ReclassException
from reclass.defaults import *
+from reclass.errors import ReclassException
from reclass.constants import MODE_NODEINFO
from reclass.version import *
@@ -28,7 +29,8 @@
options = get_options(RECLASS_NAME, VERSION, DESCRIPTION, defaults=defaults)
storage = get_storage(options.storage_type, options.nodes_uri, options.classes_uri)
class_mappings = defaults.get('class_mappings')
- reclass = Core(storage, class_mappings, default_environment='base')
+ settings = Settings(defaults)
+ reclass = Core(storage, class_mappings, settings)
if options.mode == MODE_NODEINFO:
data = reclass.nodeinfo(options.nodename)
diff --git a/reclass/config.py b/reclass/config.py
index 2653586..2bfaf02 100644
--- a/reclass/config.py
+++ b/reclass/config.py
@@ -11,7 +11,7 @@
import errors
from defaults import *
-from constants import MODE_NODEINFO, MODE_INVENTORY
+from constants import MODE_NODEINFO, MODE_INVENTORY
from reclass import get_path_mangler
def make_db_options_group(parser, defaults={}):
diff --git a/reclass/core.py b/reclass/core.py
index f1de527..8082d80 100644
--- a/reclass/core.py
+++ b/reclass/core.py
@@ -14,18 +14,18 @@
import shlex
import string
import yaml
+from reclass.settings import Settings
from reclass.output.yaml_outputter import ExplicitDumper
from reclass.datatypes import Entity, Classes, Parameters, Exports
from reclass.errors import MappingFormatError, ClassNotFound, ResolveError
-from reclass.defaults import AUTOMATIC_RECLASS_PARAMETERS
class Core(object):
- def __init__(self, storage, class_mappings, input_data=None, default_environment=None):
+ def __init__(self, storage, class_mappings, settings, input_data=None):
self._storage = storage
self._class_mappings = class_mappings
+ self._settings = settings
self._input_data = input_data
- self._default_environment = default_environment
@staticmethod
def _get_timestamp():
@@ -60,7 +60,7 @@
def _get_class_mappings_entity(self, nodename):
if not self._class_mappings:
- return Entity(name='empty (class mappings)')
+ return Entity(self._settings, name='empty (class mappings)')
c = Classes()
for mapping in self._class_mappings:
matched = False
@@ -76,29 +76,29 @@
for klass in klasses:
c.append_if_new(klass)
- return Entity(classes=c,
+ return Entity(self._settings, classes=c,
name='class mappings for node {0}'.format(nodename))
def _get_input_data_entity(self):
if not self._input_data:
- return Entity(name='empty (input data)')
- p = Parameters(self._input_data)
- return Entity(parameters=p, name='input data')
+ return Entity(self._settings, name='empty (input data)')
+ p = Parameters(self._input_data, self._settings)
+ return Entity(self._settings, parameters=p, name='input data')
def _recurse_entity(self, entity, merge_base=None, seen=None, nodename=None, environment=None):
if seen is None:
seen = {}
if environment is None:
- environment = self._default_environment
+ environment = self._settings.default_environment
if merge_base is None:
- merge_base = Entity(name='empty (@{0})'.format(nodename))
+ merge_base = Entity(self._settings, name='empty (@{0})'.format(nodename))
for klass in entity.classes.as_list():
if klass not in seen:
try:
- class_entity = self._storage.get_class(klass, environment)
+ class_entity = self._storage.get_class(klass, environment, self._settings)
except ClassNotFound, e:
e.set_nodename(nodename)
raise e
@@ -117,11 +117,11 @@
return merge_base
def _get_automatic_parameters(self, nodename, environment):
- if AUTOMATIC_RECLASS_PARAMETERS:
+ if self._settings.automatic_parameters:
return Parameters({ '_reclass_': { 'name': { 'full': nodename, 'short': string.split(nodename, '.')[0] },
- 'environment': environment } })
+ 'environment': environment } }, self._settings, '__auto__')
else:
- return Parameters()
+ return Parameters({}, self._settings, '')
def _get_inventory(self):
inventory = {}
@@ -136,10 +136,10 @@
return inventory
def _node_entity(self, nodename):
- node_entity = self._storage.get_node(nodename)
+ node_entity = self._storage.get_node(nodename, self._settings)
if node_entity.environment == None:
- node_entity.environment = self._default_environment
- base_entity = Entity(name='base')
+ node_entity.environment = self._settings.default_environment
+ base_entity = Entity(self._settings, name='base')
base_entity.merge(self._get_class_mappings_entity(node_entity.name))
base_entity.merge(self._get_input_data_entity())
base_entity.merge_parameters(self._get_automatic_parameters(nodename, node_entity.environment))
diff --git a/reclass/datatypes/entity.py b/reclass/datatypes/entity.py
index 8ad66c0..5b54cf7 100644
--- a/reclass/datatypes/entity.py
+++ b/reclass/datatypes/entity.py
@@ -17,18 +17,18 @@
for merging. The name and uri of an Entity will be updated to the name and
uri of the Entity that is being merged.
'''
- def __init__(self, classes=None, applications=None, parameters=None,
+ def __init__(self, settings, classes=None, applications=None, parameters=None,
exports=None, uri=None, name=None, environment=None):
+ self._uri = uri or ''
+ self._name = name or ''
if classes is None: classes = Classes()
self._set_classes(classes)
if applications is None: applications = Applications()
self._set_applications(applications)
- if parameters is None: parameters = Parameters()
- if exports is None: exports = Exports()
+ if parameters is None: parameters = Parameters(None, settings, uri)
+ if exports is None: exports = Exports(None, settings, uri)
self._set_parameters(parameters)
self._set_exports(exports)
- self._uri = uri or ''
- self._name = name or ''
self._environment = environment
name = property(lambda s: s._name)
diff --git a/reclass/datatypes/exports.py b/reclass/datatypes/exports.py
index 8fbf880..f8f38a1 100644
--- a/reclass/datatypes/exports.py
+++ b/reclass/datatypes/exports.py
@@ -10,12 +10,11 @@
class Exports(Parameters):
- def __init__(self, mapping=None, uri=None, delimiter=None, options=None):
- super(Exports, self).__init__(mapping, uri, delimiter, options)
+ def __init__(self, mapping, settings, uri):
+ super(Exports, self).__init__(mapping, settings, uri)
def __repr__(self):
- return '%s(%r, %r)' % (self.__class__.__name__, self._base,
- self.delimiter)
+ return '%s(%r)' % (self.__class__.__name__, self._base)
def delete_key(self, key):
self._base.pop(key, None)
@@ -44,7 +43,7 @@
def _interpolate_render_from_external(self, context, path, value):
try:
- new = value.render(context, None, self._options)
+ new = value.render(context, None)
except ResolveError as e:
e.context = path
raise e
diff --git a/reclass/datatypes/parameters.py b/reclass/datatypes/parameters.py
index 411f631..b55f01a 100644
--- a/reclass/datatypes/parameters.py
+++ b/reclass/datatypes/parameters.py
@@ -11,9 +11,7 @@
import sys
import types
from collections import namedtuple
-from reclass.defaults import *
from reclass.utils.dictpath import DictPath
-from reclass.values.mergeoptions import MergeOptions
from reclass.values.value import Value
from reclass.values.valuelist import ValueList
from reclass.errors import InfiniteRecursionError, ResolveError, InterpolationError
@@ -41,21 +39,14 @@
To support these specialities, this class only exposes very limited
functionality and does not try to be a really mapping object.
'''
- DEFAULT_PATH_DELIMITER = PARAMETER_INTERPOLATION_DELIMITER
- DICT_KEY_OVERRIDE_PREFIX = PARAMETER_DICT_KEY_OVERRIDE_PREFIX
- def __init__(self, mapping=None, uri=None, delimiter=None, options=None):
- if delimiter is None:
- delimiter = Parameters.DEFAULT_PATH_DELIMITER
- if options is None:
- options = MergeOptions()
- self._delimiter = delimiter
+ def __init__(self, mapping, settings, uri):
+ self._settings = settings
self._base = {}
self._uri = uri
self._unrendered = None
self._escapes_handled = {}
self._has_inv_query = False
- self._options = options
self._keep_overrides = False
if mapping is not None:
# we initialise by merging
@@ -63,19 +54,18 @@
self.merge(mapping)
self._keep_overrides = False
- delimiter = property(lambda self: self._delimiter)
+ #delimiter = property(lambda self: self._delimiter)
def __len__(self):
return len(self._base)
def __repr__(self):
- return '%s(%r, %r)' % (self.__class__.__name__, self._base,
- self._delimiter)
+ return '%s(%r)' % (self.__class__.__name__, self._base)
def __eq__(self, other):
return isinstance(other, type(self)) \
and self._base == other._base \
- and self._delimiter == other._delimiter
+ and self._settings == other._settings
def __ne__(self, other):
return not self.__eq__(other)
@@ -94,7 +84,7 @@
elif isinstance(value, (Value, ValueList)):
return value
else:
- return Value(value, uri=self._uri, delimiter=self._delimiter)
+ return Value(value, self._settings, self._uri)
def _wrap_list(self, source):
return [ self._wrap_value(v) for v in source ]
@@ -104,18 +94,18 @@
def _update_value(self, cur, new, path):
if isinstance(cur, Value):
- values = ValueList(cur)
+ values = ValueList(cur, self._settings)
elif isinstance(cur, ValueList):
values = cur
else:
- values = ValueList(Value(cur, uri=self._uri, delimiter=self._delimiter))
+ values = ValueList(Value(cur, self._settings, self._uri), self._settings)
if isinstance(new, Value):
values.append(new)
elif isinstance(new, ValueList):
values.extend(new)
else:
- values.append(Value(new, uri=self._uri, delimiter=self._delimiter))
+ values.append(Value(new, self._settings, self._uri))
return values
@@ -139,10 +129,9 @@
"""
ret = cur
- ovrprfx = Parameters.DICT_KEY_OVERRIDE_PREFIX
for key, newvalue in new.iteritems():
- if key.startswith(ovrprfx) and not self._keep_overrides:
- ret[key.lstrip(ovrprfx)] = newvalue
+ if key.startswith(self._settings.dict_key_override_prefix) and not self._keep_overrides:
+ ret[key.lstrip(self._settings.dict_key_override_prefix)] = newvalue
else:
ret[key] = self._merge_recurse(ret.get(key), newvalue, path.new_subpath(key))
return ret
@@ -196,7 +185,7 @@
else:
raise TypeError('Cannot merge %s objects into %s' % (type(other),
self.__class__.__name__))
- self._base = self._merge_recurse(self._base, wrapped, DictPath(self._delimiter))
+ self._base = self._merge_recurse(self._base, wrapped, DictPath(self._settings.delimiter))
def _render_simple_container(self, container, key, value, path):
if isinstance(value, ValueList):
@@ -206,7 +195,7 @@
self._has_inv_query = True
return
else:
- value = value.merge(self._options)
+ value = value.merge()
if isinstance(value, Value) and value.is_container():
value = value.contents()
if isinstance(value, dict):
@@ -221,7 +210,7 @@
if value.has_inv_query():
self._has_inv_query = True
else:
- container[key] = value.render(None, None, self._options)
+ container[key] = value.render(None, None)
def _render_simple_dict(self, dictionary, path):
for key, value in dictionary.iteritems():
@@ -248,7 +237,7 @@
if self._unrendered is None:
self._unrendered = {}
self._has_inv_query = False
- self._render_simple_dict(self._base, DictPath(self._delimiter))
+ self._render_simple_dict(self._base, DictPath(self._settings.delimiter))
def _interpolate_inner(self, path, inventory):
value = path.get_value(self._base)
@@ -266,7 +255,7 @@
def _interpolate_render_value(self, path, value, inventory):
try:
- new = value.render(self._base, inventory, self._options)
+ new = value.render(self._base, inventory)
except ResolveError as e:
e.context = path
raise e
@@ -281,7 +270,7 @@
all_refs = False
while not all_refs:
for ref in value.get_references():
- path_from_ref = DictPath(self._delimiter, ref)
+ path_from_ref = DictPath(self._settings.delimiter, ref)
if path_from_ref in self._unrendered:
if self._unrendered[path_from_ref] is False:
@@ -295,7 +284,7 @@
self._interpolate_inner(path_from_ref, inventory)
else:
# ensure ancestor keys are already dereferenced
- ancestor = DictPath(self._delimiter)
+ ancestor = DictPath(self._settings.delimiter)
for k in path_from_ref.key_parts():
ancestor = ancestor.new_subpath(k)
if ancestor in self._unrendered:
diff --git a/reclass/datatypes/tests/test_entity.py b/reclass/datatypes/tests/test_entity.py
index 8f693f1..743cf0a 100644
--- a/reclass/datatypes/tests/test_entity.py
+++ b/reclass/datatypes/tests/test_entity.py
@@ -6,6 +6,8 @@
# Copyright © 2007–14 martin f. krafft <madduck@madduck.net>
# Released under the terms of the Artistic Licence 2.0
#
+
+from reclass.settings import Settings
from reclass.datatypes import Entity, Classes, Parameters, Applications, Exports
import unittest
try:
@@ -13,18 +15,20 @@
except ImportError:
import mock
+SETTINGS = Settings()
+
@mock.patch.multiple('reclass.datatypes', autospec=True, Classes=mock.DEFAULT,
Applications=mock.DEFAULT, Parameters=mock.DEFAULT,
Exports=mock.DEFAULT)
class TestEntity(unittest.TestCase):
def _make_instances(self, Classes, Applications, Parameters, Exports):
- return Classes(), Applications(), Parameters(), Exports()
+ return Classes(), Applications(), Parameters({}, SETTINGS, ""), Exports({}, SETTINGS, "")
def test_constructor_default(self, **mocks):
# Actually test the real objects by calling the default constructor,
# all other tests shall pass instances to the constructor
- e = Entity()
+ e = Entity(SETTINGS)
self.assertEqual(e.name, '')
self.assertEqual(e.uri, '')
self.assertIsInstance(e.classes, Classes)
@@ -34,7 +38,7 @@
def test_constructor_empty(self, **types):
instances = self._make_instances(**types)
- e = Entity(*instances)
+ e = Entity(SETTINGS, *instances)
self.assertEqual(e.name, '')
self.assertEqual(e.uri, '')
cl, al, pl, ex = [getattr(i, '__len__') for i in instances]
@@ -49,51 +53,51 @@
def test_constructor_empty_named(self, **types):
name = 'empty'
- e = Entity(*self._make_instances(**types), name=name)
+ e = Entity(SETTINGS, *self._make_instances(**types), name=name)
self.assertEqual(e.name, name)
def test_constructor_empty_uri(self, **types):
uri = 'test://uri'
- e = Entity(*self._make_instances(**types), uri=uri)
+ e = Entity(SETTINGS, *self._make_instances(**types), uri=uri)
self.assertEqual(e.uri, uri)
def test_constructor_empty_env(self, **types):
env = 'not base'
- e = Entity(*self._make_instances(**types), environment=env)
+ e = Entity(SETTINGS, *self._make_instances(**types), environment=env)
self.assertEqual(e.environment, env)
def test_equal_empty(self, **types):
instances = self._make_instances(**types)
- self.assertEqual(Entity(*instances), Entity(*instances))
+ self.assertEqual(Entity(SETTINGS, *instances), Entity(SETTINGS, *instances))
for i in instances:
i.__eq__.assert_called_once_with(i)
def test_equal_empty_named(self, **types):
instances = self._make_instances(**types)
- self.assertEqual(Entity(*instances), Entity(*instances))
+ self.assertEqual(Entity(SETTINGS, *instances), Entity(SETTINGS, *instances))
name = 'empty'
- self.assertEqual(Entity(*instances, name=name),
- Entity(*instances, name=name))
+ self.assertEqual(Entity(SETTINGS, *instances, name=name),
+ Entity(SETTINGS, *instances, name=name))
def test_unequal_empty_uri(self, **types):
instances = self._make_instances(**types)
uri = 'test://uri'
- self.assertNotEqual(Entity(*instances, uri=uri),
- Entity(*instances, uri=uri[::-1]))
+ self.assertNotEqual(Entity(SETTINGS, *instances, uri=uri),
+ Entity(SETTINGS, *instances, uri=uri[::-1]))
for i in instances:
i.__eq__.assert_called_once_with(i)
def test_unequal_empty_named(self, **types):
instances = self._make_instances(**types)
name = 'empty'
- self.assertNotEqual(Entity(*instances, name=name),
- Entity(*instances, name=name[::-1]))
+ self.assertNotEqual(Entity(SETTINGS, *instances, name=name),
+ Entity(SETTINGS, *instances, name=name[::-1]))
for i in instances:
i.__eq__.assert_called_once_with(i)
def test_unequal_types(self, **types):
instances = self._make_instances(**types)
- self.assertNotEqual(Entity(*instances, name='empty'),
+ self.assertNotEqual(Entity(SETTINGS, *instances, name='empty'),
None)
for i in instances:
self.assertEqual(i.__eq__.call_count, 0)
@@ -101,7 +105,7 @@
def _test_constructor_wrong_types(self, which_replace, **types):
instances = self._make_instances(**types)
instances[which_replace] = 'Invalid type'
- e = Entity(*instances)
+ e = Entity(SETTINGS, *instances)
def test_constructor_wrong_type_classes(self, **types):
self.assertRaises(TypeError, self._test_constructor_wrong_types, 0)
@@ -114,7 +118,7 @@
def test_merge(self, **types):
instances = self._make_instances(**types)
- e = Entity(*instances)
+ e = Entity(SETTINGS, *instances)
e.merge(e)
for i, fn in zip(instances, ('merge_unique', 'merge_unique', 'merge')):
getattr(i, fn).assert_called_once_with(i)
@@ -122,30 +126,30 @@
def test_merge_newname(self, **types):
instances = self._make_instances(**types)
newname = 'newname'
- e1 = Entity(*instances, name='oldname')
- e2 = Entity(*instances, name=newname)
+ e1 = Entity(SETTINGS, *instances, name='oldname')
+ e2 = Entity(SETTINGS, *instances, name=newname)
e1.merge(e2)
self.assertEqual(e1.name, newname)
def test_merge_newuri(self, **types):
instances = self._make_instances(**types)
newuri = 'test://uri2'
- e1 = Entity(*instances, uri='test://uri1')
- e2 = Entity(*instances, uri=newuri)
+ e1 = Entity(SETTINGS, *instances, uri='test://uri1')
+ e2 = Entity(SETTINGS, *instances, uri=newuri)
e1.merge(e2)
self.assertEqual(e1.uri, newuri)
def test_merge_newenv(self, **types):
instances = self._make_instances(**types)
newenv = 'new env'
- e1 = Entity(*instances, environment='env')
- e2 = Entity(*instances, environment=newenv)
+ e1 = Entity(SETTINGS, *instances, environment='env')
+ e2 = Entity(SETTINGS, *instances, environment=newenv)
e1.merge(e2)
self.assertEqual(e1.environment, newenv)
def test_as_dict(self, **types):
instances = self._make_instances(**types)
- entity = Entity(*instances, name='test', environment='test')
+ entity = Entity(SETTINGS, *instances, name='test', environment='test')
comp = {}
comp['classes'] = instances[0].as_list()
comp['applications'] = instances[1].as_list()
@@ -159,10 +163,10 @@
def test_exports_with_refs(self):
inventory = {'node1': {'a': 1, 'b': 2}, 'node2': {'a': 3, 'b': 4}}
- node3_exports = Exports({'a': '${a}', 'b': '${b}'})
- node3_parameters = Parameters({'name': 'node3', 'a': '${c}', 'b': 5})
+ node3_exports = Exports({'a': '${a}', 'b': '${b}'}, SETTINGS, '')
+ node3_parameters = Parameters({'name': 'node3', 'a': '${c}', 'b': 5}, SETTINGS, '')
node3_parameters.merge({'c': 3})
- node3_entity = Entity(None, None, node3_parameters, node3_exports)
+ node3_entity = Entity(SETTINGS, classes=None, applications=None, parameters=node3_parameters, exports=node3_exports)
node3_entity.interpolate_exports()
inventory['node3'] = node3_entity.exports.as_dict()
r = {'node1': {'a': 1, 'b': 2}, 'node2': {'a': 3, 'b': 4}, 'node3': {'a': 3, 'b': 5}}
@@ -170,10 +174,10 @@
def test_reference_to_an_export(self):
inventory = {'node1': {'a': 1, 'b': 2}, 'node2': {'a': 3, 'b': 4}}
- node3_exports = Exports({'a': '${a}', 'b': '${b}'})
- node3_parameters = Parameters({'name': 'node3', 'ref': '${exp}', 'a': '${c}', 'b': 5})
+ node3_exports = Exports({'a': '${a}', 'b': '${b}'}, SETTINGS, '')
+ node3_parameters = Parameters({'name': 'node3', 'ref': '${exp}', 'a': '${c}', 'b': 5}, SETTINGS, '')
node3_parameters.merge({'c': 3, 'exp': '$[ exports:a ]'})
- node3_entity = Entity(None, None, node3_parameters, node3_exports)
+ node3_entity = Entity(SETTINGS, classes=None, applications=None, parameters=node3_parameters, exports=node3_exports)
node3_entity.interpolate_exports()
inventory['node3'] = node3_entity.exports.as_dict()
node3_entity.interpolate('node3', inventory)
@@ -184,9 +188,9 @@
def test_exports_with_nested_references(self):
inventory = {'node1': {'alpha': {'a': 1, 'b': 2}}, 'node2': {'alpha': {'a': 3, 'b': 4}}}
- node3_exports = Exports({'alpha': '${alpha}'})
- node3_parameters = Parameters({'name': 'node3', 'alpha': {'a': '${one}', 'b': '${two}'}, 'beta': '$[ exports:alpha ]', 'one': '111', 'two': '${three}', 'three': '123'})
- node3_entity = Entity(None, None, node3_parameters, node3_exports)
+ node3_exports = Exports({'alpha': '${alpha}'}, SETTINGS, '')
+ node3_parameters = Parameters({'name': 'node3', 'alpha': {'a': '${one}', 'b': '${two}'}, 'beta': '$[ exports:alpha ]', 'one': '111', 'two': '${three}', 'three': '123'}, SETTINGS, '')
+ node3_entity = Entity(SETTINGS, classes=None, applications=None, parameters=node3_parameters, exports=node3_exports)
res_params = {'beta': {'node1': {'a': 1, 'b': 2}, 'node3': {'a': '111', 'b': '123'}, 'node2': {'a': 3, 'b': 4}}, 'name': 'node3', 'alpha': {'a': '111', 'b': '123'}, 'three': '123', 'two': '123', 'one': '111'}
res_inv = {'node1': {'alpha': {'a': 1, 'b': 2}}, 'node2': {'alpha': {'a': 3, 'b': 4}}, 'node3': {'alpha': {'a': '111', 'b': '123'}}}
node3_entity.interpolate_exports()
diff --git a/reclass/datatypes/tests/test_exports.py b/reclass/datatypes/tests/test_exports.py
index 68fba6c..33eccbe 100644
--- a/reclass/datatypes/tests/test_exports.py
+++ b/reclass/datatypes/tests/test_exports.py
@@ -4,14 +4,17 @@
# This file is part of reclass (http://github.com/madduck/reclass)
#
+from reclass.settings import Settings
from reclass.datatypes import Exports, Parameters
from reclass.errors import ParseError
import unittest
+SETTINGS = Settings()
+
class TestInvQuery(unittest.TestCase):
def test_overwrite_method(self):
- e = Exports({'alpha': { 'one': 1, 'two': 2}})
+ e = Exports({'alpha': { 'one': 1, 'two': 2}}, SETTINGS, '')
d = {'alpha': { 'three': 3, 'four': 4}}
e.overwrite(d)
e.initialise_interpolation()
@@ -19,77 +22,77 @@
def test_malformed_invquery(self):
with self.assertRaises(ParseError):
- p = Parameters({'exp': '$[ exports:a exports:b == self:test_value ]'})
+ p = Parameters({'exp': '$[ exports:a exports:b == self:test_value ]'}, SETTINGS, '')
with self.assertRaises(ParseError):
- p = Parameters({'exp': '$[ exports:a if exports:b self:test_value ]'})
+ p = Parameters({'exp': '$[ exports:a if exports:b self:test_value ]'}, SETTINGS, '')
with self.assertRaises(ParseError):
- p = Parameters({'exp': '$[ exports:a if exports:b == ]'})
+ p = Parameters({'exp': '$[ exports:a if exports:b == ]'}, SETTINGS, '')
with self.assertRaises(ParseError):
- p = Parameters({'exp': '$[ exports:a if exports:b == self:test_value and exports:c = self:test_value2 ]'})
+ p = Parameters({'exp': '$[ exports:a if exports:b == self:test_value and exports:c = self:test_value2 ]'}, SETTINGS, '')
with self.assertRaises(ParseError):
- p = Parameters({'exp': '$[ exports:a if exports:b == self:test_value or exports:c == ]'})
+ p = Parameters({'exp': '$[ exports:a if exports:b == self:test_value or exports:c == ]'}, SETTINGS, '')
with self.assertRaises(ParseError):
- p = Parameters({'exp': '$[ exports:a if exports:b == self:test_value anddd exports:c == self:test_value2 ]'})
+ p = Parameters({'exp': '$[ exports:a if exports:b == self:test_value anddd exports:c == self:test_value2 ]'}, SETTINGS, '')
def test_value_expr_invquery(self):
e = {'node1': {'a': 1, 'b': 2}, 'node2': {'a': 3, 'b': 4}}
- p = Parameters({'exp': '$[ exports:a ]'})
+ p = Parameters({'exp': '$[ exports:a ]'}, SETTINGS, '')
r = {'exp': {'node1': 1, 'node2': 3}}
p.interpolate(e)
self.assertEqual(p.as_dict(), r)
def test_if_expr_invquery(self):
e = {'node1': {'a': 1, 'b': 2}, 'node2': {'a': 3, 'b': 4}}
- p = Parameters({'exp': '$[ exports:a if exports:b == 4 ]'})
+ p = Parameters({'exp': '$[ exports:a if exports:b == 4 ]'}, SETTINGS, '')
r = {'exp': {'node2': 3}}
p.interpolate(e)
self.assertEqual(p.as_dict(), r)
def test_if_expr_invquery_with_refs(self):
e = {'node1': {'a': 1, 'b': 2}, 'node2': {'a': 3, 'b': 4}}
- p = Parameters({'exp': '$[ exports:a if exports:b == self:test_value ]', 'test_value': 2})
+ p = Parameters({'exp': '$[ exports:a if exports:b == self:test_value ]', 'test_value': 2}, SETTINGS, '')
r = {'exp': {'node1': 1}, 'test_value': 2}
p.interpolate(e)
self.assertEqual(p.as_dict(), r)
def test_list_if_expr_invquery(self):
e = {'node1': {'a': 1, 'b': 2}, 'node2': {'a': 3, 'b': 3}, 'node3': {'a': 3, 'b': 2}}
- p = Parameters({'exp': '$[ if exports:b == 2 ]'})
+ p = Parameters({'exp': '$[ if exports:b == 2 ]'}, SETTINGS, '')
r = {'exp': ['node1', 'node3']}
p.interpolate(e)
self.assertEqual(p.as_dict(), r)
def test_if_expr_invquery_wth_and(self):
e = {'node1': {'a': 1, 'b': 4, 'c': False}, 'node2': {'a': 3, 'b': 4, 'c': True}}
- p = Parameters({'exp': '$[ exports:a if exports:b == 4 and exports:c == True ]'})
+ p = Parameters({'exp': '$[ exports:a if exports:b == 4 and exports:c == True ]'}, SETTINGS, '')
r = {'exp': {'node2': 3}}
p.interpolate(e)
self.assertEqual(p.as_dict(), r)
def test_if_expr_invquery_wth_or(self):
e = {'node1': {'a': 1, 'b': 4}, 'node2': {'a': 3, 'b': 3}}
- p = Parameters({'exp': '$[ exports:a if exports:b == 4 or exports:b == 3 ]'})
+ p = Parameters({'exp': '$[ exports:a if exports:b == 4 or exports:b == 3 ]'}, SETTINGS, '')
r = {'exp': {'node1': 1, 'node2': 3}}
p.interpolate(e)
self.assertEqual(p.as_dict(), r)
def test_list_if_expr_invquery_with_and(self):
e = {'node1': {'a': 1, 'b': 2, 'c': 'green'}, 'node2': {'a': 3, 'b': 3}, 'node3': {'a': 3, 'b': 2, 'c': 'red'}}
- p = Parameters({'exp': '$[ if exports:b == 2 and exports:c == green ]'})
+ p = Parameters({'exp': '$[ if exports:b == 2 and exports:c == green ]'}, SETTINGS, '')
r = {'exp': ['node1']}
p.interpolate(e)
self.assertEqual(p.as_dict(), r)
def test_list_if_expr_invquery_with_and_missing(self):
e = {'node1': {'a': 1, 'b': 2, 'c': 'green'}, 'node2': {'a': 3, 'b': 3}, 'node3': {'a': 3, 'b': 2}}
- p = Parameters({'exp': '$[ if exports:b == 2 and exports:c == green ]'})
+ p = Parameters({'exp': '$[ if exports:b == 2 and exports:c == green ]'}, SETTINGS, '')
r = {'exp': ['node1']}
p.interpolate(e)
self.assertEqual(p.as_dict(), r)
def test_list_if_expr_invquery_with_and(self):
e = {'node1': {'a': 1, 'b': 2}, 'node2': {'a': 3, 'b': 3}, 'node3': {'a': 3, 'b': 4}}
- p = Parameters({'exp': '$[ if exports:b == 2 or exports:b == 4 ]'})
+ p = Parameters({'exp': '$[ if exports:b == 2 or exports:b == 4 ]'}, SETTINGS, '')
r = {'exp': ['node1', 'node3']}
p.interpolate(e)
self.assertEqual(p.as_dict(), r)
diff --git a/reclass/datatypes/tests/test_parameters.py b/reclass/datatypes/tests/test_parameters.py
index 2543ba9..a56ba65 100644
--- a/reclass/datatypes/tests/test_parameters.py
+++ b/reclass/datatypes/tests/test_parameters.py
@@ -6,10 +6,10 @@
# Copyright © 2007–14 martin f. krafft <madduck@madduck.net>
# Released under the terms of the Artistic Licence 2.0
#
+
+from reclass.settings import Settings
from reclass.datatypes import Parameters
-from reclass.defaults import REFERENCE_SENTINELS, ESCAPE_CHARACTER
from reclass.errors import InfiniteRecursionError, InterpolationError
-from reclass.values.mergeoptions import MergeOptions
import unittest
try:
import unittest.mock as mock
@@ -17,11 +17,12 @@
import mock
SIMPLE = {'one': 1, 'two': 2, 'three': 3}
+SETTINGS = Settings()
class TestParameters(unittest.TestCase):
- def _construct_mocked_params(self, iterable=None, delimiter=None):
- p = Parameters(iterable, delimiter=delimiter)
+ def _construct_mocked_params(self, iterable=None, settings=SETTINGS):
+ p = Parameters(iterable, settings, '')
self._base = base = p._base
p._base = mock.MagicMock(spec_set=dict, wraps=base)
p._base.__repr__ = mock.MagicMock(autospec=dict.__repr__,
@@ -47,22 +48,13 @@
def test_repr_empty(self):
p, b = self._construct_mocked_params()
b.__repr__.return_value = repr({})
- self.assertEqual('%r' % p, '%s(%r, %r)' % (p.__class__.__name__, {},
- Parameters.DEFAULT_PATH_DELIMITER))
+ self.assertEqual('%r' % p, '%s(%r)' % (p.__class__.__name__, {}))
b.__repr__.assert_called_once_with()
def test_repr(self):
p, b = self._construct_mocked_params(SIMPLE)
b.__repr__.return_value = repr(SIMPLE)
- self.assertEqual('%r' % p, '%s(%r, %r)' % (p.__class__.__name__, SIMPLE,
- Parameters.DEFAULT_PATH_DELIMITER))
- b.__repr__.assert_called_once_with()
-
- def test_repr_delimiter(self):
- delim = '%'
- p, b = self._construct_mocked_params(SIMPLE, delim)
- b.__repr__.return_value = repr(SIMPLE)
- self.assertEqual('%r' % p, '%s(%r, %r)' % (p.__class__.__name__, SIMPLE, delim))
+ self.assertEqual('%r' % p, '%s(%r)' % (p.__class__.__name__, SIMPLE))
b.__repr__.assert_called_once_with()
def test_equal_empty(self):
@@ -74,8 +66,7 @@
def test_equal_default_delimiter(self):
p1, b1 = self._construct_mocked_params(SIMPLE)
- p2, b2 = self._construct_mocked_params(SIMPLE,
- Parameters.DEFAULT_PATH_DELIMITER)
+ p2, b2 = self._construct_mocked_params(SIMPLE, SETTINGS)
b1.__eq__.return_value = True
self.assertEqual(p1, p2)
b1.__eq__.assert_called_once_with(b2)
@@ -95,8 +86,10 @@
b1.__eq__.assert_called_once_with(b2)
def test_unequal_delimiter(self):
- p1, b1 = self._construct_mocked_params(delimiter=':')
- p2, b2 = self._construct_mocked_params(delimiter='%')
+ settings1 = Settings({'delimiter': ':'})
+ settings2 = Settings({'delimiter': '%'})
+ p1, b1 = self._construct_mocked_params(settings=settings1)
+ p2, b2 = self._construct_mocked_params(settings=settings2)
b1.__eq__.return_value = False
self.assertNotEqual(p1, p2)
b1.__eq__.assert_called_once_with(b2)
@@ -133,8 +126,8 @@
self.assertIn(mock.call(key, value), b1.__setitem__.call_args_list)
def test_stray_occurrence_overwrites_during_interpolation(self):
- p1 = Parameters({'r' : mock.sentinel.ref, 'b': '${r}'})
- p2 = Parameters({'b' : mock.sentinel.goal})
+ p1 = Parameters({'r' : mock.sentinel.ref, 'b': '${r}'}, SETTINGS, '')
+ p2 = Parameters({'b' : mock.sentinel.goal}, SETTINGS, '')
p1.merge(p2)
p1.interpolate()
self.assertEqual(p1.as_dict()['b'], mock.sentinel.goal)
@@ -143,7 +136,7 @@
class TestParametersNoMock(unittest.TestCase):
def test_merge_scalars(self):
- p = Parameters(SIMPLE)
+ p = Parameters(SIMPLE, SETTINGS, '')
mergee = {'five':5,'four':4,'None':None,'tuple':(1,2,3)}
p.merge(mergee)
p.initialise_interpolation()
@@ -152,7 +145,7 @@
self.assertDictEqual(p.as_dict(), goal)
def test_merge_scalars_overwrite(self):
- p = Parameters(SIMPLE)
+ p = Parameters(SIMPLE, SETTINGS, '')
mergee = {'two':5,'four':4,'three':None,'one':(1,2,3)}
p.merge(mergee)
p.initialise_interpolation()
@@ -163,34 +156,35 @@
def test_merge_lists(self):
l1 = [1,2,3]
l2 = [2,3,4]
- p1 = Parameters(dict(list=l1[:]))
- p2 = Parameters(dict(list=l2))
+ p1 = Parameters(dict(list=l1[:]), SETTINGS, '')
+ p2 = Parameters(dict(list=l2), SETTINGS, '')
p1.merge(p2)
p1.initialise_interpolation()
self.assertListEqual(p1.as_dict()['list'], l1+l2)
def test_merge_list_into_scalar(self):
+ settings = Settings({'allow_list_over_scalar': True})
l = ['foo', 1, 2]
- options = MergeOptions()
- options.allow_list_over_scalar = True
- p1 = Parameters(dict(key=l[0]), options=options)
- p1.merge(Parameters(dict(key=l[1:])))
+ p1 = Parameters(dict(key=l[0]), settings, '')
+ p2 = Parameters(dict(key=l[1:]), settings, '')
+ p1.merge(p2)
p1.initialise_interpolation()
self.assertListEqual(p1.as_dict()['key'], l)
def test_merge_scalar_over_list(self):
l = ['foo', 1, 2]
- options = MergeOptions()
- options.allow_scalar_over_list = True
- p1 = Parameters(dict(key=l[:2]), options=options)
- p1.merge(Parameters(dict(key=l[2])))
+ settings = Settings({'allow_scalar_over_list': True})
+ p1 = Parameters(dict(key=l[:2]), settings, '')
+ p2 = Parameters(dict(key=l[2]), settings, '')
+ p1.merge(p2)
p1.initialise_interpolation()
self.assertEqual(p1.as_dict()['key'], l[2])
def test_merge_dicts(self):
mergee = {'five':5,'four':4,'None':None,'tuple':(1,2,3)}
- p = Parameters(dict(dict=SIMPLE))
- p.merge(Parameters(dict(dict=mergee)))
+ p = Parameters(dict(dict=SIMPLE), SETTINGS, '')
+ p2 = Parameters(dict(dict=mergee), SETTINGS, '')
+ p.merge(p2)
p.initialise_interpolation()
goal = SIMPLE.copy()
goal.update(mergee)
@@ -198,8 +192,9 @@
def test_merge_dicts_overwrite(self):
mergee = {'two':5,'four':4,'three':None,'one':(1,2,3)}
- p = Parameters(dict(dict=SIMPLE))
- p.merge(Parameters(dict(dict=mergee)))
+ p = Parameters(dict(dict=SIMPLE), SETTINGS, '')
+ p2 = Parameters(dict(dict=mergee), SETTINGS, '')
+ p.merge(p2)
p.initialise_interpolation()
goal = SIMPLE.copy()
goal.update(mergee)
@@ -213,94 +208,96 @@
'two': ['delta']}
goal = {'one': {'a': 'alpha'},
'two': ['gamma']}
- p = Parameters(dict(dict=base))
- p.merge(Parameters(dict(dict=mergee)))
+ p = Parameters(dict(dict=base), SETTINGS, '')
+ p2 = Parameters(dict(dict=mergee), SETTINGS, '')
+ p.merge(p2)
p.initialise_interpolation()
self.assertDictEqual(p.as_dict(), dict(dict=goal))
def test_merge_dict_into_scalar(self):
- p = Parameters(dict(base='foo'))
+ p = Parameters(dict(base='foo'), SETTINGS, '')
+ p2 = Parameters(dict(base=SIMPLE), SETTINGS, '')
with self.assertRaises(TypeError):
- p.merge(Parameters(dict(base=SIMPLE)))
+ p.merge(p2)
p.interpolate()
def test_merge_scalar_over_dict(self):
- options = MergeOptions()
- options.allow_scalar_over_dict = True
- p = Parameters(dict(base=SIMPLE), options=options)
+ settings = Settings({'allow_scalar_over_dict': True})
+ p = Parameters(dict(base=SIMPLE), settings, '')
mergee = {'base':'foo'}
- p.merge(Parameters(mergee))
+ p2 = Parameters(mergee, settings, '')
+ p.merge(p2)
p.initialise_interpolation()
self.assertDictEqual(p.as_dict(), mergee)
def test_interpolate_single(self):
v = 42
- d = {'foo': 'bar'.join(REFERENCE_SENTINELS),
+ d = {'foo': 'bar'.join(SETTINGS.reference_sentinels),
'bar': v}
- p = Parameters(d)
+ p = Parameters(d, SETTINGS, '')
p.interpolate()
self.assertEqual(p.as_dict()['foo'], v)
def test_interpolate_multiple(self):
v = '42'
- d = {'foo': 'bar'.join(REFERENCE_SENTINELS) + 'meep'.join(REFERENCE_SENTINELS),
+ d = {'foo': 'bar'.join(SETTINGS.reference_sentinels) + 'meep'.join(SETTINGS.reference_sentinels),
'bar': v[0],
'meep': v[1]}
- p = Parameters(d)
+ p = Parameters(d, SETTINGS, '')
p.interpolate()
self.assertEqual(p.as_dict()['foo'], v)
def test_interpolate_multilevel(self):
v = 42
- d = {'foo': 'bar'.join(REFERENCE_SENTINELS),
- 'bar': 'meep'.join(REFERENCE_SENTINELS),
+ d = {'foo': 'bar'.join(SETTINGS.reference_sentinels),
+ 'bar': 'meep'.join(SETTINGS.reference_sentinels),
'meep': v}
- p = Parameters(d)
+ p = Parameters(d, SETTINGS, '')
p.interpolate()
self.assertEqual(p.as_dict()['foo'], v)
def test_interpolate_list(self):
l = [41,42,43]
- d = {'foo': 'bar'.join(REFERENCE_SENTINELS),
+ d = {'foo': 'bar'.join(SETTINGS.reference_sentinels),
'bar': l}
- p = Parameters(d)
+ p = Parameters(d, SETTINGS, '')
p.interpolate()
self.assertEqual(p.as_dict()['foo'], l)
def test_interpolate_infrecursion(self):
v = 42
- d = {'foo': 'bar'.join(REFERENCE_SENTINELS),
- 'bar': 'foo'.join(REFERENCE_SENTINELS)}
- p = Parameters(d)
+ d = {'foo': 'bar'.join(SETTINGS.reference_sentinels),
+ 'bar': 'foo'.join(SETTINGS.reference_sentinels)}
+ p = Parameters(d, SETTINGS, '')
with self.assertRaises(InfiniteRecursionError):
p.interpolate()
def test_nested_references(self):
d = {'a': '${${z}}', 'b': 2, 'z': 'b'}
r = {'a': 2, 'b': 2, 'z': 'b'}
- p = Parameters(d)
+ p = Parameters(d, SETTINGS, '')
p.interpolate()
self.assertEqual(p.as_dict(), r)
def test_nested_deep_references(self):
d = {'one': { 'a': 1, 'b': '${one:${one:c}}', 'c': 'a' } }
r = {'one': { 'a': 1, 'b': 1, 'c': 'a'} }
- p = Parameters(d)
+ p = Parameters(d, SETTINGS, '')
p.interpolate()
self.assertEqual(p.as_dict(), r)
def test_stray_occurrence_overwrites_during_interpolation(self):
- p1 = Parameters({'r' : 1, 'b': '${r}'})
- p2 = Parameters({'b' : 2})
+ p1 = Parameters({'r' : 1, 'b': '${r}'}, SETTINGS, '')
+ p2 = Parameters({'b' : 2}, SETTINGS, '')
p1.merge(p2)
p1.interpolate()
self.assertEqual(p1.as_dict()['b'], 2)
def test_referenced_dict_deep_overwrite(self):
- p1 = Parameters({'alpha': {'one': {'a': 1, 'b': 2} } })
- p2 = Parameters({'beta': '${alpha}'})
+ p1 = Parameters({'alpha': {'one': {'a': 1, 'b': 2} } }, SETTINGS, '')
+ p2 = Parameters({'beta': '${alpha}'}, SETTINGS, '')
p3 = Parameters({'alpha': {'one': {'c': 3, 'd': 4} },
- 'beta': {'one': {'a': 99} } })
+ 'beta': {'one': {'a': 99} } }, SETTINGS, '')
r = {'alpha': {'one': {'a':1, 'b': 2, 'c': 3, 'd':4} },
'beta': {'one': {'a':99, 'b': 2, 'c': 3, 'd':4} } }
p1.merge(p2)
@@ -309,8 +306,8 @@
self.assertEqual(p1.as_dict(), r)
def test_complex_reference_overwriting(self):
- p1 = Parameters({'one': 'abc_123_${two}_${three}', 'two': 'XYZ', 'four': 4})
- p2 = Parameters({'one': 'QWERTY_${three}_${four}', 'three': '999'})
+ p1 = Parameters({'one': 'abc_123_${two}_${three}', 'two': 'XYZ', 'four': 4}, SETTINGS, '')
+ p2 = Parameters({'one': 'QWERTY_${three}_${four}', 'three': '999'}, SETTINGS, '')
r = {'one': 'QWERTY_999_4', 'two': 'XYZ', 'three': '999', 'four': 4}
p1.merge(p2)
p1.interpolate()
@@ -318,56 +315,56 @@
def test_nested_reference_with_overwriting(self):
p1 = Parameters({'one': {'a': 1, 'b': 2, 'z': 'a'},
- 'two': '${one:${one:z}}' })
- p2 = Parameters({'one': {'z': 'b'} })
+ 'two': '${one:${one:z}}' }, SETTINGS, '')
+ p2 = Parameters({'one': {'z': 'b'} }, SETTINGS, '')
r = {'one': {'a': 1, 'b':2, 'z': 'b'}, 'two': 2}
p1.merge(p2)
p1.interpolate()
self.assertEqual(p1.as_dict(), r)
def test_merge_referenced_lists(self):
- p1 = Parameters({'one': [ 1, 2, 3 ], 'two': [ 4, 5, 6 ], 'three': '${one}'})
- p2 = Parameters({'three': '${two}'})
+ p1 = Parameters({'one': [ 1, 2, 3 ], 'two': [ 4, 5, 6 ], 'three': '${one}'}, SETTINGS, '')
+ p2 = Parameters({'three': '${two}'}, SETTINGS, '')
r = {'one': [ 1, 2, 3 ], 'two': [ 4, 5, 6], 'three': [ 1, 2, 3, 4, 5, 6 ]}
p1.merge(p2)
p1.interpolate()
self.assertEqual(p1.as_dict(), r)
def test_merge_referenced_dicts(self):
- p1 = Parameters({'one': {'a': 1, 'b': 2}, 'two': {'c': 3, 'd': 4}, 'three': '${one}'})
- p2 = Parameters({'three': '${two}'})
+ p1 = Parameters({'one': {'a': 1, 'b': 2}, 'two': {'c': 3, 'd': 4}, 'three': '${one}'}, SETTINGS, '')
+ p2 = Parameters({'three': '${two}'}, SETTINGS, '')
r = {'one': {'a': 1, 'b': 2}, 'two': {'c': 3, 'd': 4}, 'three': {'a': 1, 'b': 2, 'c': 3, 'd': 4}}
p1.merge(p2)
p1.interpolate()
self.assertEqual(p1.as_dict(), r)
def test_deep_refs_in_referenced_dicts(self):
- p = Parameters({'A': '${C:a}', 'B': {'a': 1, 'b': 2}, 'C': '${B}'})
+ p = Parameters({'A': '${C:a}', 'B': {'a': 1, 'b': 2}, 'C': '${B}'}, SETTINGS, '')
r = {'A': 1, 'B': {'a': 1, 'b': 2}, 'C': {'a': 1, 'b': 2}}
p.interpolate()
self.assertEqual(p.as_dict(), r)
def test_overwrite_none(self):
- p1 = Parameters({'A': None, 'B': None, 'C': None, 'D': None, 'E': None, 'F': None})
- p2 = Parameters({'A': 'abc', 'B': [1, 2, 3], 'C': {'a': 'aaa', 'b': 'bbb'}, 'D': '${A}', 'E': '${B}', 'F': '${C}'})
+ p1 = Parameters({'A': None, 'B': None, 'C': None, 'D': None, 'E': None, 'F': None}, SETTINGS, '')
+ p2 = Parameters({'A': 'abc', 'B': [1, 2, 3], 'C': {'a': 'aaa', 'b': 'bbb'}, 'D': '${A}', 'E': '${B}', 'F': '${C}'}, SETTINGS, '')
r = {'A': 'abc', 'B': [1, 2, 3], 'C': {'a': 'aaa', 'b': 'bbb'}, 'D': 'abc', 'E': [1, 2, 3], 'F': {'a': 'aaa', 'b': 'bbb'}}
p1.merge(p2)
p1.interpolate()
self.assertEqual(p1.as_dict(), r)
def test_interpolate_escaping(self):
- v = 'bar'.join(REFERENCE_SENTINELS)
- d = {'foo': ESCAPE_CHARACTER + 'bar'.join(REFERENCE_SENTINELS),
+ v = 'bar'.join(SETTINGS.reference_sentinels)
+ d = {'foo': SETTINGS.escape_character + 'bar'.join(SETTINGS.reference_sentinels),
'bar': 'unused'}
- p = Parameters(d)
+ p = Parameters(d, SETTINGS, '')
p.initialise_interpolation()
self.assertEqual(p.as_dict()['foo'], v)
def test_interpolate_double_escaping(self):
- v = ESCAPE_CHARACTER + 'meep'
- d = {'foo': ESCAPE_CHARACTER + ESCAPE_CHARACTER + 'bar'.join(REFERENCE_SENTINELS),
+ v = SETTINGS.escape_character + 'meep'
+ d = {'foo': SETTINGS.escape_character + SETTINGS.escape_character + 'bar'.join(SETTINGS.reference_sentinels),
'bar': 'meep'}
- p = Parameters(d)
+ p = Parameters(d, SETTINGS, '')
p.interpolate()
self.assertEqual(p.as_dict()['foo'], v)
@@ -376,43 +373,43 @@
needs to be printed as-is, to ensure backwards compatibility to older versions."""
v = ' '.join([
# Escape character followed by unescapable character
- '1', ESCAPE_CHARACTER,
+ '1', SETTINGS.escape_character,
# Escape character followed by escape character
- '2', ESCAPE_CHARACTER + ESCAPE_CHARACTER,
+ '2', SETTINGS.escape_character + SETTINGS.escape_character,
# Escape character followed by interpolation end sentinel
- '3', ESCAPE_CHARACTER + REFERENCE_SENTINELS[1],
+ '3', SETTINGS.escape_character + SETTINGS.reference_sentinels[1],
# Escape character at the end of the string
- '4', ESCAPE_CHARACTER
+ '4', SETTINGS.escape_character
])
d = {'foo': v}
- p = Parameters(d)
+ p = Parameters(d, SETTINGS, '')
p.initialise_interpolation()
self.assertEqual(p.as_dict()['foo'], v)
def test_escape_close_in_ref(self):
- p1 = Parameters({'one}': 1, 'two': '${one\\}}'})
+ p1 = Parameters({'one}': 1, 'two': '${one\\}}'}, SETTINGS, '')
r = {'one}': 1, 'two': 1}
p1.interpolate()
self.assertEqual(p1.as_dict(), r)
def test_double_escape_in_ref(self):
d = {'one\\': 1, 'two': '${one\\\\}'}
- p1 = Parameters(d)
+ p1 = Parameters(d, SETTINGS, '')
r = {'one\\': 1, 'two': 1}
p1.interpolate()
self.assertEqual(p1.as_dict(), r)
def test_merging_for_multiple_nodes(self):
- p1 = Parameters({ 'alpha': { 'one': 111 }})
- p2 = Parameters({ 'beta': {'two': '${alpha:one}' }})
- p3 = Parameters({ 'beta': {'two': 222 }})
- n1 = Parameters({ 'name': 'node1'})
+ p1 = Parameters({ 'alpha': { 'one': 111 }}, SETTINGS, '')
+ p2 = Parameters({ 'beta': {'two': '${alpha:one}' }}, SETTINGS, '')
+ p3 = Parameters({ 'beta': {'two': 222 }}, SETTINGS, '')
+ n1 = Parameters({ 'name': 'node1'}, SETTINGS, '')
r1 = { 'alpha': { 'one': 111 }, 'beta': { 'two': 111 }, 'name': 'node1' }
r2 = { 'alpha': { 'one': 111 }, 'beta': { 'two': 222 }, 'name': 'node2' }
n1.merge(p1)
n1.merge(p2)
n1.interpolate()
- n2 = Parameters({'name': 'node2'})
+ n2 = Parameters({'name': 'node2'}, SETTINGS, '')
n2.merge(p1)
n2.merge(p2)
n2.merge(p3)
@@ -421,16 +418,16 @@
self.assertEqual(n2.as_dict(), r2)
def test_list_merging_for_multiple_nodes(self):
- p1 = Parameters({ 'alpha': { 'one': [1, 2] }})
- p2 = Parameters({ 'beta': {'two': '${alpha:one}' }})
- p3 = Parameters({ 'beta': {'two': [3] }})
- n1 = Parameters({ 'name': 'node1'})
+ p1 = Parameters({ 'alpha': { 'one': [1, 2] }}, SETTINGS, '')
+ p2 = Parameters({ 'beta': {'two': '${alpha:one}' }}, SETTINGS, '')
+ p3 = Parameters({ 'beta': {'two': [3] }}, SETTINGS, '')
+ n1 = Parameters({ 'name': 'node1'}, SETTINGS, '')
r1 = { 'alpha': { 'one': [1, 2] }, 'beta': { 'two': [1, 2] }, 'name': 'node1' }
r2 = { 'alpha': { 'one': [1, 2] }, 'beta': { 'two': [1, 2, 3] }, 'name': 'node2' }
n1.merge(p1)
n1.merge(p2)
n1.interpolate()
- n2 = Parameters({'name': 'node2'})
+ n2 = Parameters({'name': 'node2'}, SETTINGS, '')
n2.merge(p1)
n2.merge(p2)
n2.merge(p3)
@@ -439,16 +436,16 @@
self.assertEqual(n2.as_dict(), r2)
def test_dict_merging_for_multiple_nodes(self):
- p1 = Parameters({ 'alpha': { 'one': { 'a': 'aa', 'b': 'bb' }}})
- p2 = Parameters({ 'beta': {'two': '${alpha:one}' }})
- p3 = Parameters({ 'beta': {'two': {'c': 'cc' }}})
- n1 = Parameters({ 'name': 'node1'})
+ p1 = Parameters({ 'alpha': { 'one': { 'a': 'aa', 'b': 'bb' }}}, SETTINGS, '')
+ p2 = Parameters({ 'beta': {'two': '${alpha:one}' }}, SETTINGS, '')
+ p3 = Parameters({ 'beta': {'two': {'c': 'cc' }}}, SETTINGS, '')
+ n1 = Parameters({ 'name': 'node1'}, SETTINGS, '')
r1 = { 'alpha': { 'one': {'a': 'aa', 'b': 'bb'} }, 'beta': { 'two': {'a': 'aa', 'b': 'bb'} }, 'name': 'node1' }
r2 = { 'alpha': { 'one': {'a': 'aa', 'b': 'bb'} }, 'beta': { 'two': {'a': 'aa', 'b': 'bb', 'c': 'cc'} }, 'name': 'node2' }
n1.merge(p1)
n1.merge(p2)
n1.interpolate()
- n2 = Parameters({'name': 'node2'})
+ n2 = Parameters({'name': 'node2'}, SETTINGS, '')
n2.merge(p1)
n2.merge(p2)
n2.merge(p3)
@@ -457,14 +454,14 @@
self.assertEqual(n2.as_dict(), r2)
def test_list_merging_with_refs_for_multiple_nodes(self):
- p1 = Parameters({ 'alpha': { 'one': [1, 2], 'two': [3, 4] }})
- p2 = Parameters({ 'beta': { 'three': '${alpha:one}' }})
- p3 = Parameters({ 'beta': { 'three': '${alpha:two}' }})
- p4 = Parameters({ 'beta': { 'three': '${alpha:one}' }})
- n1 = Parameters({ 'name': 'node1' })
+ p1 = Parameters({ 'alpha': { 'one': [1, 2], 'two': [3, 4] }}, SETTINGS, '')
+ p2 = Parameters({ 'beta': { 'three': '${alpha:one}' }}, SETTINGS, '')
+ p3 = Parameters({ 'beta': { 'three': '${alpha:two}' }}, SETTINGS, '')
+ p4 = Parameters({ 'beta': { 'three': '${alpha:one}' }}, SETTINGS, '')
+ n1 = Parameters({ 'name': 'node1' }, SETTINGS, '')
r1 = {'alpha': {'one': [1, 2], 'two': [3, 4]}, 'beta': {'three': [1, 2]}, 'name': 'node1'}
r2 = {'alpha': {'one': [1, 2], 'two': [3, 4]}, 'beta': {'three': [1, 2, 3, 4, 1, 2]}, 'name': 'node2'}
- n2 = Parameters({ 'name': 'node2' })
+ n2 = Parameters({ 'name': 'node2' }, SETTINGS, '')
n2.merge(p1)
n2.merge(p2)
n2.merge(p3)
@@ -477,18 +474,18 @@
self.assertEqual(n2.as_dict(), r2)
def test_nested_refs_with_multiple_nodes(self):
- p1 = Parameters({ 'alpha': { 'one': 1, 'two': 2 } })
- p2 = Parameters({ 'beta': { 'three': 'one' } })
- p3 = Parameters({ 'beta': { 'three': 'two' } })
- p4 = Parameters({ 'beta': { 'four': '${alpha:${beta:three}}' } })
- n1 = Parameters({ 'name': 'node1' })
+ p1 = Parameters({ 'alpha': { 'one': 1, 'two': 2 } }, SETTINGS, '')
+ p2 = Parameters({ 'beta': { 'three': 'one' } }, SETTINGS, '')
+ p3 = Parameters({ 'beta': { 'three': 'two' } }, SETTINGS, '')
+ p4 = Parameters({ 'beta': { 'four': '${alpha:${beta:three}}' } }, SETTINGS, '')
+ n1 = Parameters({ 'name': 'node1' }, SETTINGS, '')
r1 = {'alpha': {'one': 1, 'two': 2}, 'beta': {'three': 'one', 'four': 1}, 'name': 'node1'}
r2 = {'alpha': {'one': 1, 'two': 2}, 'beta': {'three': 'two', 'four': 2}, 'name': 'node2'}
n1.merge(p1)
n1.merge(p4)
n1.merge(p2)
n1.interpolate()
- n2 = Parameters({ 'name': 'node2' })
+ n2 = Parameters({ 'name': 'node2' }, SETTINGS, '')
n2.merge(p1)
n2.merge(p4)
n2.merge(p3)
@@ -498,7 +495,7 @@
def test_nested_refs_error_message(self):
# beta is missing, oops
- p1 = Parameters({'alpha': {'one': 1, 'two': 2}, 'gamma': '${alpha:${beta}}'})
+ p1 = Parameters({'alpha': {'one': 1, 'two': 2}, 'gamma': '${alpha:${beta}}'}, SETTINGS, '')
with self.assertRaises(InterpolationError) as error:
p1.interpolate()
self.assertEqual(error.exception.message, "Bad references: ['beta'], for path: gamma")
diff --git a/reclass/defaults.py b/reclass/defaults.py
index 557d511..baac195 100644
--- a/reclass/defaults.py
+++ b/reclass/defaults.py
@@ -31,9 +31,12 @@
PARAMETER_DICT_KEY_OVERRIDE_PREFIX = '~'
ESCAPE_CHARACTER = '\\'
-MERGE_ALLOW_SCALAR_OVER_DICT = False
-MERGE_ALLOW_SCALAR_OVER_LIST = False
-MERGE_ALLOW_LIST_OVER_SCALAR = False
-MERGE_ALLOW_DICT_OVER_SCALAR = False
+ALLOW_SCALAR_OVER_DICT = False
+ALLOW_SCALAR_OVER_LIST = False
+ALLOW_LIST_OVER_SCALAR = False
+ALLOW_DICT_OVER_SCALAR = False
AUTOMATIC_RECLASS_PARAMETERS = True
+IGNORE_CLASS_NOT_FOUND = False
+
+DEFAULT_ENVIRONMENT = 'base'
diff --git a/reclass/settings.py b/reclass/settings.py
new file mode 100644
index 0000000..4b5928f
--- /dev/null
+++ b/reclass/settings.py
@@ -0,0 +1,34 @@
+import reclass.defaults
+import reclass.values.parser_funcs
+
+class Settings(object):
+
+ def __init__(self, options={}):
+ self.allow_scalar_over_dict = options.get('allow_scalar_over_dict', reclass.defaults.ALLOW_SCALAR_OVER_DICT)
+ self.allow_scalar_over_list = options.get('allow_scalar_over_list', reclass.defaults.ALLOW_SCALAR_OVER_LIST)
+ self.allow_list_over_scalar = options.get('allow_list_over_scalar', reclass.defaults.ALLOW_LIST_OVER_SCALAR)
+ self.allow_dict_over_scalar = options.get('allow_dict_over_scalar', reclass.defaults.ALLOW_DICT_OVER_SCALAR)
+ self.automatic_parameters = options.get('automatic_parameters', reclass.defaults.AUTOMATIC_RECLASS_PARAMETERS)
+ self.default_environment = options.get('default_environment', reclass.defaults.DEFAULT_ENVIRONMENT)
+ self.delimiter = options.get('delimiter', reclass.defaults.PARAMETER_INTERPOLATION_DELIMITER)
+ self.dict_key_override_prefix = options.get('dict_key_override_prefix', reclass.defaults.PARAMETER_DICT_KEY_OVERRIDE_PREFIX)
+ self.escape_character = options.get('escape_character', reclass.defaults.ESCAPE_CHARACTER)
+ self.export_sentinels = options.get('export_sentinels', reclass.defaults.EXPORT_SENTINELS)
+ self.reference_sentinels = options.get('reference_sentinels', reclass.defaults.REFERENCE_SENTINELS)
+
+ self.ref_parser = reclass.values.parser_funcs.get_ref_parser(self.escape_character, self.reference_sentinels, self.export_sentinels)
+ self.simple_ref_parser = reclass.values.parser_funcs.get_simple_ref_parser(self.escape_character, self.reference_sentinels, self.export_sentinels)
+
+ def __eq__(self, other):
+ return isinstance(other, type(self)) \
+ and self.allow_scalar_over_dict == other.allow_scalar_over_dict \
+ and self.allow_scalar_over_list == other.allow_scalar_over_list \
+ and self.allow_list_over_scalar == other.allow_list_over_scalar \
+ and self.allow_dict_over_scalar == other.allow_dict_over_scalar \
+ and self.automatic_parameters == other.automatic_parameters \
+ and self.default_environment == other.default_environment \
+ and self.delimiter == other.delimiter \
+ and self.dict_key_override_prefix == other.dict_key_override_prefix \
+ and self.escape_character == other.escape_character \
+ and self.export_sentinels == other.export_sentinels \
+ and self.reference_sentinels == other.reference_sentinels
diff --git a/reclass/storage/__init__.py b/reclass/storage/__init__.py
index 3990b91..f49ac16 100644
--- a/reclass/storage/__init__.py
+++ b/reclass/storage/__init__.py
@@ -14,11 +14,11 @@
name = property(lambda self: self._name)
- def get_node(self, name, merge_base=None):
+ def get_node(self, name, settings):
msg = "Storage class '{0}' does not implement node entity retrieval."
raise NotImplementedError(msg.format(self.name))
- def get_class(self, name):
+ def get_class(self, name, environment, settings):
msg = "Storage class '{0}' does not implement class entity retrieval."
raise NotImplementedError(msg.format(self.name))
diff --git a/reclass/storage/memcache_proxy.py b/reclass/storage/memcache_proxy.py
index 6c898a2..405ea8e 100644
--- a/reclass/storage/memcache_proxy.py
+++ b/reclass/storage/memcache_proxy.py
@@ -30,25 +30,25 @@
name = property(lambda self: self._real_storage.name)
- def get_node(self, name):
+ def get_node(self, name, settings):
if not self._cache_nodes:
- return self._real_storage.get_node(name)
+ return self._real_storage.get_node(name, settings)
try:
return self._nodes_cache[name]
except KeyError, e:
- ret = self._real_storage.get_node(name)
+ ret = self._real_storage.get_node(name, settings)
self._nodes_cache[name] = ret
return ret
- def get_class(self, name, environment):
+ def get_class(self, name, environment, settings):
if not self._cache_classes:
- return self._real_storage.get_class(name, environment)
+ return self._real_storage.get_class(name, environment, settings)
try:
return self._classes_cache[environment][name]
except KeyError, e:
if environment not in self._classes_cache:
self._classes_cache[environment] = dict()
- ret = self._real_storage.get_class(name, environment)
+ ret = self._real_storage.get_class(name, environment, settings)
self._classes_cache[environment][name] = ret
return ret
diff --git a/reclass/storage/mixed/__init__.py b/reclass/storage/mixed/__init__.py
index d9983fd..4651e00 100644
--- a/reclass/storage/mixed/__init__.py
+++ b/reclass/storage/mixed/__init__.py
@@ -47,12 +47,12 @@
ret = ret['uri']
return self.MixedUri(uri['storage_type'], ret)
- def get_node(self, name):
- return self._nodes_storage.get_node(name)
+ def get_node(self, name, settings):
+ return self._nodes_storage.get_node(name, settings)
- def get_class(self, name, environment):
+ def get_class(self, name, environment, settings):
storage = self._classes_storage.get(environment, self._classes_default_storage)
- return storage.get_class(name, environment=environment)
+ return storage.get_class(name, environment, settings)
def enumerate_nodes(self):
return self._nodes_storage.enumerate_nodes()
diff --git a/reclass/storage/tests/test_memcache_proxy.py b/reclass/storage/tests/test_memcache_proxy.py
index 6764251..a47c29d 100644
--- a/reclass/storage/tests/test_memcache_proxy.py
+++ b/reclass/storage/tests/test_memcache_proxy.py
@@ -6,6 +6,8 @@
# Copyright © 2007–14 martin f. krafft <madduck@madduck.net>
# Released under the terms of the Artistic Licence 2.0
#
+
+from reclass.settings import Settings
from reclass.storage.memcache_proxy import MemcacheProxy
from reclass.storage import NodeStorageBase
@@ -22,48 +24,48 @@
def test_no_nodes_caching(self):
p = MemcacheProxy(self._storage, cache_nodes=False)
- NAME = 'foo'; NAME2 = 'bar'; RET = 'baz'
+ NAME = 'foo'; NAME2 = 'bar'; RET = 'baz'; SETTINGS = Settings()
self._storage.get_node.return_value = RET
- self.assertEqual(p.get_node(NAME), RET)
- self.assertEqual(p.get_node(NAME), RET)
- self.assertEqual(p.get_node(NAME2), RET)
- self.assertEqual(p.get_node(NAME2), RET)
- expected = [mock.call(NAME), mock.call(NAME),
- mock.call(NAME2), mock.call(NAME2)]
+ self.assertEqual(p.get_node(NAME, SETTINGS), RET)
+ self.assertEqual(p.get_node(NAME, SETTINGS), RET)
+ self.assertEqual(p.get_node(NAME2, SETTINGS), RET)
+ self.assertEqual(p.get_node(NAME2, SETTINGS), RET)
+ expected = [mock.call(NAME, SETTINGS), mock.call(NAME, SETTINGS),
+ mock.call(NAME2, SETTINGS), mock.call(NAME2, SETTINGS)]
self.assertListEqual(self._storage.get_node.call_args_list, expected)
def test_nodes_caching(self):
p = MemcacheProxy(self._storage, cache_nodes=True)
- NAME = 'foo'; NAME2 = 'bar'; RET = 'baz'
+ NAME = 'foo'; NAME2 = 'bar'; RET = 'baz'; SETTINGS = Settings()
self._storage.get_node.return_value = RET
- self.assertEqual(p.get_node(NAME), RET)
- self.assertEqual(p.get_node(NAME), RET)
- self.assertEqual(p.get_node(NAME2), RET)
- self.assertEqual(p.get_node(NAME2), RET)
- expected = [mock.call(NAME), mock.call(NAME2)] # called once each
+ self.assertEqual(p.get_node(NAME, SETTINGS), RET)
+ self.assertEqual(p.get_node(NAME, SETTINGS), RET)
+ self.assertEqual(p.get_node(NAME2, SETTINGS), RET)
+ self.assertEqual(p.get_node(NAME2, SETTINGS), RET)
+ expected = [mock.call(NAME, SETTINGS), mock.call(NAME2, SETTINGS)] # called once each
self.assertListEqual(self._storage.get_node.call_args_list, expected)
def test_no_classes_caching(self):
p = MemcacheProxy(self._storage, cache_classes=False)
- NAME = 'foo'; NAME2 = 'bar'; RET = 'baz'
+ NAME = 'foo'; NAME2 = 'bar'; RET = 'baz'; SETTINGS = Settings()
self._storage.get_class.return_value = RET
- self.assertEqual(p.get_class(NAME, None), RET)
- self.assertEqual(p.get_class(NAME, None), RET)
- self.assertEqual(p.get_class(NAME2, None), RET)
- self.assertEqual(p.get_class(NAME2, None), RET)
- expected = [mock.call(NAME, None), mock.call(NAME, None),
- mock.call(NAME2, None), mock.call(NAME2, None)]
+ self.assertEqual(p.get_class(NAME, None, SETTINGS), RET)
+ self.assertEqual(p.get_class(NAME, None, SETTINGS), RET)
+ self.assertEqual(p.get_class(NAME2, None, SETTINGS), RET)
+ self.assertEqual(p.get_class(NAME2, None, SETTINGS), RET)
+ expected = [mock.call(NAME, None, SETTINGS), mock.call(NAME, None, SETTINGS),
+ mock.call(NAME2, None, SETTINGS), mock.call(NAME2, None, SETTINGS)]
self.assertListEqual(self._storage.get_class.call_args_list, expected)
def test_classes_caching(self):
p = MemcacheProxy(self._storage, cache_classes=True)
- NAME = 'foo'; NAME2 = 'bar'; RET = 'baz'
+ NAME = 'foo'; NAME2 = 'bar'; RET = 'baz'; SETTINGS = Settings()
self._storage.get_class.return_value = RET
- self.assertEqual(p.get_class(NAME, None), RET)
- self.assertEqual(p.get_class(NAME, None), RET)
- self.assertEqual(p.get_class(NAME2, None), RET)
- self.assertEqual(p.get_class(NAME2, None), RET)
- expected = [mock.call(NAME, None), mock.call(NAME2, None)] # called once each
+ self.assertEqual(p.get_class(NAME, None, SETTINGS), RET)
+ self.assertEqual(p.get_class(NAME, None, SETTINGS), RET)
+ self.assertEqual(p.get_class(NAME2, None, SETTINGS), RET)
+ self.assertEqual(p.get_class(NAME2, None, SETTINGS), RET)
+ expected = [mock.call(NAME, None, SETTINGS), mock.call(NAME2, None, SETTINGS)] # called once each
self.assertListEqual(self._storage.get_class.call_args_list, expected)
def test_nodelist_no_caching(self):
diff --git a/reclass/storage/yaml_fs/__init__.py b/reclass/storage/yaml_fs/__init__.py
index 35e4f05..20388ac 100644
--- a/reclass/storage/yaml_fs/__init__.py
+++ b/reclass/storage/yaml_fs/__init__.py
@@ -87,7 +87,7 @@
d.walk(register_fn)
return ret
- def get_node(self, name):
+ def get_node(self, name, settings):
vvv('GET NODE {0}'.format(name))
try:
relpath = self._nodes[name]
@@ -95,16 +95,16 @@
name = os.path.splitext(relpath)[0]
except KeyError, e:
raise reclass.errors.NodeNotFound(self.name, name, self.nodes_uri)
- entity = YamlData.from_file(path).get_entity(name)
+ entity = YamlData.from_file(path).get_entity(name, settings)
return entity
- def get_class(self, name, environment):
+ def get_class(self, name, environment, setings):
vvv('GET CLASS {0}'.format(name))
try:
path = os.path.join(self.classes_uri, self._classes[name])
except KeyError, e:
raise reclass.errors.ClassNotFound(self.name, name, self.classes_uri)
- entity = YamlData.from_file(path).get_entity(name)
+ entity = YamlData.from_file(path).get_entity(name, settings)
return entity
def enumerate_nodes(self):
diff --git a/reclass/storage/yaml_git/__init__.py b/reclass/storage/yaml_git/__init__.py
index 3482423..94eca59 100644
--- a/reclass/storage/yaml_git/__init__.py
+++ b/reclass/storage/yaml_git/__init__.py
@@ -221,13 +221,13 @@
nodes_uri = property(lambda self: self._nodes_uri)
classes_uri = property(lambda self: self._classes_uri)
- def get_node(self, name):
+ def get_node(self, name, settings):
file = self._nodes[name]
blob = self._repos[self._nodes_uri.repo].get(file.id)
- entity = YamlData.from_string(blob.data, 'git_fs://{0} {1} {2}'.format(self._nodes_uri.repo, self._nodes_uri.branch, file.path)).get_entity(name)
+ entity = YamlData.from_string(blob.data, 'git_fs://{0} {1} {2}'.format(self._nodes_uri.repo, self._nodes_uri.branch, file.path)).get_entity(name, setings)
return entity
- def get_class(self, name, environment):
+ def get_class(self, name, environment, settings):
uri = self._env_to_uri(environment)
if uri.root is not None:
name = '{0}.{1}'.format(uri.root, name)
@@ -239,7 +239,7 @@
raise reclass.errors.NotFoundError("File " + name + " missing from " + uri.repo + " branch " + uri.branch)
file = self._repos[uri.repo].files[uri.branch][name]
blob = self._repos[uri.repo].get(file.id)
- entity = YamlData.from_string(blob.data, 'git_fs://{0} {1} {2}'.format(uri.repo, uri.branch, file.path)).get_entity(name)
+ entity = YamlData.from_string(blob.data, 'git_fs://{0} {1} {2}'.format(uri.repo, uri.branch, file.path)).get_entity(name, settings)
return entity
def enumerate_nodes(self):
diff --git a/reclass/storage/yamldata.py b/reclass/storage/yamldata.py
index 31cc8ff..0dda2b7 100644
--- a/reclass/storage/yamldata.py
+++ b/reclass/storage/yamldata.py
@@ -47,9 +47,9 @@
def get_data(self):
return self._data
- def get_entity(self, name=None):
- if name is None:
- name = self._uri
+ def get_entity(self, name, settings):
+ #if name is None:
+ # name = self._uri
classes = self._data.get('classes')
if classes is None:
@@ -64,17 +64,17 @@
parameters = self._data.get('parameters')
if parameters is None:
parameters = {}
- parameters = datatypes.Parameters(parameters, uri=self._uri)
+ parameters = datatypes.Parameters(parameters, settings, self._uri)
exports = self._data.get('exports')
if exports is None:
exports = {}
- exports = datatypes.Exports(exports, uri=self._uri)
+ exports = datatypes.Exports(exports, settings, self._uri)
env = self._data.get('environment', None)
- return datatypes.Entity(classes, applications, parameters, exports,
- name=name, environment=env, uri=self.uri)
+ return datatypes.Entity(settings, classes=classes, applications=applications, parameters=parameters,
+ exports=exports, name=name, environment=env, uri=self.uri)
def __str__(self):
return '<{0} {1}, {2}>'.format(self.__class__.__name__, self._uri,
diff --git a/reclass/values/compitem.py b/reclass/values/compitem.py
index ea342a5..5786934 100644
--- a/reclass/values/compitem.py
+++ b/reclass/values/compitem.py
@@ -4,13 +4,15 @@
# This file is part of reclass
#
+from reclass.settings import Settings
from item import Item
class CompItem(Item):
- def __init__(self, items):
+ def __init__(self, items, settings):
self.type = Item.COMPOSITE
self._items = items
+ self._settings = settings
self._refs = []
self._allRefs = False
self.assembleRefs()
diff --git a/reclass/values/dictitem.py b/reclass/values/dictitem.py
index bc58f67..d778fe2 100644
--- a/reclass/values/dictitem.py
+++ b/reclass/values/dictitem.py
@@ -4,13 +4,15 @@
# This file is part of reclass
#
+from reclass.settings import Settings
from item import Item
class DictItem(Item):
- def __init__(self, item):
+ def __init__(self, item, settings):
self.type = Item.DICTIONARY
self._dict = item
+ self._settings = settings
def contents(self):
return self._dict
@@ -18,9 +20,9 @@
def is_container(self):
return True
- def merge_over(self, item, options):
+ def merge_over(self, item):
if item.type == Item.SCALAR:
- if item.contents() is None or options.allow_dict_over_scalar:
+ if item.contents() is None or self._settings.allow_dict_over_scalar:
return self
else:
raise TypeError('allow dict over scalar = False: cannot merge %s onto %s' % (repr(self), repr(item)))
diff --git a/reclass/values/invitem.py b/reclass/values/invitem.py
index 50eb388..024ef99 100644
--- a/reclass/values/invitem.py
+++ b/reclass/values/invitem.py
@@ -8,6 +8,7 @@
import pyparsing as pp
from item import Item
+from reclass.settings import Settings
from reclass.utils.dictpath import DictPath
from reclass.errors import ExpressionError, ParseError, ResolveError
@@ -195,9 +196,9 @@
_parser = _get_parser()
- def __init__(self, item, delimiter):
+ def __init__(self, item, settings):
self.type = Item.INV_QUERY
- self._delimiter = delimiter
+ self._settings = settings
self._parse_expression(item.render(None, None))
def _parse_expression(self, expr):
@@ -213,16 +214,16 @@
raise ExpressionError('Failed to parse %s' % str(self._expr))
if self._expr_type == _VALUE:
- self._value_path = DictPath(self._delimiter, self._expr[0][1]).drop_first()
- self._question = Question([], self._delimiter)
+ self._value_path = DictPath(self._settings.delimiter, self._expr[0][1]).drop_first()
+ self._question = Question([], self._settings.delimiter)
self._refs = []
elif self._expr_type == _TEST:
- self._value_path = DictPath(self._delimiter, self._expr[0][1]).drop_first()
- self._question = Question(self._expr[2:], self._delimiter)
+ self._value_path = DictPath(self._settings.delimiter, self._expr[0][1]).drop_first()
+ self._question = Question(self._expr[2:], self._settings.delimiter)
self._refs = self._question.refs()
elif self._expr_type == _LIST_TEST:
self._value_path = None
- self._question = Question(self._expr[1:], self._delimiter)
+ self._question = Question(self._expr[1:], self._settings.delimiter)
self._refs = self._question.refs()
else:
raise ExpressionError('Unknown expression type: %s' % self._expr_type)
diff --git a/reclass/values/item.py b/reclass/values/item.py
index 4728142..57fd0e3 100644
--- a/reclass/values/item.py
+++ b/reclass/values/item.py
@@ -34,7 +34,7 @@
msg = "Item class {0} does not implement contents()"
raise NotImplementedError(msg.format(self.__class__.__name__))
- def merge_over(self, item, options):
+ def merge_over(self, item):
msg = "Item class {0} does not implement merge_over()"
raise NotImplementedError(msg.format(self.__class__.__name__))
diff --git a/reclass/values/listitem.py b/reclass/values/listitem.py
index ede8251..c7f29d0 100644
--- a/reclass/values/listitem.py
+++ b/reclass/values/listitem.py
@@ -5,12 +5,14 @@
#
from item import Item
+from reclass.settings import Settings
class ListItem(Item):
- def __init__(self, item):
+ def __init__(self, item, settings):
self.type = Item.LIST
self._list = item
+ self._settings = settings
def contents(self):
return self._list
@@ -21,14 +23,14 @@
def render(self, context, inventory):
return self._list
- def merge_over(self, item, options):
+ def merge_over(self, item):
if item.type == Item.LIST:
item._list.extend(self._list)
return item
elif item.type == Item.SCALAR:
if item.contents() is None:
return self
- elif options.allow_list_over_scalar:
+ elif self._settings.allow_list_over_scalar:
self._list.insert(0, item.contents())
return self
else:
diff --git a/reclass/values/mergeoptions.py b/reclass/values/mergeoptions.py
deleted file mode 100644
index c5a7e59..0000000
--- a/reclass/values/mergeoptions.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from reclass.defaults import *
-
-class MergeOptions(object):
- def __init__ (self):
- self.allow_scalar_over_dict = MERGE_ALLOW_SCALAR_OVER_DICT
- self.allow_scalar_over_list = MERGE_ALLOW_SCALAR_OVER_LIST
- self.allow_list_over_scalar = MERGE_ALLOW_LIST_OVER_SCALAR
- self.allow_dict_over_scalar = MERGE_ALLOW_DICT_OVER_SCALAR
diff --git a/reclass/values/parser.py b/reclass/values/parser.py
index c15c8d1..1b0ba4b 100644
--- a/reclass/values/parser.py
+++ b/reclass/values/parser.py
@@ -11,120 +11,31 @@
from refitem import RefItem
from scaitem import ScaItem
-from reclass.defaults import ESCAPE_CHARACTER, REFERENCE_SENTINELS, EXPORT_SENTINELS
from reclass.errors import ParseError
-
-_STR = 1
-_REF = 2
-_INV = 3
-
-_ESCAPE = ESCAPE_CHARACTER
-_DOUBLE_ESCAPE = _ESCAPE + _ESCAPE
-
-_REF_OPEN = REFERENCE_SENTINELS[0]
-_REF_CLOSE = REFERENCE_SENTINELS[1]
-_REF_CLOSE_FIRST = _REF_CLOSE[0]
-_REF_ESCAPE_OPEN = _ESCAPE + _REF_OPEN
-_REF_ESCAPE_CLOSE = _ESCAPE + _REF_CLOSE
-_REF_DOUBLE_ESCAPE_OPEN = _DOUBLE_ESCAPE + _REF_OPEN
-_REF_DOUBLE_ESCAPE_CLOSE = _DOUBLE_ESCAPE + _REF_CLOSE
-_REF_EXCLUDES = _ESCAPE + _REF_OPEN + _REF_CLOSE
-
-_INV_OPEN = EXPORT_SENTINELS[0]
-_INV_CLOSE = EXPORT_SENTINELS[1]
-_INV_CLOSE_FIRST = _INV_CLOSE[0]
-_INV_ESCAPE_OPEN = _ESCAPE + _INV_OPEN
-_INV_ESCAPE_CLOSE = _ESCAPE + _INV_CLOSE
-_INV_DOUBLE_ESCAPE_OPEN = _DOUBLE_ESCAPE + _INV_OPEN
-_INV_DOUBLE_ESCAPE_CLOSE = _DOUBLE_ESCAPE + _INV_CLOSE
-_INV_EXCLUDES = _ESCAPE + _INV_OPEN + _INV_CLOSE
-
-_EXCLUDES = _ESCAPE + _REF_OPEN + _REF_CLOSE + _INV_OPEN + _INV_CLOSE
-
-def _string(string, location, tokens):
- token = tokens[0]
- tokens[0] = (_STR, token)
-
-def _reference(string, location, tokens):
- token = list(tokens[0])
- tokens[0] = (_REF, token)
-
-def _invquery(string, location, tokens):
- token = list(tokens[0])
- tokens[0] = (_INV, token)
-
-def _get_parser():
- double_escape = pp.Combine(pp.Literal(_DOUBLE_ESCAPE) + pp.MatchFirst([pp.FollowedBy(_REF_OPEN), pp.FollowedBy(_REF_CLOSE),
- pp.FollowedBy(_INV_OPEN), pp.FollowedBy(_INV_CLOSE)])).setParseAction(pp.replaceWith(_ESCAPE))
-
- ref_open = pp.Literal(_REF_OPEN).suppress()
- ref_close = pp.Literal(_REF_CLOSE).suppress()
- ref_not_open = ~pp.Literal(_REF_OPEN) + ~pp.Literal(_REF_ESCAPE_OPEN) + ~pp.Literal(_REF_DOUBLE_ESCAPE_OPEN)
- ref_not_close = ~pp.Literal(_REF_CLOSE) + ~pp.Literal(_REF_ESCAPE_CLOSE) + ~pp.Literal(_REF_DOUBLE_ESCAPE_CLOSE)
- ref_escape_open = pp.Literal(_REF_ESCAPE_OPEN).setParseAction(pp.replaceWith(_REF_OPEN))
- ref_escape_close = pp.Literal(_REF_ESCAPE_CLOSE).setParseAction(pp.replaceWith(_REF_CLOSE))
- ref_text = pp.CharsNotIn(_REF_EXCLUDES) | pp.CharsNotIn(_REF_CLOSE_FIRST, exact=1)
- ref_content = pp.Combine(pp.OneOrMore(ref_not_open + ref_not_close + ref_text))
- ref_string = pp.MatchFirst([double_escape, ref_escape_open, ref_escape_close, ref_content]).setParseAction(_string)
- ref_item = pp.Forward()
- ref_items = pp.OneOrMore(ref_item)
- reference = (ref_open + pp.Group(ref_items) + ref_close).setParseAction(_reference)
- ref_item << (reference | ref_string)
-
- inv_open = pp.Literal(_INV_OPEN).suppress()
- inv_close = pp.Literal(_INV_CLOSE).suppress()
- inv_not_open = ~pp.Literal(_INV_OPEN) + ~pp.Literal(_INV_ESCAPE_OPEN) + ~pp.Literal(_INV_DOUBLE_ESCAPE_OPEN)
- inv_not_close = ~pp.Literal(_INV_CLOSE) + ~pp.Literal(_INV_ESCAPE_CLOSE) + ~pp.Literal(_INV_DOUBLE_ESCAPE_CLOSE)
- inv_escape_open = pp.Literal(_INV_ESCAPE_OPEN).setParseAction(pp.replaceWith(_INV_OPEN))
- inv_escape_close = pp.Literal(_INV_ESCAPE_CLOSE).setParseAction(pp.replaceWith(_INV_CLOSE))
- inv_text = pp.CharsNotIn(_INV_CLOSE_FIRST)
- inv_content = pp.Combine(pp.OneOrMore(inv_not_close + inv_text))
- inv_string = pp.MatchFirst([double_escape, inv_escape_open, inv_escape_close, inv_content]).setParseAction(_string)
- inv_items = pp.OneOrMore(inv_string)
- export = (inv_open + pp.Group(inv_items) + inv_close).setParseAction(_invquery)
-
- text = pp.CharsNotIn(_EXCLUDES) | pp.CharsNotIn('', exact=1)
- content = pp.Combine(pp.OneOrMore(ref_not_open + inv_not_open + text))
- string = pp.MatchFirst([double_escape, ref_escape_open, inv_escape_open, content]).setParseAction(_string)
-
- item = reference | export | string
- line = pp.OneOrMore(item) + pp.StringEnd()
- return line
-
-def _get_simple_ref_parser():
- string = pp.CharsNotIn(_EXCLUDES).setParseAction(_string)
- ref_open = pp.Literal(_REF_OPEN).suppress()
- ref_close = pp.Literal(_REF_CLOSE).suppress()
- reference = (ref_open + pp.Group(string) + ref_close).setParseAction(_reference)
- line = pp.StringStart() + pp.Optional(string) + reference + pp.Optional(string) + pp.StringEnd()
- return line
-
+from reclass.values.parser_funcs import STR, REF, INV
class Parser(object):
- _parser = _get_parser()
- _simple_ref_parser = _get_simple_ref_parser()
-
- def parse(self, value, delimiter):
- self._delimiter = delimiter
+ def parse(self, value, settings):
+ self._settings = settings
dollars = value.count('$')
if dollars == 0:
# speed up: only use pyparsing if there is a $ in the string
- return ScaItem(value)
+ return ScaItem(value, self._settings)
elif dollars == 1:
# speed up: try a simple reference
try:
- tokens = self._simple_ref_parser.leaveWhitespace().parseString(value).asList()
+ tokens = self._settings.simple_ref_parser.leaveWhitespace().parseString(value).asList()
except pp.ParseException as e:
# fall back on the full parser
try:
- tokens = self._parser.leaveWhitespace().parseString(value).asList()
+ tokens = self._settings.ref_parser.leaveWhitespace().parseString(value).asList()
except pp.ParseException as e:
raise ParseError(e.msg, e.line, e.col, e.lineno)
else:
# use the full parser
try:
- tokens = self._parser.leaveWhitespace().parseString(value).asList()
+ tokens = self._settings.ref_parser.leaveWhitespace().parseString(value).asList()
except pp.ParseException as e:
raise ParseError(e.msg, e.line, e.col, e.lineno)
@@ -132,22 +43,22 @@
if len(items) == 1:
return items[0]
else:
- return CompItem(items)
+ return CompItem(items, self._settings)
- _create_dict = { _STR: (lambda s, v: ScaItem(v)),
- _REF: (lambda s, v: s._create_ref(v)),
- _INV: (lambda s, v: s._create_inv(v)) }
+ _create_dict = { STR: (lambda s, v: ScaItem(v, s._settings)),
+ REF: (lambda s, v: s._create_ref(v)),
+ INV: (lambda s, v: s._create_inv(v)) }
def _create_items(self, tokens):
return [ self._create_dict[t](self, v) for t, v in tokens ]
def _create_ref(self, tokens):
items = [ self._create_dict[t](self, v) for t, v in tokens ]
- return RefItem(items, self._delimiter)
+ return RefItem(items, self._settings)
def _create_inv(self, tokens):
- items = [ ScaItem(v) for t, v in tokens ]
+ items = [ ScaItem(v, self._settings) for t, v in tokens ]
if len(items) == 1:
- return InvItem(items[0], self._delimiter)
+ return InvItem(items[0], self._settings)
else:
- return InvItem(CompItem(items), self._delimiter)
+ return InvItem(CompItem(items), self._settings)
diff --git a/reclass/values/parser_funcs.py b/reclass/values/parser_funcs.py
new file mode 100644
index 0000000..bd5a1ba
--- /dev/null
+++ b/reclass/values/parser_funcs.py
@@ -0,0 +1,99 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass
+#
+
+import pyparsing as pp
+
+STR = 1
+REF = 2
+INV = 3
+
+def _string(string, location, tokens):
+ token = tokens[0]
+ tokens[0] = (STR, token)
+
+def _reference(string, location, tokens):
+ token = list(tokens[0])
+ tokens[0] = (REF, token)
+
+def _invquery(string, location, tokens):
+ token = list(tokens[0])
+ tokens[0] = (INV, token)
+
+def get_ref_parser(escape_character, reference_sentinels, export_sentinels):
+ _ESCAPE = escape_character
+ _DOUBLE_ESCAPE = _ESCAPE + _ESCAPE
+
+ _REF_OPEN = reference_sentinels[0]
+ _REF_CLOSE = reference_sentinels[1]
+ _REF_CLOSE_FIRST = _REF_CLOSE[0]
+ _REF_ESCAPE_OPEN = _ESCAPE + _REF_OPEN
+ _REF_ESCAPE_CLOSE = _ESCAPE + _REF_CLOSE
+ _REF_DOUBLE_ESCAPE_OPEN = _DOUBLE_ESCAPE + _REF_OPEN
+ _REF_DOUBLE_ESCAPE_CLOSE = _DOUBLE_ESCAPE + _REF_CLOSE
+ _REF_EXCLUDES = _ESCAPE + _REF_OPEN + _REF_CLOSE
+
+ _INV_OPEN = export_sentinels[0]
+ _INV_CLOSE = export_sentinels[1]
+ _INV_CLOSE_FIRST = _INV_CLOSE[0]
+ _INV_ESCAPE_OPEN = _ESCAPE + _INV_OPEN
+ _INV_ESCAPE_CLOSE = _ESCAPE + _INV_CLOSE
+ _INV_DOUBLE_ESCAPE_OPEN = _DOUBLE_ESCAPE + _INV_OPEN
+ _INV_DOUBLE_ESCAPE_CLOSE = _DOUBLE_ESCAPE + _INV_CLOSE
+ _INV_EXCLUDES = _ESCAPE + _INV_OPEN + _INV_CLOSE
+
+ _EXCLUDES = _ESCAPE + _REF_OPEN + _REF_CLOSE + _INV_OPEN + _INV_CLOSE
+
+ double_escape = pp.Combine(pp.Literal(_DOUBLE_ESCAPE) + pp.MatchFirst([pp.FollowedBy(_REF_OPEN), pp.FollowedBy(_REF_CLOSE),
+ pp.FollowedBy(_INV_OPEN), pp.FollowedBy(_INV_CLOSE)])).setParseAction(pp.replaceWith(_ESCAPE))
+
+ ref_open = pp.Literal(_REF_OPEN).suppress()
+ ref_close = pp.Literal(_REF_CLOSE).suppress()
+ ref_not_open = ~pp.Literal(_REF_OPEN) + ~pp.Literal(_REF_ESCAPE_OPEN) + ~pp.Literal(_REF_DOUBLE_ESCAPE_OPEN)
+ ref_not_close = ~pp.Literal(_REF_CLOSE) + ~pp.Literal(_REF_ESCAPE_CLOSE) + ~pp.Literal(_REF_DOUBLE_ESCAPE_CLOSE)
+ ref_escape_open = pp.Literal(_REF_ESCAPE_OPEN).setParseAction(pp.replaceWith(_REF_OPEN))
+ ref_escape_close = pp.Literal(_REF_ESCAPE_CLOSE).setParseAction(pp.replaceWith(_REF_CLOSE))
+ ref_text = pp.CharsNotIn(_REF_EXCLUDES) | pp.CharsNotIn(_REF_CLOSE_FIRST, exact=1)
+ ref_content = pp.Combine(pp.OneOrMore(ref_not_open + ref_not_close + ref_text))
+ ref_string = pp.MatchFirst([double_escape, ref_escape_open, ref_escape_close, ref_content]).setParseAction(_string)
+ ref_item = pp.Forward()
+ ref_items = pp.OneOrMore(ref_item)
+ reference = (ref_open + pp.Group(ref_items) + ref_close).setParseAction(_reference)
+ ref_item << (reference | ref_string)
+
+ inv_open = pp.Literal(_INV_OPEN).suppress()
+ inv_close = pp.Literal(_INV_CLOSE).suppress()
+ inv_not_open = ~pp.Literal(_INV_OPEN) + ~pp.Literal(_INV_ESCAPE_OPEN) + ~pp.Literal(_INV_DOUBLE_ESCAPE_OPEN)
+ inv_not_close = ~pp.Literal(_INV_CLOSE) + ~pp.Literal(_INV_ESCAPE_CLOSE) + ~pp.Literal(_INV_DOUBLE_ESCAPE_CLOSE)
+ inv_escape_open = pp.Literal(_INV_ESCAPE_OPEN).setParseAction(pp.replaceWith(_INV_OPEN))
+ inv_escape_close = pp.Literal(_INV_ESCAPE_CLOSE).setParseAction(pp.replaceWith(_INV_CLOSE))
+ inv_text = pp.CharsNotIn(_INV_CLOSE_FIRST)
+ inv_content = pp.Combine(pp.OneOrMore(inv_not_close + inv_text))
+ inv_string = pp.MatchFirst([double_escape, inv_escape_open, inv_escape_close, inv_content]).setParseAction(_string)
+ inv_items = pp.OneOrMore(inv_string)
+ export = (inv_open + pp.Group(inv_items) + inv_close).setParseAction(_invquery)
+
+ text = pp.CharsNotIn(_EXCLUDES) | pp.CharsNotIn('', exact=1)
+ content = pp.Combine(pp.OneOrMore(ref_not_open + inv_not_open + text))
+ string = pp.MatchFirst([double_escape, ref_escape_open, inv_escape_open, content]).setParseAction(_string)
+
+ item = reference | export | string
+ line = pp.OneOrMore(item) + pp.StringEnd()
+ return line
+
+def get_simple_ref_parser(escape_character, reference_sentinels, export_sentinels):
+ _ESCAPE = escape_character
+ _REF_OPEN = reference_sentinels[0]
+ _REF_CLOSE = reference_sentinels[1]
+ _INV_OPEN = export_sentinels[0]
+ _INV_CLOSE = export_sentinels[1]
+ _EXCLUDES = _ESCAPE + _REF_OPEN + _REF_CLOSE + _INV_OPEN + _INV_CLOSE
+
+ string = pp.CharsNotIn(_EXCLUDES).setParseAction(_string)
+ ref_open = pp.Literal(_REF_OPEN).suppress()
+ ref_close = pp.Literal(_REF_CLOSE).suppress()
+ reference = (ref_open + pp.Group(string) + ref_close).setParseAction(_reference)
+ line = pp.StringStart() + pp.Optional(string) + reference + pp.Optional(string) + pp.StringEnd()
+ return line
diff --git a/reclass/values/refitem.py b/reclass/values/refitem.py
index a69bbf5..ebb9708 100644
--- a/reclass/values/refitem.py
+++ b/reclass/values/refitem.py
@@ -5,14 +5,15 @@
#
from item import Item
+from reclass.settings import Settings
from reclass.utils.dictpath import DictPath
from reclass.errors import ResolveError
class RefItem(Item):
- def __init__(self, items, delimiter):
+ def __init__(self, items, settings):
self.type = Item.REFERENCE
- self._delimiter = delimiter
+ self._settings = settings
self._items = items
self._refs = []
self._allRefs = False
@@ -47,7 +48,7 @@
return self._refs
def _resolve(self, ref, context):
- path = DictPath(self._delimiter, ref)
+ path = DictPath(self._settings.delimiter, ref)
try:
return path.get_value(context)
except (KeyError, TypeError) as e:
diff --git a/reclass/values/scaitem.py b/reclass/values/scaitem.py
index 151e123..df574d9 100644
--- a/reclass/values/scaitem.py
+++ b/reclass/values/scaitem.py
@@ -4,27 +4,29 @@
# This file is part of reclass
#
+from reclass.settings import Settings
from item import Item
class ScaItem(Item):
- def __init__(self, value):
+ def __init__(self, value, settings):
self.type = Item.SCALAR
self._value = value
+ self._settings = settings
def contents(self):
return self._value
- def merge_over(self, item, options):
+ def merge_over(self, item):
if item.type == Item.SCALAR:
return self
elif item.type == Item.LIST:
- if options.allow_scalar_over_list:
+ if self._settings.allow_scalar_over_list:
return self
else:
raise TypeError('allow scalar over list = False: cannot merge %s over %s' % (repr(self), repr(item)))
elif item.type == Item.DICTIONARY:
- if options.allow_scalar_over_dict:
+ if self._settings.allow_scalar_over_dict:
return self
else:
raise TypeError('allow scalar over dict = False: cannot merge %s over %s' % (repr(self), repr(item)))
diff --git a/reclass/values/tests/test_value.py b/reclass/values/tests/test_value.py
index 1b83094..51425cc 100644
--- a/reclass/values/tests/test_value.py
+++ b/reclass/values/tests/test_value.py
@@ -9,16 +9,17 @@
import pyparsing as pp
+from reclass.settings import Settings
from reclass.values.value import Value
-from reclass.defaults import REFERENCE_SENTINELS, \
- PARAMETER_INTERPOLATION_DELIMITER
from reclass.errors import ResolveError, \
IncompleteInterpolationError, ParseError
import unittest
+SETTINGS = Settings()
+
def _var(s):
- return '%s%s%s' % (REFERENCE_SENTINELS[0], s,
- REFERENCE_SENTINELS[1])
+ return '%s%s%s' % (SETTINGS.reference_sentinels[0], s,
+ SETTINGS.reference_sentinels[1])
CONTEXT = {'favcolour':'yellow',
'motd':{'greeting':'Servus!',
@@ -37,13 +38,13 @@
def test_simple_string(self):
s = 'my cat likes to hide in boxes'
- tv = Value(s)
+ tv = Value(s, SETTINGS, '')
self.assertFalse(tv.has_references())
self.assertEquals(tv.render(CONTEXT, None), s)
def _test_solo_ref(self, key):
s = _var(key)
- tv = Value(s)
+ tv = Value(s, SETTINGS, '')
res = tv.render(CONTEXT, None)
self.assertTrue(tv.has_references())
self.assertEqual(res, CONTEXT[key])
@@ -65,7 +66,7 @@
def test_single_subst_bothends(self):
s = 'I like ' + _var('favcolour') + ' and I like it'
- tv = Value(s)
+ tv = Value(s, SETTINGS, '')
self.assertTrue(tv.has_references())
self.assertEqual(tv.render(CONTEXT, None),
_poor_mans_template(s, 'favcolour',
@@ -73,7 +74,7 @@
def test_single_subst_start(self):
s = _var('favcolour') + ' is my favourite colour'
- tv = Value(s)
+ tv = Value(s, SETTINGS, '')
self.assertTrue(tv.has_references())
self.assertEqual(tv.render(CONTEXT, None),
_poor_mans_template(s, 'favcolour',
@@ -81,34 +82,34 @@
def test_single_subst_end(self):
s = 'I like ' + _var('favcolour')
- tv = Value(s)
+ tv = Value(s, SETTINGS, '')
self.assertTrue(tv.has_references())
self.assertEqual(tv.render(CONTEXT, None),
_poor_mans_template(s, 'favcolour',
CONTEXT['favcolour']))
def test_deep_subst_solo(self):
- var = PARAMETER_INTERPOLATION_DELIMITER.join(('motd', 'greeting'))
- s = _var(var)
- tv = Value(s)
+ motd = SETTINGS.delimiter.join(('motd', 'greeting'))
+ s = _var(motd)
+ tv = Value(s, SETTINGS, '')
self.assertTrue(tv.has_references())
self.assertEqual(tv.render(CONTEXT, None),
- _poor_mans_template(s, var,
+ _poor_mans_template(s, motd,
CONTEXT['motd']['greeting']))
def test_multiple_subst(self):
- greet = PARAMETER_INTERPOLATION_DELIMITER.join(('motd', 'greeting'))
+ greet = SETTINGS.delimiter.join(('motd', 'greeting'))
s = _var(greet) + ' I like ' + _var('favcolour') + '!'
- tv = Value(s)
+ tv = Value(s, SETTINGS, '')
self.assertTrue(tv.has_references())
want = _poor_mans_template(s, greet, CONTEXT['motd']['greeting'])
want = _poor_mans_template(want, 'favcolour', CONTEXT['favcolour'])
self.assertEqual(tv.render(CONTEXT, None), want)
def test_multiple_subst_flush(self):
- greet = PARAMETER_INTERPOLATION_DELIMITER.join(('motd', 'greeting'))
+ greet = SETTINGS.delimiter.join(('motd', 'greeting'))
s = _var(greet) + ' I like ' + _var('favcolour')
- tv = Value(s)
+ tv = Value(s, SETTINGS, '')
self.assertTrue(tv.has_references())
want = _poor_mans_template(s, greet, CONTEXT['motd']['greeting'])
want = _poor_mans_template(want, 'favcolour', CONTEXT['favcolour'])
@@ -116,14 +117,14 @@
def test_undefined_variable(self):
s = _var('no_such_variable')
- tv = Value(s)
+ tv = Value(s, SETTINGS, '')
with self.assertRaises(ResolveError):
tv.render(CONTEXT, None)
def test_incomplete_variable(self):
- s = REFERENCE_SENTINELS[0] + 'incomplete'
+ s = SETTINGS.reference_sentinels[0] + 'incomplete'
with self.assertRaises(ParseError):
- tv = Value(s)
+ tv = Value(s, SETTINGS, '')
if __name__ == '__main__':
unittest.main()
diff --git a/reclass/values/value.py b/reclass/values/value.py
index fcca3ba..23401fb 100644
--- a/reclass/values/value.py
+++ b/reclass/values/value.py
@@ -8,24 +8,23 @@
from dictitem import DictItem
from listitem import ListItem
from scaitem import ScaItem
-from reclass.defaults import PARAMETER_INTERPOLATION_DELIMITER
from reclass.errors import ResolveError
class Value(object):
_parser = Parser()
- def __init__(self, value, uri=None, delimiter=PARAMETER_INTERPOLATION_DELIMITER):
- self._delimiter = delimiter
+ def __init__(self, value, settings, uri):
+ self._settings = settings
self._uri = uri
if isinstance(value, str):
- self._item = self._parser.parse(value, delimiter)
+ self._item = self._parser.parse(value, self._settings)
elif isinstance(value, list):
- self._item = ListItem(value)
+ self._item = ListItem(value, self._settings)
elif isinstance(value, dict):
- self._item = DictItem(value)
+ self._item = DictItem(value, self._settings)
else:
- self._item = ScaItem(value)
+ self._item = ScaItem(value, self._settings)
def is_container(self):
return self._item.is_container()
@@ -49,7 +48,7 @@
if self._item.has_references():
self._item.assembleRefs(context)
- def render(self, context, inventory, options=None):
+ def render(self, context, inventory):
try:
return self._item.render(context, inventory)
except ResolveError as e:
@@ -59,8 +58,8 @@
def contents(self):
return self._item.contents()
- def merge_over(self, value, options):
- self._item = self._item.merge_over(value._item, options)
+ def merge_over(self, value):
+ self._item = self._item.merge_over(value._item)
return self
def __repr__(self):
diff --git a/reclass/values/valuelist.py b/reclass/values/valuelist.py
index 3dc9c62..64a7cb5 100644
--- a/reclass/values/valuelist.py
+++ b/reclass/values/valuelist.py
@@ -8,7 +8,8 @@
class ValueList(object):
- def __init__(self, value):
+ def __init__(self, value, settings):
+ self._settings = settings
self._refs = []
self._allRefs = True
self._values = [ value ]
@@ -59,16 +60,16 @@
if value.allRefs() is False:
self._allRefs = False
- def merge(self, options):
+ def merge(self):
output = None
for n, value in enumerate(self._values):
if output is None:
output = value
else:
- output = value.merge_over(output, options)
+ output = value.merge_over(output)
return output
- def render(self, context, inventory, options):
+ def render(self, context, inventory):
from reclass.datatypes.parameters import Parameters
output = None
@@ -80,8 +81,8 @@
else:
new = value.render(context, inventory)
if isinstance(output, dict) and isinstance(new, dict):
- p1 = Parameters(output, delimiter=value._delimiter)
- p2 = Parameters(new, delimiter=value._delimiter)
+ p1 = Parameters(output, self._settings, None)
+ p2 = Parameters(new, self._settings, None)
p1.merge(p2)
output = p1.as_dict()
continue