diff options
Diffstat (limited to 'Lib/test')
181 files changed, 7016 insertions, 2129 deletions
diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 44d6c71..78208c3 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -2543,7 +2543,7 @@ class _TestPicklingConnections(BaseTestCase): l = socket.socket() l.bind((test.support.HOST, 0)) - l.listen(1) + l.listen() conn.send(l.getsockname()) new_conn, addr = l.accept() conn.send(new_conn) @@ -3190,7 +3190,7 @@ class TestWait(unittest.TestCase): from multiprocessing.connection import wait l = socket.socket() l.bind((test.support.HOST, 0)) - l.listen(4) + l.listen() addr = l.getsockname() readers = [] procs = [] diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index f42b0f9..564ff52 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -3,6 +3,7 @@ See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases """ +import decimal import sys import pickle import random @@ -50,6 +51,17 @@ class TestModule(unittest.TestCase): self.assertEqual(datetime.MINYEAR, 1) self.assertEqual(datetime.MAXYEAR, 9999) + def test_name_cleanup(self): + if '_Fast' not in str(self): + return + datetime = datetime_module + names = set(name for name in dir(datetime) + if not name.startswith('__') and not name.endswith('__')) + allowed = set(['MAXYEAR', 'MINYEAR', 'date', 'datetime', + 'datetime_CAPI', 'time', 'timedelta', 'timezone', + 'tzinfo']) + self.assertEqual(names - allowed, set([])) + ############################################################################# # tzinfo tests @@ -616,8 +628,12 @@ class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase): # Single-field rounding. eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0 + eq(td(milliseconds=0.5/1000), td(microseconds=0)) + eq(td(milliseconds=-0.5/1000), td(microseconds=0)) eq(td(milliseconds=0.6/1000), td(microseconds=1)) eq(td(milliseconds=-0.6/1000), td(microseconds=-1)) + eq(td(seconds=0.5/10**6), td(microseconds=0)) + eq(td(seconds=-0.5/10**6), td(microseconds=0)) # Rounding due to contributions from more than one field. us_per_hour = 3600e6 @@ -1131,11 +1147,13 @@ class TestDate(HarmlessMixedComparison, unittest.TestCase): #check that this standard extension works t.strftime("%f") - def test_format(self): dt = self.theclass(2007, 9, 10) self.assertEqual(dt.__format__(''), str(dt)) + with self.assertRaisesRegex(TypeError, '^must be str, not int$'): + dt.__format__(123) + # check that a derived class's __str__() gets called class A(self.theclass): def __str__(self): @@ -1391,9 +1409,10 @@ class TestDate(HarmlessMixedComparison, unittest.TestCase): for month_byte in b'9', b'\0', b'\r', b'\xff': self.assertRaises(TypeError, self.theclass, base[:2] + month_byte + base[3:]) - # Good bytes, but bad tzinfo: - self.assertRaises(TypeError, self.theclass, - bytes([1] * len(base)), 'EST') + if issubclass(self.theclass, datetime): + # Good bytes, but bad tzinfo: + with self.assertRaisesRegex(TypeError, '^bad tzinfo state arg$'): + self.theclass(bytes([1] * len(base)), 'EST') for ord_byte in range(1, 13): # This shouldn't blow up because of the month byte alone. If @@ -1469,6 +1488,9 @@ class TestDateTime(TestDate): dt = self.theclass(2007, 9, 10, 4, 5, 1, 123) self.assertEqual(dt.__format__(''), str(dt)) + with self.assertRaisesRegex(TypeError, '^must be str, not int$'): + dt.__format__(123) + # check that a derived class's __str__() gets called class A(self.theclass): def __str__(self): @@ -1790,6 +1812,7 @@ class TestDateTime(TestDate): tzinfo=timezone(timedelta(hours=-5), 'EST')) self.assertEqual(t.timestamp(), 18000 + 3600 + 2*60 + 3 + 4*1e-6) + def test_microsecond_rounding(self): for fts in [self.theclass.fromtimestamp, self.theclass.utcfromtimestamp]: @@ -1840,6 +1863,7 @@ class TestDateTime(TestDate): for insane in -1e200, 1e200: self.assertRaises(OverflowError, self.theclass.utcfromtimestamp, insane) + @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps") def test_negative_float_fromtimestamp(self): # The result is tz-dependent; at least test that this doesn't @@ -2219,6 +2243,9 @@ class TestTime(HarmlessMixedComparison, unittest.TestCase): t = self.theclass(1, 2, 3, 4) self.assertEqual(t.__format__(''), str(t)) + with self.assertRaisesRegex(TypeError, '^must be str, not int$'): + t.__format__(123) + # check that a derived class's __str__() gets called class A(self.theclass): def __str__(self): @@ -2282,13 +2309,14 @@ class TestTime(HarmlessMixedComparison, unittest.TestCase): self.assertEqual(orig, derived) def test_bool(self): + # time is always True. cls = self.theclass self.assertTrue(cls(1)) self.assertTrue(cls(0, 1)) self.assertTrue(cls(0, 0, 1)) self.assertTrue(cls(0, 0, 0, 1)) - self.assertFalse(cls(0)) - self.assertFalse(cls()) + self.assertTrue(cls(0)) + self.assertTrue(cls()) def test_replace(self): cls = self.theclass @@ -2347,6 +2375,9 @@ class TestTime(HarmlessMixedComparison, unittest.TestCase): for hour_byte in ' ', '9', chr(24), '\xff': self.assertRaises(TypeError, self.theclass, hour_byte + base[1:]) + # Good bytes, but bad tzinfo: + with self.assertRaisesRegex(TypeError, '^bad tzinfo state arg$'): + self.theclass(bytes([1] * len(base)), 'EST') # A mixin for classes with a tzinfo= argument. Subclasses must define # theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever) @@ -2606,7 +2637,7 @@ class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase): self.assertRaises(TypeError, t.strftime, "%Z") # Issue #6697: - if '_Fast' in str(type(self)): + if '_Fast' in str(self): Badtzname.tz = '\ud800' self.assertRaises(ValueError, t.strftime, "%Z") @@ -2641,7 +2672,7 @@ class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase): self.assertEqual(derived.tzname(), 'cookie') def test_more_bool(self): - # Test cases with non-None tzinfo. + # time is always True. cls = self.theclass t = cls(0, tzinfo=FixedOffset(-300, "")) @@ -2651,23 +2682,11 @@ class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase): self.assertTrue(t) t = cls(5, tzinfo=FixedOffset(300, "")) - self.assertFalse(t) + self.assertTrue(t) t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, "")) - self.assertFalse(t) - - # Mostly ensuring this doesn't overflow internally. - t = cls(0, tzinfo=FixedOffset(23*60 + 59, "")) self.assertTrue(t) - # But this should yield a value error -- the utcoffset is bogus. - t = cls(0, tzinfo=FixedOffset(24*60, "")) - self.assertRaises(ValueError, lambda: bool(t)) - - # Likewise. - t = cls(0, tzinfo=FixedOffset(-24*60, "")) - self.assertRaises(ValueError, lambda: bool(t)) - def test_replace(self): cls = self.theclass z100 = FixedOffset(100, "+100") @@ -3780,6 +3799,60 @@ class Oddballs(unittest.TestCase): self.assertEqual(as_datetime, datetime_sc) self.assertEqual(datetime_sc, as_datetime) + def test_extra_attributes(self): + for x in [date.today(), + time(), + datetime.utcnow(), + timedelta(), + tzinfo(), + timezone(timedelta())]: + with self.assertRaises(AttributeError): + x.abc = 1 + + def test_check_arg_types(self): + class Number: + def __init__(self, value): + self.value = value + def __int__(self): + return self.value + + for xx in [decimal.Decimal(10), + decimal.Decimal('10.9'), + Number(10)]: + self.assertEqual(datetime(10, 10, 10, 10, 10, 10, 10), + datetime(xx, xx, xx, xx, xx, xx, xx)) + + with self.assertRaisesRegex(TypeError, '^an integer is required ' + '\(got type str\)$'): + datetime(10, 10, '10') + + f10 = Number(10.9) + with self.assertRaisesRegex(TypeError, '^__int__ returned non-int ' + '\(type float\)$'): + datetime(10, 10, f10) + + class Float(float): + pass + s10 = Float(10.9) + with self.assertRaisesRegex(TypeError, '^integer argument expected, ' + 'got float$'): + datetime(10, 10, s10) + + with self.assertRaises(TypeError): + datetime(10., 10, 10) + with self.assertRaises(TypeError): + datetime(10, 10., 10) + with self.assertRaises(TypeError): + datetime(10, 10, 10.) + with self.assertRaises(TypeError): + datetime(10, 10, 10, 10.) + with self.assertRaises(TypeError): + datetime(10, 10, 10, 10, 10.) + with self.assertRaises(TypeError): + datetime(10, 10, 10, 10, 10, 10.) + with self.assertRaises(TypeError): + datetime(10, 10, 10, 10, 10, 10, 10.) + def test_main(): support.run_unittest(__name__) diff --git a/Lib/test/fork_wait.py b/Lib/test/fork_wait.py index 19b54ec..713039d 100644 --- a/Lib/test/fork_wait.py +++ b/Lib/test/fork_wait.py @@ -48,7 +48,12 @@ class ForkWait(unittest.TestCase): for i in range(NUM_THREADS): _thread.start_new(self.f, (i,)) - time.sleep(LONGSLEEP) + # busy-loop to wait for threads + deadline = time.monotonic() + 10.0 + while len(self.alive) < NUM_THREADS: + time.sleep(0.1) + if deadline < time.monotonic(): + break a = sorted(self.alive.keys()) self.assertEqual(a, list(range(NUM_THREADS))) diff --git a/Lib/test/imghdrdata/python.exr b/Lib/test/imghdrdata/python.exr Binary files differnew file mode 100644 index 0000000..773c81e --- /dev/null +++ b/Lib/test/imghdrdata/python.exr diff --git a/Lib/test/imghdrdata/python.webp b/Lib/test/imghdrdata/python.webp Binary files differnew file mode 100644 index 0000000..e824ec7 --- /dev/null +++ b/Lib/test/imghdrdata/python.webp diff --git a/Lib/test/inspect_fodder2.py b/Lib/test/inspect_fodder2.py index bd7106f..e452235 100644 --- a/Lib/test/inspect_fodder2.py +++ b/Lib/test/inspect_fodder2.py @@ -109,3 +109,16 @@ def annotated(arg1: list): #line 109 def keyword_only_arg(*, arg): pass + +from functools import wraps + +def decorator(func): + @wraps(func) + def fake(): + return 42 + return fake + +#line 121 +@decorator +def real(): + return 20 diff --git a/Lib/test/list_tests.py b/Lib/test/list_tests.py index 42e118b..9069337 100644 --- a/Lib/test/list_tests.py +++ b/Lib/test/list_tests.py @@ -30,6 +30,12 @@ class CommonTest(seq_tests.CommonTest): self.assertNotEqual(id(a), id(b)) self.assertEqual(a, b) + def test_getitem_error(self): + msg = "list indices must be integers or slices" + with self.assertRaisesRegex(TypeError, msg): + a = [] + a['a'] = "python" + def test_repr(self): l0 = [] l2 = [0, 1, 2] @@ -120,6 +126,10 @@ class CommonTest(seq_tests.CommonTest): a[-1] = 9 self.assertEqual(a, self.type2test([5,6,7,8,9])) + msg = "list indices must be integers or slices" + with self.assertRaisesRegex(TypeError, msg): + a['a'] = "python" + def test_delitem(self): a = self.type2test([0, 1]) del a[1] diff --git a/Lib/test/lock_tests.py b/Lib/test/lock_tests.py index 1cbcea2..136f1c3 100644 --- a/Lib/test/lock_tests.py +++ b/Lib/test/lock_tests.py @@ -82,7 +82,13 @@ class BaseLockTests(BaseTestCase): def test_repr(self): lock = self.locktype() - repr(lock) + self.assertRegex(repr(lock), "<unlocked .* object (.*)?at .*>") + del lock + + def test_locked_repr(self): + lock = self.locktype() + lock.acquire() + self.assertRegex(repr(lock), "<locked .* object (.*)?at .*>") del lock def test_acquire_destroy(self): diff --git a/Lib/test/mock_socket.py b/Lib/test/mock_socket.py index e36724f..b28c473 100644 --- a/Lib/test/mock_socket.py +++ b/Lib/test/mock_socket.py @@ -35,8 +35,9 @@ class MockFile: class MockSocket: """Mock socket object used by smtpd and smtplib tests. """ - def __init__(self): + def __init__(self, family=None): global _reply_data + self.family = family self.output = [] self.lines = [] if _reply_data: @@ -101,15 +102,14 @@ class MockSocket: return len(data) def getpeername(self): - return 'peer' + return ('peer-address', 'peer-port') def close(self): pass def socket(family=None, type=None, proto=None): - return MockSocket() - + return MockSocket(family) def create_connection(address, timeout=socket_module._GLOBAL_DEFAULT_TIMEOUT, source_address=None): @@ -144,13 +144,16 @@ def gethostname(): def gethostbyname(name): return "" +def getaddrinfo(*args, **kw): + return socket_module.getaddrinfo(*args, **kw) gaierror = socket_module.gaierror error = socket_module.error # Constants -AF_INET = None -SOCK_STREAM = None +AF_INET = socket_module.AF_INET +AF_INET6 = socket_module.AF_INET6 +SOCK_STREAM = socket_module.SOCK_STREAM SOL_SOCKET = None SO_REUSEADDR = None diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py index 5963175..444a103 100644 --- a/Lib/test/pickletester.py +++ b/Lib/test/pickletester.py @@ -1153,16 +1153,62 @@ class AbstractPickleTests(unittest.TestCase): self.assertGreaterEqual(num_additems, 2) def test_simple_newobj(self): - x = object.__new__(SimpleNewObj) # avoid __init__ + x = SimpleNewObj.__new__(SimpleNewObj, 0xface) # avoid __init__ x.abc = 666 for proto in protocols: - s = self.dumps(x, proto) - self.assertEqual(opcode_in_pickle(pickle.NEWOBJ, s), - 2 <= proto < 4) - self.assertEqual(opcode_in_pickle(pickle.NEWOBJ_EX, s), - proto >= 4) - y = self.loads(s) # will raise TypeError if __init__ called - self.assert_is_copy(x, y) + with self.subTest(proto=proto): + s = self.dumps(x, proto) + if proto < 1: + self.assertIn(b'\nL64206', s) # LONG + else: + self.assertIn(b'M\xce\xfa', s) # BININT2 + self.assertEqual(opcode_in_pickle(pickle.NEWOBJ, s), + 2 <= proto) + self.assertFalse(opcode_in_pickle(pickle.NEWOBJ_EX, s)) + y = self.loads(s) # will raise TypeError if __init__ called + self.assert_is_copy(x, y) + + def test_complex_newobj(self): + x = ComplexNewObj.__new__(ComplexNewObj, 0xface) # avoid __init__ + x.abc = 666 + for proto in protocols: + with self.subTest(proto=proto): + s = self.dumps(x, proto) + if proto < 1: + self.assertIn(b'\nL64206', s) # LONG + elif proto < 2: + self.assertIn(b'M\xce\xfa', s) # BININT2 + elif proto < 4: + self.assertIn(b'X\x04\x00\x00\x00FACE', s) # BINUNICODE + else: + self.assertIn(b'\x8c\x04FACE', s) # SHORT_BINUNICODE + self.assertEqual(opcode_in_pickle(pickle.NEWOBJ, s), + 2 <= proto) + self.assertFalse(opcode_in_pickle(pickle.NEWOBJ_EX, s)) + y = self.loads(s) # will raise TypeError if __init__ called + self.assert_is_copy(x, y) + + def test_complex_newobj_ex(self): + x = ComplexNewObjEx.__new__(ComplexNewObjEx, 0xface) # avoid __init__ + x.abc = 666 + for proto in protocols: + with self.subTest(proto=proto): + if 2 <= proto < 4: + self.assertRaises(ValueError, self.dumps, x, proto) + continue + s = self.dumps(x, proto) + if proto < 1: + self.assertIn(b'\nL64206', s) # LONG + elif proto < 2: + self.assertIn(b'M\xce\xfa', s) # BININT2 + else: + assert proto >= 4 + self.assertIn(b'\x8c\x04FACE', s) # SHORT_BINUNICODE + self.assertFalse(opcode_in_pickle(pickle.NEWOBJ, s)) + self.assertEqual(opcode_in_pickle(pickle.NEWOBJ_EX, s), + 4 <= proto) + y = self.loads(s) # will raise TypeError if __init__ called + self.assert_is_copy(x, y) def test_newobj_list_slots(self): x = SlotList([1, 2, 3]) @@ -1636,6 +1682,27 @@ class AbstractPickleTests(unittest.TestCase): unpickled = self.loads(self.dumps(method, proto)) self.assertEqual(method(*args), unpickled(*args)) + def test_local_lookup_error(self): + # Test that whichmodule() errors out cleanly when looking up + # an assumed globally-reachable object fails. + def f(): + pass + # Since the function is local, lookup will fail + for proto in range(0, pickle.HIGHEST_PROTOCOL + 1): + with self.assertRaises((AttributeError, pickle.PicklingError)): + pickletools.dis(self.dumps(f, proto)) + # Same without a __module__ attribute (exercises a different path + # in _pickle.c). + del f.__module__ + for proto in range(0, pickle.HIGHEST_PROTOCOL + 1): + with self.assertRaises((AttributeError, pickle.PicklingError)): + pickletools.dis(self.dumps(f, proto)) + # Yet a different path. + f.__name__ = f.__qualname__ + for proto in range(0, pickle.HIGHEST_PROTOCOL + 1): + with self.assertRaises((AttributeError, pickle.PicklingError)): + pickletools.dis(self.dumps(f, proto)) + class BigmemPickleTests(unittest.TestCase): @@ -1870,12 +1937,20 @@ myclasses = [MyInt, MyFloat, class SlotList(MyList): __slots__ = ["foo"] -class SimpleNewObj(object): - def __init__(self, a, b, c): +class SimpleNewObj(int): + def __init__(self, *args, **kwargs): # raise an error, to make sure this isn't called raise TypeError("SimpleNewObj.__init__() didn't expect to get called") def __eq__(self, other): - return self.__dict__ == other.__dict__ + return int(self) == int(other) and self.__dict__ == other.__dict__ + +class ComplexNewObj(SimpleNewObj): + def __getnewargs__(self): + return ('%X' % self, 16) + +class ComplexNewObjEx(SimpleNewObj): + def __getnewargs_ex__(self): + return ('%X' % self,), {'base': 16} class BadGetattr: def __getattr__(self, key): diff --git a/Lib/test/pystone.py b/Lib/test/pystone.py index a41f1e5..1f67e66 100755 --- a/Lib/test/pystone.py +++ b/Lib/test/pystone.py @@ -41,7 +41,7 @@ Version History: LOOPS = 50000 -from time import clock +from time import time __version__ = "1.2" @@ -93,10 +93,10 @@ def Proc0(loops=LOOPS): global PtrGlb global PtrGlbNext - starttime = clock() + starttime = time() for i in range(loops): pass - nulltime = clock() - starttime + nulltime = time() - starttime PtrGlbNext = Record() PtrGlb = Record() @@ -108,7 +108,7 @@ def Proc0(loops=LOOPS): String1Loc = "DHRYSTONE PROGRAM, 1'ST STRING" Array2Glob[8][7] = 10 - starttime = clock() + starttime = time() for i in range(loops): Proc5() @@ -134,7 +134,7 @@ def Proc0(loops=LOOPS): IntLoc2 = 7 * (IntLoc3 - IntLoc2) - IntLoc1 IntLoc1 = Proc2(IntLoc1) - benchtime = clock() - starttime - nulltime + benchtime = time() - starttime - nulltime if benchtime == 0.0: loopsPerBenchtime = 0.0 else: diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py index b633631..ea1287d 100755 --- a/Lib/test/regrtest.py +++ b/Lib/test/regrtest.py @@ -1276,6 +1276,10 @@ def runtest_inner(test, verbose, quiet, def test_runner(): loader = unittest.TestLoader() tests = loader.loadTestsFromModule(the_module) + for error in loader.errors: + print(error, file=sys.stderr) + if loader.errors: + raise Exception("errors while loading tests") support.run_unittest(tests) test_runner() if huntrleaks: diff --git a/Lib/test/ssl_servers.py b/Lib/test/ssl_servers.py index 759b3f4..f9d30cf 100644 --- a/Lib/test/ssl_servers.py +++ b/Lib/test/ssl_servers.py @@ -150,7 +150,7 @@ class HTTPSServerThread(threading.Thread): def make_https_server(case, *, context=None, certfile=CERTFILE, host=HOST, handler_class=None): if context is None: - context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) # We assume the certfile contains both private key and certificate context.load_cert_chain(certfile) server = HTTPSServerThread(context, host, handler_class) @@ -182,6 +182,8 @@ if __name__ == "__main__": parser.add_argument('--curve-name', dest='curve_name', type=str, action='store', help='curve name for EC-based Diffie-Hellman') + parser.add_argument('--ciphers', dest='ciphers', type=str, + help='allowed cipher list') parser.add_argument('--dh', dest='dh_file', type=str, action='store', help='PEM file containing DH parameters') args = parser.parse_args() @@ -192,12 +194,14 @@ if __name__ == "__main__": else: handler_class = RootedHTTPRequestHandler handler_class.root = os.getcwd() - context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) context.load_cert_chain(CERTFILE) if args.curve_name: context.set_ecdh_curve(args.curve_name) if args.dh_file: context.load_dh_params(args.dh_file) + if args.ciphers: + context.set_ciphers(args.ciphers) server = HTTPSServer(("", args.port), handler_class, context) if args.verbose: diff --git a/Lib/test/string_tests.py b/Lib/test/string_tests.py index 242a931..6e26474 100644 --- a/Lib/test/string_tests.py +++ b/Lib/test/string_tests.py @@ -1176,8 +1176,7 @@ class MixinStrUnicodeUserStringTest: self.checkraises(TypeError, 'abc', '__mod__') self.checkraises(TypeError, '%(foo)s', '__mod__', 42) self.checkraises(TypeError, '%s%s', '__mod__', (42,)) - with self.assertWarns(DeprecationWarning): - self.checkraises(TypeError, '%c', '__mod__', (None,)) + self.checkraises(TypeError, '%c', '__mod__', (None,)) self.checkraises(ValueError, '%(foo', '__mod__', {}) self.checkraises(TypeError, '%(foo)s %(bar)s', '__mod__', ('foo', 42)) self.checkraises(TypeError, '%d', '__mod__', "42") # not numeric diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index d98068c..205c47c 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -1040,7 +1040,8 @@ def open_urlresource(url, *args, **kw): # Verify the requirement before downloading the file requires('urlfetch') - print('\tfetching %s ...' % url, file=get_original_stdout()) + if verbose: + print('\tfetching %s ...' % url, file=get_original_stdout()) opener = urllib.request.build_opener() if gzip: opener.addheaders.append(('Accept-Encoding', 'gzip')) diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 1164f3f..d7e6cda 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -20,15 +20,6 @@ class StdIOBuffer(StringIO): class TestCase(unittest.TestCase): - def assertEqual(self, obj1, obj2): - if obj1 != obj2: - print('') - print(repr(obj1)) - print(repr(obj2)) - print(obj1) - print(obj2) - super(TestCase, self).assertEqual(obj1, obj2) - def setUp(self): # The tests assume that line wrapping occurs at 80 columns, but this # behaviour can be overridden by setting the COLUMNS environment @@ -1993,14 +1984,9 @@ class TestAddSubparsers(TestCase): ''')) def _test_subparser_help(self, args_str, expected_help): - try: + with self.assertRaises(ArgumentParserError) as cm: self.parser.parse_args(args_str.split()) - except ArgumentParserError: - err = sys.exc_info()[1] - if err.stdout != expected_help: - print(repr(expected_help)) - print(repr(err.stdout)) - self.assertEqual(err.stdout, expected_help) + self.assertEqual(expected_help, cm.exception.stdout) def test_subparser1_help(self): self._test_subparser_help('5.0 1 -h', textwrap.dedent('''\ @@ -2846,15 +2832,15 @@ class TestGetDefault(TestCase): def test_get_default(self): parser = ErrorRaisingArgumentParser() - self.assertEqual(None, parser.get_default("foo")) - self.assertEqual(None, parser.get_default("bar")) + self.assertIsNone(parser.get_default("foo")) + self.assertIsNone(parser.get_default("bar")) parser.add_argument("--foo") - self.assertEqual(None, parser.get_default("foo")) - self.assertEqual(None, parser.get_default("bar")) + self.assertIsNone(parser.get_default("foo")) + self.assertIsNone(parser.get_default("bar")) parser.add_argument("--bar", type=int, default=42) - self.assertEqual(None, parser.get_default("foo")) + self.assertIsNone(parser.get_default("foo")) self.assertEqual(42, parser.get_default("bar")) parser.set_defaults(foo="badger") @@ -2869,18 +2855,16 @@ class TestNamespaceContainsSimple(TestCase): def test_empty(self): ns = argparse.Namespace() - self.assertEqual('' in ns, False) - self.assertEqual('' not in ns, True) - self.assertEqual('x' in ns, False) + self.assertNotIn('', ns) + self.assertNotIn('x', ns) def test_non_empty(self): ns = argparse.Namespace(x=1, y=2) - self.assertEqual('x' in ns, True) - self.assertEqual('x' not in ns, False) - self.assertEqual('y' in ns, True) - self.assertEqual('' in ns, False) - self.assertEqual('xx' in ns, False) - self.assertEqual('z' in ns, False) + self.assertNotIn('', ns) + self.assertIn('x', ns) + self.assertIn('y', ns) + self.assertNotIn('xx', ns) + self.assertNotIn('z', ns) # ===================== # Help formatting tests @@ -2936,13 +2920,6 @@ class TestHelpFormattingMetaclass(type): def _test(self, tester, parser_text): expected_text = getattr(tester, self.func_suffix) expected_text = textwrap.dedent(expected_text) - if expected_text != parser_text: - print(repr(expected_text)) - print(repr(parser_text)) - for char1, char2 in zip(expected_text, parser_text): - if char1 != char2: - print('first diff: %r %r' % (char1, char2)) - break tester.assertEqual(expected_text, parser_text) def test_format(self, tester): @@ -4223,24 +4200,17 @@ class TestInvalidArgumentConstructors(TestCase): self.assertValueError('foo', action='baz') self.assertValueError('--foo', action=('store', 'append')) parser = argparse.ArgumentParser() - try: + with self.assertRaises(ValueError) as cm: parser.add_argument("--foo", action="store-true") - except ValueError: - e = sys.exc_info()[1] - expected = 'unknown action' - msg = 'expected %r, found %r' % (expected, e) - self.assertTrue(expected in str(e), msg) + self.assertIn('unknown action', str(cm.exception)) def test_multiple_dest(self): parser = argparse.ArgumentParser() parser.add_argument(dest='foo') - try: + with self.assertRaises(ValueError) as cm: parser.add_argument('bar', dest='baz') - except ValueError: - e = sys.exc_info()[1] - expected = 'dest supplied twice for positional argument' - msg = 'expected %r, found %r' % (expected, e) - self.assertTrue(expected in str(e), msg) + self.assertIn('dest supplied twice for positional argument', + str(cm.exception)) def test_no_argument_actions(self): for action in ['store_const', 'store_true', 'store_false', @@ -4397,18 +4367,10 @@ class TestConflictHandling(TestCase): class TestOptionalsHelpVersionActions(TestCase): """Test the help and version actions""" - def _get_error(self, func, *args, **kwargs): - try: - func(*args, **kwargs) - except ArgumentParserError: - return sys.exc_info()[1] - else: - self.assertRaises(ArgumentParserError, func, *args, **kwargs) - def assertPrintHelpExit(self, parser, args_str): - self.assertEqual( - parser.format_help(), - self._get_error(parser.parse_args, args_str.split()).stdout) + with self.assertRaises(ArgumentParserError) as cm: + parser.parse_args(args_str.split()) + self.assertEqual(parser.format_help(), cm.exception.stdout) def assertArgumentParserError(self, parser, *args): self.assertRaises(ArgumentParserError, parser.parse_args, args) @@ -4423,8 +4385,9 @@ class TestOptionalsHelpVersionActions(TestCase): def test_version_format(self): parser = ErrorRaisingArgumentParser(prog='PPP') parser.add_argument('-v', '--version', action='version', version='%(prog)s 3.5') - msg = self._get_error(parser.parse_args, ['-v']).stdout - self.assertEqual('PPP 3.5\n', msg) + with self.assertRaises(ArgumentParserError) as cm: + parser.parse_args(['-v']) + self.assertEqual('PPP 3.5\n', cm.exception.stdout) def test_version_no_help(self): parser = ErrorRaisingArgumentParser(add_help=False) @@ -4436,8 +4399,9 @@ class TestOptionalsHelpVersionActions(TestCase): def test_version_action(self): parser = ErrorRaisingArgumentParser(prog='XXX') parser.add_argument('-V', action='version', version='%(prog)s 3.7') - msg = self._get_error(parser.parse_args, ['-V']).stdout - self.assertEqual('XXX 3.7\n', msg) + with self.assertRaises(ArgumentParserError) as cm: + parser.parse_args(['-V']) + self.assertEqual('XXX 3.7\n', cm.exception.stdout) def test_no_help(self): parser = ErrorRaisingArgumentParser(add_help=False) @@ -4607,14 +4571,10 @@ class TestArgumentTypeError(TestCase): parser = ErrorRaisingArgumentParser(prog='PROG', add_help=False) parser.add_argument('x', type=spam) - try: + with self.assertRaises(ArgumentParserError) as cm: parser.parse_args(['XXX']) - except ArgumentParserError: - expected = 'usage: PROG x\nPROG: error: argument x: spam!\n' - msg = sys.exc_info()[1].stderr - self.assertEqual(expected, msg) - else: - self.fail() + self.assertEqual('usage: PROG x\nPROG: error: argument x: spam!\n', + cm.exception.stderr) # ========================= # MessageContentError tests diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index 07c9bf9..adb0fd3 100644 --- a/Lib/test/test_array.py +++ b/Lib/test/test_array.py @@ -394,7 +394,9 @@ class BaseTest: self.assertEqual(a, b) def test_tofromstring(self): - nb_warnings = 4 + # Warnings not raised when arguments are incorrect as Argument Clinic + # handles that before the warning can be raised. + nb_warnings = 2 with warnings.catch_warnings(record=True) as r: warnings.filterwarnings("always", message=r"(to|from)string\(\) is deprecated", diff --git a/Lib/test/test_asdl_parser.py b/Lib/test/test_asdl_parser.py new file mode 100644 index 0000000..7a6426a --- /dev/null +++ b/Lib/test/test_asdl_parser.py @@ -0,0 +1,122 @@ +"""Tests for the asdl parser in Parser/asdl.py""" + +import importlib.machinery +import os +from os.path import dirname +import sys +import sysconfig +import unittest + + +# This test is only relevant for from-source builds of Python. +if not sysconfig.is_python_build(): + raise unittest.SkipTest('test irrelevant for an installed Python') + +src_base = dirname(dirname(dirname(__file__))) +parser_dir = os.path.join(src_base, 'Parser') + + +class TestAsdlParser(unittest.TestCase): + @classmethod + def setUpClass(cls): + # Loads the asdl module dynamically, since it's not in a real importable + # package. + # Parses Python.asdl into a ast.Module and run the check on it. + # There's no need to do this for each test method, hence setUpClass. + sys.path.insert(0, parser_dir) + loader = importlib.machinery.SourceFileLoader( + 'asdl', os.path.join(parser_dir, 'asdl.py')) + cls.asdl = loader.load_module() + cls.mod = cls.asdl.parse(os.path.join(parser_dir, 'Python.asdl')) + cls.assertTrue(cls.asdl.check(cls.mod), 'Module validation failed') + + @classmethod + def tearDownClass(cls): + del sys.path[0] + + def setUp(self): + # alias stuff from the class, for convenience + self.asdl = TestAsdlParser.asdl + self.mod = TestAsdlParser.mod + self.types = self.mod.types + + def test_module(self): + self.assertEqual(self.mod.name, 'Python') + self.assertIn('stmt', self.types) + self.assertIn('expr', self.types) + self.assertIn('mod', self.types) + + def test_definitions(self): + defs = self.mod.dfns + self.assertIsInstance(defs[0], self.asdl.Type) + self.assertIsInstance(defs[0].value, self.asdl.Sum) + + self.assertIsInstance(self.types['withitem'], self.asdl.Product) + self.assertIsInstance(self.types['alias'], self.asdl.Product) + + def test_product(self): + alias = self.types['alias'] + self.assertEqual( + str(alias), + 'Product([Field(identifier, name), Field(identifier, asname, opt=True)])') + + def test_attributes(self): + stmt = self.types['stmt'] + self.assertEqual(len(stmt.attributes), 2) + self.assertEqual(str(stmt.attributes[0]), 'Field(int, lineno)') + self.assertEqual(str(stmt.attributes[1]), 'Field(int, col_offset)') + + def test_constructor_fields(self): + ehandler = self.types['excepthandler'] + self.assertEqual(len(ehandler.types), 1) + self.assertEqual(len(ehandler.attributes), 2) + + cons = ehandler.types[0] + self.assertIsInstance(cons, self.asdl.Constructor) + self.assertEqual(len(cons.fields), 3) + + f0 = cons.fields[0] + self.assertEqual(f0.type, 'expr') + self.assertEqual(f0.name, 'type') + self.assertTrue(f0.opt) + + f1 = cons.fields[1] + self.assertEqual(f1.type, 'identifier') + self.assertEqual(f1.name, 'name') + self.assertTrue(f1.opt) + + f2 = cons.fields[2] + self.assertEqual(f2.type, 'stmt') + self.assertEqual(f2.name, 'body') + self.assertFalse(f2.opt) + self.assertTrue(f2.seq) + + def test_visitor(self): + class CustomVisitor(self.asdl.VisitorBase): + def __init__(self): + super().__init__() + self.names_with_seq = [] + + def visitModule(self, mod): + for dfn in mod.dfns: + self.visit(dfn) + + def visitType(self, type): + self.visit(type.value) + + def visitSum(self, sum): + for t in sum.types: + self.visit(t) + + def visitConstructor(self, cons): + for f in cons.fields: + if f.seq: + self.names_with_seq.append(cons.name) + + v = CustomVisitor() + v.visit(self.types['mod']) + self.assertEqual(v.names_with_seq, ['Module', 'Interactive', 'Suite']) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_asynchat.py b/Lib/test/test_asynchat.py index 2dc9d0c..3a33fc8 100644 --- a/Lib/test/test_asynchat.py +++ b/Lib/test/test_asynchat.py @@ -12,6 +12,7 @@ import socket import sys import time import unittest +import warnings import unittest.mock try: import threading @@ -38,7 +39,7 @@ if threading: self.start_resend_event = None def run(self): - self.sock.listen(1) + self.sock.listen() self.event.set() conn, client = self.sock.accept() self.buffer = b"" @@ -298,7 +299,10 @@ class TestHelperFunctions(unittest.TestCase): class TestFifo(unittest.TestCase): def test_basic(self): - f = asynchat.fifo() + with self.assertWarns(DeprecationWarning) as cm: + f = asynchat.fifo() + self.assertEqual(str(cm.warning), + "fifo class will be removed in Python 3.6") f.push(7) f.push(b'a') self.assertEqual(len(f), 2) @@ -313,7 +317,10 @@ class TestFifo(unittest.TestCase): self.assertEqual(f.pop(), (0, None)) def test_given_list(self): - f = asynchat.fifo([b'x', 17, 3]) + with self.assertWarns(DeprecationWarning) as cm: + f = asynchat.fifo([b'x', 17, 3]) + self.assertEqual(str(cm.warning), + "fifo class will be removed in Python 3.6") self.assertEqual(len(f), 3) self.assertEqual(f.pop(), (1, b'x')) self.assertEqual(f.pop(), (1, 17)) diff --git a/Lib/test/test_asyncore.py b/Lib/test/test_asyncore.py index d44726d..3857916 100644 --- a/Lib/test/test_asyncore.py +++ b/Lib/test/test_asyncore.py @@ -7,7 +7,6 @@ import sys import time import errno import struct -import warnings from test import support from io import BytesIO @@ -65,7 +64,7 @@ class crashingdummy: # used when testing senders; just collects what it gets until newline is sent def capture_server(evt, buf, serv): try: - serv.listen(5) + serv.listen() conn, addr = serv.accept() except socket.timeout: pass @@ -298,23 +297,6 @@ class DispatcherTests(unittest.TestCase): 'warning: unhandled connect event'] self.assertEqual(lines, expected) - def test_issue_8594(self): - # XXX - this test is supposed to be removed in next major Python - # version - d = asyncore.dispatcher(socket.socket()) - # make sure the error message no longer refers to the socket - # object but the dispatcher instance instead - self.assertRaisesRegex(AttributeError, 'dispatcher instance', - getattr, d, 'foo') - # cheap inheritance with the underlying socket is supposed - # to still work but a DeprecationWarning is expected - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - family = d.family - self.assertEqual(family, socket.AF_INET) - self.assertEqual(len(w), 1) - self.assertTrue(issubclass(w[0].category, DeprecationWarning)) - def test_strerror(self): # refers to bug #8573 err = asyncore._strerror(errno.EPERM) @@ -331,9 +313,8 @@ class dispatcherwithsend_noread(asyncore.dispatcher_with_send): def handle_connect(self): pass -class DispatcherWithSendTests(unittest.TestCase): - usepoll = False +class DispatcherWithSendTests(unittest.TestCase): def setUp(self): pass @@ -383,10 +364,6 @@ class DispatcherWithSendTests(unittest.TestCase): self.fail("join() timed out") - -class DispatcherWithSendTests_UsePoll(DispatcherWithSendTests): - usepoll = True - @unittest.skipUnless(hasattr(asyncore, 'file_wrapper'), 'asyncore.file_wrapper required') class FileWrapperTest(unittest.TestCase): diff --git a/Lib/test/test_augassign.py b/Lib/test/test_augassign.py index 9a59c58..19b7687 100644 --- a/Lib/test/test_augassign.py +++ b/Lib/test/test_augassign.py @@ -136,6 +136,14 @@ class AugAssignTest(unittest.TestCase): output.append("__imul__ called") return self + def __matmul__(self, val): + output.append("__matmul__ called") + def __rmatmul__(self, val): + output.append("__rmatmul__ called") + def __imatmul__(self, val): + output.append("__imatmul__ called") + return self + def __div__(self, val): output.append("__div__ called") def __rdiv__(self, val): @@ -233,6 +241,10 @@ class AugAssignTest(unittest.TestCase): 1 * x x *= 1 + x @ 1 + 1 @ x + x @= 1 + x / 1 1 / x x /= 1 @@ -279,6 +291,9 @@ __isub__ called __mul__ called __rmul__ called __imul__ called +__matmul__ called +__rmatmul__ called +__imatmul__ called __truediv__ called __rtruediv__ called __itruediv__ called diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py index 1667847..1cdc771 100644 --- a/Lib/test/test_buffer.py +++ b/Lib/test/test_buffer.py @@ -216,7 +216,7 @@ def iter_format(nitems, testobj='ndarray'): for t in iter_mode(nitems, testobj): yield t if testobj != 'ndarray': - raise StopIteration + return yield struct_items(nitems, testobj) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 14366c6..6166da5 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -312,11 +312,11 @@ class BuiltinTest(unittest.TestCase): self.assertRaises(TypeError, compile) self.assertRaises(ValueError, compile, 'print(42)\n', '<string>', 'badmode') self.assertRaises(ValueError, compile, 'print(42)\n', '<string>', 'single', 0xff) - self.assertRaises(TypeError, compile, chr(0), 'f', 'exec') + self.assertRaises(ValueError, compile, chr(0), 'f', 'exec') self.assertRaises(TypeError, compile, 'pass', '?', 'exec', mode='eval', source='0', filename='tmp') compile('print("\xe5")\n', '', 'exec') - self.assertRaises(TypeError, compile, chr(0), 'f', 'exec') + self.assertRaises(ValueError, compile, chr(0), 'f', 'exec') self.assertRaises(ValueError, compile, str('a = 1'), 'f', 'bad') # test the optimize argument @@ -1094,7 +1094,7 @@ class BuiltinTest(unittest.TestCase): self.assertAlmostEqual(pow(-1, 0.5), 1j) self.assertAlmostEqual(pow(-1, 1/3), 0.5 + 0.8660254037844386j) - self.assertRaises(TypeError, pow, -1, -2, 3) + self.assertRaises(ValueError, pow, -1, -2, 3) self.assertRaises(ValueError, pow, 1, 2, 0) self.assertRaises(TypeError, pow) diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index e15807e..e8dde5d 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -722,6 +722,11 @@ class BaseBytesTest: class BytesTest(BaseBytesTest, unittest.TestCase): type2test = bytes + def test_getitem_error(self): + msg = "byte indices must be integers or slices" + with self.assertRaisesRegex(TypeError, msg): + b'python'['a'] + def test_buffer_is_readonly(self): fd = os.open(__file__, os.O_RDONLY) with open(fd, "rb", buffering=0) as f: @@ -776,6 +781,17 @@ class BytesTest(BaseBytesTest, unittest.TestCase): class ByteArrayTest(BaseBytesTest, unittest.TestCase): type2test = bytearray + def test_getitem_error(self): + msg = "bytearray indices must be integers or slices" + with self.assertRaisesRegex(TypeError, msg): + bytearray(b'python')['a'] + + def test_setitem_error(self): + msg = "bytearray indices must be integers or slices" + with self.assertRaisesRegex(TypeError, msg): + b = bytearray(b'python') + b['a'] = "python" + def test_nohash(self): self.assertRaises(TypeError, hash, bytearray()) diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index ba7c38d..de8d65a 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -150,6 +150,23 @@ class CAPITest(unittest.TestCase): self.assertEqual(_testcapi.docstring_with_signature_and_extra_newlines.__text_signature__, "($module, /, parameter)") + def test_c_type_with_matrix_multiplication(self): + M = _testcapi.matmulType + m1 = M() + m2 = M() + self.assertEqual(m1 @ m2, ("matmul", m1, m2)) + self.assertEqual(m1 @ 42, ("matmul", m1, 42)) + self.assertEqual(42 @ m1, ("matmul", 42, m1)) + o = m1 + o @= m2 + self.assertEqual(o, ("imatmul", m1, m2)) + o = m1 + o @= 42 + self.assertEqual(o, ("imatmul", m1, 42)) + o = 42 + o @= m1 + self.assertEqual(o, ("matmul", 42, m1)) + @unittest.skipUnless(threading, 'Threading required for this test.') class TestPendingCalls(unittest.TestCase): @@ -268,7 +285,7 @@ class EmbeddingTests(unittest.TestCase): exename += ext exepath = os.path.dirname(sys.executable) else: - exepath = os.path.join(basepath, "Modules") + exepath = os.path.join(basepath, "Programs") self.test_exe = exe = os.path.join(exepath, exename) if not os.path.exists(exe): self.skipTest("%r doesn't exist" % exe) @@ -287,12 +304,13 @@ class EmbeddingTests(unittest.TestCase): cmd.extend(args) p = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + stderr=subprocess.PIPE, + universal_newlines=True) (out, err) = p.communicate() self.assertEqual(p.returncode, 0, "bad returncode %d, stderr is %r" % (p.returncode, err)) - return out.decode("latin1"), err.decode("latin1") + return out, err def test_subinterps(self): # This is just a "don't crash" test @@ -319,34 +337,38 @@ class EmbeddingTests(unittest.TestCase): print() print(out) print(err) + expected_errors = sys.__stdout__.errors expected_stdin_encoding = sys.__stdin__.encoding expected_pipe_encoding = self._get_default_pipe_encoding() - expected_output = os.linesep.join([ + expected_output = '\n'.join([ "--- Use defaults ---", "Expected encoding: default", "Expected errors: default", - "stdin: {0}:strict", - "stdout: {1}:strict", - "stderr: {1}:backslashreplace", + "stdin: {in_encoding}:{errors}", + "stdout: {out_encoding}:{errors}", + "stderr: {out_encoding}:backslashreplace", "--- Set errors only ---", "Expected encoding: default", - "Expected errors: surrogateescape", - "stdin: {0}:surrogateescape", - "stdout: {1}:surrogateescape", - "stderr: {1}:backslashreplace", + "Expected errors: ignore", + "stdin: {in_encoding}:ignore", + "stdout: {out_encoding}:ignore", + "stderr: {out_encoding}:backslashreplace", "--- Set encoding only ---", "Expected encoding: latin-1", "Expected errors: default", - "stdin: latin-1:strict", - "stdout: latin-1:strict", + "stdin: latin-1:{errors}", + "stdout: latin-1:{errors}", "stderr: latin-1:backslashreplace", "--- Set encoding and errors ---", "Expected encoding: latin-1", - "Expected errors: surrogateescape", - "stdin: latin-1:surrogateescape", - "stdout: latin-1:surrogateescape", - "stderr: latin-1:backslashreplace"]).format(expected_stdin_encoding, - expected_pipe_encoding) + "Expected errors: replace", + "stdin: latin-1:replace", + "stdout: latin-1:replace", + "stderr: latin-1:backslashreplace"]) + expected_output = expected_output.format( + in_encoding=expected_stdin_encoding, + out_encoding=expected_pipe_encoding, + errors=expected_errors) # This is useful if we ever trip over odd platform behaviour self.maxDiff = None self.assertEqual(out.strip(), expected_output) diff --git a/Lib/test/test_code_module.py b/Lib/test/test_code_module.py index 7a80a80..9b17767 100644 --- a/Lib/test/test_code_module.py +++ b/Lib/test/test_code_module.py @@ -1,6 +1,7 @@ "Test InteractiveConsole and InteractiveInterpreter from code module" import sys import unittest +from textwrap import dedent from contextlib import ExitStack from unittest import mock from test import support @@ -78,6 +79,40 @@ class TestInteractiveConsole(unittest.TestCase): self.console.interact(banner='') self.assertEqual(len(self.stderr.method_calls), 1) + def test_cause_tb(self): + self.infunc.side_effect = ["raise ValueError('') from AttributeError", + EOFError('Finished')] + self.console.interact() + output = ''.join(''.join(call[1]) for call in self.stderr.method_calls) + expected = dedent(""" + AttributeError + + The above exception was the direct cause of the following exception: + + Traceback (most recent call last): + File "<console>", line 1, in <module> + ValueError + """) + self.assertIn(expected, output) + + def test_context_tb(self): + self.infunc.side_effect = ["try: ham\nexcept: eggs\n", + EOFError('Finished')] + self.console.interact() + output = ''.join(''.join(call[1]) for call in self.stderr.method_calls) + expected = dedent(""" + Traceback (most recent call last): + File "<console>", line 1, in <module> + NameError: name 'ham' is not defined + + During handling of the above exception, another exception occurred: + + Traceback (most recent call last): + File "<console>", line 2, in <module> + NameError: name 'eggs' is not defined + """) + self.assertIn(expected, output) + def test_main(): support.run_unittest(TestInteractiveConsole) diff --git a/Lib/test/test_codeccallbacks.py b/Lib/test/test_codeccallbacks.py index 84804bb..9743791 100644 --- a/Lib/test/test_codeccallbacks.py +++ b/Lib/test/test_codeccallbacks.py @@ -158,6 +158,22 @@ class CodecCallbackTest(unittest.TestCase): sout = b"a\xac\\u1234\xa4\\u8000\\U0010ffff" self.assertEqual(sin.encode("iso-8859-15", "backslashreplace"), sout) + def test_nameescape(self): + # Does the same as backslashescape, but prefers ``\N{...}`` escape + # sequences. + sin = "a\xac\u1234\u20ac\u8000\U0010ffff" + sout = (b'a\\N{NOT SIGN}\\N{ETHIOPIC SYLLABLE SEE}\\N{EURO SIGN}' + b'\\N{CJK UNIFIED IDEOGRAPH-8000}\\U0010ffff') + self.assertEqual(sin.encode("ascii", "namereplace"), sout) + + sout = (b'a\xac\\N{ETHIOPIC SYLLABLE SEE}\\N{EURO SIGN}' + b'\\N{CJK UNIFIED IDEOGRAPH-8000}\\U0010ffff') + self.assertEqual(sin.encode("latin-1", "namereplace"), sout) + + sout = (b'a\xac\\N{ETHIOPIC SYLLABLE SEE}\xa4' + b'\\N{CJK UNIFIED IDEOGRAPH-8000}\\U0010ffff') + self.assertEqual(sin.encode("iso-8859-15", "namereplace"), sout) + def test_decoding_callbacks(self): # This is a test for a decoding callback handler # that allows the decoding of the invalid sequence @@ -297,7 +313,7 @@ class CodecCallbackTest(unittest.TestCase): def test_longstrings(self): # test long strings to check for memory overflow problems errors = [ "strict", "ignore", "replace", "xmlcharrefreplace", - "backslashreplace"] + "backslashreplace", "namereplace"] # register the handlers under different names, # to prevent the codec from recognizing the name for err in errors: @@ -611,6 +627,81 @@ class CodecCallbackTest(unittest.TestCase): ("\\udfff", 1) ) + def test_badandgoodnamereplaceexceptions(self): + # "namereplace" complains about a non-exception passed in + self.assertRaises( + TypeError, + codecs.namereplace_errors, + 42 + ) + # "namereplace" complains about the wrong exception types + self.assertRaises( + TypeError, + codecs.namereplace_errors, + UnicodeError("ouch") + ) + # "namereplace" can only be used for encoding + self.assertRaises( + TypeError, + codecs.namereplace_errors, + UnicodeDecodeError("ascii", bytearray(b"\xff"), 0, 1, "ouch") + ) + self.assertRaises( + TypeError, + codecs.namereplace_errors, + UnicodeTranslateError("\u3042", 0, 1, "ouch") + ) + # Use the correct exception + self.assertEqual( + codecs.namereplace_errors( + UnicodeEncodeError("ascii", "\u3042", 0, 1, "ouch")), + ("\\N{HIRAGANA LETTER A}", 1) + ) + self.assertEqual( + codecs.namereplace_errors( + UnicodeEncodeError("ascii", "\x00", 0, 1, "ouch")), + ("\\x00", 1) + ) + self.assertEqual( + codecs.namereplace_errors( + UnicodeEncodeError("ascii", "\xff", 0, 1, "ouch")), + ("\\N{LATIN SMALL LETTER Y WITH DIAERESIS}", 1) + ) + self.assertEqual( + codecs.namereplace_errors( + UnicodeEncodeError("ascii", "\u0100", 0, 1, "ouch")), + ("\\N{LATIN CAPITAL LETTER A WITH MACRON}", 1) + ) + self.assertEqual( + codecs.namereplace_errors( + UnicodeEncodeError("ascii", "\uffff", 0, 1, "ouch")), + ("\\uffff", 1) + ) + if SIZEOF_WCHAR_T > 0: + self.assertEqual( + codecs.namereplace_errors( + UnicodeEncodeError("ascii", "\U00010000", + 0, 1, "ouch")), + ("\\N{LINEAR B SYLLABLE B008 A}", 1) + ) + self.assertEqual( + codecs.namereplace_errors( + UnicodeEncodeError("ascii", "\U0010ffff", + 0, 1, "ouch")), + ("\\U0010ffff", 1) + ) + # Lone surrogates (regardless of unicode width) + self.assertEqual( + codecs.namereplace_errors( + UnicodeEncodeError("ascii", "\ud800", 0, 1, "ouch")), + ("\\ud800", 1) + ) + self.assertEqual( + codecs.namereplace_errors( + UnicodeEncodeError("ascii", "\udfff", 0, 1, "ouch")), + ("\\udfff", 1) + ) + def test_badhandlerresults(self): results = ( 42, "foo", (1,2,3), ("foo", 1, 3), ("foo", None), ("foo",), ("foo", 1, 3), ("foo", None), ("foo",) ) encs = ("ascii", "latin-1", "iso-8859-1", "iso-8859-15") @@ -651,6 +742,10 @@ class CodecCallbackTest(unittest.TestCase): codecs.backslashreplace_errors, codecs.lookup_error("backslashreplace") ) + self.assertEqual( + codecs.namereplace_errors, + codecs.lookup_error("namereplace") + ) def test_unencodablereplacement(self): def unencrepl(exc): @@ -804,7 +899,8 @@ class CodecCallbackTest(unittest.TestCase): class D(dict): def __getitem__(self, key): raise ValueError - for err in ("strict", "replace", "xmlcharrefreplace", "backslashreplace", "test.posreturn"): + for err in ("strict", "replace", "xmlcharrefreplace", + "backslashreplace", "namereplace", "test.posreturn"): self.assertRaises(UnicodeError, codecs.charmap_encode, "\xff", err, {0xff: None}) self.assertRaises(ValueError, codecs.charmap_encode, "\xff", err, D()) self.assertRaises(TypeError, codecs.charmap_encode, "\xff", err, {0xff: 300}) @@ -819,7 +915,7 @@ class CodecCallbackTest(unittest.TestCase): def __getitem__(self, key): raise ValueError #self.assertRaises(ValueError, "\xff".translate, D()) - self.assertRaises(TypeError, "\xff".translate, {0xff: sys.maxunicode+1}) + self.assertRaises(ValueError, "\xff".translate, {0xff: sys.maxunicode+1}) self.assertRaises(TypeError, "\xff".translate, {0xff: ()}) def test_bug828737(self): diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index 6629ccd..7fed1f7 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -349,6 +349,8 @@ class ReadTest(MixInCheckStateHandling): self.assertRaises(UnicodeEncodeError, "\ud800".encode, self.encoding) self.assertEqual("[\uDC80]".encode(self.encoding, "backslashreplace"), "[\\udc80]".encode(self.encoding)) + self.assertEqual("[\uDC80]".encode(self.encoding, "namereplace"), + "[\\udc80]".encode(self.encoding)) self.assertEqual("[\uDC80]".encode(self.encoding, "xmlcharrefreplace"), "[�]".encode(self.encoding)) self.assertEqual("[\uDC80]".encode(self.encoding, "ignore"), @@ -808,6 +810,7 @@ class CP65001Test(ReadTest, unittest.TestCase): ('\udc80', 'ignore', b''), ('\udc80', 'replace', b'?'), ('\udc80', 'backslashreplace', b'\\udc80'), + ('\udc80', 'namereplace', b'\\udc80'), ('\udc80', 'surrogatepass', b'\xed\xb2\x80'), )) else: @@ -869,6 +872,8 @@ class CP65001Test(ReadTest, unittest.TestCase): self.assertRaises(UnicodeDecodeError, b"\xed\xa0\x80".decode, "cp65001") self.assertEqual("[\uDC80]".encode("cp65001", "backslashreplace"), b'[\\udc80]') + self.assertEqual("[\uDC80]".encode("cp65001", "namereplace"), + b'[\\udc80]') self.assertEqual("[\uDC80]".encode("cp65001", "xmlcharrefreplace"), b'[�]') self.assertEqual("[\uDC80]".encode("cp65001", "surrogateescape"), @@ -890,10 +895,6 @@ class CP65001Test(ReadTest, unittest.TestCase): "\U00010fff\uD800") self.assertTrue(codecs.lookup_error("surrogatepass")) - def test_readline(self): - self.skipTest("issue #20571: code page 65001 codec does not " - "support partial decoder yet") - class UTF7Test(ReadTest, unittest.TestCase): encoding = "utf-7" @@ -1612,6 +1613,12 @@ class CodecsModuleTest(unittest.TestCase): self.assertEqual(codecs.decode(b'abc'), 'abc') self.assertRaises(UnicodeDecodeError, codecs.decode, b'\xff', 'ascii') + # test keywords + self.assertEqual(codecs.decode(obj=b'\xe4\xf6\xfc', encoding='latin-1'), + '\xe4\xf6\xfc') + self.assertEqual(codecs.decode(b'[\xff]', 'ascii', errors='ignore'), + '[]') + def test_encode(self): self.assertEqual(codecs.encode('\xe4\xf6\xfc', 'latin-1'), b'\xe4\xf6\xfc') @@ -1620,6 +1627,12 @@ class CodecsModuleTest(unittest.TestCase): self.assertEqual(codecs.encode('abc'), b'abc') self.assertRaises(UnicodeEncodeError, codecs.encode, '\xffff', 'ascii') + # test keywords + self.assertEqual(codecs.encode(obj='\xe4\xf6\xfc', encoding='latin-1'), + b'\xe4\xf6\xfc') + self.assertEqual(codecs.encode('[\xff]', 'ascii', errors='ignore'), + b'[]') + def test_register(self): self.assertRaises(TypeError, codecs.register) self.assertRaises(TypeError, codecs.register, 42) @@ -1668,6 +1681,7 @@ class CodecsModuleTest(unittest.TestCase): "register_error", "lookup_error", "strict_errors", "replace_errors", "ignore_errors", "xmlcharrefreplace_errors", "backslashreplace_errors", + "namereplace_errors", "open", "EncodedFile", "iterencode", "iterdecode", "BOM", "BOM_BE", "BOM_LE", @@ -2818,15 +2832,15 @@ class CodePageTest(unittest.TestCase): self.assertRaisesRegex(UnicodeEncodeError, 'cp932', codecs.code_page_encode, 932, '\xff') self.assertRaisesRegex(UnicodeDecodeError, 'cp932', - codecs.code_page_decode, 932, b'\x81\x00') + codecs.code_page_decode, 932, b'\x81\x00', 'strict', True) self.assertRaisesRegex(UnicodeDecodeError, 'CP_UTF8', - codecs.code_page_decode, self.CP_UTF8, b'\xff') + codecs.code_page_decode, self.CP_UTF8, b'\xff', 'strict', True) def check_decode(self, cp, tests): for raw, errors, expected in tests: if expected is not None: try: - decoded = codecs.code_page_decode(cp, raw, errors) + decoded = codecs.code_page_decode(cp, raw, errors, True) except UnicodeDecodeError as err: self.fail('Unable to decode %a from "cp%s" with ' 'errors=%r: %s' % (raw, cp, errors, err)) @@ -2838,7 +2852,7 @@ class CodePageTest(unittest.TestCase): self.assertLessEqual(decoded[1], len(raw)) else: self.assertRaises(UnicodeDecodeError, - codecs.code_page_decode, cp, raw, errors) + codecs.code_page_decode, cp, raw, errors, True) def check_encode(self, cp, tests): for text, errors, expected in tests: @@ -2866,7 +2880,12 @@ class CodePageTest(unittest.TestCase): ('[\xff]', 'replace', b'[y]'), ('[\u20ac]', 'replace', b'[?]'), ('[\xff]', 'backslashreplace', b'[\\xff]'), + ('[\xff]', 'namereplace', + b'[\\N{LATIN SMALL LETTER Y WITH DIAERESIS}]'), ('[\xff]', 'xmlcharrefreplace', b'[ÿ]'), + ('\udcff', 'strict', None), + ('[\udcff]', 'surrogateescape', b'[\xff]'), + ('[\udcff]', 'surrogatepass', None), )) self.check_decode(932, ( (b'abc', 'strict', 'abc'), @@ -2876,6 +2895,7 @@ class CodePageTest(unittest.TestCase): (b'[\xff]', 'ignore', '[]'), (b'[\xff]', 'replace', '[\ufffd]'), (b'[\xff]', 'surrogateescape', '[\udcff]'), + (b'[\xff]', 'surrogatepass', None), (b'\x81\x00abc', 'strict', None), (b'\x81\x00abc', 'ignore', '\x00abc'), (b'\x81\x00abc', 'replace', '\ufffd\x00abc'), @@ -2886,9 +2906,12 @@ class CodePageTest(unittest.TestCase): ('abc', 'strict', b'abc'), ('\xe9\u20ac', 'strict', b'\xe9\x80'), ('\xff', 'strict', b'\xff'), + # test error handlers ('\u0141', 'strict', None), ('\u0141', 'ignore', b''), ('\u0141', 'replace', b'L'), + ('\udc98', 'surrogateescape', b'\x98'), + ('\udc98', 'surrogatepass', None), )) self.check_decode(1252, ( (b'abc', 'strict', 'abc'), diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index df1c63c..a94b5da 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -518,7 +518,7 @@ class TestOneTrickPonyABCs(ABCTestCase): class NextOnly: def __next__(self): yield 1 - raise StopIteration + return self.assertNotIsInstance(NextOnly(), Iterator) def test_Sized(self): @@ -647,6 +647,59 @@ class TestCollectionABCs(ABCTestCase): a, b = OneTwoThreeSet(), OneTwoThreeSet() self.assertTrue(hash(a) == hash(b)) + def test_isdisjoint_Set(self): + class MySet(Set): + def __init__(self, itr): + self.contents = itr + def __contains__(self, x): + return x in self.contents + def __iter__(self): + return iter(self.contents) + def __len__(self): + return len([x for x in self.contents]) + s1 = MySet((1, 2, 3)) + s2 = MySet((4, 5, 6)) + s3 = MySet((1, 5, 6)) + self.assertTrue(s1.isdisjoint(s2)) + self.assertFalse(s1.isdisjoint(s3)) + + def test_equality_Set(self): + class MySet(Set): + def __init__(self, itr): + self.contents = itr + def __contains__(self, x): + return x in self.contents + def __iter__(self): + return iter(self.contents) + def __len__(self): + return len([x for x in self.contents]) + s1 = MySet((1,)) + s2 = MySet((1, 2)) + s3 = MySet((3, 4)) + s4 = MySet((3, 4)) + self.assertTrue(s2 > s1) + self.assertTrue(s1 < s2) + self.assertFalse(s2 <= s1) + self.assertFalse(s2 <= s3) + self.assertFalse(s1 >= s2) + self.assertEqual(s3, s4) + self.assertNotEqual(s2, s3) + + def test_arithmetic_Set(self): + class MySet(Set): + def __init__(self, itr): + self.contents = itr + def __contains__(self, x): + return x in self.contents + def __iter__(self): + return iter(self.contents) + def __len__(self): + return len([x for x in self.contents]) + s1 = MySet((1, 2, 3)) + s2 = MySet((3, 4, 5)) + s3 = s1 & s2 + self.assertEqual(s3, MySet((3,))) + def test_MutableSet(self): self.assertIsInstance(set(), MutableSet) self.assertTrue(issubclass(set, MutableSet)) @@ -1378,6 +1431,21 @@ class TestOrderedDict(unittest.TestCase): self.assertEqual(list(od.items()), pairs) self.assertEqual(list(reversed(od)), [t[0] for t in reversed(pairs)]) + self.assertEqual(list(reversed(od.keys())), + [t[0] for t in reversed(pairs)]) + self.assertEqual(list(reversed(od.values())), + [t[1] for t in reversed(pairs)]) + self.assertEqual(list(reversed(od.items())), list(reversed(pairs))) + + def test_detect_deletion_during_iteration(self): + od = OrderedDict.fromkeys('abc') + it = iter(od) + key = next(it) + del od[key] + with self.assertRaises(Exception): + # Note, the exact exception raised is not guaranteed + # The only guarantee that the next() will not succeed + next(it) def test_popitem(self): pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)] diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py index 2a42238..7506c70 100644 --- a/Lib/test/test_compileall.py +++ b/Lib/test/test_compileall.py @@ -10,6 +10,13 @@ import time import unittest import io +from unittest import mock, skipUnless +try: + from concurrent.futures import ProcessPoolExecutor + _have_multiprocessing = True +except ImportError: + _have_multiprocessing = False + from test import support, script_helper class CompileallTests(unittest.TestCase): @@ -106,6 +113,33 @@ class CompileallTests(unittest.TestCase): debug_override=not optimize) self.assertTrue(os.path.isfile(cached3)) + @mock.patch('compileall.ProcessPoolExecutor') + def test_compile_pool_called(self, pool_mock): + compileall.compile_dir(self.directory, quiet=True, workers=5) + self.assertTrue(pool_mock.called) + + def test_compile_workers_non_positive(self): + with self.assertRaisesRegex(ValueError, + "workers must be greater or equal to 0"): + compileall.compile_dir(self.directory, workers=-1) + + @mock.patch('compileall.ProcessPoolExecutor') + def test_compile_workers_cpu_count(self, pool_mock): + compileall.compile_dir(self.directory, quiet=True, workers=0) + self.assertEqual(pool_mock.call_args[1]['max_workers'], None) + + @mock.patch('compileall.ProcessPoolExecutor') + @mock.patch('compileall.compile_file') + def test_compile_one_worker(self, compile_file_mock, pool_mock): + compileall.compile_dir(self.directory, quiet=True) + self.assertFalse(pool_mock.called) + self.assertTrue(compile_file_mock.called) + + @mock.patch('compileall.ProcessPoolExecutor', new=None) + def test_compile_missing_multiprocessing(self): + with self.assertRaisesRegex(NotImplementedError, + "multiprocessing support not available"): + compileall.compile_dir(self.directory, quiet=True, workers=5) class EncodingTest(unittest.TestCase): """Issue 6716: compileall should escape source code when printing errors @@ -273,12 +307,53 @@ class CommandLineTests(unittest.TestCase): self.assertCompiled(subinitfn) self.assertCompiled(hamfn) + def test_recursion_limit(self): + subpackage = os.path.join(self.pkgdir, 'spam') + subpackage2 = os.path.join(subpackage, 'ham') + subpackage3 = os.path.join(subpackage2, 'eggs') + for pkg in (subpackage, subpackage2, subpackage3): + script_helper.make_pkg(pkg) + + subinitfn = os.path.join(subpackage, '__init__.py') + hamfn = script_helper.make_script(subpackage, 'ham', '') + spamfn = script_helper.make_script(subpackage2, 'spam', '') + eggfn = script_helper.make_script(subpackage3, 'egg', '') + + self.assertRunOK('-q', '-r 0', self.pkgdir) + self.assertNotCompiled(subinitfn) + self.assertFalse( + os.path.exists(os.path.join(subpackage, '__pycache__'))) + + self.assertRunOK('-q', '-r 1', self.pkgdir) + self.assertCompiled(subinitfn) + self.assertCompiled(hamfn) + self.assertNotCompiled(spamfn) + + self.assertRunOK('-q', '-r 2', self.pkgdir) + self.assertCompiled(subinitfn) + self.assertCompiled(hamfn) + self.assertCompiled(spamfn) + self.assertNotCompiled(eggfn) + + self.assertRunOK('-q', '-r 5', self.pkgdir) + self.assertCompiled(subinitfn) + self.assertCompiled(hamfn) + self.assertCompiled(spamfn) + self.assertCompiled(eggfn) + def test_quiet(self): noisy = self.assertRunOK(self.pkgdir) quiet = self.assertRunOK('-q', self.pkgdir) self.assertNotEqual(b'', noisy) self.assertEqual(b'', quiet) + def test_silent(self): + script_helper.make_script(self.pkgdir, 'crunchyfrog', 'bad(syntax') + _, quiet, _ = self.assertRunNotOK('-q', self.pkgdir) + _, silent, _ = self.assertRunNotOK('-qq', self.pkgdir) + self.assertNotEqual(b'', quiet) + self.assertEqual(b'', silent) + def test_regexp(self): self.assertRunOK('-q', '-x', r'ba[^\\/]*$', self.pkgdir) self.assertNotCompiled(self.barfn) @@ -379,6 +454,29 @@ class CommandLineTests(unittest.TestCase): out = self.assertRunOK('badfilename') self.assertRegex(out, b"Can't list 'badfilename'") + @skipUnless(_have_multiprocessing, "requires multiprocessing") + def test_workers(self): + bar2fn = script_helper.make_script(self.directory, 'bar2', '') + files = [] + for suffix in range(5): + pkgdir = os.path.join(self.directory, 'foo{}'.format(suffix)) + os.mkdir(pkgdir) + fn = script_helper.make_script(pkgdir, '__init__', '') + files.append(script_helper.make_script(pkgdir, 'bar2', '')) + + self.assertRunOK(self.directory, '-j', '0') + self.assertCompiled(bar2fn) + for file in files: + self.assertCompiled(file) + + @mock.patch('compileall.compile_dir') + def test_workers_available_cores(self, compile_dir): + with mock.patch("sys.argv", + new=[sys.executable, self.directory, "-j0"]): + compileall.main() + self.assertTrue(compile_dir.called) + self.assertEqual(compile_dir.call_args[-1]['workers'], None) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_concurrent_futures.py b/Lib/test/test_concurrent_futures.py index c74b2ca..86802c2 100644 --- a/Lib/test/test_concurrent_futures.py +++ b/Lib/test/test_concurrent_futures.py @@ -11,6 +11,7 @@ test.support.import_module('threading') from test.script_helper import assert_python_ok +import os import sys import threading import time @@ -425,6 +426,13 @@ class ExecutorTest: self.assertTrue(collected, "Stale reference not collected within timeout.") + def test_max_workers_negative(self): + for number in (0, -1): + with self.assertRaisesRegex(ValueError, + "max_workers must be greater " + "than 0"): + self.executor_type(max_workers=number) + class ThreadPoolExecutorTest(ThreadPoolMixin, ExecutorTest, unittest.TestCase): def test_map_submits_without_iteration(self): @@ -437,6 +445,11 @@ class ThreadPoolExecutorTest(ThreadPoolMixin, ExecutorTest, unittest.TestCase): self.executor.shutdown(wait=True) self.assertCountEqual(finished, range(10)) + def test_default_workers(self): + executor = self.executor_type() + self.assertEqual(executor._max_workers, + (os.cpu_count() or 1) * 5) + class ProcessPoolExecutorTest(ProcessPoolMixin, ExecutorTest, unittest.TestCase): def test_killed_child(self): @@ -451,6 +464,48 @@ class ProcessPoolExecutorTest(ProcessPoolMixin, ExecutorTest, unittest.TestCase) # Submitting other jobs fails as well. self.assertRaises(BrokenProcessPool, self.executor.submit, pow, 2, 8) + def test_map_chunksize(self): + def bad_map(): + list(self.executor.map(pow, range(40), range(40), chunksize=-1)) + + ref = list(map(pow, range(40), range(40))) + self.assertEqual( + list(self.executor.map(pow, range(40), range(40), chunksize=6)), + ref) + self.assertEqual( + list(self.executor.map(pow, range(40), range(40), chunksize=50)), + ref) + self.assertEqual( + list(self.executor.map(pow, range(40), range(40), chunksize=40)), + ref) + self.assertRaises(ValueError, bad_map) + + @classmethod + def _test_traceback(cls): + raise RuntimeError(123) # some comment + + def test_traceback(self): + # We want ensure that the traceback from the child process is + # contained in the traceback raised in the main process. + future = self.executor.submit(self._test_traceback) + with self.assertRaises(Exception) as cm: + future.result() + + exc = cm.exception + self.assertIs(type(exc), RuntimeError) + self.assertEqual(exc.args, (123,)) + cause = exc.__cause__ + self.assertIs(type(cause), futures.process._RemoteTraceback) + self.assertIn('raise RuntimeError(123) # some comment', cause.tb) + + with test.support.captured_stderr() as f1: + try: + raise exc + except RuntimeError: + sys.excepthook(*sys.exc_info()) + self.assertIn('raise RuntimeError(123) # some comment', + f1.getvalue()) + class FutureTests(unittest.TestCase): def test_done_callback_with_result(self): diff --git a/Lib/test/test_configparser.py b/Lib/test/test_configparser.py index 09c229a..470d2cd 100644 --- a/Lib/test/test_configparser.py +++ b/Lib/test/test_configparser.py @@ -579,7 +579,7 @@ boolean {0[0]} NO return e else: self.fail("expected exception type %s.%s" - % (exc.__module__, exc.__name__)) + % (exc.__module__, exc.__qualname__)) def test_boolean(self): cf = self.fromstring( @@ -1584,6 +1584,34 @@ class CoverageOneHundredTestCase(unittest.TestCase): """) self.assertEqual(repr(parser['section']), '<Section: section>') + def test_inconsistent_converters_state(self): + parser = configparser.ConfigParser() + import decimal + parser.converters['decimal'] = decimal.Decimal + parser.read_string(""" + [s1] + one = 1 + [s2] + two = 2 + """) + self.assertIn('decimal', parser.converters) + self.assertEqual(parser.getdecimal('s1', 'one'), 1) + self.assertEqual(parser.getdecimal('s2', 'two'), 2) + self.assertEqual(parser['s1'].getdecimal('one'), 1) + self.assertEqual(parser['s2'].getdecimal('two'), 2) + del parser.getdecimal + with self.assertRaises(AttributeError): + parser.getdecimal('s1', 'one') + self.assertIn('decimal', parser.converters) + del parser.converters['decimal'] + self.assertNotIn('decimal', parser.converters) + with self.assertRaises(AttributeError): + parser.getdecimal('s1', 'one') + with self.assertRaises(AttributeError): + parser['s1'].getdecimal('one') + with self.assertRaises(AttributeError): + parser['s2'].getdecimal('two') + class ExceptionPicklingTestCase(unittest.TestCase): """Tests for issue #13760: ConfigParser exceptions are not picklable.""" @@ -1776,5 +1804,252 @@ class InlineCommentStrippingTestCase(unittest.TestCase): self.assertEqual(s['k3'], 'v3;#//still v3# and still v3') +class ExceptionContextTestCase(unittest.TestCase): + """ Test that implementation details doesn't leak + through raising exceptions. """ + + def test_get_basic_interpolation(self): + parser = configparser.ConfigParser() + parser.read_string(""" + [Paths] + home_dir: /Users + my_dir: %(home_dir1)s/lumberjack + my_pictures: %(my_dir)s/Pictures + """) + cm = self.assertRaises(configparser.InterpolationMissingOptionError) + with cm: + parser.get('Paths', 'my_dir') + self.assertIs(cm.exception.__suppress_context__, True) + + def test_get_extended_interpolation(self): + parser = configparser.ConfigParser( + interpolation=configparser.ExtendedInterpolation()) + parser.read_string(""" + [Paths] + home_dir: /Users + my_dir: ${home_dir1}/lumberjack + my_pictures: ${my_dir}/Pictures + """) + cm = self.assertRaises(configparser.InterpolationMissingOptionError) + with cm: + parser.get('Paths', 'my_dir') + self.assertIs(cm.exception.__suppress_context__, True) + + def test_missing_options(self): + parser = configparser.ConfigParser() + parser.read_string(""" + [Paths] + home_dir: /Users + """) + with self.assertRaises(configparser.NoSectionError) as cm: + parser.options('test') + self.assertIs(cm.exception.__suppress_context__, True) + + def test_missing_section(self): + config = configparser.ConfigParser() + with self.assertRaises(configparser.NoSectionError) as cm: + config.set('Section1', 'an_int', '15') + self.assertIs(cm.exception.__suppress_context__, True) + + def test_remove_option(self): + config = configparser.ConfigParser() + with self.assertRaises(configparser.NoSectionError) as cm: + config.remove_option('Section1', 'an_int') + self.assertIs(cm.exception.__suppress_context__, True) + + +class ConvertersTestCase(BasicTestCase, unittest.TestCase): + """Introduced in 3.5, issue #18159.""" + + config_class = configparser.ConfigParser + + def newconfig(self, defaults=None): + instance = super().newconfig(defaults=defaults) + instance.converters['list'] = lambda v: [e.strip() for e in v.split() + if e.strip()] + return instance + + def test_converters(self): + cfg = self.newconfig() + self.assertIn('boolean', cfg.converters) + self.assertIn('list', cfg.converters) + self.assertIsNone(cfg.converters['int']) + self.assertIsNone(cfg.converters['float']) + self.assertIsNone(cfg.converters['boolean']) + self.assertIsNotNone(cfg.converters['list']) + self.assertEqual(len(cfg.converters), 4) + with self.assertRaises(ValueError): + cfg.converters[''] = lambda v: v + with self.assertRaises(ValueError): + cfg.converters[None] = lambda v: v + cfg.read_string(""" + [s] + str = string + int = 1 + float = 0.5 + list = a b c d e f g + bool = yes + """) + s = cfg['s'] + self.assertEqual(s['str'], 'string') + self.assertEqual(s['int'], '1') + self.assertEqual(s['float'], '0.5') + self.assertEqual(s['list'], 'a b c d e f g') + self.assertEqual(s['bool'], 'yes') + self.assertEqual(cfg.get('s', 'str'), 'string') + self.assertEqual(cfg.get('s', 'int'), '1') + self.assertEqual(cfg.get('s', 'float'), '0.5') + self.assertEqual(cfg.get('s', 'list'), 'a b c d e f g') + self.assertEqual(cfg.get('s', 'bool'), 'yes') + self.assertEqual(cfg.get('s', 'str'), 'string') + self.assertEqual(cfg.getint('s', 'int'), 1) + self.assertEqual(cfg.getfloat('s', 'float'), 0.5) + self.assertEqual(cfg.getlist('s', 'list'), ['a', 'b', 'c', 'd', + 'e', 'f', 'g']) + self.assertEqual(cfg.getboolean('s', 'bool'), True) + self.assertEqual(s.get('str'), 'string') + self.assertEqual(s.getint('int'), 1) + self.assertEqual(s.getfloat('float'), 0.5) + self.assertEqual(s.getlist('list'), ['a', 'b', 'c', 'd', + 'e', 'f', 'g']) + self.assertEqual(s.getboolean('bool'), True) + with self.assertRaises(AttributeError): + cfg.getdecimal('s', 'float') + with self.assertRaises(AttributeError): + s.getdecimal('float') + import decimal + cfg.converters['decimal'] = decimal.Decimal + self.assertIn('decimal', cfg.converters) + self.assertIsNotNone(cfg.converters['decimal']) + self.assertEqual(len(cfg.converters), 5) + dec0_5 = decimal.Decimal('0.5') + self.assertEqual(cfg.getdecimal('s', 'float'), dec0_5) + self.assertEqual(s.getdecimal('float'), dec0_5) + del cfg.converters['decimal'] + self.assertNotIn('decimal', cfg.converters) + self.assertEqual(len(cfg.converters), 4) + with self.assertRaises(AttributeError): + cfg.getdecimal('s', 'float') + with self.assertRaises(AttributeError): + s.getdecimal('float') + with self.assertRaises(KeyError): + del cfg.converters['decimal'] + with self.assertRaises(KeyError): + del cfg.converters[''] + with self.assertRaises(KeyError): + del cfg.converters[None] + + +class BlatantOverrideConvertersTestCase(unittest.TestCase): + """What if somebody overrode a getboolean()? We want to make sure that in + this case the automatic converters do not kick in.""" + + config = """ + [one] + one = false + two = false + three = long story short + + [two] + one = false + two = false + three = four + """ + + def test_converters_at_init(self): + cfg = configparser.ConfigParser(converters={'len': len}) + cfg.read_string(self.config) + self._test_len(cfg) + self.assertIsNotNone(cfg.converters['len']) + + def test_inheritance(self): + class StrangeConfigParser(configparser.ConfigParser): + gettysburg = 'a historic borough in south central Pennsylvania' + + def getboolean(self, section, option, *, raw=False, vars=None, + fallback=configparser._UNSET): + if section == option: + return True + return super().getboolean(section, option, raw=raw, vars=vars, + fallback=fallback) + def getlen(self, section, option, *, raw=False, vars=None, + fallback=configparser._UNSET): + return self._get_conv(section, option, len, raw=raw, vars=vars, + fallback=fallback) + + cfg = StrangeConfigParser() + cfg.read_string(self.config) + self._test_len(cfg) + self.assertIsNone(cfg.converters['len']) + self.assertTrue(cfg.getboolean('one', 'one')) + self.assertTrue(cfg.getboolean('two', 'two')) + self.assertFalse(cfg.getboolean('one', 'two')) + self.assertFalse(cfg.getboolean('two', 'one')) + cfg.converters['boolean'] = cfg._convert_to_boolean + self.assertFalse(cfg.getboolean('one', 'one')) + self.assertFalse(cfg.getboolean('two', 'two')) + self.assertFalse(cfg.getboolean('one', 'two')) + self.assertFalse(cfg.getboolean('two', 'one')) + + def _test_len(self, cfg): + self.assertEqual(len(cfg.converters), 4) + self.assertIn('boolean', cfg.converters) + self.assertIn('len', cfg.converters) + self.assertNotIn('tysburg', cfg.converters) + self.assertIsNone(cfg.converters['int']) + self.assertIsNone(cfg.converters['float']) + self.assertIsNone(cfg.converters['boolean']) + self.assertEqual(cfg.getlen('one', 'one'), 5) + self.assertEqual(cfg.getlen('one', 'two'), 5) + self.assertEqual(cfg.getlen('one', 'three'), 16) + self.assertEqual(cfg.getlen('two', 'one'), 5) + self.assertEqual(cfg.getlen('two', 'two'), 5) + self.assertEqual(cfg.getlen('two', 'three'), 4) + self.assertEqual(cfg.getlen('two', 'four', fallback=0), 0) + with self.assertRaises(configparser.NoOptionError): + cfg.getlen('two', 'four') + self.assertEqual(cfg['one'].getlen('one'), 5) + self.assertEqual(cfg['one'].getlen('two'), 5) + self.assertEqual(cfg['one'].getlen('three'), 16) + self.assertEqual(cfg['two'].getlen('one'), 5) + self.assertEqual(cfg['two'].getlen('two'), 5) + self.assertEqual(cfg['two'].getlen('three'), 4) + self.assertEqual(cfg['two'].getlen('four', 0), 0) + self.assertEqual(cfg['two'].getlen('four'), None) + + def test_instance_assignment(self): + cfg = configparser.ConfigParser() + cfg.getboolean = lambda section, option: True + cfg.getlen = lambda section, option: len(cfg[section][option]) + cfg.read_string(self.config) + self.assertEqual(len(cfg.converters), 3) + self.assertIn('boolean', cfg.converters) + self.assertNotIn('len', cfg.converters) + self.assertIsNone(cfg.converters['int']) + self.assertIsNone(cfg.converters['float']) + self.assertIsNone(cfg.converters['boolean']) + self.assertTrue(cfg.getboolean('one', 'one')) + self.assertTrue(cfg.getboolean('two', 'two')) + self.assertTrue(cfg.getboolean('one', 'two')) + self.assertTrue(cfg.getboolean('two', 'one')) + cfg.converters['boolean'] = cfg._convert_to_boolean + self.assertFalse(cfg.getboolean('one', 'one')) + self.assertFalse(cfg.getboolean('two', 'two')) + self.assertFalse(cfg.getboolean('one', 'two')) + self.assertFalse(cfg.getboolean('two', 'one')) + self.assertEqual(cfg.getlen('one', 'one'), 5) + self.assertEqual(cfg.getlen('one', 'two'), 5) + self.assertEqual(cfg.getlen('one', 'three'), 16) + self.assertEqual(cfg.getlen('two', 'one'), 5) + self.assertEqual(cfg.getlen('two', 'two'), 5) + self.assertEqual(cfg.getlen('two', 'three'), 4) + # If a getter impl is assigned straight to the instance, it won't + # be available on the section proxies. + with self.assertRaises(AttributeError): + self.assertEqual(cfg['one'].getlen('one'), 5) + with self.assertRaises(AttributeError): + self.assertEqual(cfg['two'].getlen('one'), 5) + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_contextlib.py b/Lib/test/test_contextlib.py index 39cc776..c52066b 100644 --- a/Lib/test/test_contextlib.py +++ b/Lib/test/test_contextlib.py @@ -718,60 +718,76 @@ class TestExitStack(unittest.TestCase): stack.push(cm) self.assertIs(stack._exit_callbacks[-1], cm) -class TestRedirectStdout(unittest.TestCase): + +class TestRedirectStream: + + redirect_stream = None + orig_stream = None @support.requires_docstrings def test_instance_docs(self): # Issue 19330: ensure context manager instances have good docstrings - cm_docstring = redirect_stdout.__doc__ - obj = redirect_stdout(None) + cm_docstring = self.redirect_stream.__doc__ + obj = self.redirect_stream(None) self.assertEqual(obj.__doc__, cm_docstring) def test_no_redirect_in_init(self): - orig_stdout = sys.stdout - redirect_stdout(None) - self.assertIs(sys.stdout, orig_stdout) + orig_stdout = getattr(sys, self.orig_stream) + self.redirect_stream(None) + self.assertIs(getattr(sys, self.orig_stream), orig_stdout) def test_redirect_to_string_io(self): f = io.StringIO() msg = "Consider an API like help(), which prints directly to stdout" - orig_stdout = sys.stdout - with redirect_stdout(f): - print(msg) - self.assertIs(sys.stdout, orig_stdout) + orig_stdout = getattr(sys, self.orig_stream) + with self.redirect_stream(f): + print(msg, file=getattr(sys, self.orig_stream)) + self.assertIs(getattr(sys, self.orig_stream), orig_stdout) s = f.getvalue().strip() self.assertEqual(s, msg) def test_enter_result_is_target(self): f = io.StringIO() - with redirect_stdout(f) as enter_result: + with self.redirect_stream(f) as enter_result: self.assertIs(enter_result, f) def test_cm_is_reusable(self): f = io.StringIO() - write_to_f = redirect_stdout(f) - orig_stdout = sys.stdout + write_to_f = self.redirect_stream(f) + orig_stdout = getattr(sys, self.orig_stream) with write_to_f: - print("Hello", end=" ") + print("Hello", end=" ", file=getattr(sys, self.orig_stream)) with write_to_f: - print("World!") - self.assertIs(sys.stdout, orig_stdout) + print("World!", file=getattr(sys, self.orig_stream)) + self.assertIs(getattr(sys, self.orig_stream), orig_stdout) s = f.getvalue() self.assertEqual(s, "Hello World!\n") def test_cm_is_reentrant(self): f = io.StringIO() - write_to_f = redirect_stdout(f) - orig_stdout = sys.stdout + write_to_f = self.redirect_stream(f) + orig_stdout = getattr(sys, self.orig_stream) with write_to_f: - print("Hello", end=" ") + print("Hello", end=" ", file=getattr(sys, self.orig_stream)) with write_to_f: - print("World!") - self.assertIs(sys.stdout, orig_stdout) + print("World!", file=getattr(sys, self.orig_stream)) + self.assertIs(getattr(sys, self.orig_stream), orig_stdout) s = f.getvalue() self.assertEqual(s, "Hello World!\n") +class TestRedirectStdout(TestRedirectStream, unittest.TestCase): + + redirect_stream = redirect_stdout + orig_stream = "stdout" + + +class TestRedirectStderr(TestRedirectStream, unittest.TestCase): + + redirect_stream = redirect_stderr + orig_stream = "stderr" + + class TestSuppress(unittest.TestCase): @support.requires_docstrings diff --git a/Lib/test/test_cprofile.py b/Lib/test/test_cprofile.py index ce5d27e..f18983f 100644 --- a/Lib/test/test_cprofile.py +++ b/Lib/test/test_cprofile.py @@ -11,7 +11,7 @@ from test.profilee import testfunc class CProfileTest(ProfileTest): profilerclass = cProfile.Profile profilermodule = cProfile - expected_max_output = "{built-in method max}" + expected_max_output = "{built-in method builtins.max}" def get_expected_output(self): return _ProfileOutput @@ -72,9 +72,9 @@ profilee.py:84(helper2_indirect) <- 2 0.000 0.140 profilee.py:88(helper2) <- 6 0.234 0.300 profilee.py:55(helper) 2 0.078 0.100 profilee.py:84(helper2_indirect) profilee.py:98(subhelper) <- 8 0.064 0.080 profilee.py:88(helper2) -{built-in method exc_info} <- 4 0.000 0.000 profilee.py:73(helper1) -{built-in method hasattr} <- 4 0.000 0.004 profilee.py:73(helper1) +{built-in method builtins.hasattr} <- 4 0.000 0.004 profilee.py:73(helper1) 8 0.000 0.008 profilee.py:88(helper2) +{built-in method sys.exc_info} <- 4 0.000 0.000 profilee.py:73(helper1) {method 'append' of 'list' objects} <- 4 0.000 0.000 profilee.py:73(helper1)""" _ProfileOutput['print_callees'] = """\ <string>:1(<module>) -> 1 0.270 1.000 profilee.py:25(testfunc) @@ -87,12 +87,12 @@ profilee.py:48(mul) -> profilee.py:55(helper) -> 4 0.116 0.120 profilee.py:73(helper1) 2 0.000 0.140 profilee.py:84(helper2_indirect) 6 0.234 0.300 profilee.py:88(helper2) -profilee.py:73(helper1) -> 4 0.000 0.000 {built-in method exc_info} +profilee.py:73(helper1) -> 4 0.000 0.004 {built-in method builtins.hasattr} profilee.py:84(helper2_indirect) -> 2 0.006 0.040 profilee.py:35(factorial) 2 0.078 0.100 profilee.py:88(helper2) profilee.py:88(helper2) -> 8 0.064 0.080 profilee.py:98(subhelper) profilee.py:98(subhelper) -> 16 0.016 0.016 profilee.py:110(__getattr__) -{built-in method hasattr} -> 12 0.012 0.012 profilee.py:110(__getattr__)""" +{built-in method builtins.hasattr} -> 12 0.012 0.012 profilee.py:110(__getattr__)""" if __name__ == "__main__": main() diff --git a/Lib/test/test_csv.py b/Lib/test/test_csv.py index 7e2485f..3f3f328 100644 --- a/Lib/test/test_csv.py +++ b/Lib/test/test_csv.py @@ -575,6 +575,16 @@ class TestDictFields(unittest.TestCase): fileobj.readline() # header self.assertEqual(fileobj.read(), "10,,abc\r\n") + def test_write_multiple_dict_rows(self): + fileobj = StringIO() + writer = csv.DictWriter(fileobj, fieldnames=["f1", "f2", "f3"]) + writer.writeheader() + self.assertEqual(fileobj.getvalue(), "f1,f2,f3\r\n") + writer.writerows([{"f1": 1, "f2": "abc", "f3": "f"}, + {"f1": 2, "f2": 5, "f3": "xyz"}]) + self.assertEqual(fileobj.getvalue(), + "f1,f2,f3\r\n1,abc,f\r\n2,5,xyz\r\n") + def test_write_no_fields(self): fileobj = StringIO() self.assertRaises(TypeError, csv.DictWriter, fileobj) @@ -773,7 +783,7 @@ class TestDialectValidity(unittest.TestCase): with self.assertRaises(csv.Error) as cm: mydialect() self.assertEqual(str(cm.exception), - '"quotechar" must be an 1-character string') + '"quotechar" must be a 1-character string') mydialect.quotechar = 4 with self.assertRaises(csv.Error) as cm: @@ -796,13 +806,13 @@ class TestDialectValidity(unittest.TestCase): with self.assertRaises(csv.Error) as cm: mydialect() self.assertEqual(str(cm.exception), - '"delimiter" must be an 1-character string') + '"delimiter" must be a 1-character string') mydialect.delimiter = "" with self.assertRaises(csv.Error) as cm: mydialect() self.assertEqual(str(cm.exception), - '"delimiter" must be an 1-character string') + '"delimiter" must be a 1-character string') mydialect.delimiter = b"," with self.assertRaises(csv.Error) as cm: diff --git a/Lib/test/test_datetime.py b/Lib/test/test_datetime.py index d9ddb32..2d4eb52 100644 --- a/Lib/test/test_datetime.py +++ b/Lib/test/test_datetime.py @@ -1,20 +1,20 @@ import unittest import sys + from test.support import import_fresh_module, run_unittest TESTS = 'test.datetimetester' -# XXX: import_fresh_module() is supposed to leave sys.module cache untouched, -# XXX: but it does not, so we have to save and restore it ourselves. -save_sys_modules = sys.modules.copy() try: pure_tests = import_fresh_module(TESTS, fresh=['datetime', '_strptime'], blocked=['_datetime']) fast_tests = import_fresh_module(TESTS, fresh=['datetime', '_datetime', '_strptime']) finally: - sys.modules.clear() - sys.modules.update(save_sys_modules) + # XXX: import_fresh_module() is supposed to leave sys.module cache untouched, + # XXX: but it does not, so we have to cleanup ourselves. + for modname in ['datetime', '_datetime', '_strptime']: + sys.modules.pop(modname, None) test_modules = [pure_tests, fast_tests] test_suffixes = ["_Pure", "_Fast"] # XXX(gb) First run all the _Pure tests, then all the _Fast tests. You might diff --git a/Lib/test/test_dbm_dumb.py b/Lib/test/test_dbm_dumb.py index 29f48a3..ee5a32f 100644 --- a/Lib/test/test_dbm_dumb.py +++ b/Lib/test/test_dbm_dumb.py @@ -217,6 +217,14 @@ class DumbDBMTestCase(unittest.TestCase): self.assertEqual(str(cm.exception), "DBM object has already been closed") + def test_create_new(self): + with dumbdbm.open(_fname, 'n') as f: + for k in self._dict: + f[k] = self._dict[k] + + with dumbdbm.open(_fname, 'n') as f: + self.assertEqual(f.keys(), []) + def tearDown(self): _delete_files() diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index a178f6f..137aaa5 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -33,12 +33,13 @@ import unittest import numbers import locale from test.support import (run_unittest, run_doctest, is_resource_enabled, - requires_IEEE_754) + requires_IEEE_754, requires_docstrings) from test.support import (check_warnings, import_fresh_module, TestFailed, run_with_locale, cpython_only) import random import time import warnings +import inspect try: import threading except ImportError: @@ -4174,9 +4175,7 @@ class CheckAttributes(unittest.TestCase): self.assertEqual(C.__version__, P.__version__) self.assertEqual(C.__libmpdec_version__, P.__libmpdec_version__) - x = dir(C) - y = [s for s in dir(P) if '__' in s or not s.startswith('_')] - self.assertEqual(set(x) - set(y), set()) + self.assertEqual(dir(C), dir(P)) def test_context_attributes(self): @@ -4455,18 +4454,6 @@ class PyCoverage(Coverage): class PyFunctionality(unittest.TestCase): """Extra functionality in decimal.py""" - def test_py_quantize_watchexp(self): - # watchexp functionality - Decimal = P.Decimal - localcontext = P.localcontext - - with localcontext() as c: - c.prec = 1 - c.Emax = 1 - c.Emin = -1 - x = Decimal(99999).quantize(Decimal("1e3"), watchexp=False) - self.assertEqual(x, Decimal('1.00E+5')) - def test_py_alternate_formatting(self): # triples giving a format, a Decimal, and the expected result Decimal = P.Decimal @@ -5409,6 +5396,143 @@ class CWhitebox(unittest.TestCase): y = Decimal(10**(9*25)).__sizeof__() self.assertEqual(y, x+4) +@requires_docstrings +@unittest.skipUnless(C, "test requires C version") +class SignatureTest(unittest.TestCase): + """Function signatures""" + + def test_inspect_module(self): + for attr in dir(P): + if attr.startswith('_'): + continue + p_func = getattr(P, attr) + c_func = getattr(C, attr) + if (attr == 'Decimal' or attr == 'Context' or + inspect.isfunction(p_func)): + p_sig = inspect.signature(p_func) + c_sig = inspect.signature(c_func) + + # parameter names: + c_names = list(c_sig.parameters.keys()) + p_names = [x for x in p_sig.parameters.keys() if not + x.startswith('_')] + + self.assertEqual(c_names, p_names, + msg="parameter name mismatch in %s" % p_func) + + c_kind = [x.kind for x in c_sig.parameters.values()] + p_kind = [x[1].kind for x in p_sig.parameters.items() if not + x[0].startswith('_')] + + # parameters: + if attr != 'setcontext': + self.assertEqual(c_kind, p_kind, + msg="parameter kind mismatch in %s" % p_func) + + def test_inspect_types(self): + + POS = inspect._ParameterKind.POSITIONAL_ONLY + POS_KWD = inspect._ParameterKind.POSITIONAL_OR_KEYWORD + + # Type heuristic (type annotations would help!): + pdict = {C: {'other': C.Decimal(1), + 'third': C.Decimal(1), + 'x': C.Decimal(1), + 'y': C.Decimal(1), + 'z': C.Decimal(1), + 'a': C.Decimal(1), + 'b': C.Decimal(1), + 'c': C.Decimal(1), + 'exp': C.Decimal(1), + 'modulo': C.Decimal(1), + 'num': "1", + 'f': 1.0, + 'rounding': C.ROUND_HALF_UP, + 'context': C.getcontext()}, + P: {'other': P.Decimal(1), + 'third': P.Decimal(1), + 'a': P.Decimal(1), + 'b': P.Decimal(1), + 'c': P.Decimal(1), + 'exp': P.Decimal(1), + 'modulo': P.Decimal(1), + 'num': "1", + 'f': 1.0, + 'rounding': P.ROUND_HALF_UP, + 'context': P.getcontext()}} + + def mkargs(module, sig): + args = [] + kwargs = {} + for name, param in sig.parameters.items(): + if name == 'self': continue + if param.kind == POS: + args.append(pdict[module][name]) + elif param.kind == POS_KWD: + kwargs[name] = pdict[module][name] + else: + raise TestFailed("unexpected parameter kind") + return args, kwargs + + def tr(s): + """The C Context docstrings use 'x' in order to prevent confusion + with the article 'a' in the descriptions.""" + if s == 'x': return 'a' + if s == 'y': return 'b' + if s == 'z': return 'c' + return s + + def doit(ty): + p_type = getattr(P, ty) + c_type = getattr(C, ty) + for attr in dir(p_type): + if attr.startswith('_'): + continue + p_func = getattr(p_type, attr) + c_func = getattr(c_type, attr) + if inspect.isfunction(p_func): + p_sig = inspect.signature(p_func) + c_sig = inspect.signature(c_func) + + # parameter names: + p_names = list(p_sig.parameters.keys()) + c_names = [tr(x) for x in c_sig.parameters.keys()] + + self.assertEqual(c_names, p_names, + msg="parameter name mismatch in %s" % p_func) + + p_kind = [x.kind for x in p_sig.parameters.values()] + c_kind = [x.kind for x in c_sig.parameters.values()] + + # 'self' parameter: + self.assertIs(p_kind[0], POS_KWD) + self.assertIs(c_kind[0], POS) + + # remaining parameters: + if ty == 'Decimal': + self.assertEqual(c_kind[1:], p_kind[1:], + msg="parameter kind mismatch in %s" % p_func) + else: # Context methods are positional only in the C version. + self.assertEqual(len(c_kind), len(p_kind), + msg="parameter kind mismatch in %s" % p_func) + + # Run the function: + args, kwds = mkargs(C, c_sig) + try: + getattr(c_type(9), attr)(*args, **kwds) + except Exception as err: + raise TestFailed("invalid signature for %s: %s %s" % (c_func, args, kwds)) + + args, kwds = mkargs(P, p_sig) + try: + getattr(p_type(9), attr)(*args, **kwds) + except Exception as err: + raise TestFailed("invalid signature for %s: %s %s" % (p_func, args, kwds)) + + doit('Decimal') + doit('Context') + + all_tests = [ CExplicitConstructionTest, PyExplicitConstructionTest, CImplicitConstructionTest, PyImplicitConstructionTest, @@ -5434,6 +5558,7 @@ if not C: all_tests = all_tests[1::2] else: all_tests.insert(0, CheckAttributes) + all_tests.insert(1, SignatureTest) def test_main(arith=None, verbose=None, todo_tests=None, debug=None): diff --git a/Lib/test/test_defaultdict.py b/Lib/test/test_defaultdict.py index 532d535..48d1cb5 100644 --- a/Lib/test/test_defaultdict.py +++ b/Lib/test/test_defaultdict.py @@ -157,8 +157,9 @@ class TestDefaultDict(unittest.TestCase): def _factory(self): return [] d = sub() - self.assertTrue(repr(d).startswith( - "defaultdict(<bound method sub._factory of defaultdict(...")) + self.assertRegex(repr(d), + r"defaultdict\(<bound method .*sub\._factory " + r"of defaultdict\(\.\.\., \{\}\)>, \{\}\)") # NOTE: printing a subclass of a builtin type does not call its # tp_print slot. So this part is essentially the same test as above. diff --git a/Lib/test/test_deque.py b/Lib/test/test_deque.py index 5ecbc73..e336e5a 100644 --- a/Lib/test/test_deque.py +++ b/Lib/test/test_deque.py @@ -508,6 +508,11 @@ class TestBasic(unittest.TestCase): for s in ('abcd', range(2000)): self.assertEqual(list(reversed(deque(s))), list(reversed(s))) + def test_reversed_new(self): + klass = type(reversed(deque())) + for s in ('abcd', range(2000)): + self.assertEqual(list(klass(deque(s))), list(reversed(s))) + def test_gc_doesnt_blowup(self): import gc # This used to assert-fail in deque_traverse() under a debug diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 26c273a..0c88fd2 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -4160,6 +4160,7 @@ order (MRO) for bases """ ('__add__', 'x + y', 'x += y'), ('__sub__', 'x - y', 'x -= y'), ('__mul__', 'x * y', 'x *= y'), + ('__matmul__', 'x @ y', 'x @= y'), ('__truediv__', 'operator.truediv(x, y)', None), ('__floordiv__', 'operator.floordiv(x, y)', None), ('__div__', 'x / y', 'x /= y'), @@ -4422,6 +4423,61 @@ order (MRO) for bases """ self.assertIn("__dict__", Base.__dict__) self.assertNotIn("__dict__", Sub.__dict__) + def test_bound_method_repr(self): + class Foo: + def method(self): + pass + self.assertRegex(repr(Foo().method), + r"<bound method .*Foo\.method of <.*Foo object at .*>>") + + + class Base: + def method(self): + pass + class Derived1(Base): + pass + class Derived2(Base): + def method(self): + pass + base = Base() + derived1 = Derived1() + derived2 = Derived2() + super_d2 = super(Derived2, derived2) + self.assertRegex(repr(base.method), + r"<bound method .*Base\.method of <.*Base object at .*>>") + self.assertRegex(repr(derived1.method), + r"<bound method .*Base\.method of <.*Derived1 object at .*>>") + self.assertRegex(repr(derived2.method), + r"<bound method .*Derived2\.method of <.*Derived2 object at .*>>") + self.assertRegex(repr(super_d2.method), + r"<bound method .*Base\.method of <.*Derived2 object at .*>>") + + class Foo: + @classmethod + def method(cls): + pass + foo = Foo() + self.assertRegex(repr(foo.method), # access via instance + r"<bound method .*Foo\.method of <class '.*Foo'>>") + self.assertRegex(repr(Foo.method), # access via the class + r"<bound method .*Foo\.method of <class '.*Foo'>>") + + + class MyCallable: + def __call__(self, arg): + pass + func = MyCallable() # func has no __name__ or __qualname__ attributes + instance = object() + method = types.MethodType(func, instance) + self.assertRegex(repr(method), + r"<bound method \? of <object object at .*>>") + func.__name__ = "name" + self.assertRegex(repr(method), + r"<bound method name of <object object at .*>>") + func.__qualname__ = "qualname" + self.assertRegex(repr(method), + r"<bound method qualname of <object object at .*>>") + class DictProxyTests(unittest.TestCase): def setUp(self): @@ -4536,26 +4592,15 @@ class PicklingTests(unittest.TestCase): def _check_reduce(self, proto, obj, args=(), kwargs={}, state=None, listitems=None, dictitems=None): - if proto >= 4: + if proto >= 2: reduce_value = obj.__reduce_ex__(proto) - self.assertEqual(reduce_value[:3], - (copyreg.__newobj_ex__, - (type(obj), args, kwargs), - state)) - if listitems is not None: - self.assertListEqual(list(reduce_value[3]), listitems) + if kwargs: + self.assertEqual(reduce_value[0], copyreg.__newobj_ex__) + self.assertEqual(reduce_value[1], (type(obj), args, kwargs)) else: - self.assertIsNone(reduce_value[3]) - if dictitems is not None: - self.assertDictEqual(dict(reduce_value[4]), dictitems) - else: - self.assertIsNone(reduce_value[4]) - elif proto >= 2: - reduce_value = obj.__reduce_ex__(proto) - self.assertEqual(reduce_value[:3], - (copyreg.__newobj__, - (type(obj),) + args, - state)) + self.assertEqual(reduce_value[0], copyreg.__newobj__) + self.assertEqual(reduce_value[1], (type(obj),) + args) + self.assertEqual(reduce_value[2], state) if listitems is not None: self.assertListEqual(list(reduce_value[3]), listitems) else: diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index b8daff7..4f30183 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -230,6 +230,9 @@ dis_traceback = """\ TRACEBACK_CODE.co_firstlineno + 4, TRACEBACK_CODE.co_firstlineno + 5) +def _g(x): + yield x + class DisTests(unittest.TestCase): def get_disassembly(self, func, lasti=-1, wrapper=True): @@ -315,6 +318,11 @@ class DisTests(unittest.TestCase): method_bytecode = _C(1).__init__.__code__.co_code self.do_disassembly_test(method_bytecode, dis_c_instance_method_bytes) + def test_disassemble_generator(self): + gen_func_disas = self.get_disassembly(_g) # Disassemble generator function + gen_disas = self.get_disassembly(_g(1)) # Disassemble generator itself + self.assertEqual(gen_disas, gen_func_disas) + def test_dis_none(self): try: del sys.last_traceback @@ -561,10 +569,10 @@ expected_jumpy_line = 1 #_instructions = dis.get_instructions(outer, first_line=expected_outer_line) #print('expected_opinfo_outer = [\n ', #',\n '.join(map(str, _instructions)), ',\n]', sep='') -#_instructions = dis.get_instructions(outer(), first_line=expected_outer_line) +#_instructions = dis.get_instructions(outer(), first_line=expected_f_line) #print('expected_opinfo_f = [\n ', #',\n '.join(map(str, _instructions)), ',\n]', sep='') -#_instructions = dis.get_instructions(outer()(), first_line=expected_outer_line) +#_instructions = dis.get_instructions(outer()(), first_line=expected_inner_line) #print('expected_opinfo_inner = [\n ', #',\n '.join(map(str, _instructions)), ',\n]', sep='') #_instructions = dis.get_instructions(jumpy, first_line=expected_jumpy_line) @@ -635,12 +643,12 @@ expected_opinfo_inner = [ ] expected_opinfo_jumpy = [ - Instruction(opname='SETUP_LOOP', opcode=120, arg=74, argval=77, argrepr='to 77', offset=0, starts_line=3, is_jump_target=False), + Instruction(opname='SETUP_LOOP', opcode=120, arg=68, argval=71, argrepr='to 71', offset=0, starts_line=3, is_jump_target=False), Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='range', argrepr='range', offset=3, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_CONST', opcode=100, arg=1, argval=10, argrepr='10', offset=6, starts_line=None, is_jump_target=False), Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=9, starts_line=None, is_jump_target=False), Instruction(opname='GET_ITER', opcode=68, arg=None, argval=None, argrepr='', offset=12, starts_line=None, is_jump_target=False), - Instruction(opname='FOR_ITER', opcode=93, arg=50, argval=66, argrepr='to 66', offset=13, starts_line=None, is_jump_target=True), + Instruction(opname='FOR_ITER', opcode=93, arg=44, argval=60, argrepr='to 60', offset=13, starts_line=None, is_jump_target=True), Instruction(opname='STORE_FAST', opcode=125, arg=0, argval='i', argrepr='i', offset=16, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=19, starts_line=4, is_jump_target=False), Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=22, starts_line=None, is_jump_target=False), @@ -649,92 +657,88 @@ expected_opinfo_jumpy = [ Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=29, starts_line=5, is_jump_target=False), Instruction(opname='LOAD_CONST', opcode=100, arg=2, argval=4, argrepr='4', offset=32, starts_line=None, is_jump_target=False), Instruction(opname='COMPARE_OP', opcode=107, arg=0, argval='<', argrepr='<', offset=35, starts_line=None, is_jump_target=False), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=47, argval=47, argrepr='', offset=38, starts_line=None, is_jump_target=False), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=44, argval=44, argrepr='', offset=38, starts_line=None, is_jump_target=False), Instruction(opname='JUMP_ABSOLUTE', opcode=113, arg=13, argval=13, argrepr='', offset=41, starts_line=6, is_jump_target=False), - Instruction(opname='JUMP_FORWARD', opcode=110, arg=0, argval=47, argrepr='to 47', offset=44, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=47, starts_line=7, is_jump_target=True), - Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=6, argrepr='6', offset=50, starts_line=None, is_jump_target=False), - Instruction(opname='COMPARE_OP', opcode=107, arg=4, argval='>', argrepr='>', offset=53, starts_line=None, is_jump_target=False), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=13, argval=13, argrepr='', offset=56, starts_line=None, is_jump_target=False), - Instruction(opname='BREAK_LOOP', opcode=80, arg=None, argval=None, argrepr='', offset=59, starts_line=8, is_jump_target=False), - Instruction(opname='JUMP_ABSOLUTE', opcode=113, arg=13, argval=13, argrepr='', offset=60, starts_line=None, is_jump_target=False), - Instruction(opname='JUMP_ABSOLUTE', opcode=113, arg=13, argval=13, argrepr='', offset=63, starts_line=None, is_jump_target=False), - Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=66, starts_line=None, is_jump_target=True), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=67, starts_line=10, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=70, starts_line=None, is_jump_target=False), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=73, starts_line=None, is_jump_target=False), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=76, starts_line=None, is_jump_target=False), - Instruction(opname='SETUP_LOOP', opcode=120, arg=74, argval=154, argrepr='to 154', offset=77, starts_line=11, is_jump_target=True), - Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=80, starts_line=None, is_jump_target=True), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=143, argval=143, argrepr='', offset=83, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=86, starts_line=12, is_jump_target=False), - Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=89, starts_line=None, is_jump_target=False), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=92, starts_line=None, is_jump_target=False), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=95, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=96, starts_line=13, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=1, argrepr='1', offset=99, starts_line=None, is_jump_target=False), - Instruction(opname='INPLACE_SUBTRACT', opcode=56, arg=None, argval=None, argrepr='', offset=102, starts_line=None, is_jump_target=False), - Instruction(opname='STORE_FAST', opcode=125, arg=0, argval='i', argrepr='i', offset=103, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=106, starts_line=14, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=6, argrepr='6', offset=109, starts_line=None, is_jump_target=False), - Instruction(opname='COMPARE_OP', opcode=107, arg=4, argval='>', argrepr='>', offset=112, starts_line=None, is_jump_target=False), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=124, argval=124, argrepr='', offset=115, starts_line=None, is_jump_target=False), - Instruction(opname='JUMP_ABSOLUTE', opcode=113, arg=80, argval=80, argrepr='', offset=118, starts_line=15, is_jump_target=False), - Instruction(opname='JUMP_FORWARD', opcode=110, arg=0, argval=124, argrepr='to 124', offset=121, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=124, starts_line=16, is_jump_target=True), - Instruction(opname='LOAD_CONST', opcode=100, arg=2, argval=4, argrepr='4', offset=127, starts_line=None, is_jump_target=False), - Instruction(opname='COMPARE_OP', opcode=107, arg=0, argval='<', argrepr='<', offset=130, starts_line=None, is_jump_target=False), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=80, argval=80, argrepr='', offset=133, starts_line=None, is_jump_target=False), - Instruction(opname='BREAK_LOOP', opcode=80, arg=None, argval=None, argrepr='', offset=136, starts_line=17, is_jump_target=False), - Instruction(opname='JUMP_ABSOLUTE', opcode=113, arg=80, argval=80, argrepr='', offset=137, starts_line=None, is_jump_target=False), - Instruction(opname='JUMP_ABSOLUTE', opcode=113, arg=80, argval=80, argrepr='', offset=140, starts_line=None, is_jump_target=False), - Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=143, starts_line=None, is_jump_target=True), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=144, starts_line=19, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=6, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=147, starts_line=None, is_jump_target=False), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=150, starts_line=None, is_jump_target=False), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=153, starts_line=None, is_jump_target=False), - Instruction(opname='SETUP_FINALLY', opcode=122, arg=72, argval=229, argrepr='to 229', offset=154, starts_line=20, is_jump_target=True), - Instruction(opname='SETUP_EXCEPT', opcode=121, arg=12, argval=172, argrepr='to 172', offset=157, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=1, argrepr='1', offset=160, starts_line=21, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=7, argval=0, argrepr='0', offset=163, starts_line=None, is_jump_target=False), - Instruction(opname='BINARY_TRUE_DIVIDE', opcode=27, arg=None, argval=None, argrepr='', offset=166, starts_line=None, is_jump_target=False), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=167, starts_line=None, is_jump_target=False), - Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=168, starts_line=None, is_jump_target=False), - Instruction(opname='JUMP_FORWARD', opcode=110, arg=28, argval=200, argrepr='to 200', offset=169, starts_line=None, is_jump_target=False), - Instruction(opname='DUP_TOP', opcode=4, arg=None, argval=None, argrepr='', offset=172, starts_line=22, is_jump_target=True), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=2, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=173, starts_line=None, is_jump_target=False), - Instruction(opname='COMPARE_OP', opcode=107, arg=10, argval='exception match', argrepr='exception match', offset=176, starts_line=None, is_jump_target=False), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=199, argval=199, argrepr='', offset=179, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=44, starts_line=7, is_jump_target=True), + Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=6, argrepr='6', offset=47, starts_line=None, is_jump_target=False), + Instruction(opname='COMPARE_OP', opcode=107, arg=4, argval='>', argrepr='>', offset=50, starts_line=None, is_jump_target=False), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=13, argval=13, argrepr='', offset=53, starts_line=None, is_jump_target=False), + Instruction(opname='BREAK_LOOP', opcode=80, arg=None, argval=None, argrepr='', offset=56, starts_line=8, is_jump_target=False), + Instruction(opname='JUMP_ABSOLUTE', opcode=113, arg=13, argval=13, argrepr='', offset=57, starts_line=None, is_jump_target=False), + Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=60, starts_line=None, is_jump_target=True), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=61, starts_line=10, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=64, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=67, starts_line=None, is_jump_target=False), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=70, starts_line=None, is_jump_target=False), + Instruction(opname='SETUP_LOOP', opcode=120, arg=68, argval=142, argrepr='to 142', offset=71, starts_line=11, is_jump_target=True), + Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=74, starts_line=None, is_jump_target=True), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=131, argval=131, argrepr='', offset=77, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=80, starts_line=12, is_jump_target=False), + Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=83, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=86, starts_line=None, is_jump_target=False), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=89, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=90, starts_line=13, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=1, argrepr='1', offset=93, starts_line=None, is_jump_target=False), + Instruction(opname='INPLACE_SUBTRACT', opcode=56, arg=None, argval=None, argrepr='', offset=96, starts_line=None, is_jump_target=False), + Instruction(opname='STORE_FAST', opcode=125, arg=0, argval='i', argrepr='i', offset=97, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=100, starts_line=14, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=6, argrepr='6', offset=103, starts_line=None, is_jump_target=False), + Instruction(opname='COMPARE_OP', opcode=107, arg=4, argval='>', argrepr='>', offset=106, starts_line=None, is_jump_target=False), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=115, argval=115, argrepr='', offset=109, starts_line=None, is_jump_target=False), + Instruction(opname='JUMP_ABSOLUTE', opcode=113, arg=74, argval=74, argrepr='', offset=112, starts_line=15, is_jump_target=False), + Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=115, starts_line=16, is_jump_target=True), + Instruction(opname='LOAD_CONST', opcode=100, arg=2, argval=4, argrepr='4', offset=118, starts_line=None, is_jump_target=False), + Instruction(opname='COMPARE_OP', opcode=107, arg=0, argval='<', argrepr='<', offset=121, starts_line=None, is_jump_target=False), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=74, argval=74, argrepr='', offset=124, starts_line=None, is_jump_target=False), + Instruction(opname='BREAK_LOOP', opcode=80, arg=None, argval=None, argrepr='', offset=127, starts_line=17, is_jump_target=False), + Instruction(opname='JUMP_ABSOLUTE', opcode=113, arg=74, argval=74, argrepr='', offset=128, starts_line=None, is_jump_target=False), + Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=131, starts_line=None, is_jump_target=True), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=132, starts_line=19, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=6, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=135, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=138, starts_line=None, is_jump_target=False), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=141, starts_line=None, is_jump_target=False), + Instruction(opname='SETUP_FINALLY', opcode=122, arg=72, argval=217, argrepr='to 217', offset=142, starts_line=20, is_jump_target=True), + Instruction(opname='SETUP_EXCEPT', opcode=121, arg=12, argval=160, argrepr='to 160', offset=145, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=1, argrepr='1', offset=148, starts_line=21, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=7, argval=0, argrepr='0', offset=151, starts_line=None, is_jump_target=False), + Instruction(opname='BINARY_TRUE_DIVIDE', opcode=27, arg=None, argval=None, argrepr='', offset=154, starts_line=None, is_jump_target=False), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=155, starts_line=None, is_jump_target=False), + Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=156, starts_line=None, is_jump_target=False), + Instruction(opname='JUMP_FORWARD', opcode=110, arg=28, argval=188, argrepr='to 188', offset=157, starts_line=None, is_jump_target=False), + Instruction(opname='DUP_TOP', opcode=4, arg=None, argval=None, argrepr='', offset=160, starts_line=22, is_jump_target=True), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=2, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=161, starts_line=None, is_jump_target=False), + Instruction(opname='COMPARE_OP', opcode=107, arg=10, argval='exception match', argrepr='exception match', offset=164, starts_line=None, is_jump_target=False), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=187, argval=187, argrepr='', offset=167, starts_line=None, is_jump_target=False), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=170, starts_line=None, is_jump_target=False), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=171, starts_line=None, is_jump_target=False), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=172, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=173, starts_line=23, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=8, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=176, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=179, starts_line=None, is_jump_target=False), Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=182, starts_line=None, is_jump_target=False), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=183, starts_line=None, is_jump_target=False), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=184, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=185, starts_line=23, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=8, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=188, starts_line=None, is_jump_target=False), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=191, starts_line=None, is_jump_target=False), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=194, starts_line=None, is_jump_target=False), - Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=195, starts_line=None, is_jump_target=False), - Instruction(opname='JUMP_FORWARD', opcode=110, arg=26, argval=225, argrepr='to 225', offset=196, starts_line=None, is_jump_target=False), - Instruction(opname='END_FINALLY', opcode=88, arg=None, argval=None, argrepr='', offset=199, starts_line=None, is_jump_target=True), - Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=200, starts_line=25, is_jump_target=True), - Instruction(opname='SETUP_WITH', opcode=143, arg=17, argval=223, argrepr='to 223', offset=203, starts_line=None, is_jump_target=False), - Instruction(opname='STORE_FAST', opcode=125, arg=1, argval='dodgy', argrepr='dodgy', offset=206, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=209, starts_line=26, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=9, argval='Never reach this', argrepr="'Never reach this'", offset=212, starts_line=None, is_jump_target=False), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=215, starts_line=None, is_jump_target=False), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=218, starts_line=None, is_jump_target=False), - Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=219, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=220, starts_line=None, is_jump_target=False), - Instruction(opname='WITH_CLEANUP', opcode=81, arg=None, argval=None, argrepr='', offset=223, starts_line=None, is_jump_target=True), - Instruction(opname='END_FINALLY', opcode=88, arg=None, argval=None, argrepr='', offset=224, starts_line=None, is_jump_target=False), - Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=225, starts_line=None, is_jump_target=True), - Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=226, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=229, starts_line=28, is_jump_target=True), - Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=232, starts_line=None, is_jump_target=False), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=235, starts_line=None, is_jump_target=False), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=238, starts_line=None, is_jump_target=False), - Instruction(opname='END_FINALLY', opcode=88, arg=None, argval=None, argrepr='', offset=239, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=240, starts_line=None, is_jump_target=False), - Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=243, starts_line=None, is_jump_target=False), + Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=183, starts_line=None, is_jump_target=False), + Instruction(opname='JUMP_FORWARD', opcode=110, arg=26, argval=213, argrepr='to 213', offset=184, starts_line=None, is_jump_target=False), + Instruction(opname='END_FINALLY', opcode=88, arg=None, argval=None, argrepr='', offset=187, starts_line=None, is_jump_target=True), + Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=188, starts_line=25, is_jump_target=True), + Instruction(opname='SETUP_WITH', opcode=143, arg=17, argval=211, argrepr='to 211', offset=191, starts_line=None, is_jump_target=False), + Instruction(opname='STORE_FAST', opcode=125, arg=1, argval='dodgy', argrepr='dodgy', offset=194, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=197, starts_line=26, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=9, argval='Never reach this', argrepr="'Never reach this'", offset=200, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=203, starts_line=None, is_jump_target=False), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=206, starts_line=None, is_jump_target=False), + Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=207, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=208, starts_line=None, is_jump_target=False), + Instruction(opname='WITH_CLEANUP', opcode=81, arg=None, argval=None, argrepr='', offset=211, starts_line=None, is_jump_target=True), + Instruction(opname='END_FINALLY', opcode=88, arg=None, argval=None, argrepr='', offset=212, starts_line=None, is_jump_target=False), + Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=213, starts_line=None, is_jump_target=True), + Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=214, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=217, starts_line=28, is_jump_target=True), + Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=220, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=223, starts_line=None, is_jump_target=False), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=226, starts_line=None, is_jump_target=False), + Instruction(opname='END_FINALLY', opcode=88, arg=None, argval=None, argrepr='', offset=227, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=228, starts_line=None, is_jump_target=False), + Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=231, starts_line=None, is_jump_target=False), ] # One last piece of inspect fodder to check the default line number handling diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index 4137d5a..95700a3 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -4,6 +4,7 @@ Test script for doctest. from test import support import doctest +import functools import os import sys @@ -434,7 +435,7 @@ We'll simulate a __file__ attr that ends in pyc: >>> tests = finder.find(sample_func) >>> print(tests) # doctest: +ELLIPSIS - [<DocTest sample_func from ...:18 (1 example)>] + [<DocTest sample_func from ...:19 (1 example)>] The exact name depends on how test_doctest was invoked, so allow for leading path components. @@ -2096,22 +2097,9 @@ def test_DocTestSuite(): >>> suite.run(unittest.TestResult()) <unittest.result.TestResult run=0 errors=0 failures=0> - However, if DocTestSuite finds no docstrings, it raises an error: + The module need not contain any docstrings either: - >>> try: - ... doctest.DocTestSuite('test.sample_doctest_no_docstrings') - ... except ValueError as e: - ... error = e - - >>> print(error.args[1]) - has no docstrings - - You can prevent this error by passing a DocTestFinder instance with - the `exclude_empty` keyword argument set to False: - - >>> finder = doctest.DocTestFinder(exclude_empty=False) - >>> suite = doctest.DocTestSuite('test.sample_doctest_no_docstrings', - ... test_finder=finder) + >>> suite = doctest.DocTestSuite('test.sample_doctest_no_docstrings') >>> suite.run(unittest.TestResult()) <unittest.result.TestResult run=0 errors=0 failures=0> @@ -2121,6 +2109,22 @@ def test_DocTestSuite(): >>> suite.run(unittest.TestResult()) <unittest.result.TestResult run=9 errors=0 failures=4> + We can also provide a DocTestFinder: + + >>> finder = doctest.DocTestFinder() + >>> suite = doctest.DocTestSuite('test.sample_doctest', + ... test_finder=finder) + >>> suite.run(unittest.TestResult()) + <unittest.result.TestResult run=9 errors=0 failures=4> + + The DocTestFinder need not return any tests: + + >>> finder = doctest.DocTestFinder() + >>> suite = doctest.DocTestSuite('test.sample_doctest_no_docstrings', + ... test_finder=finder) + >>> suite.run(unittest.TestResult()) + <unittest.result.TestResult run=0 errors=0 failures=0> + We can supply global variables. If we pass globs, they will be used instead of the module globals. Here we'll pass an empty globals, triggering an extra error: @@ -2168,7 +2172,7 @@ def test_DocTestSuite(): >>> test.test_doctest.sillySetup Traceback (most recent call last): ... - AttributeError: 'module' object has no attribute 'sillySetup' + AttributeError: module 'test.test_doctest' has no attribute 'sillySetup' The setUp and tearDown funtions are passed test objects. Here we'll use the setUp function to supply the missing variable y: @@ -2314,7 +2318,7 @@ def test_DocFileSuite(): >>> test.test_doctest.sillySetup Traceback (most recent call last): ... - AttributeError: 'module' object has no attribute 'sillySetup' + AttributeError: module 'test.test_doctest' has no attribute 'sillySetup' The setUp and tearDown funtions are passed test objects. Here, we'll use a setUp function to set the favorite color in @@ -2361,6 +2365,22 @@ def test_trailing_space_in_test(): foo \n """ +class Wrapper: + def __init__(self, func): + self.func = func + functools.update_wrapper(self, func) + + def __call__(self, *args, **kwargs): + self.func(*args, **kwargs) + +@Wrapper +def test_look_in_unwrapped(): + """ + Docstrings in wrapped functions must be detected as well. + + >>> 'one other test' + 'one other test' + """ def test_unittest_reportflags(): """Default unittest reporting flags can be set to control reporting @@ -2927,7 +2947,7 @@ Invalid doctest option: def test_main(): # Check the doctest cases in doctest itself: - support.run_doctest(doctest, verbosity=True) + ret = support.run_doctest(doctest, verbosity=True) # Check the doctest cases defined here: from test import test_doctest support.run_doctest(test_doctest, verbosity=True) diff --git a/Lib/test/test_docxmlrpc.py b/Lib/test/test_docxmlrpc.py index 06161f2..d9fd917 100644 --- a/Lib/test/test_docxmlrpc.py +++ b/Lib/test/test_docxmlrpc.py @@ -87,10 +87,11 @@ class DocXMLRPCHTTPGETServer(unittest.TestCase): threading.Thread(target=server, args=(self.evt, 1)).start() # wait for port to be assigned - n = 1000 - while n > 0 and PORT is None: - time.sleep(0.001) - n -= 1 + deadline = time.monotonic() + 10.0 + while PORT is None: + time.sleep(0.010) + if time.monotonic() > deadline: + break self.client = http.client.HTTPConnection("localhost:%d" % PORT) diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py index c3ecd0ab..d16d461 100644 --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -1636,6 +1636,10 @@ class TestMIMEText(unittest.TestCase): msg = MIMEText('hello there', _charset='us-ascii') eq(msg.get_charset().input_charset, 'us-ascii') eq(msg['content-type'], 'text/plain; charset="us-ascii"') + # Also accept a Charset instance + msg = MIMEText('hello there', _charset=Charset('utf-8')) + eq(msg.get_charset().input_charset, 'utf-8') + eq(msg['content-type'], 'text/plain; charset="utf-8"') def test_7bit_input(self): eq = self.assertEqual diff --git a/Lib/test/test_email/test_message.py b/Lib/test/test_email/test_message.py index 50e1a63..d78049e 100644 --- a/Lib/test/test_email/test_message.py +++ b/Lib/test/test_email/test_message.py @@ -723,24 +723,14 @@ class TestEmailMessageBase: def test_is_attachment(self): m = self._make_message() self.assertFalse(m.is_attachment()) - with self.assertWarns(DeprecationWarning): - self.assertFalse(m.is_attachment) m['Content-Disposition'] = 'inline' self.assertFalse(m.is_attachment()) - with self.assertWarns(DeprecationWarning): - self.assertFalse(m.is_attachment) m.replace_header('Content-Disposition', 'attachment') self.assertTrue(m.is_attachment()) - with self.assertWarns(DeprecationWarning): - self.assertTrue(m.is_attachment) m.replace_header('Content-Disposition', 'AtTachMent') self.assertTrue(m.is_attachment()) - with self.assertWarns(DeprecationWarning): - self.assertTrue(m.is_attachment) m.set_param('filename', 'abc.png', 'Content-Disposition') self.assertTrue(m.is_attachment()) - with self.assertWarns(DeprecationWarning): - self.assertTrue(m.is_attachment) class TestEmailMessage(TestEmailMessageBase, TestEmailBase): diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index dccaa4f..7d172c8 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -646,6 +646,23 @@ class TestEnum(unittest.TestCase): self.assertIn(e, SummerMonth) self.assertIs(type(e), SummerMonth) + def test_programatic_function_string_with_start(self): + SummerMonth = Enum('SummerMonth', 'june july august', start=10) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate('june july august'.split(), 10): + e = SummerMonth(i) + self.assertEqual(int(e.value), i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, SummerMonth) + self.assertIs(type(e), SummerMonth) + def test_programatic_function_string_list(self): SummerMonth = Enum('SummerMonth', ['june', 'july', 'august']) lst = list(SummerMonth) @@ -663,6 +680,23 @@ class TestEnum(unittest.TestCase): self.assertIn(e, SummerMonth) self.assertIs(type(e), SummerMonth) + def test_programatic_function_string_list_with_start(self): + SummerMonth = Enum('SummerMonth', ['june', 'july', 'august'], start=20) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate('june july august'.split(), 20): + e = SummerMonth(i) + self.assertEqual(int(e.value), i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, SummerMonth) + self.assertIs(type(e), SummerMonth) + def test_programatic_function_iterable(self): SummerMonth = Enum( 'SummerMonth', @@ -719,6 +753,22 @@ class TestEnum(unittest.TestCase): self.assertIn(e, SummerMonth) self.assertIs(type(e), SummerMonth) + def test_programatic_function_type_with_start(self): + SummerMonth = Enum('SummerMonth', 'june july august', type=int, start=30) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate('june july august'.split(), 30): + e = SummerMonth(i) + self.assertEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, SummerMonth) + self.assertIs(type(e), SummerMonth) + def test_programatic_function_type_from_subclass(self): SummerMonth = IntEnum('SummerMonth', 'june july august') lst = list(SummerMonth) @@ -735,6 +785,22 @@ class TestEnum(unittest.TestCase): self.assertIn(e, SummerMonth) self.assertIs(type(e), SummerMonth) + def test_programatic_function_type_from_subclass_with_start(self): + SummerMonth = IntEnum('SummerMonth', 'june july august', start=40) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate('june july august'.split(), 40): + e = SummerMonth(i) + self.assertEqual(e, i) + self.assertEqual(e.name, month) + self.assertIn(e, SummerMonth) + self.assertIs(type(e), SummerMonth) + def test_subclassing(self): if isinstance(Name, Exception): raise Name diff --git a/Lib/test/test_epoll.py b/Lib/test/test_epoll.py index b37f033..d727403 100644 --- a/Lib/test/test_epoll.py +++ b/Lib/test/test_epoll.py @@ -44,7 +44,7 @@ class TestEPoll(unittest.TestCase): def setUp(self): self.serverSocket = socket.socket() self.serverSocket.bind(('127.0.0.1', 0)) - self.serverSocket.listen(1) + self.serverSocket.listen() self.connections = [self.serverSocket] def tearDown(self): diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index 8dcefe4..e396054 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -17,6 +17,10 @@ try: HAVE_THREADS = True except ImportError: HAVE_THREADS = False +try: + import _testcapi +except ImportError: + _testcapi = None TIMEOUT = 0.5 @@ -135,26 +139,32 @@ class FaultHandlerTests(unittest.TestCase): 3, 'Floating point exception') - @unittest.skipIf(not hasattr(faulthandler, '_sigbus'), - "need faulthandler._sigbus()") + @unittest.skipIf(_testcapi is None, 'need _testcapi') + @unittest.skipUnless(hasattr(signal, 'SIGBUS'), 'need signal.SIGBUS') def test_sigbus(self): self.check_fatal_error(""" + import _testcapi import faulthandler + import signal + faulthandler.enable() - faulthandler._sigbus() + _testcapi.raise_signal(signal.SIGBUS) """, - 3, + 6, 'Bus error') - @unittest.skipIf(not hasattr(faulthandler, '_sigill'), - "need faulthandler._sigill()") + @unittest.skipIf(_testcapi is None, 'need _testcapi') + @unittest.skipUnless(hasattr(signal, 'SIGILL'), 'need signal.SIGILL') def test_sigill(self): self.check_fatal_error(""" + import _testcapi import faulthandler + import signal + faulthandler.enable() - faulthandler._sigill() + _testcapi.raise_signal(signal.SIGILL) """, - 3, + 6, 'Illegal instruction') def test_fatal_error(self): diff --git a/Lib/test/test_fileio.py b/Lib/test/test_fileio.py index c37482e..743ca5c 100644 --- a/Lib/test/test_fileio.py +++ b/Lib/test/test_fileio.py @@ -60,6 +60,15 @@ class AutoFileTests(unittest.TestCase): self.assertRaises((AttributeError, TypeError), setattr, f, attr, 'oops') + def testBlksize(self): + # test private _blksize attribute + blksize = io.DEFAULT_BUFFER_SIZE + # try to get preferred blksize from stat.st_blksize, if available + if hasattr(os, 'fstat'): + fst = os.fstat(self.f.fileno()) + blksize = getattr(fst, 'st_blksize', blksize) + self.assertEqual(self.f._blksize, blksize) + def testReadinto(self): # verify readinto self.f.write(bytes([1, 2])) @@ -103,14 +112,26 @@ class AutoFileTests(unittest.TestCase): self.assertRaises(TypeError, self.f.write, "Hello!") def testRepr(self): - self.assertEqual(repr(self.f), "<_io.FileIO name=%r mode=%r>" - % (self.f.name, self.f.mode)) + self.assertEqual( + repr(self.f), "<_io.FileIO name=%r mode=%r closefd=True>" + % (self.f.name, self.f.mode)) del self.f.name - self.assertEqual(repr(self.f), "<_io.FileIO fd=%r mode=%r>" - % (self.f.fileno(), self.f.mode)) + self.assertEqual( + repr(self.f), "<_io.FileIO fd=%r mode=%r closefd=True>" + % (self.f.fileno(), self.f.mode)) self.f.close() self.assertEqual(repr(self.f), "<_io.FileIO [closed]>") + def testReprNoCloseFD(self): + fd = os.open(TESTFN, os.O_RDONLY) + try: + with _FileIO(fd, 'r', closefd=False) as f: + self.assertEqual(repr(f), + "<_io.FileIO name=%r mode=%r closefd=False>" + % (f.name, f.mode)) + finally: + os.close(fd) + def testErrors(self): f = self.f self.assertTrue(not f.isatty()) @@ -141,7 +162,7 @@ class AutoFileTests(unittest.TestCase): def testOpendir(self): # Issue 3703: opening a directory should fill the errno # Windows always returns "[Errno 13]: Permission denied - # Unix calls dircheck() and returns "[Errno 21]: Is a directory" + # Unix uses fstat and returns "[Errno 21]: Is a directory" try: _FileIO('.', 'r') except OSError as e: @@ -352,8 +373,8 @@ class OtherFileTests(unittest.TestCase): def testConstructorHandlesNULChars(self): fn_with_NUL = 'foo\0bar' - self.assertRaises(TypeError, _FileIO, fn_with_NUL, 'w') - self.assertRaises(TypeError, _FileIO, bytes(fn_with_NUL, 'ascii'), 'w') + self.assertRaises(ValueError, _FileIO, fn_with_NUL, 'w') + self.assertRaises(ValueError, _FileIO, bytes(fn_with_NUL, 'ascii'), 'w') def testInvalidFd(self): self.assertRaises(ValueError, _FileIO, -10) diff --git a/Lib/test/test_fork1.py b/Lib/test/test_fork1.py index e0626df..8bcbd46 100644 --- a/Lib/test/test_fork1.py +++ b/Lib/test/test_fork1.py @@ -18,13 +18,14 @@ get_attribute(os, 'fork') class ForkTest(ForkWait): def wait_impl(self, cpid): - for i in range(10): + deadline = time.monotonic() + 10.0 + while time.monotonic() <= deadline: # waitpid() shouldn't hang, but some of the buildbots seem to hang # in the forking tests. This is an attempt to fix the problem. spid, status = os.waitpid(cpid, os.WNOHANG) if spid == cpid: break - time.sleep(1.0) + time.sleep(0.1) self.assertEqual(spid, cpid) self.assertEqual(status, 0, "cause = %d, exit = %d" % (status&0xff, status>>8)) diff --git a/Lib/test/test_format.py b/Lib/test/test_format.py index fc71e48..631bf35 100644 --- a/Lib/test/test_format.py +++ b/Lib/test/test_format.py @@ -142,8 +142,6 @@ class FormatTest(unittest.TestCase): testformat("%#+027.23X", big, "+0X0001234567890ABCDEF12345") # same, except no 0 flag testformat("%#+27.23X", big, " +0X001234567890ABCDEF12345") - with self.assertWarns(DeprecationWarning): - testformat("%x", float(big), "123456_______________", 6) big = 0o12345670123456701234567012345670 # 32 octal digits testformat("%o", big, "12345670123456701234567012345670") testformat("%o", -big, "-12345670123456701234567012345670") @@ -183,8 +181,6 @@ class FormatTest(unittest.TestCase): testformat("%034.33o", big, "0012345670123456701234567012345670") # base marker shouldn't change that testformat("%0#34.33o", big, "0o012345670123456701234567012345670") - with self.assertWarns(DeprecationWarning): - testformat("%o", float(big), "123456__________________________", 6) # Some small ints, in both Python int and flavors). testformat("%d", 42, "42") testformat("%d", -42, "-42") @@ -195,8 +191,6 @@ class FormatTest(unittest.TestCase): testformat("%#x", 1, "0x1") testformat("%#X", 1, "0X1") testformat("%#X", 1, "0X1") - with self.assertWarns(DeprecationWarning): - testformat("%#x", 1.0, "0x1") testformat("%#o", 1, "0o1") testformat("%#o", 1, "0o1") testformat("%#o", 0, "0o0") @@ -213,14 +207,10 @@ class FormatTest(unittest.TestCase): testformat("%x", -0x42, "-42") testformat("%x", 0x42, "42") testformat("%x", -0x42, "-42") - with self.assertWarns(DeprecationWarning): - testformat("%x", float(0x42), "42") testformat("%o", 0o42, "42") testformat("%o", -0o42, "-42") testformat("%o", 0o42, "42") testformat("%o", -0o42, "-42") - with self.assertWarns(DeprecationWarning): - testformat("%o", float(0o42), "42") testformat("%r", "\u0378", "'\\u0378'") # non printable testformat("%a", "\u0378", "'\\u0378'") # non printable testformat("%r", "\u0374", "'\u0374'") # printable diff --git a/Lib/test/test_fractions.py b/Lib/test/test_fractions.py index 3336532..e86d5ce 100644 --- a/Lib/test/test_fractions.py +++ b/Lib/test/test_fractions.py @@ -330,7 +330,6 @@ class FractionTest(unittest.TestCase): self.assertTypedEquals(F(-2, 10), round(F(-15, 100), 1)) self.assertTypedEquals(F(-2, 10), round(F(-25, 100), 1)) - def testArithmetic(self): self.assertEqual(F(1, 2), F(1, 10) + F(2, 5)) self.assertEqual(F(-3, 10), F(1, 10) - F(2, 5)) @@ -402,6 +401,8 @@ class FractionTest(unittest.TestCase): self.assertTypedEquals(2.0 , 4 ** F(1, 2)) self.assertTypedEquals(0.25, 2.0 ** F(-2, 1)) self.assertTypedEquals(1.0 + 0j, (1.0 + 0j) ** F(1, 10)) + self.assertRaises(ZeroDivisionError, operator.pow, + F(0, 1), -2) def testMixingWithDecimal(self): # Decimal refuses mixed arithmetic (but not mixed comparisons) diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py index d3be7d6..aef66da 100644 --- a/Lib/test/test_ftplib.py +++ b/Lib/test/test_ftplib.py @@ -73,7 +73,7 @@ class DummyDTPHandler(asynchat.async_chat): super(DummyDTPHandler, self).push(what.encode('ascii')) def handle_error(self): - raise + raise Exception class DummyFTPHandler(asynchat.async_chat): @@ -118,7 +118,7 @@ class DummyFTPHandler(asynchat.async_chat): self.push('550 command "%s" not understood.' %cmd) def handle_error(self): - raise + raise Exception def push(self, data): asynchat.async_chat.push(self, data.encode('ascii') + b'\r\n') @@ -134,7 +134,7 @@ class DummyFTPHandler(asynchat.async_chat): def cmd_pasv(self, arg): with socket.socket() as sock: sock.bind((self.socket.getsockname()[0], 0)) - sock.listen(5) + sock.listen() sock.settimeout(TIMEOUT) ip, port = sock.getsockname()[:2] ip = ip.replace('.', ','); p1 = port / 256; p2 = port % 256 @@ -152,7 +152,7 @@ class DummyFTPHandler(asynchat.async_chat): def cmd_epsv(self, arg): with socket.socket(socket.AF_INET6) as sock: sock.bind((self.socket.getsockname()[0], 0)) - sock.listen(5) + sock.listen() sock.settimeout(TIMEOUT) port = sock.getsockname()[1] self.push('229 entering extended passive mode (|||%d|)' %port) @@ -296,7 +296,7 @@ class DummyFTPServer(asyncore.dispatcher, threading.Thread): return 0 def handle_error(self): - raise + raise Exception if ssl is not None: @@ -394,7 +394,7 @@ if ssl is not None: raise def handle_error(self): - raise + raise Exception def close(self): if (isinstance(self.socket, ssl.SSLSocket) and @@ -670,7 +670,7 @@ class TestFTPClass(TestCase): self.assertRaises(StopIteration, next, self.client.mlsd()) set_data('') for x in self.client.mlsd(): - self.fail("unexpected data %s" % data) + self.fail("unexpected data %s" % x) def test_makeport(self): with self.client.makeport(): @@ -979,7 +979,7 @@ class TestTimeouts(TestCase): # 1) when the connection is ready to be accepted. # 2) when it is safe for the caller to close the connection # 3) when we have closed the socket - self.sock.listen(5) + self.sock.listen() # (1) Signal the caller that we are ready to accept the connection. self.evt.set() try: @@ -1049,19 +1049,8 @@ class TestTimeouts(TestCase): ftp.close() -class TestNetrcDeprecation(TestCase): - - def test_deprecation(self): - with support.temp_cwd(), support.EnvironmentVarGuard() as env: - env['HOME'] = os.getcwd() - open('.netrc', 'w').close() - with self.assertWarns(DeprecationWarning): - ftplib.Netrc() - - - def test_main(): - tests = [TestFTPClass, TestTimeouts, TestNetrcDeprecation, + tests = [TestFTPClass, TestTimeouts, TestIPv6Environment, TestTLS_FTPClassMixin, TestTLS_FTPClass] diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index 1012053..fbb43e4 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -880,6 +880,24 @@ class TestTotalOrdering(unittest.TestCase): with self.assertRaises(TypeError): a <= b + def test_pickle(self): + for proto in range(4, pickle.HIGHEST_PROTOCOL + 1): + for name in '__lt__', '__gt__', '__le__', '__ge__': + with self.subTest(method=name, proto=proto): + method = getattr(Orderable_LT, name) + method_copy = pickle.loads(pickle.dumps(method, proto)) + self.assertIs(method_copy, method) + +@functools.total_ordering +class Orderable_LT: + def __init__(self, value): + self.value = value + def __lt__(self, other): + return self.value < other.value + def __eq__(self, other): + return self.value == other.value + + class TestLRU(unittest.TestCase): def test_lru(self): diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index 91afe47..3882f4c 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -50,6 +50,45 @@ class FinalizationTest(unittest.TestCase): self.assertEqual(gc.garbage, old_garbage) +class GeneratorTest(unittest.TestCase): + + def test_name(self): + def func(): + yield 1 + + # check generator names + gen = func() + self.assertEqual(gen.__name__, "func") + self.assertEqual(gen.__qualname__, + "GeneratorTest.test_name.<locals>.func") + + # modify generator names + gen.__name__ = "name" + gen.__qualname__ = "qualname" + self.assertEqual(gen.__name__, "name") + self.assertEqual(gen.__qualname__, "qualname") + + # generator names must be a string and cannot be deleted + self.assertRaises(TypeError, setattr, gen, '__name__', 123) + self.assertRaises(TypeError, setattr, gen, '__qualname__', 123) + self.assertRaises(TypeError, delattr, gen, '__name__') + self.assertRaises(TypeError, delattr, gen, '__qualname__') + + # modify names of the function creating the generator + func.__qualname__ = "func_qualname" + func.__name__ = "func_name" + gen = func() + self.assertEqual(gen.__name__, "func_name") + self.assertEqual(gen.__qualname__, "func_qualname") + + # unnamed generator + gen = (x for x in range(10)) + self.assertEqual(gen.__name__, + "<genexpr>") + self.assertEqual(gen.__qualname__, + "GeneratorTest.test_name.<locals>.<genexpr>") + + tutorial_tests = """ Let's try a simple generator: diff --git a/Lib/test/test_genericpath.py b/Lib/test/test_genericpath.py index e59ed4d..f2722bc 100644 --- a/Lib/test/test_genericpath.py +++ b/Lib/test/test_genericpath.py @@ -434,6 +434,40 @@ class CommonTest(GenericTest): with support.temp_cwd(name): self.test_abspath() + def test_join_errors(self): + # Check join() raises friendly TypeErrors. + with support.check_warnings(('', BytesWarning), quiet=True): + errmsg = "Can't mix strings and bytes in path components" + with self.assertRaisesRegex(TypeError, errmsg): + self.pathmodule.join(b'bytes', 'str') + with self.assertRaisesRegex(TypeError, errmsg): + self.pathmodule.join('str', b'bytes') + # regression, see #15377 + errmsg = r'join\(\) argument must be str or bytes, not %r' + with self.assertRaisesRegex(TypeError, errmsg % 'int'): + self.pathmodule.join(42, 'str') + with self.assertRaisesRegex(TypeError, errmsg % 'int'): + self.pathmodule.join('str', 42) + with self.assertRaisesRegex(TypeError, errmsg % 'bytearray'): + self.pathmodule.join(bytearray(b'foo'), bytearray(b'bar')) + + def test_relpath_errors(self): + # Check relpath() raises friendly TypeErrors. + with support.check_warnings(('', (BytesWarning, DeprecationWarning)), + quiet=True): + errmsg = "Can't mix strings and bytes in path components" + with self.assertRaisesRegex(TypeError, errmsg): + self.pathmodule.relpath(b'bytes', 'str') + with self.assertRaisesRegex(TypeError, errmsg): + self.pathmodule.relpath('str', b'bytes') + errmsg = r'relpath\(\) argument must be str or bytes, not %r' + with self.assertRaisesRegex(TypeError, errmsg % 'int'): + self.pathmodule.relpath(42, 'str') + with self.assertRaisesRegex(TypeError, errmsg % 'int'): + self.pathmodule.relpath('str', 42) + with self.assertRaisesRegex(TypeError, errmsg % 'bytearray'): + self.pathmodule.relpath(bytearray(b'foo'), bytearray(b'bar')) + if __name__=="__main__": unittest.main() diff --git a/Lib/test/test_getargs2.py b/Lib/test/test_getargs2.py index 1853a2d..cf1279d 100644 --- a/Lib/test/test_getargs2.py +++ b/Lib/test/test_getargs2.py @@ -482,7 +482,7 @@ class Bytes_TestCase(unittest.TestCase): def test_s(self): from _testcapi import getargs_s self.assertEqual(getargs_s('abc\xe9'), b'abc\xc3\xa9') - self.assertRaises(TypeError, getargs_s, 'nul:\0') + self.assertRaises(ValueError, getargs_s, 'nul:\0') self.assertRaises(TypeError, getargs_s, b'bytes') self.assertRaises(TypeError, getargs_s, bytearray(b'bytearray')) self.assertRaises(TypeError, getargs_s, memoryview(b'memoryview')) @@ -509,7 +509,7 @@ class Bytes_TestCase(unittest.TestCase): def test_z(self): from _testcapi import getargs_z self.assertEqual(getargs_z('abc\xe9'), b'abc\xc3\xa9') - self.assertRaises(TypeError, getargs_z, 'nul:\0') + self.assertRaises(ValueError, getargs_z, 'nul:\0') self.assertRaises(TypeError, getargs_z, b'bytes') self.assertRaises(TypeError, getargs_z, bytearray(b'bytearray')) self.assertRaises(TypeError, getargs_z, memoryview(b'memoryview')) @@ -537,7 +537,7 @@ class Bytes_TestCase(unittest.TestCase): from _testcapi import getargs_y self.assertRaises(TypeError, getargs_y, 'abc\xe9') self.assertEqual(getargs_y(b'bytes'), b'bytes') - self.assertRaises(TypeError, getargs_y, b'nul:\0') + self.assertRaises(ValueError, getargs_y, b'nul:\0') self.assertRaises(TypeError, getargs_y, bytearray(b'bytearray')) self.assertRaises(TypeError, getargs_y, memoryview(b'memoryview')) self.assertRaises(TypeError, getargs_y, None) @@ -577,7 +577,7 @@ class Unicode_TestCase(unittest.TestCase): def test_u(self): from _testcapi import getargs_u self.assertEqual(getargs_u('abc\xe9'), 'abc\xe9') - self.assertRaises(TypeError, getargs_u, 'nul:\0') + self.assertRaises(ValueError, getargs_u, 'nul:\0') self.assertRaises(TypeError, getargs_u, b'bytes') self.assertRaises(TypeError, getargs_u, bytearray(b'bytearray')) self.assertRaises(TypeError, getargs_u, memoryview(b'memoryview')) @@ -595,7 +595,7 @@ class Unicode_TestCase(unittest.TestCase): def test_Z(self): from _testcapi import getargs_Z self.assertEqual(getargs_Z('abc\xe9'), 'abc\xe9') - self.assertRaises(TypeError, getargs_Z, 'nul:\0') + self.assertRaises(ValueError, getargs_Z, 'nul:\0') self.assertRaises(TypeError, getargs_Z, b'bytes') self.assertRaises(TypeError, getargs_Z, bytearray(b'bytearray')) self.assertRaises(TypeError, getargs_Z, memoryview(b'memoryview')) diff --git a/Lib/test/test_gettext.py b/Lib/test/test_gettext.py index abb312f..c7ceb3f 100644 --- a/Lib/test/test_gettext.py +++ b/Lib/test/test_gettext.py @@ -33,6 +33,55 @@ IHNiZSBsYmhlIENsZ3ViYSBjZWJ0ZW56ZiBvbCBjZWJpdnF2YXQgbmEgdmFncmVzbnByIGdiIGd1 ciBUQUgKdHJnZ3JrZyB6cmZmbnRyIHBuZ255YnQgeXZvZW5lbC4AYmFjb24Ad2luayB3aW5rAA== ''' +# This data contains an invalid major version number (5) +# An unexpected major version number should be treated as an error when +# parsing a .mo file + +GNU_MO_DATA_BAD_MAJOR_VERSION = b'''\ +3hIElQAABQAGAAAAHAAAAEwAAAALAAAAfAAAAAAAAACoAAAAFQAAAKkAAAAjAAAAvwAAAKEAAADj +AAAABwAAAIUBAAALAAAAjQEAAEUBAACZAQAAFgAAAN8CAAAeAAAA9gIAAKEAAAAVAwAABQAAALcD +AAAJAAAAvQMAAAEAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABQAAAAYAAAACAAAAAFJh +eW1vbmQgTHV4dXJ5IFlhY2gtdABUaGVyZSBpcyAlcyBmaWxlAFRoZXJlIGFyZSAlcyBmaWxlcwBU +aGlzIG1vZHVsZSBwcm92aWRlcyBpbnRlcm5hdGlvbmFsaXphdGlvbiBhbmQgbG9jYWxpemF0aW9u +CnN1cHBvcnQgZm9yIHlvdXIgUHl0aG9uIHByb2dyYW1zIGJ5IHByb3ZpZGluZyBhbiBpbnRlcmZh +Y2UgdG8gdGhlIEdOVQpnZXR0ZXh0IG1lc3NhZ2UgY2F0YWxvZyBsaWJyYXJ5LgBtdWxsdXNrAG51 +ZGdlIG51ZGdlAFByb2plY3QtSWQtVmVyc2lvbjogMi4wClBPLVJldmlzaW9uLURhdGU6IDIwMDAt +MDgtMjkgMTI6MTktMDQ6MDAKTGFzdC1UcmFuc2xhdG9yOiBKLiBEYXZpZCBJYsOhw7FleiA8ai1k +YXZpZEBub29zLmZyPgpMYW5ndWFnZS1UZWFtOiBYWCA8cHl0aG9uLWRldkBweXRob24ub3JnPgpN +SU1FLVZlcnNpb246IDEuMApDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9aXNvLTg4 +NTktMQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBub25lCkdlbmVyYXRlZC1CeTogcHlnZXR0 +ZXh0LnB5IDEuMQpQbHVyYWwtRm9ybXM6IG5wbHVyYWxzPTI7IHBsdXJhbD1uIT0xOwoAVGhyb2F0 +d29iYmxlciBNYW5ncm92ZQBIYXkgJXMgZmljaGVybwBIYXkgJXMgZmljaGVyb3MAR3V2ZiB6YnFo +eXIgY2ViaXZxcmYgdmFncmVhbmd2YmFueXZtbmd2YmEgbmFxIHlicG55dm1uZ3ZiYQpmaGNjYmVn +IHNiZSBsYmhlIENsZ3ViYSBjZWJ0ZW56ZiBvbCBjZWJpdnF2YXQgbmEgdmFncmVzbnByIGdiIGd1 +ciBUQUgKdHJnZ3JrZyB6cmZmbnRyIHBuZ255YnQgeXZvZW5lbC4AYmFjb24Ad2luayB3aW5rAA== +''' + +# This data contains an invalid minor version number (7) +# An unexpected minor version number only indicates that some of the file's +# contents may not be able to be read. It does not indicate an error. + +GNU_MO_DATA_BAD_MINOR_VERSION = b'''\ +3hIElQcAAAAGAAAAHAAAAEwAAAALAAAAfAAAAAAAAACoAAAAFQAAAKkAAAAjAAAAvwAAAKEAAADj +AAAABwAAAIUBAAALAAAAjQEAAEUBAACZAQAAFgAAAN8CAAAeAAAA9gIAAKEAAAAVAwAABQAAALcD +AAAJAAAAvQMAAAEAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABQAAAAYAAAACAAAAAFJh +eW1vbmQgTHV4dXJ5IFlhY2gtdABUaGVyZSBpcyAlcyBmaWxlAFRoZXJlIGFyZSAlcyBmaWxlcwBU +aGlzIG1vZHVsZSBwcm92aWRlcyBpbnRlcm5hdGlvbmFsaXphdGlvbiBhbmQgbG9jYWxpemF0aW9u +CnN1cHBvcnQgZm9yIHlvdXIgUHl0aG9uIHByb2dyYW1zIGJ5IHByb3ZpZGluZyBhbiBpbnRlcmZh +Y2UgdG8gdGhlIEdOVQpnZXR0ZXh0IG1lc3NhZ2UgY2F0YWxvZyBsaWJyYXJ5LgBtdWxsdXNrAG51 +ZGdlIG51ZGdlAFByb2plY3QtSWQtVmVyc2lvbjogMi4wClBPLVJldmlzaW9uLURhdGU6IDIwMDAt +MDgtMjkgMTI6MTktMDQ6MDAKTGFzdC1UcmFuc2xhdG9yOiBKLiBEYXZpZCBJYsOhw7FleiA8ai1k +YXZpZEBub29zLmZyPgpMYW5ndWFnZS1UZWFtOiBYWCA8cHl0aG9uLWRldkBweXRob24ub3JnPgpN +SU1FLVZlcnNpb246IDEuMApDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9aXNvLTg4 +NTktMQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBub25lCkdlbmVyYXRlZC1CeTogcHlnZXR0 +ZXh0LnB5IDEuMQpQbHVyYWwtRm9ybXM6IG5wbHVyYWxzPTI7IHBsdXJhbD1uIT0xOwoAVGhyb2F0 +d29iYmxlciBNYW5ncm92ZQBIYXkgJXMgZmljaGVybwBIYXkgJXMgZmljaGVyb3MAR3V2ZiB6YnFo +eXIgY2ViaXZxcmYgdmFncmVhbmd2YmFueXZtbmd2YmEgbmFxIHlicG55dm1uZ3ZiYQpmaGNjYmVn +IHNiZSBsYmhlIENsZ3ViYSBjZWJ0ZW56ZiBvbCBjZWJpdnF2YXQgbmEgdmFncmVzbnByIGdiIGd1 +ciBUQUgKdHJnZ3JrZyB6cmZmbnRyIHBuZ255YnQgeXZvZW5lbC4AYmFjb24Ad2luayB3aW5rAA== +''' + + UMO_DATA = b'''\ 3hIElQAAAAACAAAAHAAAACwAAAAFAAAAPAAAAAAAAABQAAAABAAAAFEAAAAPAQAAVgAAAAQAAABm AQAAAQAAAAIAAAAAAAAAAAAAAAAAAAAAYWLDngBQcm9qZWN0LUlkLVZlcnNpb246IDIuMApQTy1S @@ -56,6 +105,8 @@ bGUKR2VuZXJhdGVkLUJ5OiBweWdldHRleHQucHkgMS4zCgA= LOCALEDIR = os.path.join('xx', 'LC_MESSAGES') MOFILE = os.path.join(LOCALEDIR, 'gettext.mo') +MOFILE_BAD_MAJOR_VERSION = os.path.join(LOCALEDIR, 'gettext_bad_major_version.mo') +MOFILE_BAD_MINOR_VERSION = os.path.join(LOCALEDIR, 'gettext_bad_minor_version.mo') UMOFILE = os.path.join(LOCALEDIR, 'ugettext.mo') MMOFILE = os.path.join(LOCALEDIR, 'metadata.mo') @@ -66,6 +117,10 @@ class GettextBaseTest(unittest.TestCase): os.makedirs(LOCALEDIR) with open(MOFILE, 'wb') as fp: fp.write(base64.decodebytes(GNU_MO_DATA)) + with open(MOFILE_BAD_MAJOR_VERSION, 'wb') as fp: + fp.write(base64.decodebytes(GNU_MO_DATA_BAD_MAJOR_VERSION)) + with open(MOFILE_BAD_MINOR_VERSION, 'wb') as fp: + fp.write(base64.decodebytes(GNU_MO_DATA_BAD_MINOR_VERSION)) with open(UMOFILE, 'wb') as fp: fp.write(base64.decodebytes(UMO_DATA)) with open(MMOFILE, 'wb') as fp: @@ -166,6 +221,21 @@ class GettextTestCase2(GettextBaseTest): def test_textdomain(self): self.assertEqual(gettext.textdomain(), 'gettext') + def test_bad_major_version(self): + with open(MOFILE_BAD_MAJOR_VERSION, 'rb') as fp: + with self.assertRaises(OSError) as cm: + gettext.GNUTranslations(fp) + + exception = cm.exception + self.assertEqual(exception.errno, 0) + self.assertEqual(exception.strerror, "Bad version number 5") + self.assertEqual(exception.filename, MOFILE_BAD_MAJOR_VERSION) + + def test_bad_minor_version(self): + with open(MOFILE_BAD_MINOR_VERSION, 'rb') as fp: + # Check that no error is thrown with a bad minor version number + gettext.GNUTranslations(fp) + def test_some_translations(self): eq = self.assertEqual # test some translations diff --git a/Lib/test/test_glob.py b/Lib/test/test_glob.py index a5ab8d6..21b0153 100644 --- a/Lib/test/test_glob.py +++ b/Lib/test/test_glob.py @@ -4,8 +4,8 @@ import shutil import sys import unittest -from test.support import (run_unittest, TESTFN, skip_unless_symlink, - can_symlink, create_empty_file) +from test.support import (TESTFN, skip_unless_symlink, + can_symlink, create_empty_file, change_cwd) class GlobTests(unittest.TestCase): @@ -13,6 +13,9 @@ class GlobTests(unittest.TestCase): def norm(self, *parts): return os.path.normpath(os.path.join(self.tempdir, *parts)) + def joins(self, *tuples): + return [os.path.join(self.tempdir, *parts) for parts in tuples] + def mktemp(self, *parts): filename = self.norm(*parts) base, file = os.path.split(filename) @@ -38,17 +41,17 @@ class GlobTests(unittest.TestCase): def tearDown(self): shutil.rmtree(self.tempdir) - def glob(self, *parts): + def glob(self, *parts, **kwargs): if len(parts) == 1: pattern = parts[0] else: pattern = os.path.join(*parts) p = os.path.join(self.tempdir, pattern) - res = glob.glob(p) - self.assertEqual(list(glob.iglob(p)), res) + res = glob.glob(p, **kwargs) + self.assertEqual(list(glob.iglob(p, **kwargs)), res) bres = [os.fsencode(x) for x in res] - self.assertEqual(glob.glob(os.fsencode(p)), bres) - self.assertEqual(list(glob.iglob(os.fsencode(p))), bres) + self.assertEqual(glob.glob(os.fsencode(p), **kwargs), bres) + self.assertEqual(list(glob.iglob(os.fsencode(p), **kwargs)), bres) return res def assertSequencesEqual_noorder(self, l1, l2): @@ -192,9 +195,118 @@ class GlobTests(unittest.TestCase): check('//?/c:/?', '//?/c:/[?]') check('//*/*/*', '//*/*/[*]') -def test_main(): - run_unittest(GlobTests) + def rglob(self, *parts, **kwargs): + return self.glob(*parts, recursive=True, **kwargs) + + def test_recursive_glob(self): + eq = self.assertSequencesEqual_noorder + full = [('ZZZ',), + ('a',), ('a', 'D'), + ('a', 'bcd'), + ('a', 'bcd', 'EF'), + ('a', 'bcd', 'efg'), + ('a', 'bcd', 'efg', 'ha'), + ('aaa',), ('aaa', 'zzzF'), + ('aab',), ('aab', 'F'), + ] + if can_symlink(): + full += [('sym1',), ('sym2',), + ('sym3',), + ('sym3', 'EF'), + ('sym3', 'efg'), + ('sym3', 'efg', 'ha'), + ] + eq(self.rglob('**'), self.joins(('',), *full)) + eq(self.rglob('.', '**'), self.joins(('.',''), + *(('.',) + i for i in full))) + dirs = [('a', ''), ('a', 'bcd', ''), ('a', 'bcd', 'efg', ''), + ('aaa', ''), ('aab', '')] + if can_symlink(): + dirs += [('sym3', ''), ('sym3', 'efg', '')] + eq(self.rglob('**', ''), self.joins(('',), *dirs)) + + eq(self.rglob('a', '**'), self.joins( + ('a', ''), ('a', 'D'), ('a', 'bcd'), ('a', 'bcd', 'EF'), + ('a', 'bcd', 'efg'), ('a', 'bcd', 'efg', 'ha'))) + eq(self.rglob('a**'), self.joins(('a',), ('aaa',), ('aab',))) + expect = [('a', 'bcd', 'EF')] + if can_symlink(): + expect += [('sym3', 'EF')] + eq(self.rglob('**', 'EF'), self.joins(*expect)) + expect = [('a', 'bcd', 'EF'), ('aaa', 'zzzF'), ('aab', 'F')] + if can_symlink(): + expect += [('sym3', 'EF')] + eq(self.rglob('**', '*F'), self.joins(*expect)) + eq(self.rglob('**', '*F', ''), []) + eq(self.rglob('**', 'bcd', '*'), self.joins( + ('a', 'bcd', 'EF'), ('a', 'bcd', 'efg'))) + eq(self.rglob('a', '**', 'bcd'), self.joins(('a', 'bcd'))) + + predir = os.path.abspath(os.curdir) + try: + os.chdir(self.tempdir) + join = os.path.join + eq(glob.glob('**', recursive=True), [join(*i) for i in full]) + eq(glob.glob(join('**', ''), recursive=True), + [join(*i) for i in dirs]) + eq(glob.glob(join('**','zz*F'), recursive=True), + [join('aaa', 'zzzF')]) + eq(glob.glob('**zz*F', recursive=True), []) + expect = [join('a', 'bcd', 'EF')] + if can_symlink(): + expect += [join('sym3', 'EF')] + eq(glob.glob(join('**', 'EF'), recursive=True), expect) + finally: + os.chdir(predir) + + +@skip_unless_symlink +class SymlinkLoopGlobTests(unittest.TestCase): + + def test_selflink(self): + tempdir = TESTFN + "_dir" + os.makedirs(tempdir) + self.addCleanup(shutil.rmtree, tempdir) + with change_cwd(tempdir): + os.makedirs('dir') + create_empty_file(os.path.join('dir', 'file')) + os.symlink(os.curdir, os.path.join('dir', 'link')) + + results = glob.glob('**', recursive=True) + self.assertEqual(len(results), len(set(results))) + results = set(results) + depth = 0 + while results: + path = os.path.join(*(['dir'] + ['link'] * depth)) + self.assertIn(path, results) + results.remove(path) + if not results: + break + path = os.path.join(path, 'file') + self.assertIn(path, results) + results.remove(path) + depth += 1 + + results = glob.glob(os.path.join('**', 'file'), recursive=True) + self.assertEqual(len(results), len(set(results))) + results = set(results) + depth = 0 + while results: + path = os.path.join(*(['dir'] + ['link'] * depth + ['file'])) + self.assertIn(path, results) + results.remove(path) + depth += 1 + + results = glob.glob(os.path.join('**', ''), recursive=True) + self.assertEqual(len(results), len(set(results))) + results = set(results) + depth = 0 + while results: + path = os.path.join(*(['dir'] + ['link'] * depth + [''])) + self.assertIn(path, results) + results.remove(path) + depth += 1 if __name__ == "__main__": - test_main() + unittest.main() diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index 70d85b1..7069fb9 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -1016,6 +1016,20 @@ class GrammarTests(unittest.TestCase): self.assertFalse((False is 2) is 3) self.assertFalse(False is 2 is 3) + def test_matrix_mul(self): + # This is not intended to be a comprehensive test, rather just to be few + # samples of the @ operator in test_grammar.py. + class M: + def __matmul__(self, o): + return 4 + def __imatmul__(self, o): + self.other = o + return self + m = M() + self.assertEqual(m @ m, 4) + m @= 42 + self.assertEqual(m.other, 42) + def test_main(): run_unittest(TokenTests, GrammarTests) diff --git a/Lib/test/test_heapq.py b/Lib/test/test_heapq.py index b5a2fd8..0dcd8c5 100644 --- a/Lib/test/test_heapq.py +++ b/Lib/test/test_heapq.py @@ -6,14 +6,15 @@ import unittest from test import support from unittest import TestCase, skipUnless +from operator import itemgetter py_heapq = support.import_fresh_module('heapq', blocked=['_heapq']) c_heapq = support.import_fresh_module('heapq', fresh=['_heapq']) # _heapq.nlargest/nsmallest are saved in heapq._nlargest/_smallest when # _heapq is imported, so check them there -func_names = ['heapify', 'heappop', 'heappush', 'heappushpop', - 'heapreplace', '_nlargest', '_nsmallest'] +func_names = ['heapify', 'heappop', 'heappush', 'heappushpop', 'heapreplace', + '_heappop_max', '_heapreplace_max', '_heapify_max'] class TestModules(TestCase): def test_py_functions(self): @@ -152,11 +153,21 @@ class TestHeap: def test_merge(self): inputs = [] - for i in range(random.randrange(5)): - row = sorted(random.randrange(1000) for j in range(random.randrange(10))) + for i in range(random.randrange(25)): + row = [] + for j in range(random.randrange(100)): + tup = random.choice('ABC'), random.randrange(-500, 500) + row.append(tup) inputs.append(row) - self.assertEqual(sorted(chain(*inputs)), list(self.module.merge(*inputs))) - self.assertEqual(list(self.module.merge()), []) + + for key in [None, itemgetter(0), itemgetter(1), itemgetter(1, 0)]: + for reverse in [False, True]: + seqs = [] + for seq in inputs: + seqs.append(sorted(seq, key=key, reverse=reverse)) + self.assertEqual(sorted(chain(*inputs), key=key, reverse=reverse), + list(self.module.merge(*seqs, key=key, reverse=reverse))) + self.assertEqual(list(self.module.merge()), []) def test_merge_does_not_suppress_index_error(self): # Issue 19018: Heapq.merge suppresses IndexError from user generator diff --git a/Lib/test/test_htmlparser.py b/Lib/test/test_htmlparser.py index 2d771a2..de8f3e8 100644 --- a/Lib/test/test_htmlparser.py +++ b/Lib/test/test_htmlparser.py @@ -85,7 +85,7 @@ class EventCollectorCharrefs(EventCollector): class TestCaseBase(unittest.TestCase): def get_collector(self): - raise NotImplementedError + return EventCollector(convert_charrefs=False) def _run_check(self, source, expected_events, collector=None): if collector is None: @@ -105,21 +105,8 @@ class TestCaseBase(unittest.TestCase): self._run_check(source, events, EventCollectorExtra(convert_charrefs=False)) - def _parse_error(self, source): - def parse(source=source): - parser = self.get_collector() - parser.feed(source) - parser.close() - with self.assertRaises(html.parser.HTMLParseError): - with self.assertWarns(DeprecationWarning): - parse() - -class HTMLParserStrictTestCase(TestCaseBase): - - def get_collector(self): - with support.check_warnings(("", DeprecationWarning), quite=False): - return EventCollector(strict=True, convert_charrefs=False) +class HTMLParserTestCase(TestCaseBase): def test_processing_instruction_only(self): self._run_check("<?processing instruction>", [ @@ -201,9 +188,6 @@ text ("data", "this < text > contains < bare>pointy< brackets"), ]) - def test_illegal_declarations(self): - self._parse_error('<!spacer type="block" height="25">') - def test_starttag_end_boundary(self): self._run_check("""<a b='<'>""", [("starttag", "a", [("b", "<")])]) self._run_check("""<a b='>'>""", [("starttag", "a", [("b", ">")])]) @@ -238,25 +222,6 @@ text self._run_check(["<!--abc--", ">"], output) self._run_check(["<!--abc-->", ""], output) - def test_starttag_junk_chars(self): - self._parse_error("</>") - self._parse_error("</$>") - self._parse_error("</") - self._parse_error("</a") - self._parse_error("<a<a>") - self._parse_error("</a<a>") - self._parse_error("<!") - self._parse_error("<a") - self._parse_error("<a foo='bar'") - self._parse_error("<a foo='bar") - self._parse_error("<a foo='>'") - self._parse_error("<a foo='>") - self._parse_error("<a$>") - self._parse_error("<a$b>") - self._parse_error("<a$b/>") - self._parse_error("<a$b >") - self._parse_error("<a$b />") - def test_valid_doctypes(self): # from http://www.w3.org/QA/2002/04/valid-dtd-list.html dtds = ['HTML', # HTML5 doctype @@ -281,9 +246,6 @@ text self._run_check("<!DOCTYPE %s>" % dtd, [('decl', 'DOCTYPE ' + dtd)]) - def test_declaration_junk_chars(self): - self._parse_error("<!DOCTYPE foo $ >") - def test_startendtag(self): self._run_check("<p/>", [ ("startendtag", "p", []), @@ -384,7 +346,8 @@ text self._run_check(html, expected) def test_convert_charrefs(self): - collector = lambda: EventCollectorCharrefs(convert_charrefs=True) + # default value for convert_charrefs is now True + collector = lambda: EventCollectorCharrefs() self.assertTrue(collector().convert_charrefs) charrefs = ['"', '"', '"', '"', '"', '"'] # check charrefs in the middle of the text/attributes @@ -421,23 +384,8 @@ text self._run_check('no charrefs here', [('data', 'no charrefs here')], collector=collector()) - -class HTMLParserTolerantTestCase(HTMLParserStrictTestCase): - - def get_collector(self): - return EventCollector(convert_charrefs=False) - - def test_deprecation_warnings(self): - with self.assertWarns(DeprecationWarning): - EventCollector() # convert_charrefs not passed explicitly - with self.assertWarns(DeprecationWarning): - EventCollector(strict=True) - with self.assertWarns(DeprecationWarning): - EventCollector(strict=False) - with self.assertRaises(html.parser.HTMLParseError): - with self.assertWarns(DeprecationWarning): - EventCollector().error('test') - + # the remaining tests were for the "tolerant" parser (which is now + # the default), and check various kind of broken markup def test_tolerant_parsing(self): self._run_check('<html <html>te>>xt&a<<bc</a></html>\n' '<img src="URL><//img></html</html>', [ @@ -686,11 +634,7 @@ class HTMLParserTolerantTestCase(HTMLParserStrictTestCase): self._run_check(html, expected) -class AttributesStrictTestCase(TestCaseBase): - - def get_collector(self): - with support.check_warnings(("", DeprecationWarning), quite=False): - return EventCollector(strict=True, convert_charrefs=False) +class AttributesTestCase(TestCaseBase): def test_attr_syntax(self): output = [ @@ -747,12 +691,6 @@ class AttributesStrictTestCase(TestCaseBase): [("starttag", "html", [("foo", "\u20AC&aa&unsupported;")])]) - -class AttributesTolerantTestCase(AttributesStrictTestCase): - - def get_collector(self): - return EventCollector(convert_charrefs=False) - def test_attr_funky_names2(self): self._run_check( "<a $><b $=%><c \=/>", diff --git a/Lib/test/test_http_cookies.py b/Lib/test/test_http_cookies.py index e984252..ee30f17 100644 --- a/Lib/test/test_http_cookies.py +++ b/Lib/test/test_http_cookies.py @@ -141,13 +141,6 @@ class CookieTests(unittest.TestCase): self.assertEqual(C['eggs']['httponly'], 'foo') self.assertEqual(C['eggs']['secure'], 'bar') - def test_bad_attrs(self): - # issue 16611: make sure we don't break backward compatibility. - C = cookies.SimpleCookie() - C.load('cookie=with; invalid; version; second=cookie;') - self.assertEqual(C.output(), - 'Set-Cookie: cookie=with\r\nSet-Cookie: second=cookie') - def test_extra_spaces(self): C = cookies.SimpleCookie() C.load('eggs = scrambled ; secure ; path = bar ; foo=foo ') @@ -182,7 +175,10 @@ class CookieTests(unittest.TestCase): def test_invalid_cookies(self): # Accepting these could be a security issue C = cookies.SimpleCookie() - for s in (']foo=x', '[foo=x', 'blah]foo=x', 'blah[foo=x'): + for s in (']foo=x', '[foo=x', 'blah]foo=x', 'blah[foo=x', + 'Set-Cookie: foo=bar', 'Set-Cookie: foo', + 'foo=bar; baz', 'baz; foo=bar', + 'secure;foo=bar', 'Version=1;foo=bar'): C.load(s) self.assertEqual(dict(C), {}) self.assertEqual(C.output(), '') diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index 1456de3..6261276 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -18,6 +18,26 @@ CERT_fakehostname = os.path.join(here, 'keycert2.pem') # Self-signed cert file for self-signed.pythontest.net CERT_selfsigned_pythontestdotnet = os.path.join(here, 'selfsigned_pythontestdotnet.pem') +# constants for testing chunked encoding +chunked_start = ( + 'HTTP/1.1 200 OK\r\n' + 'Transfer-Encoding: chunked\r\n\r\n' + 'a\r\n' + 'hello worl\r\n' + '3\r\n' + 'd! \r\n' + '8\r\n' + 'and now \r\n' + '22\r\n' + 'for something completely different\r\n' +) +chunked_expected = b'hello world! and now for something completely different' +chunk_extension = ";foo=bar" +last_chunk = "0\r\n" +last_chunk_extended = "0" + chunk_extension + "\r\n" +trailers = "X-Dummy: foo\r\nX-Dumm2: bar\r\n" +chunked_end = "\r\n" + HOST = support.HOST class FakeSocket: @@ -442,20 +462,8 @@ class BasicTest(TestCase): conn.request('POST', 'test', conn) def test_chunked(self): - chunked_start = ( - 'HTTP/1.1 200 OK\r\n' - 'Transfer-Encoding: chunked\r\n\r\n' - 'a\r\n' - 'hello worl\r\n' - '3\r\n' - 'd! \r\n' - '8\r\n' - 'and now \r\n' - '22\r\n' - 'for something completely different\r\n' - ) - expected = b'hello world! and now for something completely different' - sock = FakeSocket(chunked_start + '0\r\n') + expected = chunked_expected + sock = FakeSocket(chunked_start + last_chunk + chunked_end) resp = client.HTTPResponse(sock, method="GET") resp.begin() self.assertEqual(resp.read(), expected) @@ -463,7 +471,7 @@ class BasicTest(TestCase): # Various read sizes for n in range(1, 12): - sock = FakeSocket(chunked_start + '0\r\n') + sock = FakeSocket(chunked_start + last_chunk + chunked_end) resp = client.HTTPResponse(sock, method="GET") resp.begin() self.assertEqual(resp.read(n) + resp.read(n) + resp.read(), expected) @@ -486,23 +494,12 @@ class BasicTest(TestCase): resp.close() def test_readinto_chunked(self): - chunked_start = ( - 'HTTP/1.1 200 OK\r\n' - 'Transfer-Encoding: chunked\r\n\r\n' - 'a\r\n' - 'hello worl\r\n' - '3\r\n' - 'd! \r\n' - '8\r\n' - 'and now \r\n' - '22\r\n' - 'for something completely different\r\n' - ) - expected = b'hello world! and now for something completely different' + + expected = chunked_expected nexpected = len(expected) b = bytearray(128) - sock = FakeSocket(chunked_start + '0\r\n') + sock = FakeSocket(chunked_start + last_chunk + chunked_end) resp = client.HTTPResponse(sock, method="GET") resp.begin() n = resp.readinto(b) @@ -512,7 +509,7 @@ class BasicTest(TestCase): # Various read sizes for n in range(1, 12): - sock = FakeSocket(chunked_start + '0\r\n') + sock = FakeSocket(chunked_start + last_chunk + chunked_end) resp = client.HTTPResponse(sock, method="GET") resp.begin() m = memoryview(b) @@ -548,7 +545,7 @@ class BasicTest(TestCase): '1\r\n' 'd\r\n' ) - sock = FakeSocket(chunked_start + '0\r\n') + sock = FakeSocket(chunked_start + last_chunk + chunked_end) resp = client.HTTPResponse(sock, method="HEAD") resp.begin() self.assertEqual(resp.read(), b'') @@ -568,7 +565,7 @@ class BasicTest(TestCase): '1\r\n' 'd\r\n' ) - sock = FakeSocket(chunked_start + '0\r\n') + sock = FakeSocket(chunked_start + last_chunk + chunked_end) resp = client.HTTPResponse(sock, method="HEAD") resp.begin() b = bytearray(5) @@ -643,6 +640,7 @@ class BasicTest(TestCase): + '0' * 65536 + 'a\r\n' 'hello world\r\n' '0\r\n' + '\r\n' ) resp = client.HTTPResponse(FakeSocket(body)) resp.begin() @@ -698,6 +696,239 @@ class BasicTest(TestCase): self.assertTrue(response.closed) self.assertTrue(conn.sock.file_closed) + def test_chunked_extension(self): + extra = '3;foo=bar\r\n' + 'abc\r\n' + expected = chunked_expected + b'abc' + + sock = FakeSocket(chunked_start + extra + last_chunk_extended + chunked_end) + resp = client.HTTPResponse(sock, method="GET") + resp.begin() + self.assertEqual(resp.read(), expected) + resp.close() + + def test_chunked_missing_end(self): + """some servers may serve up a short chunked encoding stream""" + expected = chunked_expected + sock = FakeSocket(chunked_start + last_chunk) #no terminating crlf + resp = client.HTTPResponse(sock, method="GET") + resp.begin() + self.assertEqual(resp.read(), expected) + resp.close() + + def test_chunked_trailers(self): + """See that trailers are read and ignored""" + expected = chunked_expected + sock = FakeSocket(chunked_start + last_chunk + trailers + chunked_end) + resp = client.HTTPResponse(sock, method="GET") + resp.begin() + self.assertEqual(resp.read(), expected) + # we should have reached the end of the file + self.assertEqual(sock.file.read(100), b"") #we read to the end + resp.close() + + def test_chunked_sync(self): + """Check that we don't read past the end of the chunked-encoding stream""" + expected = chunked_expected + extradata = "extradata" + sock = FakeSocket(chunked_start + last_chunk + trailers + chunked_end + extradata) + resp = client.HTTPResponse(sock, method="GET") + resp.begin() + self.assertEqual(resp.read(), expected) + # the file should now have our extradata ready to be read + self.assertEqual(sock.file.read(100), extradata.encode("ascii")) #we read to the end + resp.close() + + def test_content_length_sync(self): + """Check that we don't read past the end of the Content-Length stream""" + extradata = "extradata" + expected = b"Hello123\r\n" + sock = FakeSocket('HTTP/1.1 200 OK\r\nContent-Length: 10\r\n\r\nHello123\r\n' + extradata) + resp = client.HTTPResponse(sock, method="GET") + resp.begin() + self.assertEqual(resp.read(), expected) + # the file should now have our extradata ready to be read + self.assertEqual(sock.file.read(100), extradata.encode("ascii")) #we read to the end + resp.close() + +class ExtendedReadTest(TestCase): + """ + Test peek(), read1(), readline() + """ + lines = ( + 'HTTP/1.1 200 OK\r\n' + '\r\n' + 'hello world!\n' + 'and now \n' + 'for something completely different\n' + 'foo' + ) + lines_expected = lines[lines.find('hello'):].encode("ascii") + lines_chunked = ( + 'HTTP/1.1 200 OK\r\n' + 'Transfer-Encoding: chunked\r\n\r\n' + 'a\r\n' + 'hello worl\r\n' + '3\r\n' + 'd!\n\r\n' + '9\r\n' + 'and now \n\r\n' + '23\r\n' + 'for something completely different\n\r\n' + '3\r\n' + 'foo\r\n' + '0\r\n' # terminating chunk + '\r\n' # end of trailers + ) + + def setUp(self): + sock = FakeSocket(self.lines) + resp = client.HTTPResponse(sock, method="GET") + resp.begin() + resp.fp = io.BufferedReader(resp.fp) + self.resp = resp + + + + def test_peek(self): + resp = self.resp + # patch up the buffered peek so that it returns not too much stuff + oldpeek = resp.fp.peek + def mypeek(n=-1): + p = oldpeek(n) + if n >= 0: + return p[:n] + return p[:10] + resp.fp.peek = mypeek + + all = [] + while True: + # try a short peek + p = resp.peek(3) + if p: + self.assertGreater(len(p), 0) + # then unbounded peek + p2 = resp.peek() + self.assertGreaterEqual(len(p2), len(p)) + self.assertTrue(p2.startswith(p)) + next = resp.read(len(p2)) + self.assertEqual(next, p2) + else: + next = resp.read() + self.assertFalse(next) + all.append(next) + if not next: + break + self.assertEqual(b"".join(all), self.lines_expected) + + def test_readline(self): + resp = self.resp + self._verify_readline(self.resp.readline, self.lines_expected) + + def _verify_readline(self, readline, expected): + all = [] + while True: + # short readlines + line = readline(5) + if line and line != b"foo": + if len(line) < 5: + self.assertTrue(line.endswith(b"\n")) + all.append(line) + if not line: + break + self.assertEqual(b"".join(all), expected) + + def test_read1(self): + resp = self.resp + def r(): + res = resp.read1(4) + self.assertLessEqual(len(res), 4) + return res + readliner = Readliner(r) + self._verify_readline(readliner.readline, self.lines_expected) + + def test_read1_unbounded(self): + resp = self.resp + all = [] + while True: + data = resp.read1() + if not data: + break + all.append(data) + self.assertEqual(b"".join(all), self.lines_expected) + + def test_read1_bounded(self): + resp = self.resp + all = [] + while True: + data = resp.read1(10) + if not data: + break + self.assertLessEqual(len(data), 10) + all.append(data) + self.assertEqual(b"".join(all), self.lines_expected) + + def test_read1_0(self): + self.assertEqual(self.resp.read1(0), b"") + + def test_peek_0(self): + p = self.resp.peek(0) + self.assertLessEqual(0, len(p)) + +class ExtendedReadTestChunked(ExtendedReadTest): + """ + Test peek(), read1(), readline() in chunked mode + """ + lines = ( + 'HTTP/1.1 200 OK\r\n' + 'Transfer-Encoding: chunked\r\n\r\n' + 'a\r\n' + 'hello worl\r\n' + '3\r\n' + 'd!\n\r\n' + '9\r\n' + 'and now \n\r\n' + '23\r\n' + 'for something completely different\n\r\n' + '3\r\n' + 'foo\r\n' + '0\r\n' # terminating chunk + '\r\n' # end of trailers + ) + + +class Readliner: + """ + a simple readline class that uses an arbitrary read function and buffering + """ + def __init__(self, readfunc): + self.readfunc = readfunc + self.remainder = b"" + + def readline(self, limit): + data = [] + datalen = 0 + read = self.remainder + try: + while True: + idx = read.find(b'\n') + if idx != -1: + break + if datalen + len(read) >= limit: + idx = limit - datalen - 1 + # read more data + data.append(read) + read = self.readfunc() + if not read: + idx = 0 #eof condition + break + idx += 1 + data.append(read[:idx]) + self.remainder = read[idx:] + return b"".join(data) + except: + self.remainder = b"".join(data) + raise + class OfflineTest(TestCase): def test_responses(self): self.assertEqual(client.responses[client.NOT_FOUND], "Not Found") @@ -708,7 +939,7 @@ class SourceAddressTest(TestCase): self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.port = support.bind_port(self.serv) self.source_port = support.find_unused_port() - self.serv.listen(5) + self.serv.listen() self.conn = None def tearDown(self): @@ -740,7 +971,7 @@ class TimeoutTest(TestCase): def setUp(self): self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) TimeoutTest.PORT = support.bind_port(self.serv) - self.serv.listen(5) + self.serv.listen() def tearDown(self): self.serv.close() @@ -1087,7 +1318,8 @@ class TunnelTests(TestCase): def test_main(verbose=None): support.run_unittest(HeaderTests, OfflineTest, BasicTest, TimeoutTest, HTTPSTest, RequestBodyTest, SourceAddressTest, - HTTPResponseTest, TunnelTests) + HTTPResponseTest, ExtendedReadTest, + ExtendedReadTestChunked, TunnelTests) if __name__ == '__main__': test_main() diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py index b34e652..5485a2a 100644 --- a/Lib/test/test_imaplib.py +++ b/Lib/test/test_imaplib.py @@ -11,7 +11,8 @@ import socketserver import time import calendar -from test.support import reap_threads, verbose, transient_internet, run_with_tz, run_with_locale +from test.support import (reap_threads, verbose, transient_internet, + run_with_tz, run_with_locale) import unittest from datetime import datetime, timezone, timedelta try: @@ -19,8 +20,8 @@ try: except ImportError: ssl = None -CERTFILE = None -CAFILE = None +CERTFILE = os.path.join(os.path.dirname(__file__) or os.curdir, "keycert3.pem") +CAFILE = os.path.join(os.path.dirname(__file__) or os.curdir, "pycacert.pem") class TestImaplib(unittest.TestCase): @@ -41,17 +42,15 @@ class TestImaplib(unittest.TestCase): def test_Internaldate2tuple_issue10941(self): self.assertNotEqual(imaplib.Internaldate2tuple( b'25 (INTERNALDATE "02-Apr-2000 02:30:00 +0000")'), - imaplib.Internaldate2tuple( - b'25 (INTERNALDATE "02-Apr-2000 03:30:00 +0000")')) - - + imaplib.Internaldate2tuple( + b'25 (INTERNALDATE "02-Apr-2000 03:30:00 +0000")')) def timevalues(self): return [2000000000, 2000000000.0, time.localtime(2000000000), (2033, 5, 18, 5, 33, 20, -1, -1, -1), (2033, 5, 18, 5, 33, 20, -1, -1, 1), datetime.fromtimestamp(2000000000, - timezone(timedelta(0, 2*60*60))), + timezone(timedelta(0, 2 * 60 * 60))), '"18-May-2033 05:33:20 +0200"'] @run_with_locale('LC_ALL', 'de_DE', 'fr_FR') @@ -72,7 +71,6 @@ class TestImaplib(unittest.TestCase): if ssl: - class SecureTCPServer(socketserver.TCPServer): def get_request(self): @@ -93,13 +91,17 @@ else: class SimpleIMAPHandler(socketserver.StreamRequestHandler): - timeout = 1 continuation = None capabilities = '' + def setup(self): + super().setup() + self.server.logged = None + def _send(self, message): - if verbose: print("SENT: %r" % message.strip()) + if verbose: + print("SENT: %r" % message.strip()) self.wfile.write(message) def _send_line(self, message): @@ -132,7 +134,8 @@ class SimpleIMAPHandler(socketserver.StreamRequestHandler): if line.endswith(b'\r\n'): break - if verbose: print('GOT: %r' % line.strip()) + if verbose: + print('GOT: %r' % line.strip()) if self.continuation: try: self.continuation.send(line) @@ -144,8 +147,8 @@ class SimpleIMAPHandler(socketserver.StreamRequestHandler): cmd = splitline[1] args = splitline[2:] - if hasattr(self, 'cmd_'+cmd): - continuation = getattr(self, 'cmd_'+cmd)(tag, args) + if hasattr(self, 'cmd_' + cmd): + continuation = getattr(self, 'cmd_' + cmd)(tag, args) if continuation: self.continuation = continuation next(continuation) @@ -153,16 +156,25 @@ class SimpleIMAPHandler(socketserver.StreamRequestHandler): self._send_tagged(tag, 'BAD', cmd + ' unknown') def cmd_CAPABILITY(self, tag, args): - caps = 'IMAP4rev1 ' + self.capabilities if self.capabilities else 'IMAP4rev1' + caps = ('IMAP4rev1 ' + self.capabilities + if self.capabilities + else 'IMAP4rev1') self._send_textline('* CAPABILITY ' + caps) self._send_tagged(tag, 'OK', 'CAPABILITY completed') def cmd_LOGOUT(self, tag, args): + self.server.logged = None self._send_textline('* BYE IMAP4ref1 Server logging out') self._send_tagged(tag, 'OK', 'LOGOUT completed') + def cmd_LOGIN(self, tag, args): + self.server.logged = args[0] + self._send_tagged(tag, 'OK', 'LOGIN completed') -class BaseThreadedNetworkedTests(unittest.TestCase): + +class ThreadedNetworkedTests(unittest.TestCase): + server_class = socketserver.TCPServer + imap_class = imaplib.IMAP4 def make_server(self, addr, hdlr): @@ -172,7 +184,8 @@ class BaseThreadedNetworkedTests(unittest.TestCase): self.server_close() raise - if verbose: print("creating server") + if verbose: + print("creating server") server = MyServer(addr, hdlr) self.assertEqual(server.server_address, server.socket.getsockname()) @@ -188,18 +201,21 @@ class BaseThreadedNetworkedTests(unittest.TestCase): # Short poll interval to make the test finish quickly. # Time between requests is short enough that we won't wake # up spuriously too many times. - kwargs={'poll_interval':0.01}) + kwargs={'poll_interval': 0.01}) t.daemon = True # In case this function raises. t.start() - if verbose: print("server running") + if verbose: + print("server running") return server, t def reap_server(self, server, thread): - if verbose: print("waiting for server") + if verbose: + print("waiting for server") server.shutdown() server.server_close() thread.join() - if verbose: print("done") + if verbose: + print("done") @contextmanager def reaped_server(self, hdlr): @@ -256,7 +272,7 @@ class BaseThreadedNetworkedTests(unittest.TestCase): def cmd_AUTHENTICATE(self, tag, args): self._send_tagged(tag, 'NO', 'unrecognized authentication ' - 'type {}'.format(args[0])) + 'type {}'.format(args[0])) with self.reaped_pair(MyServer) as (server, client): with self.assertRaises(imaplib.IMAP4.error): @@ -290,13 +306,13 @@ class BaseThreadedNetworkedTests(unittest.TestCase): code, data = client.authenticate('MYAUTH', lambda x: b'fake') self.assertEqual(code, 'OK') self.assertEqual(server.response, - b'ZmFrZQ==\r\n') #b64 encoded 'fake' + b'ZmFrZQ==\r\n') # b64 encoded 'fake' with self.reaped_pair(MyServer) as (server, client): code, data = client.authenticate('MYAUTH', lambda x: 'fake') self.assertEqual(code, 'OK') self.assertEqual(server.response, - b'ZmFrZQ==\r\n') #b64 encoded 'fake' + b'ZmFrZQ==\r\n') # b64 encoded 'fake' @reap_threads def test_login_cram_md5(self): @@ -307,9 +323,10 @@ class BaseThreadedNetworkedTests(unittest.TestCase): def cmd_AUTHENTICATE(self, tag, args): self._send_textline('+ PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2Uucm' - 'VzdG9uLm1jaS5uZXQ=') + 'VzdG9uLm1jaS5uZXQ=') r = yield - if r == b'dGltIGYxY2E2YmU0NjRiOWVmYTFjY2E2ZmZkNmNmMmQ5ZjMy\r\n': + if (r == b'dGltIGYxY2E2YmU0NjRiOWVmYT' + b'FjY2E2ZmZkNmNmMmQ5ZjMy\r\n'): self._send_tagged(tag, 'OK', 'CRAM-MD5 successful') else: self._send_tagged(tag, 'NO', 'No access') @@ -324,27 +341,45 @@ class BaseThreadedNetworkedTests(unittest.TestCase): ret, data = client.login_cram_md5("tim", b"tanstaaftanstaaf") self.assertEqual(ret, "OK") - def test_linetoolong(self): class TooLongHandler(SimpleIMAPHandler): def handle(self): # Send a very long response line - self.wfile.write(b'* OK ' + imaplib._MAXLINE*b'x' + b'\r\n') + self.wfile.write(b'* OK ' + imaplib._MAXLINE * b'x' + b'\r\n') with self.reaped_server(TooLongHandler) as server: self.assertRaises(imaplib.IMAP4.error, self.imap_class, *server.server_address) + @reap_threads + def test_simple_with_statement(self): + # simplest call + with self.reaped_server(SimpleIMAPHandler) as server: + with self.imap_class(*server.server_address): + pass -class ThreadedNetworkedTests(BaseThreadedNetworkedTests): + @reap_threads + def test_with_statement(self): + with self.reaped_server(SimpleIMAPHandler) as server: + with self.imap_class(*server.server_address) as imap: + imap.login('user', 'pass') + self.assertEqual(server.logged, 'user') + self.assertIsNone(server.logged) - server_class = socketserver.TCPServer - imap_class = imaplib.IMAP4 + @reap_threads + def test_with_statement_logout(self): + # what happens if already logout in the block? + with self.reaped_server(SimpleIMAPHandler) as server: + with self.imap_class(*server.server_address) as imap: + imap.login('user', 'pass') + self.assertEqual(server.logged, 'user') + imap.logout() + self.assertIsNone(server.logged) + self.assertIsNone(server.logged) @unittest.skipUnless(ssl, "SSL not available") -class ThreadedNetworkedTestsSSL(BaseThreadedNetworkedTests): - +class ThreadedNetworkedTestsSSL(ThreadedNetworkedTests): server_class = SecureTCPServer imap_class = IMAP4_SSL @@ -355,8 +390,9 @@ class ThreadedNetworkedTestsSSL(BaseThreadedNetworkedTests): ssl_context.check_hostname = True ssl_context.load_verify_locations(CAFILE) - with self.assertRaisesRegex(ssl.CertificateError, - "hostname '127.0.0.1' doesn't match 'localhost'"): + with self.assertRaisesRegex( + ssl.CertificateError, + "hostname '127.0.0.1' doesn't match 'localhost'"): with self.reaped_server(SimpleIMAPHandler) as server: client = self.imap_class(*server.server_address, ssl_context=ssl_context) @@ -368,6 +404,8 @@ class ThreadedNetworkedTestsSSL(BaseThreadedNetworkedTests): client.shutdown() +@unittest.skipUnless( + support.is_resource_enabled('network'), 'network resource disabled') class RemoteIMAPTest(unittest.TestCase): host = 'cyrus.andrew.cmu.edu' port = 143 @@ -401,6 +439,8 @@ class RemoteIMAPTest(unittest.TestCase): @unittest.skipUnless(ssl, "SSL not available") +@unittest.skipUnless( + support.is_resource_enabled('network'), 'network resource disabled') class RemoteIMAP_STARTTLSTest(RemoteIMAPTest): def setUp(self): @@ -454,7 +494,8 @@ class RemoteIMAP_SSLTest(RemoteIMAPTest): def test_logincapa_with_client_ssl_context(self): with transient_internet(self.host): - _server = self.imap_class(self.host, self.port, ssl_context=self.create_ssl_context()) + _server = self.imap_class( + self.host, self.port, ssl_context=self.create_ssl_context()) self.check_logincapa(_server) def test_logout(self): @@ -465,35 +506,15 @@ class RemoteIMAP_SSLTest(RemoteIMAPTest): def test_ssl_context_certfile_exclusive(self): with transient_internet(self.host): - self.assertRaises(ValueError, self.imap_class, self.host, self.port, - certfile=CERTFILE, ssl_context=self.create_ssl_context()) + self.assertRaises( + ValueError, self.imap_class, self.host, self.port, + certfile=CERTFILE, ssl_context=self.create_ssl_context()) def test_ssl_context_keyfile_exclusive(self): with transient_internet(self.host): - self.assertRaises(ValueError, self.imap_class, self.host, self.port, - keyfile=CERTFILE, ssl_context=self.create_ssl_context()) - - -def load_tests(*args): - tests = [TestImaplib] - - if support.is_resource_enabled('network'): - if ssl: - global CERTFILE, CAFILE - CERTFILE = os.path.join(os.path.dirname(__file__) or os.curdir, - "keycert3.pem") - if not os.path.exists(CERTFILE): - raise support.TestFailed("Can't read certificate files!") - CAFILE = os.path.join(os.path.dirname(__file__) or os.curdir, - "pycacert.pem") - if not os.path.exists(CAFILE): - raise support.TestFailed("Can't read CA file!") - tests.extend([ - ThreadedNetworkedTests, ThreadedNetworkedTestsSSL, - RemoteIMAPTest, RemoteIMAP_SSLTest, RemoteIMAP_STARTTLSTest, - ]) - - return unittest.TestSuite([unittest.makeSuite(test) for test in tests]) + self.assertRaises( + ValueError, self.imap_class, self.host, self.port, + keyfile=CERTFILE, ssl_context=self.create_ssl_context()) if __name__ == "__main__": diff --git a/Lib/test/test_imghdr.py b/Lib/test/test_imghdr.py index 0ad4343..b54daf8 100644 --- a/Lib/test/test_imghdr.py +++ b/Lib/test/test_imghdr.py @@ -16,7 +16,9 @@ TEST_FILES = ( ('python.ras', 'rast'), ('python.sgi', 'rgb'), ('python.tiff', 'tiff'), - ('python.xbm', 'xbm') + ('python.xbm', 'xbm'), + ('python.webp', 'webp'), + ('python.exr', 'exr'), ) class UnseekableIO(io.FileIO): diff --git a/Lib/test/test_import.py b/Lib/test/test_import/__init__.py index b4842c5..fd21fa2 100644 --- a/Lib/test/test_import.py +++ b/Lib/test/test_import/__init__.py @@ -568,7 +568,7 @@ class RelativeImportTests(unittest.TestCase): def test_relimport_star(self): # This will import * from .test_import. - from . import relimport + from .. import relimport self.assertTrue(hasattr(relimport, "RelativeImportTests")) def test_issue3221(self): @@ -1068,6 +1068,46 @@ class ImportTracebackTests(unittest.TestCase): __isolated=False) +class CircularImportTests(unittest.TestCase): + + """See the docstrings of the modules being imported for the purpose of the + test.""" + + def tearDown(self): + """Make sure no modules pre-exist in sys.modules which are being used to + test.""" + for key in list(sys.modules.keys()): + if key.startswith('test.test_import.data.circular_imports'): + del sys.modules[key] + + def test_direct(self): + try: + import test.test_import.data.circular_imports.basic + except ImportError: + self.fail('circular import through relative imports failed') + + def test_indirect(self): + try: + import test.test_import.data.circular_imports.indirect + except ImportError: + self.fail('relative import in module contributing to circular ' + 'import failed') + + def test_subpackage(self): + try: + import test.test_import.data.circular_imports.subpackage + except ImportError: + self.fail('circular import involving a subpackage failed') + + def test_rebinding(self): + try: + import test.test_import.data.circular_imports.rebinding as rebinding + except ImportError: + self.fail('circular import with rebinding of module attribute failed') + from test.test_import.data.circular_imports.subpkg import util + self.assertIs(util.util, rebinding.util) + + if __name__ == '__main__': # Test needs to be a package, so we can do relative imports. unittest.main() diff --git a/Lib/test/test_import/__main__.py b/Lib/test/test_import/__main__.py new file mode 100644 index 0000000..24f02a1 --- /dev/null +++ b/Lib/test/test_import/__main__.py @@ -0,0 +1,3 @@ +import unittest + +unittest.main('test.test_import') diff --git a/Lib/test/test_import/data/circular_imports/basic.py b/Lib/test/test_import/data/circular_imports/basic.py new file mode 100644 index 0000000..3e41e39 --- /dev/null +++ b/Lib/test/test_import/data/circular_imports/basic.py @@ -0,0 +1,2 @@ +"""Circular imports through direct, relative imports.""" +from . import basic2 diff --git a/Lib/test/test_import/data/circular_imports/basic2.py b/Lib/test/test_import/data/circular_imports/basic2.py new file mode 100644 index 0000000..00bd2f2 --- /dev/null +++ b/Lib/test/test_import/data/circular_imports/basic2.py @@ -0,0 +1 @@ +from . import basic diff --git a/Lib/test/test_import/data/circular_imports/indirect.py b/Lib/test/test_import/data/circular_imports/indirect.py new file mode 100644 index 0000000..6925788 --- /dev/null +++ b/Lib/test/test_import/data/circular_imports/indirect.py @@ -0,0 +1 @@ +from . import basic, basic2 diff --git a/Lib/test/test_import/data/circular_imports/rebinding.py b/Lib/test/test_import/data/circular_imports/rebinding.py new file mode 100644 index 0000000..2b77375 --- /dev/null +++ b/Lib/test/test_import/data/circular_imports/rebinding.py @@ -0,0 +1,3 @@ +"""Test the binding of names when a circular import shares the same name as an +attribute.""" +from .rebinding2 import util diff --git a/Lib/test/test_import/data/circular_imports/rebinding2.py b/Lib/test/test_import/data/circular_imports/rebinding2.py new file mode 100644 index 0000000..57a9e69 --- /dev/null +++ b/Lib/test/test_import/data/circular_imports/rebinding2.py @@ -0,0 +1,3 @@ +from .subpkg import util +from . import rebinding +util = util.util diff --git a/Lib/test/test_import/data/circular_imports/subpackage.py b/Lib/test/test_import/data/circular_imports/subpackage.py new file mode 100644 index 0000000..7b412f7 --- /dev/null +++ b/Lib/test/test_import/data/circular_imports/subpackage.py @@ -0,0 +1,2 @@ +"""Circular import involving a sub-package.""" +from .subpkg import subpackage2 diff --git a/Lib/test/test_import/data/circular_imports/subpkg/subpackage2.py b/Lib/test/test_import/data/circular_imports/subpkg/subpackage2.py new file mode 100644 index 0000000..17b893a --- /dev/null +++ b/Lib/test/test_import/data/circular_imports/subpkg/subpackage2.py @@ -0,0 +1,2 @@ +#from .util import util +from .. import subpackage diff --git a/Lib/test/test_import/data/circular_imports/subpkg/util.py b/Lib/test/test_import/data/circular_imports/subpkg/util.py new file mode 100644 index 0000000..343bd84 --- /dev/null +++ b/Lib/test/test_import/data/circular_imports/subpkg/util.py @@ -0,0 +1,2 @@ +def util(): + pass diff --git a/Lib/test/test_import/data/circular_imports/util.py b/Lib/test/test_import/data/circular_imports/util.py new file mode 100644 index 0000000..343bd84 --- /dev/null +++ b/Lib/test/test_import/data/circular_imports/util.py @@ -0,0 +1,2 @@ +def util(): + pass diff --git a/Lib/test/test_importlib/builtin/test_finder.py b/Lib/test/test_importlib/builtin/test_finder.py index 934562f..a2e6e1e 100644 --- a/Lib/test/test_importlib/builtin/test_finder.py +++ b/Lib/test/test_importlib/builtin/test_finder.py @@ -1,21 +1,21 @@ from .. import abc from .. import util -from . import util as builtin_util -frozen_machinery, source_machinery = util.import_importlib('importlib.machinery') +machinery = util.import_importlib('importlib.machinery') import sys import unittest +@unittest.skipIf(util.BUILTINS.good_name is None, 'no reasonable builtin module') class FindSpecTests(abc.FinderTests): """Test find_spec() for built-in modules.""" def test_module(self): # Common case. - with util.uncache(builtin_util.NAME): - found = self.machinery.BuiltinImporter.find_spec(builtin_util.NAME) + with util.uncache(util.BUILTINS.good_name): + found = self.machinery.BuiltinImporter.find_spec(util.BUILTINS.good_name) self.assertTrue(found) self.assertEqual(found.origin, 'built-in') @@ -39,23 +39,26 @@ class FindSpecTests(abc.FinderTests): def test_ignore_path(self): # The value for 'path' should always trigger a failed import. - with util.uncache(builtin_util.NAME): - spec = self.machinery.BuiltinImporter.find_spec(builtin_util.NAME, + with util.uncache(util.BUILTINS.good_name): + spec = self.machinery.BuiltinImporter.find_spec(util.BUILTINS.good_name, ['pkg']) self.assertIsNone(spec) -Frozen_FindSpecTests, Source_FindSpecTests = util.test_both(FindSpecTests, - machinery=[frozen_machinery, source_machinery]) +(Frozen_FindSpecTests, + Source_FindSpecTests + ) = util.test_both(FindSpecTests, machinery=machinery) + +@unittest.skipIf(util.BUILTINS.good_name is None, 'no reasonable builtin module') class FinderTests(abc.FinderTests): """Test find_module() for built-in modules.""" def test_module(self): # Common case. - with util.uncache(builtin_util.NAME): - found = self.machinery.BuiltinImporter.find_module(builtin_util.NAME) + with util.uncache(util.BUILTINS.good_name): + found = self.machinery.BuiltinImporter.find_module(util.BUILTINS.good_name) self.assertTrue(found) self.assertTrue(hasattr(found, 'load_module')) @@ -72,13 +75,15 @@ class FinderTests(abc.FinderTests): def test_ignore_path(self): # The value for 'path' should always trigger a failed import. - with util.uncache(builtin_util.NAME): - loader = self.machinery.BuiltinImporter.find_module(builtin_util.NAME, + with util.uncache(util.BUILTINS.good_name): + loader = self.machinery.BuiltinImporter.find_module(util.BUILTINS.good_name, ['pkg']) self.assertIsNone(loader) -Frozen_FinderTests, Source_FinderTests = util.test_both(FinderTests, - machinery=[frozen_machinery, source_machinery]) + +(Frozen_FinderTests, + Source_FinderTests + ) = util.test_both(FinderTests, machinery=machinery) if __name__ == '__main__': diff --git a/Lib/test/test_importlib/builtin/test_loader.py b/Lib/test/test_importlib/builtin/test_loader.py index 1f83574..1684ab6 100644 --- a/Lib/test/test_importlib/builtin/test_loader.py +++ b/Lib/test/test_importlib/builtin/test_loader.py @@ -1,14 +1,13 @@ from .. import abc from .. import util -from . import util as builtin_util -frozen_machinery, source_machinery = util.import_importlib('importlib.machinery') +machinery = util.import_importlib('importlib.machinery') import sys import types import unittest - +@unittest.skipIf(util.BUILTINS.good_name is None, 'no reasonable builtin module') class LoaderTests(abc.LoaderTests): """Test load_module() for built-in modules.""" @@ -29,8 +28,8 @@ class LoaderTests(abc.LoaderTests): def test_module(self): # Common case. - with util.uncache(builtin_util.NAME): - module = self.load_module(builtin_util.NAME) + with util.uncache(util.BUILTINS.good_name): + module = self.load_module(util.BUILTINS.good_name) self.verify(module) # Built-in modules cannot be a package. @@ -41,9 +40,9 @@ class LoaderTests(abc.LoaderTests): def test_module_reuse(self): # Test that the same module is used in a reload. - with util.uncache(builtin_util.NAME): - module1 = self.load_module(builtin_util.NAME) - module2 = self.load_module(builtin_util.NAME) + with util.uncache(util.BUILTINS.good_name): + module1 = self.load_module(util.BUILTINS.good_name) + module2 = self.load_module(util.BUILTINS.good_name) self.assertIs(module1, module2) def test_unloadable(self): @@ -66,40 +65,44 @@ class LoaderTests(abc.LoaderTests): self.assertEqual(cm.exception.name, module_name) -Frozen_LoaderTests, Source_LoaderTests = util.test_both(LoaderTests, - machinery=[frozen_machinery, source_machinery]) +(Frozen_LoaderTests, + Source_LoaderTests + ) = util.test_both(LoaderTests, machinery=machinery) +@unittest.skipIf(util.BUILTINS.good_name is None, 'no reasonable builtin module') class InspectLoaderTests: """Tests for InspectLoader methods for BuiltinImporter.""" def test_get_code(self): # There is no code object. - result = self.machinery.BuiltinImporter.get_code(builtin_util.NAME) + result = self.machinery.BuiltinImporter.get_code(util.BUILTINS.good_name) self.assertIsNone(result) def test_get_source(self): # There is no source. - result = self.machinery.BuiltinImporter.get_source(builtin_util.NAME) + result = self.machinery.BuiltinImporter.get_source(util.BUILTINS.good_name) self.assertIsNone(result) def test_is_package(self): # Cannot be a package. - result = self.machinery.BuiltinImporter.is_package(builtin_util.NAME) + result = self.machinery.BuiltinImporter.is_package(util.BUILTINS.good_name) self.assertFalse(result) + @unittest.skipIf(util.BUILTINS.bad_name is None, 'all modules are built in') def test_not_builtin(self): # Modules not built-in should raise ImportError. for meth_name in ('get_code', 'get_source', 'is_package'): method = getattr(self.machinery.BuiltinImporter, meth_name) with self.assertRaises(ImportError) as cm: - method(builtin_util.BAD_NAME) - self.assertRaises(builtin_util.BAD_NAME) + method(util.BUILTINS.bad_name) + self.assertRaises(util.BUILTINS.bad_name) + -Frozen_InspectLoaderTests, Source_InspectLoaderTests = util.test_both( - InspectLoaderTests, - machinery=[frozen_machinery, source_machinery]) +(Frozen_InspectLoaderTests, + Source_InspectLoaderTests + ) = util.test_both(InspectLoaderTests, machinery=machinery) if __name__ == '__main__': diff --git a/Lib/test/test_importlib/builtin/util.py b/Lib/test/test_importlib/builtin/util.py deleted file mode 100644 index 5704699..0000000 --- a/Lib/test/test_importlib/builtin/util.py +++ /dev/null @@ -1,7 +0,0 @@ -import sys - -assert 'errno' in sys.builtin_module_names -NAME = 'errno' - -assert 'importlib' not in sys.builtin_module_names -BAD_NAME = 'importlib' diff --git a/Lib/test/test_importlib/extension/test_case_sensitivity.py b/Lib/test/test_importlib/extension/test_case_sensitivity.py index bb2528e..c7d6ca6 100644 --- a/Lib/test/test_importlib/extension/test_case_sensitivity.py +++ b/Lib/test/test_importlib/extension/test_case_sensitivity.py @@ -4,22 +4,21 @@ from test import support import unittest from .. import util -from . import util as ext_util -frozen_machinery, source_machinery = util.import_importlib('importlib.machinery') +machinery = util.import_importlib('importlib.machinery') # XXX find_spec tests -@unittest.skipIf(ext_util.FILENAME is None, '_testcapi not available') +@unittest.skipIf(util.EXTENSIONS.filename is None, '_testcapi not available') @util.case_insensitive_tests class ExtensionModuleCaseSensitivityTest: def find_module(self): - good_name = ext_util.NAME + good_name = util.EXTENSIONS.name bad_name = good_name.upper() assert good_name != bad_name - finder = self.machinery.FileFinder(ext_util.PATH, + finder = self.machinery.FileFinder(util.EXTENSIONS.path, (self.machinery.ExtensionFileLoader, self.machinery.EXTENSION_SUFFIXES)) return finder.find_module(bad_name) @@ -42,9 +41,10 @@ class ExtensionModuleCaseSensitivityTest: loader = self.find_module() self.assertTrue(hasattr(loader, 'load_module')) -Frozen_ExtensionCaseSensitivity, Source_ExtensionCaseSensitivity = util.test_both( - ExtensionModuleCaseSensitivityTest, - machinery=[frozen_machinery, source_machinery]) + +(Frozen_ExtensionCaseSensitivity, + Source_ExtensionCaseSensitivity + ) = util.test_both(ExtensionModuleCaseSensitivityTest, machinery=machinery) if __name__ == '__main__': diff --git a/Lib/test/test_importlib/extension/test_finder.py b/Lib/test/test_importlib/extension/test_finder.py index 990f29c..71bf67f 100644 --- a/Lib/test/test_importlib/extension/test_finder.py +++ b/Lib/test/test_importlib/extension/test_finder.py @@ -1,8 +1,7 @@ from .. import abc -from .. import util as test_util -from . import util +from .. import util -machinery = test_util.import_importlib('importlib.machinery') +machinery = util.import_importlib('importlib.machinery') import unittest import warnings @@ -14,7 +13,7 @@ class FinderTests(abc.FinderTests): """Test the finder for extension modules.""" def find_module(self, fullname): - importer = self.machinery.FileFinder(util.PATH, + importer = self.machinery.FileFinder(util.EXTENSIONS.path, (self.machinery.ExtensionFileLoader, self.machinery.EXTENSION_SUFFIXES)) with warnings.catch_warnings(): @@ -22,7 +21,7 @@ class FinderTests(abc.FinderTests): return importer.find_module(fullname) def test_module(self): - self.assertTrue(self.find_module(util.NAME)) + self.assertTrue(self.find_module(util.EXTENSIONS.name)) # No extension module as an __init__ available for testing. test_package = test_package_in_package = None @@ -36,8 +35,10 @@ class FinderTests(abc.FinderTests): def test_failure(self): self.assertIsNone(self.find_module('asdfjkl;')) -Frozen_FinderTests, Source_FinderTests = test_util.test_both( - FinderTests, machinery=machinery) + +(Frozen_FinderTests, + Source_FinderTests + ) = util.test_both(FinderTests, machinery=machinery) if __name__ == '__main__': diff --git a/Lib/test/test_importlib/extension/test_loader.py b/Lib/test/test_importlib/extension/test_loader.py index fd9abf2..aefd050 100644 --- a/Lib/test/test_importlib/extension/test_loader.py +++ b/Lib/test/test_importlib/extension/test_loader.py @@ -1,4 +1,3 @@ -from . import util as ext_util from .. import abc from .. import util @@ -15,8 +14,8 @@ class LoaderTests(abc.LoaderTests): """Test load_module() for extension modules.""" def setUp(self): - self.loader = self.machinery.ExtensionFileLoader(ext_util.NAME, - ext_util.FILEPATH) + self.loader = self.machinery.ExtensionFileLoader(util.EXTENSIONS.name, + util.EXTENSIONS.file_path) def load_module(self, fullname): return self.loader.load_module(fullname) @@ -29,23 +28,23 @@ class LoaderTests(abc.LoaderTests): self.load_module('XXX') def test_equality(self): - other = self.machinery.ExtensionFileLoader(ext_util.NAME, - ext_util.FILEPATH) + other = self.machinery.ExtensionFileLoader(util.EXTENSIONS.name, + util.EXTENSIONS.file_path) self.assertEqual(self.loader, other) def test_inequality(self): - other = self.machinery.ExtensionFileLoader('_' + ext_util.NAME, - ext_util.FILEPATH) + other = self.machinery.ExtensionFileLoader('_' + util.EXTENSIONS.name, + util.EXTENSIONS.file_path) self.assertNotEqual(self.loader, other) def test_module(self): - with util.uncache(ext_util.NAME): - module = self.load_module(ext_util.NAME) - for attr, value in [('__name__', ext_util.NAME), - ('__file__', ext_util.FILEPATH), + with util.uncache(util.EXTENSIONS.name): + module = self.load_module(util.EXTENSIONS.name) + for attr, value in [('__name__', util.EXTENSIONS.name), + ('__file__', util.EXTENSIONS.file_path), ('__package__', '')]: self.assertEqual(getattr(module, attr), value) - self.assertIn(ext_util.NAME, sys.modules) + self.assertIn(util.EXTENSIONS.name, sys.modules) self.assertIsInstance(module.__loader__, self.machinery.ExtensionFileLoader) @@ -56,9 +55,9 @@ class LoaderTests(abc.LoaderTests): test_lacking_parent = None def test_module_reuse(self): - with util.uncache(ext_util.NAME): - module1 = self.load_module(ext_util.NAME) - module2 = self.load_module(ext_util.NAME) + with util.uncache(util.EXTENSIONS.name): + module1 = self.load_module(util.EXTENSIONS.name) + module2 = self.load_module(util.EXTENSIONS.name) self.assertIs(module1, module2) # No easy way to trigger a failure after a successful import. @@ -71,14 +70,15 @@ class LoaderTests(abc.LoaderTests): self.assertEqual(cm.exception.name, name) def test_is_package(self): - self.assertFalse(self.loader.is_package(ext_util.NAME)) + self.assertFalse(self.loader.is_package(util.EXTENSIONS.name)) for suffix in self.machinery.EXTENSION_SUFFIXES: path = os.path.join('some', 'path', 'pkg', '__init__' + suffix) loader = self.machinery.ExtensionFileLoader('pkg', path) self.assertTrue(loader.is_package('pkg')) -Frozen_LoaderTests, Source_LoaderTests = util.test_both( - LoaderTests, machinery=machinery) +(Frozen_LoaderTests, + Source_LoaderTests + ) = util.test_both(LoaderTests, machinery=machinery) diff --git a/Lib/test/test_importlib/extension/test_path_hook.py b/Lib/test/test_importlib/extension/test_path_hook.py index 49d6734..8f4b8bb 100644 --- a/Lib/test/test_importlib/extension/test_path_hook.py +++ b/Lib/test/test_importlib/extension/test_path_hook.py @@ -1,7 +1,6 @@ -from .. import util as test_util -from . import util +from .. import util -machinery = test_util.import_importlib('importlib.machinery') +machinery = util.import_importlib('importlib.machinery') import collections import sys @@ -22,10 +21,12 @@ class PathHookTests: def test_success(self): # Path hook should handle a directory where a known extension module # exists. - self.assertTrue(hasattr(self.hook(util.PATH), 'find_module')) + self.assertTrue(hasattr(self.hook(util.EXTENSIONS.path), 'find_module')) -Frozen_PathHooksTests, Source_PathHooksTests = test_util.test_both( - PathHookTests, machinery=machinery) + +(Frozen_PathHooksTests, + Source_PathHooksTests + ) = util.test_both(PathHookTests, machinery=machinery) if __name__ == '__main__': diff --git a/Lib/test/test_importlib/extension/util.py b/Lib/test/test_importlib/extension/util.py deleted file mode 100644 index 8d089f0..0000000 --- a/Lib/test/test_importlib/extension/util.py +++ /dev/null @@ -1,19 +0,0 @@ -from importlib import machinery -import os -import sys - -PATH = None -EXT = None -FILENAME = None -NAME = '_testcapi' -try: - for PATH in sys.path: - for EXT in machinery.EXTENSION_SUFFIXES: - FILENAME = NAME + EXT - FILEPATH = os.path.join(PATH, FILENAME) - if os.path.exists(os.path.join(PATH, FILENAME)): - raise StopIteration - else: - PATH = EXT = FILENAME = FILEPATH = None -except StopIteration: - pass diff --git a/Lib/test/test_importlib/frozen/test_finder.py b/Lib/test/test_importlib/frozen/test_finder.py index f9f97f3..519aa02 100644 --- a/Lib/test/test_importlib/frozen/test_finder.py +++ b/Lib/test/test_importlib/frozen/test_finder.py @@ -37,8 +37,10 @@ class FindSpecTests(abc.FinderTests): spec = self.find('<not real>') self.assertIsNone(spec) -Frozen_FindSpecTests, Source_FindSpecTests = util.test_both(FindSpecTests, - machinery=machinery) + +(Frozen_FindSpecTests, + Source_FindSpecTests + ) = util.test_both(FindSpecTests, machinery=machinery) class FinderTests(abc.FinderTests): @@ -72,8 +74,10 @@ class FinderTests(abc.FinderTests): loader = self.find('<not real>') self.assertIsNone(loader) -Frozen_FinderTests, Source_FinderTests = util.test_both(FinderTests, - machinery=machinery) + +(Frozen_FinderTests, + Source_FinderTests + ) = util.test_both(FinderTests, machinery=machinery) if __name__ == '__main__': diff --git a/Lib/test/test_importlib/frozen/test_loader.py b/Lib/test/test_importlib/frozen/test_loader.py index 7c01464..603c7d7 100644 --- a/Lib/test/test_importlib/frozen/test_loader.py +++ b/Lib/test/test_importlib/frozen/test_loader.py @@ -85,8 +85,10 @@ class ExecModuleTests(abc.LoaderTests): self.exec_module('_not_real') self.assertEqual(cm.exception.name, '_not_real') -Frozen_ExecModuleTests, Source_ExecModuleTests = util.test_both(ExecModuleTests, - machinery=machinery) + +(Frozen_ExecModuleTests, + Source_ExecModuleTests + ) = util.test_both(ExecModuleTests, machinery=machinery) class LoaderTests(abc.LoaderTests): @@ -175,8 +177,10 @@ class LoaderTests(abc.LoaderTests): self.machinery.FrozenImporter.load_module('_not_real') self.assertEqual(cm.exception.name, '_not_real') -Frozen_LoaderTests, Source_LoaderTests = util.test_both(LoaderTests, - machinery=machinery) + +(Frozen_LoaderTests, + Source_LoaderTests + ) = util.test_both(LoaderTests, machinery=machinery) class InspectLoaderTests: @@ -214,8 +218,9 @@ class InspectLoaderTests: method('importlib') self.assertEqual(cm.exception.name, 'importlib') -Frozen_ILTests, Source_ILTests = util.test_both(InspectLoaderTests, - machinery=machinery) +(Frozen_ILTests, + Source_ILTests + ) = util.test_both(InspectLoaderTests, machinery=machinery) if __name__ == '__main__': diff --git a/Lib/test/test_importlib/import_/test___loader__.py b/Lib/test/test_importlib/import_/test___loader__.py index 6df8010..4b18093 100644 --- a/Lib/test/test_importlib/import_/test___loader__.py +++ b/Lib/test/test_importlib/import_/test___loader__.py @@ -4,7 +4,6 @@ import types import unittest from .. import util -from . import util as import_util class SpecLoaderMock: @@ -12,6 +11,9 @@ class SpecLoaderMock: def find_spec(self, fullname, path=None, target=None): return machinery.ModuleSpec(fullname, self) + def create_module(self, spec): + return None + def exec_module(self, module): pass @@ -24,8 +26,10 @@ class SpecLoaderAttributeTests: module = self.__import__('blah') self.assertEqual(loader, module.__loader__) -Frozen_SpecTests, Source_SpecTests = util.test_both( - SpecLoaderAttributeTests, __import__=import_util.__import__) + +(Frozen_SpecTests, + Source_SpecTests + ) = util.test_both(SpecLoaderAttributeTests, __import__=util.__import__) class LoaderMock: @@ -62,8 +66,9 @@ class LoaderAttributeTests: self.assertEqual(loader, module.__loader__) -Frozen_Tests, Source_Tests = util.test_both(LoaderAttributeTests, - __import__=import_util.__import__) +(Frozen_Tests, + Source_Tests + ) = util.test_both(LoaderAttributeTests, __import__=util.__import__) if __name__ == '__main__': diff --git a/Lib/test/test_importlib/import_/test___package__.py b/Lib/test/test_importlib/import_/test___package__.py index 2e19725..c7d3a2a 100644 --- a/Lib/test/test_importlib/import_/test___package__.py +++ b/Lib/test/test_importlib/import_/test___package__.py @@ -6,7 +6,6 @@ of using the typical __path__/__name__ test). """ import unittest from .. import util -from . import util as import_util class Using__package__: @@ -70,17 +69,23 @@ class Using__package__: with self.assertRaises(TypeError): self.__import__('', globals, {}, ['relimport'], 1) + class Using__package__PEP302(Using__package__): mock_modules = util.mock_modules -Frozen_UsingPackagePEP302, Source_UsingPackagePEP302 = util.test_both( - Using__package__PEP302, __import__=import_util.__import__) -class Using__package__PEP302(Using__package__): +(Frozen_UsingPackagePEP302, + Source_UsingPackagePEP302 + ) = util.test_both(Using__package__PEP302, __import__=util.__import__) + + +class Using__package__PEP451(Using__package__): mock_modules = util.mock_spec -Frozen_UsingPackagePEP451, Source_UsingPackagePEP451 = util.test_both( - Using__package__PEP302, __import__=import_util.__import__) + +(Frozen_UsingPackagePEP451, + Source_UsingPackagePEP451 + ) = util.test_both(Using__package__PEP451, __import__=util.__import__) class Setting__package__: @@ -95,7 +100,7 @@ class Setting__package__: """ - __import__ = import_util.__import__[1] + __import__ = util.__import__['Source'] # [top-level] def test_top_level(self): diff --git a/Lib/test/test_importlib/import_/test_api.py b/Lib/test/test_importlib/import_/test_api.py index 439c105..7069d9e 100644 --- a/Lib/test/test_importlib/import_/test_api.py +++ b/Lib/test/test_importlib/import_/test_api.py @@ -1,5 +1,4 @@ from .. import util -from . import util as import_util from importlib import machinery import sys @@ -18,6 +17,10 @@ class BadSpecFinderLoader: return spec @staticmethod + def create_module(spec): + return None + + @staticmethod def exec_module(module): if module.__name__ == SUBMOD_NAME: raise ImportError('I cannot be loaded!') @@ -79,15 +82,19 @@ class APITest: class OldAPITests(APITest): bad_finder_loader = BadLoaderFinder -Frozen_OldAPITests, Source_OldAPITests = util.test_both( - OldAPITests, __import__=import_util.__import__) + +(Frozen_OldAPITests, + Source_OldAPITests + ) = util.test_both(OldAPITests, __import__=util.__import__) class SpecAPITests(APITest): bad_finder_loader = BadSpecFinderLoader -Frozen_SpecAPITests, Source_SpecAPITests = util.test_both( - SpecAPITests, __import__=import_util.__import__) + +(Frozen_SpecAPITests, + Source_SpecAPITests + ) = util.test_both(SpecAPITests, __import__=util.__import__) if __name__ == '__main__': diff --git a/Lib/test/test_importlib/import_/test_caching.py b/Lib/test/test_importlib/import_/test_caching.py index c292ee4..8079add 100644 --- a/Lib/test/test_importlib/import_/test_caching.py +++ b/Lib/test/test_importlib/import_/test_caching.py @@ -1,6 +1,5 @@ """Test that sys.modules is used properly by import.""" from .. import util -from . import util as import_util import sys from types import MethodType import unittest @@ -39,15 +38,17 @@ class UseCache: self.__import__(name) self.assertEqual(cm.exception.name, name) -Frozen_UseCache, Source_UseCache = util.test_both( - UseCache, __import__=import_util.__import__) + +(Frozen_UseCache, + Source_UseCache + ) = util.test_both(UseCache, __import__=util.__import__) class ImportlibUseCache(UseCache, unittest.TestCase): # Pertinent only to PEP 302; exec_module() doesn't return a module. - __import__ = import_util.__import__[1] + __import__ = util.__import__['Source'] def create_mock(self, *names, return_=None): mock = util.mock_modules(*names) diff --git a/Lib/test/test_importlib/import_/test_fromlist.py b/Lib/test/test_importlib/import_/test_fromlist.py index a755b75..8045465 100644 --- a/Lib/test/test_importlib/import_/test_fromlist.py +++ b/Lib/test/test_importlib/import_/test_fromlist.py @@ -1,6 +1,5 @@ """Test that the semantics relating to the 'fromlist' argument are correct.""" from .. import util -from . import util as import_util import unittest @@ -29,8 +28,10 @@ class ReturnValue: module = self.__import__('pkg.module', fromlist=['attr']) self.assertEqual(module.__name__, 'pkg.module') -Frozen_ReturnValue, Source_ReturnValue = util.test_both( - ReturnValue, __import__=import_util.__import__) + +(Frozen_ReturnValue, + Source_ReturnValue + ) = util.test_both(ReturnValue, __import__=util.__import__) class HandlingFromlist: @@ -121,8 +122,10 @@ class HandlingFromlist: self.assertEqual(module.module1.__name__, 'pkg.module1') self.assertEqual(module.module2.__name__, 'pkg.module2') -Frozen_FromList, Source_FromList = util.test_both( - HandlingFromlist, __import__=import_util.__import__) + +(Frozen_FromList, + Source_FromList + ) = util.test_both(HandlingFromlist, __import__=util.__import__) if __name__ == '__main__': diff --git a/Lib/test/test_importlib/import_/test_meta_path.py b/Lib/test/test_importlib/import_/test_meta_path.py index 5eeb145..c452cdd 100644 --- a/Lib/test/test_importlib/import_/test_meta_path.py +++ b/Lib/test/test_importlib/import_/test_meta_path.py @@ -1,5 +1,4 @@ from .. import util -from . import util as import_util import importlib._bootstrap import sys from types import MethodType @@ -46,8 +45,10 @@ class CallingOrder: self.assertEqual(len(w), 1) self.assertTrue(issubclass(w[-1].category, ImportWarning)) -Frozen_CallingOrder, Source_CallingOrder = util.test_both( - CallingOrder, __import__=import_util.__import__) + +(Frozen_CallingOrder, + Source_CallingOrder + ) = util.test_both(CallingOrder, __import__=util.__import__) class CallSignature: @@ -100,19 +101,25 @@ class CallSignature: self.assertEqual(args[0], mod_name) self.assertIs(args[1], path) + class CallSignaturePEP302(CallSignature): mock_modules = util.mock_modules finder_name = 'find_module' -Frozen_CallSignaturePEP302, Source_CallSignaturePEP302 = util.test_both( - CallSignaturePEP302, __import__=import_util.__import__) + +(Frozen_CallSignaturePEP302, + Source_CallSignaturePEP302 + ) = util.test_both(CallSignaturePEP302, __import__=util.__import__) + class CallSignaturePEP451(CallSignature): mock_modules = util.mock_spec finder_name = 'find_spec' -Frozen_CallSignaturePEP451, Source_CallSignaturePEP451 = util.test_both( - CallSignaturePEP451, __import__=import_util.__import__) + +(Frozen_CallSignaturePEP451, + Source_CallSignaturePEP451 + ) = util.test_both(CallSignaturePEP451, __import__=util.__import__) if __name__ == '__main__': diff --git a/Lib/test/test_importlib/import_/test_packages.py b/Lib/test/test_importlib/import_/test_packages.py index 55a5d14..3755b84 100644 --- a/Lib/test/test_importlib/import_/test_packages.py +++ b/Lib/test/test_importlib/import_/test_packages.py @@ -1,5 +1,4 @@ from .. import util -from . import util as import_util import sys import unittest import importlib @@ -102,8 +101,10 @@ class ParentModuleTests: finally: support.unload(subname) -Frozen_ParentTests, Source_ParentTests = util.test_both( - ParentModuleTests, __import__=import_util.__import__) + +(Frozen_ParentTests, + Source_ParentTests + ) = util.test_both(ParentModuleTests, __import__=util.__import__) if __name__ == '__main__': diff --git a/Lib/test/test_importlib/import_/test_path.py b/Lib/test/test_importlib/import_/test_path.py index 1274f8c..9a3c4fe 100644 --- a/Lib/test/test_importlib/import_/test_path.py +++ b/Lib/test/test_importlib/import_/test_path.py @@ -1,11 +1,11 @@ from .. import util -from . import util as import_util importlib = util.import_importlib('importlib') machinery = util.import_importlib('importlib.machinery') import os import sys +import tempfile from types import ModuleType import unittest import warnings @@ -58,7 +58,7 @@ class FinderTests: module = '<test module>' path = '<test path>' importer = util.mock_spec(module) - hook = import_util.mock_path_hook(path, importer=importer) + hook = util.mock_path_hook(path, importer=importer) with util.import_state(path_hooks=[hook]): loader = self.machinery.PathFinder.find_module(module, [path]) self.assertIs(loader, importer) @@ -83,7 +83,7 @@ class FinderTests: path = '' module = '<test module>' importer = util.mock_spec(module) - hook = import_util.mock_path_hook(os.getcwd(), importer=importer) + hook = util.mock_path_hook(os.getcwd(), importer=importer) with util.import_state(path=[path], path_hooks=[hook]): loader = self.machinery.PathFinder.find_module(module) self.assertIs(loader, importer) @@ -112,8 +112,69 @@ class FinderTests: if email is not missing: sys.modules['email'] = email -Frozen_FinderTests, Source_FinderTests = util.test_both( - FinderTests, importlib=importlib, machinery=machinery) + def test_finder_with_find_module(self): + class TestFinder: + def find_module(self, fullname): + return self.to_return + failing_finder = TestFinder() + failing_finder.to_return = None + path = 'testing path' + with util.import_state(path_importer_cache={path: failing_finder}): + self.assertIsNone( + self.machinery.PathFinder.find_spec('whatever', [path])) + success_finder = TestFinder() + success_finder.to_return = __loader__ + with util.import_state(path_importer_cache={path: success_finder}): + spec = self.machinery.PathFinder.find_spec('whatever', [path]) + self.assertEqual(spec.loader, __loader__) + + def test_finder_with_find_loader(self): + class TestFinder: + loader = None + portions = [] + def find_loader(self, fullname): + return self.loader, self.portions + path = 'testing path' + with util.import_state(path_importer_cache={path: TestFinder()}): + self.assertIsNone( + self.machinery.PathFinder.find_spec('whatever', [path])) + success_finder = TestFinder() + success_finder.loader = __loader__ + with util.import_state(path_importer_cache={path: success_finder}): + spec = self.machinery.PathFinder.find_spec('whatever', [path]) + self.assertEqual(spec.loader, __loader__) + + def test_finder_with_find_spec(self): + class TestFinder: + spec = None + def find_spec(self, fullname, target=None): + return self.spec + path = 'testing path' + with util.import_state(path_importer_cache={path: TestFinder()}): + self.assertIsNone( + self.machinery.PathFinder.find_spec('whatever', [path])) + success_finder = TestFinder() + success_finder.spec = self.machinery.ModuleSpec('whatever', __loader__) + with util.import_state(path_importer_cache={path: success_finder}): + got = self.machinery.PathFinder.find_spec('whatever', [path]) + self.assertEqual(got, success_finder.spec) + + @unittest.skipIf(sys.platform == 'win32', "cwd can't not exist on Windows") + def test_deleted_cwd(self): + # Issue #22834 + self.addCleanup(os.chdir, os.getcwd()) + with tempfile.TemporaryDirectory() as path: + os.chdir(path) + with util.import_state(path=['']): + # Do not want FileNotFoundError raised. + self.assertIsNone(self.machinery.PathFinder.find_spec('whatever')) + + + + +(Frozen_FinderTests, + Source_FinderTests + ) = util.test_both(FinderTests, importlib=importlib, machinery=machinery) class PathEntryFinderTests: @@ -136,8 +197,10 @@ class PathEntryFinderTests: path_hooks=[Finder]): self.machinery.PathFinder.find_spec('importlib') -Frozen_PEFTests, Source_PEFTests = util.test_both( - PathEntryFinderTests, machinery=machinery) + +(Frozen_PEFTests, + Source_PEFTests + ) = util.test_both(PathEntryFinderTests, machinery=machinery) if __name__ == '__main__': diff --git a/Lib/test/test_importlib/import_/test_relative_imports.py b/Lib/test/test_importlib/import_/test_relative_imports.py index b216e9c..28bb6f7 100644 --- a/Lib/test/test_importlib/import_/test_relative_imports.py +++ b/Lib/test/test_importlib/import_/test_relative_imports.py @@ -1,6 +1,5 @@ """Test relative imports (PEP 328).""" from .. import util -from . import util as import_util import sys import unittest @@ -208,8 +207,10 @@ class RelativeImports: with self.assertRaises(KeyError): self.__import__('sys', level=1) -Frozen_RelativeImports, Source_RelativeImports = util.test_both( - RelativeImports, __import__=import_util.__import__) + +(Frozen_RelativeImports, + Source_RelativeImports + ) = util.test_both(RelativeImports, __import__=util.__import__) if __name__ == '__main__': diff --git a/Lib/test/test_importlib/import_/util.py b/Lib/test/test_importlib/import_/util.py deleted file mode 100644 index dcb490f..0000000 --- a/Lib/test/test_importlib/import_/util.py +++ /dev/null @@ -1,20 +0,0 @@ -from .. import util - -frozen_importlib, source_importlib = util.import_importlib('importlib') - -import builtins -import functools -import importlib -import unittest - - -__import__ = staticmethod(builtins.__import__), staticmethod(source_importlib.__import__) - - -def mock_path_hook(*entries, importer): - """A mock sys.path_hooks entry.""" - def hook(entry): - if entry not in entries: - raise ImportError - return importer - return hook diff --git a/Lib/test/test_importlib/source/test_case_sensitivity.py b/Lib/test/test_importlib/source/test_case_sensitivity.py index efd3146..29e95b2 100644 --- a/Lib/test/test_importlib/source/test_case_sensitivity.py +++ b/Lib/test/test_importlib/source/test_case_sensitivity.py @@ -1,6 +1,5 @@ """Test case-sensitivity (PEP 235).""" from .. import util -from . import util as source_util importlib = util.import_importlib('importlib') machinery = util.import_importlib('importlib.machinery') @@ -32,7 +31,7 @@ class CaseSensitivityTest: """Look for a module with matching and non-matching sensitivity.""" sensitive_pkg = 'sensitive.{0}'.format(self.name) insensitive_pkg = 'insensitive.{0}'.format(self.name.lower()) - context = source_util.create_modules(insensitive_pkg, sensitive_pkg) + context = util.create_modules(insensitive_pkg, sensitive_pkg) with context as mapping: sensitive_path = os.path.join(mapping['.root'], 'sensitive') insensitive_path = os.path.join(mapping['.root'], 'insensitive') @@ -63,20 +62,28 @@ class CaseSensitivityTest: self.assertIsNotNone(insensitive) self.assertIn(self.name, insensitive.get_filename(self.name)) + class CaseSensitivityTestPEP302(CaseSensitivityTest): def find(self, finder): return finder.find_module(self.name) -Frozen_CaseSensitivityTestPEP302, Source_CaseSensitivityTestPEP302 = util.test_both( - CaseSensitivityTestPEP302, importlib=importlib, machinery=machinery) + +(Frozen_CaseSensitivityTestPEP302, + Source_CaseSensitivityTestPEP302 + ) = util.test_both(CaseSensitivityTestPEP302, importlib=importlib, + machinery=machinery) + class CaseSensitivityTestPEP451(CaseSensitivityTest): def find(self, finder): found = finder.find_spec(self.name) return found.loader if found is not None else found -Frozen_CaseSensitivityTestPEP451, Source_CaseSensitivityTestPEP451 = util.test_both( - CaseSensitivityTestPEP451, importlib=importlib, machinery=machinery) + +(Frozen_CaseSensitivityTestPEP451, + Source_CaseSensitivityTestPEP451 + ) = util.test_both(CaseSensitivityTestPEP451, importlib=importlib, + machinery=machinery) if __name__ == '__main__': diff --git a/Lib/test/test_importlib/source/test_file_loader.py b/Lib/test/test_importlib/source/test_file_loader.py index 2d415f9..73f4c62 100644 --- a/Lib/test/test_importlib/source/test_file_loader.py +++ b/Lib/test/test_importlib/source/test_file_loader.py @@ -1,6 +1,5 @@ from .. import abc from .. import util -from . import util as source_util importlib = util.import_importlib('importlib') importlib_abc = util.import_importlib('importlib.abc') @@ -71,7 +70,7 @@ class SimpleTest(abc.LoaderTests): # [basic] def test_module(self): - with source_util.create_modules('_temp') as mapping: + with util.create_modules('_temp') as mapping: loader = self.machinery.SourceFileLoader('_temp', mapping['_temp']) with warnings.catch_warnings(): warnings.simplefilter('ignore', DeprecationWarning) @@ -83,7 +82,7 @@ class SimpleTest(abc.LoaderTests): self.assertEqual(getattr(module, attr), value) def test_package(self): - with source_util.create_modules('_pkg.__init__') as mapping: + with util.create_modules('_pkg.__init__') as mapping: loader = self.machinery.SourceFileLoader('_pkg', mapping['_pkg.__init__']) with warnings.catch_warnings(): @@ -98,7 +97,7 @@ class SimpleTest(abc.LoaderTests): def test_lacking_parent(self): - with source_util.create_modules('_pkg.__init__', '_pkg.mod')as mapping: + with util.create_modules('_pkg.__init__', '_pkg.mod')as mapping: loader = self.machinery.SourceFileLoader('_pkg.mod', mapping['_pkg.mod']) with warnings.catch_warnings(): @@ -115,7 +114,7 @@ class SimpleTest(abc.LoaderTests): return lambda name: fxn(name) + 1 def test_module_reuse(self): - with source_util.create_modules('_temp') as mapping: + with util.create_modules('_temp') as mapping: loader = self.machinery.SourceFileLoader('_temp', mapping['_temp']) with warnings.catch_warnings(): warnings.simplefilter('ignore', DeprecationWarning) @@ -139,7 +138,7 @@ class SimpleTest(abc.LoaderTests): attributes = ('__file__', '__path__', '__package__') value = '<test>' name = '_temp' - with source_util.create_modules(name) as mapping: + with util.create_modules(name) as mapping: orig_module = types.ModuleType(name) for attr in attributes: setattr(orig_module, attr, value) @@ -159,7 +158,7 @@ class SimpleTest(abc.LoaderTests): # [syntax error] def test_bad_syntax(self): - with source_util.create_modules('_temp') as mapping: + with util.create_modules('_temp') as mapping: with open(mapping['_temp'], 'w') as file: file.write('=') loader = self.machinery.SourceFileLoader('_temp', mapping['_temp']) @@ -190,11 +189,11 @@ class SimpleTest(abc.LoaderTests): if os.path.exists(pycache): shutil.rmtree(pycache) - @source_util.writes_bytecode_files + @util.writes_bytecode_files def test_timestamp_overflow(self): # When a modification timestamp is larger than 2**32, it should be # truncated rather than raise an OverflowError. - with source_util.create_modules('_temp') as mapping: + with util.create_modules('_temp') as mapping: source = mapping['_temp'] compiled = self.util.cache_from_source(source) with open(source, 'w') as f: @@ -236,9 +235,11 @@ class SimpleTest(abc.LoaderTests): warnings.simplefilter('ignore', DeprecationWarning) loader.load_module('bad name') -Frozen_SimpleTest, Source_SimpleTest = util.test_both( - SimpleTest, importlib=importlib, machinery=machinery, abc=importlib_abc, - util=importlib_util) + +(Frozen_SimpleTest, + Source_SimpleTest + ) = util.test_both(SimpleTest, importlib=importlib, machinery=machinery, + abc=importlib_abc, util=importlib_util) class BadBytecodeTest: @@ -275,45 +276,45 @@ class BadBytecodeTest: return bytecode_path def _test_empty_file(self, test, *, del_source=False): - with source_util.create_modules('_temp') as mapping: + with util.create_modules('_temp') as mapping: bc_path = self.manipulate_bytecode('_temp', mapping, lambda bc: b'', del_source=del_source) test('_temp', mapping, bc_path) - @source_util.writes_bytecode_files + @util.writes_bytecode_files def _test_partial_magic(self, test, *, del_source=False): # When their are less than 4 bytes to a .pyc, regenerate it if # possible, else raise ImportError. - with source_util.create_modules('_temp') as mapping: + with util.create_modules('_temp') as mapping: bc_path = self.manipulate_bytecode('_temp', mapping, lambda bc: bc[:3], del_source=del_source) test('_temp', mapping, bc_path) def _test_magic_only(self, test, *, del_source=False): - with source_util.create_modules('_temp') as mapping: + with util.create_modules('_temp') as mapping: bc_path = self.manipulate_bytecode('_temp', mapping, lambda bc: bc[:4], del_source=del_source) test('_temp', mapping, bc_path) def _test_partial_timestamp(self, test, *, del_source=False): - with source_util.create_modules('_temp') as mapping: + with util.create_modules('_temp') as mapping: bc_path = self.manipulate_bytecode('_temp', mapping, lambda bc: bc[:7], del_source=del_source) test('_temp', mapping, bc_path) def _test_partial_size(self, test, *, del_source=False): - with source_util.create_modules('_temp') as mapping: + with util.create_modules('_temp') as mapping: bc_path = self.manipulate_bytecode('_temp', mapping, lambda bc: bc[:11], del_source=del_source) test('_temp', mapping, bc_path) def _test_no_marshal(self, *, del_source=False): - with source_util.create_modules('_temp') as mapping: + with util.create_modules('_temp') as mapping: bc_path = self.manipulate_bytecode('_temp', mapping, lambda bc: bc[:12], del_source=del_source) @@ -322,7 +323,7 @@ class BadBytecodeTest: self.import_(file_path, '_temp') def _test_non_code_marshal(self, *, del_source=False): - with source_util.create_modules('_temp') as mapping: + with util.create_modules('_temp') as mapping: bytecode_path = self.manipulate_bytecode('_temp', mapping, lambda bc: bc[:12] + marshal.dumps(b'abcd'), del_source=del_source) @@ -333,7 +334,7 @@ class BadBytecodeTest: self.assertEqual(cm.exception.path, bytecode_path) def _test_bad_marshal(self, *, del_source=False): - with source_util.create_modules('_temp') as mapping: + with util.create_modules('_temp') as mapping: bytecode_path = self.manipulate_bytecode('_temp', mapping, lambda bc: bc[:12] + b'<test>', del_source=del_source) @@ -342,11 +343,12 @@ class BadBytecodeTest: self.import_(file_path, '_temp') def _test_bad_magic(self, test, *, del_source=False): - with source_util.create_modules('_temp') as mapping: + with util.create_modules('_temp') as mapping: bc_path = self.manipulate_bytecode('_temp', mapping, lambda bc: b'\x00\x00\x00\x00' + bc[4:]) test('_temp', mapping, bc_path) + class BadBytecodeTestPEP451(BadBytecodeTest): def import_(self, file, module_name): @@ -355,6 +357,7 @@ class BadBytecodeTestPEP451(BadBytecodeTest): module.__spec__ = self.util.spec_from_loader(module_name, loader) loader.exec_module(module) + class BadBytecodeTestPEP302(BadBytecodeTest): def import_(self, file, module_name): @@ -371,7 +374,7 @@ class SourceLoaderBadBytecodeTest: def setUpClass(cls): cls.loader = cls.machinery.SourceFileLoader - @source_util.writes_bytecode_files + @util.writes_bytecode_files def test_empty_file(self): # When a .pyc is empty, regenerate it if possible, else raise # ImportError. @@ -390,7 +393,7 @@ class SourceLoaderBadBytecodeTest: self._test_partial_magic(test) - @source_util.writes_bytecode_files + @util.writes_bytecode_files def test_magic_only(self): # When there is only the magic number, regenerate the .pyc if possible, # else raise EOFError. @@ -401,7 +404,7 @@ class SourceLoaderBadBytecodeTest: self._test_magic_only(test) - @source_util.writes_bytecode_files + @util.writes_bytecode_files def test_bad_magic(self): # When the magic number is different, the bytecode should be # regenerated. @@ -413,7 +416,7 @@ class SourceLoaderBadBytecodeTest: self._test_bad_magic(test) - @source_util.writes_bytecode_files + @util.writes_bytecode_files def test_partial_timestamp(self): # When the timestamp is partial, regenerate the .pyc, else # raise EOFError. @@ -424,7 +427,7 @@ class SourceLoaderBadBytecodeTest: self._test_partial_timestamp(test) - @source_util.writes_bytecode_files + @util.writes_bytecode_files def test_partial_size(self): # When the size is partial, regenerate the .pyc, else # raise EOFError. @@ -435,29 +438,29 @@ class SourceLoaderBadBytecodeTest: self._test_partial_size(test) - @source_util.writes_bytecode_files + @util.writes_bytecode_files def test_no_marshal(self): # When there is only the magic number and timestamp, raise EOFError. self._test_no_marshal() - @source_util.writes_bytecode_files + @util.writes_bytecode_files def test_non_code_marshal(self): self._test_non_code_marshal() # XXX ImportError when sourceless # [bad marshal] - @source_util.writes_bytecode_files + @util.writes_bytecode_files def test_bad_marshal(self): # Bad marshal data should raise a ValueError. self._test_bad_marshal() # [bad timestamp] - @source_util.writes_bytecode_files + @util.writes_bytecode_files def test_old_timestamp(self): # When the timestamp is older than the source, bytecode should be # regenerated. zeros = b'\x00\x00\x00\x00' - with source_util.create_modules('_temp') as mapping: + with util.create_modules('_temp') as mapping: py_compile.compile(mapping['_temp']) bytecode_path = self.util.cache_from_source(mapping['_temp']) with open(bytecode_path, 'r+b') as bytecode_file: @@ -471,10 +474,10 @@ class SourceLoaderBadBytecodeTest: self.assertEqual(bytecode_file.read(4), source_timestamp) # [bytecode read-only] - @source_util.writes_bytecode_files + @util.writes_bytecode_files def test_read_only_bytecode(self): # When bytecode is read-only but should be rewritten, fail silently. - with source_util.create_modules('_temp') as mapping: + with util.create_modules('_temp') as mapping: # Create bytecode that will need to be re-created. py_compile.compile(mapping['_temp']) bytecode_path = self.util.cache_from_source(mapping['_temp']) @@ -491,21 +494,29 @@ class SourceLoaderBadBytecodeTest: # Make writable for eventual clean-up. os.chmod(bytecode_path, stat.S_IWUSR) + class SourceLoaderBadBytecodeTestPEP451( SourceLoaderBadBytecodeTest, BadBytecodeTestPEP451): pass -Frozen_SourceBadBytecodePEP451, Source_SourceBadBytecodePEP451 = util.test_both( - SourceLoaderBadBytecodeTestPEP451, importlib=importlib, machinery=machinery, - abc=importlib_abc, util=importlib_util) + +(Frozen_SourceBadBytecodePEP451, + Source_SourceBadBytecodePEP451 + ) = util.test_both(SourceLoaderBadBytecodeTestPEP451, importlib=importlib, + machinery=machinery, abc=importlib_abc, + util=importlib_util) + class SourceLoaderBadBytecodeTestPEP302( SourceLoaderBadBytecodeTest, BadBytecodeTestPEP302): pass -Frozen_SourceBadBytecodePEP302, Source_SourceBadBytecodePEP302 = util.test_both( - SourceLoaderBadBytecodeTestPEP302, importlib=importlib, machinery=machinery, - abc=importlib_abc, util=importlib_util) + +(Frozen_SourceBadBytecodePEP302, + Source_SourceBadBytecodePEP302 + ) = util.test_both(SourceLoaderBadBytecodeTestPEP302, importlib=importlib, + machinery=machinery, abc=importlib_abc, + util=importlib_util) class SourcelessLoaderBadBytecodeTest: @@ -567,21 +578,29 @@ class SourcelessLoaderBadBytecodeTest: def test_non_code_marshal(self): self._test_non_code_marshal(del_source=True) + class SourcelessLoaderBadBytecodeTestPEP451(SourcelessLoaderBadBytecodeTest, BadBytecodeTestPEP451): pass -Frozen_SourcelessBadBytecodePEP451, Source_SourcelessBadBytecodePEP451 = util.test_both( - SourcelessLoaderBadBytecodeTestPEP451, importlib=importlib, - machinery=machinery, abc=importlib_abc, util=importlib_util) + +(Frozen_SourcelessBadBytecodePEP451, + Source_SourcelessBadBytecodePEP451 + ) = util.test_both(SourcelessLoaderBadBytecodeTestPEP451, importlib=importlib, + machinery=machinery, abc=importlib_abc, + util=importlib_util) + class SourcelessLoaderBadBytecodeTestPEP302(SourcelessLoaderBadBytecodeTest, BadBytecodeTestPEP302): pass -Frozen_SourcelessBadBytecodePEP302, Source_SourcelessBadBytecodePEP302 = util.test_both( - SourcelessLoaderBadBytecodeTestPEP302, importlib=importlib, - machinery=machinery, abc=importlib_abc, util=importlib_util) + +(Frozen_SourcelessBadBytecodePEP302, + Source_SourcelessBadBytecodePEP302 + ) = util.test_both(SourcelessLoaderBadBytecodeTestPEP302, importlib=importlib, + machinery=machinery, abc=importlib_abc, + util=importlib_util) if __name__ == '__main__': diff --git a/Lib/test/test_importlib/source/test_finder.py b/Lib/test/test_importlib/source/test_finder.py index 473297b..f372b85 100644 --- a/Lib/test/test_importlib/source/test_finder.py +++ b/Lib/test/test_importlib/source/test_finder.py @@ -1,6 +1,5 @@ from .. import abc from .. import util -from . import util as source_util machinery = util.import_importlib('importlib.machinery') @@ -60,7 +59,7 @@ class FinderTests(abc.FinderTests): """ if create is None: create = {test} - with source_util.create_modules(*create) as mapping: + with util.create_modules(*create) as mapping: if compile_: for name in compile_: py_compile.compile(mapping[name]) @@ -100,14 +99,14 @@ class FinderTests(abc.FinderTests): # [sub module] def test_module_in_package(self): - with source_util.create_modules('pkg.__init__', 'pkg.sub') as mapping: + with util.create_modules('pkg.__init__', 'pkg.sub') as mapping: pkg_dir = os.path.dirname(mapping['pkg.__init__']) loader = self.import_(pkg_dir, 'pkg.sub') self.assertTrue(hasattr(loader, 'load_module')) # [sub package] def test_package_in_package(self): - context = source_util.create_modules('pkg.__init__', 'pkg.sub.__init__') + context = util.create_modules('pkg.__init__', 'pkg.sub.__init__') with context as mapping: pkg_dir = os.path.dirname(mapping['pkg.__init__']) loader = self.import_(pkg_dir, 'pkg.sub') @@ -120,7 +119,7 @@ class FinderTests(abc.FinderTests): self.assertIn('__init__', loader.get_filename(name)) def test_failure(self): - with source_util.create_modules('blah') as mapping: + with util.create_modules('blah') as mapping: nothing = self.import_(mapping['.root'], 'sdfsadsadf') self.assertIsNone(nothing) @@ -147,7 +146,7 @@ class FinderTests(abc.FinderTests): # Regression test for http://bugs.python.org/issue14846 def test_dir_removal_handling(self): mod = 'mod' - with source_util.create_modules(mod) as mapping: + with util.create_modules(mod) as mapping: finder = self.get_finder(mapping['.root']) found = self._find(finder, 'mod', loader_only=True) self.assertIsNotNone(found) @@ -196,8 +195,10 @@ class FinderTestsPEP451(FinderTests): spec = finder.find_spec(name) return spec.loader if spec is not None else spec -Frozen_FinderTestsPEP451, Source_FinderTestsPEP451 = util.test_both( - FinderTestsPEP451, machinery=machinery) + +(Frozen_FinderTestsPEP451, + Source_FinderTestsPEP451 + ) = util.test_both(FinderTestsPEP451, machinery=machinery) class FinderTestsPEP420(FinderTests): @@ -210,8 +211,10 @@ class FinderTestsPEP420(FinderTests): loader_portions = finder.find_loader(name) return loader_portions[0] if loader_only else loader_portions -Frozen_FinderTestsPEP420, Source_FinderTestsPEP420 = util.test_both( - FinderTestsPEP420, machinery=machinery) + +(Frozen_FinderTestsPEP420, + Source_FinderTestsPEP420 + ) = util.test_both(FinderTestsPEP420, machinery=machinery) class FinderTestsPEP302(FinderTests): @@ -223,9 +226,10 @@ class FinderTestsPEP302(FinderTests): warnings.simplefilter("ignore", DeprecationWarning) return finder.find_module(name) -Frozen_FinderTestsPEP302, Source_FinderTestsPEP302 = util.test_both( - FinderTestsPEP302, machinery=machinery) +(Frozen_FinderTestsPEP302, + Source_FinderTestsPEP302 + ) = util.test_both(FinderTestsPEP302, machinery=machinery) if __name__ == '__main__': diff --git a/Lib/test/test_importlib/source/test_path_hook.py b/Lib/test/test_importlib/source/test_path_hook.py index 92da772..e6a2415 100644 --- a/Lib/test/test_importlib/source/test_path_hook.py +++ b/Lib/test/test_importlib/source/test_path_hook.py @@ -1,5 +1,4 @@ from .. import util -from . import util as source_util machinery = util.import_importlib('importlib.machinery') @@ -15,7 +14,7 @@ class PathHookTest: self.machinery.SOURCE_SUFFIXES)) def test_success(self): - with source_util.create_modules('dummy') as mapping: + with util.create_modules('dummy') as mapping: self.assertTrue(hasattr(self.path_hook()(mapping['.root']), 'find_module')) @@ -23,7 +22,10 @@ class PathHookTest: # The empty string represents the cwd. self.assertTrue(hasattr(self.path_hook()(''), 'find_module')) -Frozen_PathHookTest, Source_PathHooktest = util.test_both(PathHookTest, machinery=machinery) + +(Frozen_PathHookTest, + Source_PathHooktest + ) = util.test_both(PathHookTest, machinery=machinery) if __name__ == '__main__': diff --git a/Lib/test/test_importlib/source/test_source_encoding.py b/Lib/test/test_importlib/source/test_source_encoding.py index c62dfa1..b604afb 100644 --- a/Lib/test/test_importlib/source/test_source_encoding.py +++ b/Lib/test/test_importlib/source/test_source_encoding.py @@ -1,5 +1,4 @@ from .. import util -from . import util as source_util machinery = util.import_importlib('importlib.machinery') @@ -37,7 +36,7 @@ class EncodingTest: module_name = '_temp' def run_test(self, source): - with source_util.create_modules(self.module_name) as mapping: + with util.create_modules(self.module_name) as mapping: with open(mapping[self.module_name], 'wb') as file: file.write(source) loader = self.machinery.SourceFileLoader(self.module_name, @@ -89,6 +88,7 @@ class EncodingTest: with self.assertRaises(SyntaxError): self.run_test(source) + class EncodingTestPEP451(EncodingTest): def load(self, loader): @@ -97,8 +97,11 @@ class EncodingTestPEP451(EncodingTest): loader.exec_module(module) return module -Frozen_EncodingTestPEP451, Source_EncodingTestPEP451 = util.test_both( - EncodingTestPEP451, machinery=machinery) + +(Frozen_EncodingTestPEP451, + Source_EncodingTestPEP451 + ) = util.test_both(EncodingTestPEP451, machinery=machinery) + class EncodingTestPEP302(EncodingTest): @@ -107,8 +110,10 @@ class EncodingTestPEP302(EncodingTest): warnings.simplefilter('ignore', DeprecationWarning) return loader.load_module(self.module_name) -Frozen_EncodingTestPEP302, Source_EncodingTestPEP302 = util.test_both( - EncodingTestPEP302, machinery=machinery) + +(Frozen_EncodingTestPEP302, + Source_EncodingTestPEP302 + ) = util.test_both(EncodingTestPEP302, machinery=machinery) class LineEndingTest: @@ -120,7 +125,7 @@ class LineEndingTest: module_name = '_temp' source_lines = [b"a = 42", b"b = -13", b''] source = line_ending.join(source_lines) - with source_util.create_modules(module_name) as mapping: + with util.create_modules(module_name) as mapping: with open(mapping[module_name], 'wb') as file: file.write(source) loader = self.machinery.SourceFileLoader(module_name, @@ -139,6 +144,7 @@ class LineEndingTest: def test_lf(self): self.run_test(b'\n') + class LineEndingTestPEP451(LineEndingTest): def load(self, loader, module_name): @@ -147,8 +153,11 @@ class LineEndingTestPEP451(LineEndingTest): loader.exec_module(module) return module -Frozen_LineEndingTestPEP451, Source_LineEndingTestPEP451 = util.test_both( - LineEndingTestPEP451, machinery=machinery) + +(Frozen_LineEndingTestPEP451, + Source_LineEndingTestPEP451 + ) = util.test_both(LineEndingTestPEP451, machinery=machinery) + class LineEndingTestPEP302(LineEndingTest): @@ -157,8 +166,10 @@ class LineEndingTestPEP302(LineEndingTest): warnings.simplefilter('ignore', DeprecationWarning) return loader.load_module(module_name) -Frozen_LineEndingTestPEP302, Source_LineEndingTestPEP302 = util.test_both( - LineEndingTestPEP302, machinery=machinery) + +(Frozen_LineEndingTestPEP302, + Source_LineEndingTestPEP302 + ) = util.test_both(LineEndingTestPEP302, machinery=machinery) if __name__ == '__main__': diff --git a/Lib/test/test_importlib/source/util.py b/Lib/test/test_importlib/source/util.py deleted file mode 100644 index 63cd25a..0000000 --- a/Lib/test/test_importlib/source/util.py +++ /dev/null @@ -1,96 +0,0 @@ -from .. import util -import contextlib -import errno -import functools -import os -import os.path -import sys -import tempfile -from test import support - - -def writes_bytecode_files(fxn): - """Decorator to protect sys.dont_write_bytecode from mutation and to skip - tests that require it to be set to False.""" - if sys.dont_write_bytecode: - return lambda *args, **kwargs: None - @functools.wraps(fxn) - def wrapper(*args, **kwargs): - original = sys.dont_write_bytecode - sys.dont_write_bytecode = False - try: - to_return = fxn(*args, **kwargs) - finally: - sys.dont_write_bytecode = original - return to_return - return wrapper - - -def ensure_bytecode_path(bytecode_path): - """Ensure that the __pycache__ directory for PEP 3147 pyc file exists. - - :param bytecode_path: File system path to PEP 3147 pyc file. - """ - try: - os.mkdir(os.path.dirname(bytecode_path)) - except OSError as error: - if error.errno != errno.EEXIST: - raise - - -@contextlib.contextmanager -def create_modules(*names): - """Temporarily create each named module with an attribute (named 'attr') - that contains the name passed into the context manager that caused the - creation of the module. - - All files are created in a temporary directory returned by - tempfile.mkdtemp(). This directory is inserted at the beginning of - sys.path. When the context manager exits all created files (source and - bytecode) are explicitly deleted. - - No magic is performed when creating packages! This means that if you create - a module within a package you must also create the package's __init__ as - well. - - """ - source = 'attr = {0!r}' - created_paths = [] - mapping = {} - state_manager = None - uncache_manager = None - try: - temp_dir = tempfile.mkdtemp() - mapping['.root'] = temp_dir - import_names = set() - for name in names: - if not name.endswith('__init__'): - import_name = name - else: - import_name = name[:-len('.__init__')] - import_names.add(import_name) - if import_name in sys.modules: - del sys.modules[import_name] - name_parts = name.split('.') - file_path = temp_dir - for directory in name_parts[:-1]: - file_path = os.path.join(file_path, directory) - if not os.path.exists(file_path): - os.mkdir(file_path) - created_paths.append(file_path) - file_path = os.path.join(file_path, name_parts[-1] + '.py') - with open(file_path, 'w') as file: - file.write(source.format(name)) - created_paths.append(file_path) - mapping[name] = file_path - uncache_manager = util.uncache(*import_names) - uncache_manager.__enter__() - state_manager = util.import_state(path=[temp_dir]) - state_manager.__enter__() - yield mapping - finally: - if state_manager is not None: - state_manager.__exit__(None, None, None) - if uncache_manager is not None: - uncache_manager.__exit__(None, None, None) - support.rmtree(temp_dir) diff --git a/Lib/test/test_importlib/test_abc.py b/Lib/test/test_importlib/test_abc.py index a1f8e76..d4bf915 100644 --- a/Lib/test/test_importlib/test_abc.py +++ b/Lib/test/test_importlib/test_abc.py @@ -10,12 +10,13 @@ import unittest from unittest import mock import warnings -from . import util +from . import util as test_util + +init = test_util.import_importlib('importlib') +abc = test_util.import_importlib('importlib.abc') +machinery = test_util.import_importlib('importlib.machinery') +util = test_util.import_importlib('importlib.util') -frozen_init, source_init = util.import_importlib('importlib') -frozen_abc, source_abc = util.import_importlib('importlib.abc') -machinery = util.import_importlib('importlib.machinery') -frozen_util, source_util = util.import_importlib('importlib.util') ##### Inheritance ############################################################## class InheritanceTests: @@ -26,8 +27,7 @@ class InheritanceTests: subclasses = [] superclasses = [] - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + def setUp(self): self.superclasses = [getattr(self.abc, class_name) for class_name in self.superclass_names] if hasattr(self, 'subclass_names'): @@ -36,11 +36,11 @@ class InheritanceTests: # checking across module boundaries (i.e. the _bootstrap in abc is # not the same as the one in machinery). That means stealing one of # the modules from the other to make sure the same instance is used. - self.subclasses = [getattr(self.abc.machinery, class_name) - for class_name in self.subclass_names] + machinery = self.abc.machinery + self.subclasses = [getattr(machinery, class_name) + for class_name in self.subclass_names] assert self.subclasses or self.superclasses, self.__class__ - testing = self.__class__.__name__.partition('_')[2] - self.__test = getattr(self.abc, testing) + self.__test = getattr(self.abc, self._NAME) def test_subclasses(self): # Test that the expected subclasses inherit. @@ -54,94 +54,97 @@ class InheritanceTests: self.assertTrue(issubclass(self.__test, superclass), "{0} is not a superclass of {1}".format(superclass, self.__test)) -def create_inheritance_tests(base_class): - def set_frozen(ns): - ns['abc'] = frozen_abc - def set_source(ns): - ns['abc'] = source_abc - - classes = [] - for prefix, ns_set in [('Frozen', set_frozen), ('Source', set_source)]: - classes.append(types.new_class('_'.join([prefix, base_class.__name__]), - (base_class, unittest.TestCase), - exec_body=ns_set)) - return classes - class MetaPathFinder(InheritanceTests): superclass_names = ['Finder'] subclass_names = ['BuiltinImporter', 'FrozenImporter', 'PathFinder', 'WindowsRegistryFinder'] -tests = create_inheritance_tests(MetaPathFinder) -Frozen_MetaPathFinderInheritanceTests, Source_MetaPathFinderInheritanceTests = tests + +(Frozen_MetaPathFinderInheritanceTests, + Source_MetaPathFinderInheritanceTests + ) = test_util.test_both(MetaPathFinder, abc=abc) class PathEntryFinder(InheritanceTests): superclass_names = ['Finder'] subclass_names = ['FileFinder'] -tests = create_inheritance_tests(PathEntryFinder) -Frozen_PathEntryFinderInheritanceTests, Source_PathEntryFinderInheritanceTests = tests + +(Frozen_PathEntryFinderInheritanceTests, + Source_PathEntryFinderInheritanceTests + ) = test_util.test_both(PathEntryFinder, abc=abc) class ResourceLoader(InheritanceTests): superclass_names = ['Loader'] -tests = create_inheritance_tests(ResourceLoader) -Frozen_ResourceLoaderInheritanceTests, Source_ResourceLoaderInheritanceTests = tests + +(Frozen_ResourceLoaderInheritanceTests, + Source_ResourceLoaderInheritanceTests + ) = test_util.test_both(ResourceLoader, abc=abc) class InspectLoader(InheritanceTests): superclass_names = ['Loader'] subclass_names = ['BuiltinImporter', 'FrozenImporter', 'ExtensionFileLoader'] -tests = create_inheritance_tests(InspectLoader) -Frozen_InspectLoaderInheritanceTests, Source_InspectLoaderInheritanceTests = tests + +(Frozen_InspectLoaderInheritanceTests, + Source_InspectLoaderInheritanceTests + ) = test_util.test_both(InspectLoader, abc=abc) class ExecutionLoader(InheritanceTests): superclass_names = ['InspectLoader'] subclass_names = ['ExtensionFileLoader'] -tests = create_inheritance_tests(ExecutionLoader) -Frozen_ExecutionLoaderInheritanceTests, Source_ExecutionLoaderInheritanceTests = tests + +(Frozen_ExecutionLoaderInheritanceTests, + Source_ExecutionLoaderInheritanceTests + ) = test_util.test_both(ExecutionLoader, abc=abc) class FileLoader(InheritanceTests): superclass_names = ['ResourceLoader', 'ExecutionLoader'] subclass_names = ['SourceFileLoader', 'SourcelessFileLoader'] -tests = create_inheritance_tests(FileLoader) -Frozen_FileLoaderInheritanceTests, Source_FileLoaderInheritanceTests = tests + +(Frozen_FileLoaderInheritanceTests, + Source_FileLoaderInheritanceTests + ) = test_util.test_both(FileLoader, abc=abc) class SourceLoader(InheritanceTests): superclass_names = ['ResourceLoader', 'ExecutionLoader'] subclass_names = ['SourceFileLoader'] -tests = create_inheritance_tests(SourceLoader) -Frozen_SourceLoaderInheritanceTests, Source_SourceLoaderInheritanceTests = tests + +(Frozen_SourceLoaderInheritanceTests, + Source_SourceLoaderInheritanceTests + ) = test_util.test_both(SourceLoader, abc=abc) + ##### Default return values #################################################### -def make_abc_subclasses(base_class): - classes = [] - for kind, abc in [('Frozen', frozen_abc), ('Source', source_abc)]: - name = '_'.join([kind, base_class.__name__]) - base_classes = base_class, getattr(abc, base_class.__name__) - classes.append(types.new_class(name, base_classes)) - return classes - -def make_return_value_tests(base_class, test_class): - frozen_class, source_class = make_abc_subclasses(base_class) - tests = [] - for prefix, class_in_test in [('Frozen', frozen_class), ('Source', source_class)]: - def set_ns(ns): - ns['ins'] = class_in_test() - tests.append(types.new_class('_'.join([prefix, test_class.__name__]), - (test_class, unittest.TestCase), - exec_body=set_ns)) - return tests + +def make_abc_subclasses(base_class, name=None, inst=False, **kwargs): + if name is None: + name = base_class.__name__ + base = {kind: getattr(splitabc, name) + for kind, splitabc in abc.items()} + return {cls._KIND: cls() if inst else cls + for cls in test_util.split_frozen(base_class, base, **kwargs)} + + +class ABCTestHarness: + + @property + def ins(self): + # Lazily set ins on the class. + cls = self.SPLIT[self._KIND] + ins = cls() + self.__class__.ins = ins + return ins class MetaPathFinder: @@ -149,10 +152,10 @@ class MetaPathFinder: def find_module(self, fullname, path): return super().find_module(fullname, path) -Frozen_MPF, Source_MPF = make_abc_subclasses(MetaPathFinder) +class MetaPathFinderDefaultsTests(ABCTestHarness): -class MetaPathFinderDefaultsTests: + SPLIT = make_abc_subclasses(MetaPathFinder) def test_find_module(self): # Default should return None. @@ -163,8 +166,9 @@ class MetaPathFinderDefaultsTests: self.ins.invalidate_caches() -tests = make_return_value_tests(MetaPathFinder, MetaPathFinderDefaultsTests) -Frozen_MPFDefaultTests, Source_MPFDefaultTests = tests +(Frozen_MPFDefaultTests, + Source_MPFDefaultTests + ) = test_util.test_both(MetaPathFinderDefaultsTests) class PathEntryFinder: @@ -172,10 +176,10 @@ class PathEntryFinder: def find_loader(self, fullname): return super().find_loader(fullname) -Frozen_PEF, Source_PEF = make_abc_subclasses(PathEntryFinder) +class PathEntryFinderDefaultsTests(ABCTestHarness): -class PathEntryFinderDefaultsTests: + SPLIT = make_abc_subclasses(PathEntryFinder) def test_find_loader(self): self.assertEqual((None, []), self.ins.find_loader('something')) @@ -188,8 +192,9 @@ class PathEntryFinderDefaultsTests: self.ins.invalidate_caches() -tests = make_return_value_tests(PathEntryFinder, PathEntryFinderDefaultsTests) -Frozen_PEFDefaultTests, Source_PEFDefaultTests = tests +(Frozen_PEFDefaultTests, + Source_PEFDefaultTests + ) = test_util.test_both(PathEntryFinderDefaultsTests) class Loader: @@ -198,10 +203,9 @@ class Loader: return super().load_module(fullname) -Frozen_L, Source_L = make_abc_subclasses(Loader) +class LoaderDefaultsTests(ABCTestHarness): - -class LoaderDefaultsTests: + SPLIT = make_abc_subclasses(Loader) def test_load_module(self): with self.assertRaises(ImportError): @@ -217,8 +221,9 @@ class LoaderDefaultsTests: self.assertTrue(repr(mod)) -tests = make_return_value_tests(Loader, LoaderDefaultsTests) -Frozen_LDefaultTests, SourceLDefaultTests = tests +(Frozen_LDefaultTests, + SourceLDefaultTests + ) = test_util.test_both(LoaderDefaultsTests) class ResourceLoader(Loader): @@ -227,18 +232,18 @@ class ResourceLoader(Loader): return super().get_data(path) -Frozen_RL, Source_RL = make_abc_subclasses(ResourceLoader) - +class ResourceLoaderDefaultsTests(ABCTestHarness): -class ResourceLoaderDefaultsTests: + SPLIT = make_abc_subclasses(ResourceLoader) def test_get_data(self): with self.assertRaises(IOError): self.ins.get_data('/some/path') -tests = make_return_value_tests(ResourceLoader, ResourceLoaderDefaultsTests) -Frozen_RLDefaultTests, Source_RLDefaultTests = tests +(Frozen_RLDefaultTests, + Source_RLDefaultTests + ) = test_util.test_both(ResourceLoaderDefaultsTests) class InspectLoader(Loader): @@ -250,10 +255,12 @@ class InspectLoader(Loader): return super().get_source(fullname) -Frozen_IL, Source_IL = make_abc_subclasses(InspectLoader) +SPLIT_IL = make_abc_subclasses(InspectLoader) -class InspectLoaderDefaultsTests: +class InspectLoaderDefaultsTests(ABCTestHarness): + + SPLIT = SPLIT_IL def test_is_package(self): with self.assertRaises(ImportError): @@ -264,8 +271,9 @@ class InspectLoaderDefaultsTests: self.ins.get_source('blah') -tests = make_return_value_tests(InspectLoader, InspectLoaderDefaultsTests) -Frozen_ILDefaultTests, Source_ILDefaultTests = tests +(Frozen_ILDefaultTests, + Source_ILDefaultTests + ) = test_util.test_both(InspectLoaderDefaultsTests) class ExecutionLoader(InspectLoader): @@ -273,21 +281,25 @@ class ExecutionLoader(InspectLoader): def get_filename(self, fullname): return super().get_filename(fullname) -Frozen_EL, Source_EL = make_abc_subclasses(ExecutionLoader) + +SPLIT_EL = make_abc_subclasses(ExecutionLoader) -class ExecutionLoaderDefaultsTests: +class ExecutionLoaderDefaultsTests(ABCTestHarness): + + SPLIT = SPLIT_EL def test_get_filename(self): with self.assertRaises(ImportError): self.ins.get_filename('blah') -tests = make_return_value_tests(ExecutionLoader, InspectLoaderDefaultsTests) -Frozen_ELDefaultTests, Source_ELDefaultsTests = tests +(Frozen_ELDefaultTests, + Source_ELDefaultsTests + ) = test_util.test_both(InspectLoaderDefaultsTests) -##### MetaPathFinder concrete methods ########################################## +##### MetaPathFinder concrete methods ########################################## class MetaPathFinderFindModuleTests: @classmethod @@ -317,13 +329,12 @@ class MetaPathFinderFindModuleTests: self.assertIs(found, spec.loader) -Frozen_MPFFindModuleTests, Source_MPFFindModuleTests = util.test_both( - MetaPathFinderFindModuleTests, - abc=(frozen_abc, source_abc), - util=(frozen_util, source_util)) +(Frozen_MPFFindModuleTests, + Source_MPFFindModuleTests + ) = test_util.test_both(MetaPathFinderFindModuleTests, abc=abc, util=util) -##### PathEntryFinder concrete methods ######################################### +##### PathEntryFinder concrete methods ######################################### class PathEntryFinderFindLoaderTests: @classmethod @@ -361,11 +372,10 @@ class PathEntryFinderFindLoaderTests: self.assertEqual(paths, found[1]) -Frozen_PEFFindLoaderTests, Source_PEFFindLoaderTests = util.test_both( - PathEntryFinderFindLoaderTests, - abc=(frozen_abc, source_abc), - machinery=machinery, - util=(frozen_util, source_util)) +(Frozen_PEFFindLoaderTests, + Source_PEFFindLoaderTests + ) = test_util.test_both(PathEntryFinderFindLoaderTests, abc=abc, util=util, + machinery=machinery) ##### Loader concrete methods ################################################## @@ -386,7 +396,7 @@ class LoaderLoadModuleTests: def test_fresh(self): loader = self.loader() name = 'blah' - with util.uncache(name): + with test_util.uncache(name): loader.load_module(name) module = loader.found self.assertIs(sys.modules[name], module) @@ -404,7 +414,7 @@ class LoaderLoadModuleTests: module = types.ModuleType(name) module.__spec__ = self.util.spec_from_loader(name, loader) module.__loader__ = loader - with util.uncache(name): + with test_util.uncache(name): sys.modules[name] = module loader.load_module(name) found = loader.found @@ -412,10 +422,9 @@ class LoaderLoadModuleTests: self.assertIs(module, sys.modules[name]) -Frozen_LoaderLoadModuleTests, Source_LoaderLoadModuleTests = util.test_both( - LoaderLoadModuleTests, - abc=(frozen_abc, source_abc), - util=(frozen_util, source_util)) +(Frozen_LoaderLoadModuleTests, + Source_LoaderLoadModuleTests + ) = test_util.test_both(LoaderLoadModuleTests, abc=abc, util=util) ##### InspectLoader concrete methods ########################################### @@ -461,11 +470,10 @@ class InspectLoaderSourceToCodeTests: self.assertEqual(code.co_filename, '<string>') -class Frozen_ILSourceToCodeTests(InspectLoaderSourceToCodeTests, unittest.TestCase): - InspectLoaderSubclass = Frozen_IL - -class Source_ILSourceToCodeTests(InspectLoaderSourceToCodeTests, unittest.TestCase): - InspectLoaderSubclass = Source_IL +(Frozen_ILSourceToCodeTests, + Source_ILSourceToCodeTests + ) = test_util.test_both(InspectLoaderSourceToCodeTests, + InspectLoaderSubclass=SPLIT_IL) class InspectLoaderGetCodeTests: @@ -495,11 +503,10 @@ class InspectLoaderGetCodeTests: loader.get_code('blah') -class Frozen_ILGetCodeTests(InspectLoaderGetCodeTests, unittest.TestCase): - InspectLoaderSubclass = Frozen_IL - -class Source_ILGetCodeTests(InspectLoaderGetCodeTests, unittest.TestCase): - InspectLoaderSubclass = Source_IL +(Frozen_ILGetCodeTests, + Source_ILGetCodeTests + ) = test_util.test_both(InspectLoaderGetCodeTests, + InspectLoaderSubclass=SPLIT_IL) class InspectLoaderLoadModuleTests: @@ -543,11 +550,10 @@ class InspectLoaderLoadModuleTests: self.assertEqual(module, sys.modules[self.module_name]) -class Frozen_ILLoadModuleTests(InspectLoaderLoadModuleTests, unittest.TestCase): - InspectLoaderSubclass = Frozen_IL - -class Source_ILLoadModuleTests(InspectLoaderLoadModuleTests, unittest.TestCase): - InspectLoaderSubclass = Source_IL +(Frozen_ILLoadModuleTests, + Source_ILLoadModuleTests + ) = test_util.test_both(InspectLoaderLoadModuleTests, + InspectLoaderSubclass=SPLIT_IL) ##### ExecutionLoader concrete methods ######################################### @@ -608,15 +614,14 @@ class ExecutionLoaderGetCodeTests: self.assertEqual(module.attr, 42) -class Frozen_ELGetCodeTests(ExecutionLoaderGetCodeTests, unittest.TestCase): - ExecutionLoaderSubclass = Frozen_EL - -class Source_ELGetCodeTests(ExecutionLoaderGetCodeTests, unittest.TestCase): - ExecutionLoaderSubclass = Source_EL +(Frozen_ELGetCodeTests, + Source_ELGetCodeTests + ) = test_util.test_both(ExecutionLoaderGetCodeTests, + ExecutionLoaderSubclass=SPLIT_EL) ##### SourceLoader concrete methods ############################################ -class SourceLoader: +class SourceOnlyLoader: # Globals that should be defined for all modules. source = (b"_ = '::'.join([__name__, __file__, __cached__, __package__, " @@ -637,10 +642,10 @@ class SourceLoader: return '<module>' -Frozen_SourceOnlyL, Source_SourceOnlyL = make_abc_subclasses(SourceLoader) +SPLIT_SOL = make_abc_subclasses(SourceOnlyLoader, 'SourceLoader') -class SourceLoader(SourceLoader): +class SourceLoader(SourceOnlyLoader): source_mtime = 1 @@ -677,11 +682,7 @@ class SourceLoader(SourceLoader): return path == self.bytecode_path -Frozen_SL, Source_SL = make_abc_subclasses(SourceLoader) -Frozen_SL.util = frozen_util -Source_SL.util = source_util -Frozen_SL.init = frozen_init -Source_SL.init = source_init +SPLIT_SL = make_abc_subclasses(SourceLoader, util=util, init=init) class SourceLoaderTestHarness: @@ -765,7 +766,7 @@ class SourceOnlyLoaderTests(SourceLoaderTestHarness): # Loading a module should set __name__, __loader__, __package__, # __path__ (for packages), __file__, and __cached__. # The module should also be put into sys.modules. - with util.uncache(self.name): + with test_util.uncache(self.name): with warnings.catch_warnings(): warnings.simplefilter('ignore', DeprecationWarning) module = self.loader.load_module(self.name) @@ -778,7 +779,7 @@ class SourceOnlyLoaderTests(SourceLoaderTestHarness): # is a package. # Testing the values for a package are covered by test_load_module. self.setUp(is_package=False) - with util.uncache(self.name): + with test_util.uncache(self.name): with warnings.catch_warnings(): warnings.simplefilter('ignore', DeprecationWarning) module = self.loader.load_module(self.name) @@ -798,13 +799,10 @@ class SourceOnlyLoaderTests(SourceLoaderTestHarness): self.assertEqual(returned_source, source) -class Frozen_SourceOnlyLTests(SourceOnlyLoaderTests, unittest.TestCase): - loader_mock = Frozen_SourceOnlyL - util = frozen_util - -class Source_SourceOnlyLTests(SourceOnlyLoaderTests, unittest.TestCase): - loader_mock = Source_SourceOnlyL - util = source_util +(Frozen_SourceOnlyLoaderTests, + Source_SourceOnlyLoaderTests + ) = test_util.test_both(SourceOnlyLoaderTests, util=util, + loader_mock=SPLIT_SOL) @unittest.skipIf(sys.dont_write_bytecode, "sys.dont_write_bytecode is true") @@ -896,15 +894,10 @@ class SourceLoaderBytecodeTests(SourceLoaderTestHarness): self.verify_code(code_object) -class Frozen_SLBytecodeTests(SourceLoaderBytecodeTests, unittest.TestCase): - loader_mock = Frozen_SL - init = frozen_init - util = frozen_util - -class SourceSLBytecodeTests(SourceLoaderBytecodeTests, unittest.TestCase): - loader_mock = Source_SL - init = source_init - util = source_util +(Frozen_SLBytecodeTests, + SourceSLBytecodeTests + ) = test_util.test_both(SourceLoaderBytecodeTests, init=init, util=util, + loader_mock=SPLIT_SL) class SourceLoaderGetSourceTests: @@ -940,11 +933,10 @@ class SourceLoaderGetSourceTests: self.assertEqual(mock.get_source(name), expect) -class Frozen_SourceOnlyLGetSourceTests(SourceLoaderGetSourceTests, unittest.TestCase): - SourceOnlyLoaderMock = Frozen_SourceOnlyL - -class Source_SourceOnlyLGetSourceTests(SourceLoaderGetSourceTests, unittest.TestCase): - SourceOnlyLoaderMock = Source_SourceOnlyL +(Frozen_SourceOnlyLoaderGetSourceTests, + Source_SourceOnlyLoaderGetSourceTests + ) = test_util.test_both(SourceLoaderGetSourceTests, + SourceOnlyLoaderMock=SPLIT_SOL) if __name__ == '__main__': diff --git a/Lib/test/test_importlib/test_api.py b/Lib/test/test_importlib/test_api.py index 2a2d42b..6bc3c56 100644 --- a/Lib/test/test_importlib/test_api.py +++ b/Lib/test/test_importlib/test_api.py @@ -1,8 +1,8 @@ -from . import util +from . import util as test_util -frozen_init, source_init = util.import_importlib('importlib') -frozen_util, source_util = util.import_importlib('importlib.util') -frozen_machinery, source_machinery = util.import_importlib('importlib.machinery') +init = test_util.import_importlib('importlib') +util = test_util.import_importlib('importlib.util') +machinery = test_util.import_importlib('importlib.machinery') import os.path import sys @@ -18,8 +18,8 @@ class ImportModuleTests: def test_module_import(self): # Test importing a top-level module. - with util.mock_modules('top_level') as mock: - with util.import_state(meta_path=[mock]): + with test_util.mock_modules('top_level') as mock: + with test_util.import_state(meta_path=[mock]): module = self.init.import_module('top_level') self.assertEqual(module.__name__, 'top_level') @@ -28,8 +28,8 @@ class ImportModuleTests: pkg_name = 'pkg' pkg_long_name = '{0}.__init__'.format(pkg_name) name = '{0}.mod'.format(pkg_name) - with util.mock_modules(pkg_long_name, name) as mock: - with util.import_state(meta_path=[mock]): + with test_util.mock_modules(pkg_long_name, name) as mock: + with test_util.import_state(meta_path=[mock]): module = self.init.import_module(name) self.assertEqual(module.__name__, name) @@ -40,16 +40,16 @@ class ImportModuleTests: module_name = 'mod' absolute_name = '{0}.{1}'.format(pkg_name, module_name) relative_name = '.{0}'.format(module_name) - with util.mock_modules(pkg_long_name, absolute_name) as mock: - with util.import_state(meta_path=[mock]): + with test_util.mock_modules(pkg_long_name, absolute_name) as mock: + with test_util.import_state(meta_path=[mock]): self.init.import_module(pkg_name) module = self.init.import_module(relative_name, pkg_name) self.assertEqual(module.__name__, absolute_name) def test_deep_relative_package_import(self): modules = ['a.__init__', 'a.b.__init__', 'a.c'] - with util.mock_modules(*modules) as mock: - with util.import_state(meta_path=[mock]): + with test_util.mock_modules(*modules) as mock: + with test_util.import_state(meta_path=[mock]): self.init.import_module('a') self.init.import_module('a.b') module = self.init.import_module('..c', 'a.b') @@ -61,8 +61,8 @@ class ImportModuleTests: pkg_name = 'pkg' pkg_long_name = '{0}.__init__'.format(pkg_name) name = '{0}.mod'.format(pkg_name) - with util.mock_modules(pkg_long_name, name) as mock: - with util.import_state(meta_path=[mock]): + with test_util.mock_modules(pkg_long_name, name) as mock: + with test_util.import_state(meta_path=[mock]): self.init.import_module(pkg_name) module = self.init.import_module(name, pkg_name) self.assertEqual(module.__name__, name) @@ -86,16 +86,15 @@ class ImportModuleTests: b_load_count += 1 code = {'a': load_a, 'a.b': load_b} modules = ['a.__init__', 'a.b'] - with util.mock_modules(*modules, module_code=code) as mock: - with util.import_state(meta_path=[mock]): + with test_util.mock_modules(*modules, module_code=code) as mock: + with test_util.import_state(meta_path=[mock]): self.init.import_module('a.b') self.assertEqual(b_load_count, 1) -class Frozen_ImportModuleTests(ImportModuleTests, unittest.TestCase): - init = frozen_init -class Source_ImportModuleTests(ImportModuleTests, unittest.TestCase): - init = source_init +(Frozen_ImportModuleTests, + Source_ImportModuleTests + ) = test_util.test_both(ImportModuleTests, init=init) class FindLoaderTests: @@ -107,7 +106,7 @@ class FindLoaderTests: def test_sys_modules(self): # If a module with __loader__ is in sys.modules, then return it. name = 'some_mod' - with util.uncache(name): + with test_util.uncache(name): module = types.ModuleType(name) loader = 'a loader!' module.__loader__ = loader @@ -120,7 +119,7 @@ class FindLoaderTests: def test_sys_modules_loader_is_None(self): # If sys.modules[name].__loader__ is None, raise ValueError. name = 'some_mod' - with util.uncache(name): + with test_util.uncache(name): module = types.ModuleType(name) module.__loader__ = None sys.modules[name] = module @@ -133,7 +132,7 @@ class FindLoaderTests: # Should raise ValueError # Issue #17099 name = 'some_mod' - with util.uncache(name): + with test_util.uncache(name): module = types.ModuleType(name) try: del module.__loader__ @@ -148,8 +147,8 @@ class FindLoaderTests: def test_success(self): # Return the loader found on sys.meta_path. name = 'some_mod' - with util.uncache(name): - with util.import_state(meta_path=[self.FakeMetaFinder]): + with test_util.uncache(name): + with test_util.import_state(meta_path=[self.FakeMetaFinder]): with warnings.catch_warnings(): warnings.simplefilter('ignore', DeprecationWarning) self.assertEqual((name, None), self.init.find_loader(name)) @@ -158,8 +157,8 @@ class FindLoaderTests: # Searching on a path should work. name = 'some_mod' path = 'path to some place' - with util.uncache(name): - with util.import_state(meta_path=[self.FakeMetaFinder]): + with test_util.uncache(name): + with test_util.import_state(meta_path=[self.FakeMetaFinder]): with warnings.catch_warnings(): warnings.simplefilter('ignore', DeprecationWarning) self.assertEqual((name, path), @@ -171,11 +170,10 @@ class FindLoaderTests: warnings.simplefilter('ignore', DeprecationWarning) self.assertIsNone(self.init.find_loader('nevergoingtofindthismodule')) -class Frozen_FindLoaderTests(FindLoaderTests, unittest.TestCase): - init = frozen_init -class Source_FindLoaderTests(FindLoaderTests, unittest.TestCase): - init = source_init +(Frozen_FindLoaderTests, + Source_FindLoaderTests + ) = test_util.test_both(FindLoaderTests, init=init) class ReloadTests: @@ -195,10 +193,10 @@ class ReloadTests: module = type(sys)('top_level') module.spam = 3 sys.modules['top_level'] = module - mock = util.mock_modules('top_level', - module_code={'top_level': code}) + mock = test_util.mock_modules('top_level', + module_code={'top_level': code}) with mock: - with util.import_state(meta_path=[mock]): + with test_util.import_state(meta_path=[mock]): module = self.init.import_module('top_level') reloaded = self.init.reload(module) actual = sys.modules['top_level'] @@ -230,7 +228,7 @@ class ReloadTests: def test_reload_location_changed(self): name = 'spam' with support.temp_cwd(None) as cwd: - with util.uncache('spam'): + with test_util.uncache('spam'): with support.DirsOnSysPath(cwd): # Start as a plain module. self.init.invalidate_caches() @@ -281,7 +279,7 @@ class ReloadTests: def test_reload_namespace_changed(self): name = 'spam' with support.temp_cwd(None) as cwd: - with util.uncache('spam'): + with test_util.uncache('spam'): with support.DirsOnSysPath(cwd): # Start as a namespace package. self.init.invalidate_caches() @@ -338,20 +336,16 @@ class ReloadTests: # See #19851. name = 'spam' subname = 'ham' - with util.temp_module(name, pkg=True) as pkg_dir: - fullname, _ = util.submodule(name, subname, pkg_dir) + with test_util.temp_module(name, pkg=True) as pkg_dir: + fullname, _ = test_util.submodule(name, subname, pkg_dir) ham = self.init.import_module(fullname) reloaded = self.init.reload(ham) self.assertIs(reloaded, ham) -class Frozen_ReloadTests(ReloadTests, unittest.TestCase): - init = frozen_init - util = frozen_util - -class Source_ReloadTests(ReloadTests, unittest.TestCase): - init = source_init - util = source_util +(Frozen_ReloadTests, + Source_ReloadTests + ) = test_util.test_both(ReloadTests, init=init, util=util) class InvalidateCacheTests: @@ -384,11 +378,10 @@ class InvalidateCacheTests: self.addCleanup(lambda: sys.path_importer_cache.__delitem__(key)) self.init.invalidate_caches() # Shouldn't trigger an exception. -class Frozen_InvalidateCacheTests(InvalidateCacheTests, unittest.TestCase): - init = frozen_init -class Source_InvalidateCacheTests(InvalidateCacheTests, unittest.TestCase): - init = source_init +(Frozen_InvalidateCacheTests, + Source_InvalidateCacheTests + ) = test_util.test_both(InvalidateCacheTests, init=init) class FrozenImportlibTests(unittest.TestCase): @@ -398,6 +391,7 @@ class FrozenImportlibTests(unittest.TestCase): # Can't do an isinstance() check since separate copies of importlib # may have been used for import, so just check the name is not for the # frozen loader. + source_init = init['Source'] self.assertNotEqual(source_init.__loader__.__class__.__name__, 'FrozenImporter') @@ -426,11 +420,10 @@ class StartupTests: elif self.machinery.FrozenImporter.find_module(name): self.assertIsNot(module.__spec__, None) -class Frozen_StartupTests(StartupTests, unittest.TestCase): - machinery = frozen_machinery -class Source_StartupTests(StartupTests, unittest.TestCase): - machinery = source_machinery +(Frozen_StartupTests, + Source_StartupTests + ) = test_util.test_both(StartupTests, machinery=machinery) if __name__ == '__main__': diff --git a/Lib/test/test_importlib/test_lazy.py b/Lib/test/test_importlib/test_lazy.py new file mode 100644 index 0000000..2e191bb --- /dev/null +++ b/Lib/test/test_importlib/test_lazy.py @@ -0,0 +1,132 @@ +import importlib +from importlib import abc +from importlib import util +import unittest + +from . import util as test_util + + +class CollectInit: + + def __init__(self, *args, **kwargs): + self.args = args + self.kwargs = kwargs + + def exec_module(self, module): + return self + + +class LazyLoaderFactoryTests(unittest.TestCase): + + def test_init(self): + factory = util.LazyLoader.factory(CollectInit) + # E.g. what importlib.machinery.FileFinder instantiates loaders with + # plus keyword arguments. + lazy_loader = factory('module name', 'module path', kw='kw') + loader = lazy_loader.loader + self.assertEqual(('module name', 'module path'), loader.args) + self.assertEqual({'kw': 'kw'}, loader.kwargs) + + def test_validation(self): + # No exec_module(), no lazy loading. + with self.assertRaises(TypeError): + util.LazyLoader.factory(object) + + +class TestingImporter(abc.MetaPathFinder, abc.Loader): + + module_name = 'lazy_loader_test' + mutated_name = 'changed' + loaded = None + source_code = 'attr = 42; __name__ = {!r}'.format(mutated_name) + + def find_spec(self, name, path, target=None): + if name != self.module_name: + return None + return util.spec_from_loader(name, util.LazyLoader(self)) + + def exec_module(self, module): + exec(self.source_code, module.__dict__) + self.loaded = module + + +class LazyLoaderTests(unittest.TestCase): + + def test_init(self): + with self.assertRaises(TypeError): + util.LazyLoader(object) + + def new_module(self, source_code=None): + loader = TestingImporter() + if source_code is not None: + loader.source_code = source_code + spec = util.spec_from_loader(TestingImporter.module_name, + util.LazyLoader(loader)) + module = spec.loader.create_module(spec) + module.__spec__ = spec + module.__loader__ = spec.loader + spec.loader.exec_module(module) + # Module is now lazy. + self.assertIsNone(loader.loaded) + return module + + def test_e2e(self): + # End-to-end test to verify the load is in fact lazy. + importer = TestingImporter() + assert importer.loaded is None + with test_util.uncache(importer.module_name): + with test_util.import_state(meta_path=[importer]): + module = importlib.import_module(importer.module_name) + self.assertIsNone(importer.loaded) + # Trigger load. + self.assertEqual(module.__loader__, importer) + self.assertIsNotNone(importer.loaded) + self.assertEqual(module, importer.loaded) + + def test_attr_unchanged(self): + # An attribute only mutated as a side-effect of import should not be + # changed needlessly. + module = self.new_module() + self.assertEqual(TestingImporter.mutated_name, module.__name__) + + def test_new_attr(self): + # A new attribute should persist. + module = self.new_module() + module.new_attr = 42 + self.assertEqual(42, module.new_attr) + + def test_mutated_preexisting_attr(self): + # Changing an attribute that already existed on the module -- + # e.g. __name__ -- should persist. + module = self.new_module() + module.__name__ = 'bogus' + self.assertEqual('bogus', module.__name__) + + def test_mutated_attr(self): + # Changing an attribute that comes into existence after an import + # should persist. + module = self.new_module() + module.attr = 6 + self.assertEqual(6, module.attr) + + def test_delete_eventual_attr(self): + # Deleting an attribute should stay deleted. + module = self.new_module() + del module.attr + self.assertFalse(hasattr(module, 'attr')) + + def test_delete_preexisting_attr(self): + module = self.new_module() + del module.__name__ + self.assertFalse(hasattr(module, '__name__')) + + def test_module_substitution_error(self): + source_code = 'import sys; sys.modules[__name__] = 42' + module = self.new_module(source_code) + with test_util.uncache(TestingImporter.module_name): + with self.assertRaises(ValueError): + module.__name__ + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_importlib/test_locks.py b/Lib/test/test_importlib/test_locks.py index dc97ba1..df0af12 100644 --- a/Lib/test/test_importlib/test_locks.py +++ b/Lib/test/test_importlib/test_locks.py @@ -1,7 +1,6 @@ -from . import util -frozen_init, source_init = util.import_importlib('importlib') -frozen_bootstrap = frozen_init._bootstrap -source_bootstrap = source_init._bootstrap +from . import util as test_util + +init = test_util.import_importlib('importlib') import sys import time @@ -32,14 +31,20 @@ if threading is not None: test_timeout = None # _release_save() unsupported test_release_save_unacquired = None + # lock status in repr unsupported + test_repr = None + test_locked_repr = None - class Frozen_ModuleLockAsRLockTests(ModuleLockAsRLockTests, lock_tests.RLockTests): - LockType = frozen_bootstrap._ModuleLock - - class Source_ModuleLockAsRLockTests(ModuleLockAsRLockTests, lock_tests.RLockTests): - LockType = source_bootstrap._ModuleLock + LOCK_TYPES = {kind: splitinit._bootstrap._ModuleLock + for kind, splitinit in init.items()} + (Frozen_ModuleLockAsRLockTests, + Source_ModuleLockAsRLockTests + ) = test_util.test_both(ModuleLockAsRLockTests, lock_tests.RLockTests, + LockType=LOCK_TYPES) else: + LOCK_TYPES = {} + class Frozen_ModuleLockAsRLockTests(unittest.TestCase): pass @@ -47,78 +52,94 @@ else: pass -class DeadlockAvoidanceTests: - - def setUp(self): - try: - self.old_switchinterval = sys.getswitchinterval() - sys.setswitchinterval(0.000001) - except AttributeError: - self.old_switchinterval = None - - def tearDown(self): - if self.old_switchinterval is not None: - sys.setswitchinterval(self.old_switchinterval) - - def run_deadlock_avoidance_test(self, create_deadlock): - NLOCKS = 10 - locks = [self.LockType(str(i)) for i in range(NLOCKS)] - pairs = [(locks[i], locks[(i+1)%NLOCKS]) for i in range(NLOCKS)] - if create_deadlock: - NTHREADS = NLOCKS - else: - NTHREADS = NLOCKS - 1 - barrier = threading.Barrier(NTHREADS) - results = [] - def _acquire(lock): - """Try to acquire the lock. Return True on success, False on deadlock.""" +if threading is not None: + class DeadlockAvoidanceTests: + + def setUp(self): try: - lock.acquire() - except self.DeadlockError: - return False + self.old_switchinterval = sys.getswitchinterval() + sys.setswitchinterval(0.000001) + except AttributeError: + self.old_switchinterval = None + + def tearDown(self): + if self.old_switchinterval is not None: + sys.setswitchinterval(self.old_switchinterval) + + def run_deadlock_avoidance_test(self, create_deadlock): + NLOCKS = 10 + locks = [self.LockType(str(i)) for i in range(NLOCKS)] + pairs = [(locks[i], locks[(i+1)%NLOCKS]) for i in range(NLOCKS)] + if create_deadlock: + NTHREADS = NLOCKS else: - return True - def f(): - a, b = pairs.pop() - ra = _acquire(a) - barrier.wait() - rb = _acquire(b) - results.append((ra, rb)) - if rb: - b.release() - if ra: - a.release() - lock_tests.Bunch(f, NTHREADS).wait_for_finished() - self.assertEqual(len(results), NTHREADS) - return results - - def test_deadlock(self): - results = self.run_deadlock_avoidance_test(True) - # At least one of the threads detected a potential deadlock on its - # second acquire() call. It may be several of them, because the - # deadlock avoidance mechanism is conservative. - nb_deadlocks = results.count((True, False)) - self.assertGreaterEqual(nb_deadlocks, 1) - self.assertEqual(results.count((True, True)), len(results) - nb_deadlocks) - - def test_no_deadlock(self): - results = self.run_deadlock_avoidance_test(False) - self.assertEqual(results.count((True, False)), 0) - self.assertEqual(results.count((True, True)), len(results)) - -@unittest.skipUnless(threading, "threads needed for this test") -class Frozen_DeadlockAvoidanceTests(DeadlockAvoidanceTests, unittest.TestCase): - LockType = frozen_bootstrap._ModuleLock - DeadlockError = frozen_bootstrap._DeadlockError - -@unittest.skipUnless(threading, "threads needed for this test") -class Source_DeadlockAvoidanceTests(DeadlockAvoidanceTests, unittest.TestCase): - LockType = source_bootstrap._ModuleLock - DeadlockError = source_bootstrap._DeadlockError + NTHREADS = NLOCKS - 1 + barrier = threading.Barrier(NTHREADS) + results = [] + + def _acquire(lock): + """Try to acquire the lock. Return True on success, + False on deadlock.""" + try: + lock.acquire() + except self.DeadlockError: + return False + else: + return True + + def f(): + a, b = pairs.pop() + ra = _acquire(a) + barrier.wait() + rb = _acquire(b) + results.append((ra, rb)) + if rb: + b.release() + if ra: + a.release() + lock_tests.Bunch(f, NTHREADS).wait_for_finished() + self.assertEqual(len(results), NTHREADS) + return results + + def test_deadlock(self): + results = self.run_deadlock_avoidance_test(True) + # At least one of the threads detected a potential deadlock on its + # second acquire() call. It may be several of them, because the + # deadlock avoidance mechanism is conservative. + nb_deadlocks = results.count((True, False)) + self.assertGreaterEqual(nb_deadlocks, 1) + self.assertEqual(results.count((True, True)), len(results) - nb_deadlocks) + + def test_no_deadlock(self): + results = self.run_deadlock_avoidance_test(False) + self.assertEqual(results.count((True, False)), 0) + self.assertEqual(results.count((True, True)), len(results)) + + + DEADLOCK_ERRORS = {kind: splitinit._bootstrap._DeadlockError + for kind, splitinit in init.items()} + + (Frozen_DeadlockAvoidanceTests, + Source_DeadlockAvoidanceTests + ) = test_util.test_both(DeadlockAvoidanceTests, + LockType=LOCK_TYPES, + DeadlockError=DEADLOCK_ERRORS) +else: + DEADLOCK_ERRORS = {} + + class Frozen_DeadlockAvoidanceTests(unittest.TestCase): + pass + + class Source_DeadlockAvoidanceTests(unittest.TestCase): + pass class LifetimeTests: + @property + def bootstrap(self): + return self.init._bootstrap + def test_lock_lifetime(self): name = "xyzzy" self.assertNotIn(name, self.bootstrap._module_locks) @@ -135,11 +156,10 @@ class LifetimeTests: self.assertEqual(0, len(self.bootstrap._module_locks), self.bootstrap._module_locks) -class Frozen_LifetimeTests(LifetimeTests, unittest.TestCase): - bootstrap = frozen_bootstrap -class Source_LifetimeTests(LifetimeTests, unittest.TestCase): - bootstrap = source_bootstrap +(Frozen_LifetimeTests, + Source_LifetimeTests + ) = test_util.test_both(LifetimeTests, init=init) @support.reap_threads diff --git a/Lib/test/test_importlib/test_spec.py b/Lib/test/test_importlib/test_spec.py index 71541f6..8b333e8 100644 --- a/Lib/test/test_importlib/test_spec.py +++ b/Lib/test/test_importlib/test_spec.py @@ -1,10 +1,8 @@ -from . import util +from . import util as test_util -frozen_init, source_init = util.import_importlib('importlib') -frozen_bootstrap = frozen_init._bootstrap -source_bootstrap = source_init._bootstrap -frozen_machinery, source_machinery = util.import_importlib('importlib.machinery') -frozen_util, source_util = util.import_importlib('importlib.util') +init = test_util.import_importlib('importlib') +machinery = test_util.import_importlib('importlib.machinery') +util = test_util.import_importlib('importlib.util') import os.path from test.support import CleanImport @@ -36,6 +34,9 @@ class TestLoader: def _is_package(self, name): return self.package + def create_module(self, spec): + return None + class NewLoader(TestLoader): @@ -52,6 +53,8 @@ class LegacyLoader(TestLoader): with warnings.catch_warnings(): warnings.simplefilter("ignore", DeprecationWarning) + frozen_util = util['Frozen'] + @frozen_util.module_for_loader def load_module(self, module): module.ham = self.HAM @@ -221,18 +224,17 @@ class ModuleSpecTests: self.assertEqual(self.loc_spec.cached, 'spam.pyc') -class Frozen_ModuleSpecTests(ModuleSpecTests, unittest.TestCase): - util = frozen_util - machinery = frozen_machinery - - -class Source_ModuleSpecTests(ModuleSpecTests, unittest.TestCase): - util = source_util - machinery = source_machinery +(Frozen_ModuleSpecTests, + Source_ModuleSpecTests + ) = test_util.test_both(ModuleSpecTests, util=util, machinery=machinery) class ModuleSpecMethodsTests: + @property + def bootstrap(self): + return self.init._bootstrap + def setUp(self): self.name = 'spam' self.path = 'spam.py' @@ -243,152 +245,14 @@ class ModuleSpecMethodsTests: origin=self.path) self.loc_spec._set_fileattr = True - # init_module_attrs - - def test_init_module_attrs(self): - module = type(sys)(self.name) - spec = self.machinery.ModuleSpec(self.name, self.loader) - self.bootstrap._SpecMethods(spec).init_module_attrs(module) - - self.assertEqual(module.__name__, spec.name) - self.assertIs(module.__loader__, spec.loader) - self.assertEqual(module.__package__, spec.parent) - self.assertIs(module.__spec__, spec) - self.assertFalse(hasattr(module, '__path__')) - self.assertFalse(hasattr(module, '__file__')) - self.assertFalse(hasattr(module, '__cached__')) - - def test_init_module_attrs_package(self): - module = type(sys)(self.name) - spec = self.machinery.ModuleSpec(self.name, self.loader) - spec.submodule_search_locations = ['spam', 'ham'] - self.bootstrap._SpecMethods(spec).init_module_attrs(module) - - self.assertEqual(module.__name__, spec.name) - self.assertIs(module.__loader__, spec.loader) - self.assertEqual(module.__package__, spec.parent) - self.assertIs(module.__spec__, spec) - self.assertIs(module.__path__, spec.submodule_search_locations) - self.assertFalse(hasattr(module, '__file__')) - self.assertFalse(hasattr(module, '__cached__')) - - def test_init_module_attrs_location(self): - module = type(sys)(self.name) - spec = self.loc_spec - self.bootstrap._SpecMethods(spec).init_module_attrs(module) - - self.assertEqual(module.__name__, spec.name) - self.assertIs(module.__loader__, spec.loader) - self.assertEqual(module.__package__, spec.parent) - self.assertIs(module.__spec__, spec) - self.assertFalse(hasattr(module, '__path__')) - self.assertEqual(module.__file__, spec.origin) - self.assertEqual(module.__cached__, - self.util.cache_from_source(spec.origin)) - - def test_init_module_attrs_different_name(self): - module = type(sys)('eggs') - spec = self.machinery.ModuleSpec(self.name, self.loader) - self.bootstrap._SpecMethods(spec).init_module_attrs(module) - - self.assertEqual(module.__name__, spec.name) - - def test_init_module_attrs_different_spec(self): - module = type(sys)(self.name) - module.__spec__ = self.machinery.ModuleSpec('eggs', object()) - spec = self.machinery.ModuleSpec(self.name, self.loader) - self.bootstrap._SpecMethods(spec).init_module_attrs(module) - - self.assertEqual(module.__name__, spec.name) - self.assertIs(module.__loader__, spec.loader) - self.assertEqual(module.__package__, spec.parent) - self.assertIs(module.__spec__, spec) - - def test_init_module_attrs_already_set(self): - module = type(sys)('ham.eggs') - module.__loader__ = object() - module.__package__ = 'ham' - module.__path__ = ['eggs'] - module.__file__ = 'ham/eggs/__init__.py' - module.__cached__ = self.util.cache_from_source(module.__file__) - original = vars(module).copy() - spec = self.loc_spec - spec.submodule_search_locations = [''] - self.bootstrap._SpecMethods(spec).init_module_attrs(module) - - self.assertIs(module.__loader__, original['__loader__']) - self.assertEqual(module.__package__, original['__package__']) - self.assertIs(module.__path__, original['__path__']) - self.assertEqual(module.__file__, original['__file__']) - self.assertEqual(module.__cached__, original['__cached__']) - - def test_init_module_attrs_immutable(self): - module = object() - spec = self.loc_spec - spec.submodule_search_locations = [''] - self.bootstrap._SpecMethods(spec).init_module_attrs(module) - - self.assertFalse(hasattr(module, '__name__')) - self.assertFalse(hasattr(module, '__loader__')) - self.assertFalse(hasattr(module, '__package__')) - self.assertFalse(hasattr(module, '__spec__')) - self.assertFalse(hasattr(module, '__path__')) - self.assertFalse(hasattr(module, '__file__')) - self.assertFalse(hasattr(module, '__cached__')) - - # create() - - def test_create(self): - created = self.bootstrap._SpecMethods(self.spec).create() - - self.assertEqual(created.__name__, self.spec.name) - self.assertIs(created.__loader__, self.spec.loader) - self.assertEqual(created.__package__, self.spec.parent) - self.assertIs(created.__spec__, self.spec) - self.assertFalse(hasattr(created, '__path__')) - self.assertFalse(hasattr(created, '__file__')) - self.assertFalse(hasattr(created, '__cached__')) - - def test_create_from_loader(self): - module = type(sys.implementation)() - class CreatingLoader(TestLoader): - def create_module(self, spec): - return module - self.spec.loader = CreatingLoader() - created = self.bootstrap._SpecMethods(self.spec).create() - - self.assertIs(created, module) - self.assertEqual(created.__name__, self.spec.name) - self.assertIs(created.__loader__, self.spec.loader) - self.assertEqual(created.__package__, self.spec.parent) - self.assertIs(created.__spec__, self.spec) - self.assertFalse(hasattr(created, '__path__')) - self.assertFalse(hasattr(created, '__file__')) - self.assertFalse(hasattr(created, '__cached__')) - - def test_create_from_loader_not_handled(self): - class CreatingLoader(TestLoader): - def create_module(self, spec): - return None - self.spec.loader = CreatingLoader() - created = self.bootstrap._SpecMethods(self.spec).create() - - self.assertEqual(created.__name__, self.spec.name) - self.assertIs(created.__loader__, self.spec.loader) - self.assertEqual(created.__package__, self.spec.parent) - self.assertIs(created.__spec__, self.spec) - self.assertFalse(hasattr(created, '__path__')) - self.assertFalse(hasattr(created, '__file__')) - self.assertFalse(hasattr(created, '__cached__')) - # exec() def test_exec(self): self.spec.loader = NewLoader() - module = self.bootstrap._SpecMethods(self.spec).create() + module = self.util.module_from_spec(self.spec) sys.modules[self.name] = module self.assertFalse(hasattr(module, 'eggs')) - self.bootstrap._SpecMethods(self.spec).exec(module) + self.bootstrap._exec(self.spec, module) self.assertEqual(module.eggs, 1) @@ -397,7 +261,7 @@ class ModuleSpecMethodsTests: def test_load(self): self.spec.loader = NewLoader() with CleanImport(self.spec.name): - loaded = self.bootstrap._SpecMethods(self.spec).load() + loaded = self.bootstrap._load(self.spec) installed = sys.modules[self.spec.name] self.assertEqual(loaded.eggs, 1) @@ -410,7 +274,7 @@ class ModuleSpecMethodsTests: sys.modules[module.__name__] = replacement self.spec.loader = ReplacingLoader() with CleanImport(self.spec.name): - loaded = self.bootstrap._SpecMethods(self.spec).load() + loaded = self.bootstrap._load(self.spec) installed = sys.modules[self.spec.name] self.assertIs(loaded, replacement) @@ -423,7 +287,7 @@ class ModuleSpecMethodsTests: self.spec.loader = FailedLoader() with CleanImport(self.spec.name): with self.assertRaises(RuntimeError): - loaded = self.bootstrap._SpecMethods(self.spec).load() + loaded = self.bootstrap._load(self.spec) self.assertNotIn(self.spec.name, sys.modules) def test_load_failed_removed(self): @@ -434,20 +298,20 @@ class ModuleSpecMethodsTests: self.spec.loader = FailedLoader() with CleanImport(self.spec.name): with self.assertRaises(RuntimeError): - loaded = self.bootstrap._SpecMethods(self.spec).load() + loaded = self.bootstrap._load(self.spec) self.assertNotIn(self.spec.name, sys.modules) def test_load_legacy(self): self.spec.loader = LegacyLoader() with CleanImport(self.spec.name): - loaded = self.bootstrap._SpecMethods(self.spec).load() + loaded = self.bootstrap._load(self.spec) self.assertEqual(loaded.ham, -1) def test_load_legacy_attributes(self): self.spec.loader = LegacyLoader() with CleanImport(self.spec.name): - loaded = self.bootstrap._SpecMethods(self.spec).load() + loaded = self.bootstrap._load(self.spec) self.assertIs(loaded.__loader__, self.spec.loader) self.assertEqual(loaded.__package__, self.spec.parent) @@ -461,7 +325,7 @@ class ModuleSpecMethodsTests: return module self.spec.loader = ImmutableLoader() with CleanImport(self.spec.name): - loaded = self.bootstrap._SpecMethods(self.spec).load() + loaded = self.bootstrap._load(self.spec) self.assertIs(sys.modules[self.spec.name], module) @@ -470,8 +334,8 @@ class ModuleSpecMethodsTests: def test_reload(self): self.spec.loader = NewLoader() with CleanImport(self.spec.name): - loaded = self.bootstrap._SpecMethods(self.spec).load() - reloaded = self.bootstrap._SpecMethods(self.spec).exec(loaded) + loaded = self.bootstrap._load(self.spec) + reloaded = self.bootstrap._exec(self.spec, loaded) installed = sys.modules[self.spec.name] self.assertEqual(loaded.eggs, 1) @@ -481,9 +345,9 @@ class ModuleSpecMethodsTests: def test_reload_modified(self): self.spec.loader = NewLoader() with CleanImport(self.spec.name): - loaded = self.bootstrap._SpecMethods(self.spec).load() + loaded = self.bootstrap._load(self.spec) loaded.eggs = 2 - reloaded = self.bootstrap._SpecMethods(self.spec).exec(loaded) + reloaded = self.bootstrap._exec(self.spec, loaded) self.assertEqual(loaded.eggs, 1) self.assertIs(reloaded, loaded) @@ -491,9 +355,9 @@ class ModuleSpecMethodsTests: def test_reload_extra_attributes(self): self.spec.loader = NewLoader() with CleanImport(self.spec.name): - loaded = self.bootstrap._SpecMethods(self.spec).load() + loaded = self.bootstrap._load(self.spec) loaded.available = False - reloaded = self.bootstrap._SpecMethods(self.spec).exec(loaded) + reloaded = self.bootstrap._exec(self.spec, loaded) self.assertFalse(loaded.available) self.assertIs(reloaded, loaded) @@ -501,12 +365,12 @@ class ModuleSpecMethodsTests: def test_reload_init_module_attrs(self): self.spec.loader = NewLoader() with CleanImport(self.spec.name): - loaded = self.bootstrap._SpecMethods(self.spec).load() + loaded = self.bootstrap._load(self.spec) loaded.__name__ = 'ham' del loaded.__loader__ del loaded.__package__ del loaded.__spec__ - self.bootstrap._SpecMethods(self.spec).exec(loaded) + self.bootstrap._exec(self.spec, loaded) self.assertEqual(loaded.__name__, self.spec.name) self.assertIs(loaded.__loader__, self.spec.loader) @@ -519,8 +383,8 @@ class ModuleSpecMethodsTests: def test_reload_legacy(self): self.spec.loader = LegacyLoader() with CleanImport(self.spec.name): - loaded = self.bootstrap._SpecMethods(self.spec).load() - reloaded = self.bootstrap._SpecMethods(self.spec).exec(loaded) + loaded = self.bootstrap._load(self.spec) + reloaded = self.bootstrap._exec(self.spec, loaded) installed = sys.modules[self.spec.name] self.assertEqual(loaded.ham, -1) @@ -528,20 +392,18 @@ class ModuleSpecMethodsTests: self.assertIs(installed, loaded) -class Frozen_ModuleSpecMethodsTests(ModuleSpecMethodsTests, unittest.TestCase): - bootstrap = frozen_bootstrap - machinery = frozen_machinery - util = frozen_util - - -class Source_ModuleSpecMethodsTests(ModuleSpecMethodsTests, unittest.TestCase): - bootstrap = source_bootstrap - machinery = source_machinery - util = source_util +(Frozen_ModuleSpecMethodsTests, + Source_ModuleSpecMethodsTests + ) = test_util.test_both(ModuleSpecMethodsTests, init=init, util=util, + machinery=machinery) class ModuleReprTests: + @property + def bootstrap(self): + return self.init._bootstrap + def setUp(self): self.module = type(os)('spam') self.spec = self.machinery.ModuleSpec('spam', TestLoader()) @@ -625,16 +487,10 @@ class ModuleReprTests: self.assertEqual(modrepr, '<module {!r}>'.format('spam')) -class Frozen_ModuleReprTests(ModuleReprTests, unittest.TestCase): - bootstrap = frozen_bootstrap - machinery = frozen_machinery - util = frozen_util - - -class Source_ModuleReprTests(ModuleReprTests, unittest.TestCase): - bootstrap = source_bootstrap - machinery = source_machinery - util = source_util +(Frozen_ModuleReprTests, + Source_ModuleReprTests + ) = test_util.test_both(ModuleReprTests, init=init, util=util, + machinery=machinery) class FactoryTests: @@ -787,13 +643,14 @@ class FactoryTests: # spec_from_file_location() def test_spec_from_file_location_default(self): - if self.machinery is source_machinery: - raise unittest.SkipTest('not sure why this is breaking...') spec = self.util.spec_from_file_location(self.name, self.path) self.assertEqual(spec.name, self.name) + # Need to use a circuitous route to get at importlib.machinery to make + # sure the same class object is used in the isinstance() check as + # would have been used to create the loader. self.assertIsInstance(spec.loader, - self.machinery.SourceFileLoader) + self.util.abc.machinery.SourceFileLoader) self.assertEqual(spec.loader.name, self.name) self.assertEqual(spec.loader.path, self.path) self.assertEqual(spec.origin, self.path) @@ -947,11 +804,10 @@ class FactoryTests: self.assertTrue(spec.has_location) -class Frozen_FactoryTests(FactoryTests, unittest.TestCase): - util = frozen_util - machinery = frozen_machinery +(Frozen_FactoryTests, + Source_FactoryTests + ) = test_util.test_both(FactoryTests, util=util, machinery=machinery) -class Source_FactoryTests(FactoryTests, unittest.TestCase): - util = source_util - machinery = source_machinery +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py index b2823c6..2493af7 100644 --- a/Lib/test/test_importlib/test_util.py +++ b/Lib/test/test_importlib/test_util.py @@ -1,8 +1,8 @@ -from importlib import util -from . import util as test_util -frozen_init, source_init = test_util.import_importlib('importlib') -frozen_machinery, source_machinery = test_util.import_importlib('importlib.machinery') -frozen_util, source_util = test_util.import_importlib('importlib.util') +from . import util +abc = util.import_importlib('importlib.abc') +init = util.import_importlib('importlib') +machinery = util.import_importlib('importlib.machinery') +importlib_util = util.import_importlib('importlib.util') import os import sys @@ -32,8 +32,94 @@ class DecodeSourceBytesTests: self.assertEqual(self.util.decode_source(source_bytes), '\n'.join([self.source, self.source])) -Frozen_DecodeSourceBytesTests, Source_DecodeSourceBytesTests = test_util.test_both( - DecodeSourceBytesTests, util=[frozen_util, source_util]) + +(Frozen_DecodeSourceBytesTests, + Source_DecodeSourceBytesTests + ) = util.test_both(DecodeSourceBytesTests, util=importlib_util) + + +class ModuleFromSpecTests: + + def test_no_create_module(self): + class Loader: + def exec_module(self, module): + pass + spec = self.machinery.ModuleSpec('test', Loader()) + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter('always') + module = self.util.module_from_spec(spec) + self.assertEqual(1, len(w)) + self.assertTrue(issubclass(w[0].category, DeprecationWarning)) + self.assertIn('create_module', str(w[0].message)) + self.assertIsInstance(module, types.ModuleType) + self.assertEqual(module.__name__, spec.name) + + def test_create_module_returns_None(self): + class Loader(self.abc.Loader): + def create_module(self, spec): + return None + spec = self.machinery.ModuleSpec('test', Loader()) + module = self.util.module_from_spec(spec) + self.assertIsInstance(module, types.ModuleType) + self.assertEqual(module.__name__, spec.name) + + def test_create_module(self): + name = 'already set' + class CustomModule(types.ModuleType): + pass + class Loader(self.abc.Loader): + def create_module(self, spec): + module = CustomModule(spec.name) + module.__name__ = name + return module + spec = self.machinery.ModuleSpec('test', Loader()) + module = self.util.module_from_spec(spec) + self.assertIsInstance(module, CustomModule) + self.assertEqual(module.__name__, name) + + def test___name__(self): + spec = self.machinery.ModuleSpec('test', object()) + module = self.util.module_from_spec(spec) + self.assertEqual(module.__name__, spec.name) + + def test___spec__(self): + spec = self.machinery.ModuleSpec('test', object()) + module = self.util.module_from_spec(spec) + self.assertEqual(module.__spec__, spec) + + def test___loader__(self): + loader = object() + spec = self.machinery.ModuleSpec('test', loader) + module = self.util.module_from_spec(spec) + self.assertIs(module.__loader__, loader) + + def test___package__(self): + spec = self.machinery.ModuleSpec('test.pkg', object()) + module = self.util.module_from_spec(spec) + self.assertEqual(module.__package__, spec.parent) + + def test___path__(self): + spec = self.machinery.ModuleSpec('test', object(), is_package=True) + module = self.util.module_from_spec(spec) + self.assertEqual(module.__path__, spec.submodule_search_locations) + + def test___file__(self): + spec = self.machinery.ModuleSpec('test', object(), origin='some/path') + spec.has_location = True + module = self.util.module_from_spec(spec) + self.assertEqual(module.__file__, spec.origin) + + def test___cached__(self): + spec = self.machinery.ModuleSpec('test', object()) + spec.cached = 'some/path' + spec.has_location = True + module = self.util.module_from_spec(spec) + self.assertEqual(module.__cached__, spec.cached) + +(Frozen_ModuleFromSpecTests, + Source_ModuleFromSpecTests +) = util.test_both(ModuleFromSpecTests, abc=abc, machinery=machinery, + util=importlib_util) class ModuleForLoaderTests: @@ -70,7 +156,7 @@ class ModuleForLoaderTests: # Test that when no module exists in sys.modules a new module is # created. module_name = 'a.b.c' - with test_util.uncache(module_name): + with util.uncache(module_name): module = self.return_module(module_name) self.assertIn(module_name, sys.modules) self.assertIsInstance(module, types.ModuleType) @@ -88,7 +174,7 @@ class ModuleForLoaderTests: module = types.ModuleType('a.b.c') module.__loader__ = 42 module.__package__ = 42 - with test_util.uncache(name): + with util.uncache(name): sys.modules[name] = module loader = FakeLoader() returned_module = loader.load_module(name) @@ -100,7 +186,7 @@ class ModuleForLoaderTests: # Test that a module is removed from sys.modules if added but an # exception is raised. name = 'a.b.c' - with test_util.uncache(name): + with util.uncache(name): self.raise_exception(name) self.assertNotIn(name, sys.modules) @@ -108,7 +194,7 @@ class ModuleForLoaderTests: # Test that a failure on reload leaves the module in-place. name = 'a.b.c' module = types.ModuleType(name) - with test_util.uncache(name): + with util.uncache(name): sys.modules[name] = module self.raise_exception(name) self.assertIs(module, sys.modules[name]) @@ -127,7 +213,7 @@ class ModuleForLoaderTests: name = 'mod' module = FalseModule(name) - with test_util.uncache(name): + with util.uncache(name): self.assertFalse(module) sys.modules[name] = module given = self.return_module(name) @@ -146,7 +232,7 @@ class ModuleForLoaderTests: return module name = 'pkg.mod' - with test_util.uncache(name): + with util.uncache(name): loader = FakeLoader(False) module = loader.load_module(name) self.assertEqual(module.__name__, name) @@ -154,15 +240,17 @@ class ModuleForLoaderTests: self.assertEqual(module.__package__, 'pkg') name = 'pkg.sub' - with test_util.uncache(name): + with util.uncache(name): loader = FakeLoader(True) module = loader.load_module(name) self.assertEqual(module.__name__, name) self.assertIs(module.__loader__, loader) self.assertEqual(module.__package__, name) -Frozen_ModuleForLoaderTests, Source_ModuleForLoaderTests = test_util.test_both( - ModuleForLoaderTests, util=[frozen_util, source_util]) + +(Frozen_ModuleForLoaderTests, + Source_ModuleForLoaderTests + ) = util.test_both(ModuleForLoaderTests, util=importlib_util) class SetPackageTests: @@ -222,18 +310,25 @@ class SetPackageTests: self.assertEqual(wrapped.__name__, fxn.__name__) self.assertEqual(wrapped.__qualname__, fxn.__qualname__) -Frozen_SetPackageTests, Source_SetPackageTests = test_util.test_both( - SetPackageTests, util=[frozen_util, source_util]) + +(Frozen_SetPackageTests, + Source_SetPackageTests + ) = util.test_both(SetPackageTests, util=importlib_util) class SetLoaderTests: """Tests importlib.util.set_loader().""" - class DummyLoader: - @util.set_loader - def load_module(self, module): - return self.module + @property + def DummyLoader(self): + # Set DummyLoader on the class lazily. + class DummyLoader: + @self.util.set_loader + def load_module(self, module): + return self.module + self.__class__.DummyLoader = DummyLoader + return DummyLoader def test_no_attribute(self): loader = self.DummyLoader() @@ -262,17 +357,10 @@ class SetLoaderTests: warnings.simplefilter('ignore', DeprecationWarning) self.assertEqual(42, loader.load_module('blah').__loader__) -class Frozen_SetLoaderTests(SetLoaderTests, unittest.TestCase): - class DummyLoader: - @frozen_util.set_loader - def load_module(self, module): - return self.module -class Source_SetLoaderTests(SetLoaderTests, unittest.TestCase): - class DummyLoader: - @source_util.set_loader - def load_module(self, module): - return self.module +(Frozen_SetLoaderTests, + Source_SetLoaderTests + ) = util.test_both(SetLoaderTests, util=importlib_util) class ResolveNameTests: @@ -307,9 +395,10 @@ class ResolveNameTests: with self.assertRaises(ValueError): self.util.resolve_name('..bacon', 'spam') -Frozen_ResolveNameTests, Source_ResolveNameTests = test_util.test_both( - ResolveNameTests, - util=[frozen_util, source_util]) + +(Frozen_ResolveNameTests, + Source_ResolveNameTests + ) = util.test_both(ResolveNameTests, util=importlib_util) class FindSpecTests: @@ -320,7 +409,7 @@ class FindSpecTests: def test_sys_modules(self): name = 'some_mod' - with test_util.uncache(name): + with util.uncache(name): module = types.ModuleType(name) loader = 'a loader!' spec = self.machinery.ModuleSpec(name, loader) @@ -332,7 +421,7 @@ class FindSpecTests: def test_sys_modules_without___loader__(self): name = 'some_mod' - with test_util.uncache(name): + with util.uncache(name): module = types.ModuleType(name) del module.__loader__ loader = 'a loader!' @@ -344,7 +433,7 @@ class FindSpecTests: def test_sys_modules_spec_is_None(self): name = 'some_mod' - with test_util.uncache(name): + with util.uncache(name): module = types.ModuleType(name) module.__spec__ = None sys.modules[name] = module @@ -353,7 +442,7 @@ class FindSpecTests: def test_sys_modules_loader_is_None(self): name = 'some_mod' - with test_util.uncache(name): + with util.uncache(name): module = types.ModuleType(name) spec = self.machinery.ModuleSpec(name, None) module.__spec__ = spec @@ -363,7 +452,7 @@ class FindSpecTests: def test_sys_modules_spec_is_not_set(self): name = 'some_mod' - with test_util.uncache(name): + with util.uncache(name): module = types.ModuleType(name) try: del module.__spec__ @@ -375,8 +464,8 @@ class FindSpecTests: def test_success(self): name = 'some_mod' - with test_util.uncache(name): - with test_util.import_state(meta_path=[self.FakeMetaFinder]): + with util.uncache(name): + with util.import_state(meta_path=[self.FakeMetaFinder]): self.assertEqual((name, None, None), self.util.find_spec(name)) @@ -384,8 +473,8 @@ class FindSpecTests: # # Searching on a path should work. # name = 'some_mod' # path = 'path to some place' -# with test_util.uncache(name): -# with test_util.import_state(meta_path=[self.FakeMetaFinder]): +# with util.uncache(name): +# with util.import_state(meta_path=[self.FakeMetaFinder]): # self.assertEqual((name, path, None), # self.util.find_spec(name, path)) @@ -396,8 +485,8 @@ class FindSpecTests: def test_find_submodule(self): name = 'spam' subname = 'ham' - with test_util.temp_module(name, pkg=True) as pkg_dir: - fullname, _ = test_util.submodule(name, subname, pkg_dir) + with util.temp_module(name, pkg=True) as pkg_dir: + fullname, _ = util.submodule(name, subname, pkg_dir) spec = self.util.find_spec(fullname) self.assertIsNot(spec, None) self.assertIn(name, sorted(sys.modules)) @@ -409,9 +498,9 @@ class FindSpecTests: def test_find_submodule_parent_already_imported(self): name = 'spam' subname = 'ham' - with test_util.temp_module(name, pkg=True) as pkg_dir: + with util.temp_module(name, pkg=True) as pkg_dir: self.init.import_module(name) - fullname, _ = test_util.submodule(name, subname, pkg_dir) + fullname, _ = util.submodule(name, subname, pkg_dir) spec = self.util.find_spec(fullname) self.assertIsNot(spec, None) self.assertIn(name, sorted(sys.modules)) @@ -423,8 +512,8 @@ class FindSpecTests: def test_find_relative_module(self): name = 'spam' subname = 'ham' - with test_util.temp_module(name, pkg=True) as pkg_dir: - fullname, _ = test_util.submodule(name, subname, pkg_dir) + with util.temp_module(name, pkg=True) as pkg_dir: + fullname, _ = util.submodule(name, subname, pkg_dir) relname = '.' + subname spec = self.util.find_spec(relname, name) self.assertIsNot(spec, None) @@ -437,8 +526,8 @@ class FindSpecTests: def test_find_relative_module_missing_package(self): name = 'spam' subname = 'ham' - with test_util.temp_module(name, pkg=True) as pkg_dir: - fullname, _ = test_util.submodule(name, subname, pkg_dir) + with util.temp_module(name, pkg=True) as pkg_dir: + fullname, _ = util.submodule(name, subname, pkg_dir) relname = '.' + subname with self.assertRaises(ValueError): self.util.find_spec(relname) @@ -446,15 +535,10 @@ class FindSpecTests: self.assertNotIn(fullname, sorted(sys.modules)) -class Frozen_FindSpecTests(FindSpecTests, unittest.TestCase): - init = frozen_init - machinery = frozen_machinery - util = frozen_util - -class Source_FindSpecTests(FindSpecTests, unittest.TestCase): - init = source_init - machinery = source_machinery - util = source_util +(Frozen_FindSpecTests, + Source_FindSpecTests + ) = util.test_both(FindSpecTests, init=init, util=importlib_util, + machinery=machinery) class MagicNumberTests: @@ -467,8 +551,10 @@ class MagicNumberTests: # The magic number uses \r\n to come out wrong when splitting on lines. self.assertTrue(self.util.MAGIC_NUMBER.endswith(b'\r\n')) -Frozen_MagicNumberTests, Source_MagicNumberTests = test_util.test_both( - MagicNumberTests, util=[frozen_util, source_util]) + +(Frozen_MagicNumberTests, + Source_MagicNumberTests + ) = util.test_both(MagicNumberTests, util=importlib_util) class PEP3147Tests: @@ -583,9 +669,10 @@ class PEP3147Tests: ValueError, self.util.source_from_cache, '/foo/bar/foo.cpython-32.foo.pyc') -Frozen_PEP3147Tests, Source_PEP3147Tests = test_util.test_both( - PEP3147Tests, - util=[frozen_util, source_util]) + +(Frozen_PEP3147Tests, + Source_PEP3147Tests + ) = util.test_both(PEP3147Tests, util=importlib_util) if __name__ == '__main__': diff --git a/Lib/test/test_importlib/test_windows.py b/Lib/test/test_importlib/test_windows.py index 96b4adc..c893bcf 100644 --- a/Lib/test/test_importlib/test_windows.py +++ b/Lib/test/test_importlib/test_windows.py @@ -1,14 +1,64 @@ -from . import util -frozen_machinery, source_machinery = util.import_importlib('importlib.machinery') +from . import util as test_util +machinery = test_util.import_importlib('importlib.machinery') +import os +import re import sys import unittest +from test import support +from distutils.util import get_platform +from contextlib import contextmanager +from .util import temp_module + +support.import_module('winreg', required_on=['win']) +from winreg import ( + CreateKey, HKEY_CURRENT_USER, + SetValue, REG_SZ, KEY_ALL_ACCESS, + EnumKey, CloseKey, DeleteKey, OpenKey +) + +def delete_registry_tree(root, subkey): + try: + hkey = OpenKey(root, subkey, access=KEY_ALL_ACCESS) + except OSError: + # subkey does not exist + return + while True: + try: + subsubkey = EnumKey(hkey, 0) + except OSError: + # no more subkeys + break + delete_registry_tree(hkey, subsubkey) + CloseKey(hkey) + DeleteKey(root, subkey) + +@contextmanager +def setup_module(machinery, name, path=None): + if machinery.WindowsRegistryFinder.DEBUG_BUILD: + root = machinery.WindowsRegistryFinder.REGISTRY_KEY_DEBUG + else: + root = machinery.WindowsRegistryFinder.REGISTRY_KEY + key = root.format(fullname=name, + sys_version=sys.version[:3]) + try: + with temp_module(name, "a = 1") as location: + subkey = CreateKey(HKEY_CURRENT_USER, key) + if path is None: + path = location + ".py" + SetValue(subkey, "", REG_SZ, path) + yield + finally: + if machinery.WindowsRegistryFinder.DEBUG_BUILD: + key = os.path.dirname(key) + delete_registry_tree(HKEY_CURRENT_USER, key) @unittest.skipUnless(sys.platform.startswith('win'), 'requires Windows') class WindowsRegistryFinderTests: - - # XXX Need a test that finds the spec via the registry. + # The module name is process-specific, allowing for + # simultaneous runs of the same test on a single machine. + test_module = "spamham{}".format(os.getpid()) def test_find_spec_missing(self): spec = self.machinery.WindowsRegistryFinder.find_spec('spam') @@ -18,12 +68,42 @@ class WindowsRegistryFinderTests: loader = self.machinery.WindowsRegistryFinder.find_module('spam') self.assertIs(loader, None) + def test_module_found(self): + with setup_module(self.machinery, self.test_module): + loader = self.machinery.WindowsRegistryFinder.find_module(self.test_module) + spec = self.machinery.WindowsRegistryFinder.find_spec(self.test_module) + self.assertIsNot(loader, None) + self.assertIsNot(spec, None) + + def test_module_not_found(self): + with setup_module(self.machinery, self.test_module, path="."): + loader = self.machinery.WindowsRegistryFinder.find_module(self.test_module) + spec = self.machinery.WindowsRegistryFinder.find_spec(self.test_module) + self.assertIsNone(loader) + self.assertIsNone(spec) + +(Frozen_WindowsRegistryFinderTests, + Source_WindowsRegistryFinderTests + ) = test_util.test_both(WindowsRegistryFinderTests, machinery=machinery) + +@unittest.skipUnless(sys.platform.startswith('win'), 'requires Windows') +class WindowsExtensionSuffixTests: + def test_tagged_suffix(self): + suffixes = self.machinery.EXTENSION_SUFFIXES + expected_tag = ".cp{0.major}{0.minor}-{1}.pyd".format(sys.version_info, + re.sub('[^a-zA-Z0-9]', '_', get_platform())) + try: + untagged_i = suffixes.index(".pyd") + except ValueError: + untagged_i = suffixes.index("_d.pyd") + expected_tag = "_d" + expected_tag -class Frozen_WindowsRegistryFinderTests(WindowsRegistryFinderTests, - unittest.TestCase): - machinery = frozen_machinery + self.assertIn(expected_tag, suffixes) + # Ensure the tags are in the correct order + tagged_i = suffixes.index(expected_tag) + self.assertLess(tagged_i, untagged_i) -class Source_WindowsRegistryFinderTests(WindowsRegistryFinderTests, - unittest.TestCase): - machinery = source_machinery +(Frozen_WindowsExtensionSuffixTests, + Source_WindowsExtensionSuffixTests + ) = test_util.test_both(WindowsExtensionSuffixTests, machinery=machinery) diff --git a/Lib/test/test_importlib/util.py b/Lib/test/test_importlib/util.py index 885cec3..aa4cd7e 100644 --- a/Lib/test/test_importlib/util.py +++ b/Lib/test/test_importlib/util.py @@ -1,31 +1,85 @@ -from contextlib import contextmanager -from importlib import util, invalidate_caches +import builtins +import contextlib +import errno +import functools +import importlib +from importlib import machinery, util, invalidate_caches +import os import os.path from test import support import unittest import sys +import tempfile import types +BUILTINS = types.SimpleNamespace() +BUILTINS.good_name = None +BUILTINS.bad_name = None +if 'errno' in sys.builtin_module_names: + BUILTINS.good_name = 'errno' +if 'importlib' not in sys.builtin_module_names: + BUILTINS.bad_name = 'importlib' + +EXTENSIONS = types.SimpleNamespace() +EXTENSIONS.path = None +EXTENSIONS.ext = None +EXTENSIONS.filename = None +EXTENSIONS.file_path = None +EXTENSIONS.name = '_testcapi' + +def _extension_details(): + global EXTENSIONS + for path in sys.path: + for ext in machinery.EXTENSION_SUFFIXES: + filename = EXTENSIONS.name + ext + file_path = os.path.join(path, filename) + if os.path.exists(file_path): + EXTENSIONS.path = path + EXTENSIONS.ext = ext + EXTENSIONS.filename = filename + EXTENSIONS.file_path = file_path + return + +_extension_details() + + def import_importlib(module_name): """Import a module from importlib both w/ and w/o _frozen_importlib.""" fresh = ('importlib',) if '.' in module_name else () frozen = support.import_fresh_module(module_name) source = support.import_fresh_module(module_name, fresh=fresh, blocked=('_frozen_importlib',)) + return {'Frozen': frozen, 'Source': source} + + +def specialize_class(cls, kind, base=None, **kwargs): + # XXX Support passing in submodule names--load (and cache) them? + # That would clean up the test modules a bit more. + if base is None: + base = unittest.TestCase + elif not isinstance(base, type): + base = base[kind] + name = '{}_{}'.format(kind, cls.__name__) + bases = (cls, base) + specialized = types.new_class(name, bases) + specialized.__module__ = cls.__module__ + specialized._NAME = cls.__name__ + specialized._KIND = kind + for attr, values in kwargs.items(): + value = values[kind] + setattr(specialized, attr, value) + return specialized + + +def split_frozen(cls, base=None, **kwargs): + frozen = specialize_class(cls, 'Frozen', base, **kwargs) + source = specialize_class(cls, 'Source', base, **kwargs) return frozen, source -def test_both(test_class, **kwargs): - frozen_tests = types.new_class('Frozen_'+test_class.__name__, - (test_class, unittest.TestCase)) - source_tests = types.new_class('Source_'+test_class.__name__, - (test_class, unittest.TestCase)) - frozen_tests.__module__ = source_tests.__module__ = test_class.__module__ - for attr, (frozen_value, source_value) in kwargs.items(): - setattr(frozen_tests, attr, frozen_value) - setattr(source_tests, attr, source_value) - return frozen_tests, source_tests +def test_both(test_class, base=None, **kwargs): + return split_frozen(test_class, base, **kwargs) CASE_INSENSITIVE_FS = True @@ -38,6 +92,10 @@ if sys.platform not in ('win32', 'cygwin'): if not os.path.exists(changed_name): CASE_INSENSITIVE_FS = False +source_importlib = import_importlib('importlib')['Source'] +__import__ = {'Frozen': staticmethod(builtins.__import__), + 'Source': staticmethod(source_importlib.__import__)} + def case_insensitive_tests(test): """Class decorator that nullifies tests requiring a case-insensitive @@ -53,7 +111,7 @@ def submodule(parent, name, pkg_dir, content=''): return '{}.{}'.format(parent, name), path -@contextmanager +@contextlib.contextmanager def uncache(*names): """Uncache a module from sys.modules. @@ -79,7 +137,7 @@ def uncache(*names): pass -@contextmanager +@contextlib.contextmanager def temp_module(name, content='', *, pkg=False): conflicts = [n for n in sys.modules if n.partition('.')[0] == name] with support.temp_cwd(None) as cwd: @@ -103,7 +161,7 @@ def temp_module(name, content='', *, pkg=False): yield location -@contextmanager +@contextlib.contextmanager def import_state(**kwargs): """Context manager to manage the various importers and stored state in the sys module. @@ -198,6 +256,7 @@ class mock_modules(_ImporterMock): raise return self.modules[fullname] + class mock_spec(_ImporterMock): """Importer mock using PEP 451 APIs.""" @@ -223,3 +282,99 @@ class mock_spec(_ImporterMock): self.module_code[module.__spec__.name]() except KeyError: pass + + +def writes_bytecode_files(fxn): + """Decorator to protect sys.dont_write_bytecode from mutation and to skip + tests that require it to be set to False.""" + if sys.dont_write_bytecode: + return lambda *args, **kwargs: None + @functools.wraps(fxn) + def wrapper(*args, **kwargs): + original = sys.dont_write_bytecode + sys.dont_write_bytecode = False + try: + to_return = fxn(*args, **kwargs) + finally: + sys.dont_write_bytecode = original + return to_return + return wrapper + + +def ensure_bytecode_path(bytecode_path): + """Ensure that the __pycache__ directory for PEP 3147 pyc file exists. + + :param bytecode_path: File system path to PEP 3147 pyc file. + """ + try: + os.mkdir(os.path.dirname(bytecode_path)) + except OSError as error: + if error.errno != errno.EEXIST: + raise + + +@contextlib.contextmanager +def create_modules(*names): + """Temporarily create each named module with an attribute (named 'attr') + that contains the name passed into the context manager that caused the + creation of the module. + + All files are created in a temporary directory returned by + tempfile.mkdtemp(). This directory is inserted at the beginning of + sys.path. When the context manager exits all created files (source and + bytecode) are explicitly deleted. + + No magic is performed when creating packages! This means that if you create + a module within a package you must also create the package's __init__ as + well. + + """ + source = 'attr = {0!r}' + created_paths = [] + mapping = {} + state_manager = None + uncache_manager = None + try: + temp_dir = tempfile.mkdtemp() + mapping['.root'] = temp_dir + import_names = set() + for name in names: + if not name.endswith('__init__'): + import_name = name + else: + import_name = name[:-len('.__init__')] + import_names.add(import_name) + if import_name in sys.modules: + del sys.modules[import_name] + name_parts = name.split('.') + file_path = temp_dir + for directory in name_parts[:-1]: + file_path = os.path.join(file_path, directory) + if not os.path.exists(file_path): + os.mkdir(file_path) + created_paths.append(file_path) + file_path = os.path.join(file_path, name_parts[-1] + '.py') + with open(file_path, 'w') as file: + file.write(source.format(name)) + created_paths.append(file_path) + mapping[name] = file_path + uncache_manager = uncache(*import_names) + uncache_manager.__enter__() + state_manager = import_state(path=[temp_dir]) + state_manager.__enter__() + yield mapping + finally: + if state_manager is not None: + state_manager.__exit__(None, None, None) + if uncache_manager is not None: + uncache_manager.__exit__(None, None, None) + support.rmtree(temp_dir) + + +def mock_path_hook(*entries, importer): + """A mock sys.path_hooks entry.""" + def hook(entry): + if entry not in entries: + raise ImportError + return importer + return hook diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index fc3bf07..bbc5385 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -1,3 +1,4 @@ +import builtins import collections import datetime import functools @@ -8,6 +9,7 @@ import linecache import os from os.path import normcase import _pickle +import pickle import re import shutil import sys @@ -23,7 +25,7 @@ except ImportError: ThreadPoolExecutor = None from test.support import run_unittest, TESTFN, DirsOnSysPath, cpython_only -from test.support import MISSING_C_DOCSTRINGS +from test.support import MISSING_C_DOCSTRINGS, cpython_only from test.script_helper import assert_python_ok, assert_python_failure from test import inspect_fodder as mod from test import inspect_fodder2 as mod2 @@ -76,6 +78,7 @@ def generator_function_example(self): for i in range(2): yield i + class TestPredicates(IsTestBase): def test_sixteen(self): count = len([x for x in dir(inspect) if x.startswith('is')]) @@ -182,6 +185,14 @@ class TestInterpreterStack(IsTestBase): (modfile, 43, 'argue', [' spam(a, b, c)\n'], 0)) self.assertEqual(revise(*mod.st[3][1:]), (modfile, 39, 'abuse', [' self.argue(a, b, c)\n'], 0)) + # Test named tuple fields + record = mod.st[0] + self.assertIs(record.frame, mod.fr) + self.assertEqual(record.lineno, 16) + self.assertEqual(record.filename, mod.__file__) + self.assertEqual(record.function, 'eggs') + self.assertIn('inspect.stack()', record.code_context[0]) + self.assertEqual(record.index, 0) def test_trace(self): self.assertEqual(len(git.tr), 3) @@ -369,6 +380,9 @@ class TestDecorators(GetSourceBase): def test_replacing_decorator(self): self.assertSourceEqual(mod2.gone, 9, 10) + def test_getsource_unwrap(self): + self.assertSourceEqual(mod2.real, 122, 124) + class TestOneliners(GetSourceBase): fodderModule = mod2 def test_oneline_lambda(self): @@ -1615,6 +1629,68 @@ class TestGetGeneratorState(unittest.TestCase): self.assertRaises(TypeError, inspect.getgeneratorlocals, (2,3)) +class MySignature(inspect.Signature): + # Top-level to make it picklable; + # used in test_signature_object_pickle + pass + +class MyParameter(inspect.Parameter): + # Top-level to make it picklable; + # used in test_signature_object_pickle + pass + + @cpython_only + @unittest.skipIf(MISSING_C_DOCSTRINGS, + "Signature information for builtins requires docstrings") + def test_builtins_have_signatures(self): + # This checks all builtin callables in CPython have signatures + # A few have signatures Signature can't yet handle, so we skip those + # since they will have to wait until PEP 457 adds the required + # introspection support to the inspect module + # Some others also haven't been converted yet for various other + # reasons, so we also skip those for the time being, but design + # the test to fail in order to indicate when it needs to be + # updated. + no_signature = set() + # These need PEP 457 groups + needs_groups = ["range", "slice", "dir", "getattr", + "next", "iter", "vars"] + no_signature |= needs_groups + # These need PEP 457 groups or a signature change to accept None + needs_semantic_update = ["round"] + no_signature |= needs_semantic_update + # These need *args support in Argument Clinic + needs_varargs = ["min", "max", "print", "__build_class__"] + no_signature |= needs_varargs + # These simply weren't covered in the initial AC conversion + # for builtin callables + not_converted_yet = ["open", "__import__"] + no_signature |= not_converted_yet + # These builtin types are expected to provide introspection info + types_with_signatures = set() + # Check the signatures we expect to be there + ns = vars(builtins) + for name, obj in sorted(ns.items()): + if not callable(obj): + continue + # The builtin types haven't been converted to AC yet + if isinstance(obj, type) and (name not in types_with_signatures): + # Note that this also skips all the exception types + no_signature.append(name) + if (name in no_signature): + # Not yet converted + continue + with self.subTest(builtin=name): + self.assertIsNotNone(inspect.signature(obj)) + # Check callables that haven't been converted don't claim a signature + # This ensures this test will start failing as more signatures are + # added, so the affected items can be moved into the scope of the + # regression test above + for name in no_signature: + with self.subTest(builtin=name): + self.assertIsNone(ns[name].__text_signature__) + + class TestSignatureObject(unittest.TestCase): @staticmethod def signature(func): @@ -1672,6 +1748,37 @@ class TestSignatureObject(unittest.TestCase): with self.assertRaisesRegex(ValueError, 'follows default argument'): S((pkd, pk)) + self.assertTrue(repr(sig).startswith('<Signature')) + self.assertTrue('"(po, pk' in repr(sig)) + + def test_signature_object_pickle(self): + def foo(a, b, *, c:1={}, **kw) -> {42:'ham'}: pass + foo_partial = functools.partial(foo, a=1) + + sig = inspect.signature(foo_partial) + + for ver in range(pickle.HIGHEST_PROTOCOL + 1): + with self.subTest(pickle_ver=ver, subclass=False): + sig_pickled = pickle.loads(pickle.dumps(sig, ver)) + self.assertEqual(sig, sig_pickled) + + # Test that basic sub-classing works + sig = inspect.signature(foo) + myparam = MyParameter(name='z', kind=inspect.Parameter.POSITIONAL_ONLY) + myparams = collections.OrderedDict(sig.parameters, a=myparam) + mysig = MySignature().replace(parameters=myparams.values(), + return_annotation=sig.return_annotation) + self.assertTrue(isinstance(mysig, MySignature)) + self.assertTrue(isinstance(mysig.parameters['z'], MyParameter)) + + for ver in range(pickle.HIGHEST_PROTOCOL + 1): + with self.subTest(pickle_ver=ver, subclass=True): + sig_pickled = pickle.loads(pickle.dumps(mysig, ver)) + self.assertEqual(mysig, sig_pickled) + self.assertTrue(isinstance(sig_pickled, MySignature)) + self.assertTrue(isinstance(sig_pickled.parameters['z'], + MyParameter)) + def test_signature_immutability(self): def test(a): pass @@ -2435,49 +2542,91 @@ class TestSignatureObject(unittest.TestCase): def bar(a, *, b:int) -> float: pass self.assertEqual(inspect.signature(foo), inspect.signature(bar)) + self.assertEqual( + hash(inspect.signature(foo)), hash(inspect.signature(bar))) def bar(a, *, b:int) -> int: pass self.assertNotEqual(inspect.signature(foo), inspect.signature(bar)) + self.assertNotEqual( + hash(inspect.signature(foo)), hash(inspect.signature(bar))) def bar(a, *, b:int): pass self.assertNotEqual(inspect.signature(foo), inspect.signature(bar)) + self.assertNotEqual( + hash(inspect.signature(foo)), hash(inspect.signature(bar))) def bar(a, *, b:int=42) -> float: pass self.assertNotEqual(inspect.signature(foo), inspect.signature(bar)) + self.assertNotEqual( + hash(inspect.signature(foo)), hash(inspect.signature(bar))) def bar(a, *, c) -> float: pass self.assertNotEqual(inspect.signature(foo), inspect.signature(bar)) + self.assertNotEqual( + hash(inspect.signature(foo)), hash(inspect.signature(bar))) def bar(a, b:int) -> float: pass self.assertNotEqual(inspect.signature(foo), inspect.signature(bar)) + self.assertNotEqual( + hash(inspect.signature(foo)), hash(inspect.signature(bar))) def spam(b:int, a) -> float: pass self.assertNotEqual(inspect.signature(spam), inspect.signature(bar)) + self.assertNotEqual( + hash(inspect.signature(spam)), hash(inspect.signature(bar))) def foo(*, a, b, c): pass def bar(*, c, b, a): pass self.assertEqual(inspect.signature(foo), inspect.signature(bar)) + self.assertEqual( + hash(inspect.signature(foo)), hash(inspect.signature(bar))) def foo(*, a=1, b, c): pass def bar(*, c, b, a=1): pass self.assertEqual(inspect.signature(foo), inspect.signature(bar)) + self.assertEqual( + hash(inspect.signature(foo)), hash(inspect.signature(bar))) def foo(pos, *, a=1, b, c): pass def bar(pos, *, c, b, a=1): pass self.assertEqual(inspect.signature(foo), inspect.signature(bar)) + self.assertEqual( + hash(inspect.signature(foo)), hash(inspect.signature(bar))) def foo(pos, *, a, b, c): pass def bar(pos, *, c, b, a=1): pass self.assertNotEqual(inspect.signature(foo), inspect.signature(bar)) + self.assertNotEqual( + hash(inspect.signature(foo)), hash(inspect.signature(bar))) def foo(pos, *args, a=42, b, c, **kwargs:int): pass def bar(pos, *args, c, b, a=42, **kwargs:int): pass self.assertEqual(inspect.signature(foo), inspect.signature(bar)) + self.assertEqual( + hash(inspect.signature(foo)), hash(inspect.signature(bar))) + + def test_signature_hashable(self): + S = inspect.Signature + P = inspect.Parameter - def test_signature_unhashable(self): def foo(a): pass - sig = inspect.signature(foo) + foo_sig = inspect.signature(foo) + + manual_sig = S(parameters=[P('a', P.POSITIONAL_OR_KEYWORD)]) + + self.assertEqual(hash(foo_sig), hash(manual_sig)) + self.assertNotEqual(hash(foo_sig), + hash(manual_sig.replace(return_annotation='spam'))) + + def bar(a) -> 1: pass + self.assertNotEqual(hash(foo_sig), hash(inspect.signature(bar))) + + def foo(a={}): pass with self.assertRaisesRegex(TypeError, 'unhashable type'): - hash(sig) + hash(inspect.signature(foo)) + + def foo(a) -> {}: pass + with self.assertRaisesRegex(TypeError, 'unhashable type'): + hash(inspect.signature(foo)) def test_signature_str(self): def foo(a:int=1, *, b, c=None, **kwargs) -> 42: @@ -2551,6 +2700,19 @@ class TestSignatureObject(unittest.TestCase): self.assertEqual(self.signature(Spam.foo), self.signature(Ham.foo)) + def test_signature_from_callable_python_obj(self): + class MySignature(inspect.Signature): pass + def foo(a, *, b:1): pass + foo_sig = MySignature.from_callable(foo) + self.assertTrue(isinstance(foo_sig, MySignature)) + + @unittest.skipIf(MISSING_C_DOCSTRINGS, + "Signature information for builtins requires docstrings") + def test_signature_from_callable_builtin_obj(self): + class MySignature(inspect.Signature): pass + sig = MySignature.from_callable(_pickle.Pickler) + self.assertTrue(isinstance(sig, MySignature)) + class TestParameterObject(unittest.TestCase): def test_signature_parameter_kinds(self): @@ -2596,6 +2758,16 @@ class TestParameterObject(unittest.TestCase): p.replace(kind=inspect.Parameter.VAR_POSITIONAL) self.assertTrue(repr(p).startswith('<Parameter')) + self.assertTrue('"a=42"' in repr(p)) + + def test_signature_parameter_hashable(self): + P = inspect.Parameter + foo = P('foo', kind=P.POSITIONAL_ONLY) + self.assertEqual(hash(foo), hash(P('foo', kind=P.POSITIONAL_ONLY))) + self.assertNotEqual(hash(foo), hash(P('foo', kind=P.POSITIONAL_ONLY, + default=42))) + self.assertNotEqual(hash(foo), + hash(foo.replace(kind=P.VAR_POSITIONAL))) def test_signature_parameter_equality(self): P = inspect.Parameter @@ -2607,13 +2779,6 @@ class TestParameterObject(unittest.TestCase): self.assertEqual(p, P('foo', default=42, kind=inspect.Parameter.KEYWORD_ONLY)) - def test_signature_parameter_unhashable(self): - p = inspect.Parameter('foo', default=42, - kind=inspect.Parameter.KEYWORD_ONLY) - - with self.assertRaisesRegex(TypeError, 'unhashable type'): - hash(p) - def test_signature_parameter_replace(self): p = inspect.Parameter('foo', default=42, kind=inspect.Parameter.KEYWORD_ONLY) @@ -2922,6 +3087,16 @@ class TestBoundArguments(unittest.TestCase): ba4 = inspect.signature(bar).bind(1) self.assertNotEqual(ba, ba4) + def test_signature_bound_arguments_pickle(self): + def foo(a, b, *, c:1={}, **kw) -> {42:'ham'}: pass + sig = inspect.signature(foo) + ba = sig.bind(20, 30, z={}) + + for ver in range(pickle.HIGHEST_PROTOCOL + 1): + with self.subTest(pickle_ver=ver): + ba_pickled = pickle.loads(pickle.dumps(ba, ver)) + self.assertEqual(ba, ba_pickled) + class TestSignaturePrivateHelpers(unittest.TestCase): def test_signature_get_bound_param(self): diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index a424f76..c867502 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -44,10 +44,6 @@ try: import threading except ImportError: threading = None -try: - import fcntl -except ImportError: - fcntl = None def _default_chunk_size(): """Get the default TextIOWrapper chunk size""" @@ -367,8 +363,8 @@ class IOTest(unittest.TestCase): def test_open_handles_NUL_chars(self): fn_with_NUL = 'foo\0bar' - self.assertRaises(TypeError, self.open, fn_with_NUL, 'w') - self.assertRaises(TypeError, self.open, bytes(fn_with_NUL, 'ascii'), 'w') + self.assertRaises(ValueError, self.open, fn_with_NUL, 'w') + self.assertRaises(ValueError, self.open, bytes(fn_with_NUL, 'ascii'), 'w') def test_raw_file_io(self): with self.open(support.TESTFN, "wb", buffering=0) as f: @@ -780,7 +776,7 @@ class CommonBufferedTests: def test_repr(self): raw = self.MockRawIO() b = self.tp(raw) - clsname = "%s.%s" % (self.tp.__module__, self.tp.__name__) + clsname = "%s.%s" % (self.tp.__module__, self.tp.__qualname__) self.assertEqual(repr(b), "<%s>" % clsname) raw.name = "dummy" self.assertEqual(repr(b), "<%s name='dummy'>" % clsname) @@ -945,6 +941,71 @@ class BufferedReaderTest(unittest.TestCase, CommonBufferedTests): self.assertEqual(bufio.readinto(b), 1) self.assertEqual(b, b"cb") + def test_readinto1(self): + buffer_size = 10 + rawio = self.MockRawIO((b"abc", b"de", b"fgh", b"jkl")) + bufio = self.tp(rawio, buffer_size=buffer_size) + b = bytearray(2) + self.assertEqual(bufio.peek(3), b'abc') + self.assertEqual(rawio._reads, 1) + self.assertEqual(bufio.readinto1(b), 2) + self.assertEqual(b, b"ab") + self.assertEqual(rawio._reads, 1) + self.assertEqual(bufio.readinto1(b), 1) + self.assertEqual(b[:1], b"c") + self.assertEqual(rawio._reads, 1) + self.assertEqual(bufio.readinto1(b), 2) + self.assertEqual(b, b"de") + self.assertEqual(rawio._reads, 2) + b = bytearray(2*buffer_size) + self.assertEqual(bufio.peek(3), b'fgh') + self.assertEqual(rawio._reads, 3) + self.assertEqual(bufio.readinto1(b), 6) + self.assertEqual(b[:6], b"fghjkl") + self.assertEqual(rawio._reads, 4) + + def test_readinto_array(self): + buffer_size = 60 + data = b"a" * 26 + rawio = self.MockRawIO((data,)) + bufio = self.tp(rawio, buffer_size=buffer_size) + + # Create an array with element size > 1 byte + b = array.array('i', b'x' * 32) + assert len(b) != 16 + + # Read into it. We should get as many *bytes* as we can fit into b + # (which is more than the number of elements) + n = bufio.readinto(b) + self.assertGreater(n, len(b)) + + # Check that old contents of b are preserved + bm = memoryview(b).cast('B') + self.assertLess(n, len(bm)) + self.assertEqual(bm[:n], data[:n]) + self.assertEqual(bm[n:], b'x' * (len(bm[n:]))) + + def test_readinto1_array(self): + buffer_size = 60 + data = b"a" * 26 + rawio = self.MockRawIO((data,)) + bufio = self.tp(rawio, buffer_size=buffer_size) + + # Create an array with element size > 1 byte + b = array.array('i', b'x' * 32) + assert len(b) != 16 + + # Read into it. We should get as many *bytes* as we can fit into b + # (which is more than the number of elements) + n = bufio.readinto1(b) + self.assertGreater(n, len(b)) + + # Check that old contents of b are preserved + bm = memoryview(b).cast('B') + self.assertLess(n, len(bm)) + self.assertEqual(bm[:n], data[:n]) + self.assertEqual(bm[n:], b'x' * (len(bm[n:]))) + def test_readlines(self): def bufio(): rawio = self.MockRawIO((b"abc\n", b"d\n", b"ef")) @@ -2795,6 +2856,17 @@ class TextIOWrapperTest(unittest.TestCase): self.assertFalse(err) self.assertEqual("ok", out.decode().strip()) + def test_read_byteslike(self): + r = MemviewBytesIO(b'Just some random string\n') + t = self.TextIOWrapper(r, 'utf-8') + + # TextIOwrapper will not read the full string, because + # we truncate it to a multiple of the native int size + # so that we can construct a more complex memoryview. + bytes_val = _to_memoryview(r.getvalue()).tobytes() + + self.assertEqual(t.read(200), bytes_val.decode('utf-8')) + def test_issue22849(self): class F(object): def readable(self): return True @@ -2811,6 +2883,25 @@ class TextIOWrapperTest(unittest.TestCase): t = self.TextIOWrapper(F(), encoding='utf-8') +class MemviewBytesIO(io.BytesIO): + '''A BytesIO object whose read method returns memoryviews + rather than bytes''' + + def read1(self, len_): + return _to_memoryview(super().read1(len_)) + + def read(self, len_): + return _to_memoryview(super().read(len_)) + +def _to_memoryview(buf): + '''Convert bytes-object *buf* to a non-trivial memoryview''' + + arr = array.array('i') + idx = len(buf) - len(buf) % arr.itemsize + arr.frombytes(buf[:idx]) + return memoryview(arr) + + class CTextIOWrapperTest(TextIOWrapperTest): io = io shutdown_error = "RuntimeError: could not find io module state" @@ -3057,6 +3148,8 @@ class MiscIOTest(unittest.TestCase): self.assertRaises(ValueError, f.readall) if hasattr(f, "readinto"): self.assertRaises(ValueError, f.readinto, bytearray(1024)) + if hasattr(f, "readinto1"): + self.assertRaises(ValueError, f.readinto1, bytearray(1024)) self.assertRaises(ValueError, f.readline) self.assertRaises(ValueError, f.readlines) self.assertRaises(ValueError, f.seek, 0) @@ -3170,26 +3263,20 @@ class MiscIOTest(unittest.TestCase): with self.open(support.TESTFN, **kwargs) as f: self.assertRaises(TypeError, pickle.dumps, f, protocol) - @unittest.skipUnless(fcntl, 'fcntl required for this test') def test_nonblock_pipe_write_bigbuf(self): self._test_nonblock_pipe_write(16*1024) - @unittest.skipUnless(fcntl, 'fcntl required for this test') def test_nonblock_pipe_write_smallbuf(self): self._test_nonblock_pipe_write(1024) - def _set_non_blocking(self, fd): - flags = fcntl.fcntl(fd, fcntl.F_GETFL) - self.assertNotEqual(flags, -1) - res = fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK) - self.assertEqual(res, 0) - + @unittest.skipUnless(hasattr(os, 'set_blocking'), + 'os.set_blocking() required for this test') def _test_nonblock_pipe_write(self, bufsize): sent = [] received = [] r, w = os.pipe() - self._set_non_blocking(r) - self._set_non_blocking(w) + os.set_blocking(r, False) + os.set_blocking(w, False) # To exercise all code paths in the C implementation we need # to play with buffer sizes. For instance, if we choose a diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py index a0fe55c..5ec2cd4 100644 --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -8,6 +8,7 @@ import unittest import re import contextlib import operator +import pickle import ipaddress @@ -82,6 +83,13 @@ class CommonTestMixin: self.assertRaises(TypeError, hex, self.factory(1)) self.assertRaises(TypeError, bytes, self.factory(1)) + def pickle_test(self, addr): + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + with self.subTest(proto=proto): + x = self.factory(addr) + y = pickle.loads(pickle.dumps(x, proto)) + self.assertEqual(y, x) + class CommonTestMixin_v4(CommonTestMixin): @@ -247,6 +255,9 @@ class AddressTestCase_v4(BaseTestCase, CommonTestMixin_v4): assertBadOctet("257.0.0.0", 257) assertBadOctet("192.168.0.999", 999) + def test_pickle(self): + self.pickle_test('192.0.2.1') + class AddressTestCase_v6(BaseTestCase, CommonTestMixin_v6): factory = ipaddress.IPv6Address @@ -379,6 +390,9 @@ class AddressTestCase_v6(BaseTestCase, CommonTestMixin_v6): assertBadPart("02001:db8::", "02001") assertBadPart('2001:888888::1', "888888") + def test_pickle(self): + self.pickle_test('2001:db8::') + class NetmaskTestMixin_v4(CommonTestMixin_v4): """Input validation on interfaces and networks is very similar""" @@ -442,6 +456,11 @@ class NetmaskTestMixin_v4(CommonTestMixin_v4): assertBadNetmask("1.1.1.1", "pudding") assertBadNetmask("1.1.1.1", "::") + def test_pickle(self): + self.pickle_test('192.0.2.0/27') + self.pickle_test('192.0.2.0/31') # IPV4LENGTH - 1 + self.pickle_test('192.0.2.0') # IPV4LENGTH + class InterfaceTestCase_v4(BaseTestCase, NetmaskTestMixin_v4): factory = ipaddress.IPv4Interface @@ -500,6 +519,11 @@ class NetmaskTestMixin_v6(CommonTestMixin_v6): assertBadNetmask("::1", "pudding") assertBadNetmask("::", "::") + def test_pickle(self): + self.pickle_test('2001:db8::1000/124') + self.pickle_test('2001:db8::1000/127') # IPV6LENGTH - 1 + self.pickle_test('2001:db8::1000') # IPV6LENGTH + class InterfaceTestCase_v6(BaseTestCase, NetmaskTestMixin_v6): factory = ipaddress.IPv6Interface @@ -633,6 +657,119 @@ class IpaddrUnitTest(unittest.TestCase): self.assertEqual("IPv6Interface('::1/128')", repr(ipaddress.IPv6Interface('::1'))) + # issue #16531: constructing IPv4Network from a (address, mask) tuple + def testIPv4Tuple(self): + # /32 + ip = ipaddress.IPv4Address('192.0.2.1') + net = ipaddress.IPv4Network('192.0.2.1/32') + self.assertEqual(ipaddress.IPv4Network(('192.0.2.1', 32)), net) + self.assertEqual(ipaddress.IPv4Network((ip, 32)), net) + self.assertEqual(ipaddress.IPv4Network((3221225985, 32)), net) + self.assertEqual(ipaddress.IPv4Network(('192.0.2.1', + '255.255.255.255')), net) + self.assertEqual(ipaddress.IPv4Network((ip, + '255.255.255.255')), net) + self.assertEqual(ipaddress.IPv4Network((3221225985, + '255.255.255.255')), net) + # strict=True and host bits set + with self.assertRaises(ValueError): + ipaddress.IPv4Network(('192.0.2.1', 24)) + with self.assertRaises(ValueError): + ipaddress.IPv4Network((ip, 24)) + with self.assertRaises(ValueError): + ipaddress.IPv4Network((3221225985, 24)) + with self.assertRaises(ValueError): + ipaddress.IPv4Network(('192.0.2.1', '255.255.255.0')) + with self.assertRaises(ValueError): + ipaddress.IPv4Network((ip, '255.255.255.0')) + with self.assertRaises(ValueError): + ipaddress.IPv4Network((3221225985, '255.255.255.0')) + # strict=False and host bits set + net = ipaddress.IPv4Network('192.0.2.0/24') + self.assertEqual(ipaddress.IPv4Network(('192.0.2.1', 24), + strict=False), net) + self.assertEqual(ipaddress.IPv4Network((ip, 24), + strict=False), net) + self.assertEqual(ipaddress.IPv4Network((3221225985, 24), + strict=False), net) + self.assertEqual(ipaddress.IPv4Network(('192.0.2.1', + '255.255.255.0'), + strict=False), net) + self.assertEqual(ipaddress.IPv4Network((ip, + '255.255.255.0'), + strict=False), net) + self.assertEqual(ipaddress.IPv4Network((3221225985, + '255.255.255.0'), + strict=False), net) + + # /24 + ip = ipaddress.IPv4Address('192.0.2.0') + net = ipaddress.IPv4Network('192.0.2.0/24') + self.assertEqual(ipaddress.IPv4Network(('192.0.2.0', + '255.255.255.0')), net) + self.assertEqual(ipaddress.IPv4Network((ip, + '255.255.255.0')), net) + self.assertEqual(ipaddress.IPv4Network((3221225984, + '255.255.255.0')), net) + self.assertEqual(ipaddress.IPv4Network(('192.0.2.0', 24)), net) + self.assertEqual(ipaddress.IPv4Network((ip, 24)), net) + self.assertEqual(ipaddress.IPv4Network((3221225984, 24)), net) + + self.assertEqual(ipaddress.IPv4Interface(('192.0.2.1', 24)), + ipaddress.IPv4Interface('192.0.2.1/24')) + self.assertEqual(ipaddress.IPv4Interface((3221225985, 24)), + ipaddress.IPv4Interface('192.0.2.1/24')) + + # issue #16531: constructing IPv6Network from a (address, mask) tuple + def testIPv6Tuple(self): + # /128 + ip = ipaddress.IPv6Address('2001:db8::') + net = ipaddress.IPv6Network('2001:db8::/128') + self.assertEqual(ipaddress.IPv6Network(('2001:db8::', '128')), + net) + self.assertEqual(ipaddress.IPv6Network( + (42540766411282592856903984951653826560, 128)), + net) + self.assertEqual(ipaddress.IPv6Network((ip, '128')), + net) + ip = ipaddress.IPv6Address('2001:db8::') + net = ipaddress.IPv6Network('2001:db8::/96') + self.assertEqual(ipaddress.IPv6Network(('2001:db8::', '96')), + net) + self.assertEqual(ipaddress.IPv6Network( + (42540766411282592856903984951653826560, 96)), + net) + self.assertEqual(ipaddress.IPv6Network((ip, '96')), + net) + + # strict=True and host bits set + ip = ipaddress.IPv6Address('2001:db8::1') + with self.assertRaises(ValueError): + ipaddress.IPv6Network(('2001:db8::1', 96)) + with self.assertRaises(ValueError): + ipaddress.IPv6Network(( + 42540766411282592856903984951653826561, 96)) + with self.assertRaises(ValueError): + ipaddress.IPv6Network((ip, 96)) + # strict=False and host bits set + net = ipaddress.IPv6Network('2001:db8::/96') + self.assertEqual(ipaddress.IPv6Network(('2001:db8::1', 96), + strict=False), + net) + self.assertEqual(ipaddress.IPv6Network( + (42540766411282592856903984951653826561, 96), + strict=False), + net) + self.assertEqual(ipaddress.IPv6Network((ip, 96), strict=False), + net) + + # /96 + self.assertEqual(ipaddress.IPv6Interface(('2001:db8::1', '96')), + ipaddress.IPv6Interface('2001:db8::1/96')) + self.assertEqual(ipaddress.IPv6Interface( + (42540766411282592856903984951653826561, '96')), + ipaddress.IPv6Interface('2001:db8::1/96')) + # issue57 def testAddressIntMath(self): self.assertEqual(ipaddress.IPv4Address('1.1.1.1') + 255, @@ -653,20 +790,18 @@ class IpaddrUnitTest(unittest.TestCase): 2 ** ipaddress.IPV6LENGTH) def testInternals(self): - first, last = ipaddress._find_address_range([ - ipaddress.IPv4Address('10.10.10.10'), - ipaddress.IPv4Address('10.10.10.12')]) - self.assertEqual(first, last) + ip1 = ipaddress.IPv4Address('10.10.10.10') + ip2 = ipaddress.IPv4Address('10.10.10.11') + ip3 = ipaddress.IPv4Address('10.10.10.12') + self.assertEqual(list(ipaddress._find_address_range([ip1])), + [(ip1, ip1)]) + self.assertEqual(list(ipaddress._find_address_range([ip1, ip3])), + [(ip1, ip1), (ip3, ip3)]) + self.assertEqual(list(ipaddress._find_address_range([ip1, ip2, ip3])), + [(ip1, ip3)]) self.assertEqual(128, ipaddress._count_righthand_zero_bits(0, 128)) self.assertEqual("IPv4Network('1.2.3.0/24')", repr(self.ipv4_network)) - def testMissingAddressVersion(self): - class Broken(ipaddress._BaseAddress): - pass - broken = Broken('127.0.0.1') - with self.assertRaisesRegex(NotImplementedError, "Broken.*version"): - broken.version - def testMissingNetworkVersion(self): class Broken(ipaddress._BaseNetwork): pass @@ -1598,6 +1733,14 @@ class IpaddrUnitTest(unittest.TestCase): addr3.exploded) self.assertEqual('192.168.178.1', addr4.exploded) + def testReversePointer(self): + addr1 = ipaddress.IPv4Address('127.0.0.1') + addr2 = ipaddress.IPv6Address('2001:db8::1') + self.assertEqual('1.0.0.127.in-addr.arpa', addr1.reverse_pointer) + self.assertEqual('1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.' + + 'b.d.0.1.0.0.2.ip6.arpa', + addr2.reverse_pointer) + def testIntRepresentation(self): self.assertEqual(16909060, int(self.ipv4_address)) self.assertEqual(42540616829182469433547762482097946625, diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py index 511094e..5cb0b08 100644 --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -1837,8 +1837,6 @@ class RegressionTests(unittest.TestCase): hist.append(3) yield 2 hist.append(4) - if x: - raise StopIteration hist = [] self.assertRaises(AssertionError, list, chain(gen1(), gen2(False))) diff --git a/Lib/test/test_json/test_encode_basestring_ascii.py b/Lib/test/test_json/test_encode_basestring_ascii.py index 480afd6..3a4ad18 100644 --- a/Lib/test/test_json/test_encode_basestring_ascii.py +++ b/Lib/test/test_json/test_encode_basestring_ascii.py @@ -11,9 +11,6 @@ CASES = [ (' s p a c e d ', '" s p a c e d "'), ('\U0001d120', '"\\ud834\\udd20"'), ('\u03b1\u03a9', '"\\u03b1\\u03a9"'), - ('\u03b1\u03a9', '"\\u03b1\\u03a9"'), - ('\u03b1\u03a9', '"\\u03b1\\u03a9"'), - ('\u03b1\u03a9', '"\\u03b1\\u03a9"'), ("`1~!@#$%^&*()_+-={':[,]}|;.</>?", '"`1~!@#$%^&*()_+-={\':[,]}|;.</>?"'), ('\x08\x0c\n\r\t', '"\\b\\f\\n\\r\\t"'), ('\u0123\u4567\u89ab\ucdef\uabcd\uef4a', '"\\u0123\\u4567\\u89ab\\ucdef\\uabcd\\uef4a"'), diff --git a/Lib/test/test_json/test_tool.py b/Lib/test/test_json/test_tool.py index 0c39e56..bd63e2b 100644 --- a/Lib/test/test_json/test_tool.py +++ b/Lib/test/test_json/test_tool.py @@ -6,6 +6,7 @@ import subprocess from test import support from test.script_helper import assert_python_ok + class TestTool(unittest.TestCase): data = """ @@ -15,7 +16,7 @@ class TestTool(unittest.TestCase): :"yes"} ] """ - expect = textwrap.dedent("""\ + expect_without_sort_keys = textwrap.dedent("""\ [ [ "blorpie" @@ -37,6 +38,28 @@ class TestTool(unittest.TestCase): ] """) + expect = textwrap.dedent("""\ + [ + [ + "blorpie" + ], + [ + "whoops" + ], + [], + "d-shtaeou", + "d-nthiouh", + "i-vhbjkhnth", + { + "nifty": 87 + }, + { + "morefield": false, + "field": "yes" + } + ] + """) + def test_stdin_stdout(self): with subprocess.Popen( (sys.executable, '-m', 'json.tool'), @@ -55,6 +78,7 @@ class TestTool(unittest.TestCase): def test_infile_stdout(self): infile = self._create_infile() rc, out, err = assert_python_ok('-m', 'json.tool', infile) + self.assertEqual(rc, 0) self.assertEqual(out.splitlines(), self.expect.encode().splitlines()) self.assertEqual(err, b'') @@ -65,5 +89,20 @@ class TestTool(unittest.TestCase): self.addCleanup(os.remove, outfile) with open(outfile, "r") as fp: self.assertEqual(fp.read(), self.expect) + self.assertEqual(rc, 0) self.assertEqual(out, b'') self.assertEqual(err, b'') + + def test_help_flag(self): + rc, out, err = assert_python_ok('-m', 'json.tool', '-h') + self.assertEqual(rc, 0) + self.assertTrue(out.startswith(b'usage: ')) + self.assertEqual(err, b'') + + def test_sort_keys_flag(self): + infile = self._create_infile() + rc, out, err = assert_python_ok('-m', 'json.tool', '--sort-keys', infile) + self.assertEqual(rc, 0) + self.assertEqual(out.splitlines(), + self.expect_without_sort_keys.encode().splitlines()) + self.assertEqual(err, b'') diff --git a/Lib/test/test_kqueue.py b/Lib/test/test_kqueue.py index f79bd89..2585ca6 100644 --- a/Lib/test/test_kqueue.py +++ b/Lib/test/test_kqueue.py @@ -114,7 +114,7 @@ class TestKQueue(unittest.TestCase): def test_queue_event(self): serverSocket = socket.socket() serverSocket.bind(('127.0.0.1', 0)) - serverSocket.listen(1) + serverSocket.listen() client = socket.socket() client.setblocking(False) try: diff --git a/Lib/test/test_locale.py b/Lib/test/test_locale.py index e979753..81fe57d 100644 --- a/Lib/test/test_locale.py +++ b/Lib/test/test_locale.py @@ -524,5 +524,59 @@ class TestMiscellaneous(unittest.TestCase): locale.setlocale(locale.LC_ALL, (b'not', b'valid')) +class BaseDelocalizeTest(BaseLocalizedTest): + + def _test_delocalize(self, value, out): + self.assertEqual(locale.delocalize(value), out) + + def _test_atof(self, value, out): + self.assertEqual(locale.atof(value), out) + + def _test_atoi(self, value, out): + self.assertEqual(locale.atoi(value), out) + + +class TestEnUSDelocalize(EnUSCookedTest, BaseDelocalizeTest): + + def test_delocalize(self): + self._test_delocalize('50000.00', '50000.00') + self._test_delocalize('50,000.00', '50000.00') + + def test_atof(self): + self._test_atof('50000.00', 50000.) + self._test_atof('50,000.00', 50000.) + + def test_atoi(self): + self._test_atoi('50000', 50000) + self._test_atoi('50,000', 50000) + + +class TestCDelocalizeTest(CCookedTest, BaseDelocalizeTest): + + def test_delocalize(self): + self._test_delocalize('50000.00', '50000.00') + + def test_atof(self): + self._test_atof('50000.00', 50000.) + + def test_atoi(self): + self._test_atoi('50000', 50000) + + +class TestfrFRDelocalizeTest(FrFRCookedTest, BaseDelocalizeTest): + + def test_delocalize(self): + self._test_delocalize('50000,00', '50000.00') + self._test_delocalize('50 000,00', '50000.00') + + def test_atof(self): + self._test_atof('50000,00', 50000.) + self._test_atof('50 000,00', 50000.) + + def test_atoi(self): + self._test_atoi('50000', 50000) + self._test_atoi('50 000', 50000) + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 54be217..9674322 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -1,4 +1,4 @@ -# Copyright 2001-2013 by Vinay Sajip. All Rights Reserved. +# Copyright 2001-2014 by Vinay Sajip. All Rights Reserved. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose and without fee is hereby granted, @@ -16,7 +16,7 @@ """Test harness for the logging module. Run all tests. -Copyright (C) 2001-2013 Vinay Sajip. All Rights Reserved. +Copyright (C) 2001-2014 Vinay Sajip. All Rights Reserved. """ import logging @@ -34,14 +34,12 @@ import os import queue import random import re -import select import socket import struct import sys import tempfile from test.script_helper import assert_python_ok -from test.support import (captured_stdout, run_with_locale, run_unittest, - patch, requires_zlib, TestHandler, Matcher, HOST) +from test import support import textwrap import time import unittest @@ -51,16 +49,12 @@ try: import threading # The following imports are needed only for tests which # require threading - import asynchat import asyncore - import errno from http.server import HTTPServer, BaseHTTPRequestHandler import smtpd from urllib.parse import urlparse, parse_qs from socketserver import (ThreadingUDPServer, DatagramRequestHandler, - ThreadingTCPServer, StreamRequestHandler, - ThreadingUnixStreamServer, - ThreadingUnixDatagramServer) + ThreadingTCPServer, StreamRequestHandler) except ImportError: threading = None try: @@ -645,22 +639,23 @@ class StreamHandlerTest(BaseTest): h = TestStreamHandler(BadStream()) r = logging.makeLogRecord({}) old_raise = logging.raiseExceptions - old_stderr = sys.stderr + try: h.handle(r) self.assertIs(h.error_record, r) + h = logging.StreamHandler(BadStream()) - sys.stderr = sio = io.StringIO() - h.handle(r) - self.assertIn('\nRuntimeError: deliberate mistake\n', - sio.getvalue()) + with support.captured_stderr() as stderr: + h.handle(r) + msg = '\nRuntimeError: deliberate mistake\n' + self.assertIn(msg, stderr.getvalue()) + logging.raiseExceptions = False - sys.stderr = sio = io.StringIO() - h.handle(r) - self.assertEqual('', sio.getvalue()) + with support.captured_stderr() as stderr: + h.handle(r) + self.assertEqual('', stderr.getvalue()) finally: logging.raiseExceptions = old_raise - sys.stderr = old_stderr # -- The following section could be moved into a server_helper.py module # -- if it proves to be of wider utility than just test_logging @@ -688,7 +683,8 @@ if threading: """ def __init__(self, addr, handler, poll_interval, sockmap): - smtpd.SMTPServer.__init__(self, addr, None, map=sockmap) + smtpd.SMTPServer.__init__(self, addr, None, map=sockmap, + decode_data=True) self.port = self.socket.getsockname()[1] self._handler = handler self._thread = None @@ -930,10 +926,10 @@ class SMTPHandlerTest(BaseTest): TIMEOUT = 8.0 def test_basic(self): sockmap = {} - server = TestSMTPServer((HOST, 0), self.process_message, 0.001, + server = TestSMTPServer((support.HOST, 0), self.process_message, 0.001, sockmap) server.start() - addr = (HOST, server.port) + addr = (support.HOST, server.port) h = logging.handlers.SMTPHandler(addr, 'me', 'you', 'Log', timeout=self.TIMEOUT) self.assertEqual(h.toaddrs, ['you']) @@ -1249,7 +1245,7 @@ class ConfigFileTest(BaseTest): def test_config0_ok(self): # A simple config file which overrides the default settings. - with captured_stdout() as output: + with support.captured_stdout() as output: self.apply_config(self.config0) logger = logging.getLogger() # Won't output anything @@ -1264,7 +1260,7 @@ class ConfigFileTest(BaseTest): def test_config0_using_cp_ok(self): # A simple config file which overrides the default settings. - with captured_stdout() as output: + with support.captured_stdout() as output: file = io.StringIO(textwrap.dedent(self.config0)) cp = configparser.ConfigParser() cp.read_file(file) @@ -1282,7 +1278,7 @@ class ConfigFileTest(BaseTest): def test_config1_ok(self, config=config1): # A config file defining a sub-parser as well. - with captured_stdout() as output: + with support.captured_stdout() as output: self.apply_config(config) logger = logging.getLogger("compiler.parser") # Both will output a message @@ -1305,7 +1301,7 @@ class ConfigFileTest(BaseTest): def test_config4_ok(self): # A config file specifying a custom formatter class. - with captured_stdout() as output: + with support.captured_stdout() as output: self.apply_config(self.config4) logger = logging.getLogger() try: @@ -1325,7 +1321,7 @@ class ConfigFileTest(BaseTest): self.test_config1_ok(config=self.config6) def test_config7_ok(self): - with captured_stdout() as output: + with support.captured_stdout() as output: self.apply_config(self.config1a) logger = logging.getLogger("compiler.parser") # See issue #11424. compiler-hyphenated sorts @@ -1345,7 +1341,7 @@ class ConfigFileTest(BaseTest): ], stream=output) # Original logger output is empty. self.assert_log_lines([]) - with captured_stdout() as output: + with support.captured_stdout() as output: self.apply_config(self.config7) logger = logging.getLogger("compiler.parser") self.assertFalse(logger.disabled) @@ -2492,7 +2488,7 @@ class ConfigDictTest(BaseTest): def test_config0_ok(self): # A simple config which overrides the default settings. - with captured_stdout() as output: + with support.captured_stdout() as output: self.apply_config(self.config0) logger = logging.getLogger() # Won't output anything @@ -2507,7 +2503,7 @@ class ConfigDictTest(BaseTest): def test_config1_ok(self, config=config1): # A config defining a sub-parser as well. - with captured_stdout() as output: + with support.captured_stdout() as output: self.apply_config(config) logger = logging.getLogger("compiler.parser") # Both will output a message @@ -2538,7 +2534,7 @@ class ConfigDictTest(BaseTest): def test_config4_ok(self): # A config specifying a custom formatter class. - with captured_stdout() as output: + with support.captured_stdout() as output: self.apply_config(self.config4) #logger = logging.getLogger() try: @@ -2553,7 +2549,7 @@ class ConfigDictTest(BaseTest): def test_config4a_ok(self): # A config specifying a custom formatter class. - with captured_stdout() as output: + with support.captured_stdout() as output: self.apply_config(self.config4a) #logger = logging.getLogger() try: @@ -2573,7 +2569,7 @@ class ConfigDictTest(BaseTest): self.assertRaises(Exception, self.apply_config, self.config6) def test_config7_ok(self): - with captured_stdout() as output: + with support.captured_stdout() as output: self.apply_config(self.config1) logger = logging.getLogger("compiler.parser") # Both will output a message @@ -2585,7 +2581,7 @@ class ConfigDictTest(BaseTest): ], stream=output) # Original logger output is empty. self.assert_log_lines([]) - with captured_stdout() as output: + with support.captured_stdout() as output: self.apply_config(self.config7) logger = logging.getLogger("compiler.parser") self.assertTrue(logger.disabled) @@ -2602,7 +2598,7 @@ class ConfigDictTest(BaseTest): #Same as test_config_7_ok but don't disable old loggers. def test_config_8_ok(self): - with captured_stdout() as output: + with support.captured_stdout() as output: self.apply_config(self.config1) logger = logging.getLogger("compiler.parser") # All will output a message @@ -2614,7 +2610,7 @@ class ConfigDictTest(BaseTest): ], stream=output) # Original logger output is empty. self.assert_log_lines([]) - with captured_stdout() as output: + with support.captured_stdout() as output: self.apply_config(self.config8) logger = logging.getLogger("compiler.parser") self.assertFalse(logger.disabled) @@ -2635,7 +2631,7 @@ class ConfigDictTest(BaseTest): self.assert_log_lines([]) def test_config_8a_ok(self): - with captured_stdout() as output: + with support.captured_stdout() as output: self.apply_config(self.config1a) logger = logging.getLogger("compiler.parser") # See issue #11424. compiler-hyphenated sorts @@ -2655,7 +2651,7 @@ class ConfigDictTest(BaseTest): ], stream=output) # Original logger output is empty. self.assert_log_lines([]) - with captured_stdout() as output: + with support.captured_stdout() as output: self.apply_config(self.config8a) logger = logging.getLogger("compiler.parser") self.assertFalse(logger.disabled) @@ -2678,7 +2674,7 @@ class ConfigDictTest(BaseTest): self.assert_log_lines([]) def test_config_9_ok(self): - with captured_stdout() as output: + with support.captured_stdout() as output: self.apply_config(self.config9) logger = logging.getLogger("compiler.parser") #Nothing will be output since both handler and logger are set to WARNING @@ -2696,7 +2692,7 @@ class ConfigDictTest(BaseTest): ], stream=output) def test_config_10_ok(self): - with captured_stdout() as output: + with support.captured_stdout() as output: self.apply_config(self.config10) logger = logging.getLogger("compiler.parser") logger.warning(self.next_message()) @@ -2724,7 +2720,7 @@ class ConfigDictTest(BaseTest): self.assertRaises(Exception, self.apply_config, self.config13) def test_config14_ok(self): - with captured_stdout() as output: + with support.captured_stdout() as output: self.apply_config(self.config14) h = logging._handlers['hand1'] self.assertEqual(h.foo, 'bar') @@ -2763,7 +2759,7 @@ class ConfigDictTest(BaseTest): @unittest.skipUnless(threading, 'Threading required for this test.') def test_listen_config_10_ok(self): - with captured_stdout() as output: + with support.captured_stdout() as output: self.setup_via_listener(json.dumps(self.config10)) logger = logging.getLogger("compiler.parser") logger.warning(self.next_message()) @@ -2783,7 +2779,7 @@ class ConfigDictTest(BaseTest): @unittest.skipUnless(threading, 'Threading required for this test.') def test_listen_config_1_ok(self): - with captured_stdout() as output: + with support.captured_stdout() as output: self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1)) logger = logging.getLogger("compiler.parser") # Both will output a message @@ -2810,7 +2806,7 @@ class ConfigDictTest(BaseTest): # First, specify a verification function that will fail. # We expect to see no output, since our configuration # never took effect. - with captured_stdout() as output: + with support.captured_stdout() as output: self.setup_via_listener(to_send, verify_fail) # Both will output a message logger.info(self.next_message()) @@ -2825,7 +2821,7 @@ class ConfigDictTest(BaseTest): # Now, perform no verification. Our configuration # should take effect. - with captured_stdout() as output: + with support.captured_stdout() as output: self.setup_via_listener(to_send) # no verify callable specified logger = logging.getLogger("compiler.parser") # Both will output a message @@ -2843,7 +2839,7 @@ class ConfigDictTest(BaseTest): # Now, perform verification which transforms the bytes. - with captured_stdout() as output: + with support.captured_stdout() as output: self.setup_via_listener(to_send[::-1], verify_reverse) logger = logging.getLogger("compiler.parser") # Both will output a message @@ -2998,7 +2994,7 @@ class QueueHandlerTest(BaseTest): @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'), 'logging.handlers.QueueListener required for this test') def test_queue_listener(self): - handler = TestHandler(Matcher()) + handler = support.TestHandler(support.Matcher()) listener = logging.handlers.QueueListener(self.queue, handler) listener.start() try: @@ -3166,32 +3162,35 @@ class LastResortTest(BaseTest): # Test the last resort handler root = self.root_logger root.removeHandler(self.root_hdlr) - old_stderr = sys.stderr old_lastresort = logging.lastResort old_raise_exceptions = logging.raiseExceptions + try: - sys.stderr = sio = io.StringIO() - root.debug('This should not appear') - self.assertEqual(sio.getvalue(), '') - root.warning('This is your final chance!') - self.assertEqual(sio.getvalue(), 'This is your final chance!\n') - #No handlers and no last resort, so 'No handlers' message + with support.captured_stderr() as stderr: + root.debug('This should not appear') + self.assertEqual(stderr.getvalue(), '') + root.warning('Final chance!') + self.assertEqual(stderr.getvalue(), 'Final chance!\n') + + # No handlers and no last resort, so 'No handlers' message logging.lastResort = None - sys.stderr = sio = io.StringIO() - root.warning('This is your final chance!') - self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n') + with support.captured_stderr() as stderr: + root.warning('Final chance!') + msg = 'No handlers could be found for logger "root"\n' + self.assertEqual(stderr.getvalue(), msg) + # 'No handlers' message only printed once - sys.stderr = sio = io.StringIO() - root.warning('This is your final chance!') - self.assertEqual(sio.getvalue(), '') + with support.captured_stderr() as stderr: + root.warning('Final chance!') + self.assertEqual(stderr.getvalue(), '') + + # If raiseExceptions is False, no message is printed root.manager.emittedNoHandlerWarning = False - #If raiseExceptions is False, no message is printed logging.raiseExceptions = False - sys.stderr = sio = io.StringIO() - root.warning('This is your final chance!') - self.assertEqual(sio.getvalue(), '') + with support.captured_stderr() as stderr: + root.warning('Final chance!') + self.assertEqual(stderr.getvalue(), '') finally: - sys.stderr = old_stderr root.addHandler(self.root_hdlr) logging.lastResort = old_lastresort logging.raiseExceptions = old_raise_exceptions @@ -3322,8 +3321,8 @@ class ModuleLevelMiscTest(BaseTest): def _test_log(self, method, level=None): called = [] - patch(self, logging, 'basicConfig', - lambda *a, **kw: called.append((a, kw))) + support.patch(self, logging, 'basicConfig', + lambda *a, **kw: called.append((a, kw))) recording = RecordingHandler() logging.root.addHandler(recording) @@ -3494,7 +3493,7 @@ class BasicConfigTest(unittest.TestCase): self.assertEqual(logging.root.level, self.original_logging_level) def test_strformatstyle(self): - with captured_stdout() as output: + with support.captured_stdout() as output: logging.basicConfig(stream=sys.stdout, style="{") logging.error("Log an error") sys.stdout.seek(0) @@ -3502,7 +3501,7 @@ class BasicConfigTest(unittest.TestCase): "ERROR:root:Log an error") def test_stringtemplatestyle(self): - with captured_stdout() as output: + with support.captured_stdout() as output: logging.basicConfig(stream=sys.stdout, style="$") logging.error("Log an error") sys.stdout.seek(0) @@ -3619,7 +3618,7 @@ class BasicConfigTest(unittest.TestCase): self.addCleanup(logging.root.setLevel, old_level) called.append((a, kw)) - patch(self, logging, 'basicConfig', my_basic_config) + support.patch(self, logging, 'basicConfig', my_basic_config) log_method = getattr(logging, method) if level is not None: @@ -3685,6 +3684,19 @@ class LoggerAdapterTest(unittest.TestCase): self.assertEqual(record.exc_info, (exc.__class__, exc, exc.__traceback__)) + def test_exception_excinfo(self): + try: + 1 / 0 + except ZeroDivisionError as e: + exc = e + + self.adapter.exception('exc_info test', exc_info=exc) + + self.assertEqual(len(self.recording.records), 1) + record = self.recording.records[0] + self.assertEqual(record.exc_info, + (exc.__class__, exc, exc.__traceback__)) + def test_critical(self): msg = 'critical test! %r' self.adapter.critical(msg, self.recording) @@ -3759,8 +3771,8 @@ class LoggerTest(BaseTest): def test_find_caller_with_stack_info(self): called = [] - patch(self, logging.traceback, 'print_stack', - lambda f, file: called.append(file.getvalue())) + support.patch(self, logging.traceback, 'print_stack', + lambda f, file: called.append(file.getvalue())) self.logger.findCaller(stack_info=True) @@ -3897,7 +3909,7 @@ class RotatingFileHandlerTest(BaseFileTest): self.assertFalse(os.path.exists(namer(self.fn + ".3"))) rh.close() - @requires_zlib + @support.requires_zlib def test_rotator(self): def namer(name): return name + ".gz" @@ -4130,22 +4142,20 @@ class NTEventLogHandlerTest(BaseTest): # Set the locale to the platform-dependent default. I have no idea # why the test does this, but in any case we save the current locale # first and restore it at the end. -@run_with_locale('LC_ALL', '') +@support.run_with_locale('LC_ALL', '') def test_main(): - run_unittest(BuiltinLevelsTest, BasicFilterTest, - CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest, - ConfigFileTest, SocketHandlerTest, DatagramHandlerTest, - MemoryTest, EncodingTest, WarningsTest, ConfigDictTest, - ManagerTest, FormatterTest, BufferingFormatterTest, - StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest, - QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest, - BasicConfigTest, LoggerAdapterTest, LoggerTest, - SMTPHandlerTest, FileHandlerTest, RotatingFileHandlerTest, - LastResortTest, LogRecordTest, ExceptionTest, - SysLogHandlerTest, HTTPHandlerTest, NTEventLogHandlerTest, - TimedRotatingFileHandlerTest, UnixSocketHandlerTest, - UnixDatagramHandlerTest, UnixSysLogHandlerTest - ) + support.run_unittest( + BuiltinLevelsTest, BasicFilterTest, CustomLevelsAndFiltersTest, + HandlerTest, MemoryHandlerTest, ConfigFileTest, SocketHandlerTest, + DatagramHandlerTest, MemoryTest, EncodingTest, WarningsTest, + ConfigDictTest, ManagerTest, FormatterTest, BufferingFormatterTest, + StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest, + QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest, BasicConfigTest, + LoggerAdapterTest, LoggerTest, SMTPHandlerTest, FileHandlerTest, + RotatingFileHandlerTest, LastResortTest, LogRecordTest, + ExceptionTest, SysLogHandlerTest, HTTPHandlerTest, + NTEventLogHandlerTest, TimedRotatingFileHandlerTest, + UnixSocketHandlerTest, UnixDatagramHandlerTest, UnixSysLogHandlerTest) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_lzma.py b/Lib/test/test_lzma.py index 07fadbd..cded28c 100644 --- a/Lib/test/test_lzma.py +++ b/Lib/test/test_lzma.py @@ -135,6 +135,97 @@ class CompressorDecompressorTestCase(unittest.TestCase): self.assertTrue(lzd.eof) self.assertEqual(lzd.unused_data, b"") + def test_decompressor_chunks_maxsize(self): + lzd = LZMADecompressor() + max_length = 100 + out = [] + + # Feed first half the input + len_ = len(COMPRESSED_XZ) // 2 + out.append(lzd.decompress(COMPRESSED_XZ[:len_], + max_length=max_length)) + self.assertFalse(lzd.needs_input) + self.assertEqual(len(out[-1]), max_length) + + # Retrieve more data without providing more input + out.append(lzd.decompress(b'', max_length=max_length)) + self.assertFalse(lzd.needs_input) + self.assertEqual(len(out[-1]), max_length) + + # Retrieve more data while providing more input + out.append(lzd.decompress(COMPRESSED_XZ[len_:], + max_length=max_length)) + self.assertLessEqual(len(out[-1]), max_length) + + # Retrieve remaining uncompressed data + while not lzd.eof: + out.append(lzd.decompress(b'', max_length=max_length)) + self.assertLessEqual(len(out[-1]), max_length) + + out = b"".join(out) + self.assertEqual(out, INPUT) + self.assertEqual(lzd.check, lzma.CHECK_CRC64) + self.assertEqual(lzd.unused_data, b"") + + def test_decompressor_inputbuf_1(self): + # Test reusing input buffer after moving existing + # contents to beginning + lzd = LZMADecompressor() + out = [] + + # Create input buffer and fill it + self.assertEqual(lzd.decompress(COMPRESSED_XZ[:100], + max_length=0), b'') + + # Retrieve some results, freeing capacity at beginning + # of input buffer + out.append(lzd.decompress(b'', 2)) + + # Add more data that fits into input buffer after + # moving existing data to beginning + out.append(lzd.decompress(COMPRESSED_XZ[100:105], 15)) + + # Decompress rest of data + out.append(lzd.decompress(COMPRESSED_XZ[105:])) + self.assertEqual(b''.join(out), INPUT) + + def test_decompressor_inputbuf_2(self): + # Test reusing input buffer by appending data at the + # end right away + lzd = LZMADecompressor() + out = [] + + # Create input buffer and empty it + self.assertEqual(lzd.decompress(COMPRESSED_XZ[:200], + max_length=0), b'') + out.append(lzd.decompress(b'')) + + # Fill buffer with new data + out.append(lzd.decompress(COMPRESSED_XZ[200:280], 2)) + + # Append some more data, not enough to require resize + out.append(lzd.decompress(COMPRESSED_XZ[280:300], 2)) + + # Decompress rest of data + out.append(lzd.decompress(COMPRESSED_XZ[300:])) + self.assertEqual(b''.join(out), INPUT) + + def test_decompressor_inputbuf_3(self): + # Test reusing input buffer after extending it + + lzd = LZMADecompressor() + out = [] + + # Create almost full input buffer + out.append(lzd.decompress(COMPRESSED_XZ[:200], 5)) + + # Add even more data to it, requiring resize + out.append(lzd.decompress(COMPRESSED_XZ[200:300], 5)) + + # Decompress rest of data + out.append(lzd.decompress(COMPRESSED_XZ[300:])) + self.assertEqual(b''.join(out), INPUT) + def test_decompressor_unused_data(self): lzd = LZMADecompressor() extra = b"fooblibar" diff --git a/Lib/test/test_macpath.py b/Lib/test/test_macpath.py index 22f8491..80bec7a 100644 --- a/Lib/test/test_macpath.py +++ b/Lib/test/test_macpath.py @@ -142,6 +142,8 @@ class MacPathTestCase(unittest.TestCase): class MacCommonTest(test_genericpath.CommonTest, unittest.TestCase): pathmodule = macpath + test_relpath_errors = None + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_marshal.py b/Lib/test/test_marshal.py index 068c471..d2d6b48 100644 --- a/Lib/test/test_marshal.py +++ b/Lib/test/test_marshal.py @@ -188,7 +188,7 @@ class BugsTestCase(unittest.TestCase): head = last = [] # The max stack depth should match the value in Python/marshal.c. if os.name == 'nt' and hasattr(sys, 'gettotalrefcount'): - MAX_MARSHAL_STACK_DEPTH = 1500 + MAX_MARSHAL_STACK_DEPTH = 1000 else: MAX_MARSHAL_STACK_DEPTH = 2000 for i in range(MAX_MARSHAL_STACK_DEPTH - 2): diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 48f84ba..023dea9 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -422,9 +422,17 @@ class MathTests(unittest.TestCase): self.assertEqual(math.factorial(i), py_factorial(i)) self.assertRaises(ValueError, math.factorial, -1) self.assertRaises(ValueError, math.factorial, -1.0) + self.assertRaises(ValueError, math.factorial, -10**100) + self.assertRaises(ValueError, math.factorial, -1e100) self.assertRaises(ValueError, math.factorial, math.pi) - self.assertRaises(OverflowError, math.factorial, sys.maxsize+1) - self.assertRaises(OverflowError, math.factorial, 10e100) + + # Other implementations may place different upper bounds. + @support.cpython_only + def testFactorialHugeInputs(self): + # Currently raises ValueError for inputs that are too large + # to fit into a C long. + self.assertRaises(OverflowError, math.factorial, 10**100) + self.assertRaises(OverflowError, math.factorial, 1e100) def testFloor(self): self.assertRaises(TypeError, math.floor) @@ -975,6 +983,17 @@ class MathTests(unittest.TestCase): self.assertFalse(math.isinf(0.)) self.assertFalse(math.isinf(1.)) + @requires_IEEE_754 + def test_nan_constant(self): + self.assertTrue(math.isnan(math.nan)) + + @requires_IEEE_754 + def test_inf_constant(self): + self.assertTrue(math.isinf(math.inf)) + self.assertGreater(math.inf, 0.0) + self.assertEqual(math.inf, float("inf")) + self.assertEqual(-math.inf, float("-inf")) + # RED_FLAG 16-Oct-2000 Tim # While 2.0 is more consistent about exceptions than previous releases, it # still fails this part of the test on some platforms. For now, we only diff --git a/Lib/test/test_memoryio.py b/Lib/test/test_memoryio.py index 7cae8b2..ab67cf3 100644 --- a/Lib/test/test_memoryio.py +++ b/Lib/test/test_memoryio.py @@ -9,6 +9,7 @@ from test import support import io import _pyio as pyio import pickle +import sys class MemorySeekTestMixin: @@ -712,12 +713,57 @@ class CBytesIOTest(PyBytesIOTest): @support.cpython_only def test_sizeof(self): - basesize = support.calcobjsize('P2nN2Pn') + basesize = support.calcobjsize('P2nN2PnP') check = self.check_sizeof self.assertEqual(object.__sizeof__(io.BytesIO()), basesize) check(io.BytesIO(), basesize ) - check(io.BytesIO(b'a'), basesize + 1 + 1 ) - check(io.BytesIO(b'a' * 1000), basesize + 1000 + 1 ) + check(io.BytesIO(b'a'), basesize + 1 ) + check(io.BytesIO(b'a' * 1000), basesize + 1000) + + # Various tests of copy-on-write behaviour for BytesIO. + + def _test_cow_mutation(self, mutation): + # Common code for all BytesIO copy-on-write mutation tests. + imm = b' ' * 1024 + old_rc = sys.getrefcount(imm) + memio = self.ioclass(imm) + self.assertEqual(sys.getrefcount(imm), old_rc + 1) + mutation(memio) + self.assertEqual(sys.getrefcount(imm), old_rc) + + @support.cpython_only + def test_cow_truncate(self): + # Ensure truncate causes a copy. + def mutation(memio): + memio.truncate(1) + self._test_cow_mutation(mutation) + + @support.cpython_only + def test_cow_write(self): + # Ensure write that would not cause a resize still results in a copy. + def mutation(memio): + memio.seek(0) + memio.write(b'foo') + self._test_cow_mutation(mutation) + + @support.cpython_only + def test_cow_setstate(self): + # __setstate__ should cause buffer to be released. + memio = self.ioclass(b'foooooo') + state = memio.__getstate__() + def mutation(memio): + memio.__setstate__(state) + self._test_cow_mutation(mutation) + + @support.cpython_only + def test_cow_mutable(self): + # BytesIO should accept only Bytes for copy-on-write sharing, since + # arbitrary buffer-exporting objects like bytearray() aren't guaranteed + # to be immutable. + ba = bytearray(1024) + old_rc = sys.getrefcount(ba) + memio = self.ioclass(ba) + self.assertEqual(sys.getrefcount(ba), old_rc) class CStringIOTest(PyStringIOTest): ioclass = io.StringIO diff --git a/Lib/test/test_module.py b/Lib/test/test_module.py index 1230293..9da3536 100644 --- a/Lib/test/test_module.py +++ b/Lib/test/test_module.py @@ -30,6 +30,22 @@ class ModuleTests(unittest.TestCase): pass self.assertEqual(foo.__doc__, ModuleType.__doc__) + def test_unintialized_missing_getattr(self): + # Issue 8297 + # test the text in the AttributeError of an uninitialized module + foo = ModuleType.__new__(ModuleType) + self.assertRaisesRegex( + AttributeError, "module has no attribute 'not_here'", + getattr, foo, "not_here") + + def test_missing_getattr(self): + # Issue 8297 + # test the text in the AttributeError + foo = ModuleType("foo") + self.assertRaisesRegex( + AttributeError, "module 'foo' has no attribute 'not_here'", + getattr, foo, "not_here") + def test_no_docstring(self): # Regularly initialized module, no docstring foo = ModuleType("foo") @@ -211,6 +227,14 @@ a = A(destroyed)""" b"len = len", b"shutil.rmtree = rmtree"}) + def test_descriptor_errors_propogate(self): + class Descr: + def __get__(self, o, t): + raise RuntimeError + class M(ModuleType): + melon = Descr() + self.assertRaises(RuntimeError, getattr, M("mymod"), "melon") + # frozen and namespace module reprs are tested in importlib. diff --git a/Lib/test/test_operator.py b/Lib/test/test_operator.py index ab58a98..1bd0391 100644 --- a/Lib/test/test_operator.py +++ b/Lib/test/test_operator.py @@ -203,6 +203,15 @@ class OperatorTestCase: self.assertRaises(TypeError, operator.mul, None, None) self.assertTrue(operator.mul(5, 2) == 10) + def test_matmul(self): + operator = self.module + self.assertRaises(TypeError, operator.matmul) + self.assertRaises(TypeError, operator.matmul, 42, 42) + class M: + def __matmul__(self, other): + return other - 1 + self.assertEqual(M() @ 42, 41) + def test_neg(self): operator = self.module self.assertRaises(TypeError, operator.neg) @@ -416,6 +425,7 @@ class OperatorTestCase: def __ilshift__ (self, other): return "ilshift" def __imod__ (self, other): return "imod" def __imul__ (self, other): return "imul" + def __imatmul__ (self, other): return "imatmul" def __ior__ (self, other): return "ior" def __ipow__ (self, other): return "ipow" def __irshift__ (self, other): return "irshift" @@ -430,6 +440,7 @@ class OperatorTestCase: self.assertEqual(operator.ilshift (c, 5), "ilshift") self.assertEqual(operator.imod (c, 5), "imod") self.assertEqual(operator.imul (c, 5), "imul") + self.assertEqual(operator.imatmul (c, 5), "imatmul") self.assertEqual(operator.ior (c, 5), "ior") self.assertEqual(operator.ipow (c, 5), "ipow") self.assertEqual(operator.irshift (c, 5), "irshift") diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 152ba93..e28a3db 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -4,6 +4,7 @@ import os import errno +import getpass import unittest import warnings import sys @@ -40,9 +41,35 @@ try: import fcntl except ImportError: fcntl = None +try: + import _winapi +except ImportError: + _winapi = None +try: + import grp + groups = [g.gr_gid for g in grp.getgrall() if getpass.getuser() in g.gr_mem] + if hasattr(os, 'getgid'): + process_gid = os.getgid() + if process_gid not in groups: + groups.append(process_gid) +except ImportError: + groups = [] +try: + import pwd + all_users = [u.pw_uid for u in pwd.getpwall()] +except ImportError: + all_users = [] +try: + from _testcapi import INT_MAX, PY_SSIZE_T_MAX +except ImportError: + INT_MAX = PY_SSIZE_T_MAX = sys.maxsize from test.script_helper import assert_python_ok +root_in_posix = False +if hasattr(os, 'geteuid'): + root_in_posix = (os.geteuid() == 0) + with warnings.catch_warnings(): warnings.simplefilter("ignore", DeprecationWarning) os.stat_float_times(True) @@ -116,6 +143,26 @@ class FileTests(unittest.TestCase): self.assertEqual(type(s), bytes) self.assertEqual(s, b"spam") + @support.cpython_only + # Skip the test on 32-bit platforms: the number of bytes must fit in a + # Py_ssize_t type + @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, + "needs INT_MAX < PY_SSIZE_T_MAX") + @support.bigmemtest(size=INT_MAX + 10, memuse=1, dry_run=False) + def test_large_read(self, size): + with open(support.TESTFN, "wb") as fp: + fp.write(b'test') + self.addCleanup(support.unlink, support.TESTFN) + + # Issue #21932: Make sure that os.read() does not raise an + # OverflowError for size larger than INT_MAX + with open(support.TESTFN, "rb") as fp: + data = os.read(fp.fileno(), size) + + # The test does not try to read more than 2 GB at once because the + # operating system is free to return less bytes than requested. + self.assertEqual(data, b'test') + def test_write(self): # os.write() accepts bytes- and buffer-like objects but not strings fd = os.open(support.TESTFN, os.O_CREAT | os.O_WRONLY) @@ -533,6 +580,28 @@ class StatAttributeTests(unittest.TestCase): os.stat(r) self.assertEqual(ctx.exception.errno, errno.EBADF) + def check_file_attributes(self, result): + self.assertTrue(hasattr(result, 'st_file_attributes')) + self.assertTrue(isinstance(result.st_file_attributes, int)) + self.assertTrue(0 <= result.st_file_attributes <= 0xFFFFFFFF) + + @unittest.skipUnless(sys.platform == "win32", + "st_file_attributes is Win32 specific") + def test_file_attributes(self): + # test file st_file_attributes (FILE_ATTRIBUTE_DIRECTORY not set) + result = os.stat(self.fname) + self.check_file_attributes(result) + self.assertEqual( + result.st_file_attributes & stat.FILE_ATTRIBUTE_DIRECTORY, + 0) + + # test directory st_file_attributes (FILE_ATTRIBUTE_DIRECTORY set) + result = os.stat(support.TESTFN) + self.check_file_attributes(result) + self.assertEqual( + result.st_file_attributes & stat.FILE_ATTRIBUTE_DIRECTORY, + stat.FILE_ATTRIBUTE_DIRECTORY) + from test import mapping_tests class EnvironTests(mapping_tests.BasicTestMappingProtocol): @@ -912,17 +981,6 @@ class MakedirTests(unittest.TestCase): os.makedirs(path, mode=mode, exist_ok=True) os.umask(old_mask) - @unittest.skipUnless(hasattr(os, 'chown'), 'test needs os.chown') - def test_chown_uid_gid_arguments_must_be_index(self): - stat = os.stat(support.TESTFN) - uid = stat.st_uid - gid = stat.st_gid - for value in (-1.0, -1j, decimal.Decimal(-1), fractions.Fraction(-2, 2)): - self.assertRaises(TypeError, os.chown, support.TESTFN, value, gid) - self.assertRaises(TypeError, os.chown, support.TESTFN, uid, value) - self.assertIsNone(os.chown(support.TESTFN, uid, gid)) - self.assertIsNone(os.chown(support.TESTFN, -1, -1)) - def test_exist_ok_s_isgid_directory(self): path = os.path.join(support.TESTFN, 'dir1') S_ISGID = stat.S_ISGID @@ -973,6 +1031,58 @@ class MakedirTests(unittest.TestCase): os.removedirs(path) +@unittest.skipUnless(hasattr(os, 'chown'), "Test needs chown") +class ChownFileTests(unittest.TestCase): + + def setUpClass(): + os.mkdir(support.TESTFN) + + def test_chown_uid_gid_arguments_must_be_index(self): + stat = os.stat(support.TESTFN) + uid = stat.st_uid + gid = stat.st_gid + for value in (-1.0, -1j, decimal.Decimal(-1), fractions.Fraction(-2, 2)): + self.assertRaises(TypeError, os.chown, support.TESTFN, value, gid) + self.assertRaises(TypeError, os.chown, support.TESTFN, uid, value) + self.assertIsNone(os.chown(support.TESTFN, uid, gid)) + self.assertIsNone(os.chown(support.TESTFN, -1, -1)) + + @unittest.skipUnless(len(groups) > 1, "test needs more than one group") + def test_chown(self): + gid_1, gid_2 = groups[:2] + uid = os.stat(support.TESTFN).st_uid + os.chown(support.TESTFN, uid, gid_1) + gid = os.stat(support.TESTFN).st_gid + self.assertEqual(gid, gid_1) + os.chown(support.TESTFN, uid, gid_2) + gid = os.stat(support.TESTFN).st_gid + self.assertEqual(gid, gid_2) + + @unittest.skipUnless(root_in_posix and len(all_users) > 1, + "test needs root privilege and more than one user") + def test_chown_with_root(self): + uid_1, uid_2 = all_users[:2] + gid = os.stat(support.TESTFN).st_gid + os.chown(support.TESTFN, uid_1, gid) + uid = os.stat(support.TESTFN).st_uid + self.assertEqual(uid, uid_1) + os.chown(support.TESTFN, uid_2, gid) + uid = os.stat(support.TESTFN).st_uid + self.assertEqual(uid, uid_2) + + @unittest.skipUnless(not root_in_posix and len(all_users) > 1, + "test needs non-root account and more than one user") + def test_chown_without_permission(self): + uid_1, uid_2 = all_users[:2] + gid = os.stat(support.TESTFN).st_gid + with self.assertRaisesRegex(PermissionError, "Operation not permitted"): + os.chown(support.TESTFN, uid_1, gid) + os.chown(support.TESTFN, uid_2, gid) + + def tearDownClass(): + os.rmdir(support.TESTFN) + + class RemoveDirsTests(unittest.TestCase): def setUp(self): os.makedirs(support.TESTFN) @@ -1339,6 +1449,16 @@ class TestInvalidFD(unittest.TestCase): def test_writev(self): self.check(os.writev, [b'abc']) + def test_inheritable(self): + self.check(os.get_inheritable) + self.check(os.set_inheritable, True) + + @unittest.skipUnless(hasattr(os, 'get_blocking'), + 'needs os.get_blocking() and os.set_blocking()') + def test_blocking(self): + self.check(os.get_blocking) + self.check(os.set_blocking, True) + class LinkTests(unittest.TestCase): def setUp(self): @@ -1786,6 +1906,37 @@ class Win32SymlinkTests(unittest.TestCase): shutil.rmtree(level1) +@unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") +class Win32JunctionTests(unittest.TestCase): + junction = 'junctiontest' + junction_target = os.path.dirname(os.path.abspath(__file__)) + + def setUp(self): + assert os.path.exists(self.junction_target) + assert not os.path.exists(self.junction) + + def tearDown(self): + if os.path.exists(self.junction): + # os.rmdir delegates to Windows' RemoveDirectoryW, + # which removes junction points safely. + os.rmdir(self.junction) + + def test_create_junction(self): + _winapi.CreateJunction(self.junction_target, self.junction) + self.assertTrue(os.path.exists(self.junction)) + self.assertTrue(os.path.isdir(self.junction)) + + # Junctions are not recognized as links. + self.assertFalse(os.path.islink(self.junction)) + + def test_unlink_removes_junction(self): + _winapi.CreateJunction(self.junction_target, self.junction) + self.assertTrue(os.path.exists(self.junction)) + + os.unlink(self.junction) + self.assertFalse(os.path.exists(self.junction)) + + @support.skip_unless_symlink class NonLocalSymlinkTests(unittest.TestCase): @@ -1992,11 +2143,13 @@ class TestSendfile(unittest.TestCase): @classmethod def setUpClass(cls): + cls.key = support.threading_setup() with open(support.TESTFN, "wb") as f: f.write(cls.DATA) @classmethod def tearDownClass(cls): + support.threading_cleanup(*cls.key) support.unlink(support.TESTFN) def setUp(self): @@ -2523,41 +2676,27 @@ class FDInheritanceTests(unittest.TestCase): self.assertEqual(os.get_inheritable(slave_fd), False) -@support.reap_threads -def test_main(): - support.run_unittest( - FileTests, - StatAttributeTests, - EnvironTests, - WalkTests, - FwalkTests, - MakedirTests, - DevNullTests, - URandomTests, - ExecTests, - Win32ErrorTests, - TestInvalidFD, - PosixUidGidTests, - Pep383Tests, - Win32KillTests, - Win32ListdirTests, - Win32SymlinkTests, - NonLocalSymlinkTests, - FSEncodingTests, - DeviceEncodingTests, - PidTests, - LoginTests, - LinkTests, - TestSendfile, - ProgramPriorityTests, - ExtendedAttributeTests, - Win32DeprecatedBytesAPI, - TermsizeTests, - OSErrorTests, - RemoveDirsTests, - CPUCountTests, - FDInheritanceTests, - ) +@unittest.skipUnless(hasattr(os, 'get_blocking'), + 'needs os.get_blocking() and os.set_blocking()') +class BlockingTests(unittest.TestCase): + def test_blocking(self): + fd = os.open(__file__, os.O_RDONLY) + self.addCleanup(os.close, fd) + self.assertEqual(os.get_blocking(fd), True) + + os.set_blocking(fd, False) + self.assertEqual(os.get_blocking(fd), False) + + os.set_blocking(fd, True) + self.assertEqual(os.get_blocking(fd), True) + + + +class ExportsTests(unittest.TestCase): + def test_os_all(self): + self.assertIn('open', os.__all__) + self.assertIn('walk', os.__all__) + if __name__ == "__main__": - test_main() + unittest.main() diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index ab88c34..f4ee519 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -4,13 +4,10 @@ import os import errno import pathlib import pickle -import shutil import socket import stat -import sys import tempfile import unittest -from contextlib import contextmanager from test import support TESTFN = support.TESTFN @@ -743,7 +740,6 @@ class PureWindowsPathTest(_BasePurePathTest, unittest.TestCase): self.assertEqual(P('//Some/SHARE/a/B'), P('//somE/share/A/b')) def test_as_uri(self): - from urllib.parse import quote_from_bytes P = self.cls with self.assertRaises(ValueError): P('/a/b').as_uri() @@ -1265,11 +1261,55 @@ class _BasePathTest(object): p = self.cls.cwd() self._test_cwd(p) + def _test_home(self, p): + q = self.cls(os.path.expanduser('~')) + self.assertEqual(p, q) + self.assertEqual(str(p), str(q)) + self.assertIs(type(p), type(q)) + self.assertTrue(p.is_absolute()) + + def test_home(self): + p = self.cls.home() + self._test_home(p) + + def test_samefile(self): + fileA_path = os.path.join(BASE, 'fileA') + fileB_path = os.path.join(BASE, 'dirB', 'fileB') + p = self.cls(fileA_path) + pp = self.cls(fileA_path) + q = self.cls(fileB_path) + self.assertTrue(p.samefile(fileA_path)) + self.assertTrue(p.samefile(pp)) + self.assertFalse(p.samefile(fileB_path)) + self.assertFalse(p.samefile(q)) + # Test the non-existent file case + non_existent = os.path.join(BASE, 'foo') + r = self.cls(non_existent) + self.assertRaises(FileNotFoundError, p.samefile, r) + self.assertRaises(FileNotFoundError, p.samefile, non_existent) + self.assertRaises(FileNotFoundError, r.samefile, p) + self.assertRaises(FileNotFoundError, r.samefile, non_existent) + self.assertRaises(FileNotFoundError, r.samefile, r) + self.assertRaises(FileNotFoundError, r.samefile, non_existent) + def test_empty_path(self): # The empty path points to '.' p = self.cls('') self.assertEqual(p.stat(), os.stat('.')) + def test_expanduser_common(self): + P = self.cls + p = P('~') + self.assertEqual(p.expanduser(), P(os.path.expanduser('~'))) + p = P('foo') + self.assertEqual(p.expanduser(), p) + p = P('/~') + self.assertEqual(p.expanduser(), p) + p = P('../~') + self.assertEqual(p.expanduser(), p) + p = P(P('').absolute().anchor) / '~' + self.assertEqual(p.expanduser(), p) + def test_exists(self): P = self.cls p = P(BASE) @@ -1297,6 +1337,23 @@ class _BasePathTest(object): self.assertIsInstance(f, io.RawIOBase) self.assertEqual(f.read().strip(), b"this is file A") + def test_read_write_bytes(self): + p = self.cls(BASE) + (p / 'fileA').write_bytes(b'abcdefg') + self.assertEqual((p / 'fileA').read_bytes(), b'abcdefg') + # check that trying to write str does not truncate the file + self.assertRaises(TypeError, (p / 'fileA').write_bytes, 'somestr') + self.assertEqual((p / 'fileA').read_bytes(), b'abcdefg') + + def test_read_write_text(self): + p = self.cls(BASE) + (p / 'fileA').write_text('äbcdefg', encoding='latin-1') + self.assertEqual((p / 'fileA').read_text( + encoding='utf-8', errors='ignore'), 'bcdefg') + # check that trying to write bytes does not truncate the file + self.assertRaises(TypeError, (p / 'fileA').write_text, b'somebytes') + self.assertEqual((p / 'fileA').read_text(encoding='latin-1'), 'äbcdefg') + def test_iterdir(self): P = self.cls p = P(BASE) @@ -1600,6 +1657,59 @@ class _BasePathTest(object): # the parent's permissions follow the default process settings self.assertEqual(stat.S_IMODE(p.parent.stat().st_mode), mode) + def test_mkdir_exist_ok(self): + p = self.cls(BASE, 'dirB') + st_ctime_first = p.stat().st_ctime + self.assertTrue(p.exists()) + self.assertTrue(p.is_dir()) + with self.assertRaises(FileExistsError) as cm: + p.mkdir() + self.assertEqual(cm.exception.errno, errno.EEXIST) + p.mkdir(exist_ok=True) + self.assertTrue(p.exists()) + self.assertEqual(p.stat().st_ctime, st_ctime_first) + + def test_mkdir_exist_ok_with_parent(self): + p = self.cls(BASE, 'dirC') + self.assertTrue(p.exists()) + with self.assertRaises(FileExistsError) as cm: + p.mkdir() + self.assertEqual(cm.exception.errno, errno.EEXIST) + p = p / 'newdirC' + p.mkdir(parents=True) + st_ctime_first = p.stat().st_ctime + self.assertTrue(p.exists()) + with self.assertRaises(FileExistsError) as cm: + p.mkdir(parents=True) + self.assertEqual(cm.exception.errno, errno.EEXIST) + p.mkdir(parents=True, exist_ok=True) + self.assertTrue(p.exists()) + self.assertEqual(p.stat().st_ctime, st_ctime_first) + + def test_mkdir_with_child_file(self): + p = self.cls(BASE, 'dirB', 'fileB') + self.assertTrue(p.exists()) + # An exception is raised when the last path component is an existing + # regular file, regardless of whether exist_ok is true or not. + with self.assertRaises(FileExistsError) as cm: + p.mkdir(parents=True) + self.assertEqual(cm.exception.errno, errno.EEXIST) + with self.assertRaises(FileExistsError) as cm: + p.mkdir(parents=True, exist_ok=True) + self.assertEqual(cm.exception.errno, errno.EEXIST) + + def test_mkdir_no_parents_file(self): + p = self.cls(BASE, 'fileA') + self.assertTrue(p.exists()) + # An exception is raised when the last path component is an existing + # regular file, regardless of whether exist_ok is true or not. + with self.assertRaises(FileExistsError) as cm: + p.mkdir() + self.assertEqual(cm.exception.errno, errno.EEXIST) + with self.assertRaises(FileExistsError) as cm: + p.mkdir(exist_ok=True) + self.assertEqual(cm.exception.errno, errno.EEXIST) + @with_symlinks def test_symlink_to(self): P = self.cls(BASE) @@ -1842,7 +1952,6 @@ class PosixPathTest(_BasePathTest, unittest.TestCase): @with_symlinks def test_resolve_loop(self): # Loop detection for broken symlinks under POSIX - P = self.cls # Loops with relative symlinks os.symlink('linkX/inside', join('linkX')) self._check_symlink_loop(BASE, 'linkX') @@ -1874,6 +1983,48 @@ class PosixPathTest(_BasePathTest, unittest.TestCase): self.assertEqual(given, expect) self.assertEqual(set(p.rglob("FILEd*")), set()) + def test_expanduser(self): + P = self.cls + support.import_module('pwd') + import pwd + pwdent = pwd.getpwuid(os.getuid()) + username = pwdent.pw_name + userhome = pwdent.pw_dir.rstrip('/') + # find arbitrary different user (if exists) + for pwdent in pwd.getpwall(): + othername = pwdent.pw_name + otherhome = pwdent.pw_dir.rstrip('/') + if othername != username and otherhome: + break + + p1 = P('~/Documents') + p2 = P('~' + username + '/Documents') + p3 = P('~' + othername + '/Documents') + p4 = P('../~' + username + '/Documents') + p5 = P('/~' + username + '/Documents') + p6 = P('') + p7 = P('~fakeuser/Documents') + + with support.EnvironmentVarGuard() as env: + env.pop('HOME', None) + + self.assertEqual(p1.expanduser(), P(userhome) / 'Documents') + self.assertEqual(p2.expanduser(), P(userhome) / 'Documents') + self.assertEqual(p3.expanduser(), P(otherhome) / 'Documents') + self.assertEqual(p4.expanduser(), p4) + self.assertEqual(p5.expanduser(), p5) + self.assertEqual(p6.expanduser(), p6) + self.assertRaises(RuntimeError, p7.expanduser) + + env['HOME'] = '/tmp' + self.assertEqual(p1.expanduser(), P('/tmp/Documents')) + self.assertEqual(p2.expanduser(), P(userhome) / 'Documents') + self.assertEqual(p3.expanduser(), P(otherhome) / 'Documents') + self.assertEqual(p4.expanduser(), p4) + self.assertEqual(p5.expanduser(), p5) + self.assertEqual(p6.expanduser(), p6) + self.assertRaises(RuntimeError, p7.expanduser) + @only_nt class WindowsPathTest(_BasePathTest, unittest.TestCase): @@ -1889,6 +2040,61 @@ class WindowsPathTest(_BasePathTest, unittest.TestCase): p = P(BASE, "dirC") self.assertEqual(set(p.rglob("FILEd")), { P(BASE, "dirC/dirD/fileD") }) + def test_expanduser(self): + P = self.cls + with support.EnvironmentVarGuard() as env: + env.pop('HOME', None) + env.pop('USERPROFILE', None) + env.pop('HOMEPATH', None) + env.pop('HOMEDRIVE', None) + env['USERNAME'] = 'alice' + + # test that the path returns unchanged + p1 = P('~/My Documents') + p2 = P('~alice/My Documents') + p3 = P('~bob/My Documents') + p4 = P('/~/My Documents') + p5 = P('d:~/My Documents') + p6 = P('') + self.assertRaises(RuntimeError, p1.expanduser) + self.assertRaises(RuntimeError, p2.expanduser) + self.assertRaises(RuntimeError, p3.expanduser) + self.assertEqual(p4.expanduser(), p4) + self.assertEqual(p5.expanduser(), p5) + self.assertEqual(p6.expanduser(), p6) + + def check(): + env.pop('USERNAME', None) + self.assertEqual(p1.expanduser(), + P('C:/Users/alice/My Documents')) + self.assertRaises(KeyError, p2.expanduser) + env['USERNAME'] = 'alice' + self.assertEqual(p2.expanduser(), + P('C:/Users/alice/My Documents')) + self.assertEqual(p3.expanduser(), + P('C:/Users/bob/My Documents')) + self.assertEqual(p4.expanduser(), p4) + self.assertEqual(p5.expanduser(), p5) + self.assertEqual(p6.expanduser(), p6) + + # test the first lookup key in the env vars + env['HOME'] = 'C:\\Users\\alice' + check() + + # test that HOMEPATH is available instead + env.pop('HOME', None) + env['HOMEPATH'] = 'C:\\Users\\alice' + check() + + env['HOMEDRIVE'] = 'C:\\' + env['HOMEPATH'] = 'Users\\alice' + check() + + env.pop('HOMEDRIVE', None) + env.pop('HOMEPATH', None) + env['USERPROFILE'] = 'C:\\Users\\alice' + check() + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_pkgutil.py b/Lib/test/test_pkgutil.py index e0c8635de..57ebf1f 100644 --- a/Lib/test/test_pkgutil.py +++ b/Lib/test/test_pkgutil.py @@ -104,6 +104,9 @@ class PkgutilTests(unittest.TestCase): class PkgutilPEP302Tests(unittest.TestCase): class MyTestLoader(object): + def create_module(self, spec): + return None + def exec_module(self, mod): # Count how many times the module is reloaded mod.__dict__['loads'] = mod.__dict__.get('loads', 0) + 1 diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py index b3de43b..ededbdb 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -307,7 +307,7 @@ class PlatformTest(unittest.TestCase): with mock.patch('platform._UNIXCONFDIR', tempdir): distname, version, distid = platform.linux_distribution() - self.assertEqual(distname, 'Fedora') + self.assertEqual(distname, 'Fedora') self.assertEqual(version, '19') self.assertEqual(distid, 'Schr\xf6dinger\u2019s Cat') diff --git a/Lib/test/test_poplib.py b/Lib/test/test_poplib.py index 8a3c9f4..14a519d 100644 --- a/Lib/test/test_poplib.py +++ b/Lib/test/test_poplib.py @@ -345,23 +345,18 @@ class TestPOP3Class(TestCase): if SUPPORTS_SSL: + from test.test_ftplib import SSLConnection - class DummyPOP3_SSLHandler(DummyPOP3Handler): + class DummyPOP3_SSLHandler(SSLConnection, DummyPOP3Handler): def __init__(self, conn): asynchat.async_chat.__init__(self, conn) - ssl_socket = ssl.wrap_socket(self.socket, certfile=CERTFILE, - server_side=True, - do_handshake_on_connect=False) - self.del_channel() - self.set_socket(ssl_socket) - # Must try handshake before calling push() - self.tls_active = True - self.tls_starting = True - self._do_tls_handshake() + self.secure_connection() self.set_terminator(b"\r\n") self.in_buffer = [] self.push('+OK dummy pop3 server ready. <timestamp>') + self.tls_active = True + self.tls_starting = False @requires_ssl @@ -452,7 +447,7 @@ class TestTimeouts(TestCase): del self.thread # Clear out any dangling Thread objects. def server(self, evt, serv): - serv.listen(5) + serv.listen() evt.set() try: conn, addr = serv.accept() diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index 72fdd16..f37f2de 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -9,7 +9,6 @@ import errno import sys import time import os -import fcntl import platform import pwd import shutil @@ -355,7 +354,7 @@ class PosixTester(unittest.TestCase): def test_oscloexec(self): fd = os.open(support.TESTFN, os.O_RDONLY|os.O_CLOEXEC) self.addCleanup(os.close, fd) - self.assertTrue(fcntl.fcntl(fd, fcntl.F_GETFD) & fcntl.FD_CLOEXEC) + self.assertFalse(os.get_inheritable(fd)) @unittest.skipUnless(hasattr(posix, 'O_EXLOCK'), 'test needs posix.O_EXLOCK') @@ -605,8 +604,8 @@ class PosixTester(unittest.TestCase): self.addCleanup(os.close, w) self.assertFalse(os.get_inheritable(r)) self.assertFalse(os.get_inheritable(w)) - self.assertTrue(fcntl.fcntl(r, fcntl.F_GETFL) & os.O_NONBLOCK) - self.assertTrue(fcntl.fcntl(w, fcntl.F_GETFL) & os.O_NONBLOCK) + self.assertFalse(os.get_blocking(r)) + self.assertFalse(os.get_blocking(w)) # try reading from an empty pipe: this should fail, not block self.assertRaises(OSError, os.read, r, 1) # try a write big enough to fill-up the pipe: this should either diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py index ec2fbae..b2454794 100644 --- a/Lib/test/test_posixpath.py +++ b/Lib/test/test_posixpath.py @@ -57,18 +57,6 @@ class PosixPathTest(unittest.TestCase): self.assertEqual(posixpath.join(b"/foo/", b"bar/", b"baz/"), b"/foo/bar/baz/") - def test_join_errors(self): - # Check posixpath.join raises friendly TypeErrors. - errmsg = "Can't mix strings and bytes in path components" - with self.assertRaisesRegex(TypeError, errmsg): - posixpath.join(b'bytes', 'str') - with self.assertRaisesRegex(TypeError, errmsg): - posixpath.join('str', b'bytes') - # regression, see #15377 - with self.assertRaises(TypeError) as cm: - posixpath.join(None, 'str') - self.assertNotEqual(cm.exception.args[0], errmsg) - def test_split(self): self.assertEqual(posixpath.split("/foo/bar"), ("/foo", "bar")) self.assertEqual(posixpath.split("/"), ("/", "")) diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py index 8916861..9b783c3 100644 --- a/Lib/test/test_pty.py +++ b/Lib/test/test_pty.py @@ -1,7 +1,6 @@ from test.support import verbose, run_unittest, import_module, reap_children -#Skip these tests if either fcntl or termios is not available -fcntl = import_module('fcntl') +# Skip these tests if termios is not available import_module('termios') import errno @@ -84,16 +83,18 @@ class PtyTest(unittest.TestCase): # in master_open(), we need to read the EOF. # Ensure the fd is non-blocking in case there's nothing to read. - orig_flags = fcntl.fcntl(master_fd, fcntl.F_GETFL) - fcntl.fcntl(master_fd, fcntl.F_SETFL, orig_flags | os.O_NONBLOCK) + blocking = os.get_blocking(master_fd) try: - s1 = os.read(master_fd, 1024) - self.assertEqual(b'', s1) - except OSError as e: - if e.errno != errno.EAGAIN: - raise - # Restore the original flags. - fcntl.fcntl(master_fd, fcntl.F_SETFL, orig_flags) + os.set_blocking(master_fd, False) + try: + s1 = os.read(master_fd, 1024) + self.assertEqual(b'', s1) + except OSError as e: + if e.errno != errno.EAGAIN: + raise + finally: + # Restore the original flags. + os.set_blocking(master_fd, blocking) debug("Writing to slave_fd") os.write(slave_fd, TEST_STRING_1) diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py index 8bf9b20..6742444 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -2,7 +2,6 @@ import os import sys import builtins import contextlib -import difflib import inspect import pydoc import keyword @@ -357,15 +356,6 @@ def get_pydoc_text(module): output = patt.sub('', output) return output.strip(), loc -def print_diffs(text1, text2): - "Prints unified diffs for two texts" - # XXX now obsolete, use unittest built-in support - lines1 = text1.splitlines(keepends=True) - lines2 = text2.splitlines(keepends=True) - diffs = difflib.unified_diff(lines1, lines2, n=0, fromfile='expected', - tofile='got') - print('\n' + ''.join(diffs)) - def get_html_title(text): # Bit of hack, but good enough for test purposes header, _, _ = text.partition("</head>") @@ -411,9 +401,7 @@ class PydocDocTest(unittest.TestCase): expected_html = expected_html_pattern % ( (mod_url, mod_file, doc_loc) + expected_html_data_docstrings) - if result != expected_html: - print_diffs(expected_html, result) - self.fail("outputs are not equal, see diff above") + self.assertEqual(result, expected_html) @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") @@ -426,9 +414,7 @@ class PydocDocTest(unittest.TestCase): (doc_loc,) + expected_text_data_docstrings + (inspect.getabsfile(pydoc_mod),)) - if result != expected_text: - print_diffs(expected_text, result) - self.fail("outputs are not equal, see diff above") + self.assertEqual(expected_text, result) def test_text_enum_member_with_value_zero(self): # Test issue #20654 to ensure enum member with value 0 can be @@ -886,9 +872,7 @@ class PydocWithMetaClasses(unittest.TestCase): expected_text = expected_dynamicattribute_pattern % ( (__name__,) + expected_text_data_docstrings[:2]) result = output.getvalue().strip() - if result != expected_text: - print_diffs(expected_text, result) - self.fail("outputs are not equal, see diff above") + self.assertEqual(expected_text, result) @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") @@ -909,9 +893,7 @@ class PydocWithMetaClasses(unittest.TestCase): helper(Class) expected_text = expected_virtualattribute_pattern1 % __name__ result = output.getvalue().strip() - if result != expected_text: - print_diffs(expected_text, result) - self.fail("outputs are not equal, see diff above") + self.assertEqual(expected_text, result) @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") @@ -951,19 +933,13 @@ class PydocWithMetaClasses(unittest.TestCase): helper(Class1) expected_text1 = expected_virtualattribute_pattern2 % __name__ result1 = output.getvalue().strip() - if result1 != expected_text1: - print_diffs(expected_text1, result1) - fail1 = True + self.assertEqual(expected_text1, result1) output = StringIO() helper = pydoc.Helper(output=output) helper(Class2) expected_text2 = expected_virtualattribute_pattern3 % __name__ result2 = output.getvalue().strip() - if result2 != expected_text2: - print_diffs(expected_text2, result2) - fail2 = True - if fail1 or fail2: - self.fail("outputs are not equal, see diff above") + self.assertEqual(expected_text2, result2) @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") @@ -980,9 +956,7 @@ class PydocWithMetaClasses(unittest.TestCase): helper(C) expected_text = expected_missingattribute_pattern % __name__ result = output.getvalue().strip() - if result != expected_text: - print_diffs(expected_text, result) - self.fail("outputs are not equal, see diff above") + self.assertEqual(expected_text, result) @reap_threads diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index d2547d4..6e90b2f 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -84,7 +84,7 @@ class ReTests(unittest.TestCase): self.assertEqual(re.sub("(?i)b+", "x", "bbbb BBBB"), 'x x') self.assertEqual(re.sub(r'\d+', self.bump_num, '08.2 -2 23x99y'), '9.3 -3 24x100y') - self.assertEqual(re.sub(r'\d+', self.bump_num, '08.2 -2 23x99y', 3), + self.assertEqual(re.sub(r'\d+', self.bump_num, '08.2 -2 23x99y', count=3), '9.3 -3 23x99y') self.assertEqual(re.sub('.', lambda m: r"\n", 'x'), '\\n') @@ -155,8 +155,8 @@ class ReTests(unittest.TestCase): self.assertEqual(re.sub('x', r'\09', 'x'), '\0' + '9') self.assertEqual(re.sub('x', r'\0a', 'x'), '\0' + 'a') - self.assertEqual(re.sub('x', r'\400', 'x'), '\0') - self.assertEqual(re.sub('x', r'\777', 'x'), '\377') + self.assertRaises(re.error, re.sub, 'x', r'\400', 'x') + self.assertRaises(re.error, re.sub, 'x', r'\777', 'x') self.assertRaises(re.error, re.sub, 'x', r'\1', 'x') self.assertRaises(re.error, re.sub, 'x', r'\8', 'x') @@ -180,7 +180,7 @@ class ReTests(unittest.TestCase): def test_qualified_re_sub(self): self.assertEqual(re.sub('a', 'b', 'aaaaa'), 'bbbbb') - self.assertEqual(re.sub('a', 'b', 'aaaaa', 1), 'baaaa') + self.assertEqual(re.sub('a', 'b', 'aaaaa', count=1), 'baaaa') def test_bug_114660(self): self.assertEqual(re.sub(r'(\S)\s+(\S)', r'\1 \2', 'hello there'), @@ -194,6 +194,7 @@ class ReTests(unittest.TestCase): def test_symbolic_groups(self): re.compile('(?P<a>x)(?P=a)(?(a)y)') re.compile('(?P<a1>x)(?P=a1)(?(a1)y)') + re.compile('(?P<a1>x)\1(?(1)y)') self.assertRaises(re.error, re.compile, '(?P<a>)(?P<a>)') self.assertRaises(re.error, re.compile, '(?Px)') self.assertRaises(re.error, re.compile, '(?P=)') @@ -213,6 +214,10 @@ class ReTests(unittest.TestCase): re.compile('(?P<µ>x)(?P=µ)(?(µ)y)') re.compile('(?P<𝔘𝔫𝔦𝔠𝔬𝔡𝔢>x)(?P=𝔘𝔫𝔦𝔠𝔬𝔡𝔢)(?(𝔘𝔫𝔦𝔠𝔬𝔡𝔢)y)') self.assertRaises(re.error, re.compile, '(?P<©>x)') + # Support > 100 groups. + pat = '|'.join('x(?P<a%d>%x)y' % (i, i) for i in range(1, 200 + 1)) + pat = '(?:%s)(?(200)z|t)' % pat + self.assertEqual(re.match(pat, 'xc8yz').span(), (0, 5)) def test_symbolic_refs(self): self.assertRaises(re.error, re.sub, '(?P<a>x)', '\g<a', 'xx') @@ -221,21 +226,26 @@ class ReTests(unittest.TestCase): self.assertRaises(re.error, re.sub, '(?P<a>x)', '\g<a a>', 'xx') self.assertRaises(re.error, re.sub, '(?P<a>x)', '\g<>', 'xx') self.assertRaises(re.error, re.sub, '(?P<a>x)', '\g<1a1>', 'xx') + self.assertRaises(re.error, re.sub, '(?P<a>x)', r'\g<2>', 'xx') + self.assertRaises(re.error, re.sub, '(?P<a>x)', r'\2', 'xx') self.assertRaises(IndexError, re.sub, '(?P<a>x)', '\g<ab>', 'xx') - self.assertRaises(re.error, re.sub, '(?P<a>x)|(?P<b>y)', '\g<b>', 'xx') - self.assertRaises(re.error, re.sub, '(?P<a>x)|(?P<b>y)', '\\2', 'xx') + self.assertEqual(re.sub('(?P<a>x)|(?P<b>y)', r'\g<b>', 'xx'), '') + self.assertEqual(re.sub('(?P<a>x)|(?P<b>y)', r'\2', 'xx'), '') self.assertRaises(re.error, re.sub, '(?P<a>x)', '\g<-1>', 'xx') # New valid/invalid identifiers in Python 3 self.assertEqual(re.sub('(?P<µ>x)', r'\g<µ>', 'xx'), 'xx') self.assertEqual(re.sub('(?P<𝔘𝔫𝔦𝔠𝔬𝔡𝔢>x)', r'\g<𝔘𝔫𝔦𝔠𝔬𝔡𝔢>', 'xx'), 'xx') self.assertRaises(re.error, re.sub, '(?P<a>x)', r'\g<©>', 'xx') + # Support > 100 groups. + pat = '|'.join('x(?P<a%d>%x)y' % (i, i) for i in range(1, 200 + 1)) + self.assertEqual(re.sub(pat, '\g<200>', 'xc8yzxc8y'), 'c8zc8') def test_re_subn(self): self.assertEqual(re.subn("(?i)b+", "x", "bbbb BBBB"), ('x x', 2)) self.assertEqual(re.subn("b+", "x", "bbbb BBBB"), ('x BBBB', 1)) self.assertEqual(re.subn("b+", "x", "xyz"), ('xyz', 0)) self.assertEqual(re.subn("b*", "x", "xyz"), ('xxxyxzx', 4)) - self.assertEqual(re.subn("b*", "x", "xyz", 2), ('xxxyz', 2)) + self.assertEqual(re.subn("b*", "x", "xyz", count=2), ('xxxyz', 2)) def test_re_split(self): for string in ":a:b::c", S(":a:b::c"): @@ -273,11 +283,11 @@ class ReTests(unittest.TestCase): ['', 'a', '', '', 'c']) def test_qualified_re_split(self): - self.assertEqual(re.split(":", ":a:b::c", 2), ['', 'a', 'b::c']) - self.assertEqual(re.split(':', 'a:b:c:d', 2), ['a', 'b', 'c:d']) - self.assertEqual(re.split("(:)", ":a:b::c", 2), + self.assertEqual(re.split(":", ":a:b::c", maxsplit=2), ['', 'a', 'b::c']) + self.assertEqual(re.split(':', 'a:b:c:d', maxsplit=2), ['a', 'b', 'c:d']) + self.assertEqual(re.split("(:)", ":a:b::c", maxsplit=2), ['', ':', 'a', ':', 'b::c']) - self.assertEqual(re.split("(:*)", ":a:b::c", 2), + self.assertEqual(re.split("(:*)", ":a:b::c", maxsplit=2), ['', ':', 'a', ':', 'b::c']) def test_re_findall(self): @@ -405,6 +415,10 @@ class ReTests(unittest.TestCase): self.assertIsNone(p.match('abd')) self.assertIsNone(p.match('ac')) + # Support > 100 groups. + pat = '|'.join('x(?P<a%d>%x)y' % (i, i) for i in range(1, 200 + 1)) + pat = '(?:%s)(?(200)z)' % pat + self.assertEqual(re.match(pat, 'xc8yz').span(), (0, 5)) def test_re_groupref(self): self.assertEqual(re.match(r'^(\|)?([^()]+)\1$', '|a|').groups(), @@ -428,6 +442,10 @@ class ReTests(unittest.TestCase): "first second") .expand(r"\2 \1 \g<second> \g<first>"), "second first second first") + self.assertEqual(re.match("(?P<first>first)|(?P<second>second)", + "first") + .expand(r"\2 \g<second>"), + " ") def test_repeat_minmax(self): self.assertIsNone(re.match("^(\w){1}$", "abc")) @@ -484,10 +502,6 @@ class ReTests(unittest.TestCase): "abcd abc bcd bx", re.ASCII).group(1), "bx") self.assertEqual(re.search(r"\B(b.)\B", "abc bcd bc abxd", re.ASCII).group(1), "bx") - self.assertEqual(re.search(r"\b(b.)\b", - "abcd abc bcd bx", re.LOCALE).group(1), "bx") - self.assertEqual(re.search(r"\B(b.)\B", - "abc bcd bc abxd", re.LOCALE).group(1), "bx") self.assertEqual(re.search(r"^abc$", "\nabc\n", re.M).group(0), "abc") self.assertEqual(re.search(r"^\Aabc\Z$", "abc", re.M).group(0), "abc") self.assertIsNone(re.search(r"^\Aabc\Z$", "\nabc\n", re.M)) @@ -508,11 +522,23 @@ class ReTests(unittest.TestCase): b"1aa! a").group(0), b"1aa! a") self.assertEqual(re.search(r"\d\D\w\W\s\S", "1aa! a", re.ASCII).group(0), "1aa! a") - self.assertEqual(re.search(r"\d\D\w\W\s\S", - "1aa! a", re.LOCALE).group(0), "1aa! a") self.assertEqual(re.search(br"\d\D\w\W\s\S", b"1aa! a", re.LOCALE).group(0), b"1aa! a") + def test_other_escapes(self): + self.assertRaises(re.error, re.compile, "\\") + self.assertEqual(re.match(r"\(", '(').group(), '(') + self.assertIsNone(re.match(r"\(", ')')) + self.assertEqual(re.match(r"\\", '\\').group(), '\\') + self.assertEqual(re.match(r"\y", 'y').group(), 'y') + self.assertIsNone(re.match(r"\y", 'z')) + self.assertEqual(re.match(r"[\]]", ']').group(), ']') + self.assertIsNone(re.match(r"[\]]", '[')) + self.assertEqual(re.match(r"[a\-c]", '-').group(), '-') + self.assertIsNone(re.match(r"[a\-c]", 'b')) + self.assertEqual(re.match(r"[\^a]+", 'a^').group(), 'a^') + self.assertIsNone(re.match(r"[\^a]+", 'b')) + def test_string_boundaries(self): # See http://bugs.python.org/issue10713 self.assertEqual(re.search(r"\b(abc)\b", "abc").group(1), @@ -661,9 +687,12 @@ class ReTests(unittest.TestCase): self.assertEqual(_sre.getlower(ord('A'), 0), ord('a')) self.assertEqual(_sre.getlower(ord('A'), re.LOCALE), ord('a')) self.assertEqual(_sre.getlower(ord('A'), re.UNICODE), ord('a')) + self.assertEqual(_sre.getlower(ord('A'), re.ASCII), ord('a')) self.assertEqual(re.match("abc", "ABC", re.I).group(0), "ABC") self.assertEqual(re.match(b"abc", b"ABC", re.I).group(0), b"ABC") + self.assertEqual(re.match("abc", "ABC", re.I|re.A).group(0), "ABC") + self.assertEqual(re.match(b"abc", b"ABC", re.I|re.L).group(0), b"ABC") def test_not_literal(self): self.assertEqual(re.search("\s([^a])", " b").group(1), "b") @@ -748,8 +777,10 @@ class ReTests(unittest.TestCase): self.assertEqual(re.X, re.VERBOSE) def test_flags(self): - for flag in [re.I, re.M, re.X, re.S, re.L]: + for flag in [re.I, re.M, re.X, re.S, re.A, re.U]: self.assertTrue(re.compile('^pattern$', flag)) + for flag in [re.I, re.M, re.X, re.S, re.A, re.L]: + self.assertTrue(re.compile(b'^pattern$', flag)) def test_sre_character_literals(self): for i in [0, 8, 16, 32, 64, 127, 128, 255, 256, 0xFFFF, 0x10000, 0x10FFFF]: @@ -771,7 +802,7 @@ class ReTests(unittest.TestCase): self.assertTrue(re.match(r"\08", "\0008")) self.assertTrue(re.match(r"\01", "\001")) self.assertTrue(re.match(r"\018", "\0018")) - self.assertTrue(re.match(r"\567", chr(0o167))) + self.assertRaises(re.error, re.match, r"\567", "") self.assertRaises(re.error, re.match, r"\911", "") self.assertRaises(re.error, re.match, r"\x1", "") self.assertRaises(re.error, re.match, r"\x1z", "") @@ -799,12 +830,13 @@ class ReTests(unittest.TestCase): self.assertTrue(re.match(r"[\U%08x]" % i, chr(i))) self.assertTrue(re.match(r"[\U%08x0]" % i, chr(i)+"0")) self.assertTrue(re.match(r"[\U%08xz]" % i, chr(i)+"z")) - self.assertTrue(re.match(r"[\U0001d49c-\U0001d4b5]", "\U0001d49e")) + self.assertRaises(re.error, re.match, r"[\567]", "") self.assertRaises(re.error, re.match, r"[\911]", "") self.assertRaises(re.error, re.match, r"[\x1z]", "") self.assertRaises(re.error, re.match, r"[\u123z]", "") self.assertRaises(re.error, re.match, r"[\U0001234z]", "") self.assertRaises(re.error, re.match, r"[\U00110000]", "") + self.assertTrue(re.match(r"[\U0001d49c-\U0001d4b5]", "\U0001d49e")) def test_sre_byte_literals(self): for i in [0, 8, 16, 32, 64, 127, 128, 255]: @@ -820,7 +852,7 @@ class ReTests(unittest.TestCase): self.assertTrue(re.match(br"\08", b"\0008")) self.assertTrue(re.match(br"\01", b"\001")) self.assertTrue(re.match(br"\018", b"\0018")) - self.assertTrue(re.match(br"\567", bytes([0o167]))) + self.assertRaises(re.error, re.match, br"\567", b"") self.assertRaises(re.error, re.match, br"\911", b"") self.assertRaises(re.error, re.match, br"\x1", b"") self.assertRaises(re.error, re.match, br"\x1z", b"") @@ -837,6 +869,7 @@ class ReTests(unittest.TestCase): self.assertTrue(re.match((r"[\x%02xz]" % i).encode(), bytes([i]))) self.assertTrue(re.match(br"[\u]", b'u')) self.assertTrue(re.match(br"[\U]", b'U')) + self.assertRaises(re.error, re.match, br"[\567]", b"") self.assertRaises(re.error, re.match, br"[\911]", b"") self.assertRaises(re.error, re.match, br"[\x1z]", b"") @@ -1031,8 +1064,8 @@ class ReTests(unittest.TestCase): def test_inline_flags(self): # Bug #1700 - upper_char = chr(0x1ea0) # Latin Capital Letter A with Dot Bellow - lower_char = chr(0x1ea1) # Latin Small Letter A with Dot Bellow + upper_char = '\u1ea0' # Latin Capital Letter A with Dot Below + lower_char = '\u1ea1' # Latin Small Letter A with Dot Below p = re.compile(upper_char, re.I | re.U) q = p.match(lower_char) @@ -1112,6 +1145,52 @@ class ReTests(unittest.TestCase): self.assertRaises(ValueError, re.compile, '(?a)\w', re.UNICODE) self.assertRaises(ValueError, re.compile, '(?au)\w') + def test_locale_flag(self): + import locale + _, enc = locale.getlocale(locale.LC_CTYPE) + # Search non-ASCII letter + for i in range(128, 256): + try: + c = bytes([i]).decode(enc) + sletter = c.lower() + if sletter == c: continue + bletter = sletter.encode(enc) + if len(bletter) != 1: continue + if bletter.decode(enc) != sletter: continue + bpat = re.escape(bytes([i])) + break + except (UnicodeError, TypeError): + pass + else: + bletter = None + bpat = b'A' + # Bytes patterns + pat = re.compile(bpat, re.LOCALE | re.IGNORECASE) + if bletter: + self.assertTrue(pat.match(bletter)) + pat = re.compile(b'(?L)' + bpat, re.IGNORECASE) + if bletter: + self.assertTrue(pat.match(bletter)) + pat = re.compile(bpat, re.IGNORECASE) + if bletter: + self.assertIsNone(pat.match(bletter)) + pat = re.compile(b'\w', re.LOCALE) + if bletter: + self.assertTrue(pat.match(bletter)) + pat = re.compile(b'(?L)\w') + if bletter: + self.assertTrue(pat.match(bletter)) + pat = re.compile(b'\w') + if bletter: + self.assertIsNone(pat.match(bletter)) + # Incompatibilities + self.assertWarns(DeprecationWarning, re.compile, '', re.LOCALE) + self.assertWarns(DeprecationWarning, re.compile, '(?L)') + self.assertWarns(DeprecationWarning, re.compile, b'', re.LOCALE | re.ASCII) + self.assertWarns(DeprecationWarning, re.compile, b'(?L)', re.ASCII) + self.assertWarns(DeprecationWarning, re.compile, b'(?a)', re.LOCALE) + self.assertWarns(DeprecationWarning, re.compile, b'(?aL)') + def test_bug_6509(self): # Replacement strings of both types must parse properly. # all strings @@ -1139,8 +1218,10 @@ class ReTests(unittest.TestCase): # a RuntimeError is raised instead of OverflowError. long_overflow = 2**128 self.assertRaises(TypeError, re.finditer, "a", {}) - self.assertRaises(OverflowError, _sre.compile, "abc", 0, [long_overflow]) - self.assertRaises(TypeError, _sre.compile, {}, 0, []) + with self.assertRaises(OverflowError): + _sre.compile("abc", 0, [long_overflow], 0, [], []) + with self.assertRaises(TypeError): + _sre.compile({}, 0, [], 0, [], []) def test_search_dot_unicode(self): self.assertTrue(re.search("123.*-", '123abc-')) @@ -1278,22 +1359,22 @@ class ReTests(unittest.TestCase): with captured_stdout() as out: re.compile(pat, re.DEBUG) dump = '''\ -subpattern 1 - literal 46 -subpattern None - branch - in - literal 99 - literal 104 - or - literal 112 - literal 121 -subpattern None - groupref_exists 1 - at at_end - else - literal 58 - literal 32 +SUBPATTERN 1 + LITERAL 46 +SUBPATTERN None + BRANCH + IN + LITERAL 99 + LITERAL 104 + OR + LITERAL 112 + LITERAL 121 +SUBPATTERN None + GROUPREF_EXISTS 1 + AT AT_END + ELSE + LITERAL 58 + LITERAL 32 ''' self.assertEqual(out.getvalue(), dump) # Debug output is output again even a second time (bypassing @@ -1361,6 +1442,42 @@ subpattern None self.assertIsNone(re.match(b'(?Li)\xc5', b'\xe5')) self.assertIsNone(re.match(b'(?Li)\xe5', b'\xc5')) + def test_error(self): + with self.assertRaises(re.error) as cm: + re.compile('(\u20ac))') + err = cm.exception + self.assertIsInstance(err.pattern, str) + self.assertEqual(err.pattern, '(\u20ac))') + self.assertEqual(err.pos, 3) + self.assertEqual(err.lineno, 1) + self.assertEqual(err.colno, 4) + self.assertIn(err.msg, str(err)) + self.assertIn(' at position 3', str(err)) + self.assertNotIn(' at position 3', err.msg) + # Bytes pattern + with self.assertRaises(re.error) as cm: + re.compile(b'(\xa4))') + err = cm.exception + self.assertIsInstance(err.pattern, bytes) + self.assertEqual(err.pattern, b'(\xa4))') + self.assertEqual(err.pos, 3) + # Multiline pattern + with self.assertRaises(re.error) as cm: + re.compile(""" + ( + abc + ) + ) + ( + """, re.VERBOSE) + err = cm.exception + self.assertEqual(err.pos, 77) + self.assertEqual(err.lineno, 5) + self.assertEqual(err.colno, 17) + self.assertIn(err.msg, str(err)) + self.assertIn(' at position 77', str(err)) + self.assertIn('(line 5, column 17)', str(err)) + class PatternReprTests(unittest.TestCase): def check(self, pattern, expected): @@ -1405,6 +1522,10 @@ class PatternReprTests(unittest.TestCase): self.check_flags(b'bytes pattern', re.A, "re.compile(b'bytes pattern', re.ASCII)") + def test_locale(self): + self.check_flags(b'bytes pattern', re.L, + "re.compile(b'bytes pattern', re.LOCALE)") + def test_quotes(self): self.check('random "double quoted" pattern', '''re.compile('random "double quoted" pattern')''') @@ -1518,8 +1639,16 @@ class ExternalTests(unittest.TestCase): pass else: with self.subTest('bytes pattern match'): - bpat = re.compile(bpat) - self.assertTrue(bpat.search(bs)) + obj = re.compile(bpat) + self.assertTrue(obj.search(bs)) + + # Try the match with LOCALE enabled, and check that it + # still succeeds. + with self.subTest('locale-sensitive match'): + obj = re.compile(bpat, re.LOCALE) + result = obj.search(bs) + if result is None: + print('=== Fails on locale-sensitive match', t) # Try the match with the search area limited to the extent # of the match and see if it still succeeds. \B will @@ -1537,13 +1666,6 @@ class ExternalTests(unittest.TestCase): obj = re.compile(pattern, re.IGNORECASE) self.assertTrue(obj.search(s)) - # Try the match with LOCALE enabled, and check that it - # still succeeds. - if '(?u)' not in pattern: - with self.subTest('locale-sensitive match'): - obj = re.compile(pattern, re.LOCALE) - self.assertTrue(obj.search(s)) - # Try the match with UNICODE locale enabled, and check # that it still succeeds. with self.subTest('unicode-sensitive match'): diff --git a/Lib/test/test_readline.py b/Lib/test/test_readline.py index 0b2b0a5..23d6194 100644 --- a/Lib/test/test_readline.py +++ b/Lib/test/test_readline.py @@ -2,8 +2,9 @@ Very minimal unittests for parts of the readline module. """ import os +import tempfile import unittest -from test.support import run_unittest, import_module +from test.support import run_unittest, import_module, unlink from test.script_helper import assert_python_ok # Skip tests if there is no readline module @@ -42,6 +43,45 @@ class TestHistoryManipulation (unittest.TestCase): self.assertEqual(readline.get_current_history_length(), 1) + @unittest.skipUnless(hasattr(readline, "append_history_file"), + "append_history not available") + def test_write_read_append(self): + hfile = tempfile.NamedTemporaryFile(delete=False) + hfile.close() + hfilename = hfile.name + self.addCleanup(unlink, hfilename) + + # test write-clear-read == nop + readline.clear_history() + readline.add_history("first line") + readline.add_history("second line") + readline.write_history_file(hfilename) + + readline.clear_history() + self.assertEqual(readline.get_current_history_length(), 0) + + readline.read_history_file(hfilename) + self.assertEqual(readline.get_current_history_length(), 2) + self.assertEqual(readline.get_history_item(1), "first line") + self.assertEqual(readline.get_history_item(2), "second line") + + # test append + readline.append_history_file(1, hfilename) + readline.clear_history() + readline.read_history_file(hfilename) + self.assertEqual(readline.get_current_history_length(), 3) + self.assertEqual(readline.get_history_item(1), "first line") + self.assertEqual(readline.get_history_item(2), "second line") + self.assertEqual(readline.get_history_item(3), "second line") + + # test 'no such file' behaviour + os.unlink(hfilename) + with self.assertRaises(FileNotFoundError): + readline.append_history_file(1, hfilename) + + # write_history_file can create the target + readline.write_history_file(hfilename) + class TestReadline(unittest.TestCase): diff --git a/Lib/test/test_reprlib.py b/Lib/test/test_reprlib.py index ae67f06..a51c4d7 100644 --- a/Lib/test/test_reprlib.py +++ b/Lib/test/test_reprlib.py @@ -10,7 +10,7 @@ import importlib import importlib.util import unittest -from test.support import run_unittest, create_empty_file, verbose +from test.support import create_empty_file, verbose from reprlib import repr as r # Don't shadow builtin repr from reprlib import Repr from reprlib import recursive_repr @@ -70,18 +70,18 @@ class ReprTests(unittest.TestCase): eq(r([1, 2, 3, 4, 5, 6, 7]), "[1, 2, 3, 4, 5, 6, ...]") # Sets give up after 6 as well - eq(r(set([])), "set([])") - eq(r(set([1])), "set([1])") - eq(r(set([1, 2, 3])), "set([1, 2, 3])") - eq(r(set([1, 2, 3, 4, 5, 6])), "set([1, 2, 3, 4, 5, 6])") - eq(r(set([1, 2, 3, 4, 5, 6, 7])), "set([1, 2, 3, 4, 5, 6, ...])") + eq(r(set([])), "set()") + eq(r(set([1])), "{1}") + eq(r(set([1, 2, 3])), "{1, 2, 3}") + eq(r(set([1, 2, 3, 4, 5, 6])), "{1, 2, 3, 4, 5, 6}") + eq(r(set([1, 2, 3, 4, 5, 6, 7])), "{1, 2, 3, 4, 5, 6, ...}") # Frozensets give up after 6 as well - eq(r(frozenset([])), "frozenset([])") - eq(r(frozenset([1])), "frozenset([1])") - eq(r(frozenset([1, 2, 3])), "frozenset([1, 2, 3])") - eq(r(frozenset([1, 2, 3, 4, 5, 6])), "frozenset([1, 2, 3, 4, 5, 6])") - eq(r(frozenset([1, 2, 3, 4, 5, 6, 7])), "frozenset([1, 2, 3, 4, 5, 6, ...])") + eq(r(frozenset([])), "frozenset()") + eq(r(frozenset([1])), "frozenset({1})") + eq(r(frozenset([1, 2, 3])), "frozenset({1, 2, 3})") + eq(r(frozenset([1, 2, 3, 4, 5, 6])), "frozenset({1, 2, 3, 4, 5, 6})") + eq(r(frozenset([1, 2, 3, 4, 5, 6, 7])), "frozenset({1, 2, 3, 4, 5, 6, ...})") # collections.deque after 6 eq(r(deque([1, 2, 3, 4, 5, 6, 7])), "deque([1, 2, 3, 4, 5, 6, ...])") @@ -94,7 +94,7 @@ class ReprTests(unittest.TestCase): eq(r(d), "{'alice': 1, 'arthur': 1, 'bob': 2, 'charles': 3, ...}") # array.array after 5. - eq(r(array('i')), "array('i', [])") + eq(r(array('i')), "array('i')") eq(r(array('i', [1])), "array('i', [1])") eq(r(array('i', [1, 2])), "array('i', [1, 2])") eq(r(array('i', [1, 2, 3])), "array('i', [1, 2, 3])") @@ -103,6 +103,20 @@ class ReprTests(unittest.TestCase): eq(r(array('i', [1, 2, 3, 4, 5, 6])), "array('i', [1, 2, 3, 4, 5, ...])") + def test_set_literal(self): + eq = self.assertEqual + eq(r({1}), "{1}") + eq(r({1, 2, 3}), "{1, 2, 3}") + eq(r({1, 2, 3, 4, 5, 6}), "{1, 2, 3, 4, 5, 6}") + eq(r({1, 2, 3, 4, 5, 6, 7}), "{1, 2, 3, 4, 5, 6, ...}") + + def test_frozenset(self): + eq = self.assertEqual + eq(r(frozenset({1})), "frozenset({1})") + eq(r(frozenset({1, 2, 3})), "frozenset({1, 2, 3})") + eq(r(frozenset({1, 2, 3, 4, 5, 6})), "frozenset({1, 2, 3, 4, 5, 6})") + eq(r(frozenset({1, 2, 3, 4, 5, 6, 7})), "frozenset({1, 2, 3, 4, 5, 6, ...})") + def test_numbers(self): eq = self.assertEqual eq(r(123), repr(123)) @@ -123,7 +137,7 @@ class ReprTests(unittest.TestCase): eq(r(i2), expected) i3 = ClassWithFailingRepr() - eq(r(i3), ("<ClassWithFailingRepr instance at %x>"%id(i3))) + eq(r(i3), ("<ClassWithFailingRepr instance at %#x>"%id(i3))) s = r(ClassWithFailingRepr) self.assertTrue(s.startswith("<class ")) @@ -373,11 +387,5 @@ class TestRecursiveRepr(unittest.TestCase): m.append(m) self.assertEqual(repr(m), '<a, b, c, d, e, +++, x, +++>') -def test_main(): - run_unittest(ReprTests) - run_unittest(LongReprTest) - run_unittest(TestRecursiveRepr) - - if __name__ == "__main__": - test_main() + unittest.main() diff --git a/Lib/test/test_selectors.py b/Lib/test/test_selectors.py index 8eddd17..9521481 100644 --- a/Lib/test/test_selectors.py +++ b/Lib/test/test_selectors.py @@ -4,14 +4,12 @@ import random import selectors import signal import socket +import sys from test import support from time import sleep import unittest import unittest.mock -try: - from time import monotonic as time -except ImportError: - from time import time as time +from time import monotonic as time try: import resource except ImportError: @@ -24,7 +22,7 @@ else: def socketpair(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0): with socket.socket(family, type, proto) as l: l.bind((support.HOST, 0)) - l.listen(3) + l.listen() c = socket.socket(family, type, proto) try: c.connect(l.getsockname()) @@ -187,8 +185,8 @@ class BaseSelectorTestCase(unittest.TestCase): s.register(wr, selectors.EVENT_WRITE) s.close() - self.assertRaises(KeyError, s.get_key, rd) - self.assertRaises(KeyError, s.get_key, wr) + self.assertRaises(RuntimeError, s.get_key, rd) + self.assertRaises(RuntimeError, s.get_key, wr) self.assertRaises(KeyError, mapping.__getitem__, rd) self.assertRaises(KeyError, mapping.__getitem__, wr) @@ -257,8 +255,8 @@ class BaseSelectorTestCase(unittest.TestCase): sel.register(rd, selectors.EVENT_READ) sel.register(wr, selectors.EVENT_WRITE) - self.assertRaises(KeyError, s.get_key, rd) - self.assertRaises(KeyError, s.get_key, wr) + self.assertRaises(RuntimeError, s.get_key, rd) + self.assertRaises(RuntimeError, s.get_key, wr) def test_fileno(self): s = self.SELECTOR() @@ -454,10 +452,18 @@ class KqueueSelectorTestCase(BaseSelectorTestCase, ScalableSelectorMixIn): SELECTOR = getattr(selectors, 'KqueueSelector', None) +@unittest.skipUnless(hasattr(selectors, 'DevpollSelector'), + "Test needs selectors.DevpollSelector") +class DevpollSelectorTestCase(BaseSelectorTestCase, ScalableSelectorMixIn): + + SELECTOR = getattr(selectors, 'DevpollSelector', None) + + + def test_main(): tests = [DefaultSelectorTestCase, SelectSelectorTestCase, PollSelectorTestCase, EpollSelectorTestCase, - KqueueSelectorTestCase] + KqueueSelectorTestCase, DevpollSelectorTestCase] support.run_unittest(*tests) support.reap_children() diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py index 65e4243..c25386f 100644 --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -931,7 +931,7 @@ class TestBasicOpsString(TestBasicOps, unittest.TestCase): class TestBasicOpsBytes(TestBasicOps, unittest.TestCase): def setUp(self): - self.case = "string set" + self.case = "bytes set" self.values = [b"a", b"b", b"c"] self.set = set(self.values) self.dup = set(self.values) diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index 9325bc7..c5545ba 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -34,6 +34,12 @@ try: except ImportError: BZ2_SUPPORTED = False +try: + import lzma + LZMA_SUPPORTED = True +except ImportError: + LZMA_SUPPORTED = False + TESTFN2 = TESTFN + "2" try: @@ -1189,6 +1195,8 @@ class TestShutil(unittest.TestCase): formats = ['tar', 'gztar', 'zip'] if BZ2_SUPPORTED: formats.append('bztar') + if LZMA_SUPPORTED: + formats.append('xztar') for format in formats: tmpdir = self.mkdtemp() @@ -1625,6 +1633,24 @@ class TestMove(unittest.TestCase): rv = shutil.move(self.src_file, os.path.join(self.dst_dir, 'bar')) self.assertEqual(rv, os.path.join(self.dst_dir, 'bar')) + @mock_rename + def test_move_file_special_function(self): + moved = [] + def _copy(src, dst): + moved.append((src, dst)) + shutil.move(self.src_file, self.dst_dir, copy_function=_copy) + self.assertEqual(len(moved), 1) + + @mock_rename + def test_move_dir_special_function(self): + moved = [] + def _copy(src, dst): + moved.append((src, dst)) + support.create_empty_file(os.path.join(self.src_dir, 'child')) + support.create_empty_file(os.path.join(self.src_dir, 'child1')) + shutil.move(self.src_dir, self.dst_dir, copy_function=_copy) + self.assertEqual(len(moved), 3) + class TestCopyFile(unittest.TestCase): diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py index 74f74af..8f3dde4 100644 --- a/Lib/test/test_signal.py +++ b/Lib/test/test_signal.py @@ -1,10 +1,12 @@ import unittest from test import support from contextlib import closing +import enum import gc import pickle import select import signal +import socket import struct import subprocess import traceback @@ -14,6 +16,10 @@ try: import threading except ImportError: threading = None +try: + import _testcapi +except ImportError: + _testcapi = None class HandlerBCalled(Exception): @@ -39,6 +45,23 @@ def ignoring_eintr(__func, *args, **kwargs): return None +class GenericTests(unittest.TestCase): + + @unittest.skipIf(threading is None, "test needs threading module") + def test_enums(self): + for name in dir(signal): + sig = getattr(signal, name) + if name in {'SIG_DFL', 'SIG_IGN'}: + self.assertIsInstance(sig, signal.Handlers) + elif name in {'SIG_BLOCK', 'SIG_UNBLOCK', 'SIG_SETMASK'}: + self.assertIsInstance(sig, signal.Sigmasks) + elif name.startswith('SIG') and not name.startswith('SIG_'): + self.assertIsInstance(sig, signal.Signals) + elif name.startswith('CTRL_'): + self.assertIsInstance(sig, signal.Signals) + self.assertEqual(sys.platform, "win32") + + @unittest.skipIf(sys.platform == "win32", "Not valid on Windows") class InterProcessSignalTests(unittest.TestCase): MAX_DURATION = 20 # Entire test should last at most 20 sec. @@ -195,6 +218,7 @@ class PosixTests(unittest.TestCase): def test_getsignal(self): hup = signal.signal(signal.SIGHUP, self.trivial_signal_handler) + self.assertIsInstance(hup, signal.Handlers) self.assertEqual(signal.getsignal(signal.SIGHUP), self.trivial_signal_handler) signal.signal(signal.SIGHUP, hup) @@ -229,15 +253,77 @@ class WakeupFDTests(unittest.TestCase): def test_invalid_fd(self): fd = support.make_bad_fd() - self.assertRaises(ValueError, signal.set_wakeup_fd, fd) + self.assertRaises((ValueError, OSError), + signal.set_wakeup_fd, fd) + + def test_invalid_socket(self): + sock = socket.socket() + fd = sock.fileno() + sock.close() + self.assertRaises((ValueError, OSError), + signal.set_wakeup_fd, fd) + + def test_set_wakeup_fd_result(self): + r1, w1 = os.pipe() + self.addCleanup(os.close, r1) + self.addCleanup(os.close, w1) + r2, w2 = os.pipe() + self.addCleanup(os.close, r2) + self.addCleanup(os.close, w2) + + if hasattr(os, 'set_blocking'): + os.set_blocking(w1, False) + os.set_blocking(w2, False) + + signal.set_wakeup_fd(w1) + self.assertEqual(signal.set_wakeup_fd(w2), w1) + self.assertEqual(signal.set_wakeup_fd(-1), w2) + self.assertEqual(signal.set_wakeup_fd(-1), -1) + + def test_set_wakeup_fd_socket_result(self): + sock1 = socket.socket() + self.addCleanup(sock1.close) + sock1.setblocking(False) + fd1 = sock1.fileno() + + sock2 = socket.socket() + self.addCleanup(sock2.close) + sock2.setblocking(False) + fd2 = sock2.fileno() + + signal.set_wakeup_fd(fd1) + self.assertEqual(signal.set_wakeup_fd(fd2), fd1) + self.assertEqual(signal.set_wakeup_fd(-1), fd2) + self.assertEqual(signal.set_wakeup_fd(-1), -1) + + # On Windows, files are always blocking and Windows does not provide a + # function to test if a socket is in non-blocking mode. + @unittest.skipIf(sys.platform == "win32", "tests specific to POSIX") + def test_set_wakeup_fd_blocking(self): + rfd, wfd = os.pipe() + self.addCleanup(os.close, rfd) + self.addCleanup(os.close, wfd) + + # fd must be non-blocking + os.set_blocking(wfd, True) + with self.assertRaises(ValueError) as cm: + signal.set_wakeup_fd(wfd) + self.assertEqual(str(cm.exception), + "the fd %s must be in non-blocking mode" % wfd) + + # non-blocking is ok + os.set_blocking(wfd, False) + signal.set_wakeup_fd(wfd) + signal.set_wakeup_fd(-1) @unittest.skipIf(sys.platform == "win32", "Not valid on Windows") class WakeupSignalTests(unittest.TestCase): + @unittest.skipIf(_testcapi is None, 'need _testcapi') def check_wakeup(self, test_body, *signals, ordered=True): # use a subprocess to have only one thread code = """if 1: - import fcntl + import _testcapi import os import signal import struct @@ -260,10 +346,7 @@ class WakeupSignalTests(unittest.TestCase): signal.signal(signal.SIGALRM, handler) read, write = os.pipe() - for fd in (read, write): - flags = fcntl.fcntl(fd, fcntl.F_GETFL, 0) - flags = flags | os.O_NONBLOCK - fcntl.fcntl(fd, fcntl.F_SETFL, flags) + os.set_blocking(write, False) signal.set_wakeup_fd(write) test() @@ -271,21 +354,21 @@ class WakeupSignalTests(unittest.TestCase): os.close(read) os.close(write) - """.format(signals, ordered, test_body) + """.format(tuple(map(int, signals)), ordered, test_body) assert_python_ok('-c', code) + @unittest.skipIf(_testcapi is None, 'need _testcapi') def test_wakeup_write_error(self): # Issue #16105: write() errors in the C signal handler should not # pass silently. # Use a subprocess to have only one thread. code = """if 1: + import _testcapi import errno - import fcntl import os import signal import sys - import time from test.support import captured_stderr def handler(signum, frame): @@ -293,15 +376,13 @@ class WakeupSignalTests(unittest.TestCase): signal.signal(signal.SIGALRM, handler) r, w = os.pipe() - flags = fcntl.fcntl(r, fcntl.F_GETFL, 0) - fcntl.fcntl(r, fcntl.F_SETFL, flags | os.O_NONBLOCK) + os.set_blocking(r, False) # Set wakeup_fd a read-only file descriptor to trigger the error signal.set_wakeup_fd(r) try: with captured_stderr() as err: - signal.alarm(1) - time.sleep(5.0) + _testcapi.raise_signal(signal.SIGALRM) except ZeroDivisionError: # An ignored exception should have been printed out on stderr err = err.getvalue() @@ -312,6 +393,9 @@ class WakeupSignalTests(unittest.TestCase): raise AssertionError(err) else: raise AssertionError("ZeroDivisionError not raised") + + os.close(r) + os.close(w) """ r, w = os.pipe() try: @@ -375,9 +459,10 @@ class WakeupSignalTests(unittest.TestCase): def test_signum(self): self.check_wakeup("""def test(): + import _testcapi signal.signal(signal.SIGUSR1, handler) - os.kill(os.getpid(), signal.SIGUSR1) - os.kill(os.getpid(), signal.SIGALRM) + _testcapi.raise_signal(signal.SIGUSR1) + _testcapi.raise_signal(signal.SIGALRM) """, signal.SIGUSR1, signal.SIGALRM) @unittest.skipUnless(hasattr(signal, 'pthread_sigmask'), @@ -391,13 +476,97 @@ class WakeupSignalTests(unittest.TestCase): signal.signal(signum2, handler) signal.pthread_sigmask(signal.SIG_BLOCK, (signum1, signum2)) - os.kill(os.getpid(), signum1) - os.kill(os.getpid(), signum2) + _testcapi.raise_signal(signum1) + _testcapi.raise_signal(signum2) # Unblocking the 2 signals calls the C signal handler twice signal.pthread_sigmask(signal.SIG_UNBLOCK, (signum1, signum2)) """, signal.SIGUSR1, signal.SIGUSR2, ordered=False) +@unittest.skipUnless(hasattr(socket, 'socketpair'), 'need socket.socketpair') +class WakeupSocketSignalTests(unittest.TestCase): + + @unittest.skipIf(_testcapi is None, 'need _testcapi') + def test_socket(self): + # use a subprocess to have only one thread + code = """if 1: + import signal + import socket + import struct + import _testcapi + + signum = signal.SIGINT + signals = (signum,) + + def handler(signum, frame): + pass + + signal.signal(signum, handler) + + read, write = socket.socketpair() + read.setblocking(False) + write.setblocking(False) + signal.set_wakeup_fd(write.fileno()) + + _testcapi.raise_signal(signum) + + data = read.recv(1) + if not data: + raise Exception("no signum written") + raised = struct.unpack('B', data) + if raised != signals: + raise Exception("%r != %r" % (raised, signals)) + + read.close() + write.close() + """ + + assert_python_ok('-c', code) + + @unittest.skipIf(_testcapi is None, 'need _testcapi') + def test_send_error(self): + # Use a subprocess to have only one thread. + if os.name == 'nt': + action = 'send' + else: + action = 'write' + code = """if 1: + import errno + import signal + import socket + import sys + import time + import _testcapi + from test.support import captured_stderr + + signum = signal.SIGINT + + def handler(signum, frame): + pass + + signal.signal(signum, handler) + + read, write = socket.socketpair() + read.setblocking(False) + write.setblocking(False) + + signal.set_wakeup_fd(write.fileno()) + + # Close sockets: send() will fail + read.close() + write.close() + + with captured_stderr() as err: + _testcapi.raise_signal(signum) + + err = err.getvalue() + if ('Exception ignored when trying to {action} to the signal wakeup fd' + not in err): + raise AssertionError(err) + """.format(action=action) + assert_python_ok('-c', code) + + @unittest.skipIf(sys.platform == "win32", "Not valid on Windows") class SiginterruptTest(unittest.TestCase): @@ -428,18 +597,22 @@ class SiginterruptTest(unittest.TestCase): sys.stdout.flush() # run the test twice - for loop in range(2): - # send a SIGALRM in a second (during the read) - signal.alarm(1) - try: - # blocking call: read from a pipe without data - os.read(r, 1) - except OSError as err: - if err.errno != errno.EINTR: - raise - else: - sys.exit(2) - sys.exit(3) + try: + for loop in range(2): + # send a SIGALRM in a second (during the read) + signal.alarm(1) + try: + # blocking call: read from a pipe without data + os.read(r, 1) + except OSError as err: + if err.errno != errno.EINTR: + raise + else: + sys.exit(2) + sys.exit(3) + finally: + os.close(r) + os.close(w) """ % (interrupt,) with spawn_python('-c', code) as process: try: @@ -604,6 +777,8 @@ class PendingSignalsTests(unittest.TestCase): signal.pthread_sigmask(signal.SIG_BLOCK, [signum]) os.kill(os.getpid(), signum) pending = signal.sigpending() + for sig in pending: + assert isinstance(sig, signal.Signals), repr(pending) if pending != {signum}: raise Exception('%s != {%s}' % (pending, signum)) try: @@ -660,6 +835,7 @@ class PendingSignalsTests(unittest.TestCase): code = '''if 1: import signal import sys + from signal import Signals def handler(signum, frame): 1/0 @@ -702,6 +878,7 @@ class PendingSignalsTests(unittest.TestCase): def test(signum): signal.alarm(1) received = signal.sigwait([signum]) + assert isinstance(received, signal.Signals), received if received != signum: raise Exception('received %s, not %s' % (received, signum)) ''') @@ -842,8 +1019,14 @@ class PendingSignalsTests(unittest.TestCase): def kill(signum): os.kill(os.getpid(), signum) + def check_mask(mask): + for sig in mask: + assert isinstance(sig, signal.Signals), repr(sig) + def read_sigmask(): - return signal.pthread_sigmask(signal.SIG_BLOCK, []) + sigmask = signal.pthread_sigmask(signal.SIG_BLOCK, []) + check_mask(sigmask) + return sigmask signum = signal.SIGUSR1 @@ -852,6 +1035,7 @@ class PendingSignalsTests(unittest.TestCase): # Unblock SIGUSR1 (and copy the old mask) to test our signal handler old_mask = signal.pthread_sigmask(signal.SIG_UNBLOCK, [signum]) + check_mask(old_mask) try: kill(signum) except ZeroDivisionError: @@ -861,11 +1045,13 @@ class PendingSignalsTests(unittest.TestCase): # Block and then raise SIGUSR1. The signal is blocked: the signal # handler is not called, and the signal is now pending - signal.pthread_sigmask(signal.SIG_BLOCK, [signum]) + mask = signal.pthread_sigmask(signal.SIG_BLOCK, [signum]) + check_mask(mask) kill(signum) # Check the new mask blocked = read_sigmask() + check_mask(blocked) if signum not in blocked: raise Exception("%s not in %s" % (signum, blocked)) if old_mask ^ blocked != {signum}: @@ -928,8 +1114,9 @@ class PendingSignalsTests(unittest.TestCase): def test_main(): try: - support.run_unittest(PosixTests, InterProcessSignalTests, + support.run_unittest(GenericTests, PosixTests, InterProcessSignalTests, WakeupFDTests, WakeupSignalTests, + WakeupSocketSignalTests, SiginterruptTest, ItimerTest, WindowsSignalTests, PendingSignalsTests) finally: diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index 8325222..79af49c 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -147,7 +147,7 @@ class HelperFunctionsTests(unittest.TestCase): re.escape(os.path.join(pth_dir, pth_fn))) # XXX: ditto previous XXX comment. self.assertRegex(err_out.getvalue(), 'Traceback') - self.assertRegex(err_out.getvalue(), 'TypeError') + self.assertRegex(err_out.getvalue(), 'ValueError') def test_addsitedir(self): # Same tests for test_addpackage since addsitedir() essentially just @@ -235,20 +235,18 @@ class HelperFunctionsTests(unittest.TestCase): # OS X framework builds site.PREFIXES = ['Python.framework'] dirs = site.getsitepackages() - self.assertEqual(len(dirs), 3) + self.assertEqual(len(dirs), 2) wanted = os.path.join('/Library', sysconfig.get_config_var("PYTHONFRAMEWORK"), sys.version[:3], 'site-packages') - self.assertEqual(dirs[2], wanted) + self.assertEqual(dirs[1], wanted) elif os.sep == '/': # OS X non-framwework builds, Linux, FreeBSD, etc - self.assertEqual(len(dirs), 2) + self.assertEqual(len(dirs), 1) wanted = os.path.join('xoxo', 'lib', 'python' + sys.version[:3], 'site-packages') self.assertEqual(dirs[0], wanted) - wanted = os.path.join('xoxo', 'lib', 'site-python') - self.assertEqual(dirs[1], wanted) else: # other platforms self.assertEqual(len(dirs), 2) diff --git a/Lib/test/test_smtpd.py b/Lib/test/test_smtpd.py index 93f14c4..6eb47f1 100644 --- a/Lib/test/test_smtpd.py +++ b/Lib/test/test_smtpd.py @@ -1,4 +1,5 @@ import unittest +import textwrap from test import support, mock_socket import socket import io @@ -7,15 +8,22 @@ import asyncore class DummyServer(smtpd.SMTPServer): - def __init__(self, localaddr, remoteaddr): - smtpd.SMTPServer.__init__(self, localaddr, remoteaddr) + def __init__(self, *args, **kwargs): + smtpd.SMTPServer.__init__(self, *args, **kwargs) self.messages = [] + if self._decode_data: + self.return_status = 'return status' + else: + self.return_status = b'return status' def process_message(self, peer, mailfrom, rcpttos, data): self.messages.append((peer, mailfrom, rcpttos, data)) - if data == 'return status': + if data == self.return_status: return '250 Okish' + def process_smtputf8_message(self, *args, **kwargs): + return '250 SMTPUTF8 message okish' + class DummyDispatcherBroken(Exception): pass @@ -31,9 +39,10 @@ class SMTPDServerTest(unittest.TestCase): smtpd.socket = asyncore.socket = mock_socket def test_process_message_unimplemented(self): - server = smtpd.SMTPServer('a', 'b') + server = smtpd.SMTPServer((support.HOST, 0), ('b', 0), + decode_data=True) conn, addr = server.accept() - channel = smtpd.SMTPChannel(server, conn, addr) + channel = smtpd.SMTPChannel(server, conn, addr, decode_data=True) def write_line(line): channel.socket.queue_recv(line) @@ -45,19 +54,163 @@ class SMTPDServerTest(unittest.TestCase): write_line(b'DATA') self.assertRaises(NotImplementedError, write_line, b'spam\r\n.\r\n') + def test_process_smtputf8_message_unimplemented(self): + server = smtpd.SMTPServer((support.HOST, 0), ('b', 0), + enable_SMTPUTF8=True) + conn, addr = server.accept() + channel = smtpd.SMTPChannel(server, conn, addr, enable_SMTPUTF8=True) + + def write_line(line): + channel.socket.queue_recv(line) + channel.handle_read() + + write_line(b'EHLO example') + write_line(b'MAIL From: <eggs@example> BODY=8BITMIME SMTPUTF8') + write_line(b'RCPT To: <spam@example>') + write_line(b'DATA') + self.assertRaises(NotImplementedError, write_line, b'spam\r\n.\r\n') + + def test_decode_data_default_warns(self): + with self.assertWarns(DeprecationWarning): + smtpd.SMTPServer((support.HOST, 0), ('b', 0)) + + def test_decode_data_and_enable_SMTPUTF8_raises(self): + self.assertRaises( + ValueError, + smtpd.SMTPServer, + (support.HOST, 0), + ('b', 0), + enable_SMTPUTF8=True, + decode_data=True) + + def tearDown(self): + asyncore.close_all() + asyncore.socket = smtpd.socket = socket + + +class DebuggingServerTest(unittest.TestCase): + + def setUp(self): + smtpd.socket = asyncore.socket = mock_socket + + def send_data(self, channel, data, enable_SMTPUTF8=False): + def write_line(line): + channel.socket.queue_recv(line) + channel.handle_read() + write_line(b'EHLO example') + if enable_SMTPUTF8: + write_line(b'MAIL From:eggs@example BODY=8BITMIME SMTPUTF8') + else: + write_line(b'MAIL From:eggs@example') + write_line(b'RCPT To:spam@example') + write_line(b'DATA') + write_line(data) + write_line(b'.') + + def test_process_message_with_decode_data_true(self): + server = smtpd.DebuggingServer((support.HOST, 0), ('b', 0), + decode_data=True) + conn, addr = server.accept() + channel = smtpd.SMTPChannel(server, conn, addr, decode_data=True) + with support.captured_stdout() as s: + self.send_data(channel, b'From: test\n\nhello\n') + stdout = s.getvalue() + self.assertEqual(stdout, textwrap.dedent("""\ + ---------- MESSAGE FOLLOWS ---------- + From: test + X-Peer: peer-address + + hello + ------------ END MESSAGE ------------ + """)) + + def test_process_message_with_decode_data_false(self): + server = smtpd.DebuggingServer((support.HOST, 0), ('b', 0), + decode_data=False) + conn, addr = server.accept() + channel = smtpd.SMTPChannel(server, conn, addr, decode_data=False) + with support.captured_stdout() as s: + self.send_data(channel, b'From: test\n\nh\xc3\xa9llo\xff\n') + stdout = s.getvalue() + self.assertEqual(stdout, textwrap.dedent("""\ + ---------- MESSAGE FOLLOWS ---------- + b'From: test' + b'X-Peer: peer-address' + b'' + b'h\\xc3\\xa9llo\\xff' + ------------ END MESSAGE ------------ + """)) + + def test_process_message_with_enable_SMTPUTF8_true(self): + server = smtpd.DebuggingServer((support.HOST, 0), ('b', 0), + enable_SMTPUTF8=True) + conn, addr = server.accept() + channel = smtpd.SMTPChannel(server, conn, addr, enable_SMTPUTF8=True) + with support.captured_stdout() as s: + self.send_data(channel, b'From: test\n\nh\xc3\xa9llo\xff\n') + stdout = s.getvalue() + self.assertEqual(stdout, textwrap.dedent("""\ + ---------- MESSAGE FOLLOWS ---------- + b'From: test' + b'X-Peer: peer-address' + b'' + b'h\\xc3\\xa9llo\\xff' + ------------ END MESSAGE ------------ + """)) + + def test_process_SMTPUTF8_message_with_enable_SMTPUTF8_true(self): + server = smtpd.DebuggingServer((support.HOST, 0), ('b', 0), + enable_SMTPUTF8=True) + conn, addr = server.accept() + channel = smtpd.SMTPChannel(server, conn, addr, enable_SMTPUTF8=True) + with support.captured_stdout() as s: + self.send_data(channel, b'From: test\n\nh\xc3\xa9llo\xff\n', + enable_SMTPUTF8=True) + stdout = s.getvalue() + self.assertEqual(stdout, textwrap.dedent("""\ + ----- SMTPUTF8 MESSAGE FOLLOWS ------ + b'From: test' + b'X-Peer: peer-address' + b'' + b'h\\xc3\\xa9llo\\xff' + ------------ END MESSAGE ------------ + """)) + def tearDown(self): asyncore.close_all() asyncore.socket = smtpd.socket = socket +class TestFamilyDetection(unittest.TestCase): + def setUp(self): + smtpd.socket = asyncore.socket = mock_socket + + def tearDown(self): + asyncore.close_all() + asyncore.socket = smtpd.socket = socket + + @unittest.skipUnless(support.IPV6_ENABLED, "IPv6 not enabled") + def test_socket_uses_IPv6(self): + server = smtpd.SMTPServer((support.HOSTv6, 0), (support.HOST, 0), + decode_data=False) + self.assertEqual(server.socket.family, socket.AF_INET6) + + def test_socket_uses_IPv4(self): + server = smtpd.SMTPServer((support.HOST, 0), (support.HOSTv6, 0), + decode_data=False) + self.assertEqual(server.socket.family, socket.AF_INET) + + class SMTPDChannelTest(unittest.TestCase): def setUp(self): smtpd.socket = asyncore.socket = mock_socket self.old_debugstream = smtpd.DEBUGSTREAM self.debug = smtpd.DEBUGSTREAM = io.StringIO() - self.server = DummyServer('a', 'b') + self.server = DummyServer((support.HOST, 0), ('b', 0), + decode_data=True) conn, addr = self.server.accept() - self.channel = smtpd.SMTPChannel(self.server, conn, addr) + self.channel = smtpd.SMTPChannel(self.server, conn, addr, + decode_data=True) def tearDown(self): asyncore.close_all() @@ -69,7 +222,9 @@ class SMTPDChannelTest(unittest.TestCase): self.channel.handle_read() def test_broken_connect(self): - self.assertRaises(DummyDispatcherBroken, BrokenDummyServer, 'a', 'b') + self.assertRaises( + DummyDispatcherBroken, BrokenDummyServer, + (support.HOST, 0), ('b', 0), decode_data=True) def test_server_accept(self): self.server.handle_accept() @@ -214,6 +369,12 @@ class SMTPDChannelTest(unittest.TestCase): self.assertEqual(self.channel.socket.last, b'500 Error: line too long\r\n') + def test_MAIL_command_rejects_SMTPUTF8_by_default(self): + self.write_line(b'EHLO example') + self.write_line( + b'MAIL from: <naive@example.com> BODY=8BITMIME SMTPUTF8') + self.assertEqual(self.channel.socket.last[0:1], b'5') + def test_data_longer_than_default_data_size_limit(self): # Hack the default so we don't have to generate so much data. self.channel.data_size_limit = 1048 @@ -387,7 +548,10 @@ class SMTPDChannelTest(unittest.TestCase): self.write_line(b'data\r\nmore\r\n.') self.assertEqual(self.channel.socket.last, b'250 OK\r\n') self.assertEqual(self.server.messages, - [('peer', 'eggs@example', ['spam@example'], 'data\nmore')]) + [(('peer-address', 'peer-port'), + 'eggs@example', + ['spam@example'], + 'data\nmore')]) def test_DATA_syntax(self): self.write_line(b'HELO example') @@ -417,7 +581,10 @@ class SMTPDChannelTest(unittest.TestCase): self.write_line(b'DATA') self.write_line(b'data\r\n.') self.assertEqual(self.server.messages, - [('peer', 'eggs@example', ['spam@example','ham@example'], 'data')]) + [(('peer-address', 'peer-port'), + 'eggs@example', + ['spam@example','ham@example'], + 'data')]) def test_manual_status(self): # checks that the Channel is able to return a custom status message @@ -439,7 +606,10 @@ class SMTPDChannelTest(unittest.TestCase): self.write_line(b'DATA') self.write_line(b'data\r\n.') self.assertEqual(self.server.messages, - [('peer', 'foo@example', ['eggs@example'], 'data')]) + [(('peer-address', 'peer-port'), + 'foo@example', + ['eggs@example'], + 'data')]) def test_HELO_RSET(self): self.write_line(b'HELO example') @@ -502,6 +672,24 @@ class SMTPDChannelTest(unittest.TestCase): with support.check_warnings(('', DeprecationWarning)): self.channel._SMTPChannel__addr = 'spam' + def test_decode_data_default_warning(self): + with self.assertWarns(DeprecationWarning): + server = DummyServer((support.HOST, 0), ('b', 0)) + conn, addr = self.server.accept() + with self.assertWarns(DeprecationWarning): + smtpd.SMTPChannel(server, conn, addr) + +@unittest.skipUnless(support.IPV6_ENABLED, "IPv6 not enabled") +class SMTPDChannelIPv6Test(SMTPDChannelTest): + def setUp(self): + smtpd.socket = asyncore.socket = mock_socket + self.old_debugstream = smtpd.DEBUGSTREAM + self.debug = smtpd.DEBUGSTREAM = io.StringIO() + self.server = DummyServer((support.HOSTv6, 0), ('b', 0), + decode_data=True) + conn, addr = self.server.accept() + self.channel = smtpd.SMTPChannel(self.server, conn, addr, + decode_data=True) class SMTPDChannelWithDataSizeLimitTest(unittest.TestCase): @@ -509,10 +697,12 @@ class SMTPDChannelWithDataSizeLimitTest(unittest.TestCase): smtpd.socket = asyncore.socket = mock_socket self.old_debugstream = smtpd.DEBUGSTREAM self.debug = smtpd.DEBUGSTREAM = io.StringIO() - self.server = DummyServer('a', 'b') + self.server = DummyServer((support.HOST, 0), ('b', 0), + decode_data=True) conn, addr = self.server.accept() # Set DATA size limit to 32 bytes for easy testing - self.channel = smtpd.SMTPChannel(self.server, conn, addr, 32) + self.channel = smtpd.SMTPChannel(self.server, conn, addr, 32, + decode_data=True) def tearDown(self): asyncore.close_all() @@ -536,7 +726,10 @@ class SMTPDChannelWithDataSizeLimitTest(unittest.TestCase): self.write_line(b'data\r\nmore\r\n.') self.assertEqual(self.channel.socket.last, b'250 OK\r\n') self.assertEqual(self.server.messages, - [('peer', 'eggs@example', ['spam@example'], 'data\nmore')]) + [(('peer-address', 'peer-port'), + 'eggs@example', + ['spam@example'], + 'data\nmore')]) def test_data_limit_dialog_too_much_data(self): self.write_line(b'HELO example') @@ -553,5 +746,181 @@ class SMTPDChannelWithDataSizeLimitTest(unittest.TestCase): b'552 Error: Too much mail data\r\n') +class SMTPDChannelWithDecodeDataFalse(unittest.TestCase): + + def setUp(self): + smtpd.socket = asyncore.socket = mock_socket + self.old_debugstream = smtpd.DEBUGSTREAM + self.debug = smtpd.DEBUGSTREAM = io.StringIO() + self.server = DummyServer((support.HOST, 0), ('b', 0), + decode_data=False) + conn, addr = self.server.accept() + # Set decode_data to False + self.channel = smtpd.SMTPChannel(self.server, conn, addr, + decode_data=False) + + def tearDown(self): + asyncore.close_all() + asyncore.socket = smtpd.socket = socket + smtpd.DEBUGSTREAM = self.old_debugstream + + def write_line(self, line): + self.channel.socket.queue_recv(line) + self.channel.handle_read() + + def test_ascii_data(self): + self.write_line(b'HELO example') + self.write_line(b'MAIL From:eggs@example') + self.write_line(b'RCPT To:spam@example') + self.write_line(b'DATA') + self.write_line(b'plain ascii text') + self.write_line(b'.') + self.assertEqual(self.channel.received_data, b'plain ascii text') + + def test_utf8_data(self): + self.write_line(b'HELO example') + self.write_line(b'MAIL From:eggs@example') + self.write_line(b'RCPT To:spam@example') + self.write_line(b'DATA') + self.write_line(b'utf8 enriched text: \xc5\xbc\xc5\xba\xc4\x87') + self.write_line(b'and some plain ascii') + self.write_line(b'.') + self.assertEqual( + self.channel.received_data, + b'utf8 enriched text: \xc5\xbc\xc5\xba\xc4\x87\n' + b'and some plain ascii') + + +class SMTPDChannelWithDecodeDataTrue(unittest.TestCase): + + def setUp(self): + smtpd.socket = asyncore.socket = mock_socket + self.old_debugstream = smtpd.DEBUGSTREAM + self.debug = smtpd.DEBUGSTREAM = io.StringIO() + self.server = DummyServer((support.HOST, 0), ('b', 0), + decode_data=True) + conn, addr = self.server.accept() + # Set decode_data to True + self.channel = smtpd.SMTPChannel(self.server, conn, addr, + decode_data=True) + + def tearDown(self): + asyncore.close_all() + asyncore.socket = smtpd.socket = socket + smtpd.DEBUGSTREAM = self.old_debugstream + + def write_line(self, line): + self.channel.socket.queue_recv(line) + self.channel.handle_read() + + def test_ascii_data(self): + self.write_line(b'HELO example') + self.write_line(b'MAIL From:eggs@example') + self.write_line(b'RCPT To:spam@example') + self.write_line(b'DATA') + self.write_line(b'plain ascii text') + self.write_line(b'.') + self.assertEqual(self.channel.received_data, 'plain ascii text') + + def test_utf8_data(self): + self.write_line(b'HELO example') + self.write_line(b'MAIL From:eggs@example') + self.write_line(b'RCPT To:spam@example') + self.write_line(b'DATA') + self.write_line(b'utf8 enriched text: \xc5\xbc\xc5\xba\xc4\x87') + self.write_line(b'and some plain ascii') + self.write_line(b'.') + self.assertEqual( + self.channel.received_data, + 'utf8 enriched text: żźć\nand some plain ascii') + + +class SMTPDChannelTestWithEnableSMTPUTF8True(unittest.TestCase): + def setUp(self): + smtpd.socket = asyncore.socket = mock_socket + self.old_debugstream = smtpd.DEBUGSTREAM + self.debug = smtpd.DEBUGSTREAM = io.StringIO() + self.server = DummyServer((support.HOST, 0), ('b', 0), + enable_SMTPUTF8=True) + conn, addr = self.server.accept() + self.channel = smtpd.SMTPChannel(self.server, conn, addr, + enable_SMTPUTF8=True) + + def tearDown(self): + asyncore.close_all() + asyncore.socket = smtpd.socket = socket + smtpd.DEBUGSTREAM = self.old_debugstream + + def write_line(self, line): + self.channel.socket.queue_recv(line) + self.channel.handle_read() + + def test_MAIL_command_accepts_SMTPUTF8_when_announced(self): + self.write_line(b'EHLO example') + self.write_line( + 'MAIL from: <naïve@example.com> BODY=8BITMIME SMTPUTF8'.encode( + 'utf-8') + ) + self.assertEqual(self.channel.socket.last, b'250 OK\r\n') + + def test_process_smtputf8_message(self): + self.write_line(b'EHLO example') + for mail_parameters in [b'', b'BODY=8BITMIME SMTPUTF8']: + self.write_line(b'MAIL from: <a@example> ' + mail_parameters) + self.assertEqual(self.channel.socket.last[0:3], b'250') + self.write_line(b'rcpt to:<b@example.com>') + self.assertEqual(self.channel.socket.last[0:3], b'250') + self.write_line(b'data') + self.assertEqual(self.channel.socket.last[0:3], b'354') + self.write_line(b'c\r\n.') + if mail_parameters == b'': + self.assertEqual(self.channel.socket.last, b'250 OK\r\n') + else: + self.assertEqual(self.channel.socket.last, + b'250 SMTPUTF8 message okish\r\n') + + def test_utf8_data(self): + self.write_line(b'EHLO example') + self.write_line( + 'MAIL From: naïve@examplé BODY=8BITMIME SMTPUTF8'.encode('utf-8')) + self.assertEqual(self.channel.socket.last[0:3], b'250') + self.write_line('RCPT To:späm@examplé'.encode('utf-8')) + self.assertEqual(self.channel.socket.last[0:3], b'250') + self.write_line(b'DATA') + self.assertEqual(self.channel.socket.last[0:3], b'354') + self.write_line(b'utf8 enriched text: \xc5\xbc\xc5\xba\xc4\x87') + self.write_line(b'.') + self.assertEqual( + self.channel.received_data, + b'utf8 enriched text: \xc5\xbc\xc5\xba\xc4\x87') + + def test_MAIL_command_limit_extended_with_SIZE_and_SMTPUTF8(self): + self.write_line(b'ehlo example') + fill_len = (512 + 26 + 10) - len('mail from:<@example>') + self.write_line(b'MAIL from:<' + + b'a' * (fill_len + 1) + + b'@example>') + self.assertEqual(self.channel.socket.last, + b'500 Error: line too long\r\n') + self.write_line(b'MAIL from:<' + + b'a' * fill_len + + b'@example>') + self.assertEqual(self.channel.socket.last, b'250 OK\r\n') + + def test_multiple_emails_with_extended_command_length(self): + self.write_line(b'ehlo example') + fill_len = (512 + 26 + 10) - len('mail from:<@example>') + for char in [b'a', b'b', b'c']: + self.write_line(b'MAIL from:<' + char * fill_len + b'a@example>') + self.assertEqual(self.channel.socket.last[0:3], b'500') + self.write_line(b'MAIL from:<' + char * fill_len + b'@example>') + self.assertEqual(self.channel.socket.last[0:3], b'250') + self.write_line(b'rcpt to:<hans@example.com>') + self.assertEqual(self.channel.socket.last[0:3], b'250') + self.write_line(b'data') + self.assertEqual(self.channel.socket.last[0:3], b'354') + self.write_line(b'test\r\n.') + self.assertEqual(self.channel.socket.last[0:3], b'250') + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_smtplib.py b/Lib/test/test_smtplib.py index 95a9dbe..5f12d28 100644 --- a/Lib/test/test_smtplib.py +++ b/Lib/test/test_smtplib.py @@ -10,6 +10,7 @@ import sys import time import select import errno +import base64 import unittest from test import support, mock_socket @@ -30,7 +31,7 @@ if sys.platform == 'darwin': def server(evt, buf, serv): - serv.listen(5) + serv.listen() evt.set() try: conn, addr = serv.accept() @@ -184,7 +185,8 @@ class DebuggingServerTests(unittest.TestCase): self.old_DEBUGSTREAM = smtpd.DEBUGSTREAM smtpd.DEBUGSTREAM = io.StringIO() # Pick a random unused port by passing 0 for the port number - self.serv = smtpd.DebuggingServer((HOST, 0), ('nowhere', -1)) + self.serv = smtpd.DebuggingServer((HOST, 0), ('nowhere', -1), + decode_data=True) # Keep a note of what port was assigned self.port = self.serv.socket.getsockname()[1] serv_args = (self.serv, self.serv_evt, self.client_evt) @@ -604,7 +606,8 @@ sim_auth_credentials = { 'cram-md5': ('TXIUQUBZB21LD2HLCMUUY29TIDG4OWQ0MJ' 'KWZGQ4ODNMNDA4NTGXMDRLZWMYZJDMODG1'), } -sim_auth_login_password = 'C29TZXBHC3N3B3JK' +sim_auth_login_user = 'TXIUQUBZB21LD2HLCMUUY29T' +sim_auth_plain = 'AE1YLKFAC29TZXDOZXJLLMNVBQBZB21LCGFZC3DVCMQ=' sim_lists = {'list-1':['Mr.A@somewhere.com','Mrs.C@somewhereesle.com'], 'list-2':['Ms.B@xn--fo-fka.com',], @@ -658,18 +661,16 @@ class SimSMTPChannel(smtpd.SMTPChannel): self.push('550 No access for you!') def smtp_AUTH(self, arg): - if arg.strip().lower()=='cram-md5': + mech = arg.strip().lower() + if mech=='cram-md5': self.push('334 {}'.format(sim_cram_md5_challenge)) - return - mech, auth = arg.split() - mech = mech.lower() - if mech not in sim_auth_credentials: + elif mech not in sim_auth_credentials: self.push('504 auth type unimplemented') return - if mech == 'plain' and auth==sim_auth_credentials['plain']: - self.push('235 plain auth ok') - elif mech=='login' and auth==sim_auth_credentials['login']: - self.push('334 Password:') + elif mech=='plain': + self.push('334 ') + elif mech=='login': + self.push('334 ') else: self.push('550 No access for you!') @@ -719,7 +720,8 @@ class SimSMTPServer(smtpd.SMTPServer): def handle_accepted(self, conn, addr): self._SMTPchannel = self.channel_class( - self._extra_features, self, conn, addr) + self._extra_features, self, conn, addr, + decode_data=self._decode_data) def process_message(self, peer, mailfrom, rcpttos, data): pass @@ -742,7 +744,7 @@ class SMTPSimTests(unittest.TestCase): self.serv_evt = threading.Event() self.client_evt = threading.Event() # Pick a random unused port by passing 0 for the port number - self.serv = SimSMTPServer((HOST, 0), ('nowhere', -1)) + self.serv = SimSMTPServer((HOST, 0), ('nowhere', -1), decode_data=True) # Keep a note of what port was assigned self.port = self.serv.socket.getsockname()[1] serv_args = (self.serv, self.serv_evt, self.client_evt) @@ -816,28 +818,28 @@ class SMTPSimTests(unittest.TestCase): self.assertEqual(smtp.expn(u), expected_unknown) smtp.quit() - def testAUTH_PLAIN(self): - self.serv.add_feature("AUTH PLAIN") - smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=15) - - expected_auth_ok = (235, b'plain auth ok') - self.assertEqual(smtp.login(sim_auth[0], sim_auth[1]), expected_auth_ok) - smtp.close() - - # SimSMTPChannel doesn't fully support LOGIN or CRAM-MD5 auth because they - # require a synchronous read to obtain the credentials...so instead smtpd + # SimSMTPChannel doesn't fully support AUTH because it requires a + # synchronous read to obtain the credentials...so instead smtpd # sees the credential sent by smtplib's login method as an unknown command, # which results in smtplib raising an auth error. Fortunately the error # message contains the encoded credential, so we can partially check that it # was generated correctly (partially, because the 'word' is uppercased in # the error message). + def testAUTH_PLAIN(self): + self.serv.add_feature("AUTH PLAIN") + smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=15) + try: smtp.login(sim_auth[0], sim_auth[1]) + except smtplib.SMTPAuthenticationError as err: + self.assertIn(sim_auth_plain, str(err)) + smtp.close() + def testAUTH_LOGIN(self): self.serv.add_feature("AUTH LOGIN") smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=15) try: smtp.login(sim_auth[0], sim_auth[1]) except smtplib.SMTPAuthenticationError as err: - self.assertIn(sim_auth_login_password, str(err)) + self.assertIn(sim_auth_login_user, str(err)) smtp.close() def testAUTH_CRAM_MD5(self): @@ -855,7 +857,23 @@ class SMTPSimTests(unittest.TestCase): smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=15) try: smtp.login(sim_auth[0], sim_auth[1]) except smtplib.SMTPAuthenticationError as err: - self.assertIn(sim_auth_login_password, str(err)) + self.assertIn(sim_auth_login_user, str(err)) + smtp.close() + + def test_auth_function(self): + smtp = smtplib.SMTP(HOST, self.port, + local_hostname='localhost', timeout=15) + self.serv.add_feature("AUTH CRAM-MD5") + smtp.user, smtp.password = sim_auth[0], sim_auth[1] + supported = {'CRAM-MD5': smtp.auth_cram_md5, + 'PLAIN': smtp.auth_plain, + 'LOGIN': smtp.auth_login, + } + for mechanism, method in supported.items(): + try: smtp.auth(mechanism, method) + except smtplib.SMTPAuthenticationError as err: + self.assertIn(sim_auth_credentials[mechanism.lower()].upper(), + str(err)) smtp.close() def test_quit_resets_greeting(self): diff --git a/Lib/test/test_sndhdr.py b/Lib/test/test_sndhdr.py index 5e0abe0..426417c 100644 --- a/Lib/test/test_sndhdr.py +++ b/Lib/test/test_sndhdr.py @@ -1,4 +1,5 @@ import sndhdr +import pickle import unittest from test.support import findfile @@ -18,6 +19,19 @@ class TestFormats(unittest.TestCase): what = sndhdr.what(filename) self.assertNotEqual(what, None, filename) self.assertSequenceEqual(what, expected) + self.assertEqual(what.filetype, expected[0]) + self.assertEqual(what.framerate, expected[1]) + self.assertEqual(what.nchannels, expected[2]) + self.assertEqual(what.nframes, expected[3]) + self.assertEqual(what.sampwidth, expected[4]) + + def test_pickleable(self): + filename = findfile('sndhdr.aifc', subdir="sndhdrdata") + what = sndhdr.what(filename) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + dump = pickle.dumps(what, proto) + self.assertEqual(pickle.loads(dump), what) + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index b412386..9cd9dad 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -20,6 +20,8 @@ import signal import math import pickle import struct +import random +import string try: import multiprocessing except ImportError: @@ -76,7 +78,7 @@ class SocketTCPTest(unittest.TestCase): def setUp(self): self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.port = support.bind_port(self.serv) - self.serv.listen(1) + self.serv.listen() def tearDown(self): self.serv.close() @@ -445,7 +447,7 @@ class SocketListeningTestMixin(SocketTestBase): def setUp(self): super().setUp() - self.serv.listen(1) + self.serv.listen() class ThreadedSocketTestMixin(ThreadSafeCleanupTestCase, SocketTestBase, @@ -716,11 +718,11 @@ class GeneralModuleTests(unittest.TestCase): with self.assertRaises(TypeError) as cm: s.sendto('\u2620', sockname) self.assertEqual(str(cm.exception), - "'str' does not support the buffer interface") + "a bytes-like object is required, not 'str'") with self.assertRaises(TypeError) as cm: s.sendto(5j, sockname) self.assertEqual(str(cm.exception), - "'complex' does not support the buffer interface") + "a bytes-like object is required, not 'complex'") with self.assertRaises(TypeError) as cm: s.sendto(b'foo', None) self.assertIn('not NoneType',str(cm.exception)) @@ -728,11 +730,11 @@ class GeneralModuleTests(unittest.TestCase): with self.assertRaises(TypeError) as cm: s.sendto('\u2620', 0, sockname) self.assertEqual(str(cm.exception), - "'str' does not support the buffer interface") + "a bytes-like object is required, not 'str'") with self.assertRaises(TypeError) as cm: s.sendto(5j, 0, sockname) self.assertEqual(str(cm.exception), - "'complex' does not support the buffer interface") + "a bytes-like object is required, not 'complex'") with self.assertRaises(TypeError) as cm: s.sendto(b'foo', 0, None) self.assertIn('not NoneType', str(cm.exception)) @@ -1378,10 +1380,13 @@ class GeneralModuleTests(unittest.TestCase): def test_listen_backlog(self): for backlog in 0, -1: - srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as srv: + srv.bind((HOST, 0)) + srv.listen(backlog) + + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as srv: srv.bind((HOST, 0)) - srv.listen(backlog) - srv.close() + srv.listen() @support.cpython_only def test_listen_backlog_overflow(self): @@ -3731,8 +3736,6 @@ class TCPCloserTest(ThreadedTCPSocketTest): self.cli.connect((HOST, self.port)) time.sleep(1.0) -@unittest.skipUnless(hasattr(socket, 'socketpair'), - 'test needs socket.socketpair()') @unittest.skipUnless(thread, 'Threading required for this test.') class BasicSocketPairTest(SocketPairTest): @@ -3813,7 +3816,7 @@ class NonBlockingTCPTests(ThreadedTCPSocketTest): self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM | socket.SOCK_NONBLOCK) self.port = support.bind_port(self.serv) - self.serv.listen(1) + self.serv.listen() # actual testing start = time.time() try: @@ -4588,7 +4591,7 @@ class TestLinuxAbstractNamespace(unittest.TestCase): address = b"\x00python-test-hello\x00\xff" with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s1: s1.bind(address) - s1.listen(1) + s1.listen() with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s2: s2.connect(s1.getsockname()) with s1.accept()[0] as s3: @@ -4820,7 +4823,7 @@ class TIPCThreadableTest(unittest.TestCase, ThreadableTest): srvaddr = (socket.TIPC_ADDR_NAMESEQ, TIPC_STYPE, TIPC_LOWER, TIPC_UPPER) self.srv.bind(srvaddr) - self.srv.listen(5) + self.srv.listen() self.serverExplicitReady() self.conn, self.connaddr = self.srv.accept() self.addCleanup(self.conn.close) @@ -5109,6 +5112,275 @@ class TestSocketSharing(SocketTCPTest): source.close() +@unittest.skipUnless(thread, 'Threading required for this test.') +class SendfileUsingSendTest(ThreadedTCPSocketTest): + """ + Test the send() implementation of socket.sendfile(). + """ + + FILESIZE = (10 * 1024 * 1024) # 10MB + BUFSIZE = 8192 + FILEDATA = b"" + TIMEOUT = 2 + + @classmethod + def setUpClass(cls): + def chunks(total, step): + assert total >= step + while total > step: + yield step + total -= step + if total: + yield total + + chunk = b"".join([random.choice(string.ascii_letters).encode() + for i in range(cls.BUFSIZE)]) + with open(support.TESTFN, 'wb') as f: + for csize in chunks(cls.FILESIZE, cls.BUFSIZE): + f.write(chunk) + with open(support.TESTFN, 'rb') as f: + cls.FILEDATA = f.read() + assert len(cls.FILEDATA) == cls.FILESIZE + + @classmethod + def tearDownClass(cls): + support.unlink(support.TESTFN) + + def accept_conn(self): + self.serv.settimeout(self.TIMEOUT) + conn, addr = self.serv.accept() + conn.settimeout(self.TIMEOUT) + self.addCleanup(conn.close) + return conn + + def recv_data(self, conn): + received = [] + while True: + chunk = conn.recv(self.BUFSIZE) + if not chunk: + break + received.append(chunk) + return b''.join(received) + + def meth_from_sock(self, sock): + # Depending on the mixin class being run return either send() + # or sendfile() method implementation. + return getattr(sock, "_sendfile_use_send") + + # regular file + + def _testRegularFile(self): + address = self.serv.getsockname() + file = open(support.TESTFN, 'rb') + with socket.create_connection(address) as sock, file as file: + meth = self.meth_from_sock(sock) + sent = meth(file) + self.assertEqual(sent, self.FILESIZE) + self.assertEqual(file.tell(), self.FILESIZE) + + def testRegularFile(self): + conn = self.accept_conn() + data = self.recv_data(conn) + self.assertEqual(len(data), self.FILESIZE) + self.assertEqual(data, self.FILEDATA) + + # non regular file + + def _testNonRegularFile(self): + address = self.serv.getsockname() + file = io.BytesIO(self.FILEDATA) + with socket.create_connection(address) as sock, file as file: + sent = sock.sendfile(file) + self.assertEqual(sent, self.FILESIZE) + self.assertEqual(file.tell(), self.FILESIZE) + self.assertRaises(socket._GiveupOnSendfile, + sock._sendfile_use_sendfile, file) + + def testNonRegularFile(self): + conn = self.accept_conn() + data = self.recv_data(conn) + self.assertEqual(len(data), self.FILESIZE) + self.assertEqual(data, self.FILEDATA) + + # empty file + + def _testEmptyFileSend(self): + address = self.serv.getsockname() + filename = support.TESTFN + "2" + with open(filename, 'wb'): + self.addCleanup(support.unlink, filename) + file = open(filename, 'rb') + with socket.create_connection(address) as sock, file as file: + meth = self.meth_from_sock(sock) + sent = meth(file) + self.assertEqual(sent, 0) + self.assertEqual(file.tell(), 0) + + def testEmptyFileSend(self): + conn = self.accept_conn() + data = self.recv_data(conn) + self.assertEqual(data, b"") + + # offset + + def _testOffset(self): + address = self.serv.getsockname() + file = open(support.TESTFN, 'rb') + with socket.create_connection(address) as sock, file as file: + meth = self.meth_from_sock(sock) + sent = meth(file, offset=5000) + self.assertEqual(sent, self.FILESIZE - 5000) + self.assertEqual(file.tell(), self.FILESIZE) + + def testOffset(self): + conn = self.accept_conn() + data = self.recv_data(conn) + self.assertEqual(len(data), self.FILESIZE - 5000) + self.assertEqual(data, self.FILEDATA[5000:]) + + # count + + def _testCount(self): + address = self.serv.getsockname() + file = open(support.TESTFN, 'rb') + with socket.create_connection(address, timeout=2) as sock, file as file: + count = 5000007 + meth = self.meth_from_sock(sock) + sent = meth(file, count=count) + self.assertEqual(sent, count) + self.assertEqual(file.tell(), count) + + def testCount(self): + count = 5000007 + conn = self.accept_conn() + data = self.recv_data(conn) + self.assertEqual(len(data), count) + self.assertEqual(data, self.FILEDATA[:count]) + + # count small + + def _testCountSmall(self): + address = self.serv.getsockname() + file = open(support.TESTFN, 'rb') + with socket.create_connection(address, timeout=2) as sock, file as file: + count = 1 + meth = self.meth_from_sock(sock) + sent = meth(file, count=count) + self.assertEqual(sent, count) + self.assertEqual(file.tell(), count) + + def testCountSmall(self): + count = 1 + conn = self.accept_conn() + data = self.recv_data(conn) + self.assertEqual(len(data), count) + self.assertEqual(data, self.FILEDATA[:count]) + + # count + offset + + def _testCountWithOffset(self): + address = self.serv.getsockname() + file = open(support.TESTFN, 'rb') + with socket.create_connection(address, timeout=2) as sock, file as file: + count = 100007 + meth = self.meth_from_sock(sock) + sent = meth(file, offset=2007, count=count) + self.assertEqual(sent, count) + self.assertEqual(file.tell(), count + 2007) + + def testCountWithOffset(self): + count = 100007 + conn = self.accept_conn() + data = self.recv_data(conn) + self.assertEqual(len(data), count) + self.assertEqual(data, self.FILEDATA[2007:count+2007]) + + # non blocking sockets are not supposed to work + + def _testNonBlocking(self): + address = self.serv.getsockname() + file = open(support.TESTFN, 'rb') + with socket.create_connection(address) as sock, file as file: + sock.setblocking(False) + meth = self.meth_from_sock(sock) + self.assertRaises(ValueError, meth, file) + self.assertRaises(ValueError, sock.sendfile, file) + + def testNonBlocking(self): + conn = self.accept_conn() + if conn.recv(8192): + self.fail('was not supposed to receive any data') + + # timeout (non-triggered) + + def _testWithTimeout(self): + address = self.serv.getsockname() + file = open(support.TESTFN, 'rb') + with socket.create_connection(address, timeout=2) as sock, file as file: + meth = self.meth_from_sock(sock) + sent = meth(file) + self.assertEqual(sent, self.FILESIZE) + + def testWithTimeout(self): + conn = self.accept_conn() + data = self.recv_data(conn) + self.assertEqual(len(data), self.FILESIZE) + self.assertEqual(data, self.FILEDATA) + + # timeout (triggered) + + def _testWithTimeoutTriggeredSend(self): + address = self.serv.getsockname() + file = open(support.TESTFN, 'rb') + with socket.create_connection(address, timeout=0.01) as sock, \ + file as file: + meth = self.meth_from_sock(sock) + self.assertRaises(socket.timeout, meth, file) + + def testWithTimeoutTriggeredSend(self): + conn = self.accept_conn() + conn.recv(88192) + + # errors + + def _test_errors(self): + pass + + def test_errors(self): + with open(support.TESTFN, 'rb') as file: + with socket.socket(type=socket.SOCK_DGRAM) as s: + meth = self.meth_from_sock(s) + self.assertRaisesRegex( + ValueError, "SOCK_STREAM", meth, file) + with open(support.TESTFN, 'rt') as file: + with socket.socket() as s: + meth = self.meth_from_sock(s) + self.assertRaisesRegex( + ValueError, "binary mode", meth, file) + with open(support.TESTFN, 'rb') as file: + with socket.socket() as s: + meth = self.meth_from_sock(s) + self.assertRaisesRegex(TypeError, "positive integer", + meth, file, count='2') + self.assertRaisesRegex(TypeError, "positive integer", + meth, file, count=0.1) + self.assertRaisesRegex(ValueError, "positive integer", + meth, file, count=0) + self.assertRaisesRegex(ValueError, "positive integer", + meth, file, count=-1) + + +@unittest.skipUnless(thread, 'Threading required for this test.') +@unittest.skipUnless(hasattr(os, "sendfile"), + 'os.sendfile() required for this test.') +class SendfileUsingSendfileTest(SendfileUsingSendTest): + """ + Test the sendfile() implementation of socket.sendfile(). + """ + def meth_from_sock(self, sock): + return getattr(sock, "_sendfile_use_sendfile") + + def test_main(): tests = [GeneralModuleTests, BasicTCPTest, TCPCloserTest, TCPTimeoutTest, TestExceptions, BufferIOTest, BasicTCPTest2, BasicUDPTest, UDPTimeoutTest ] @@ -5161,6 +5433,8 @@ def test_main(): InterruptedRecvTimeoutTest, InterruptedSendTimeoutTest, TestSocketSharing, + SendfileUsingSendTest, + SendfileUsingSendfileTest, ]) thread_info = support.threading_setup() diff --git a/Lib/test/test_socketserver.py b/Lib/test/test_socketserver.py index 0276f99..f5d8d52 100644 --- a/Lib/test/test_socketserver.py +++ b/Lib/test/test_socketserver.py @@ -222,38 +222,6 @@ class SocketServerTest(unittest.TestCase): socketserver.DatagramRequestHandler, self.dgram_examine) - @contextlib.contextmanager - def mocked_select_module(self): - """Mocks the select.select() call to raise EINTR for first call""" - old_select = select.select - - class MockSelect: - def __init__(self): - self.called = 0 - - def __call__(self, *args): - self.called += 1 - if self.called == 1: - # raise the exception on first call - raise OSError(errno.EINTR, os.strerror(errno.EINTR)) - else: - # Return real select value for consecutive calls - return old_select(*args) - - select.select = MockSelect() - try: - yield select.select - finally: - select.select = old_select - - def test_InterruptServerSelectCall(self): - with self.mocked_select_module() as mock_select: - pid = self.run_server(socketserver.TCPServer, - socketserver.StreamRequestHandler, - self.stream_examine) - # Make sure select was called again: - self.assertGreater(mock_select.called, 1) - # Alas, on Linux (at least) recvfrom() doesn't return a meaningful # client address so this cannot work: diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 0a7b905..b7504c6 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -86,6 +86,12 @@ def have_verify_flags(): # 0.9.8 or higher return ssl.OPENSSL_VERSION_INFO >= (0, 9, 8, 0, 15) +def utc_offset(): #NOTE: ignore issues like #1647654 + # local time = utc time + utc offset + if time.daylight and time.localtime().tm_isdst > 0: + return -time.altzone # seconds + return -time.timezone + def asn1time(cert_time): # Some versions of OpenSSL ignore seconds, see #18207 # 0.9.8.i @@ -134,6 +140,14 @@ class BasicSocketTests(unittest.TestCase): self.assertIn(ssl.HAS_SNI, {True, False}) self.assertIn(ssl.HAS_ECDH, {True, False}) + def test_str_for_enums(self): + # Make sure that the PROTOCOL_* constants have enum-like string + # reprs. + proto = ssl.PROTOCOL_SSLv23 + self.assertEqual(str(proto), '_SSLMethod.PROTOCOL_SSLv23') + ctx = ssl.SSLContext(proto) + self.assertIs(ctx.protocol, proto) + def test_random(self): v = ssl.RAND_status() if support.verbose: @@ -298,10 +312,10 @@ class BasicSocketTests(unittest.TestCase): # Version string as returned by {Open,Libre}SSL, the format might change if "LibreSSL" in s: self.assertTrue(s.startswith("LibreSSL {:d}.{:d}".format(major, minor)), - (s, t)) + (s, t, hex(n))) else: self.assertTrue(s.startswith("OpenSSL {:d}.{:d}.{:d}".format(major, minor, fix)), - (s, t)) + (s, t, hex(n))) @support.cpython_only def test_refcycle(self): @@ -505,9 +519,14 @@ class BasicSocketTests(unittest.TestCase): def test_unknown_channel_binding(self): # should raise ValueError for unknown type s = socket.socket(socket.AF_INET) - with ssl.wrap_socket(s) as ss: + s.bind(('127.0.0.1', 0)) + s.listen() + c = socket.socket(socket.AF_INET) + c.connect(s.getsockname()) + with ssl.wrap_socket(c, do_handshake_on_connect=False) as ss: with self.assertRaises(ValueError): ss.get_channel_binding("unknown-type") + s.close() @unittest.skipUnless("tls-unique" in ssl.CHANNEL_BINDING_TYPES, "'tls-unique' channel binding not available") @@ -648,6 +667,71 @@ class BasicSocketTests(unittest.TestCase): ctx.wrap_socket(s) self.assertEqual(str(cx.exception), "only stream sockets are supported") + def cert_time_ok(self, timestring, timestamp): + self.assertEqual(ssl.cert_time_to_seconds(timestring), timestamp) + + def cert_time_fail(self, timestring): + with self.assertRaises(ValueError): + ssl.cert_time_to_seconds(timestring) + + @unittest.skipUnless(utc_offset(), + 'local time needs to be different from UTC') + def test_cert_time_to_seconds_timezone(self): + # Issue #19940: ssl.cert_time_to_seconds() returns wrong + # results if local timezone is not UTC + self.cert_time_ok("May 9 00:00:00 2007 GMT", 1178668800.0) + self.cert_time_ok("Jan 5 09:34:43 2018 GMT", 1515144883.0) + + def test_cert_time_to_seconds(self): + timestring = "Jan 5 09:34:43 2018 GMT" + ts = 1515144883.0 + self.cert_time_ok(timestring, ts) + # accept keyword parameter, assert its name + self.assertEqual(ssl.cert_time_to_seconds(cert_time=timestring), ts) + # accept both %e and %d (space or zero generated by strftime) + self.cert_time_ok("Jan 05 09:34:43 2018 GMT", ts) + # case-insensitive + self.cert_time_ok("JaN 5 09:34:43 2018 GmT", ts) + self.cert_time_fail("Jan 5 09:34 2018 GMT") # no seconds + self.cert_time_fail("Jan 5 09:34:43 2018") # no GMT + self.cert_time_fail("Jan 5 09:34:43 2018 UTC") # not GMT timezone + self.cert_time_fail("Jan 35 09:34:43 2018 GMT") # invalid day + self.cert_time_fail("Jon 5 09:34:43 2018 GMT") # invalid month + self.cert_time_fail("Jan 5 24:00:00 2018 GMT") # invalid hour + self.cert_time_fail("Jan 5 09:60:43 2018 GMT") # invalid minute + + newyear_ts = 1230768000.0 + # leap seconds + self.cert_time_ok("Dec 31 23:59:60 2008 GMT", newyear_ts) + # same timestamp + self.cert_time_ok("Jan 1 00:00:00 2009 GMT", newyear_ts) + + self.cert_time_ok("Jan 5 09:34:59 2018 GMT", 1515144899) + # allow 60th second (even if it is not a leap second) + self.cert_time_ok("Jan 5 09:34:60 2018 GMT", 1515144900) + # allow 2nd leap second for compatibility with time.strptime() + self.cert_time_ok("Jan 5 09:34:61 2018 GMT", 1515144901) + self.cert_time_fail("Jan 5 09:34:62 2018 GMT") # invalid seconds + + # no special treatement for the special value: + # 99991231235959Z (rfc 5280) + self.cert_time_ok("Dec 31 23:59:59 9999 GMT", 253402300799.0) + + @support.run_with_locale('LC_ALL', '') + def test_cert_time_to_seconds_locale(self): + # `cert_time_to_seconds()` should be locale independent + + def local_february_name(): + return time.strftime('%b', (1, 2, 3, 4, 5, 6, 0, 0, 0)) + + if local_february_name().lower() == 'feb': + self.skipTest("locale-specific month name needs to be " + "different from C locale") + + # locale-independent + self.cert_time_ok("Feb 9 00:00:00 2007 GMT", 1170979200.0) + self.cert_time_fail(local_february_name() + " 9 00:00:00 2007 GMT") + class ContextTests(unittest.TestCase): @@ -1156,7 +1240,7 @@ class SSLErrorTests(unittest.TestCase): ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) with socket.socket() as s: s.bind(("127.0.0.1", 0)) - s.listen(5) + s.listen() c = socket.socket() c.connect(s.getsockname()) c.setblocking(False) @@ -1169,6 +1253,69 @@ class SSLErrorTests(unittest.TestCase): self.assertEqual(cm.exception.errno, ssl.SSL_ERROR_WANT_READ) +class MemoryBIOTests(unittest.TestCase): + + def test_read_write(self): + bio = ssl.MemoryBIO() + bio.write(b'foo') + self.assertEqual(bio.read(), b'foo') + self.assertEqual(bio.read(), b'') + bio.write(b'foo') + bio.write(b'bar') + self.assertEqual(bio.read(), b'foobar') + self.assertEqual(bio.read(), b'') + bio.write(b'baz') + self.assertEqual(bio.read(2), b'ba') + self.assertEqual(bio.read(1), b'z') + self.assertEqual(bio.read(1), b'') + + def test_eof(self): + bio = ssl.MemoryBIO() + self.assertFalse(bio.eof) + self.assertEqual(bio.read(), b'') + self.assertFalse(bio.eof) + bio.write(b'foo') + self.assertFalse(bio.eof) + bio.write_eof() + self.assertFalse(bio.eof) + self.assertEqual(bio.read(2), b'fo') + self.assertFalse(bio.eof) + self.assertEqual(bio.read(1), b'o') + self.assertTrue(bio.eof) + self.assertEqual(bio.read(), b'') + self.assertTrue(bio.eof) + + def test_pending(self): + bio = ssl.MemoryBIO() + self.assertEqual(bio.pending, 0) + bio.write(b'foo') + self.assertEqual(bio.pending, 3) + for i in range(3): + bio.read(1) + self.assertEqual(bio.pending, 3-i-1) + for i in range(3): + bio.write(b'x') + self.assertEqual(bio.pending, i+1) + bio.read() + self.assertEqual(bio.pending, 0) + + def test_buffer_types(self): + bio = ssl.MemoryBIO() + bio.write(b'foo') + self.assertEqual(bio.read(), b'foo') + bio.write(bytearray(b'bar')) + self.assertEqual(bio.read(), b'bar') + bio.write(memoryview(b'baz')) + self.assertEqual(bio.read(), b'baz') + + def test_error_types(self): + bio = ssl.MemoryBIO() + self.assertRaises(TypeError, bio.write, 'foo') + self.assertRaises(TypeError, bio.write, None) + self.assertRaises(TypeError, bio.write, True) + self.assertRaises(TypeError, bio.write, 1) + + class NetworkedTests(unittest.TestCase): def test_connect(self): @@ -1396,14 +1543,12 @@ class NetworkedTests(unittest.TestCase): def test_get_server_certificate(self): def _test_get_server_certificate(host, port, cert=None): with support.transient_internet(host): - pem = ssl.get_server_certificate((host, port), - ssl.PROTOCOL_SSLv23) + pem = ssl.get_server_certificate((host, port)) if not pem: self.fail("No server certificate on %s:%s!" % (host, port)) try: pem = ssl.get_server_certificate((host, port), - ssl.PROTOCOL_SSLv23, ca_certs=CERTFILE) except ssl.SSLError as x: #should fail @@ -1413,7 +1558,6 @@ class NetworkedTests(unittest.TestCase): self.fail("Got server certificate %s for %s:%s!" % (pem, host, port)) pem = ssl.get_server_certificate((host, port), - ssl.PROTOCOL_SSLv23, ca_certs=cert) if not pem: self.fail("No server certificate on %s:%s!" % (host, port)) @@ -1499,6 +1643,93 @@ class NetworkedTests(unittest.TestCase): self.assertIs(ss.context, ctx2) self.assertIs(ss._sslobj.context, ctx2) + +class NetworkedBIOTests(unittest.TestCase): + + def ssl_io_loop(self, sock, incoming, outgoing, func, *args, **kwargs): + # A simple IO loop. Call func(*args) depending on the error we get + # (WANT_READ or WANT_WRITE) move data between the socket and the BIOs. + timeout = kwargs.get('timeout', 10) + count = 0 + while True: + errno = None + count += 1 + try: + ret = func(*args) + except ssl.SSLError as e: + # Note that we get a spurious -1/SSL_ERROR_SYSCALL for + # non-blocking IO. The SSL_shutdown manpage hints at this. + # It *should* be safe to just ignore SYS_ERROR_SYSCALL because + # with a Memory BIO there's no syscalls (for IO at least). + if e.errno not in (ssl.SSL_ERROR_WANT_READ, + ssl.SSL_ERROR_WANT_WRITE, + ssl.SSL_ERROR_SYSCALL): + raise + errno = e.errno + # Get any data from the outgoing BIO irrespective of any error, and + # send it to the socket. + buf = outgoing.read() + sock.sendall(buf) + # If there's no error, we're done. For WANT_READ, we need to get + # data from the socket and put it in the incoming BIO. + if errno is None: + break + elif errno == ssl.SSL_ERROR_WANT_READ: + buf = sock.recv(32768) + if buf: + incoming.write(buf) + else: + incoming.write_eof() + if support.verbose: + sys.stdout.write("Needed %d calls to complete %s().\n" + % (count, func.__name__)) + return ret + + def test_handshake(self): + with support.transient_internet("svn.python.org"): + sock = socket.socket(socket.AF_INET) + sock.connect(("svn.python.org", 443)) + incoming = ssl.MemoryBIO() + outgoing = ssl.MemoryBIO() + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.verify_mode = ssl.CERT_REQUIRED + ctx.load_verify_locations(SVN_PYTHON_ORG_ROOT_CERT) + ctx.check_hostname = True + sslobj = ctx.wrap_bio(incoming, outgoing, False, 'svn.python.org') + self.assertIs(sslobj._sslobj.owner, sslobj) + self.assertIsNone(sslobj.cipher()) + self.assertIsNone(sslobj.shared_ciphers()) + self.assertRaises(ValueError, sslobj.getpeercert) + if 'tls-unique' in ssl.CHANNEL_BINDING_TYPES: + self.assertIsNone(sslobj.get_channel_binding('tls-unique')) + self.ssl_io_loop(sock, incoming, outgoing, sslobj.do_handshake) + self.assertTrue(sslobj.cipher()) + self.assertIsNone(sslobj.shared_ciphers()) + self.assertTrue(sslobj.getpeercert()) + if 'tls-unique' in ssl.CHANNEL_BINDING_TYPES: + self.assertTrue(sslobj.get_channel_binding('tls-unique')) + self.ssl_io_loop(sock, incoming, outgoing, sslobj.unwrap) + self.assertRaises(ssl.SSLError, sslobj.write, b'foo') + sock.close() + + def test_read_write_data(self): + with support.transient_internet("svn.python.org"): + sock = socket.socket(socket.AF_INET) + sock.connect(("svn.python.org", 443)) + incoming = ssl.MemoryBIO() + outgoing = ssl.MemoryBIO() + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.verify_mode = ssl.CERT_NONE + sslobj = ctx.wrap_bio(incoming, outgoing, False) + self.ssl_io_loop(sock, incoming, outgoing, sslobj.do_handshake) + req = b'GET / HTTP/1.0\r\n\r\n' + self.ssl_io_loop(sock, incoming, outgoing, sslobj.write, req) + buf = self.ssl_io_loop(sock, incoming, outgoing, sslobj.read, 1024) + self.assertEqual(buf[:5], b'HTTP/') + self.ssl_io_loop(sock, incoming, outgoing, sslobj.unwrap) + sock.close() + + try: import threading except ImportError: @@ -1547,6 +1778,7 @@ else: self.close() return False else: + self.server.shared_ciphers.append(self.sslconn.shared_ciphers()) if self.server.context.verify_mode == ssl.CERT_REQUIRED: cert = self.sslconn.getpeercert() if support.verbose and self.server.chatty: @@ -1662,6 +1894,7 @@ else: self.flag = None self.active = False self.selected_protocols = [] + self.shared_ciphers = [] self.conn_errors = [] threading.Thread.__init__(self) self.daemon = True @@ -1681,7 +1914,7 @@ else: def run(self): self.sock.settimeout(0.05) - self.sock.listen(5) + self.sock.listen() self.active = True if self.flag: # signal an event @@ -1887,14 +2120,23 @@ else: 'compression': s.compression(), 'cipher': s.cipher(), 'peercert': s.getpeercert(), - 'client_npn_protocol': s.selected_npn_protocol() + 'client_npn_protocol': s.selected_npn_protocol(), + 'version': s.version(), }) s.close() stats['server_npn_protocols'] = server.selected_protocols + stats['server_shared_ciphers'] = server.shared_ciphers return stats def try_protocol_combo(server_protocol, client_protocol, expect_success, certsreqs=None, server_options=0, client_options=0): + """ + Try to SSL-connect using *client_protocol* to *server_protocol*. + If *expect_success* is true, assert that the connection succeeds, + if it's false, assert that the connection fails. + Also, if *expect_success* is a string, assert that it is the protocol + version actually used by the connection. + """ if certsreqs is None: certsreqs = ssl.CERT_NONE certtype = { @@ -1924,8 +2166,8 @@ else: ctx.load_cert_chain(CERTFILE) ctx.load_verify_locations(CERTFILE) try: - server_params_test(client_context, server_context, - chatty=False, connectionchatty=False) + stats = server_params_test(client_context, server_context, + chatty=False, connectionchatty=False) # Protocol mismatch can result in either an SSLError, or a # "Connection reset by peer" error. except ssl.SSLError: @@ -1940,6 +2182,10 @@ else: "Client protocol %s succeeded with server protocol %s!" % (ssl.get_protocol_name(client_protocol), ssl.get_protocol_name(server_protocol))) + elif (expect_success is not True + and expect_success != stats['version']): + raise AssertionError("version mismatch: expected %r, got %r" + % (expect_success, stats['version'])) class ThreadedTests(unittest.TestCase): @@ -2106,7 +2352,7 @@ else: # and sets Event `listener_gone` to let the main thread know # the socket is gone. def listener(): - s.listen(5) + s.listen() listener_ready.set() newsock, addr = s.accept() newsock.close() @@ -2171,19 +2417,19 @@ else: " SSL2 client to SSL23 server test unexpectedly failed:\n %s\n" % str(x)) if hasattr(ssl, 'PROTOCOL_SSLv3'): - try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, True) + try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, 'SSLv3') try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True) - try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True) + try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, 'TLSv1') if hasattr(ssl, 'PROTOCOL_SSLv3'): - try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, True, ssl.CERT_OPTIONAL) + try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, 'SSLv3', ssl.CERT_OPTIONAL) try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True, ssl.CERT_OPTIONAL) - try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True, ssl.CERT_OPTIONAL) + try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, 'TLSv1', ssl.CERT_OPTIONAL) if hasattr(ssl, 'PROTOCOL_SSLv3'): - try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, True, ssl.CERT_REQUIRED) + try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, 'SSLv3', ssl.CERT_REQUIRED) try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True, ssl.CERT_REQUIRED) - try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True, ssl.CERT_REQUIRED) + try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, 'TLSv1', ssl.CERT_REQUIRED) # Server with specific SSL options if hasattr(ssl, 'PROTOCOL_SSLv3'): @@ -2203,9 +2449,9 @@ else: """Connecting to an SSLv3 server with various client options""" if support.verbose: sys.stdout.write("\n") - try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True) - try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True, ssl.CERT_OPTIONAL) - try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True, ssl.CERT_REQUIRED) + try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, 'SSLv3') + try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, 'SSLv3', ssl.CERT_OPTIONAL) + try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, 'SSLv3', ssl.CERT_REQUIRED) if hasattr(ssl, 'PROTOCOL_SSLv2'): try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv2, False) try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv23, False, @@ -2213,7 +2459,7 @@ else: try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_TLSv1, False) if no_sslv2_implies_sslv3_hello(): # No SSLv2 => client will use an SSLv3 hello on recent OpenSSLs - try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv23, True, + try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv23, 'SSLv3', client_options=ssl.OP_NO_SSLv2) @skip_if_broken_ubuntu_ssl @@ -2221,9 +2467,9 @@ else: """Connecting to a TLSv1 server with various client options""" if support.verbose: sys.stdout.write("\n") - try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True) - try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True, ssl.CERT_OPTIONAL) - try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True, ssl.CERT_REQUIRED) + try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, 'TLSv1') + try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, 'TLSv1', ssl.CERT_OPTIONAL) + try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, 'TLSv1', ssl.CERT_REQUIRED) if hasattr(ssl, 'PROTOCOL_SSLv2'): try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv2, False) if hasattr(ssl, 'PROTOCOL_SSLv3'): @@ -2239,7 +2485,7 @@ else: Testing against older TLS versions.""" if support.verbose: sys.stdout.write("\n") - try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_TLSv1_1, True) + try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_TLSv1_1, 'TLSv1.1') if hasattr(ssl, 'PROTOCOL_SSLv2'): try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_SSLv2, False) if hasattr(ssl, 'PROTOCOL_SSLv3'): @@ -2247,7 +2493,7 @@ else: try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_SSLv23, False, client_options=ssl.OP_NO_TLSv1_1) - try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1_1, True) + try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1_1, 'TLSv1.1') try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_TLSv1, False) try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1_1, False) @@ -2260,7 +2506,7 @@ else: Testing against older TLS versions.""" if support.verbose: sys.stdout.write("\n") - try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_TLSv1_2, True, + try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_TLSv1_2, 'TLSv1.2', server_options=ssl.OP_NO_SSLv3|ssl.OP_NO_SSLv2, client_options=ssl.OP_NO_SSLv3|ssl.OP_NO_SSLv2,) if hasattr(ssl, 'PROTOCOL_SSLv2'): @@ -2270,7 +2516,7 @@ else: try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_SSLv23, False, client_options=ssl.OP_NO_TLSv1_2) - try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1_2, True) + try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1_2, 'TLSv1.2') try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_TLSv1, False) try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1_2, False) try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_TLSv1_1, False) @@ -2506,6 +2752,36 @@ else: s.write(b"over\n") s.close() + def test_nonblocking_send(self): + server = ThreadedEchoServer(CERTFILE, + certreqs=ssl.CERT_NONE, + ssl_version=ssl.PROTOCOL_TLSv1, + cacerts=CERTFILE, + chatty=True, + connectionchatty=False) + with server: + s = ssl.wrap_socket(socket.socket(), + server_side=False, + certfile=CERTFILE, + ca_certs=CERTFILE, + cert_reqs=ssl.CERT_NONE, + ssl_version=ssl.PROTOCOL_TLSv1) + s.connect((HOST, server.port)) + s.setblocking(False) + + # If we keep sending data, at some point the buffers + # will be full and the call will block + buf = bytearray(8192) + def fill_buffer(): + while True: + s.send(buf) + self.assertRaises((ssl.SSLWantWriteError, + ssl.SSLWantReadError), fill_buffer) + + # Now read all the output and discard it + s.setblocking(True) + s.close() + def test_handshake_timeout(self): # Issue #5103: SSL handshake must respect the socket timeout server = socket.socket(socket.AF_INET) @@ -2515,7 +2791,7 @@ else: finish = False def serve(): - server.listen(5) + server.listen() started.set() conns = [] while not finish: @@ -2572,7 +2848,7 @@ else: peer = None def serve(): nonlocal remote, peer - server.listen(5) + server.listen() # Block on the accept and wait on the connection to close. evt.set() remote, peer = server.accept() @@ -2622,6 +2898,21 @@ else: s.connect((HOST, server.port)) self.assertIn("no shared cipher", str(server.conn_errors[0])) + def test_version_basic(self): + """ + Basic tests for SSLSocket.version(). + More tests are done in the test_protocol_*() methods. + """ + context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + with ThreadedEchoServer(CERTFILE, + ssl_version=ssl.PROTOCOL_TLSv1, + chatty=False) as server: + with context.wrap_socket(socket.socket()) as s: + self.assertIs(s.version(), None) + s.connect((HOST, server.port)) + self.assertEqual(s.version(), "TLSv1") + self.assertIs(s.version(), None) + @unittest.skipUnless(ssl.HAS_ECDH, "test requires ECDH-enabled OpenSSL") def test_default_ecdh_curve(self): # Issue #21015: elliptic curve-based Diffie Hellman key exchange @@ -2871,6 +3162,20 @@ else: self.assertEqual(cm.exception.reason, 'TLSV1_ALERT_INTERNAL_ERROR') self.assertIn("TypeError", stderr.getvalue()) + def test_shared_ciphers(self): + server_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + server_context.load_cert_chain(SIGNED_CERTFILE) + client_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + client_context.verify_mode = ssl.CERT_REQUIRED + client_context.load_verify_locations(SIGNING_CA) + client_context.set_ciphers("RC4") + server_context.set_ciphers("AES:RC4") + stats = server_params_test(client_context, server_context) + ciphers = stats['server_shared_ciphers'][0] + self.assertGreater(len(ciphers), 0) + for name, tls_version, bits in ciphers: + self.assertIn("RC4", name.split("-")) + def test_read_write_after_close_raises_valuerror(self): context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) context.verify_mode = ssl.CERT_REQUIRED @@ -2886,6 +3191,23 @@ else: self.assertRaises(ValueError, s.read, 1024) self.assertRaises(ValueError, s.write, b'hello') + def test_sendfile(self): + TEST_DATA = b"x" * 512 + with open(support.TESTFN, 'wb') as f: + f.write(TEST_DATA) + self.addCleanup(support.unlink, support.TESTFN) + context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + context.verify_mode = ssl.CERT_REQUIRED + context.load_verify_locations(CERTFILE) + context.load_cert_chain(CERTFILE) + server = ThreadedEchoServer(context=context, chatty=False) + with server: + with context.wrap_socket(socket.socket()) as s: + s.connect((HOST, server.port)) + with open(support.TESTFN, 'rb') as file: + s.sendfile(file) + self.assertEqual(s.recv(1024), TEST_DATA) + def test_main(verbose=False): if support.verbose: @@ -2919,10 +3241,11 @@ def test_main(verbose=False): if not os.path.exists(filename): raise support.TestFailed("Can't read certificate file %r" % filename) - tests = [ContextTests, BasicSocketTests, SSLErrorTests] + tests = [ContextTests, BasicSocketTests, SSLErrorTests, MemoryBIOTests] if support.is_resource_enabled('network'): tests.append(NetworkedTests) + tests.append(NetworkedBIOTests) if _have_threads: thread_info = support.threading_setup() diff --git a/Lib/test/test_stat.py b/Lib/test/test_stat.py index af6ced4..f1a5938 100644 --- a/Lib/test/test_stat.py +++ b/Lib/test/test_stat.py @@ -1,5 +1,6 @@ import unittest import os +import sys from test.support import TESTFN, import_fresh_module c_stat = import_fresh_module('stat', fresh=['_stat']) @@ -52,6 +53,26 @@ class TestFilemode: 'S_IWOTH': 0o002, 'S_IXOTH': 0o001} + # defined by the Windows API documentation + file_attributes = { + 'FILE_ATTRIBUTE_ARCHIVE': 32, + 'FILE_ATTRIBUTE_COMPRESSED': 2048, + 'FILE_ATTRIBUTE_DEVICE': 64, + 'FILE_ATTRIBUTE_DIRECTORY': 16, + 'FILE_ATTRIBUTE_ENCRYPTED': 16384, + 'FILE_ATTRIBUTE_HIDDEN': 2, + 'FILE_ATTRIBUTE_INTEGRITY_STREAM': 32768, + 'FILE_ATTRIBUTE_NORMAL': 128, + 'FILE_ATTRIBUTE_NOT_CONTENT_INDEXED': 8192, + 'FILE_ATTRIBUTE_NO_SCRUB_DATA': 131072, + 'FILE_ATTRIBUTE_OFFLINE': 4096, + 'FILE_ATTRIBUTE_READONLY': 1, + 'FILE_ATTRIBUTE_REPARSE_POINT': 1024, + 'FILE_ATTRIBUTE_SPARSE_FILE': 512, + 'FILE_ATTRIBUTE_SYSTEM': 4, + 'FILE_ATTRIBUTE_TEMPORARY': 256, + 'FILE_ATTRIBUTE_VIRTUAL': 65536} + def setUp(self): try: os.remove(TESTFN) @@ -185,6 +206,14 @@ class TestFilemode: self.assertTrue(callable(func)) self.assertEqual(func(0), 0) + @unittest.skipUnless(sys.platform == "win32", + "FILE_ATTRIBUTE_* constants are Win32 specific") + def test_file_attribute_constants(self): + for key, value in sorted(self.file_attributes.items()): + self.assertTrue(hasattr(self.statmod, key), key) + modvalue = getattr(self.statmod, key) + self.assertEqual(value, modvalue, key) + class TestFilemodeCStat(TestFilemode, unittest.TestCase): statmod = c_stat diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 03ce9d1..a02d2f4 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -85,7 +85,7 @@ class TestSupport(unittest.TestCase): def test_bind_port(self): s = socket.socket() support.bind_port(s) - s.listen(1) + s.listen() s.close() # Tests for temp_dir() diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index f8a9e5e..2da987f 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -635,6 +635,53 @@ class SysModuleTest(unittest.TestCase): expected = None self.check_fsencoding(fs_encoding, expected) + def c_locale_get_error_handler(self, isolated=False, encoding=None): + # Force the POSIX locale + env = os.environ.copy() + env["LC_ALL"] = "C" + code = '\n'.join(( + 'import sys', + 'def dump(name):', + ' std = getattr(sys, name)', + ' print("%s: %s" % (name, std.errors))', + 'dump("stdin")', + 'dump("stdout")', + 'dump("stderr")', + )) + args = [sys.executable, "-c", code] + if isolated: + args.append("-I") + elif encoding: + env['PYTHONIOENCODING'] = encoding + p = subprocess.Popen(args, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + env=env, + universal_newlines=True) + stdout, stderr = p.communicate() + return stdout + + def test_c_locale_surrogateescape(self): + out = self.c_locale_get_error_handler(isolated=True) + self.assertEqual(out, + 'stdin: surrogateescape\n' + 'stdout: surrogateescape\n' + 'stderr: backslashreplace\n') + + # replace the default error handler + out = self.c_locale_get_error_handler(encoding=':strict') + self.assertEqual(out, + 'stdin: strict\n' + 'stdout: strict\n' + 'stderr: backslashreplace\n') + + # force the encoding + out = self.c_locale_get_error_handler(encoding='iso8859-1') + self.assertEqual(out, + 'stdin: surrogateescape\n' + 'stdout: surrogateescape\n' + 'stderr: backslashreplace\n') + def test_implementation(self): # This test applies to all implementations equally. @@ -697,6 +744,27 @@ class SysModuleTest(unittest.TestCase): c = sys.getallocatedblocks() self.assertIn(c, range(b - 50, b + 50)) + def test_is_finalizing(self): + self.assertIs(sys.is_finalizing(), False) + # Don't use the atexit module because _Py_Finalizing is only set + # after calling atexit callbacks + code = """if 1: + import sys + + class AtExit: + is_finalizing = sys.is_finalizing + print = print + + def __del__(self): + self.print(self.is_finalizing(), flush=True) + + # Keep a reference in the __main__ module namespace, so the + # AtExit destructor will be called at Python exit + ref = AtExit() + """ + rc, stdout, stderr = assert_python_ok('-c', code) + self.assertEqual(stdout.rstrip(), b'True') + @test.support.cpython_only class SizeofTest(unittest.TestCase): @@ -769,7 +837,7 @@ class SizeofTest(unittest.TestCase): # buffer # XXX # builtin_function_or_method - check(len, size('3P')) # XXX check layout + check(len, size('4P')) # XXX check layout # bytearray samples = [b'', b'u'*100000] for sample in samples: @@ -870,7 +938,7 @@ class SizeofTest(unittest.TestCase): check(bar, size('PP')) # generator def get_gen(): yield 1 - check(get_gen(), size('Pb2P')) + check(get_gen(), size('Pb2PPP')) # iterator check(iter('abc'), size('lP')) # callable-iterator @@ -926,7 +994,7 @@ class SizeofTest(unittest.TestCase): # frozenset PySet_MINSIZE = 8 samples = [[], range(10), range(50)] - s = size('3n2P' + PySet_MINSIZE*'nP' + 'nP') + s = size('3n2P' + PySet_MINSIZE*'nP' + '2nP') for sample in samples: minused = len(sample) if minused == 0: tmp = 1 @@ -957,7 +1025,7 @@ class SizeofTest(unittest.TestCase): check(int, s) # (PyTypeObject + PyNumberMethods + PyMappingMethods + # PySequenceMethods + PyBufferProcs + 4P) - s = vsize('P2n15Pl4Pn9Pn11PIP') + struct.calcsize('34P 3P 10P 2P 4P') + s = vsize('P2n17Pl4Pn9Pn11PIP') + struct.calcsize('34P 3P 10P 2P 4P') # Separate block for PyDictKeysObject with 4 entries s += struct.calcsize("2nPn") + 4*struct.calcsize("n2P") # class diff --git a/Lib/test/test_sys_setprofile.py b/Lib/test/test_sys_setprofile.py index 9816e3e..e59320b 100644 --- a/Lib/test/test_sys_setprofile.py +++ b/Lib/test/test_sys_setprofile.py @@ -260,7 +260,6 @@ class ProfileHookTestCase(TestCaseBase): def f(): for i in range(2): yield i - raise StopIteration def g(p): for i in f(): pass diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index e527e40..810b76b 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -285,6 +285,18 @@ class ListTest(ReadTest, unittest.TestCase): self.assertIn(b'pax' + (b'/123' * 125) + b'/longlink link to pax' + (b'/123' * 125) + b'/longname', out) + def test_list_members(self): + tio = io.TextIOWrapper(io.BytesIO(), 'ascii', newline='\n') + def members(tar): + for tarinfo in tar.getmembers(): + if 'reg' in tarinfo.name: + yield tarinfo + with support.swap_attr(sys, 'stdout', tio): + self.tar.list(verbose=False, members=members(self.tar)) + out = tio.detach().getvalue() + self.assertIn(b'ustar/regtype', out) + self.assertNotIn(b'ustar/conttype', out) + class GzipListTest(GzipTest, ListTest): pass diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py index da8f629..d0251aa 100644 --- a/Lib/test/test_tcl.py +++ b/Lib/test/test_tcl.py @@ -415,6 +415,8 @@ class TclTest(unittest.TestCase): # XXX NaN representation can be not parsable by float() self.assertEqual(passValue((1, '2', (3.4,))), (1, '2', (3.4,)) if self.wantobjects else '1 2 3.4') + self.assertEqual(passValue(['a', ['b', 'c']]), + ('a', ('b', 'c')) if self.wantobjects else 'a {b c}') def test_user_command(self): result = None @@ -466,6 +468,7 @@ class TclTest(unittest.TestCase): # XXX NaN representation can be not parsable by float() check((), '') check((1, (2,), (3, 4), '5 6', ()), '1 2 {3 4} {5 6} {}') + check([1, [2,], [3, 4], '5 6', []], '1 2 {3 4} {5 6} {}') def test_splitlist(self): splitlist = self.interp.tk.splitlist @@ -491,12 +494,15 @@ class TclTest(unittest.TestCase): ('a 3.4', ('a', '3.4')), (('a', 3.4), ('a', 3.4)), ((), ()), + ([], ()), + (['a', ['b', 'c']], ('a', ['b', 'c'])), (call('list', 1, '2', (3.4,)), (1, '2', (3.4,)) if self.wantobjects else ('1', '2', '3.4')), ] + tk_patchlevel = get_tk_patchlevel() if tcl_version >= (8, 5): - if not self.wantobjects or get_tk_patchlevel() < (8, 5, 5): + if not self.wantobjects or tk_patchlevel < (8, 5, 5): # Before 8.5.5 dicts were converted to lists through string expected = ('12', '\u20ac', '\xe2\x82\xac', '3.4') else: @@ -505,8 +511,11 @@ class TclTest(unittest.TestCase): (call('dict', 'create', 12, '\u20ac', b'\xe2\x82\xac', (3.4,)), expected), ] + dbg_info = ('want objects? %s, Tcl version: %s, Tk patchlevel: %s' + % (self.wantobjects, tcl_version, tk_patchlevel)) for arg, res in testcases: - self.assertEqual(splitlist(arg), res, msg=arg) + self.assertEqual(splitlist(arg), res, + 'arg=%a, %s' % (arg, dbg_info)) self.assertRaises(TclError, splitlist, '{') def test_split(self): @@ -538,6 +547,9 @@ class TclTest(unittest.TestCase): (('a', 3.4), ('a', 3.4)), (('a', (2, 3.4)), ('a', (2, 3.4))), ((), ()), + ([], ()), + (['a', 'b c'], ('a', ('b', 'c'))), + (['a', ['b', 'c']], ('a', ('b', 'c'))), (call('list', 1, '2', (3.4,)), (1, '2', (3.4,)) if self.wantobjects else ('1', '2', '3.4')), diff --git a/Lib/test/test_telnetlib.py b/Lib/test/test_telnetlib.py index ee1c357..e1ef99a 100644 --- a/Lib/test/test_telnetlib.py +++ b/Lib/test/test_telnetlib.py @@ -11,7 +11,7 @@ threading = support.import_module('threading') HOST = support.HOST def server(evt, serv): - serv.listen(5) + serv.listen() evt.set() try: conn, addr = serv.accept() diff --git a/Lib/test/test_threaded_import.py b/Lib/test/test_threaded_import.py index 192fa08..5bf670c 100644 --- a/Lib/test/test_threaded_import.py +++ b/Lib/test/test_threaded_import.py @@ -115,12 +115,18 @@ class ThreadedImportTests(unittest.TestCase): errors = [] done_tasks = [] done.clear() + t0 = time.monotonic() for i in range(N): t = threading.Thread(target=task, args=(N, done, done_tasks, errors,)) t.start() - self.assertTrue(done.wait(60)) - self.assertFalse(errors) + completed = done.wait(10 * 60) + dt = time.monotonic() - t0 + if verbose: + print("%.1f ms" % (dt*1e3), flush=True, end=" ") + dbg_info = 'done: %s/%s' % (len(done_tasks), N) + self.assertFalse(errors, dbg_info) + self.assertTrue(completed, dbg_info) if verbose: print("OK.") diff --git a/Lib/test/test_timeit.py b/Lib/test/test_timeit.py index 625fb8d..b3a96bb 100644 --- a/Lib/test/test_timeit.py +++ b/Lib/test/test_timeit.py @@ -86,9 +86,10 @@ class TestTimeit(unittest.TestCase): def fake_callable_stmt(self): self.fake_timer.inc() - def timeit(self, stmt, setup, number=None): + def timeit(self, stmt, setup, number=None, globals=None): self.fake_timer = FakeTimer() - t = timeit.Timer(stmt=stmt, setup=setup, timer=self.fake_timer) + t = timeit.Timer(stmt=stmt, setup=setup, timer=self.fake_timer, + globals=globals) kwargs = {} if number is None: number = DEFAULT_NUMBER @@ -127,6 +128,17 @@ class TestTimeit(unittest.TestCase): timer=FakeTimer()) self.assertEqual(delta_time, 0) + def test_timeit_globals_args(self): + global _global_timer + _global_timer = FakeTimer() + t = timeit.Timer(stmt='_global_timer.inc()', timer=_global_timer) + self.assertRaises(NameError, t.timeit, number=3) + timeit.timeit(stmt='_global_timer.inc()', timer=_global_timer, + globals=globals(), number=3) + local_timer = FakeTimer() + timeit.timeit(stmt='local_timer.inc()', timer=local_timer, + globals=locals(), number=3) + def repeat(self, stmt, setup, repeat=None, number=None): self.fake_timer = FakeTimer() t = timeit.Timer(stmt=stmt, setup=setup, timer=self.fake_timer) diff --git a/Lib/test/test_timeout.py b/Lib/test/test_timeout.py index 703c43a..3c75dcc 100644 --- a/Lib/test/test_timeout.py +++ b/Lib/test/test_timeout.py @@ -243,14 +243,14 @@ class TCPTimeoutTestCase(TimeoutTestCase): def testAcceptTimeout(self): # Test accept() timeout support.bind_port(self.sock, self.localhost) - self.sock.listen(5) + self.sock.listen() self._sock_operation(1, 1.5, 'accept') def testSend(self): # Test send() timeout with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as serv: support.bind_port(serv, self.localhost) - serv.listen(5) + serv.listen() self.sock.connect(serv.getsockname()) # Send a lot of data in order to bypass buffering in the TCP stack. self._sock_operation(100, 1.5, 'send', b"X" * 200000) @@ -259,7 +259,7 @@ class TCPTimeoutTestCase(TimeoutTestCase): # Test sendto() timeout with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as serv: support.bind_port(serv, self.localhost) - serv.listen(5) + serv.listen() self.sock.connect(serv.getsockname()) # The address argument is ignored since we already connected. self._sock_operation(100, 1.5, 'sendto', b"X" * 200000, @@ -269,7 +269,7 @@ class TCPTimeoutTestCase(TimeoutTestCase): # Test sendall() timeout with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as serv: support.bind_port(serv, self.localhost) - serv.listen(5) + serv.listen() self.sock.connect(serv.getsockname()) # Send a lot of data in order to bypass buffering in the TCP stack. self._sock_operation(100, 1.5, 'sendall', b"X" * 200000) diff --git a/Lib/test/test_tokenize.py b/Lib/test/test_tokenize.py index 38611a7..8f74a06 100644 --- a/Lib/test/test_tokenize.py +++ b/Lib/test/test_tokenize.py @@ -464,7 +464,7 @@ Additive Multiplicative - >>> dump_tokens("x = 1//1*1/5*12%0x12") + >>> dump_tokens("x = 1//1*1/5*12%0x12@42") ENCODING 'utf-8' (0, 0) (0, 0) NAME 'x' (1, 0) (1, 1) OP '=' (1, 2) (1, 3) @@ -479,6 +479,8 @@ Multiplicative NUMBER '12' (1, 13) (1, 15) OP '%' (1, 15) (1, 16) NUMBER '0x12' (1, 16) (1, 20) + OP '@' (1, 20) (1, 21) + NUMBER '42' (1, 21) (1, 23) Unary @@ -1154,6 +1156,7 @@ class TestTokenize(TestCase): self.assertExactTypeEqual('//', token.DOUBLESLASH) self.assertExactTypeEqual('//=', token.DOUBLESLASHEQUAL) self.assertExactTypeEqual('@', token.AT) + self.assertExactTypeEqual('@=', token.ATEQUAL) self.assertExactTypeEqual('a**2+b**2==c**2', NAME, token.DOUBLESTAR, NUMBER, diff --git a/Lib/test/test_trace.py b/Lib/test/test_trace.py index 1cec710..05bf274 100644 --- a/Lib/test/test_trace.py +++ b/Lib/test/test_trace.py @@ -10,7 +10,6 @@ from trace import CoverageResults, Trace from test.tracedmodules import testmod - #------------------------------- Utilities -----------------------------------# def fix_ext_py(filename): @@ -224,6 +223,11 @@ class TestFuncs(unittest.TestCase): self.addCleanup(sys.settrace, sys.gettrace()) self.tracer = Trace(count=0, trace=0, countfuncs=1) self.filemod = my_file_and_modname() + self._saved_tracefunc = sys.gettrace() + + def tearDown(self): + if self._saved_tracefunc is not None: + sys.settrace(self._saved_tracefunc) def test_simple_caller(self): self.tracer.runfunc(traced_func_simple_caller, 1) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index c295563..6bd6fa6 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -6,6 +6,8 @@ import unittest import re from test.support import run_unittest, Error, captured_output from test.support import TESTFN, unlink, cpython_only +from test.script_helper import assert_python_ok +import textwrap import traceback @@ -92,9 +94,9 @@ class SyntaxTracebackCases(unittest.TestCase): self.assertEqual(len(err), 1) str_value = '<unprintable %s object>' % X.__name__ if X.__module__ in ('__main__', 'builtins'): - str_name = X.__name__ + str_name = X.__qualname__ else: - str_name = '.'.join([X.__module__, X.__name__]) + str_name = '.'.join([X.__module__, X.__qualname__]) self.assertEqual(err[0], "%s: %s\n" % (str_name, str_value)) def test_without_exception(self): @@ -169,6 +171,37 @@ class SyntaxTracebackCases(unittest.TestCase): # Issue #18960: coding spec should has no effect do_test("0\n# coding: GBK\n", "h\xe9 ho", 'utf-8', 5) + def test_print_traceback_at_exit(self): + # Issue #22599: Ensure that it is possible to use the traceback module + # to display an exception at Python exit + code = textwrap.dedent(""" + import sys + import traceback + + class PrintExceptionAtExit(object): + def __init__(self): + try: + x = 1 / 0 + except Exception: + self.exc_info = sys.exc_info() + # self.exc_info[1] (traceback) contains frames: + # explicitly clear the reference to self in the current + # frame to break a reference cycle + self = None + + def __del__(self): + traceback.print_exception(*self.exc_info) + + # Keep a reference in the module namespace to call the destructor + # when the module is unloaded + obj = PrintExceptionAtExit() + """) + rc, stdout, stderr = assert_python_ok('-c', code) + expected = [b'Traceback (most recent call last):', + b' File "<string>", line 8, in __init__', + b'ZeroDivisionError: division by zero'] + self.assertEqual(stderr.splitlines(), expected) + class TracebackFormatTests(unittest.TestCase): diff --git a/Lib/test/test_tuple.py b/Lib/test/test_tuple.py index 51875a1..d711116 100644 --- a/Lib/test/test_tuple.py +++ b/Lib/test/test_tuple.py @@ -6,6 +6,11 @@ import pickle class TupleTest(seq_tests.CommonTest): type2test = tuple + def test_getitem_error(self): + msg = "tuple indices must be integers or slices" + with self.assertRaisesRegex(TypeError, msg): + ()['a'] + def test_constructors(self): super().test_constructors() # calling built-in types without argument must return empty @@ -203,6 +208,14 @@ class TupleTest(seq_tests.CommonTest): with self.assertRaises(TypeError): [3,] + T((1,2)) + def test_lexicographic_ordering(self): + # Issue 21100 + a = self.type2test([1, 2]) + b = self.type2test([1, 2, 0]) + c = self.type2test([1, 3]) + self.assertLess(a, b) + self.assertLess(b, c) + def test_main(): support.run_unittest(TupleTest) diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index ec10752..11d9546 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -343,6 +343,8 @@ class TypesTests(unittest.TestCase): self.assertRaises(ValueError, 3 .__format__, ",n") # can't have ',' with 'c' self.assertRaises(ValueError, 3 .__format__, ",c") + # can't have '#' with 'c' + self.assertRaises(ValueError, 3 .__format__, "#c") # ensure that only int and float type specifiers work for format_spec in ([chr(x) for x in range(ord('a'), ord('z')+1)] + diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py index 7735a6b..e4cd99b 100644 --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -8,6 +8,7 @@ Written by Marc-Andre Lemburg (mal@lemburg.com). import _string import codecs import itertools +import operator import struct import sys import unittest @@ -250,6 +251,7 @@ class UnicodeTest(string_tests.CommonTest, {ord('a'): None, ord('b'): ''}) self.checkequalnofix('xyyx', 'xzx', 'translate', {ord('z'): 'yy'}) + # this needs maketrans() self.checkequalnofix('abababc', 'abababc', 'translate', {'b': '<i>'}) @@ -259,6 +261,33 @@ class UnicodeTest(string_tests.CommonTest, tbl = self.type2test.maketrans('abc', 'xyz', 'd') self.checkequalnofix('xyzzy', 'abdcdcbdddd', 'translate', tbl) + # various tests switching from ASCII to latin1 or the opposite; + # same length, remove a letter, or replace with a longer string. + self.assertEqual("[a]".translate(str.maketrans('a', 'X')), + "[X]") + self.assertEqual("[a]".translate(str.maketrans({'a': 'X'})), + "[X]") + self.assertEqual("[a]".translate(str.maketrans({'a': None})), + "[]") + self.assertEqual("[a]".translate(str.maketrans({'a': 'XXX'})), + "[XXX]") + self.assertEqual("[a]".translate(str.maketrans({'a': '\xe9'})), + "[\xe9]") + self.assertEqual("[a]".translate(str.maketrans({'a': '<\xe9>'})), + "[<\xe9>]") + self.assertEqual("[\xe9]".translate(str.maketrans({'\xe9': 'a'})), + "[a]") + self.assertEqual("[\xe9]".translate(str.maketrans({'\xe9': None})), + "[]") + + # invalid Unicode characters + invalid_char = 0x10ffff+1 + for before in "a\xe9\u20ac\U0010ffff": + mapping = str.maketrans({before: invalid_char}) + text = "[%s]" % before + self.assertRaises(ValueError, text.translate, mapping) + + # errors self.assertRaises(TypeError, self.type2test.maketrans) self.assertRaises(ValueError, self.type2test.maketrans, 'abc', 'defg') self.assertRaises(TypeError, self.type2test.maketrans, 2, 'def') @@ -1154,20 +1183,20 @@ class UnicodeTest(string_tests.CommonTest, self.assertEqual('%.2s' % "a\xe9\u20ac", 'a\xe9') #issue 19995 - class PsuedoInt: + class PseudoInt: def __init__(self, value): self.value = int(value) def __int__(self): return self.value def __index__(self): return self.value - class PsuedoFloat: + class PseudoFloat: def __init__(self, value): self.value = float(value) def __int__(self): return int(self.value) - pi = PsuedoFloat(3.1415) - letter_m = PsuedoInt(109) + pi = PseudoFloat(3.1415) + letter_m = PseudoInt(109) self.assertEqual('%x' % 42, '2a') self.assertEqual('%X' % 15, 'F') self.assertEqual('%o' % 9, '11') @@ -1176,11 +1205,11 @@ class UnicodeTest(string_tests.CommonTest, self.assertEqual('%X' % letter_m, '6D') self.assertEqual('%o' % letter_m, '155') self.assertEqual('%c' % letter_m, 'm') - self.assertWarns(DeprecationWarning, '%x'.__mod__, pi), - self.assertWarns(DeprecationWarning, '%x'.__mod__, 3.14), - self.assertWarns(DeprecationWarning, '%X'.__mod__, 2.11), - self.assertWarns(DeprecationWarning, '%o'.__mod__, 1.79), - self.assertWarns(DeprecationWarning, '%c'.__mod__, pi), + self.assertRaisesRegex(TypeError, '%x format: an integer is required, not float', operator.mod, '%x', 3.14), + self.assertRaisesRegex(TypeError, '%X format: an integer is required, not float', operator.mod, '%X', 2.11), + self.assertRaisesRegex(TypeError, '%o format: an integer is required, not float', operator.mod, '%o', 1.79), + self.assertRaisesRegex(TypeError, '%x format: an integer is required, not PseudoFloat', operator.mod, '%x', pi), + self.assertRaises(TypeError, operator.mod, '%c', pi), def test_formatting_with_enum(self): # issue18780 diff --git a/Lib/test/test_unicodedata.py b/Lib/test/test_unicodedata.py index 707b30e..f8788a0 100644 --- a/Lib/test/test_unicodedata.py +++ b/Lib/test/test_unicodedata.py @@ -21,7 +21,7 @@ errors = 'surrogatepass' class UnicodeMethodsTest(unittest.TestCase): # update this, if the database changes - expectedchecksum = 'e74e878de71b6e780ffac271785c3cb58f6251f3' + expectedchecksum = '618e2c1a22ee79d2235319709f16c50f987ee21f' def test_method_checksum(self): h = hashlib.sha1() @@ -79,8 +79,9 @@ class UnicodeDatabaseTest(unittest.TestCase): class UnicodeFunctionsTest(UnicodeDatabaseTest): - # update this, if the database changes - expectedchecksum = 'f0b74d26776331cc7bdc3a4698f037d73f2cee2b' + # Update this if the database changes. Make sure to do a full rebuild + # (e.g. 'make distclean && make') to get the correct checksum. + expectedchecksum = '585302895deead0c1c8478c51da9241d4efedca9' def test_function_checksum(self): data = [] h = hashlib.sha1() diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py index 4f38f0f..fcd2073 100644 --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -10,7 +10,10 @@ import unittest from unittest.mock import patch from test import support import os -import ssl +try: + import ssl +except ImportError: + ssl = None import sys import tempfile from nturl2path import url2pathname, pathname2url @@ -380,6 +383,7 @@ Content-Type: text/html; charset=iso-8859-1 with support.check_warnings(('',DeprecationWarning)): urllib.request.URLopener() + @unittest.skipUnless(ssl, "ssl module required") def test_cafile_and_context(self): context = ssl.create_default_context() with self.assertRaises(ValueError): @@ -1346,7 +1350,7 @@ class URLopener_Tests(unittest.TestCase): # serv.settimeout(3) # serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # serv.bind(("", 9093)) -# serv.listen(5) +# serv.listen() # try: # conn, addr = serv.accept() # conn.send("1 Hola mundo\n") diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py index 9ea39a4..823890e 100644 --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -1422,6 +1422,21 @@ class HandlerTests(unittest.TestCase): handler.do_open(conn, req) self.assertTrue(conn.fakesock.closed, "Connection not closed") + def test_auth_prior_handler(self): + pwd_manager = MockPasswordManager() + pwd_manager.add_password(None, 'https://example.com', + 'somebody', 'verysecret') + auth_prior_handler = urllib.request.HTTPBasicPriorAuthHandler( + pwd_manager) + http_hand = MockHTTPSHandler() + + opener = OpenerDirector() + opener.add_handler(http_hand) + opener.add_handler(auth_prior_handler) + + req = Request("https://example.com") + opener.open(req) + self.assertNotIn('Authorization', http_hand.httpconn.req_headers) class MiscTests(unittest.TestCase): diff --git a/Lib/test/test_urllibnet.py b/Lib/test/test_urllibnet.py index 4c8ba2d..42ebb6e 100644 --- a/Lib/test/test_urllibnet.py +++ b/Lib/test/test_urllibnet.py @@ -91,7 +91,8 @@ class urlopenNetworkTests(unittest.TestCase): # test getcode() with the fancy opener to get 404 error codes URL = "http://www.example.com/XXXinvalidXXX" with support.transient_internet(URL): - open_url = urllib.request.FancyURLopener().open(URL) + with self.assertWarns(DeprecationWarning): + open_url = urllib.request.FancyURLopener().open(URL) try: code = open_url.getcode() finally: diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py index 3934811..cb323d3 100644 --- a/Lib/test/test_urlparse.py +++ b/Lib/test/test_urlparse.py @@ -211,10 +211,6 @@ class UrlParseTestCase(unittest.TestCase): # "abnormal" cases from RFC 1808: self.checkJoin(RFC1808_BASE, '', 'http://a/b/c/d;p?q#f') - self.checkJoin(RFC1808_BASE, '../../../g', 'http://a/../g') - self.checkJoin(RFC1808_BASE, '../../../../g', 'http://a/../../g') - self.checkJoin(RFC1808_BASE, '/./g', 'http://a/./g') - self.checkJoin(RFC1808_BASE, '/../g', 'http://a/../g') self.checkJoin(RFC1808_BASE, 'g.', 'http://a/b/c/g.') self.checkJoin(RFC1808_BASE, '.g', 'http://a/b/c/.g') self.checkJoin(RFC1808_BASE, 'g..', 'http://a/b/c/g..') @@ -229,6 +225,13 @@ class UrlParseTestCase(unittest.TestCase): #self.checkJoin(RFC1808_BASE, 'http:g', 'http:g') #self.checkJoin(RFC1808_BASE, 'http:', 'http:') + # XXX: The following tests are no longer compatible with RFC3986 + # self.checkJoin(RFC1808_BASE, '../../../g', 'http://a/../g') + # self.checkJoin(RFC1808_BASE, '../../../../g', 'http://a/../../g') + # self.checkJoin(RFC1808_BASE, '/./g', 'http://a/./g') + # self.checkJoin(RFC1808_BASE, '/../g', 'http://a/../g') + + def test_RFC2368(self): # Issue 11467: path that starts with a number is not parsed correctly self.assertEqual(urllib.parse.urlparse('mailto:1337@example.org'), @@ -259,10 +262,6 @@ class UrlParseTestCase(unittest.TestCase): self.checkJoin(RFC2396_BASE, '../../', 'http://a/') self.checkJoin(RFC2396_BASE, '../../g', 'http://a/g') self.checkJoin(RFC2396_BASE, '', RFC2396_BASE) - self.checkJoin(RFC2396_BASE, '../../../g', 'http://a/../g') - self.checkJoin(RFC2396_BASE, '../../../../g', 'http://a/../../g') - self.checkJoin(RFC2396_BASE, '/./g', 'http://a/./g') - self.checkJoin(RFC2396_BASE, '/../g', 'http://a/../g') self.checkJoin(RFC2396_BASE, 'g.', 'http://a/b/c/g.') self.checkJoin(RFC2396_BASE, '.g', 'http://a/b/c/.g') self.checkJoin(RFC2396_BASE, 'g..', 'http://a/b/c/g..') @@ -278,10 +277,17 @@ class UrlParseTestCase(unittest.TestCase): self.checkJoin(RFC2396_BASE, 'g#s/./x', 'http://a/b/c/g#s/./x') self.checkJoin(RFC2396_BASE, 'g#s/../x', 'http://a/b/c/g#s/../x') + # XXX: The following tests are no longer compatible with RFC3986 + # self.checkJoin(RFC2396_BASE, '../../../g', 'http://a/../g') + # self.checkJoin(RFC2396_BASE, '../../../../g', 'http://a/../../g') + # self.checkJoin(RFC2396_BASE, '/./g', 'http://a/./g') + # self.checkJoin(RFC2396_BASE, '/../g', 'http://a/../g') + + def test_RFC3986(self): # Test cases from RFC3986 self.checkJoin(RFC3986_BASE, '?y','http://a/b/c/d;p?y') - self.checkJoin(RFC2396_BASE, ';x', 'http://a/b/c/;x') + self.checkJoin(RFC3986_BASE, ';x', 'http://a/b/c/;x') self.checkJoin(RFC3986_BASE, 'g:h','g:h') self.checkJoin(RFC3986_BASE, 'g','http://a/b/c/g') self.checkJoin(RFC3986_BASE, './g','http://a/b/c/g') @@ -305,17 +311,17 @@ class UrlParseTestCase(unittest.TestCase): self.checkJoin(RFC3986_BASE, '../..','http://a/') self.checkJoin(RFC3986_BASE, '../../','http://a/') self.checkJoin(RFC3986_BASE, '../../g','http://a/g') + self.checkJoin(RFC3986_BASE, '../../../g', 'http://a/g') #Abnormal Examples # The 'abnormal scenarios' are incompatible with RFC2986 parsing # Tests are here for reference. - #self.checkJoin(RFC3986_BASE, '../../../g','http://a/g') - #self.checkJoin(RFC3986_BASE, '../../../../g','http://a/g') - #self.checkJoin(RFC3986_BASE, '/./g','http://a/g') - #self.checkJoin(RFC3986_BASE, '/../g','http://a/g') - + self.checkJoin(RFC3986_BASE, '../../../g','http://a/g') + self.checkJoin(RFC3986_BASE, '../../../../g','http://a/g') + self.checkJoin(RFC3986_BASE, '/./g','http://a/g') + self.checkJoin(RFC3986_BASE, '/../g','http://a/g') self.checkJoin(RFC3986_BASE, 'g.','http://a/b/c/g.') self.checkJoin(RFC3986_BASE, '.g','http://a/b/c/.g') self.checkJoin(RFC3986_BASE, 'g..','http://a/b/c/g..') @@ -355,10 +361,8 @@ class UrlParseTestCase(unittest.TestCase): self.checkJoin(SIMPLE_BASE, '../g','http://a/b/g') self.checkJoin(SIMPLE_BASE, '../..','http://a/') self.checkJoin(SIMPLE_BASE, '../../g','http://a/g') - self.checkJoin(SIMPLE_BASE, '../../../g','http://a/../g') self.checkJoin(SIMPLE_BASE, './../g','http://a/b/g') self.checkJoin(SIMPLE_BASE, './g/.','http://a/b/c/g/') - self.checkJoin(SIMPLE_BASE, '/./g','http://a/./g') self.checkJoin(SIMPLE_BASE, 'g/./h','http://a/b/c/g/h') self.checkJoin(SIMPLE_BASE, 'g/../h','http://a/b/c/h') self.checkJoin(SIMPLE_BASE, 'http:g','http://a/b/c/g') @@ -372,6 +376,22 @@ class UrlParseTestCase(unittest.TestCase): self.checkJoin('svn://pathtorepo/dir1', 'dir2', 'svn://pathtorepo/dir2') self.checkJoin('svn+ssh://pathtorepo/dir1', 'dir2', 'svn+ssh://pathtorepo/dir2') + # XXX: The following tests are no longer compatible with RFC3986 + # self.checkJoin(SIMPLE_BASE, '../../../g','http://a/../g') + # self.checkJoin(SIMPLE_BASE, '/./g','http://a/./g') + + # test for issue22118 duplicate slashes + self.checkJoin(SIMPLE_BASE + '/', 'foo', SIMPLE_BASE + '/foo') + + # Non-RFC-defined tests, covering variations of base and trailing + # slashes + self.checkJoin('http://a/b/c/d/e/', '../../f/g/', 'http://a/b/c/f/g/') + self.checkJoin('http://a/b/c/d/e', '../../f/g/', 'http://a/b/f/g/') + self.checkJoin('http://a/b/c/d/e/', '/../../f/g/', 'http://a/f/g/') + self.checkJoin('http://a/b/c/d/e', '/../../f/g/', 'http://a/f/g/') + self.checkJoin('http://a/b/c/d/e/', '../../f/g', 'http://a/b/c/f/g') + self.checkJoin('http://a/b/', '../../f/g/', 'http://a/f/g/') + def test_RFC2732(self): str_cases = [ ('http://Test.python.org:5432/foo/', 'test.python.org', 5432), diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py index 1e8cba3..fcb8454 100644 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -1,9 +1,10 @@ -import unittest +import unittest.mock from test import support import builtins import io import os import shutil +import subprocess import uuid def importable(name): @@ -412,28 +413,27 @@ class TestUUID(unittest.TestCase): class TestInternals(unittest.TestCase): @unittest.skipUnless(os.name == 'posix', 'requires Posix') def test_find_mac(self): - data = '''\ - + data = ''' fake hwaddr cscotun0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 eth0 Link encap:Ethernet HWaddr 12:34:56:78:90:ab ''' - def mock_popen(cmd): - return io.StringIO(data) - - if shutil.which('ifconfig') is None: - path = os.pathsep.join(('/sbin', '/usr/sbin')) - if shutil.which('ifconfig', path=path) is None: - self.skipTest('requires ifconfig') - - with support.swap_attr(os, 'popen', mock_popen): - mac = uuid._find_mac( - command='ifconfig', - args='', - hw_identifiers=['hwaddr'], - get_index=lambda x: x + 1, - ) - self.assertEqual(mac, 0x1234567890ab) + + popen = unittest.mock.MagicMock() + popen.stdout = io.BytesIO(data.encode()) + + with unittest.mock.patch.object(shutil, 'which', + return_value='/sbin/ifconfig'): + with unittest.mock.patch.object(subprocess, 'Popen', + return_value=popen): + mac = uuid._find_mac( + command='ifconfig', + args='', + hw_identifiers=[b'hwaddr'], + get_index=lambda x: x + 1, + ) + + self.assertEqual(mac, 0x1234567890ab) def check_node(self, node, requires=None, network=False): if requires and node is None: @@ -454,6 +454,11 @@ eth0 Link encap:Ethernet HWaddr 12:34:56:78:90:ab self.check_node(node, 'ifconfig', True) @unittest.skipUnless(os.name == 'posix', 'requires Posix') + def test_ip_getnode(self): + node = uuid._ip_getnode() + self.check_node(node, 'ip', True) + + @unittest.skipUnless(os.name == 'posix', 'requires Posix') def test_arp_getnode(self): node = uuid._arp_getnode() self.check_node(node, 'arp', True) diff --git a/Lib/test/test_wait3.py b/Lib/test/test_wait3.py index f6a065d..bb71481 100644 --- a/Lib/test/test_wait3.py +++ b/Lib/test/test_wait3.py @@ -18,7 +18,8 @@ class Wait3Test(ForkWait): # This many iterations can be required, since some previously run # tests (e.g. test_ctypes) could have spawned a lot of children # very quickly. - for i in range(30): + deadline = time.monotonic() + 10.0 + while time.monotonic() <= deadline: # wait3() shouldn't hang, but some of the buildbots seem to hang # in the forking tests. This is an attempt to fix the problem. spid, status, rusage = os.wait3(os.WNOHANG) diff --git a/Lib/test/test_wait4.py b/Lib/test/test_wait4.py index 352c11a..b427a9b 100644 --- a/Lib/test/test_wait4.py +++ b/Lib/test/test_wait4.py @@ -19,13 +19,14 @@ class Wait4Test(ForkWait): # Issue #11185: wait4 is broken on AIX and will always return 0 # with WNOHANG. option = 0 - for i in range(10): + deadline = time.monotonic() + 10.0 + while time.monotonic() <= deadline: # wait4() shouldn't hang, but some of the buildbots seem to hang # in the forking tests. This is an attempt to fix the problem. spid, status, rusage = os.wait4(cpid, option) if spid == cpid: break - time.sleep(1.0) + time.sleep(0.1) self.assertEqual(spid, cpid) self.assertEqual(status, 0, "cause = %d, exit = %d" % (status&0xff, status>>8)) self.assertTrue(rusage) diff --git a/Lib/test/test_warnings.py b/Lib/test/test_warnings.py index fb01b83..9ac2139 100644 --- a/Lib/test/test_warnings.py +++ b/Lib/test/test_warnings.py @@ -5,7 +5,7 @@ from io import StringIO import sys import unittest from test import support -from test.script_helper import assert_python_ok +from test.script_helper import assert_python_ok, assert_python_failure from test import warning_tests @@ -432,6 +432,41 @@ class WarnTests(BaseTest): with self.assertRaises(ValueError): self.module.warn(BadStrWarning()) + def test_warning_classes(self): + class MyWarningClass(Warning): + pass + + class NonWarningSubclass: + pass + + # passing a non-subclass of Warning should raise a TypeError + with self.assertRaises(TypeError) as cm: + self.module.warn('bad warning category', '') + self.assertIn('category must be a Warning subclass, not ', + str(cm.exception)) + + with self.assertRaises(TypeError) as cm: + self.module.warn('bad warning category', NonWarningSubclass) + self.assertIn('category must be a Warning subclass, not ', + str(cm.exception)) + + # check that warning instances also raise a TypeError + with self.assertRaises(TypeError) as cm: + self.module.warn('bad warning category', MyWarningClass()) + self.assertIn('category must be a Warning subclass, not ', + str(cm.exception)) + + with self.assertWarns(MyWarningClass) as cm: + self.module.warn('good warning category', MyWarningClass) + self.assertEqual('good warning category', str(cm.warning)) + + with self.assertWarns(UserWarning) as cm: + self.module.warn('good warning category', None) + self.assertEqual('good warning category', str(cm.warning)) + + with self.assertWarns(MyWarningClass) as cm: + self.module.warn('good warning category', MyWarningClass) + self.assertIsInstance(cm.warning, Warning) class CWarnTests(WarnTests, unittest.TestCase): module = c_warnings @@ -821,7 +856,19 @@ class EnvironmentVariableTests(BaseTest): "import sys; sys.stdout.write(str(sys.warnoptions))", PYTHONWARNINGS="ignore::DeprecationWarning") self.assertEqual(stdout, - b"['ignore::UnicodeWarning', 'ignore::DeprecationWarning']") + b"['ignore::DeprecationWarning', 'ignore::UnicodeWarning']") + + def test_conflicting_envvar_and_command_line(self): + rc, stdout, stderr = assert_python_failure("-Werror::DeprecationWarning", "-c", + "import sys, warnings; sys.stdout.write(str(sys.warnoptions)); " + "warnings.warn('Message', DeprecationWarning)", + PYTHONWARNINGS="default::DeprecationWarning") + self.assertEqual(stdout, + b"['default::DeprecationWarning', 'error::DeprecationWarning']") + self.assertEqual(stderr.splitlines(), + [b"Traceback (most recent call last):", + b" File \"<string>\", line 1, in <module>", + b"DeprecationWarning: Message"]) @unittest.skipUnless(sys.getfilesystemencoding() != 'ascii', 'requires non-ascii filesystemencoding') diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index 3e7347c..e735376 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -92,6 +92,18 @@ class ReferencesTestCase(TestBase): self.check_basic_callback(create_function) self.check_basic_callback(create_bound_method) + @support.cpython_only + def test_cfunction(self): + import _testcapi + create_cfunction = _testcapi.create_cfunction + f = create_cfunction() + wr = weakref.ref(f) + self.assertIs(wr(), f) + del f + self.assertIsNone(wr()) + self.check_basic_ref(create_cfunction) + self.check_basic_callback(create_cfunction) + def test_multiple_callbacks(self): o = C() ref1 = weakref.ref(o, self.callback) @@ -1574,6 +1586,14 @@ class MappingTestCase(TestBase): self.assertEqual(len(d), 0) self.assertEqual(count, 2) + def test_make_weak_valued_dict_repr(self): + dict = weakref.WeakValueDictionary() + self.assertRegex(repr(dict), '<WeakValueDictionary at 0x.*>') + + def test_make_weak_keyed_dict_repr(self): + dict = weakref.WeakKeyDictionary() + self.assertRegex(repr(dict), '<WeakKeyDictionary at 0x.*>') + from test import mapping_tests class WeakValueDictionaryTestCase(mapping_tests.BasicTestMappingProtocol): diff --git a/Lib/test/test_wsgiref.py b/Lib/test/test_wsgiref.py index e213d77..479fcbc 100644 --- a/Lib/test/test_wsgiref.py +++ b/Lib/test/test_wsgiref.py @@ -343,6 +343,7 @@ class HeaderTests(TestCase): def testMappingInterface(self): test = [('x','y')] + self.assertEqual(len(Headers()), 0) self.assertEqual(len(Headers([])),0) self.assertEqual(len(Headers(test[:])),1) self.assertEqual(Headers(test[:]).keys(), ['x']) @@ -350,7 +351,7 @@ class HeaderTests(TestCase): self.assertEqual(Headers(test[:]).items(), test) self.assertIsNot(Headers(test).items(), test) # must be copy! - h=Headers([]) + h = Headers() del h['foo'] # should not raise an error h['Foo'] = 'bar' @@ -375,9 +376,8 @@ class HeaderTests(TestCase): def testRequireList(self): self.assertRaises(TypeError, Headers, "foo") - def testExtras(self): - h = Headers([]) + h = Headers() self.assertEqual(str(h),'\r\n') h.add_header('foo','bar',baz="spam") diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py index 71b590c..dc37be2 100644 --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -287,7 +287,7 @@ class DateTimeTestCase(unittest.TestCase): def test_repr(self): d = datetime.datetime(2007,1,2,3,4,5) t = xmlrpclib.DateTime(d) - val ="<DateTime '20070102T03:04:05' at %x>" % id(t) + val ="<DateTime '20070102T03:04:05' at %#x>" % id(t) self.assertEqual(repr(t), val) def test_decode(self): @@ -713,6 +713,23 @@ class SimpleServerTestCase(BaseServerTestCase): conn.request('POST', '/RPC2 HTTP/1.0\r\nContent-Length: 100\r\n\r\nbye') conn.close() + def test_context_manager(self): + with xmlrpclib.ServerProxy(URL) as server: + server.add(2, 3) + self.assertNotEqual(server('transport')._connection, + (None, None)) + self.assertEqual(server('transport')._connection, + (None, None)) + + def test_context_manager_method_error(self): + try: + with xmlrpclib.ServerProxy(URL) as server: + server.add(2, "a") + except xmlrpclib.Fault: + pass + self.assertEqual(server('transport')._connection, + (None, None)) + class MultiPathServerTestCase(BaseServerTestCase): threadFunc = staticmethod(http_multi_server) @@ -891,7 +908,7 @@ class GzipUtilTestCase(unittest.TestCase): data = b'\0' * (max_gzip_decode + 1) encoded = xmlrpclib.gzip_encode(data) - with self.assertRaisesRegexp(ValueError, + with self.assertRaisesRegex(ValueError, "max gzipped payload length exceeded"): xmlrpclib.gzip_decode(encoded) @@ -919,6 +936,7 @@ class ServerProxyTestCase(unittest.TestCase): p = xmlrpclib.ServerProxy(self.url, transport=t) self.assertEqual(p('transport'), t) + # This is a contrived way to make a failure occur on the server side # in order to test the _send_traceback_header flag on the server class FailingMessageClass(http.client.HTTPMessage): diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index d2da392..57431c7 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -330,6 +330,37 @@ class AbstractTestsWithSourceFile: while zipopen.read1(100): pass + def test_repr(self): + fname = 'file.name' + for f in get_files(self): + with zipfile.ZipFile(f, 'w', self.compression) as zipfp: + zipfp.write(TESTFN, fname) + r = repr(zipfp) + self.assertIn("mode='w'", r) + + with zipfile.ZipFile(f, 'r') as zipfp: + r = repr(zipfp) + if isinstance(f, str): + self.assertIn('filename=%r' % f, r) + else: + self.assertIn('file=%r' % f, r) + self.assertIn("mode='r'", r) + r = repr(zipfp.getinfo(fname)) + self.assertIn('filename=%r' % fname, r) + self.assertIn('filemode=', r) + self.assertIn('file_size=', r) + if self.compression != zipfile.ZIP_STORED: + self.assertIn('compress_type=', r) + self.assertIn('compress_size=', r) + with zipfp.open(fname) as zipopen: + r = repr(zipopen) + self.assertIn('name=%r' % fname, r) + self.assertIn("mode='r'", r) + if self.compression != zipfile.ZIP_STORED: + self.assertIn('compress_type=', r) + self.assertIn('[closed]', repr(zipopen)) + self.assertIn('[closed]', repr(zipfp)) + def tearDown(self): unlink(TESTFN) unlink(TESTFN2) diff --git a/Lib/test/test_zipimport_support.py b/Lib/test/test_zipimport_support.py index 66c3557..2e801a8 100644 --- a/Lib/test/test_zipimport_support.py +++ b/Lib/test/test_zipimport_support.py @@ -39,7 +39,7 @@ def _run_object_doctest(obj, module): # Use the object's fully qualified name if it has one # Otherwise, use the module's name try: - name = "%s.%s" % (obj.__module__, obj.__name__) + name = "%s.%s" % (obj.__module__, obj.__qualname__) except AttributeError: name = module.__name__ for example in finder.find(obj, name, module): |