Skip to content

Commit

Permalink
Bug 784841 - Part 1: Create generic container classes; r=jhammel
Browse files Browse the repository at this point in the history
We create some specialized dicts that will be used in later patches.
  • Loading branch information
indygreg committed Jan 16, 2013
1 parent 2f75285 commit 2a04826
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 0 deletions.
104 changes: 104 additions & 0 deletions python/mozbuild/mozbuild/test/test_containers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.

import unittest

from mozunit import main

from mozbuild.util import (
DefaultOnReadDict,
ReadOnlyDefaultDict,
ReadOnlyDict,
)

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)

with self.assertRaises(KeyError):
value = test['missing']

with self.assertRaises(Exception):
test['baz'] = True

class TestDefaultOnReadDict(unittest.TestCase):
def test_no_defaults(self):
original = {'foo': 1, 'bar': 2}

test = DefaultOnReadDict(original)
self.assertEqual(original, test)

with self.assertRaises(KeyError):
value = test['missing']

test['foo'] = 5
self.assertEqual(test['foo'], 5)

def test_dict_defaults(self):
original = {'foo': 1, 'bar': 2}

test = DefaultOnReadDict(original, defaults={'baz': 3})

self.assertEqual(original, test)
self.assertEqual(test['baz'], 3)

with self.assertRaises(KeyError):
value = test['missing']

test['baz'] = 4
self.assertEqual(test['baz'], 4)

def test_global_default(self):
original = {'foo': 1}

test = DefaultOnReadDict(original, defaults={'bar': 2},
global_default=10)

self.assertEqual(original, test)
self.assertEqual(test['foo'], 1)

self.assertEqual(test['bar'], 2)
self.assertEqual(test['baz'], 10)

test['bar'] = 3
test['baz'] = 12
test['other'] = 11

self.assertEqual(test['bar'], 3)
self.assertEqual(test['baz'], 12)
self.assertEqual(test['other'], 11)


class TestReadOnlyDefaultDict(unittest.TestCase):
def test_simple(self):
original = {'foo': 1, 'bar': 2}

test = ReadOnlyDefaultDict(original)

self.assertEqual(original, test)

self.assertEqual(test['foo'], 1)

with self.assertRaises(KeyError):
value = test['missing']

def test_assignment(self):
test = ReadOnlyDefaultDict({})

with self.assertRaises(Exception):
test['foo'] = True

def test_defaults(self):
test = ReadOnlyDefaultDict({}, defaults={'foo': 1})

self.assertEqual(test['foo'], 1)


if __name__ == '__main__':
main()
57 changes: 57 additions & 0 deletions python/mozbuild/mozbuild/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from __future__ import unicode_literals

import copy
import hashlib


Expand All @@ -27,3 +28,59 @@ def hash_file(path):
h.update(data)

return h.hexdigest()


class ReadOnlyDict(dict):
"""A read-only dictionary."""
def __init__(self, d):
dict.__init__(self, d)

def __setitem__(self, name, value):
raise Exception('Object does not support assignment.')


class undefined_default(object):
"""Represents an undefined argument value that isn't None."""


undefined = undefined_default()


class DefaultOnReadDict(dict):
"""A dictionary that returns default values for missing keys on read."""

def __init__(self, d, defaults=None, global_default=undefined):
"""Create an instance from an iterable with defaults.
The first argument is fed into the dict constructor.
defaults is a dict mapping keys to their default values.
global_default is the default value for *all* missing keys. If it isn't
specified, no default value for keys not in defaults will be used and
IndexError will be raised on access.
"""
dict.__init__(self, d)

self._defaults = defaults or {}
self._global_default = global_default

def __getitem__(self, k):
try:
return dict.__getitem__(self, k)
except:
pass

if k in self._defaults:
dict.__setitem__(self, k, copy.deepcopy(self._defaults[k]))
elif self._global_default != undefined:
dict.__setitem__(self, k, copy.deepcopy(self._global_default))

return dict.__getitem__(self, k)


class ReadOnlyDefaultDict(DefaultOnReadDict, ReadOnlyDict):
"""A read-only dictionary that supports default values on retrieval."""
def __init__(self, d, defaults=None, global_default=undefined):
DefaultOnReadDict.__init__(self, d, defaults, global_default)

0 comments on commit 2a04826

Please sign in to comment.