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:
Ricky Stewart 2020-01-08 16:08:15 +00:00
Родитель 8c6dffda6c
Коммит fe95d91222
5 изменённых файлов: 243 добавлений и 200 удалений

Просмотреть файл

@ -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,
{"foo": True, "bar": True})
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),
"Expected a list of strings, not an element of "
"<type 'bool'>")
six.assertRegex(
self, str(ve.exception),
"Expected a list of strings, not an element of "
"<(?: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):
raise TypeError("'%s' object does not support item assignment" %
self.__class__.__name__)
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,
iterable)
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):