зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1132771 - Implement strongly typed named tuples; r=glandium
An upcoming patch introduces a use case for a strongly typed named tuple. So, we introduce a generic factory function that can produce these types. --HG-- extra : rebase_source : 7f4d17ff28925fbe8d850c036605aa03a38f0ef2 extra : source : acdd5491f10ecf8ea4e1a14150f9a2e282e2cf5d
This commit is contained in:
Родитель
0950ccbf67
Коммит
d4f1a46062
|
@ -32,6 +32,7 @@ from mozbuild.util import (
|
|||
StrictOrderingOnAppendList,
|
||||
StrictOrderingOnAppendListWithFlagsFactory,
|
||||
TypedList,
|
||||
TypedNamedTuple,
|
||||
UnsortedError,
|
||||
)
|
||||
|
||||
|
@ -663,6 +664,33 @@ class TypedTestStrictOrderingOnAppendList(unittest.TestCase):
|
|||
|
||||
self.assertEqual(len(l), 3)
|
||||
|
||||
|
||||
class TestTypedNamedTuple(unittest.TestCase):
|
||||
def test_simple(self):
|
||||
FooBar = TypedNamedTuple('FooBar', [('foo', unicode), ('bar', int)])
|
||||
|
||||
t = FooBar(foo='foo', bar=2)
|
||||
self.assertEquals(type(t), FooBar)
|
||||
self.assertEquals(t.foo, 'foo')
|
||||
self.assertEquals(t.bar, 2)
|
||||
self.assertEquals(t[0], 'foo')
|
||||
self.assertEquals(t[1], 2)
|
||||
|
||||
FooBar('foo', 2)
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
FooBar('foo', 'not integer')
|
||||
with self.assertRaises(TypeError):
|
||||
FooBar(2, 4)
|
||||
|
||||
# Passing a tuple as the first argument is the same as passing multiple
|
||||
# arguments.
|
||||
t1 = ('foo', 3)
|
||||
t2 = FooBar(t1)
|
||||
self.assertEquals(type(t2), FooBar)
|
||||
self.assertEqual(FooBar(t1), FooBar('foo', 3))
|
||||
|
||||
|
||||
class TestGroupUnifiedFiles(unittest.TestCase):
|
||||
FILES = ['%s.cpp' % letter for letter in string.ascii_lowercase]
|
||||
|
||||
|
|
|
@ -817,6 +817,56 @@ class memoized_property(object):
|
|||
return getattr(instance, name)
|
||||
|
||||
|
||||
def TypedNamedTuple(name, fields):
|
||||
"""Factory for named tuple types with strong typing.
|
||||
|
||||
Arguments are an iterable of 2-tuples. The first member is the
|
||||
the field name. The second member is a type the field will be validated
|
||||
to be.
|
||||
|
||||
Construction of instances varies from ``collections.namedtuple``.
|
||||
|
||||
First, if a single tuple argument is given to the constructor, this is
|
||||
treated as the equivalent of passing each tuple value as a separate
|
||||
argument into __init__. e.g.::
|
||||
|
||||
t = (1, 2)
|
||||
TypedTuple(t) == TypedTuple(1, 2)
|
||||
|
||||
This behavior is meant for moz.build files, so vanilla tuples are
|
||||
automatically cast to typed tuple instances.
|
||||
|
||||
Second, fields in the tuple are validated to be instances of the specified
|
||||
type. This is done via an ``isinstance()`` check. To allow multiple types,
|
||||
pass a tuple as the allowed types field.
|
||||
"""
|
||||
cls = collections.namedtuple(name, (name for name, typ in fields))
|
||||
|
||||
class TypedTuple(cls):
|
||||
__slots__ = ()
|
||||
|
||||
def __new__(klass, *args, **kwargs):
|
||||
if len(args) == 1 and not kwargs and isinstance(args[0], tuple):
|
||||
args = args[0]
|
||||
|
||||
return super(TypedTuple, klass).__new__(klass, *args, **kwargs)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
for i, (fname, ftype) in enumerate(self._fields):
|
||||
value = self[i]
|
||||
|
||||
if not isinstance(value, ftype):
|
||||
raise TypeError('field in tuple not of proper type: %s; '
|
||||
'got %s, expected %s' % (fname,
|
||||
type(value), ftype))
|
||||
|
||||
super(TypedTuple, self).__init__(*args, **kwargs)
|
||||
|
||||
TypedTuple._fields = fields
|
||||
|
||||
return TypedTuple
|
||||
|
||||
|
||||
class TypedListMixin(object):
|
||||
'''Mixin for a list with type coercion. See TypedList.'''
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче