Bug 1247836 - Add a ReadOnlyNamespace class to mozbuild.util. r=nalexander,r=chmanchester
authorMike Hommey <mh+mozilla@glandium.org>
Thu, 03 Mar 2016 15:40:33 +0900
changeset 287128 246ed92c974a2c39e83054f6b5f42b96e0cdf266
parent 287127 f92ac71c912210210c31e452b19a56aae7eff6d4
child 287129 04b85307486049190b87746c7c7a0c5ab629a77a
push id73070
push usermh@glandium.org
push dateTue, 08 Mar 2016 06:42:45 +0000
treeherdermozilla-inbound@e654e71b61b5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnalexander, chmanchester
bugs1247836
milestone47.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1247836 - Add a ReadOnlyNamespace class to mozbuild.util. r=nalexander,r=chmanchester
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.')