aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYilei Yang <yileiyang@google.com>2022-03-21 08:41:15 -0700
committerCopybara-Service <copybara-worker@google.com>2022-03-21 08:41:41 -0700
commit6027a51124a64c5b02cad845abfe5874035c717b (patch)
treeb542a7ce0834e9a040294a7631482c63eeee5d2d
parentb0c97c0aeffbf1c384f3e53fc4a822b4b228972e (diff)
downloadabsl-py-6027a51124a64c5b02cad845abfe5874035c717b.tar.gz
Remove the use of six in absl.testing.
PiperOrigin-RevId: 436216380 Change-Id: I1058b3893eaf6e18c2872c2cf2533c3109bb972f
-rw-r--r--absl/testing/BUILD3
-rw-r--r--absl/testing/absltest.py178
-rw-r--r--absl/testing/parameterized.py24
-rw-r--r--absl/testing/xml_reporter.py18
4 files changed, 65 insertions, 158 deletions
diff --git a/absl/testing/BUILD b/absl/testing/BUILD
index de77bcc..f91f821 100644
--- a/absl/testing/BUILD
+++ b/absl/testing/BUILD
@@ -12,7 +12,6 @@ py_library(
"//absl:app",
"//absl/flags",
"//absl/logging",
- "@six_archive//:six",
],
)
@@ -36,7 +35,6 @@ py_library(
deps = [
":absltest",
"//absl:_collections_abc",
- "@six_archive//:six",
],
)
@@ -47,7 +45,6 @@ py_library(
visibility = ["//visibility:public"],
deps = [
":_pretty_print_reporter",
- "@six_archive//:six",
],
)
diff --git a/absl/testing/absltest.py b/absl/testing/absltest.py
index 37b902e..1303fd4 100644
--- a/absl/testing/absltest.py
+++ b/absl/testing/absltest.py
@@ -18,10 +18,6 @@ This module contains base classes and high-level functions for Abseil-style
tests.
"""
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import contextlib
import difflib
import enum
@@ -43,6 +39,8 @@ import sys
import tempfile
import textwrap
import unittest
+from unittest import mock # pylint: disable=unused-import Allow absltest.mock.
+from urllib import parse
try:
# The faulthandler module isn't always available, and pytype doesn't
@@ -60,9 +58,6 @@ from absl import logging
from absl._collections_abc import abc
from absl.testing import _pretty_print_reporter
from absl.testing import xml_reporter
-import six
-from six.moves import urllib
-from six.moves import xrange # pylint: disable=redefined-builtin
# Make typing an optional import to avoid it being a required dependency
# in Python 2. Type checkers will still understand the imports.
@@ -84,14 +79,6 @@ else:
_OutcomeType = unittest.case._Outcome # pytype: disable=module-attr
-if six.PY3:
- from unittest import mock # pylint: disable=unused-import
-else:
- try:
- import mock # type: ignore
- except ImportError:
- mock = None
-
# Re-export a bunch of unittest functions we support so that people don't
# have to import unittest to get them
@@ -107,7 +94,7 @@ expectedFailure = unittest.expectedFailure
FLAGS = flags.FLAGS
-_TEXT_OR_BINARY_TYPES = (six.text_type, six.binary_type)
+_TEXT_OR_BINARY_TYPES = (str, bytes)
# Suppress surplus entries in AssertionError stack traces.
__unittest = True # pylint: disable=invalid-name
@@ -296,8 +283,8 @@ def _open(filepath, mode, _open_func=open):
# type: (Text, Text, Callable[..., IO]) -> IO
"""Opens a file.
- Like open(), but compatible with Python 2 and 3. Also ensures that we can open
- real files even if tests stub out open().
+ Like open(), but ensure that we can open real files even if tests stub out
+ open().
Args:
filepath: A filepath.
@@ -307,10 +294,7 @@ def _open(filepath, mode, _open_func=open):
Returns:
The opened file object.
"""
- if six.PY2:
- return _open_func(filepath, mode)
- else:
- return _open_func(filepath, mode, encoding='utf-8')
+ return _open_func(filepath, mode, encoding='utf-8')
class _TempDir(object):
@@ -390,7 +374,7 @@ class _TempDir(object):
# Note: there's no need to clear the directory since the containing
# dir was cleared by the tempdir() function.
- _makedirs_exist_ok(path)
+ os.makedirs(path, exist_ok=True)
return _TempDir(path)
@@ -418,14 +402,14 @@ class _TempFile(object):
if file_path:
cleanup_path = os.path.join(base_path, _get_first_part(file_path))
path = os.path.join(base_path, file_path)
- _makedirs_exist_ok(os.path.dirname(path))
+ os.makedirs(os.path.dirname(path), exist_ok=True)
# The file may already exist, in which case, ensure it's writable so that
# it can be truncated.
if os.path.exists(path) and not os.access(path, os.W_OK):
stat_info = os.stat(path)
os.chmod(path, stat_info.st_mode | stat.S_IWUSR)
else:
- _makedirs_exist_ok(base_path)
+ os.makedirs(base_path, exist_ok=True)
fd, path = tempfile.mkstemp(dir=str(base_path))
os.close(fd)
cleanup_path = path
@@ -433,7 +417,7 @@ class _TempFile(object):
tf = cls(path)
if content:
- if isinstance(content, six.text_type):
+ if isinstance(content, str):
tf.write_text(content, mode=mode, encoding=encoding, errors=errors)
else:
tf.write_bytes(content, mode)
@@ -482,8 +466,6 @@ class _TempFile(object):
encoding: The encoding to use when writing the text to the file.
errors: The error handling strategy to use when converting text to bytes.
"""
- if six.PY2 and isinstance(text, bytes):
- text = text.decode(encoding, errors)
with self.open_text(mode, encoding=encoding, errors=errors) as fp:
fp.write(text)
@@ -673,12 +655,12 @@ class TestCase(unittest.TestCase):
path = os.path.join(test_path, name)
cleanup_path = os.path.join(test_path, _get_first_part(name))
else:
- _makedirs_exist_ok(test_path)
+ os.makedirs(test_path, exist_ok=True)
path = tempfile.mkdtemp(dir=test_path)
cleanup_path = path
_rmtree_ignore_errors(cleanup_path)
- _makedirs_exist_ok(path)
+ os.makedirs(path, exist_ok=True)
self._maybe_add_temp_path_cleanup(cleanup_path, cleanup)
@@ -783,7 +765,8 @@ class TestCase(unittest.TestCase):
@classmethod
def _get_tempdir_path_cls(cls):
# type: () -> Text
- return os.path.join(TEST_TMPDIR.value, _get_qualname(cls))
+ return os.path.join(TEST_TMPDIR.value,
+ cls.__qualname__.replace('__main__.', ''))
def _get_tempdir_path_test(self):
# type: () -> Text
@@ -1053,51 +1036,14 @@ class TestCase(unittest.TestCase):
def assertItemsEqual(self, expected_seq, actual_seq, msg=None):
"""Deprecated, please use assertCountEqual instead.
- This is equivalent to assertCountEqual in Python 3. An implementation of
- assertCountEqual is also provided by absltest.TestCase for Python 2.
+ This is equivalent to assertCountEqual.
Args:
expected_seq: A sequence containing elements we are expecting.
actual_seq: The sequence that we are testing.
msg: The message to be printed if the test fails.
"""
- if six.PY3:
- # The assertItemsEqual method was renamed assertCountEqual in Python 3.2
- super(TestCase, self).assertCountEqual(expected_seq, actual_seq, msg)
- else:
- super(TestCase, self).assertItemsEqual(expected_seq, actual_seq, msg)
-
- # Only override assertCountEqual in Python 2 to avoid unnecessary calls.
- if six.PY2:
-
- def assertCountEqual(self, expected_seq, actual_seq, msg=None):
- """Tests two sequences have the same elements regardless of order.
-
- It tests that the first sequence contains the same elements as the
- second, regardless of their order. When they don't, an error message
- listing the differences between the sequences will be generated.
-
- Duplicate elements are not ignored when comparing first and second.
- It verifies whether each element has the same count in both sequences.
- Equivalent to:
-
- self.assertEqual(Counter(list(expected_seq)),
- Counter(list(actual_seq)))
-
- but works with sequences of unhashable objects as well.
-
- Example:
- - [0, 1, 1] and [1, 0, 1] compare equal.
- - [0, 0, 1] and [0, 1] compare unequal.
-
- Args:
- expected_seq: A sequence containing elements we are expecting.
- actual_seq: The sequence that we are testing.
- msg: The message to be printed if the test fails.
-
- """
- # Only call super's method to avoid potential infinite recursions.
- super(TestCase, self).assertItemsEqual(expected_seq, actual_seq, msg)
+ super().assertCountEqual(expected_seq, actual_seq, msg)
def assertSameElements(self, expected_seq, actual_seq, msg=None):
"""Asserts that two sequences have the same elements (in any order).
@@ -1157,10 +1103,10 @@ class TestCase(unittest.TestCase):
# has a different error format. However, I find this slightly more readable.
def assertMultiLineEqual(self, first, second, msg=None, **kwargs):
"""Asserts that two multi-line strings are equal."""
- assert isinstance(first, six.string_types), (
- 'First argument is not a string: %r' % (first,))
- assert isinstance(second, six.string_types), (
- 'Second argument is not a string: %r' % (second,))
+ assert isinstance(first,
+ str), ('First argument is not a string: %r' % (first,))
+ assert isinstance(second,
+ str), ('Second argument is not a string: %r' % (second,))
line_limit = kwargs.pop('line_limit', 0)
if kwargs:
raise TypeError('Unexpected keyword args {}'.format(tuple(kwargs)))
@@ -1237,14 +1183,14 @@ class TestCase(unittest.TestCase):
if type(regex) is not regex_type: # pylint: disable=unidiomatic-typecheck
self.fail('regexes list must all be the same type.', message)
- if regex_type is bytes and isinstance(actual_str, six.text_type):
+ if regex_type is bytes and isinstance(actual_str, str):
regexes = [regex.decode('utf-8') for regex in regexes]
- regex_type = six.text_type
- elif regex_type is six.text_type and isinstance(actual_str, bytes):
+ regex_type = str
+ elif regex_type is str and isinstance(actual_str, bytes):
regexes = [regex.encode('utf-8') for regex in regexes]
regex_type = bytes
- if regex_type is six.text_type:
+ if regex_type is str:
regex = u'(?:%s)' % u')|(?:'.join(regexes)
elif regex_type is bytes:
regex = b'(?:' + (b')|(?:'.join(regexes)) + b')'
@@ -1276,7 +1222,7 @@ class TestCase(unittest.TestCase):
# We need bytes regexes here because `err` is bytes.
# Accommodate code which listed their output regexes w/o the b'' prefix by
# converting them to bytes for the user.
- if isinstance(regexes[0], six.text_type):
+ if isinstance(regexes[0], str):
regexes = [regex.encode('utf-8') for regex in regexes]
command_string = get_command_string(command)
@@ -1322,7 +1268,7 @@ class TestCase(unittest.TestCase):
# We need bytes regexes here because `err` is bytes.
# Accommodate code which listed their output regexes w/o the b'' prefix by
# converting them to bytes for the user.
- if isinstance(regexes[0], six.text_type):
+ if isinstance(regexes[0], str):
regexes = [regex.encode('utf-8') for regex in regexes]
command_string = get_command_string(command)
@@ -1533,7 +1479,7 @@ class TestCase(unittest.TestCase):
subsequence = list(subsequence)
longest_match = 0
- for start in xrange(1 + len(container) - len(subsequence)):
+ for start in range(1 + len(container) - len(subsequence)):
if longest_match == len(subsequence):
break
index = 0
@@ -1697,8 +1643,8 @@ class TestCase(unittest.TestCase):
if a == b:
return
- a_items = Sorted(list(six.iteritems(a)))
- b_items = Sorted(list(six.iteritems(b)))
+ a_items = Sorted(list(a.items()))
+ b_items = Sorted(list(b.items()))
unexpected = []
missing = []
@@ -1710,8 +1656,7 @@ class TestCase(unittest.TestCase):
"""Deterministic repr for dict."""
# Sort the entries based on their repr, not based on their sort order,
# which will be non-deterministic across executions, for many types.
- entries = sorted((safe_repr(k), safe_repr(v))
- for k, v in six.iteritems(dikt))
+ entries = sorted((safe_repr(k), safe_repr(v)) for k, v in dikt.items())
return '{%s}' % (', '.join('%s: %s' % pair for pair in entries))
message = ['%s != %s%s' % (Repr(a), Repr(b), ' (%s)' % msg if msg else '')]
@@ -1749,8 +1694,8 @@ class TestCase(unittest.TestCase):
def assertUrlEqual(self, a, b, msg=None):
"""Asserts that urls are equal, ignoring ordering of query params."""
- parsed_a = urllib.parse.urlparse(a)
- parsed_b = urllib.parse.urlparse(b)
+ parsed_a = parse.urlparse(a)
+ parsed_b = parse.urlparse(b)
self.assertEqual(parsed_a.scheme, parsed_b.scheme, msg)
self.assertEqual(parsed_a.netloc, parsed_b.netloc, msg)
self.assertEqual(parsed_a.path, parsed_b.path, msg)
@@ -1758,8 +1703,8 @@ class TestCase(unittest.TestCase):
self.assertEqual(sorted(parsed_a.params.split(';')),
sorted(parsed_b.params.split(';')), msg)
self.assertDictEqual(
- urllib.parse.parse_qs(parsed_a.query, keep_blank_values=True),
- urllib.parse.parse_qs(parsed_b.query, keep_blank_values=True), msg)
+ parse.parse_qs(parsed_a.query, keep_blank_values=True),
+ parse.parse_qs(parsed_b.query, keep_blank_values=True), msg)
def assertSameStructure(self, a, b, aname='a', bname='b', msg=None):
"""Asserts that two values contain the same structural content.
@@ -1897,7 +1842,7 @@ def _sorted_list_difference(expected, actual):
def _are_both_of_integer_type(a, b):
# type: (object, object) -> bool
- return isinstance(a, six.integer_types) and isinstance(b, six.integer_types)
+ return isinstance(a, int) and isinstance(b, int)
def _are_both_of_sequence_type(a, b):
@@ -1961,14 +1906,14 @@ def _walk_structure_for_problems(a, b, aname, bname, problem_list):
elif (isinstance(a, abc.Sequence) and
not isinstance(a, _TEXT_OR_BINARY_TYPES)):
minlen = min(len(a), len(b))
- for i in xrange(minlen):
+ for i in range(minlen):
_walk_structure_for_problems(
a[i], b[i], '%s[%d]' % (aname, i), '%s[%d]' % (bname, i),
problem_list)
- for i in xrange(minlen, len(a)):
+ for i in range(minlen, len(a)):
problem_list.append('%s has [%i] with value %r but %s does not' %
(aname, i, a[i], bname))
- for i in xrange(minlen, len(b)):
+ for i in range(minlen, len(b)):
problem_list.append('%s lacks [%i] but %s has it with value %r' %
(aname, i, bname, b[i]))
@@ -1985,7 +1930,7 @@ def get_command_string(command):
Returns:
A string suitable for use as a shell command.
"""
- if isinstance(command, six.string_types):
+ if isinstance(command, str):
return command
else:
if os.name == 'nt':
@@ -2020,7 +1965,7 @@ def get_command_stderr(command, env=None, close_fds=True):
# standard handles.
close_fds = False
- use_shell = isinstance(command, six.string_types)
+ use_shell = isinstance(command, str)
process = subprocess.Popen(
command,
close_fds=close_fds,
@@ -2085,7 +2030,7 @@ def _is_in_app_main():
"""Returns True iff app.run is active."""
f = sys._getframe().f_back # pylint: disable=protected-access
while f:
- if f.f_code == six.get_function_code(app.run): # pytype: disable=wrong-arg-types
+ if f.f_code == app.run.__code__:
return True
f = f.f_back
return False
@@ -2181,7 +2126,7 @@ def _run_in_app(function, args, kwargs):
# after the command-line has been parsed. So we have the for loop below
# to change back flags to their old values.
argv = FLAGS(sys.argv)
- for saved_flag in six.itervalues(saved_flags):
+ for saved_flag in saved_flags.values():
saved_flag.restore_flag()
function(argv, args, kwargs)
@@ -2203,15 +2148,10 @@ def _is_suspicious_attribute(testCaseClass, name):
if name.startswith('Test') and len(name) > 4 and name[4].isupper():
attr = getattr(testCaseClass, name)
if inspect.isfunction(attr) or inspect.ismethod(attr):
- if six.PY2:
- args = inspect.getargspec(attr)
- return (len(args.args) == 1 and args.args[0] == 'self'
- and args.varargs is None and args.keywords is None)
- else:
- args = inspect.getfullargspec(attr)
- return (len(args.args) == 1 and args.args[0] == 'self'
- and args.varargs is None and args.varkw is None and
- not args.kwonlyargs)
+ args = inspect.getfullargspec(attr)
+ return (len(args.args) == 1 and args.args[0] == 'self' and
+ args.varargs is None and args.varkw is None and
+ not args.kwonlyargs)
return False
@@ -2449,7 +2389,7 @@ def _setup_sharding(custom_loader=None):
# the test case names for this shard.
delegate_get_names = base_loader.getTestCaseNames
- bucket_iterator = itertools.cycle(xrange(total_shards))
+ bucket_iterator = itertools.cycle(range(total_shards))
def getShardedTestCaseNames(testCaseClass):
filtered_names = []
@@ -2522,7 +2462,7 @@ def _run_and_get_tests_result(argv, args, kwargs, xml_test_runner_class):
# Use an in-memory buffer (not backed by the actual file) to store the XML
# report, because some tools modify the file (e.g., create a placeholder
# with partial information, in case the test process crashes).
- xml_buffer = six.StringIO()
+ xml_buffer = io.StringIO()
kwargs['testRunner'].set_default_xml_stream(xml_buffer) # pytype: disable=attribute-error
# If we've used a seed to randomize test case ordering, we want to record it
@@ -2597,15 +2537,6 @@ def run_tests(argv, args, kwargs): # pylint: disable=line-too-long
sys.exit(not result.wasSuccessful())
-def _get_qualname(cls):
- # type: (Type) -> Text
- if six.PY3:
- name = cls.__qualname__
- else:
- name = '{}.{}'.format(cls.__module__, cls.__name__)
- return name.replace('__main__.', '')
-
-
def _rmtree_ignore_errors(path):
# type: (Text) -> None
if os.path.isfile(path):
@@ -2617,19 +2548,6 @@ def _rmtree_ignore_errors(path):
shutil.rmtree(path, ignore_errors=True)
-def _makedirs_exist_ok(dir_name):
- # type: (Text) -> None
- if six.PY3:
- os.makedirs(dir_name, exist_ok=True) # pylint: disable=unexpected-keyword-arg
- else:
- # Python 2 doesn't have the exist_ok arg, so we roll it ourselves
- try:
- os.makedirs(dir_name)
- except OSError as e:
- if e.errno != errno.EEXIST:
- raise
-
-
def _get_first_part(path):
# type: (Text) -> Text
parts = path.split(os.sep, 1)
diff --git a/absl/testing/parameterized.py b/absl/testing/parameterized.py
index 4d02c4e..f318b98 100644
--- a/absl/testing/parameterized.py
+++ b/absl/testing/parameterized.py
@@ -217,7 +217,6 @@ import unittest
from absl._collections_abc import abc
from absl.testing import absltest
-import six
_ADDR_RE = re.compile(r'\<([a-zA-Z0-9_\-\.]+) object at 0x[a-fA-F0-9]+\>')
@@ -247,15 +246,14 @@ def _clean_repr(obj):
def _non_string_or_bytes_iterable(obj):
- return (isinstance(obj, abc.Iterable) and
- not isinstance(obj, six.text_type) and
- not isinstance(obj, six.binary_type))
+ return (isinstance(obj, abc.Iterable) and not isinstance(obj, str) and
+ not isinstance(obj, bytes))
def _format_parameter_list(testcase_params):
if isinstance(testcase_params, abc.Mapping):
return ', '.join('%s=%s' % (argname, _clean_repr(value))
- for argname, value in six.iteritems(testcase_params))
+ for argname, value in testcase_params.items())
elif _non_string_or_bytes_iterable(testcase_params):
return ', '.join(map(_clean_repr, testcase_params))
else:
@@ -333,10 +331,11 @@ class _ParameterizedTestIter(object):
'Dict for named tests must contain key "%s"' % _NAMED_DICT_KEY)
# Create a new dict to avoid modifying the supplied testcase_params.
testcase_name = testcase_params[_NAMED_DICT_KEY]
- testcase_params = {k: v for k, v in six.iteritems(testcase_params)
- if k != _NAMED_DICT_KEY}
+ testcase_params = {
+ k: v for k, v in testcase_params.items() if k != _NAMED_DICT_KEY
+ }
elif _non_string_or_bytes_iterable(testcase_params):
- if not isinstance(testcase_params[0], six.string_types):
+ if not isinstance(testcase_params[0], str):
raise RuntimeError(
'The first element of named test parameters is the test name '
'suffix and must be a string')
@@ -388,7 +387,7 @@ def _modify_class(class_object, testcases, naming_type):
# NOTE: _test_params_repr is private to parameterized.TestCase and it's
# metaclass; do not use it outside of those classes.
class_object._test_params_reprs = test_params_reprs = {}
- for name, obj in six.iteritems(class_object.__dict__.copy()):
+ for name, obj in class_object.__dict__.copy().items():
if (name.startswith(unittest.TestLoader.testMethodPrefix)
and isinstance(obj, types.FunctionType)):
delattr(class_object, name)
@@ -396,7 +395,7 @@ def _modify_class(class_object, testcases, naming_type):
_update_class_dict_for_param_test_case(
class_object.__name__, methods, test_params_reprs, name,
_ParameterizedTestIter(obj, testcases, naming_type, name))
- for meth_name, meth in six.iteritems(methods):
+ for meth_name, meth in methods.items():
setattr(class_object, meth_name, meth)
@@ -554,7 +553,7 @@ class TestGeneratorMetaclass(type):
# NOTE: _test_params_repr is private to parameterized.TestCase and it's
# metaclass; do not use it outside of those classes.
test_params_reprs = dct.setdefault('_test_params_reprs', {})
- for name, obj in six.iteritems(dct.copy()):
+ for name, obj in dct.copy().items():
if (name.startswith(unittest.TestLoader.testMethodPrefix) and
_non_string_or_bytes_iterable(obj)):
# NOTE: `obj` might not be a _ParameterizedTestIter in two cases:
@@ -637,8 +636,7 @@ def _update_class_dict_for_param_test_case(
test_params_reprs[new_name] = getattr(func, '__x_params_repr__', '')
-@six.add_metaclass(TestGeneratorMetaclass)
-class TestCase(absltest.TestCase):
+class TestCase(absltest.TestCase, metaclass=TestGeneratorMetaclass):
"""Base class for test cases using the parameters decorator."""
# visibility: private; do not call outside this class.
diff --git a/absl/testing/xml_reporter.py b/absl/testing/xml_reporter.py
index 103827b..da56e39 100644
--- a/absl/testing/xml_reporter.py
+++ b/absl/testing/xml_reporter.py
@@ -27,7 +27,6 @@ import traceback
import unittest
from xml.sax import saxutils
from absl.testing import _pretty_print_reporter
-import six
# See http://www.w3.org/TR/REC-xml/#NT-Char
@@ -74,7 +73,7 @@ def _escape_cdata(s):
Returns:
An escaped version of the input string.
"""
- for char, escaped in six.iteritems(_control_character_conversions):
+ for char, escaped in _control_character_conversions.items():
s = s.replace(char, escaped)
return s.replace(']]>', ']] >')
@@ -90,13 +89,8 @@ def _iso8601_timestamp(timestamp):
"""
if timestamp is None or timestamp < 0:
return None
- # Use utcfromtimestamp in PY2 because it doesn't have a built-in UTC object
- if six.PY2:
- return '%s+00:00' % datetime.datetime.utcfromtimestamp(
- timestamp).isoformat()
- else:
- return datetime.datetime.fromtimestamp(
- timestamp, tz=datetime.timezone.utc).isoformat()
+ return datetime.datetime.fromtimestamp(
+ timestamp, tz=datetime.timezone.utc).isoformat()
def _print_xml_element_header(element, attributes, stream, indentation=''):
@@ -110,8 +104,8 @@ def _print_xml_element_header(element, attributes, stream, indentation=''):
"""
stream.write('%s<%s' % (indentation, element))
for attribute in attributes:
- if len(attribute) == 2 \
- and attribute[0] is not None and attribute[1] is not None:
+ if (len(attribute) == 2 and attribute[0] is not None and
+ attribute[1] is not None):
stream.write(' %s="%s"' % (attribute[0], attribute[1]))
stream.write('>\n')
@@ -279,7 +273,7 @@ class _TestSuiteResult(object):
_print_xml_element_header('testsuites', overall_attributes, stream)
if self._testsuites_properties:
stream.write(' <properties>\n')
- for name, value in sorted(six.iteritems(self._testsuites_properties)):
+ for name, value in sorted(self._testsuites_properties.items()):
stream.write(' <property name="%s" value="%s"></property>\n' %
(_escape_xml_attr(name), _escape_xml_attr(str(value))))
stream.write(' </properties>\n')