Bug 1247836 - Add a ReadOnlyNamespace class to mozbuild.util draft
authorMike Hommey <mh+mozilla@glandium.org>
Thu, 03 Mar 2016 15:40:33 +0900
changeset 336453 58afbcb11fd4d498cc9d5000d5b6300f5ec888d8
parent 336452 634b20820b511c66b317bd8302f3aef86b92f0a6
child 336454 80d142fc4710661033c905301528bd810b00029d
push id12065
push userbmo:mh+mozilla@glandium.org
push dateThu, 03 Mar 2016 06:46:31 +0000
bugs1247836
milestone47.0a1
Bug 1247836 - Add a ReadOnlyNamespace class to mozbuild.util
python/mozbuild/mozbuild/test/test_containers.py
python/mozbuild/mozbuild/util.py
--- a/python/mozbuild/mozbuild/test/test_containers.py
+++ b/python/mozbuild/mozbuild/test/test_containers.py
@@ -5,24 +5,45 @@
 import unittest
 
 from mozunit import main
 
 from mozbuild.util import (
     KeyedDefaultDict,
     List,
     OrderedDefaultDict,
+    ReadOnlyNamespace,
     ReadOnlyDefaultDict,
     ReadOnlyDict,
     ReadOnlyKeyedDefaultDict,
 )
 
 from collections import OrderedDict
 
 
+class TestReadOnlyNamespace(unittest.TestCase):
+    def test_basic(self):
+        test = ReadOnlyNamespace(foo=1, bar=2)
+
+        self.assertEqual(test.foo, 1)
+        self.assertEqual(test.bar, 2)
+        self.assertEqual(
+            sorted(i for i in dir(test) if not i.startswith('__')),
+            ['bar', 'foo'])
+
+        with self.assertRaises(AttributeError):
+            value = test.missing
+
+        with self.assertRaises(Exception):
+            test.foo = 2
+
+        with self.assertRaises(Exception):
+            del test.foo
+
+
 class TestReadOnlyDict(unittest.TestCase):
     def test_basic(self):
         original = {'foo': 1, 'bar': 2}
 
         test = ReadOnlyDict(original)
 
         self.assertEqual(original, test)
         self.assertEqual(test['foo'], 1)
--- a/python/mozbuild/mozbuild/util.py
+++ b/python/mozbuild/mozbuild/util.py
@@ -67,16 +67,29 @@ class EmptyValue(unicode):
     This type exists in order to support
     :py:class:`mozbuild.frontend.reader.EmptyConfig`. It should likely not be
     used elsewhere.
     """
     def __init__(self):
         super(EmptyValue, self).__init__()
 
 
+class ReadOnlyNamespace(object):
+    """A class for objects with immutable attributes set at initialization."""
+    def __init__(self, **kwargs):
+        for k, v in kwargs.iteritems():
+            super(ReadOnlyNamespace, self).__setattr__(k, v)
+
+    def __delattr__(self, key):
+        raise Exception('Object does not support deletion.')
+
+    def __setattr__(self, key, value):
+        raise Exception('Object does not support assignment.')
+
+
 class ReadOnlyDict(dict):
     """A read-only dictionary."""
     def __init__(self, *args, **kwargs):
         dict.__init__(self, *args, **kwargs)
 
     def __delitem__(self, key):
         raise Exception('Object does not support deletion.')