зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1606124 - mozbuild/util.py supports Python 3 r=mars
Many of the utility classes very heavily rely on multiple inheritance which is broadly unncessary and made debugging difficult, so I ripped it out wherever it was necessary. The indented_repr() function is also more or less a re-implementation of pprint, so to support Python 3 I've instead made it a small wrapper around pprint. (This results in the output of the function being slightly different than we're used to, as pprint does sorting/indentation/line breaking differently than indented_repr() currently does, but it'll be nice to not have to maintain this code that is slightly different than pprint for no real reason.) Differential Revision: https://phabricator.services.mozilla.com/D58306 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
8c6dffda6c
Коммит
fe95d91222
|
@ -27,7 +27,6 @@ from mozbuild.util import (
|
|||
ImmutableStrictOrderingOnAppendList,
|
||||
KeyedDefaultDict,
|
||||
List,
|
||||
ListWithAction,
|
||||
memoize,
|
||||
memoized_property,
|
||||
ReadOnlyKeyedDefaultDict,
|
||||
|
@ -998,22 +997,6 @@ def OrderedPathListWithAction(action):
|
|||
return _OrderedListWithAction
|
||||
|
||||
|
||||
def TypedListWithAction(typ, action):
|
||||
"""Returns a class which behaves as a TypedList with the provided type, but
|
||||
invokes the given given callable with each input and a context as it is
|
||||
read, storing a tuple including the result and the original item.
|
||||
|
||||
This used to extend moz.build reading to make more data available in
|
||||
filesystem-reading mode.
|
||||
"""
|
||||
class _TypedListWithAction(ContextDerivedValue, TypedList(typ), ListWithAction):
|
||||
def __init__(self, context, *args):
|
||||
def _action(item):
|
||||
return item, action(context, item)
|
||||
super(_TypedListWithAction, self).__init__(action=_action, *args)
|
||||
return _TypedListWithAction
|
||||
|
||||
|
||||
ManifestparserManifestList = OrderedPathListWithAction(read_manifestparser_manifest)
|
||||
ReftestManifestList = OrderedPathListWithAction(read_reftest_manifest)
|
||||
|
||||
|
|
|
@ -16,4 +16,5 @@ subsuite = mozbuild
|
|||
[test_mozinfo.py]
|
||||
[test_preprocessor.py]
|
||||
[test_pythonutil.py]
|
||||
[test_util.py]
|
||||
[test_util_fileavoidwrite.py]
|
||||
|
|
|
@ -34,4 +34,3 @@ skip-if = (os == "win")
|
|||
[frontend/test_namespaces.py]
|
||||
[frontend/test_reader.py]
|
||||
[frontend/test_sandbox.py]
|
||||
[test_util.py]
|
||||
|
|
|
@ -9,6 +9,7 @@ import itertools
|
|||
import hashlib
|
||||
import os
|
||||
import unittest
|
||||
import six
|
||||
import string
|
||||
import sys
|
||||
import textwrap
|
||||
|
@ -29,8 +30,8 @@ from mozbuild.util import (
|
|||
HierarchicalStringList,
|
||||
EnumString,
|
||||
EnumStringComparisonError,
|
||||
ListWithAction,
|
||||
StrictOrderingOnAppendList,
|
||||
StrictOrderingOnAppendListWithAction,
|
||||
StrictOrderingOnAppendListWithFlagsFactory,
|
||||
TypedList,
|
||||
TypedNamedTuple,
|
||||
|
@ -145,19 +146,19 @@ class TestHierarchicalStringList(unittest.TestCase):
|
|||
def test_exports_subdir(self):
|
||||
self.assertEqual(self.EXPORTS._children, {})
|
||||
self.EXPORTS.foo += ["foo.h"]
|
||||
self.assertItemsEqual(self.EXPORTS._children, {"foo": True})
|
||||
six.assertCountEqual(self, self.EXPORTS._children, {"foo": True})
|
||||
self.assertEqual(self.EXPORTS.foo._strings, ["foo.h"])
|
||||
self.EXPORTS.bar += ["bar.h"]
|
||||
self.assertItemsEqual(self.EXPORTS._children,
|
||||
six.assertCountEqual(self, self.EXPORTS._children,
|
||||
{"foo": True, "bar": True})
|
||||
self.assertEqual(self.EXPORTS.foo._strings, ["foo.h"])
|
||||
self.assertEqual(self.EXPORTS.bar._strings, ["bar.h"])
|
||||
|
||||
def test_exports_multiple_subdir(self):
|
||||
self.EXPORTS.foo.bar = ["foobar.h"]
|
||||
self.assertItemsEqual(self.EXPORTS._children, {"foo": True})
|
||||
self.assertItemsEqual(self.EXPORTS.foo._children, {"bar": True})
|
||||
self.assertItemsEqual(self.EXPORTS.foo.bar._children, {})
|
||||
six.assertCountEqual(self, self.EXPORTS._children, {"foo": True})
|
||||
six.assertCountEqual(self, self.EXPORTS.foo._children, {"bar": True})
|
||||
six.assertCountEqual(self, self.EXPORTS.foo.bar._children, {})
|
||||
self.assertEqual(self.EXPORTS._strings, [])
|
||||
self.assertEqual(self.EXPORTS.foo._strings, [])
|
||||
self.assertEqual(self.EXPORTS.foo.bar._strings, ["foobar.h"])
|
||||
|
@ -165,30 +166,34 @@ class TestHierarchicalStringList(unittest.TestCase):
|
|||
def test_invalid_exports_append(self):
|
||||
with self.assertRaises(ValueError) as ve:
|
||||
self.EXPORTS += "foo.h"
|
||||
self.assertEqual(str(ve.exception),
|
||||
"Expected a list of strings, not <type '%s'>" % str_type)
|
||||
six.assertRegex(
|
||||
self, str(ve.exception),
|
||||
"Expected a list of strings, not <(?:type|class) '%s'>" % str_type)
|
||||
|
||||
def test_invalid_exports_set(self):
|
||||
with self.assertRaises(ValueError) as ve:
|
||||
self.EXPORTS.foo = "foo.h"
|
||||
|
||||
self.assertEqual(str(ve.exception),
|
||||
"Expected a list of strings, not <type '%s'>" % str_type)
|
||||
six.assertRegex(
|
||||
self, str(ve.exception),
|
||||
"Expected a list of strings, not <(?:type|class) '%s'>" % str_type)
|
||||
|
||||
def test_invalid_exports_append_base(self):
|
||||
with self.assertRaises(ValueError) as ve:
|
||||
self.EXPORTS += "foo.h"
|
||||
|
||||
self.assertEqual(str(ve.exception),
|
||||
"Expected a list of strings, not <type '%s'>" % str_type)
|
||||
six.assertRegex(
|
||||
self, str(ve.exception),
|
||||
"Expected a list of strings, not <(?:type|class) '%s'>" % str_type)
|
||||
|
||||
def test_invalid_exports_bool(self):
|
||||
with self.assertRaises(ValueError) as ve:
|
||||
self.EXPORTS += [True]
|
||||
|
||||
self.assertEqual(str(ve.exception),
|
||||
six.assertRegex(
|
||||
self, str(ve.exception),
|
||||
"Expected a list of strings, not an element of "
|
||||
"<type 'bool'>")
|
||||
"<(?:type|class) 'bool'>")
|
||||
|
||||
def test_del_exports(self):
|
||||
with self.assertRaises(MozbuildDeletionError):
|
||||
|
@ -339,7 +344,7 @@ class TestStrictOrderingOnAppendList(unittest.TestCase):
|
|||
l2 += l
|
||||
|
||||
|
||||
class TestListWithAction(unittest.TestCase):
|
||||
class TestStrictOrderingOnAppendListWithAction(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.action = lambda a: (a, id(a))
|
||||
|
||||
|
@ -349,54 +354,54 @@ class TestListWithAction(unittest.TestCase):
|
|||
self.assertEqual(item, expected[idx])
|
||||
|
||||
def test_init(self):
|
||||
l = ListWithAction(action=self.action)
|
||||
l = StrictOrderingOnAppendListWithAction(action=self.action)
|
||||
self.assertEqual(len(l), 0)
|
||||
original = ['a', 'b', 'c']
|
||||
l = ListWithAction(['a', 'b', 'c'], action=self.action)
|
||||
expected = map(self.action, original)
|
||||
l = StrictOrderingOnAppendListWithAction(['a', 'b', 'c'], action=self.action)
|
||||
expected = [self.action(i) for i in original]
|
||||
self.assertSameList(expected, l)
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
ListWithAction('abc', action=self.action)
|
||||
StrictOrderingOnAppendListWithAction('abc', action=self.action)
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
ListWithAction()
|
||||
StrictOrderingOnAppendListWithAction()
|
||||
|
||||
def test_extend(self):
|
||||
l = ListWithAction(action=self.action)
|
||||
l = StrictOrderingOnAppendListWithAction(action=self.action)
|
||||
original = ['a', 'b']
|
||||
l.extend(original)
|
||||
expected = map(self.action, original)
|
||||
expected = [self.action(i) for i in original]
|
||||
self.assertSameList(expected, l)
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
l.extend('ab')
|
||||
|
||||
def test_slicing(self):
|
||||
l = ListWithAction(action=self.action)
|
||||
l = StrictOrderingOnAppendListWithAction(action=self.action)
|
||||
original = ['a', 'b']
|
||||
l[:] = original
|
||||
expected = map(self.action, original)
|
||||
expected = [self.action(i) for i in original]
|
||||
self.assertSameList(expected, l)
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
l[:] = 'ab'
|
||||
|
||||
def test_add(self):
|
||||
l = ListWithAction(action=self.action)
|
||||
l = StrictOrderingOnAppendListWithAction(action=self.action)
|
||||
original = ['a', 'b']
|
||||
l2 = l + original
|
||||
expected = map(self.action, original)
|
||||
expected = [self.action(i) for i in original]
|
||||
self.assertSameList(expected, l2)
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
l + 'abc'
|
||||
|
||||
def test_iadd(self):
|
||||
l = ListWithAction(action=self.action)
|
||||
l = StrictOrderingOnAppendListWithAction(action=self.action)
|
||||
original = ['a', 'b']
|
||||
l += original
|
||||
expected = map(self.action, original)
|
||||
expected = [self.action(i) for i in original]
|
||||
self.assertSameList(expected, l)
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
|
@ -445,7 +450,7 @@ class TestStrictOrderingOnAppendListWithFlagsFactory(unittest.TestCase):
|
|||
|
||||
def test_strict_ordering_on_append_list_with_flags_factory_extend(self):
|
||||
FooList = StrictOrderingOnAppendListWithFlagsFactory({
|
||||
'foo': bool, 'bar': unicode
|
||||
'foo': bool, 'bar': six.text_type
|
||||
})
|
||||
foo = FooList(['a', 'b', 'c'])
|
||||
foo['a'].foo = True
|
||||
|
@ -453,7 +458,7 @@ class TestStrictOrderingOnAppendListWithFlagsFactory(unittest.TestCase):
|
|||
|
||||
# Don't allow extending lists with different flag definitions.
|
||||
BarList = StrictOrderingOnAppendListWithFlagsFactory({
|
||||
'foo': unicode, 'baz': bool
|
||||
'foo': six.text_type, 'baz': bool
|
||||
})
|
||||
bar = BarList(['d', 'e', 'f'])
|
||||
bar['d'].foo = 'foo'
|
||||
|
@ -495,7 +500,7 @@ class TestStrictOrderingOnAppendListWithFlagsFactory(unittest.TestCase):
|
|||
foo += zot
|
||||
assertExtended(foo)
|
||||
|
||||
# Test __setslice__.
|
||||
# Test __setitem__.
|
||||
foo[3:] = []
|
||||
self.assertEqual(len(foo), 3)
|
||||
foo[3:] = zot
|
||||
|
@ -658,7 +663,7 @@ class TestTypedList(unittest.TestCase):
|
|||
# Adding a TypedList to a TypedList shouldn't even trigger the code
|
||||
# that does coercion at all.
|
||||
l2 = cls()
|
||||
list.__setslice__(l, 0, -1, [1, 2])
|
||||
list.__setitem__(l, slice(0, -1), [1, 2])
|
||||
l2 += l
|
||||
self.assertEqual(len(objs), 2)
|
||||
self.assertEqual(type(l2[0]), int)
|
||||
|
@ -673,11 +678,11 @@ class TestTypedList(unittest.TestCase):
|
|||
|
||||
class TypedTestStrictOrderingOnAppendList(unittest.TestCase):
|
||||
def test_init(self):
|
||||
class Unicode(unicode):
|
||||
def __init__(self, other):
|
||||
if not isinstance(other, unicode):
|
||||
class Unicode(six.text_type):
|
||||
def __new__(cls, other):
|
||||
if not isinstance(other, six.text_type):
|
||||
raise ValueError()
|
||||
super(Unicode, self).__init__(other)
|
||||
return six.text_type.__new__(cls, other)
|
||||
|
||||
cls = TypedList(Unicode, StrictOrderingOnAppendList)
|
||||
l = cls()
|
||||
|
@ -697,7 +702,7 @@ class TypedTestStrictOrderingOnAppendList(unittest.TestCase):
|
|||
|
||||
class TestTypedNamedTuple(unittest.TestCase):
|
||||
def test_simple(self):
|
||||
FooBar = TypedNamedTuple('FooBar', [('foo', unicode), ('bar', int)])
|
||||
FooBar = TypedNamedTuple('FooBar', [('foo', six.text_type), ('bar', int)])
|
||||
|
||||
t = FooBar(foo='foo', bar=2)
|
||||
self.assertEquals(type(t), FooBar)
|
||||
|
@ -813,7 +818,8 @@ class TestEnumString(unittest.TestCase):
|
|||
|
||||
|
||||
class TestIndentedRepr(unittest.TestCase):
|
||||
def test_indented_repr(self):
|
||||
@unittest.skipUnless(six.PY2, 'requires Python 2')
|
||||
def test_indented_repr_py2(self):
|
||||
data = textwrap.dedent(r'''
|
||||
{
|
||||
'a': 1,
|
||||
|
@ -840,6 +846,24 @@ class TestIndentedRepr(unittest.TestCase):
|
|||
|
||||
self.assertEqual(indented_repr(obj), data)
|
||||
|
||||
@unittest.skipUnless(six.PY3, 'requires Python 3')
|
||||
def test_indented_repr(self):
|
||||
data = textwrap.dedent(r'''
|
||||
{ b'c': 'xyz',
|
||||
'a': 1,
|
||||
'b': b'abc',
|
||||
'd': False,
|
||||
'e': {'a': 1, 'b': b'2', 'c': '3'},
|
||||
'f': [1, b'2', '3'],
|
||||
'pile_of_bytes': b'\xf0\x9f\x92\xa9',
|
||||
'pile_of_poo': '💩',
|
||||
'special_chars': '\\\'"\x08\n\t',
|
||||
'with_accents': 'éàñ'}''').lstrip()
|
||||
|
||||
obj = eval(data)
|
||||
|
||||
self.assertEqual(indented_repr(obj), data)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
@ -14,10 +14,11 @@ import difflib
|
|||
import errno
|
||||
import functools
|
||||
import hashlib
|
||||
import itertools
|
||||
import os
|
||||
import pprint
|
||||
import re
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from collections import (
|
||||
|
@ -398,7 +399,14 @@ def resolve_target_to_make(topobjdir, target):
|
|||
reldir = os.path.dirname(reldir)
|
||||
|
||||
|
||||
class ListMixin(object):
|
||||
class List(list):
|
||||
"""A list specialized for moz.build environments.
|
||||
|
||||
We overload the assignment and append operations to require that the
|
||||
appended thing is a list. This avoids bad surprises coming from appending
|
||||
a string to a list, which would just add each letter of the string.
|
||||
"""
|
||||
|
||||
def __init__(self, iterable=None, **kwargs):
|
||||
if iterable is None:
|
||||
iterable = []
|
||||
|
@ -406,19 +414,32 @@ class ListMixin(object):
|
|||
raise ValueError('List can only be created from other list instances.')
|
||||
|
||||
self._kwargs = kwargs
|
||||
return super(ListMixin, self).__init__(iterable, **kwargs)
|
||||
return super(List, self).__init__(iterable)
|
||||
|
||||
def extend(self, l):
|
||||
if not isinstance(l, list):
|
||||
raise ValueError('List can only be extended with other list instances.')
|
||||
|
||||
return super(ListMixin, self).extend(l)
|
||||
return super(List, self).extend(l)
|
||||
|
||||
def __setitem__(self, key, val):
|
||||
if isinstance(key, slice):
|
||||
if not isinstance(val, list):
|
||||
raise ValueError('List can only be sliced with other list '
|
||||
'instances.')
|
||||
if key.step:
|
||||
raise ValueError('List cannot be sliced with a nonzero step '
|
||||
'value')
|
||||
# Python 2 and Python 3 do this differently for some reason.
|
||||
if six.PY2:
|
||||
return super(List, self).__setslice__(key.start, key.stop,
|
||||
val)
|
||||
else:
|
||||
return super(List, self).__setitem__(key, val)
|
||||
return super(List, self).__setitem__(key, val)
|
||||
|
||||
def __setslice__(self, i, j, sequence):
|
||||
if not isinstance(sequence, list):
|
||||
raise ValueError('List can only be sliced with other list instances.')
|
||||
|
||||
return super(ListMixin, self).__setslice__(i, j, sequence)
|
||||
return self.__setitem__(slice(i, j), sequence)
|
||||
|
||||
def __add__(self, other):
|
||||
# Allow None and EmptyValue is a special case because it makes undefined
|
||||
|
@ -436,16 +457,7 @@ class ListMixin(object):
|
|||
if not isinstance(other, list):
|
||||
raise ValueError('Only lists can be appended to lists.')
|
||||
|
||||
return super(ListMixin, self).__iadd__(other)
|
||||
|
||||
|
||||
class List(ListMixin, list):
|
||||
"""A list specialized for moz.build environments.
|
||||
|
||||
We overload the assignment and append operations to require that the
|
||||
appended thing is a list. This avoids bad surprises coming from appending
|
||||
a string to a list, which would just add each letter of the string.
|
||||
"""
|
||||
return super(List, self).__iadd__(other)
|
||||
|
||||
|
||||
class UnsortedError(Exception):
|
||||
|
@ -474,7 +486,13 @@ class UnsortedError(Exception):
|
|||
return s.getvalue()
|
||||
|
||||
|
||||
class StrictOrderingOnAppendListMixin(object):
|
||||
class StrictOrderingOnAppendList(List):
|
||||
"""A list specialized for moz.build environments.
|
||||
|
||||
We overload the assignment and append operations to require that incoming
|
||||
elements be ordered. This enforces cleaner style in moz.build files.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def ensure_sorted(l):
|
||||
if isinstance(l, StrictOrderingOnAppendList):
|
||||
|
@ -493,39 +511,29 @@ class StrictOrderingOnAppendListMixin(object):
|
|||
if iterable is None:
|
||||
iterable = []
|
||||
|
||||
StrictOrderingOnAppendListMixin.ensure_sorted(iterable)
|
||||
StrictOrderingOnAppendList.ensure_sorted(iterable)
|
||||
|
||||
super(StrictOrderingOnAppendListMixin, self).__init__(iterable, **kwargs)
|
||||
super(StrictOrderingOnAppendList, self).__init__(iterable, **kwargs)
|
||||
|
||||
def extend(self, l):
|
||||
StrictOrderingOnAppendListMixin.ensure_sorted(l)
|
||||
StrictOrderingOnAppendList.ensure_sorted(l)
|
||||
|
||||
return super(StrictOrderingOnAppendListMixin, self).extend(l)
|
||||
return super(StrictOrderingOnAppendList, self).extend(l)
|
||||
|
||||
def __setslice__(self, i, j, sequence):
|
||||
StrictOrderingOnAppendListMixin.ensure_sorted(sequence)
|
||||
|
||||
return super(StrictOrderingOnAppendListMixin, self).__setslice__(i, j,
|
||||
sequence)
|
||||
def __setitem__(self, key, val):
|
||||
if isinstance(key, slice):
|
||||
StrictOrderingOnAppendList.ensure_sorted(val)
|
||||
return super(StrictOrderingOnAppendList, self).__setitem__(key, val)
|
||||
|
||||
def __add__(self, other):
|
||||
StrictOrderingOnAppendListMixin.ensure_sorted(other)
|
||||
StrictOrderingOnAppendList.ensure_sorted(other)
|
||||
|
||||
return super(StrictOrderingOnAppendListMixin, self).__add__(other)
|
||||
return super(StrictOrderingOnAppendList, self).__add__(other)
|
||||
|
||||
def __iadd__(self, other):
|
||||
StrictOrderingOnAppendListMixin.ensure_sorted(other)
|
||||
StrictOrderingOnAppendList.ensure_sorted(other)
|
||||
|
||||
return super(StrictOrderingOnAppendListMixin, self).__iadd__(other)
|
||||
|
||||
|
||||
class StrictOrderingOnAppendList(ListMixin, StrictOrderingOnAppendListMixin,
|
||||
list):
|
||||
"""A list specialized for moz.build environments.
|
||||
|
||||
We overload the assignment and append operations to require that incoming
|
||||
elements be ordered. This enforces cleaner style in moz.build files.
|
||||
"""
|
||||
return super(StrictOrderingOnAppendList, self).__iadd__(other)
|
||||
|
||||
|
||||
class ImmutableStrictOrderingOnAppendList(StrictOrderingOnAppendList):
|
||||
|
@ -548,51 +556,60 @@ class ImmutableStrictOrderingOnAppendList(StrictOrderingOnAppendList):
|
|||
raise Exception("cannot use += on this type")
|
||||
|
||||
|
||||
class ListWithActionMixin(object):
|
||||
"""Mixin to create lists with pre-processing. See ListWithAction."""
|
||||
|
||||
def __init__(self, iterable=None, action=None):
|
||||
if iterable is None:
|
||||
iterable = []
|
||||
if not callable(action):
|
||||
raise ValueError('A callabe action is required to construct '
|
||||
'a ListWithAction')
|
||||
|
||||
self._action = action
|
||||
iterable = [self._action(i) for i in iterable]
|
||||
super(ListWithActionMixin, self).__init__(iterable)
|
||||
|
||||
def extend(self, l):
|
||||
l = [self._action(i) for i in l]
|
||||
return super(ListWithActionMixin, self).extend(l)
|
||||
|
||||
def __setslice__(self, i, j, sequence):
|
||||
sequence = [self._action(item) for item in sequence]
|
||||
return super(ListWithActionMixin, self).__setslice__(i, j, sequence)
|
||||
|
||||
def __iadd__(self, other):
|
||||
other = [self._action(i) for i in other]
|
||||
return super(ListWithActionMixin, self).__iadd__(other)
|
||||
|
||||
|
||||
class StrictOrderingOnAppendListWithAction(StrictOrderingOnAppendListMixin,
|
||||
ListMixin, ListWithActionMixin, list):
|
||||
class StrictOrderingOnAppendListWithAction(StrictOrderingOnAppendList):
|
||||
"""An ordered list that accepts a callable to be applied to each item.
|
||||
|
||||
A callable (action) passed to the constructor is run on each item of input.
|
||||
The result of running the callable on each item will be stored in place of
|
||||
the original input, but the original item must be used to enforce sortedness.
|
||||
Note that the order of superclasses is therefore significant.
|
||||
"""
|
||||
|
||||
def __init__(self, iterable=(), action=None):
|
||||
if not callable(action):
|
||||
raise ValueError('A callable action is required to construct '
|
||||
'a StrictOrderingOnAppendListWithAction')
|
||||
|
||||
class ListWithAction(ListMixin, ListWithActionMixin, list):
|
||||
"""A list that accepts a callable to be applied to each item.
|
||||
self._action = action
|
||||
if not isinstance(iterable, (tuple, list)):
|
||||
raise ValueError(
|
||||
'StrictOrderingOnAppendListWithAction can only be initialized '
|
||||
'with another list')
|
||||
iterable = [self._action(i) for i in iterable]
|
||||
super(StrictOrderingOnAppendListWithAction, self).__init__(
|
||||
iterable, action=action)
|
||||
|
||||
A callable (action) may optionally be passed to the constructor to run on
|
||||
each item of input. The result of calling the callable on each item will be
|
||||
stored in place of the original input.
|
||||
"""
|
||||
def extend(self, l):
|
||||
if not isinstance(l, list):
|
||||
raise ValueError(
|
||||
'StrictOrderingOnAppendListWithAction can only be extended '
|
||||
'with another list')
|
||||
l = [self._action(i) for i in l]
|
||||
return super(StrictOrderingOnAppendListWithAction, self).extend(l)
|
||||
|
||||
def __setitem__(self, key, val):
|
||||
if isinstance(key, slice):
|
||||
if not isinstance(val, list):
|
||||
raise ValueError(
|
||||
'StrictOrderingOnAppendListWithAction can only be sliced '
|
||||
'with another list')
|
||||
val = [self._action(item) for item in val]
|
||||
return super(StrictOrderingOnAppendListWithAction, self).__setitem__(
|
||||
key, val)
|
||||
|
||||
def __add__(self, other):
|
||||
if not isinstance(other, list):
|
||||
raise ValueError(
|
||||
'StrictOrderingOnAppendListWithAction can only be added with '
|
||||
'another list')
|
||||
return super(StrictOrderingOnAppendListWithAction, self).__add__(other)
|
||||
|
||||
def __iadd__(self, other):
|
||||
if not isinstance(other, list):
|
||||
raise ValueError(
|
||||
'StrictOrderingOnAppendListWithAction can only be added with '
|
||||
'another list')
|
||||
other = [self._action(i) for i in other]
|
||||
return super(StrictOrderingOnAppendListWithAction, self).__iadd__(other)
|
||||
|
||||
|
||||
class MozbuildDeletionError(Exception):
|
||||
|
@ -685,8 +702,17 @@ def StrictOrderingOnAppendListWithFlagsFactory(flags):
|
|||
return self._flags[name]
|
||||
|
||||
def __setitem__(self, name, value):
|
||||
if not isinstance(name, slice):
|
||||
raise TypeError("'%s' object does not support item assignment" %
|
||||
self.__class__.__name__)
|
||||
result = super(StrictOrderingOnAppendListWithFlagsSpecialization,
|
||||
self).__setitem__(name, value)
|
||||
# We may have removed items.
|
||||
for k in set(self._flags.keys()) - set(self):
|
||||
del self._flags[k]
|
||||
if isinstance(value, StrictOrderingOnAppendListWithFlags):
|
||||
self._update_flags(value)
|
||||
return result
|
||||
|
||||
def _update_flags(self, other):
|
||||
if self._flags_type._flags != other._flags_type._flags:
|
||||
|
@ -701,22 +727,15 @@ def StrictOrderingOnAppendListWithFlagsFactory(flags):
|
|||
self._flags.update(other._flags)
|
||||
|
||||
def extend(self, l):
|
||||
result = super(StrictOrderingOnAppendList, self).extend(l)
|
||||
result = super(StrictOrderingOnAppendListWithFlagsSpecialization,
|
||||
self).extend(l)
|
||||
if isinstance(l, StrictOrderingOnAppendListWithFlags):
|
||||
self._update_flags(l)
|
||||
return result
|
||||
|
||||
def __setslice__(self, i, j, sequence):
|
||||
result = super(StrictOrderingOnAppendList, self).__setslice__(i, j, sequence)
|
||||
# We may have removed items.
|
||||
for name in set(self._flags.keys()) - set(self):
|
||||
del self._flags[name]
|
||||
if isinstance(sequence, StrictOrderingOnAppendListWithFlags):
|
||||
self._update_flags(sequence)
|
||||
return result
|
||||
|
||||
def __add__(self, other):
|
||||
result = super(StrictOrderingOnAppendList, self).__add__(other)
|
||||
result = super(StrictOrderingOnAppendListWithFlagsSpecialization,
|
||||
self).__add__(other)
|
||||
if isinstance(other, StrictOrderingOnAppendListWithFlags):
|
||||
# Result has flags from other but not from self, since
|
||||
# internally we duplicate self and then extend with other, and
|
||||
|
@ -728,7 +747,8 @@ def StrictOrderingOnAppendListWithFlagsFactory(flags):
|
|||
return result
|
||||
|
||||
def __iadd__(self, other):
|
||||
result = super(StrictOrderingOnAppendList, self).__iadd__(other)
|
||||
result = super(StrictOrderingOnAppendListWithFlagsSpecialization,
|
||||
self).__iadd__(other)
|
||||
if isinstance(other, StrictOrderingOnAppendListWithFlags):
|
||||
self._update_flags(other)
|
||||
return result
|
||||
|
@ -1060,54 +1080,11 @@ def TypedNamedTuple(name, fields):
|
|||
'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.'''
|
||||
|
||||
def _ensure_type(self, l):
|
||||
if isinstance(l, self.__class__):
|
||||
return l
|
||||
|
||||
return [self.normalize(e) for e in l]
|
||||
|
||||
def __init__(self, iterable=None, **kwargs):
|
||||
if iterable is None:
|
||||
iterable = []
|
||||
iterable = self._ensure_type(iterable)
|
||||
|
||||
super(TypedListMixin, self).__init__(iterable, **kwargs)
|
||||
|
||||
def extend(self, l):
|
||||
l = self._ensure_type(l)
|
||||
|
||||
return super(TypedListMixin, self).extend(l)
|
||||
|
||||
def __setslice__(self, i, j, sequence):
|
||||
sequence = self._ensure_type(sequence)
|
||||
|
||||
return super(TypedListMixin, self).__setslice__(i, j,
|
||||
sequence)
|
||||
|
||||
def __add__(self, other):
|
||||
other = self._ensure_type(other)
|
||||
|
||||
return super(TypedListMixin, self).__add__(other)
|
||||
|
||||
def __iadd__(self, other):
|
||||
other = self._ensure_type(other)
|
||||
|
||||
return super(TypedListMixin, self).__iadd__(other)
|
||||
|
||||
def append(self, other):
|
||||
self += [other]
|
||||
|
||||
|
||||
@memoize
|
||||
def TypedList(type, base_class=List):
|
||||
'''A list with type coercion.
|
||||
|
@ -1120,13 +1097,49 @@ def TypedList(type, base_class=List):
|
|||
|
||||
TypedList(unicode, StrictOrderingOnAppendList)
|
||||
'''
|
||||
class _TypedList(TypedListMixin, base_class):
|
||||
class _TypedList(base_class):
|
||||
@staticmethod
|
||||
def normalize(e):
|
||||
if not isinstance(e, type):
|
||||
e = type(e)
|
||||
return e
|
||||
|
||||
def _ensure_type(self, l):
|
||||
if isinstance(l, self.__class__):
|
||||
return l
|
||||
|
||||
return [self.normalize(e) for e in l]
|
||||
|
||||
def __init__(self, iterable=None, **kwargs):
|
||||
if iterable is None:
|
||||
iterable = []
|
||||
iterable = self._ensure_type(iterable)
|
||||
|
||||
super(_TypedList, self).__init__(iterable, **kwargs)
|
||||
|
||||
def extend(self, l):
|
||||
l = self._ensure_type(l)
|
||||
|
||||
return super(_TypedList, self).extend(l)
|
||||
|
||||
def __setitem__(self, key, val):
|
||||
val = self._ensure_type(val)
|
||||
|
||||
return super(_TypedList, self).__setitem__(key, val)
|
||||
|
||||
def __add__(self, other):
|
||||
other = self._ensure_type(other)
|
||||
|
||||
return super(_TypedList, self).__add__(other)
|
||||
|
||||
def __iadd__(self, other):
|
||||
other = self._ensure_type(other)
|
||||
|
||||
return super(_TypedList, self).__iadd__(other)
|
||||
|
||||
def append(self, other):
|
||||
self += [other]
|
||||
|
||||
return _TypedList
|
||||
|
||||
|
||||
|
@ -1150,19 +1163,19 @@ def group_unified_files(files, unified_prefix, unified_suffix,
|
|||
files = sorted(files)
|
||||
|
||||
# Our last returned list of source filenames may be short, and we
|
||||
# don't want the fill value inserted by izip_longest to be an
|
||||
# don't want the fill value inserted by zip_longest to be an
|
||||
# issue. So we do a little dance to filter it out ourselves.
|
||||
dummy_fill_value = ("dummy",)
|
||||
|
||||
def filter_out_dummy(iterable):
|
||||
return itertools.ifilter(lambda x: x != dummy_fill_value,
|
||||
return six.moves.filter(lambda x: x != dummy_fill_value,
|
||||
iterable)
|
||||
|
||||
# From the itertools documentation, slightly modified:
|
||||
def grouper(n, iterable):
|
||||
"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
|
||||
args = [iter(iterable)] * n
|
||||
return itertools.izip_longest(fillvalue=dummy_fill_value, *args)
|
||||
return six.moves.zip_longest(fillvalue=dummy_fill_value, *args)
|
||||
|
||||
for i, unified_group in enumerate(grouper(files_per_unified_file,
|
||||
files)):
|
||||
|
@ -1179,7 +1192,7 @@ def pair(iterable):
|
|||
[(1,2), (3,4), (5,6)]
|
||||
'''
|
||||
i = iter(iterable)
|
||||
return itertools.izip_longest(i, i)
|
||||
return six.moves.zip_longest(i, i)
|
||||
|
||||
|
||||
VARIABLES_RE = re.compile('\$\((\w+)\)')
|
||||
|
@ -1265,7 +1278,29 @@ def _escape_char(c):
|
|||
return six.text_type(c.encode('unicode_escape'))
|
||||
|
||||
|
||||
if six.PY2: # Not supported for py3 yet
|
||||
# The default PrettyPrinter has some issues with UTF-8, so we need to override
|
||||
# some stuff here.
|
||||
class _PrettyPrinter(pprint.PrettyPrinter):
|
||||
def format(self, object, context, maxlevels, level):
|
||||
if not (isinstance(object, six.text_type) or
|
||||
isinstance(object, six.binary_type)):
|
||||
return super(_PrettyPrinter, self).format(
|
||||
object, context, maxlevels, level)
|
||||
# This is super hacky and weird, but the output of 'repr' actually
|
||||
# varies based on the default I/O encoding of the process, which isn't
|
||||
# necessarily utf-8. Instead we open a new shell and ask what the repr
|
||||
# WOULD be assuming the default encoding is utf-8. If you can come up
|
||||
# with a better way of doing this without simply re-implementing the
|
||||
# logic of "repr", please replace this.
|
||||
env = dict(os.environ)
|
||||
env['PYTHONIOENCODING'] = 'utf-8'
|
||||
ret = six.ensure_text(subprocess.check_output(
|
||||
[sys.executable], input='print(repr(%s))' % repr(object),
|
||||
universal_newlines=True, env=env, encoding='utf-8')).strip()
|
||||
return (ret, True, False)
|
||||
|
||||
|
||||
if six.PY2: # Delete when we get rid of Python 2.
|
||||
# Mapping table between raw characters below \x80 and their escaped
|
||||
# counterpart, when they differ
|
||||
_INDENTED_REPR_TABLE = {
|
||||
|
@ -1286,7 +1321,8 @@ def indented_repr(o, indent=4):
|
|||
assumes `from __future__ import unicode_literals`.
|
||||
'''
|
||||
if six.PY3:
|
||||
raise NotImplementedError("indented_repr is not yet supported on py3")
|
||||
return _PrettyPrinter(indent=indent).pformat(o)
|
||||
# Delete everything below when we get rid of Python 2.
|
||||
one_indent = ' ' * indent
|
||||
|
||||
def recurse_indented_repr(o, level):
|
||||
|
|
Загрузка…
Ссылка в новой задаче