diff options
author | Larry Hastings <larry@hastings.org> | 2015-03-30 08:50:00 (GMT) |
---|---|---|
committer | Larry Hastings <larry@hastings.org> | 2015-03-30 08:50:00 (GMT) |
commit | 09dab7a87eaa7115eeaf73016d65f2f835e25986 (patch) | |
tree | 04a83c1cfce64d3418a245feae7fee445e510949 /Lib | |
parent | 736240399e469a4134dac32a340feca5395baa28 (diff) | |
parent | 45cff0c0e6c4a31ed3b5b88ee803320862fbd43a (diff) | |
download | cpython-09dab7a87eaa7115eeaf73016d65f2f835e25986.zip cpython-09dab7a87eaa7115eeaf73016d65f2f835e25986.tar.gz cpython-09dab7a87eaa7115eeaf73016d65f2f835e25986.tar.bz2 |
Merge 3.5.0a3 release engineering changes back into trunk.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/csv.py | 7 | ||||
-rw-r--r-- | Lib/email/_header_value_parser.py | 34 | ||||
-rwxr-xr-x | Lib/test/regrtest.py | 26 | ||||
-rw-r--r-- | Lib/test/test_csv.py | 8 | ||||
-rw-r--r-- | Lib/test/test_email/test__header_value_parser.py | 109 | ||||
-rw-r--r-- | Lib/test/test_format.py | 19 | ||||
-rw-r--r-- | Lib/test/test_time.py | 139 |
7 files changed, 236 insertions, 106 deletions
@@ -147,16 +147,13 @@ class DictWriter: if wrong_fields: raise ValueError("dict contains fields not in fieldnames: " + ", ".join([repr(x) for x in wrong_fields])) - return [rowdict.get(key, self.restval) for key in self.fieldnames] + return (rowdict.get(key, self.restval) for key in self.fieldnames) def writerow(self, rowdict): return self.writer.writerow(self._dict_to_list(rowdict)) def writerows(self, rowdicts): - rows = [] - for rowdict in rowdicts: - rows.append(self._dict_to_list(rowdict)) - return self.writer.writerows(rows) + return self.writer.writerows(map(self._dict_to_list, rowdicts)) # Guard Sniffer's type checking against builds that exclude complex() try: diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py index 1806cac..a9bdf44 100644 --- a/Lib/email/_header_value_parser.py +++ b/Lib/email/_header_value_parser.py @@ -71,6 +71,7 @@ import re import urllib # For urllib.parse.unquote from string import hexdigits from collections import OrderedDict +from operator import itemgetter from email import _encoded_words as _ew from email import errors from email import utils @@ -1098,15 +1099,34 @@ class MimeParameters(TokenList): params[name] = [] params[name].append((token.section_number, token)) for name, parts in params.items(): - parts = sorted(parts) - # XXX: there might be more recovery we could do here if, for - # example, this is really a case of a duplicate attribute name. + parts = sorted(parts, key=itemgetter(0)) + first_param = parts[0][1] + charset = first_param.charset + # Our arbitrary error recovery is to ignore duplicate parameters, + # to use appearance order if there are duplicate rfc 2231 parts, + # and to ignore gaps. This mimics the error recovery of get_param. + if not first_param.extended and len(parts) > 1: + if parts[1][0] == 0: + parts[1][1].defects.append(errors.InvalidHeaderDefect( + 'duplicate parameter name; duplicate(s) ignored')) + parts = parts[:1] + # Else assume the *0* was missing...note that this is different + # from get_param, but we registered a defect for this earlier. value_parts = [] - charset = parts[0][1].charset - for i, (section_number, param) in enumerate(parts): + i = 0 + for section_number, param in parts: if section_number != i: - param.defects.append(errors.InvalidHeaderDefect( - "inconsistent multipart parameter numbering")) + # We could get fancier here and look for a complete + # duplicate extended parameter and ignore the second one + # seen. But we're not doing that. The old code didn't. + if not param.extended: + param.defects.append(errors.InvalidHeaderDefect( + 'duplicate parameter name; duplicate ignored')) + continue + else: + param.defects.append(errors.InvalidHeaderDefect( + "inconsistent RFC2231 parameter numbering")) + i += 1 value = param.param_value if param.extended: try: diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py index ea1287d..7123ffb 100755 --- a/Lib/test/regrtest.py +++ b/Lib/test/regrtest.py @@ -1031,7 +1031,7 @@ class saved_test_environment: # to a thread, so check processes first. 'multiprocessing.process._dangling', 'threading._dangling', 'sysconfig._CONFIG_VARS', 'sysconfig._INSTALL_SCHEMES', - 'support.TESTFN', 'locale', 'warnings.showwarning', + 'files', 'locale', 'warnings.showwarning', ) def get_sys_argv(self): @@ -1187,20 +1187,16 @@ class saved_test_environment: sysconfig._INSTALL_SCHEMES.clear() sysconfig._INSTALL_SCHEMES.update(saved[2]) - def get_support_TESTFN(self): - if os.path.isfile(support.TESTFN): - result = 'f' - elif os.path.isdir(support.TESTFN): - result = 'd' - else: - result = None - return result - def restore_support_TESTFN(self, saved_value): - if saved_value is None: - if os.path.isfile(support.TESTFN): - os.unlink(support.TESTFN) - elif os.path.isdir(support.TESTFN): - shutil.rmtree(support.TESTFN) + def get_files(self): + return sorted(fn + ('/' if os.path.isdir(fn) else '') + for fn in os.listdir()) + def restore_files(self, saved_value): + fn = support.TESTFN + if fn not in saved_value and (fn + '/') not in saved_value: + if os.path.isfile(fn): + support.unlink(fn) + elif os.path.isdir(fn): + support.rmtree(fn) _lc = [getattr(locale, lc) for lc in dir(locale) if lc.startswith('LC_')] diff --git a/Lib/test/test_csv.py b/Lib/test/test_csv.py index 41ef790..7be3cc3 100644 --- a/Lib/test/test_csv.py +++ b/Lib/test/test_csv.py @@ -186,6 +186,14 @@ class Test_Csv(unittest.TestCase): self._write_test(['a',1,'p,q'], 'a,1,p\\,q', escapechar='\\', quoting = csv.QUOTE_NONE) + def test_write_iterable(self): + self._write_test(iter(['a', 1, 'p,q']), 'a,1,"p,q"') + self._write_test(iter(['a', 1, None]), 'a,1,') + self._write_test(iter([]), '') + self._write_test(iter([None]), '""') + self._write_error_test(csv.Error, iter([None]), quoting=csv.QUOTE_NONE) + self._write_test(iter([None, None]), ',') + def test_writerows(self): class BrokenFile: def write(self, buf): diff --git a/Lib/test/test_email/test__header_value_parser.py b/Lib/test/test_email/test__header_value_parser.py index 5404d19..d028f74 100644 --- a/Lib/test/test_email/test__header_value_parser.py +++ b/Lib/test/test_email/test__header_value_parser.py @@ -2456,6 +2456,115 @@ class TestParser(TestParserMixin, TestEmailBase): ";foo", ";foo", ";foo", [errors.InvalidHeaderDefect]*3 ) + +@parameterize +class Test_parse_mime_parameters(TestParserMixin, TestEmailBase): + + def mime_parameters_as_value(self, + value, + tl_str, + tl_value, + params, + defects): + mime_parameters = self._test_parse_x(parser.parse_mime_parameters, + value, tl_str, tl_value, defects) + self.assertEqual(mime_parameters.token_type, 'mime-parameters') + self.assertEqual(list(mime_parameters.params), params) + + + mime_parameters_params = { + + 'simple': ( + 'filename="abc.py"', + ' filename="abc.py"', + 'filename=abc.py', + [('filename', 'abc.py')], + []), + + 'multiple_keys': ( + 'filename="abc.py"; xyz=abc', + ' filename="abc.py"; xyz="abc"', + 'filename=abc.py; xyz=abc', + [('filename', 'abc.py'), ('xyz', 'abc')], + []), + + 'split_value': ( + "filename*0*=iso-8859-1''%32%30%31%2E; filename*1*=%74%69%66", + ' filename="201.tif"', + "filename*0*=iso-8859-1''%32%30%31%2E; filename*1*=%74%69%66", + [('filename', '201.tif')], + []), + + # Note that it is undefined what we should do for error recovery when + # there are duplicate parameter names or duplicate parts in a split + # part. We choose to ignore all duplicate parameters after the first + # and to take duplicate or missing rfc 2231 parts in apperance order. + # This is backward compatible with get_param's behavior, but the + # decisions are arbitrary. + + 'duplicate_key': ( + 'filename=abc.gif; filename=def.tiff', + ' filename="abc.gif"', + "filename=abc.gif; filename=def.tiff", + [('filename', 'abc.gif')], + [errors.InvalidHeaderDefect]), + + 'duplicate_key_with_split_value': ( + "filename*0*=iso-8859-1''%32%30%31%2E; filename*1*=%74%69%66;" + " filename=abc.gif", + ' filename="201.tif"', + "filename*0*=iso-8859-1''%32%30%31%2E; filename*1*=%74%69%66;" + " filename=abc.gif", + [('filename', '201.tif')], + [errors.InvalidHeaderDefect]), + + 'duplicate_key_with_split_value_other_order': ( + "filename=abc.gif; " + " filename*0*=iso-8859-1''%32%30%31%2E; filename*1*=%74%69%66", + ' filename="abc.gif"', + "filename=abc.gif;" + " filename*0*=iso-8859-1''%32%30%31%2E; filename*1*=%74%69%66", + [('filename', 'abc.gif')], + [errors.InvalidHeaderDefect]), + + 'duplicate_in_split_value': ( + "filename*0*=iso-8859-1''%32%30%31%2E; filename*1*=%74%69%66;" + " filename*1*=abc.gif", + ' filename="201.tifabc.gif"', + "filename*0*=iso-8859-1''%32%30%31%2E; filename*1*=%74%69%66;" + " filename*1*=abc.gif", + [('filename', '201.tifabc.gif')], + [errors.InvalidHeaderDefect]), + + 'missing_split_value': ( + "filename*0*=iso-8859-1''%32%30%31%2E; filename*3*=%74%69%66;", + ' filename="201.tif"', + "filename*0*=iso-8859-1''%32%30%31%2E; filename*3*=%74%69%66;", + [('filename', '201.tif')], + [errors.InvalidHeaderDefect]), + + 'duplicate_and_missing_split_value': ( + "filename*0*=iso-8859-1''%32%30%31%2E; filename*3*=%74%69%66;" + " filename*3*=abc.gif", + ' filename="201.tifabc.gif"', + "filename*0*=iso-8859-1''%32%30%31%2E; filename*3*=%74%69%66;" + " filename*3*=abc.gif", + [('filename', '201.tifabc.gif')], + [errors.InvalidHeaderDefect]*2), + + # Here we depart from get_param and assume the *0* was missing. + 'duplicate_with_broken_split_value': ( + "filename=abc.gif; " + " filename*2*=iso-8859-1''%32%30%31%2E; filename*3*=%74%69%66", + ' filename="abc.gif201.tif"', + "filename=abc.gif;" + " filename*2*=iso-8859-1''%32%30%31%2E; filename*3*=%74%69%66", + [('filename', 'abc.gif201.tif')], + # Defects are apparent missing *0*, and two 'out of sequence'. + [errors.InvalidHeaderDefect]*3), + + } + @parameterize class Test_parse_mime_version(TestParserMixin, TestEmailBase): diff --git a/Lib/test/test_format.py b/Lib/test/test_format.py index e1ea33f..5a2a357 100644 --- a/Lib/test/test_format.py +++ b/Lib/test/test_format.py @@ -272,9 +272,18 @@ class FormatTest(unittest.TestCase): #test_exc(unicode('abc %\u3000','raw-unicode-escape'), 1, ValueError, # "unsupported format character '?' (0x3000) at index 5") test_exc('%d', '1', TypeError, "%d format: a number is required, not str") + test_exc('%x', '1', TypeError, "%x format: a number is required, not str") + test_exc('%x', 3.14, TypeError, "%x format: an integer is required, not float") test_exc('%g', '1', TypeError, "a float is required") test_exc('no format', '1', TypeError, "not all arguments converted during string formatting") + test_exc('%c', -1, OverflowError, "%c arg not in range(0x110000)") + test_exc('%c', sys.maxunicode+1, OverflowError, + "%c arg not in range(0x110000)") + #test_exc('%c', 2**128, OverflowError, "%c arg not in range(0x110000)") + test_exc('%c', 3.14, TypeError, "%c requires int or char") + test_exc('%c', 'ab', TypeError, "%c requires int or char") + test_exc('%c', b'x', TypeError, "%c requires int or char") if maxsize == 2**31-1: # crashes 2.2.1 and earlier: @@ -339,6 +348,8 @@ class FormatTest(unittest.TestCase): "%d format: a number is required, not str") test_exc(b'%d', b'1', TypeError, "%d format: a number is required, not bytes") + test_exc(b'%x', 3.14, TypeError, + "%x format: an integer is required, not float") test_exc(b'%g', '1', TypeError, "float argument required, not str") test_exc(b'%g', b'1', TypeError, "float argument required, not bytes") test_exc(b'no format', 7, TypeError, @@ -347,11 +358,17 @@ class FormatTest(unittest.TestCase): "not all arguments converted during bytes formatting") test_exc(b'no format', bytearray(b'1'), TypeError, "not all arguments converted during bytes formatting") + test_exc(b"%c", -1, TypeError, + "%c requires an integer in range(256) or a single byte") test_exc(b"%c", 256, TypeError, "%c requires an integer in range(256) or a single byte") + test_exc(b"%c", 2**128, TypeError, + "%c requires an integer in range(256) or a single byte") test_exc(b"%c", b"Za", TypeError, "%c requires an integer in range(256) or a single byte") - test_exc(b"%c", "Yb", TypeError, + test_exc(b"%c", "Y", TypeError, + "%c requires an integer in range(256) or a single byte") + test_exc(b"%c", 3.14, TypeError, "%c requires an integer in range(256) or a single byte") test_exc(b"%b", "Xc", TypeError, "%b requires bytes, or an object that implements __bytes__, not 'str'") diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py index b0c97d5..4747cc6 100644 --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -24,17 +24,12 @@ TIME_MINYEAR = -TIME_MAXYEAR - 1 SEC_TO_NS = 10 ** 9 class _PyTime(enum.IntEnum): - # Round towards zero - ROUND_DOWN = 0 - # Round away from zero - ROUND_UP = 1 - # Round towards -Infinity - ROUND_FLOOR = 2 + # Round towards minus infinity (-inf) + ROUND_FLOOR = 0 + # Round towards infinity (+inf) + ROUND_CEILING = 1 -ALL_ROUNDING_METHODS = ( - _PyTime.ROUND_UP, - _PyTime.ROUND_DOWN, - _PyTime.ROUND_FLOOR) +ALL_ROUNDING_METHODS = (_PyTime.ROUND_FLOOR, _PyTime.ROUND_CEILING) class TimeTestCase(unittest.TestCase): @@ -614,24 +609,24 @@ class TestPytime(unittest.TestCase): def test_time_t(self): from _testcapi import pytime_object_to_time_t for obj, time_t, rnd in ( - # Round towards zero - (0, 0, _PyTime.ROUND_DOWN), - (-1, -1, _PyTime.ROUND_DOWN), - (-1.0, -1, _PyTime.ROUND_DOWN), - (-1.9, -1, _PyTime.ROUND_DOWN), - (1.0, 1, _PyTime.ROUND_DOWN), - (1.9, 1, _PyTime.ROUND_DOWN), - # Round away from zero - (0, 0, _PyTime.ROUND_UP), - (-1, -1, _PyTime.ROUND_UP), - (-1.0, -1, _PyTime.ROUND_UP), - (-1.9, -2, _PyTime.ROUND_UP), - (1.0, 1, _PyTime.ROUND_UP), - (1.9, 2, _PyTime.ROUND_UP), + # Round towards minus infinity (-inf) + (0, 0, _PyTime.ROUND_FLOOR), + (-1, -1, _PyTime.ROUND_FLOOR), + (-1.0, -1, _PyTime.ROUND_FLOOR), + (-1.9, -2, _PyTime.ROUND_FLOOR), + (1.0, 1, _PyTime.ROUND_FLOOR), + (1.9, 1, _PyTime.ROUND_FLOOR), + # Round towards infinity (+inf) + (0, 0, _PyTime.ROUND_CEILING), + (-1, -1, _PyTime.ROUND_CEILING), + (-1.0, -1, _PyTime.ROUND_CEILING), + (-1.9, -1, _PyTime.ROUND_CEILING), + (1.0, 1, _PyTime.ROUND_CEILING), + (1.9, 2, _PyTime.ROUND_CEILING), ): self.assertEqual(pytime_object_to_time_t(obj, rnd), time_t) - rnd = _PyTime.ROUND_DOWN + rnd = _PyTime.ROUND_FLOOR for invalid in self.invalid_values: self.assertRaises(OverflowError, pytime_object_to_time_t, invalid, rnd) @@ -640,39 +635,39 @@ class TestPytime(unittest.TestCase): def test_timespec(self): from _testcapi import pytime_object_to_timespec for obj, timespec, rnd in ( - # Round towards zero - (0, (0, 0), _PyTime.ROUND_DOWN), - (-1, (-1, 0), _PyTime.ROUND_DOWN), - (-1.0, (-1, 0), _PyTime.ROUND_DOWN), - (1e-9, (0, 1), _PyTime.ROUND_DOWN), - (1e-10, (0, 0), _PyTime.ROUND_DOWN), - (-1e-9, (-1, 999999999), _PyTime.ROUND_DOWN), - (-1e-10, (-1, 999999999), _PyTime.ROUND_DOWN), - (-1.2, (-2, 800000000), _PyTime.ROUND_DOWN), - (0.9999999999, (0, 999999999), _PyTime.ROUND_DOWN), - (1.1234567890, (1, 123456789), _PyTime.ROUND_DOWN), - (1.1234567899, (1, 123456789), _PyTime.ROUND_DOWN), - (-1.1234567890, (-2, 876543211), _PyTime.ROUND_DOWN), - (-1.1234567891, (-2, 876543210), _PyTime.ROUND_DOWN), - # Round away from zero - (0, (0, 0), _PyTime.ROUND_UP), - (-1, (-1, 0), _PyTime.ROUND_UP), - (-1.0, (-1, 0), _PyTime.ROUND_UP), - (1e-9, (0, 1), _PyTime.ROUND_UP), - (1e-10, (0, 1), _PyTime.ROUND_UP), - (-1e-9, (-1, 999999999), _PyTime.ROUND_UP), - (-1e-10, (-1, 999999999), _PyTime.ROUND_UP), - (-1.2, (-2, 800000000), _PyTime.ROUND_UP), - (0.9999999999, (1, 0), _PyTime.ROUND_UP), - (1.1234567890, (1, 123456790), _PyTime.ROUND_UP), - (1.1234567899, (1, 123456790), _PyTime.ROUND_UP), - (-1.1234567890, (-2, 876543211), _PyTime.ROUND_UP), - (-1.1234567891, (-2, 876543210), _PyTime.ROUND_UP), + # Round towards minus infinity (-inf) + (0, (0, 0), _PyTime.ROUND_FLOOR), + (-1, (-1, 0), _PyTime.ROUND_FLOOR), + (-1.0, (-1, 0), _PyTime.ROUND_FLOOR), + (1e-9, (0, 1), _PyTime.ROUND_FLOOR), + (1e-10, (0, 0), _PyTime.ROUND_FLOOR), + (-1e-9, (-1, 999999999), _PyTime.ROUND_FLOOR), + (-1e-10, (-1, 999999999), _PyTime.ROUND_FLOOR), + (-1.2, (-2, 800000000), _PyTime.ROUND_FLOOR), + (0.9999999999, (0, 999999999), _PyTime.ROUND_FLOOR), + (1.1234567890, (1, 123456789), _PyTime.ROUND_FLOOR), + (1.1234567899, (1, 123456789), _PyTime.ROUND_FLOOR), + (-1.1234567890, (-2, 876543211), _PyTime.ROUND_FLOOR), + (-1.1234567891, (-2, 876543210), _PyTime.ROUND_FLOOR), + # Round towards infinity (+inf) + (0, (0, 0), _PyTime.ROUND_CEILING), + (-1, (-1, 0), _PyTime.ROUND_CEILING), + (-1.0, (-1, 0), _PyTime.ROUND_CEILING), + (1e-9, (0, 1), _PyTime.ROUND_CEILING), + (1e-10, (0, 1), _PyTime.ROUND_CEILING), + (-1e-9, (-1, 999999999), _PyTime.ROUND_CEILING), + (-1e-10, (0, 0), _PyTime.ROUND_CEILING), + (-1.2, (-2, 800000000), _PyTime.ROUND_CEILING), + (0.9999999999, (1, 0), _PyTime.ROUND_CEILING), + (1.1234567890, (1, 123456790), _PyTime.ROUND_CEILING), + (1.1234567899, (1, 123456790), _PyTime.ROUND_CEILING), + (-1.1234567890, (-2, 876543211), _PyTime.ROUND_CEILING), + (-1.1234567891, (-2, 876543211), _PyTime.ROUND_CEILING), ): with self.subTest(obj=obj, round=rnd, timespec=timespec): self.assertEqual(pytime_object_to_timespec(obj, rnd), timespec) - rnd = _PyTime.ROUND_DOWN + rnd = _PyTime.ROUND_FLOOR for invalid in self.invalid_values: self.assertRaises(OverflowError, pytime_object_to_timespec, invalid, rnd) @@ -791,33 +786,26 @@ class TestPyTime_t(unittest.TestCase): PyTime_FromSecondsObject(-9223372037.0, rnd) # Conversion giving different results depending on the rounding method - UP = _PyTime.ROUND_UP - DOWN = _PyTime.ROUND_DOWN FLOOR = _PyTime.ROUND_FLOOR + CEILING = _PyTime.ROUND_CEILING for obj, ts, rnd in ( # close to zero - ( 1e-10, 1, UP), - ( 1e-10, 0, DOWN), ( 1e-10, 0, FLOOR), - (-1e-10, 0, DOWN), - (-1e-10, -1, UP), + ( 1e-10, 1, CEILING), (-1e-10, -1, FLOOR), + (-1e-10, 0, CEILING), # test rounding of the last nanosecond - ( 1.1234567899, 1123456790, UP), - ( 1.1234567899, 1123456789, DOWN), ( 1.1234567899, 1123456789, FLOOR), - (-1.1234567899, -1123456789, DOWN), - (-1.1234567899, -1123456790, UP), + ( 1.1234567899, 1123456790, CEILING), (-1.1234567899, -1123456790, FLOOR), + (-1.1234567899, -1123456789, CEILING), # close to 1 second - ( 0.9999999999, 1000000000, UP), - ( 0.9999999999, 999999999, DOWN), ( 0.9999999999, 999999999, FLOOR), - (-0.9999999999, -999999999, DOWN), - (-0.9999999999, -1000000000, UP), + ( 0.9999999999, 1000000000, CEILING), (-0.9999999999, -1000000000, FLOOR), + (-0.9999999999, -999999999, CEILING), ): with self.subTest(obj=obj, round=rnd, timestamp=ts): self.assertEqual(PyTime_FromSecondsObject(obj, rnd), ts) @@ -887,25 +875,20 @@ class TestPyTime_t(unittest.TestCase): with self.subTest(nanoseconds=ns, timeval=tv, round=rnd): self.assertEqual(PyTime_AsTimeval(ns, rnd), tv) - UP = _PyTime.ROUND_UP - DOWN = _PyTime.ROUND_DOWN FLOOR = _PyTime.ROUND_FLOOR + CEILING = _PyTime.ROUND_CEILING for ns, tv, rnd in ( # nanoseconds - (1, (0, 1), UP), - (1, (0, 0), DOWN), (1, (0, 0), FLOOR), - (-1, (0, 0), DOWN), - (-1, (-1, 999999), UP), + (1, (0, 1), CEILING), (-1, (-1, 999999), FLOOR), + (-1, (0, 0), CEILING), # seconds + nanoseconds - (1234567001, (1, 234568), UP), - (1234567001, (1, 234567), DOWN), (1234567001, (1, 234567), FLOOR), - (-1234567001, (-2, 765433), DOWN), - (-1234567001, (-2, 765432), UP), + (1234567001, (1, 234568), CEILING), (-1234567001, (-2, 765432), FLOOR), + (-1234567001, (-2, 765433), CEILING), ): with self.subTest(nanoseconds=ns, timeval=tv, round=rnd): self.assertEqual(PyTime_AsTimeval(ns, rnd), tv) |