Merge pull request #25 from salt-formulas/python3-new-adrian

New Python3 branch (by Adrian Chifor)
diff --git a/README-extentions.rst b/README-extentions.rst
index 97d78af..9085b57 100644
--- a/README-extentions.rst
+++ b/README-extentions.rst
@@ -208,6 +208,55 @@
   group_errors: True
 
 
+Use references in class names
+-----------------------------
+
+Allows to use references in the class names.
+
+Assuming following setup:
+
+.. code-block:: yaml
+  #/etc/reclass/classes/first.yml
+  parameters:
+    _class:
+      env:
+        override: 'env.dev'
+    lab:
+      name: default
+
+  #/etc/reclass/classes/lab/env/dev.yml
+  parameters:
+    lab:
+      name: dev
+
+  #/etc/reclass/classes/second.yml
+  classes:
+    - first
+    - lab.${_class:env:override}
+
+
+Reclass --nodeinfo then returns:
+
+.. code-block:: yaml
+
+  ...
+  ...
+  applications: []
+  environment: base
+  exports: {}
+  classes:
+  - first
+  - lab.${_class:env:override}
+  - second
+  parameters:
+    _class:
+      env:
+        override: env.dev
+    lab:
+      name: dev
+    ...
+    ...
+
 Inventory Queries
 -----------------
 
diff --git a/reclass/core.py b/reclass/core.py
index 9a23d89..92e7c25 100644
--- a/reclass/core.py
+++ b/reclass/core.py
@@ -22,6 +22,7 @@
 from reclass.output.yaml_outputter import ExplicitDumper
 from reclass.datatypes import Entity, Classes, Parameters, Exports
 from reclass.errors import MappingFormatError, ClassNotFound, InvQueryClassNotFound, InvQueryError, InterpolationError
+from reclass.values.parser import Parser
 
 try:
     basestring
@@ -97,6 +98,10 @@
         return Entity(self._settings, parameters=p, name='input data')
 
     def _recurse_entity(self, entity, merge_base=None, seen=None, nodename=None, environment=None):
+
+        # values/parser in order to interpolate references in classes
+        _parser = Parser()
+
         if seen is None:
             seen = {}
 
@@ -107,6 +112,8 @@
             merge_base = Entity(self._settings, name='empty (@{0})'.format(nodename))
 
         for klass in entity.classes.as_list():
+            if merge_base is not None:
+               klass=str(_parser.parse(klass, self._settings).render(merge_base.parameters.as_dict(), {}))
             if klass not in seen:
                 try:
                     class_entity = self._storage.get_class(klass, environment, self._settings)
diff --git a/reclass/datatypes/tests/test_parameters.py b/reclass/datatypes/tests/test_parameters.py
index a00f2c6..b5dc243 100644
--- a/reclass/datatypes/tests/test_parameters.py
+++ b/reclass/datatypes/tests/test_parameters.py
@@ -204,6 +204,15 @@
         p1.initialise_interpolation()
         self.assertEqual(p1.as_dict()['key'], None)
 
+    def test_merge_none_over_list_negative(self):
+        l = ['foo', 1, 2]
+        settings = Settings({'allow_none_override': False})
+        p1 = Parameters(dict(key=l[:2]), settings, '')
+        p2 = Parameters(dict(key=None), settings, '')
+        with self.assertRaises(TypeError):
+            p1.merge(p2)
+            p1.initialise_interpolation()
+
     def test_merge_none_over_dict(self):
         settings = Settings({'allow_none_override': True})
         p1 = Parameters(dict(key=SIMPLE), settings, '')
@@ -212,6 +221,14 @@
         p1.initialise_interpolation()
         self.assertEqual(p1.as_dict()['key'], None)
 
+    def test_merge_none_over_dict_negative(self):
+        settings = Settings({'allow_none_override': False})
+        p1 = Parameters(dict(key=SIMPLE), settings, '')
+        p2 = Parameters(dict(key=None), settings, '')
+        with self.assertRaises(TypeError):
+            p1.merge(p2)
+            p1.initialise_interpolation()
+
     # def test_merge_bare_dict_over_dict(self):
         # settings = Settings({'allow_bare_override': True})
         # p1 = Parameters(dict(key=SIMPLE), settings, '')
diff --git a/reclass/defaults.py b/reclass/defaults.py
index b245aa8..408307d 100644
--- a/reclass/defaults.py
+++ b/reclass/defaults.py
@@ -29,7 +29,7 @@
 OPT_ALLOW_SCALAR_OVER_LIST = False
 OPT_ALLOW_LIST_OVER_SCALAR = False
 OPT_ALLOW_DICT_OVER_SCALAR = False
-OPT_ALLOW_NONE_OVERRIDE = True
+OPT_ALLOW_NONE_OVERRIDE = False
 
 OPT_INVENTORY_IGNORE_FAILED_NODE = False
 OPT_INVENTORY_IGNORE_FAILED_RENDER = False
diff --git a/reclass/values/scaitem.py b/reclass/values/scaitem.py
index d446b3c..9de5681 100644
--- a/reclass/values/scaitem.py
+++ b/reclass/values/scaitem.py
@@ -21,12 +21,12 @@
         if item.type == Item.SCALAR:
             return self
         elif item.type == Item.LIST:
-            if self._settings.allow_scalar_over_list or (self._settings.allow_none_override and self._value in [None, 'none', 'None']):
+            if self._settings.allow_scalar_over_list or (self._settings.allow_none_override and self._value is None):
                 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 self._settings.allow_scalar_over_dict or (self._settings.allow_none_override and self._value in [None, 'none', 'None']):
+            if self._settings.allow_scalar_over_dict or (self._settings.allow_none_override and self._value is None):
                 return self
             else:
                 raise TypeError('allow scalar over dict = False: cannot merge %s over %s' % (repr(self), repr(item)))