diff options
Diffstat (limited to 'Lib/test')
279 files changed, 13014 insertions, 4487 deletions
diff --git a/Lib/test/__main__.py b/Lib/test/__main__.py index d5fbe15..19a6b2b 100644 --- a/Lib/test/__main__.py +++ b/Lib/test/__main__.py @@ -1,3 +1,2 @@ -from test import regrtest - -regrtest.main_in_temp_cwd() +from test.libregrtest import main +main() diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 11a6310..cfd801e 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -26,7 +26,7 @@ import test.support.script_helper _multiprocessing = test.support.import_module('_multiprocessing') # Skip tests if sem_open implementation is broken. test.support.import_module('multiprocessing.synchronize') -# import threading after _multiprocessing to raise a more revelant error +# import threading after _multiprocessing to raise a more relevant error # message: "No module named _multiprocessing". _multiprocessing is not compiled # without thread support. import threading @@ -1668,6 +1668,10 @@ def sqr(x, wait=0.0): def mul(x, y): return x*y +def raise_large_valuerror(wait): + time.sleep(wait) + raise ValueError("x" * 1024**2) + class SayWhenError(ValueError): pass def exception_throwing_generator(total, when): @@ -1910,6 +1914,26 @@ class _TestPool(BaseTestCase): with self.assertRaises(RuntimeError): p.apply(self._test_wrapped_exception) + def test_map_no_failfast(self): + # Issue #23992: the fail-fast behaviour when an exception is raised + # during map() would make Pool.join() deadlock, because a worker + # process would fill the result queue (after the result handler thread + # terminated, hence not draining it anymore). + + t_start = time.time() + + with self.assertRaises(ValueError): + with self.Pool(2) as p: + try: + p.map(raise_large_valuerror, [0, 1]) + finally: + time.sleep(0.5) + p.close() + p.join() + + # check that we indeed waited for all jobs + self.assertGreater(time.time() - t_start, 0.9) + def raising(): raise KeyError("key") diff --git a/Lib/test/audiotests.py b/Lib/test/audiotests.py index 0ae2242..d3e8e9e 100644 --- a/Lib/test/audiotests.py +++ b/Lib/test/audiotests.py @@ -1,9 +1,8 @@ from test.support import findfile, TESTFN, unlink -import unittest import array import io import pickle -import sys + class UnseekableIO(io.FileIO): def tell(self): diff --git a/Lib/test/autotest.py b/Lib/test/autotest.py index 41c2088..fa85cc1 100644 --- a/Lib/test/autotest.py +++ b/Lib/test/autotest.py @@ -1,6 +1,5 @@ # This should be equivalent to running regrtest.py from the cmdline. # It can be especially handy if you're in an interactive shell, e.g., # from test import autotest. - -from test import regrtest -regrtest.main() +from test.libregrtest import main +main() diff --git a/Lib/test/capath/0e4015b9.0 b/Lib/test/capath/0e4015b9.0 deleted file mode 100644 index b6d259b..0000000 --- a/Lib/test/capath/0e4015b9.0 +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIClTCCAf6gAwIBAgIJAKGU95wKR8pTMA0GCSqGSIb3DQEBBQUAMHAxCzAJBgNV -BAYTAlhZMRcwFQYDVQQHDA5DYXN0bGUgQW50aHJheDEjMCEGA1UECgwaUHl0aG9u -IFNvZnR3YXJlIEZvdW5kYXRpb24xIzAhBgNVBAMMGnNlbGYtc2lnbmVkLnB5dGhv -bnRlc3QubmV0MB4XDTE0MTEwMjE4MDkyOVoXDTI0MTAzMDE4MDkyOVowcDELMAkG -A1UEBhMCWFkxFzAVBgNVBAcMDkNhc3RsZSBBbnRocmF4MSMwIQYDVQQKDBpQeXRo -b24gU29mdHdhcmUgRm91bmRhdGlvbjEjMCEGA1UEAwwac2VsZi1zaWduZWQucHl0 -aG9udGVzdC5uZXQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANDXQXW9tjyZ -Xt0Iv2tLL1+jinr4wGg36ioLDLFkMf+2Y1GL0v0BnKYG4N1OKlAU15LXGeGer8vm -Sv/yIvmdrELvhAbbo3w4a9TMYQA4XkIVLdvu3mvNOAet+8PMJxn26dbDhG809ALv -EHY57lQsBS3G59RZyBPVqAqmImWNJnVzAgMBAAGjNzA1MCUGA1UdEQQeMByCGnNl -bGYtc2lnbmVkLnB5dGhvbnRlc3QubmV0MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcN -AQEFBQADgYEAIuzAhgMouJpNdf3URCHIineyoSt6WK/9+eyUcjlKOrDoXNZaD72h -TXMeKYoWvJyVcSLKL8ckPtDobgP2OTt0UkyAaj0n+ZHaqq1lH2yVfGUA1ILJv515 -C8BqbvVZuqm3i7ygmw3bqE/lYMgOrYtXXnqOrz6nvsE6Yc9V9rFflOM= ------END CERTIFICATE----- diff --git a/Lib/test/capath/b1930218.0 b/Lib/test/capath/b1930218.0 new file mode 100644 index 0000000..373349c --- /dev/null +++ b/Lib/test/capath/b1930218.0 @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIJALCSZLHy2iHQMA0GCSqGSIb3DQEBBQUAME0xCzAJBgNV +BAYTAlhZMSYwJAYDVQQKDB1QeXRob24gU29mdHdhcmUgRm91bmRhdGlvbiBDQTEW +MBQGA1UEAwwNb3VyLWNhLXNlcnZlcjAeFw0xMzAxMDQxOTQ3MDdaFw0yMzAxMDIx +OTQ3MDdaME0xCzAJBgNVBAYTAlhZMSYwJAYDVQQKDB1QeXRob24gU29mdHdhcmUg +Rm91bmRhdGlvbiBDQTEWMBQGA1UEAwwNb3VyLWNhLXNlcnZlcjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAOfe6eMMnwC2of0rW5bSb8zgvoa5IF7sA3pV +q+qk6flJhdJm1e3HeupWji2P50LiYiipn9Ybjuu1tJyfFKvf5pSLdh0+bSRh7Qy/ +AIphDN9cyDZzFgDNR7ptpKR0iIMjChn8Cac8SkvT5x0t5OpMVCHzJtuJNxjUArtA +Ml+k/y0c99S77I7PXIKs5nwIbEiFYQd/JeBc4Lw0X+C5BEd1yEcLjbzWyGhfM4Ni +0iBENbGtgRqKzbw1sFyLR9YY6ZwYl8wBPCnM6B7k5MG43ufCERiHWpM02KYl9xRx +6+QhotIPLi7UYgA109bvXGBLTKkU4t0VWEY3Mya35y5d7ULkxU0CAwEAAaNQME4w +HQYDVR0OBBYEFLzdYtl22hvSVGvP4GabHh57VgwLMB8GA1UdIwQYMBaAFLzdYtl2 +2hvSVGvP4GabHh57VgwLMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB +AH0K9cuN0129mY74Kw+668LZpidPLnsvDmTYHDVQTu78kLmNbajFxgawr/Mtvzu4 +QgfdGH1tlVRXhRhgRy/reBv56Bf9Wg2HFyisTGrmvCn09FVwKULeheqrbCMGZDB1 +Ao5TvF4BMzfMHs24pP3K5F9lO4MchvFVAqA6j9uRt0AUtOeN0u5zuuPlNC28lG9O +JAb3X4sOp45r3l519DKaULFEM5rQBeJ4gv/b2opj66nd0b+gYa3jnookXWIO50yR +f+/fNDY7L131hLIvxG2TlhpvMCjx2hKaZLRAMx293itTqOq+1rxOlvVE+zIYrtUf +9mmvtk57HVjsO6lTo15YyJ4= +-----END CERTIFICATE----- diff --git a/Lib/test/capath/ce7b8643.0 b/Lib/test/capath/ce7b8643.0 deleted file mode 100644 index b6d259b..0000000 --- a/Lib/test/capath/ce7b8643.0 +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIClTCCAf6gAwIBAgIJAKGU95wKR8pTMA0GCSqGSIb3DQEBBQUAMHAxCzAJBgNV -BAYTAlhZMRcwFQYDVQQHDA5DYXN0bGUgQW50aHJheDEjMCEGA1UECgwaUHl0aG9u -IFNvZnR3YXJlIEZvdW5kYXRpb24xIzAhBgNVBAMMGnNlbGYtc2lnbmVkLnB5dGhv -bnRlc3QubmV0MB4XDTE0MTEwMjE4MDkyOVoXDTI0MTAzMDE4MDkyOVowcDELMAkG -A1UEBhMCWFkxFzAVBgNVBAcMDkNhc3RsZSBBbnRocmF4MSMwIQYDVQQKDBpQeXRo -b24gU29mdHdhcmUgRm91bmRhdGlvbjEjMCEGA1UEAwwac2VsZi1zaWduZWQucHl0 -aG9udGVzdC5uZXQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANDXQXW9tjyZ -Xt0Iv2tLL1+jinr4wGg36ioLDLFkMf+2Y1GL0v0BnKYG4N1OKlAU15LXGeGer8vm -Sv/yIvmdrELvhAbbo3w4a9TMYQA4XkIVLdvu3mvNOAet+8PMJxn26dbDhG809ALv -EHY57lQsBS3G59RZyBPVqAqmImWNJnVzAgMBAAGjNzA1MCUGA1UdEQQeMByCGnNl -bGYtc2lnbmVkLnB5dGhvbnRlc3QubmV0MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcN -AQEFBQADgYEAIuzAhgMouJpNdf3URCHIineyoSt6WK/9+eyUcjlKOrDoXNZaD72h -TXMeKYoWvJyVcSLKL8ckPtDobgP2OTt0UkyAaj0n+ZHaqq1lH2yVfGUA1ILJv515 -C8BqbvVZuqm3i7ygmw3bqE/lYMgOrYtXXnqOrz6nvsE6Yc9V9rFflOM= ------END CERTIFICATE----- diff --git a/Lib/test/capath/ceff1710.0 b/Lib/test/capath/ceff1710.0 new file mode 100644 index 0000000..373349c --- /dev/null +++ b/Lib/test/capath/ceff1710.0 @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIJALCSZLHy2iHQMA0GCSqGSIb3DQEBBQUAME0xCzAJBgNV +BAYTAlhZMSYwJAYDVQQKDB1QeXRob24gU29mdHdhcmUgRm91bmRhdGlvbiBDQTEW +MBQGA1UEAwwNb3VyLWNhLXNlcnZlcjAeFw0xMzAxMDQxOTQ3MDdaFw0yMzAxMDIx +OTQ3MDdaME0xCzAJBgNVBAYTAlhZMSYwJAYDVQQKDB1QeXRob24gU29mdHdhcmUg +Rm91bmRhdGlvbiBDQTEWMBQGA1UEAwwNb3VyLWNhLXNlcnZlcjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAOfe6eMMnwC2of0rW5bSb8zgvoa5IF7sA3pV +q+qk6flJhdJm1e3HeupWji2P50LiYiipn9Ybjuu1tJyfFKvf5pSLdh0+bSRh7Qy/ +AIphDN9cyDZzFgDNR7ptpKR0iIMjChn8Cac8SkvT5x0t5OpMVCHzJtuJNxjUArtA +Ml+k/y0c99S77I7PXIKs5nwIbEiFYQd/JeBc4Lw0X+C5BEd1yEcLjbzWyGhfM4Ni +0iBENbGtgRqKzbw1sFyLR9YY6ZwYl8wBPCnM6B7k5MG43ufCERiHWpM02KYl9xRx +6+QhotIPLi7UYgA109bvXGBLTKkU4t0VWEY3Mya35y5d7ULkxU0CAwEAAaNQME4w +HQYDVR0OBBYEFLzdYtl22hvSVGvP4GabHh57VgwLMB8GA1UdIwQYMBaAFLzdYtl2 +2hvSVGvP4GabHh57VgwLMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB +AH0K9cuN0129mY74Kw+668LZpidPLnsvDmTYHDVQTu78kLmNbajFxgawr/Mtvzu4 +QgfdGH1tlVRXhRhgRy/reBv56Bf9Wg2HFyisTGrmvCn09FVwKULeheqrbCMGZDB1 +Ao5TvF4BMzfMHs24pP3K5F9lO4MchvFVAqA6j9uRt0AUtOeN0u5zuuPlNC28lG9O +JAb3X4sOp45r3l519DKaULFEM5rQBeJ4gv/b2opj66nd0b+gYa3jnookXWIO50yR +f+/fNDY7L131hLIvxG2TlhpvMCjx2hKaZLRAMx293itTqOq+1rxOlvVE+zIYrtUf +9mmvtk57HVjsO6lTo15YyJ4= +-----END CERTIFICATE----- diff --git a/Lib/test/cmath_testcases.txt b/Lib/test/cmath_testcases.txt index 9b08653..dd7e458 100644 --- a/Lib/test/cmath_testcases.txt +++ b/Lib/test/cmath_testcases.txt @@ -53,6 +53,12 @@ -- MPFR homepage at http://www.mpfr.org for more information about the -- MPFR project. +-- A minority of the test cases were generated with the help of +-- mpmath 0.19 at 100 bit accuracy (http://mpmath.org) to improve +-- coverage of real functions with real-valued arguments. These are +-- used in test.test_math.MathTests.test_testfile, as well as in +-- test_cmath. + -------------------------- -- acos: Inverse cosine -- @@ -848,6 +854,18 @@ atan0302 atan 9.9999999999999999e-161 -1.0 -> 0.78539816339744828 -184.553381029 atan0303 atan -1e-165 1.0 -> -0.78539816339744828 190.30984376228875 atan0304 atan -9.9998886718268301e-321 -1.0 -> -0.78539816339744828 -368.76019403576692 +-- Additional real values (mpmath) +atan0400 atan 1.7976931348623157e+308 0.0 -> 1.5707963267948966192 0.0 +atan0401 atan -1.7976931348623157e+308 0.0 -> -1.5707963267948966192 0.0 +atan0402 atan 1e-17 0.0 -> 1.0000000000000000715e-17 0.0 +atan0403 atan -1e-17 0.0 -> -1.0000000000000000715e-17 0.0 +atan0404 atan 0.0001 0.0 -> 0.000099999999666666673459 0.0 +atan0405 atan -0.0001 0.0 -> -0.000099999999666666673459 0.0 +atan0406 atan 0.999999999999999 0.0 -> 0.78539816339744781002 0.0 +atan0407 atan 1.000000000000001 0.0 -> 0.78539816339744886473 0.0 +atan0408 atan 14.101419947171719 0.0 -> 1.4999999999999999969 0.0 +atan0409 atan 1255.7655915007897 0.0 -> 1.5700000000000000622 0.0 + -- special values atan1000 atan -0.0 0.0 -> -0.0 0.0 atan1001 atan nan 0.0 -> nan 0.0 @@ -1514,6 +1532,11 @@ sqrt0131 sqrt -1.5477066694467245e-310 -0.0 -> 0.0 -1.2440685951533077e-155 sqrt0140 sqrt 1.6999999999999999e+308 -1.6999999999999999e+308 -> 1.4325088230154573e+154 -5.9336458271212207e+153 sqrt0141 sqrt -1.797e+308 -9.9999999999999999e+306 -> 3.7284476432057307e+152 -1.3410406899802901e+154 +-- Additional real values (mpmath) +sqrt0150 sqrt 1.7976931348623157e+308 0.0 -> 1.3407807929942596355e+154 0.0 +sqrt0151 sqrt 2.2250738585072014e-308 0.0 -> 1.4916681462400413487e-154 0.0 +sqrt0152 sqrt 5e-324 0.0 -> 2.2227587494850774834e-162 0.0 + -- special values sqrt1000 sqrt 0.0 0.0 -> 0.0 0.0 sqrt1001 sqrt -0.0 0.0 -> 0.0 0.0 @@ -1616,6 +1639,20 @@ exp0052 exp 710.0 1.5 -> 1.5802653829857376e+307 inf overflow exp0053 exp 710.0 1.6 -> -6.5231579995501372e+306 inf overflow exp0054 exp 710.0 2.8 -> -inf 7.4836177417448528e+307 overflow +-- Additional real values (mpmath) +exp0070 exp 1e-08 0.0 -> 1.00000001000000005 0.0 +exp0071 exp 0.0003 0.0 -> 1.0003000450045003375 0.0 +exp0072 exp 0.2 0.0 -> 1.2214027581601698475 0.0 +exp0073 exp 1.0 0.0 -> 2.7182818284590452354 0.0 +exp0074 exp -1e-08 0.0 -> 0.99999999000000005 0.0 +exp0075 exp -0.0003 0.0 -> 0.99970004499550033751 0.0 +exp0076 exp -1.0 0.0 -> 0.3678794411714423216 0.0 +exp0077 exp 2.220446049250313e-16 0.0 -> 1.000000000000000222 0.0 +exp0078 exp -1.1102230246251565e-16 0.0 -> 0.99999999999999988898 0.0 +exp0079 exp 2.302585092994046 0.0 -> 10.000000000000002171 0.0 +exp0080 exp -2.302585092994046 0.0 -> 0.099999999999999978292 0.0 +exp0081 exp 709.7827 0.0 -> 1.7976699566638014654e+308 0.0 + -- special values exp1000 exp 0.0 0.0 -> 1.0 0.0 exp1001 exp -0.0 0.0 -> 1.0 0.0 @@ -1708,6 +1745,23 @@ cosh0023 cosh 2.218885944363501 2.0015727395883687 -> -1.94294321081968 4.129026 cosh0030 cosh 710.5 2.3519999999999999 -> -1.2967465239355998e+308 1.3076707908857333e+308 cosh0031 cosh -710.5 0.69999999999999996 -> 1.4085466381392499e+308 -1.1864024666450239e+308 +-- Additional real values (mpmath) +cosh0050 cosh 1e-150 0.0 -> 1.0 0.0 +cosh0051 cosh 1e-18 0.0 -> 1.0 0.0 +cosh0052 cosh 1e-09 0.0 -> 1.0000000000000000005 0.0 +cosh0053 cosh 0.0003 0.0 -> 1.0000000450000003375 0.0 +cosh0054 cosh 0.2 0.0 -> 1.0200667556190758485 0.0 +cosh0055 cosh 1.0 0.0 -> 1.5430806348152437785 0.0 +cosh0056 cosh -1e-18 0.0 -> 1.0 -0.0 +cosh0057 cosh -0.0003 0.0 -> 1.0000000450000003375 -0.0 +cosh0058 cosh -1.0 0.0 -> 1.5430806348152437785 -0.0 +cosh0059 cosh 1.3169578969248168 0.0 -> 2.0000000000000001504 0.0 +cosh0060 cosh -1.3169578969248168 0.0 -> 2.0000000000000001504 -0.0 +cosh0061 cosh 17.328679513998633 0.0 -> 16777216.000000021938 0.0 +cosh0062 cosh 18.714973875118524 0.0 -> 67108864.000000043662 0.0 +cosh0063 cosh 709.7827 0.0 -> 8.9883497833190073272e+307 0.0 +cosh0064 cosh -709.7827 0.0 -> 8.9883497833190073272e+307 -0.0 + -- special values cosh1000 cosh 0.0 0.0 -> 1.0 0.0 cosh1001 cosh 0.0 inf -> nan 0.0 invalid ignore-imag-sign @@ -1800,6 +1854,24 @@ sinh0023 sinh 0.043713693678420068 0.22512549887532657 -> 0.042624198673416713 0 sinh0030 sinh 710.5 -2.3999999999999999 -> -1.3579970564885919e+308 -1.24394470907798e+308 sinh0031 sinh -710.5 0.80000000000000004 -> -1.2830671601735164e+308 1.3210954193997678e+308 +-- Additional real values (mpmath) +sinh0050 sinh 1e-100 0.0 -> 1.00000000000000002e-100 0.0 +sinh0051 sinh 5e-17 0.0 -> 4.9999999999999998955e-17 0.0 +sinh0052 sinh 1e-16 0.0 -> 9.999999999999999791e-17 0.0 +sinh0053 sinh 3.7e-08 0.0 -> 3.7000000000000008885e-8 0.0 +sinh0054 sinh 0.001 0.0 -> 0.0010000001666666750208 0.0 +sinh0055 sinh 0.2 0.0 -> 0.20133600254109399895 0.0 +sinh0056 sinh 1.0 0.0 -> 1.1752011936438014569 0.0 +sinh0057 sinh -3.7e-08 0.0 -> -3.7000000000000008885e-8 0.0 +sinh0058 sinh -0.001 0.0 -> -0.0010000001666666750208 0.0 +sinh0059 sinh -1.0 0.0 -> -1.1752011936438014569 0.0 +sinh0060 sinh 1.4436354751788103 0.0 -> 1.9999999999999999078 0.0 +sinh0061 sinh -1.4436354751788103 0.0 -> -1.9999999999999999078 0.0 +sinh0062 sinh 17.328679513998633 0.0 -> 16777215.999999992136 0.0 +sinh0063 sinh 18.714973875118524 0.0 -> 67108864.000000036211 0.0 +sinh0064 sinh 709.7827 0.0 -> 8.9883497833190073272e+307 0.0 +sinh0065 sinh -709.7827 0.0 -> -8.9883497833190073272e+307 0.0 + -- special values sinh1000 sinh 0.0 0.0 -> 0.0 0.0 sinh1001 sinh 0.0 inf -> 0.0 nan invalid ignore-real-sign @@ -1897,6 +1969,24 @@ tanh0031 tanh -711 7.4000000000000004 -> -1.0 0.0 tanh0032 tanh 1000 -2.3199999999999998 -> 1.0 0.0 tanh0033 tanh -1.0000000000000001e+300 -9.6699999999999999 -> -1.0 -0.0 +-- Additional real values (mpmath) +tanh0050 tanh 1e-100 0.0 -> 1.00000000000000002e-100 0.0 +tanh0051 tanh 5e-17 0.0 -> 4.9999999999999998955e-17 0.0 +tanh0052 tanh 1e-16 0.0 -> 9.999999999999999791e-17 0.0 +tanh0053 tanh 3.7e-08 0.0 -> 3.6999999999999983559e-8 0.0 +tanh0054 tanh 0.001 0.0 -> 0.00099999966666680002076 0.0 +tanh0055 tanh 0.2 0.0 -> 0.19737532022490401141 0.0 +tanh0056 tanh 1.0 0.0 -> 0.76159415595576488812 0.0 +tanh0057 tanh -3.7e-08 0.0 -> -3.6999999999999983559e-8 0.0 +tanh0058 tanh -0.001 0.0 -> -0.00099999966666680002076 0.0 +tanh0059 tanh -1.0 0.0 -> -0.76159415595576488812 0.0 +tanh0060 tanh 0.5493061443340549 0.0 -> 0.50000000000000003402 0.0 +tanh0061 tanh -0.5493061443340549 0.0 -> -0.50000000000000003402 0.0 +tanh0062 tanh 17.328679513998633 0.0 -> 0.99999999999999822364 0.0 +tanh0063 tanh 18.714973875118524 0.0 -> 0.99999999999999988898 0.0 +tanh0064 tanh 711 0.0 -> 1.0 0.0 +tanh0065 tanh 1.797e+308 0.0 -> 1.0 0.0 + --special values tanh1000 tanh 0.0 0.0 -> 0.0 0.0 tanh1001 tanh 0.0 inf -> nan nan invalid @@ -1985,6 +2075,22 @@ cos0021 cos 4.8048375263775256 0.0062248852898515658 -> 0.092318702015846243 0.0 cos0022 cos 7.9914515433858515 0.71659966615501436 -> -0.17375439906936566 -0.77217043527294582 cos0023 cos 0.45124351152540226 1.6992693993812158 -> 2.543477948972237 -1.1528193694875477 +-- Additional real values (mpmath) +cos0050 cos 1e-150 0.0 -> 1.0 -0.0 +cos0051 cos 1e-18 0.0 -> 1.0 -0.0 +cos0052 cos 1e-09 0.0 -> 0.9999999999999999995 -0.0 +cos0053 cos 0.0003 0.0 -> 0.9999999550000003375 -0.0 +cos0054 cos 0.2 0.0 -> 0.98006657784124162892 -0.0 +cos0055 cos 1.0 0.0 -> 0.5403023058681397174 -0.0 +cos0056 cos -1e-18 0.0 -> 1.0 0.0 +cos0057 cos -0.0003 0.0 -> 0.9999999550000003375 0.0 +cos0058 cos -1.0 0.0 -> 0.5403023058681397174 0.0 +cos0059 cos 1.0471975511965976 0.0 -> 0.50000000000000009945 -0.0 +cos0060 cos 2.5707963267948966 0.0 -> -0.84147098480789647357 -0.0 +cos0061 cos -2.5707963267948966 0.0 -> -0.84147098480789647357 0.0 +cos0062 cos 7.225663103256523 0.0 -> 0.58778525229247407559 -0.0 +cos0063 cos -8.79645943005142 0.0 -> -0.80901699437494722255 0.0 + -- special values cos1000 cos -0.0 0.0 -> 1.0 0.0 cos1001 cos -inf 0.0 -> nan 0.0 invalid ignore-imag-sign @@ -2073,6 +2179,22 @@ sin0021 sin 1.4342727387492671 0.81361889790284347 -> 1.3370960060947923 0.12336 sin0022 sin 1.1518087354403725 4.8597235966150558 -> 58.919141989603041 26.237003403758852 sin0023 sin 0.00087773078406649192 34.792379211312095 -> 565548145569.38245 644329685822700.62 +-- Additional real values (mpmath) +sin0050 sin 1e-100 0.0 -> 1.00000000000000002e-100 0.0 +sin0051 sin 3.7e-08 0.0 -> 3.6999999999999992001e-8 0.0 +sin0052 sin 0.001 0.0 -> 0.00099999983333334168748 0.0 +sin0053 sin 0.2 0.0 -> 0.19866933079506122634 0.0 +sin0054 sin 1.0 0.0 -> 0.84147098480789650665 0.0 +sin0055 sin -3.7e-08 0.0 -> -3.6999999999999992001e-8 0.0 +sin0056 sin -0.001 0.0 -> -0.00099999983333334168748 0.0 +sin0057 sin -1.0 0.0 -> -0.84147098480789650665 0.0 +sin0058 sin 0.5235987755982989 0.0 -> 0.50000000000000004642 0.0 +sin0059 sin -0.5235987755982989 0.0 -> -0.50000000000000004642 0.0 +sin0060 sin 2.6179938779914944 0.0 -> 0.49999999999999996018 -0.0 +sin0061 sin -2.6179938779914944 0.0 -> -0.49999999999999996018 -0.0 +sin0062 sin 7.225663103256523 0.0 -> 0.80901699437494673648 0.0 +sin0063 sin -8.79645943005142 0.0 -> -0.58778525229247340658 -0.0 + -- special values sin1000 sin -0.0 0.0 -> -0.0 0.0 sin1001 sin -inf 0.0 -> nan 0.0 invalid ignore-imag-sign @@ -2161,6 +2283,25 @@ tan0021 tan 1.7809617968443272 1.5052381702853379 -> -0.044066222118946903 1.093 tan0022 tan 1.1615313900880577 1.7956298728647107 -> 0.041793186826390362 1.0375339546034792 tan0023 tan 0.067014779477908945 5.8517361577457097 -> 2.2088639754800034e-06 0.9999836182420061 +-- Additional real values (mpmath) +tan0050 tan 1e-100 0.0 -> 1.00000000000000002e-100 0.0 +tan0051 tan 3.7e-08 0.0 -> 3.7000000000000017328e-8 0.0 +tan0052 tan 0.001 0.0 -> 0.0010000003333334666875 0.0 +tan0053 tan 0.2 0.0 -> 0.20271003550867249488 0.0 +tan0054 tan 1.0 0.0 -> 1.5574077246549022305 0.0 +tan0055 tan -3.7e-08 0.0 -> -3.7000000000000017328e-8 0.0 +tan0056 tan -0.001 0.0 -> -0.0010000003333334666875 0.0 +tan0057 tan -1.0 0.0 -> -1.5574077246549022305 0.0 +tan0058 tan 0.4636476090008061 0.0 -> 0.49999999999999997163 0.0 +tan0059 tan -0.4636476090008061 0.0 -> -0.49999999999999997163 0.0 +tan0060 tan 1.1071487177940904 0.0 -> 1.9999999999999995298 0.0 +tan0061 tan -1.1071487177940904 0.0 -> -1.9999999999999995298 0.0 +tan0062 tan 1.5 0.0 -> 14.101419947171719388 0.0 +tan0063 tan 1.57 0.0 -> 1255.7655915007896475 0.0 +tan0064 tan 1.5707963267948961 0.0 -> 1978937966095219.0538 0.0 +tan0065 tan 7.225663103256523 0.0 -> 1.3763819204711701522 0.0 +tan0066 tan -8.79645943005142 0.0 -> 0.7265425280053614098 0.0 + -- special values tan1000 tan -0.0 0.0 -> -0.0 0.0 tan1001 tan -inf 0.0 -> nan nan invalid diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index f5222c7..86c9373 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -2,14 +2,22 @@ See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases """ +from test.support import is_resource_enabled + +import itertools +import bisect import copy import decimal import sys +import os import pickle import random +import struct import unittest +from array import array + from operator import lt, le, gt, ge, eq, ne, truediv, floordiv, mod from test import support @@ -284,7 +292,8 @@ class TestTimeZone(unittest.TestCase): with self.assertRaises(TypeError): self.EST.dst(5) def test_tzname(self): - self.assertEqual('UTC+00:00', timezone(ZERO).tzname(None)) + self.assertEqual('UTC', timezone.utc.tzname(None)) + self.assertEqual('UTC', timezone(ZERO).tzname(None)) self.assertEqual('UTC-05:00', timezone(-5 * HOUR).tzname(None)) self.assertEqual('UTC+09:30', timezone(9.5 * HOUR).tzname(None)) self.assertEqual('UTC-00:01', timezone(timedelta(minutes=-1)).tzname(None)) @@ -1558,13 +1567,32 @@ class TestDateTime(TestDate): self.assertEqual(dt, dt2) def test_isoformat(self): - t = self.theclass(2, 3, 2, 4, 5, 1, 123) - self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123") - self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123") - self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123") - self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123") + t = self.theclass(1, 2, 3, 4, 5, 1, 123) + self.assertEqual(t.isoformat(), "0001-02-03T04:05:01.000123") + self.assertEqual(t.isoformat('T'), "0001-02-03T04:05:01.000123") + self.assertEqual(t.isoformat(' '), "0001-02-03 04:05:01.000123") + self.assertEqual(t.isoformat('\x00'), "0001-02-03\x0004:05:01.000123") + self.assertEqual(t.isoformat(timespec='hours'), "0001-02-03T04") + self.assertEqual(t.isoformat(timespec='minutes'), "0001-02-03T04:05") + self.assertEqual(t.isoformat(timespec='seconds'), "0001-02-03T04:05:01") + self.assertEqual(t.isoformat(timespec='milliseconds'), "0001-02-03T04:05:01.000") + self.assertEqual(t.isoformat(timespec='microseconds'), "0001-02-03T04:05:01.000123") + self.assertEqual(t.isoformat(timespec='auto'), "0001-02-03T04:05:01.000123") + self.assertEqual(t.isoformat(sep=' ', timespec='minutes'), "0001-02-03 04:05") + self.assertRaises(ValueError, t.isoformat, timespec='foo') # str is ISO format with the separator forced to a blank. - self.assertEqual(str(t), "0002-03-02 04:05:01.000123") + self.assertEqual(str(t), "0001-02-03 04:05:01.000123") + + t = self.theclass(1, 2, 3, 4, 5, 1, 999500, tzinfo=timezone.utc) + self.assertEqual(t.isoformat(timespec='milliseconds'), "0001-02-03T04:05:01.999+00:00") + + t = self.theclass(1, 2, 3, 4, 5, 1, 999500) + self.assertEqual(t.isoformat(timespec='milliseconds'), "0001-02-03T04:05:01.999") + + t = self.theclass(1, 2, 3, 4, 5, 1) + self.assertEqual(t.isoformat(timespec='auto'), "0001-02-03T04:05:01") + self.assertEqual(t.isoformat(timespec='milliseconds'), "0001-02-03T04:05:01.000") + self.assertEqual(t.isoformat(timespec='microseconds'), "0001-02-03T04:05:01.000000") t = self.theclass(2, 3, 2) self.assertEqual(t.isoformat(), "0002-03-02T00:00:00") @@ -1572,6 +1600,10 @@ class TestDateTime(TestDate): self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00") # str is ISO format with the separator forced to a blank. self.assertEqual(str(t), "0002-03-02 00:00:00") + # ISO format with timezone + tz = FixedOffset(timedelta(seconds=16), 'XXX') + t = self.theclass(2, 3, 2, tzinfo=tz) + self.assertEqual(t.isoformat(), "0002-03-02T00:00:00+00:00:16") def test_format(self): dt = self.theclass(2007, 9, 10, 4, 5, 1, 123) @@ -1691,6 +1723,14 @@ class TestDateTime(TestDate): self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 59, 1000000) + # bad fold + self.assertRaises(ValueError, self.theclass, + 2000, 1, 31, fold=-1) + self.assertRaises(ValueError, self.theclass, + 2000, 1, 31, fold=2) + # Positional fold: + self.assertRaises(TypeError, self.theclass, + 2000, 1, 31, 23, 59, 59, 0, None, 1) def test_hash_equality(self): d = self.theclass(2000, 12, 31, 23, 30, 17) @@ -1874,16 +1914,20 @@ class TestDateTime(TestDate): t = self.theclass(1970, 1, 1, 1, 2, 3, 4) self.assertEqual(t.timestamp(), 18000.0 + 3600 + 2*60 + 3 + 4*1e-6) - # Missing hour may produce platform-dependent result - t = self.theclass(2012, 3, 11, 2, 30) - self.assertIn(self.theclass.fromtimestamp(t.timestamp()), - [t - timedelta(hours=1), t + timedelta(hours=1)]) + # Missing hour + t0 = self.theclass(2012, 3, 11, 2, 30) + t1 = t0.replace(fold=1) + self.assertEqual(self.theclass.fromtimestamp(t1.timestamp()), + t0 - timedelta(hours=1)) + self.assertEqual(self.theclass.fromtimestamp(t0.timestamp()), + t1 + timedelta(hours=1)) # Ambiguous hour defaults to DST t = self.theclass(2012, 11, 4, 1, 30) self.assertEqual(self.theclass.fromtimestamp(t.timestamp()), t) # Timestamp may raise an overflow error on some platforms - for t in [self.theclass(1,1,1), self.theclass(9999,12,12)]: + # XXX: Do we care to support the first and last year? + for t in [self.theclass(2,1,1), self.theclass(9998,12,12)]: try: s = t.timestamp() except OverflowError: @@ -1902,6 +1946,7 @@ class TestDateTime(TestDate): self.assertEqual(t.timestamp(), 18000 + 3600 + 2*60 + 3 + 4*1e-6) + @support.run_with_tz('MSK-03') # Something east of Greenwich def test_microsecond_rounding(self): for fts in [self.theclass.fromtimestamp, self.theclass.utcfromtimestamp]: @@ -2076,11 +2121,22 @@ class TestDateTime(TestDate): self.assertRaises(TypeError, combine) # need an arg self.assertRaises(TypeError, combine, d) # need two args self.assertRaises(TypeError, combine, t, d) # args reversed - self.assertRaises(TypeError, combine, d, t, 1) # too many args + self.assertRaises(TypeError, combine, d, t, 1) # wrong tzinfo type + self.assertRaises(TypeError, combine, d, t, 1, 2) # too many args self.assertRaises(TypeError, combine, "date", "time") # wrong types self.assertRaises(TypeError, combine, d, "time") # wrong type self.assertRaises(TypeError, combine, "date", t) # wrong type + # tzinfo= argument + dt = combine(d, t, timezone.utc) + self.assertIs(dt.tzinfo, timezone.utc) + dt = combine(d, t, tzinfo=timezone.utc) + self.assertIs(dt.tzinfo, timezone.utc) + t = time() + dt = combine(dt, t) + self.assertEqual(dt.date(), d) + self.assertEqual(dt.time(), t) + def test_replace(self): cls = self.theclass args = [1, 2, 3, 4, 5, 6, 7] @@ -2107,6 +2163,7 @@ class TestDateTime(TestDate): self.assertRaises(ValueError, base.replace, year=2001) def test_astimezone(self): + return # The rest is no longer applicable # Pretty boring! The TZ test is more interesting here. astimezone() # simply can't be applied to a naive object. dt = self.theclass.now() @@ -2324,6 +2381,23 @@ class TestTime(HarmlessMixedComparison, unittest.TestCase): self.assertEqual(t.isoformat(), "00:00:00.100000") self.assertEqual(t.isoformat(), str(t)) + t = self.theclass(hour=12, minute=34, second=56, microsecond=123456) + self.assertEqual(t.isoformat(timespec='hours'), "12") + self.assertEqual(t.isoformat(timespec='minutes'), "12:34") + self.assertEqual(t.isoformat(timespec='seconds'), "12:34:56") + self.assertEqual(t.isoformat(timespec='milliseconds'), "12:34:56.123") + self.assertEqual(t.isoformat(timespec='microseconds'), "12:34:56.123456") + self.assertEqual(t.isoformat(timespec='auto'), "12:34:56.123456") + self.assertRaises(ValueError, t.isoformat, timespec='monkey') + + t = self.theclass(hour=12, minute=34, second=56, microsecond=999500) + self.assertEqual(t.isoformat(timespec='milliseconds'), "12:34:56.999") + + t = self.theclass(hour=12, minute=34, second=56, microsecond=0) + self.assertEqual(t.isoformat(timespec='milliseconds'), "12:34:56.000") + self.assertEqual(t.isoformat(timespec='microseconds'), "12:34:56.000000") + self.assertEqual(t.isoformat(timespec='auto'), "12:34:56") + def test_1653736(self): # verify it doesn't accept extra keyword arguments t = self.theclass(second=1) @@ -2582,9 +2656,9 @@ class TZInfoBase: self.assertRaises(ValueError, t.utcoffset) self.assertRaises(ValueError, t.dst) - # Not a whole number of minutes. + # Not a whole number of seconds. class C7(tzinfo): - def utcoffset(self, dt): return timedelta(seconds=61) + def utcoffset(self, dt): return timedelta(microseconds=61) def dst(self, dt): return timedelta(microseconds=-81) t = cls(1, 1, 1, tzinfo=C7()) self.assertRaises(ValueError, t.utcoffset) @@ -3884,7 +3958,7 @@ class Oddballs(unittest.TestCase): self.assertRaises(TypeError, lambda: as_date >= as_datetime) self.assertRaises(TypeError, lambda: as_datetime >= as_date) - # Neverthelss, comparison should work with the base-class (date) + # Nevertheless, comparison should work with the base-class (date) # projection if use of a date method is forced. self.assertEqual(as_date.__eq__(as_datetime), True) different_day = (as_date.day + 1) % 20 + 1 @@ -3957,5 +4031,790 @@ class Oddballs(unittest.TestCase): with self.assertRaises(TypeError): datetime(10, 10, 10, 10, 10, 10, 10.) +############################################################################# +# Local Time Disambiguation + +# An experimental reimplementation of fromutc that respects the "fold" flag. + +class tzinfo2(tzinfo): + + def fromutc(self, dt): + "datetime in UTC -> datetime in local time." + + if not isinstance(dt, datetime): + raise TypeError("fromutc() requires a datetime argument") + if dt.tzinfo is not self: + raise ValueError("dt.tzinfo is not self") + # Returned value satisfies + # dt + ldt.utcoffset() = ldt + off0 = dt.replace(fold=0).utcoffset() + off1 = dt.replace(fold=1).utcoffset() + if off0 is None or off1 is None or dt.dst() is None: + raise ValueError + if off0 == off1: + ldt = dt + off0 + off1 = ldt.utcoffset() + if off0 == off1: + return ldt + # Now, we discovered both possible offsets, so + # we can just try four possible solutions: + for off in [off0, off1]: + ldt = dt + off + if ldt.utcoffset() == off: + return ldt + ldt = ldt.replace(fold=1) + if ldt.utcoffset() == off: + return ldt + + raise ValueError("No suitable local time found") + +# Reimplementing simplified US timezones to respect the "fold" flag: + +class USTimeZone2(tzinfo2): + + def __init__(self, hours, reprname, stdname, dstname): + self.stdoffset = timedelta(hours=hours) + self.reprname = reprname + self.stdname = stdname + self.dstname = dstname + + def __repr__(self): + return self.reprname + + def tzname(self, dt): + if self.dst(dt): + return self.dstname + else: + return self.stdname + + def utcoffset(self, dt): + return self.stdoffset + self.dst(dt) + + def dst(self, dt): + if dt is None or dt.tzinfo is None: + # An exception instead may be sensible here, in one or more of + # the cases. + return ZERO + assert dt.tzinfo is self + + # Find first Sunday in April. + start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year)) + assert start.weekday() == 6 and start.month == 4 and start.day <= 7 + + # Find last Sunday in October. + end = first_sunday_on_or_after(DSTEND.replace(year=dt.year)) + assert end.weekday() == 6 and end.month == 10 and end.day >= 25 + + # Can't compare naive to aware objects, so strip the timezone from + # dt first. + dt = dt.replace(tzinfo=None) + if start + HOUR <= dt < end: + # DST is in effect. + return HOUR + elif end <= dt < end + HOUR: + # Fold (an ambiguous hour): use dt.fold to disambiguate. + return ZERO if dt.fold else HOUR + elif start <= dt < start + HOUR: + # Gap (a non-existent hour): reverse the fold rule. + return HOUR if dt.fold else ZERO + else: + # DST is off. + return ZERO + +Eastern2 = USTimeZone2(-5, "Eastern2", "EST", "EDT") +Central2 = USTimeZone2(-6, "Central2", "CST", "CDT") +Mountain2 = USTimeZone2(-7, "Mountain2", "MST", "MDT") +Pacific2 = USTimeZone2(-8, "Pacific2", "PST", "PDT") + +# Europe_Vilnius_1941 tzinfo implementation reproduces the following +# 1941 transition from Olson's tzdist: +# +# Zone NAME GMTOFF RULES FORMAT [UNTIL] +# ZoneEurope/Vilnius 1:00 - CET 1940 Aug 3 +# 3:00 - MSK 1941 Jun 24 +# 1:00 C-Eur CE%sT 1944 Aug +# +# $ zdump -v Europe/Vilnius | grep 1941 +# Europe/Vilnius Mon Jun 23 20:59:59 1941 UTC = Mon Jun 23 23:59:59 1941 MSK isdst=0 gmtoff=10800 +# Europe/Vilnius Mon Jun 23 21:00:00 1941 UTC = Mon Jun 23 23:00:00 1941 CEST isdst=1 gmtoff=7200 + +class Europe_Vilnius_1941(tzinfo): + def _utc_fold(self): + return [datetime(1941, 6, 23, 21, tzinfo=self), # Mon Jun 23 21:00:00 1941 UTC + datetime(1941, 6, 23, 22, tzinfo=self)] # Mon Jun 23 22:00:00 1941 UTC + + def _loc_fold(self): + return [datetime(1941, 6, 23, 23, tzinfo=self), # Mon Jun 23 23:00:00 1941 MSK / CEST + datetime(1941, 6, 24, 0, tzinfo=self)] # Mon Jun 24 00:00:00 1941 CEST + + def utcoffset(self, dt): + fold_start, fold_stop = self._loc_fold() + if dt < fold_start: + return 3 * HOUR + if dt < fold_stop: + return (2 if dt.fold else 3) * HOUR + # if dt >= fold_stop + return 2 * HOUR + + def dst(self, dt): + fold_start, fold_stop = self._loc_fold() + if dt < fold_start: + return 0 * HOUR + if dt < fold_stop: + return (1 if dt.fold else 0) * HOUR + # if dt >= fold_stop + return 1 * HOUR + + def tzname(self, dt): + fold_start, fold_stop = self._loc_fold() + if dt < fold_start: + return 'MSK' + if dt < fold_stop: + return ('MSK', 'CEST')[dt.fold] + # if dt >= fold_stop + return 'CEST' + + def fromutc(self, dt): + assert dt.fold == 0 + assert dt.tzinfo is self + if dt.year != 1941: + raise NotImplementedError + fold_start, fold_stop = self._utc_fold() + if dt < fold_start: + return dt + 3 * HOUR + if dt < fold_stop: + return (dt + 2 * HOUR).replace(fold=1) + # if dt >= fold_stop + return dt + 2 * HOUR + + +class TestLocalTimeDisambiguation(unittest.TestCase): + + def test_vilnius_1941_fromutc(self): + Vilnius = Europe_Vilnius_1941() + + gdt = datetime(1941, 6, 23, 20, 59, 59, tzinfo=timezone.utc) + ldt = gdt.astimezone(Vilnius) + self.assertEqual(ldt.strftime("%c %Z%z"), + 'Mon Jun 23 23:59:59 1941 MSK+0300') + self.assertEqual(ldt.fold, 0) + self.assertFalse(ldt.dst()) + + gdt = datetime(1941, 6, 23, 21, tzinfo=timezone.utc) + ldt = gdt.astimezone(Vilnius) + self.assertEqual(ldt.strftime("%c %Z%z"), + 'Mon Jun 23 23:00:00 1941 CEST+0200') + self.assertEqual(ldt.fold, 1) + self.assertTrue(ldt.dst()) + + gdt = datetime(1941, 6, 23, 22, tzinfo=timezone.utc) + ldt = gdt.astimezone(Vilnius) + self.assertEqual(ldt.strftime("%c %Z%z"), + 'Tue Jun 24 00:00:00 1941 CEST+0200') + self.assertEqual(ldt.fold, 0) + self.assertTrue(ldt.dst()) + + def test_vilnius_1941_toutc(self): + Vilnius = Europe_Vilnius_1941() + + ldt = datetime(1941, 6, 23, 22, 59, 59, tzinfo=Vilnius) + gdt = ldt.astimezone(timezone.utc) + self.assertEqual(gdt.strftime("%c %Z"), + 'Mon Jun 23 19:59:59 1941 UTC') + + ldt = datetime(1941, 6, 23, 23, 59, 59, tzinfo=Vilnius) + gdt = ldt.astimezone(timezone.utc) + self.assertEqual(gdt.strftime("%c %Z"), + 'Mon Jun 23 20:59:59 1941 UTC') + + ldt = datetime(1941, 6, 23, 23, 59, 59, tzinfo=Vilnius, fold=1) + gdt = ldt.astimezone(timezone.utc) + self.assertEqual(gdt.strftime("%c %Z"), + 'Mon Jun 23 21:59:59 1941 UTC') + + ldt = datetime(1941, 6, 24, 0, tzinfo=Vilnius) + gdt = ldt.astimezone(timezone.utc) + self.assertEqual(gdt.strftime("%c %Z"), + 'Mon Jun 23 22:00:00 1941 UTC') + + + def test_constructors(self): + t = time(0, fold=1) + dt = datetime(1, 1, 1, fold=1) + self.assertEqual(t.fold, 1) + self.assertEqual(dt.fold, 1) + with self.assertRaises(TypeError): + time(0, 0, 0, 0, None, 0) + + def test_member(self): + dt = datetime(1, 1, 1, fold=1) + t = dt.time() + self.assertEqual(t.fold, 1) + t = dt.timetz() + self.assertEqual(t.fold, 1) + + def test_replace(self): + t = time(0) + dt = datetime(1, 1, 1) + self.assertEqual(t.replace(fold=1).fold, 1) + self.assertEqual(dt.replace(fold=1).fold, 1) + self.assertEqual(t.replace(fold=0).fold, 0) + self.assertEqual(dt.replace(fold=0).fold, 0) + # Check that replacement of other fields does not change "fold". + t = t.replace(fold=1, tzinfo=Eastern) + dt = dt.replace(fold=1, tzinfo=Eastern) + self.assertEqual(t.replace(tzinfo=None).fold, 1) + self.assertEqual(dt.replace(tzinfo=None).fold, 1) + # Check that fold is a keyword-only argument + with self.assertRaises(TypeError): + t.replace(1, 1, 1, None, 1) + with self.assertRaises(TypeError): + dt.replace(1, 1, 1, 1, 1, 1, 1, None, 1) + + def test_comparison(self): + t = time(0) + dt = datetime(1, 1, 1) + self.assertEqual(t, t.replace(fold=1)) + self.assertEqual(dt, dt.replace(fold=1)) + + def test_hash(self): + t = time(0) + dt = datetime(1, 1, 1) + self.assertEqual(hash(t), hash(t.replace(fold=1))) + self.assertEqual(hash(dt), hash(dt.replace(fold=1))) + + @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0') + def test_fromtimestamp(self): + s = 1414906200 + dt0 = datetime.fromtimestamp(s) + dt1 = datetime.fromtimestamp(s + 3600) + self.assertEqual(dt0.fold, 0) + self.assertEqual(dt1.fold, 1) + + @support.run_with_tz('Australia/Lord_Howe') + def test_fromtimestamp_lord_howe(self): + tm = _time.localtime(1.4e9) + if _time.strftime('%Z%z', tm) != 'LHST+1030': + self.skipTest('Australia/Lord_Howe timezone is not supported on this platform') + # $ TZ=Australia/Lord_Howe date -r 1428158700 + # Sun Apr 5 01:45:00 LHDT 2015 + # $ TZ=Australia/Lord_Howe date -r 1428160500 + # Sun Apr 5 01:45:00 LHST 2015 + s = 1428158700 + t0 = datetime.fromtimestamp(s) + t1 = datetime.fromtimestamp(s + 1800) + self.assertEqual(t0, t1) + self.assertEqual(t0.fold, 0) + self.assertEqual(t1.fold, 1) + + + @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0') + def test_timestamp(self): + dt0 = datetime(2014, 11, 2, 1, 30) + dt1 = dt0.replace(fold=1) + self.assertEqual(dt0.timestamp() + 3600, + dt1.timestamp()) + + @support.run_with_tz('Australia/Lord_Howe') + def test_timestamp_lord_howe(self): + tm = _time.localtime(1.4e9) + if _time.strftime('%Z%z', tm) != 'LHST+1030': + self.skipTest('Australia/Lord_Howe timezone is not supported on this platform') + t = datetime(2015, 4, 5, 1, 45) + s0 = t.replace(fold=0).timestamp() + s1 = t.replace(fold=1).timestamp() + self.assertEqual(s0 + 1800, s1) + + + @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0') + def test_astimezone(self): + dt0 = datetime(2014, 11, 2, 1, 30) + dt1 = dt0.replace(fold=1) + # Convert both naive instances to aware. + adt0 = dt0.astimezone() + adt1 = dt1.astimezone() + # Check that the first instance in DST zone and the second in STD + self.assertEqual(adt0.tzname(), 'EDT') + self.assertEqual(adt1.tzname(), 'EST') + self.assertEqual(adt0 + HOUR, adt1) + # Aware instances with fixed offset tzinfo's always have fold=0 + self.assertEqual(adt0.fold, 0) + self.assertEqual(adt1.fold, 0) + + + def test_pickle_fold(self): + t = time(fold=1) + dt = datetime(1, 1, 1, fold=1) + for pickler, unpickler, proto in pickle_choices: + for x in [t, dt]: + s = pickler.dumps(x, proto) + y = unpickler.loads(s) + self.assertEqual(x, y) + self.assertEqual((0 if proto < 4 else x.fold), y.fold) + + def test_repr(self): + t = time(fold=1) + dt = datetime(1, 1, 1, fold=1) + self.assertEqual(repr(t), 'datetime.time(0, 0, fold=1)') + self.assertEqual(repr(dt), + 'datetime.datetime(1, 1, 1, 0, 0, fold=1)') + + def test_dst(self): + # Let's first establish that things work in regular times. + dt_summer = datetime(2002, 10, 27, 1, tzinfo=Eastern2) - timedelta.resolution + dt_winter = datetime(2002, 10, 27, 2, tzinfo=Eastern2) + self.assertEqual(dt_summer.dst(), HOUR) + self.assertEqual(dt_winter.dst(), ZERO) + # The disambiguation flag is ignored + self.assertEqual(dt_summer.replace(fold=1).dst(), HOUR) + self.assertEqual(dt_winter.replace(fold=1).dst(), ZERO) + + # Pick local time in the fold. + for minute in [0, 30, 59]: + dt = datetime(2002, 10, 27, 1, minute, tzinfo=Eastern2) + # With fold=0 (the default) it is in DST. + self.assertEqual(dt.dst(), HOUR) + # With fold=1 it is in STD. + self.assertEqual(dt.replace(fold=1).dst(), ZERO) + + # Pick local time in the gap. + for minute in [0, 30, 59]: + dt = datetime(2002, 4, 7, 2, minute, tzinfo=Eastern2) + # With fold=0 (the default) it is in STD. + self.assertEqual(dt.dst(), ZERO) + # With fold=1 it is in DST. + self.assertEqual(dt.replace(fold=1).dst(), HOUR) + + + def test_utcoffset(self): + # Let's first establish that things work in regular times. + dt_summer = datetime(2002, 10, 27, 1, tzinfo=Eastern2) - timedelta.resolution + dt_winter = datetime(2002, 10, 27, 2, tzinfo=Eastern2) + self.assertEqual(dt_summer.utcoffset(), -4 * HOUR) + self.assertEqual(dt_winter.utcoffset(), -5 * HOUR) + # The disambiguation flag is ignored + self.assertEqual(dt_summer.replace(fold=1).utcoffset(), -4 * HOUR) + self.assertEqual(dt_winter.replace(fold=1).utcoffset(), -5 * HOUR) + + def test_fromutc(self): + # Let's first establish that things work in regular times. + u_summer = datetime(2002, 10, 27, 6, tzinfo=Eastern2) - timedelta.resolution + u_winter = datetime(2002, 10, 27, 7, tzinfo=Eastern2) + t_summer = Eastern2.fromutc(u_summer) + t_winter = Eastern2.fromutc(u_winter) + self.assertEqual(t_summer, u_summer - 4 * HOUR) + self.assertEqual(t_winter, u_winter - 5 * HOUR) + self.assertEqual(t_summer.fold, 0) + self.assertEqual(t_winter.fold, 0) + + # What happens in the fall-back fold? + u = datetime(2002, 10, 27, 5, 30, tzinfo=Eastern2) + t0 = Eastern2.fromutc(u) + u += HOUR + t1 = Eastern2.fromutc(u) + self.assertEqual(t0, t1) + self.assertEqual(t0.fold, 0) + self.assertEqual(t1.fold, 1) + # The tricky part is when u is in the local fold: + u = datetime(2002, 10, 27, 1, 30, tzinfo=Eastern2) + t = Eastern2.fromutc(u) + self.assertEqual((t.day, t.hour), (26, 21)) + # .. or gets into the local fold after a standard time adjustment + u = datetime(2002, 10, 27, 6, 30, tzinfo=Eastern2) + t = Eastern2.fromutc(u) + self.assertEqual((t.day, t.hour), (27, 1)) + + # What happens in the spring-forward gap? + u = datetime(2002, 4, 7, 2, 0, tzinfo=Eastern2) + t = Eastern2.fromutc(u) + self.assertEqual((t.day, t.hour), (6, 21)) + + def test_mixed_compare_regular(self): + t = datetime(2000, 1, 1, tzinfo=Eastern2) + self.assertEqual(t, t.astimezone(timezone.utc)) + t = datetime(2000, 6, 1, tzinfo=Eastern2) + self.assertEqual(t, t.astimezone(timezone.utc)) + + def test_mixed_compare_fold(self): + t_fold = datetime(2002, 10, 27, 1, 45, tzinfo=Eastern2) + t_fold_utc = t_fold.astimezone(timezone.utc) + self.assertNotEqual(t_fold, t_fold_utc) + + def test_mixed_compare_gap(self): + t_gap = datetime(2002, 4, 7, 2, 45, tzinfo=Eastern2) + t_gap_utc = t_gap.astimezone(timezone.utc) + self.assertNotEqual(t_gap, t_gap_utc) + + def test_hash_aware(self): + t = datetime(2000, 1, 1, tzinfo=Eastern2) + self.assertEqual(hash(t), hash(t.replace(fold=1))) + t_fold = datetime(2002, 10, 27, 1, 45, tzinfo=Eastern2) + t_gap = datetime(2002, 4, 7, 2, 45, tzinfo=Eastern2) + self.assertEqual(hash(t_fold), hash(t_fold.replace(fold=1))) + self.assertEqual(hash(t_gap), hash(t_gap.replace(fold=1))) + +SEC = timedelta(0, 1) + +def pairs(iterable): + a, b = itertools.tee(iterable) + next(b, None) + return zip(a, b) + +class ZoneInfo(tzinfo): + zoneroot = '/usr/share/zoneinfo' + def __init__(self, ut, ti): + """ + + :param ut: array + Array of transition point timestamps + :param ti: list + A list of (offset, isdst, abbr) tuples + :return: None + """ + self.ut = ut + self.ti = ti + self.lt = self.invert(ut, ti) + + @staticmethod + def invert(ut, ti): + lt = (array('q', ut), array('q', ut)) + if ut: + offset = ti[0][0] // SEC + lt[0][0] += offset + lt[1][0] += offset + for i in range(1, len(ut)): + lt[0][i] += ti[i-1][0] // SEC + lt[1][i] += ti[i][0] // SEC + return lt + + @classmethod + def fromfile(cls, fileobj): + if fileobj.read(4).decode() != "TZif": + raise ValueError("not a zoneinfo file") + fileobj.seek(32) + counts = array('i') + counts.fromfile(fileobj, 3) + if sys.byteorder != 'big': + counts.byteswap() + + ut = array('i') + ut.fromfile(fileobj, counts[0]) + if sys.byteorder != 'big': + ut.byteswap() + + type_indices = array('B') + type_indices.fromfile(fileobj, counts[0]) + + ttis = [] + for i in range(counts[1]): + ttis.append(struct.unpack(">lbb", fileobj.read(6))) + + abbrs = fileobj.read(counts[2]) + + # Convert ttis + for i, (gmtoff, isdst, abbrind) in enumerate(ttis): + abbr = abbrs[abbrind:abbrs.find(0, abbrind)].decode() + ttis[i] = (timedelta(0, gmtoff), isdst, abbr) + + ti = [None] * len(ut) + for i, idx in enumerate(type_indices): + ti[i] = ttis[idx] + + self = cls(ut, ti) + + return self + + @classmethod + def fromname(cls, name): + path = os.path.join(cls.zoneroot, name) + with open(path, 'rb') as f: + return cls.fromfile(f) + + EPOCHORDINAL = date(1970, 1, 1).toordinal() + + def fromutc(self, dt): + """datetime in UTC -> datetime in local time.""" + + if not isinstance(dt, datetime): + raise TypeError("fromutc() requires a datetime argument") + if dt.tzinfo is not self: + raise ValueError("dt.tzinfo is not self") + + timestamp = ((dt.toordinal() - self.EPOCHORDINAL) * 86400 + + dt.hour * 3600 + + dt.minute * 60 + + dt.second) + + if timestamp < self.ut[1]: + tti = self.ti[0] + fold = 0 + else: + idx = bisect.bisect_right(self.ut, timestamp) + assert self.ut[idx-1] <= timestamp + assert idx == len(self.ut) or timestamp < self.ut[idx] + tti_prev, tti = self.ti[idx-2:idx] + # Detect fold + shift = tti_prev[0] - tti[0] + fold = (shift > timedelta(0, timestamp - self.ut[idx-1])) + dt += tti[0] + if fold: + return dt.replace(fold=1) + else: + return dt + + def _find_ti(self, dt, i): + timestamp = ((dt.toordinal() - self.EPOCHORDINAL) * 86400 + + dt.hour * 3600 + + dt.minute * 60 + + dt.second) + lt = self.lt[dt.fold] + idx = bisect.bisect_right(lt, timestamp) + + return self.ti[max(0, idx - 1)][i] + + def utcoffset(self, dt): + return self._find_ti(dt, 0) + + def dst(self, dt): + isdst = self._find_ti(dt, 1) + # XXX: We cannot accurately determine the "save" value, + # so let's return 1h whenever DST is in effect. Since + # we don't use dst() in fromutc(), it is unlikely that + # it will be needed for anything more than bool(dst()). + return ZERO if isdst else HOUR + + def tzname(self, dt): + return self._find_ti(dt, 2) + + @classmethod + def zonenames(cls, zonedir=None): + if zonedir is None: + zonedir = cls.zoneroot + zone_tab = os.path.join(zonedir, 'zone.tab') + try: + f = open(zone_tab) + except OSError: + return + with f: + for line in f: + line = line.strip() + if line and not line.startswith('#'): + yield line.split()[2] + + @classmethod + def stats(cls, start_year=1): + count = gap_count = fold_count = zeros_count = 0 + min_gap = min_fold = timedelta.max + max_gap = max_fold = ZERO + min_gap_datetime = max_gap_datetime = datetime.min + min_gap_zone = max_gap_zone = None + min_fold_datetime = max_fold_datetime = datetime.min + min_fold_zone = max_fold_zone = None + stats_since = datetime(start_year, 1, 1) # Starting from 1970 eliminates a lot of noise + for zonename in cls.zonenames(): + count += 1 + tz = cls.fromname(zonename) + for dt, shift in tz.transitions(): + if dt < stats_since: + continue + if shift > ZERO: + gap_count += 1 + if (shift, dt) > (max_gap, max_gap_datetime): + max_gap = shift + max_gap_zone = zonename + max_gap_datetime = dt + if (shift, datetime.max - dt) < (min_gap, datetime.max - min_gap_datetime): + min_gap = shift + min_gap_zone = zonename + min_gap_datetime = dt + elif shift < ZERO: + fold_count += 1 + shift = -shift + if (shift, dt) > (max_fold, max_fold_datetime): + max_fold = shift + max_fold_zone = zonename + max_fold_datetime = dt + if (shift, datetime.max - dt) < (min_fold, datetime.max - min_fold_datetime): + min_fold = shift + min_fold_zone = zonename + min_fold_datetime = dt + else: + zeros_count += 1 + trans_counts = (gap_count, fold_count, zeros_count) + print("Number of zones: %5d" % count) + print("Number of transitions: %5d = %d (gaps) + %d (folds) + %d (zeros)" % + ((sum(trans_counts),) + trans_counts)) + print("Min gap: %16s at %s in %s" % (min_gap, min_gap_datetime, min_gap_zone)) + print("Max gap: %16s at %s in %s" % (max_gap, max_gap_datetime, max_gap_zone)) + print("Min fold: %16s at %s in %s" % (min_fold, min_fold_datetime, min_fold_zone)) + print("Max fold: %16s at %s in %s" % (max_fold, max_fold_datetime, max_fold_zone)) + + + def transitions(self): + for (_, prev_ti), (t, ti) in pairs(zip(self.ut, self.ti)): + shift = ti[0] - prev_ti[0] + yield datetime.utcfromtimestamp(t), shift + + def nondst_folds(self): + """Find all folds with the same value of isdst on both sides of the transition.""" + for (_, prev_ti), (t, ti) in pairs(zip(self.ut, self.ti)): + shift = ti[0] - prev_ti[0] + if shift < ZERO and ti[1] == prev_ti[1]: + yield datetime.utcfromtimestamp(t), -shift, prev_ti[2], ti[2] + + @classmethod + def print_all_nondst_folds(cls, same_abbr=False, start_year=1): + count = 0 + for zonename in cls.zonenames(): + tz = cls.fromname(zonename) + for dt, shift, prev_abbr, abbr in tz.nondst_folds(): + if dt.year < start_year or same_abbr and prev_abbr != abbr: + continue + count += 1 + print("%3d) %-30s %s %10s %5s -> %s" % + (count, zonename, dt, shift, prev_abbr, abbr)) + + def folds(self): + for t, shift in self.transitions(): + if shift < ZERO: + yield t, -shift + + def gaps(self): + for t, shift in self.transitions(): + if shift > ZERO: + yield t, shift + + def zeros(self): + for t, shift in self.transitions(): + if not shift: + yield t + + +class ZoneInfoTest(unittest.TestCase): + zonename = 'America/New_York' + + def setUp(self): + if sys.platform == "win32": + self.skipTest("Skipping zoneinfo tests on Windows") + try: + self.tz = ZoneInfo.fromname(self.zonename) + except FileNotFoundError as err: + self.skipTest("Skipping %s: %s" % (self.zonename, err)) + + def assertEquivDatetimes(self, a, b): + self.assertEqual((a.replace(tzinfo=None), a.fold, id(a.tzinfo)), + (b.replace(tzinfo=None), b.fold, id(b.tzinfo))) + + def test_folds(self): + tz = self.tz + for dt, shift in tz.folds(): + for x in [0 * shift, 0.5 * shift, shift - timedelta.resolution]: + udt = dt + x + ldt = tz.fromutc(udt.replace(tzinfo=tz)) + self.assertEqual(ldt.fold, 1) + adt = udt.replace(tzinfo=timezone.utc).astimezone(tz) + self.assertEquivDatetimes(adt, ldt) + utcoffset = ldt.utcoffset() + self.assertEqual(ldt.replace(tzinfo=None), udt + utcoffset) + # Round trip + self.assertEquivDatetimes(ldt.astimezone(timezone.utc), + udt.replace(tzinfo=timezone.utc)) + + + for x in [-timedelta.resolution, shift]: + udt = dt + x + udt = udt.replace(tzinfo=tz) + ldt = tz.fromutc(udt) + self.assertEqual(ldt.fold, 0) + + def test_gaps(self): + tz = self.tz + for dt, shift in tz.gaps(): + for x in [0 * shift, 0.5 * shift, shift - timedelta.resolution]: + udt = dt + x + udt = udt.replace(tzinfo=tz) + ldt = tz.fromutc(udt) + self.assertEqual(ldt.fold, 0) + adt = udt.replace(tzinfo=timezone.utc).astimezone(tz) + self.assertEquivDatetimes(adt, ldt) + utcoffset = ldt.utcoffset() + self.assertEqual(ldt.replace(tzinfo=None), udt.replace(tzinfo=None) + utcoffset) + # Create a local time inside the gap + ldt = tz.fromutc(dt.replace(tzinfo=tz)) - shift + x + self.assertLess(ldt.replace(fold=1).utcoffset(), + ldt.replace(fold=0).utcoffset(), + "At %s." % ldt) + + for x in [-timedelta.resolution, shift]: + udt = dt + x + ldt = tz.fromutc(udt.replace(tzinfo=tz)) + self.assertEqual(ldt.fold, 0) + + def test_system_transitions(self): + if ('Riyadh8' in self.zonename or + # From tzdata NEWS file: + # The files solar87, solar88, and solar89 are no longer distributed. + # They were a negative experiment - that is, a demonstration that + # tz data can represent solar time only with some difficulty and error. + # Their presence in the distribution caused confusion, as Riyadh + # civil time was generally not solar time in those years. + self.zonename.startswith('right/')): + self.skipTest("Skipping %s" % self.zonename) + tz = self.tz + TZ = os.environ.get('TZ') + os.environ['TZ'] = self.zonename + try: + _time.tzset() + for udt, shift in tz.transitions(): + if udt.year >= 2037: + # System support for times around the end of 32-bit time_t + # and later is flaky on many systems. + break + s0 = (udt - datetime(1970, 1, 1)) // SEC + ss = shift // SEC # shift seconds + for x in [-40 * 3600, -20*3600, -1, 0, + ss - 1, ss + 20 * 3600, ss + 40 * 3600]: + s = s0 + x + sdt = datetime.fromtimestamp(s) + tzdt = datetime.fromtimestamp(s, tz).replace(tzinfo=None) + self.assertEquivDatetimes(sdt, tzdt) + s1 = sdt.timestamp() + self.assertEqual(s, s1) + if ss > 0: # gap + # Create local time inside the gap + dt = datetime.fromtimestamp(s0) - shift / 2 + ts0 = dt.timestamp() + ts1 = dt.replace(fold=1).timestamp() + self.assertEqual(ts0, s0 + ss / 2) + self.assertEqual(ts1, s0 - ss / 2) + finally: + if TZ is None: + del os.environ['TZ'] + else: + os.environ['TZ'] = TZ + _time.tzset() + + +class ZoneInfoCompleteTest(unittest.TestSuite): + def __init__(self): + tests = [] + if is_resource_enabled('tzdata'): + for name in ZoneInfo.zonenames(): + Test = type('ZoneInfoTest[%s]' % name, (ZoneInfoTest,), {}) + Test.zonename = name + for method in dir(Test): + if method.startswith('test_'): + tests.append(Test(method)) + super().__init__(tests) + +# Iran had a sub-minute UTC offset before 1946. +class IranTest(ZoneInfoTest): + zonename = 'Asia/Tehran' + +def load_tests(loader, standard_tests, pattern): + standard_tests.addTest(ZoneInfoCompleteTest()) + return standard_tests + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/eintrdata/eintr_tester.py b/Lib/test/eintrdata/eintr_tester.py index e3f1aa5..4fc79b1 100644 --- a/Lib/test/eintrdata/eintr_tester.py +++ b/Lib/test/eintrdata/eintr_tester.py @@ -9,7 +9,7 @@ sub-second periodicity (contrarily to signal()). """ import contextlib -import io +import faulthandler import os import select import signal @@ -50,6 +50,10 @@ class EINTRBaseTest(unittest.TestCase): signal.setitimer(signal.ITIMER_REAL, cls.signal_delay, cls.signal_period) + # Issue #25277: Use faulthandler to try to debug a hang on FreeBSD + if hasattr(faulthandler, 'dump_traceback_later'): + faulthandler.dump_traceback_later(10 * 60, exit=True) + @classmethod def stop_alarm(cls): signal.setitimer(signal.ITIMER_REAL, 0, 0) @@ -58,6 +62,8 @@ class EINTRBaseTest(unittest.TestCase): def tearDownClass(cls): cls.stop_alarm() signal.signal(signal.SIGALRM, cls.orig_handler) + if hasattr(faulthandler, 'cancel_dump_traceback_later'): + faulthandler.cancel_dump_traceback_later() def subprocess(self, *args, **kw): cmd_args = (sys.executable, '-c') + args diff --git a/Lib/test/libregrtest/__init__.py b/Lib/test/libregrtest/__init__.py new file mode 100644 index 0000000..7ba0e6e --- /dev/null +++ b/Lib/test/libregrtest/__init__.py @@ -0,0 +1,5 @@ +# We import importlib *ASAP* in order to test #15386 +import importlib + +from test.libregrtest.cmdline import _parse_args, RESOURCE_NAMES +from test.libregrtest.main import main diff --git a/Lib/test/libregrtest/cmdline.py b/Lib/test/libregrtest/cmdline.py new file mode 100644 index 0000000..1f7aaed --- /dev/null +++ b/Lib/test/libregrtest/cmdline.py @@ -0,0 +1,344 @@ +import argparse +import os +import sys +from test import support + + +USAGE = """\ +python -m test [options] [test_name1 [test_name2 ...]] +python path/to/Lib/test/regrtest.py [options] [test_name1 [test_name2 ...]] +""" + +DESCRIPTION = """\ +Run Python regression tests. + +If no arguments or options are provided, finds all files matching +the pattern "test_*" in the Lib/test subdirectory and runs +them in alphabetical order (but see -M and -u, below, for exceptions). + +For more rigorous testing, it is useful to use the following +command line: + +python -E -Wd -m test [options] [test_name1 ...] +""" + +EPILOG = """\ +Additional option details: + +-r randomizes test execution order. You can use --randseed=int to provide an +int seed value for the randomizer; this is useful for reproducing troublesome +test orders. + +-s On the first invocation of regrtest using -s, the first test file found +or the first test file given on the command line is run, and the name of +the next test is recorded in a file named pynexttest. If run from the +Python build directory, pynexttest is located in the 'build' subdirectory, +otherwise it is located in tempfile.gettempdir(). On subsequent runs, +the test in pynexttest is run, and the next test is written to pynexttest. +When the last test has been run, pynexttest is deleted. In this way it +is possible to single step through the test files. This is useful when +doing memory analysis on the Python interpreter, which process tends to +consume too many resources to run the full regression test non-stop. + +-S is used to continue running tests after an aborted run. It will +maintain the order a standard run (ie, this assumes -r is not used). +This is useful after the tests have prematurely stopped for some external +reason and you want to start running from where you left off rather +than starting from the beginning. + +-f reads the names of tests from the file given as f's argument, one +or more test names per line. Whitespace is ignored. Blank lines and +lines beginning with '#' are ignored. This is especially useful for +whittling down failures involving interactions among tests. + +-L causes the leaks(1) command to be run just before exit if it exists. +leaks(1) is available on Mac OS X and presumably on some other +FreeBSD-derived systems. + +-R runs each test several times and examines sys.gettotalrefcount() to +see if the test appears to be leaking references. The argument should +be of the form stab:run:fname where 'stab' is the number of times the +test is run to let gettotalrefcount settle down, 'run' is the number +of times further it is run and 'fname' is the name of the file the +reports are written to. These parameters all have defaults (5, 4 and +"reflog.txt" respectively), and the minimal invocation is '-R :'. + +-M runs tests that require an exorbitant amount of memory. These tests +typically try to ascertain containers keep working when containing more than +2 billion objects, which only works on 64-bit systems. There are also some +tests that try to exhaust the address space of the process, which only makes +sense on 32-bit systems with at least 2Gb of memory. The passed-in memlimit, +which is a string in the form of '2.5Gb', determines howmuch memory the +tests will limit themselves to (but they may go slightly over.) The number +shouldn't be more memory than the machine has (including swap memory). You +should also keep in mind that swap memory is generally much, much slower +than RAM, and setting memlimit to all available RAM or higher will heavily +tax the machine. On the other hand, it is no use running these tests with a +limit of less than 2.5Gb, and many require more than 20Gb. Tests that expect +to use more than memlimit memory will be skipped. The big-memory tests +generally run very, very long. + +-u is used to specify which special resource intensive tests to run, +such as those requiring large file support or network connectivity. +The argument is a comma-separated list of words indicating the +resources to test. Currently only the following are defined: + + all - Enable all special resources. + + none - Disable all special resources (this is the default). + + audio - Tests that use the audio device. (There are known + cases of broken audio drivers that can crash Python or + even the Linux kernel.) + + curses - Tests that use curses and will modify the terminal's + state and output modes. + + largefile - It is okay to run some test that may create huge + files. These tests can take a long time and may + consume >2GB of disk space temporarily. + + network - It is okay to run tests that use external network + resource, e.g. testing SSL support for sockets. + + decimal - Test the decimal module against a large suite that + verifies compliance with standards. + + cpu - Used for certain CPU-heavy tests. + + subprocess Run all tests for the subprocess module. + + urlfetch - It is okay to download files required on testing. + + gui - Run tests that require a running GUI. + + tzdata - Run tests that require timezone data. + +To enable all resources except one, use '-uall,-<resource>'. For +example, to run all the tests except for the gui tests, give the +option '-uall,-gui'. +""" + + +RESOURCE_NAMES = ('audio', 'curses', 'largefile', 'network', + 'decimal', 'cpu', 'subprocess', 'urlfetch', 'gui', 'tzdata') + +class _ArgParser(argparse.ArgumentParser): + + def error(self, message): + super().error(message + "\nPass -h or --help for complete help.") + + +def _create_parser(): + # Set prog to prevent the uninformative "__main__.py" from displaying in + # error messages when using "python -m test ...". + parser = _ArgParser(prog='regrtest.py', + usage=USAGE, + description=DESCRIPTION, + epilog=EPILOG, + add_help=False, + formatter_class=argparse.RawDescriptionHelpFormatter) + + # Arguments with this clause added to its help are described further in + # the epilog's "Additional option details" section. + more_details = ' See the section at bottom for more details.' + + group = parser.add_argument_group('General options') + # We add help explicitly to control what argument group it renders under. + group.add_argument('-h', '--help', action='help', + help='show this help message and exit') + group.add_argument('--timeout', metavar='TIMEOUT', type=float, + help='dump the traceback and exit if a test takes ' + 'more than TIMEOUT seconds; disabled if TIMEOUT ' + 'is negative or equals to zero') + group.add_argument('--wait', action='store_true', + help='wait for user input, e.g., allow a debugger ' + 'to be attached') + group.add_argument('--slaveargs', metavar='ARGS') + group.add_argument('-S', '--start', metavar='START', + help='the name of the test at which to start.' + + more_details) + + group = parser.add_argument_group('Verbosity') + group.add_argument('-v', '--verbose', action='count', + help='run tests in verbose mode with output to stdout') + group.add_argument('-w', '--verbose2', action='store_true', + help='re-run failed tests in verbose mode') + group.add_argument('-W', '--verbose3', action='store_true', + help='display test output on failure') + group.add_argument('-q', '--quiet', action='store_true', + help='no output unless one or more tests fail') + group.add_argument('-o', '--slowest', action='store_true', dest='print_slow', + help='print the slowest 10 tests') + group.add_argument('--header', action='store_true', + help='print header with interpreter info') + + group = parser.add_argument_group('Selecting tests') + group.add_argument('-r', '--randomize', action='store_true', + help='randomize test execution order.' + more_details) + group.add_argument('--randseed', metavar='SEED', + dest='random_seed', type=int, + help='pass a random seed to reproduce a previous ' + 'random run') + group.add_argument('-f', '--fromfile', metavar='FILE', + help='read names of tests to run from a file.' + + more_details) + group.add_argument('-x', '--exclude', action='store_true', + help='arguments are tests to *exclude*') + group.add_argument('-s', '--single', action='store_true', + help='single step through a set of tests.' + + more_details) + group.add_argument('-m', '--match', metavar='PAT', + dest='match_tests', + help='match test cases and methods with glob pattern PAT') + group.add_argument('-G', '--failfast', action='store_true', + help='fail as soon as a test fails (only with -v or -W)') + group.add_argument('-u', '--use', metavar='RES1,RES2,...', + action='append', type=resources_list, + help='specify which special resource intensive tests ' + 'to run.' + more_details) + group.add_argument('-M', '--memlimit', metavar='LIMIT', + help='run very large memory-consuming tests.' + + more_details) + group.add_argument('--testdir', metavar='DIR', + type=relative_filename, + help='execute test files in the specified directory ' + '(instead of the Python stdlib test suite)') + + group = parser.add_argument_group('Special runs') + group.add_argument('-l', '--findleaks', action='store_true', + help='if GC is available detect tests that leak memory') + group.add_argument('-L', '--runleaks', action='store_true', + help='run the leaks(1) command just before exit.' + + more_details) + group.add_argument('-R', '--huntrleaks', metavar='RUNCOUNTS', + type=huntrleaks, + help='search for reference leaks (needs debug build, ' + 'very slow).' + more_details) + group.add_argument('-j', '--multiprocess', metavar='PROCESSES', + dest='use_mp', type=int, + help='run PROCESSES processes at once') + group.add_argument('-T', '--coverage', action='store_true', + dest='trace', + help='turn on code coverage tracing using the trace ' + 'module') + group.add_argument('-D', '--coverdir', metavar='DIR', + type=relative_filename, + help='directory where coverage files are put') + group.add_argument('-N', '--nocoverdir', + action='store_const', const=None, dest='coverdir', + help='put coverage files alongside modules') + group.add_argument('-t', '--threshold', metavar='THRESHOLD', + type=int, + help='call gc.set_threshold(THRESHOLD)') + group.add_argument('-n', '--nowindows', action='store_true', + help='suppress error message boxes on Windows') + group.add_argument('-F', '--forever', action='store_true', + help='run the specified tests in a loop, until an ' + 'error happens') + group.add_argument('--list-tests', action='store_true', + help="only write the name of tests that will be run, " + "don't execute them") + group.add_argument('-P', '--pgo', dest='pgo', action='store_true', + help='enable Profile Guided Optimization training') + + parser.add_argument('args', nargs=argparse.REMAINDER, + help=argparse.SUPPRESS) + + return parser + + +def relative_filename(string): + # CWD is replaced with a temporary dir before calling main(), so we + # join it with the saved CWD so it ends up where the user expects. + return os.path.join(support.SAVEDCWD, string) + + +def huntrleaks(string): + args = string.split(':') + if len(args) not in (2, 3): + raise argparse.ArgumentTypeError( + 'needs 2 or 3 colon-separated arguments') + nwarmup = int(args[0]) if args[0] else 5 + ntracked = int(args[1]) if args[1] else 4 + fname = args[2] if len(args) > 2 and args[2] else 'reflog.txt' + return nwarmup, ntracked, fname + + +def resources_list(string): + u = [x.lower() for x in string.split(',')] + for r in u: + if r == 'all' or r == 'none': + continue + if r[0] == '-': + r = r[1:] + if r not in RESOURCE_NAMES: + raise argparse.ArgumentTypeError('invalid resource: ' + r) + return u + + +def _parse_args(args, **kwargs): + # Defaults + ns = argparse.Namespace(testdir=None, verbose=0, quiet=False, + exclude=False, single=False, randomize=False, fromfile=None, + findleaks=False, use_resources=None, trace=False, coverdir='coverage', + runleaks=False, huntrleaks=False, verbose2=False, print_slow=False, + random_seed=None, use_mp=None, verbose3=False, forever=False, + header=False, failfast=False, match_tests=None, pgo=False) + for k, v in kwargs.items(): + if not hasattr(ns, k): + raise TypeError('%r is an invalid keyword argument ' + 'for this function' % k) + setattr(ns, k, v) + if ns.use_resources is None: + ns.use_resources = [] + + parser = _create_parser() + parser.parse_args(args=args, namespace=ns) + + if ns.single and ns.fromfile: + parser.error("-s and -f don't go together!") + if ns.use_mp and ns.trace: + parser.error("-T and -j don't go together!") + if ns.use_mp and ns.findleaks: + parser.error("-l and -j don't go together!") + if ns.failfast and not (ns.verbose or ns.verbose3): + parser.error("-G/--failfast needs either -v or -W") + if ns.pgo and (ns.verbose or ns.verbose2 or ns.verbose3): + parser.error("--pgo/-v don't go together!") + + if ns.nowindows: + print("Warning: the --nowindows (-n) option is deprecated. " + "Use -vv to display assertions in stderr.", file=sys.stderr) + + if ns.quiet: + ns.verbose = 0 + if ns.timeout is not None: + if ns.timeout <= 0: + ns.timeout = None + if ns.use_mp is not None: + if ns.use_mp <= 0: + # Use all cores + extras for tests that like to sleep + ns.use_mp = 2 + (os.cpu_count() or 1) + if ns.use: + for a in ns.use: + for r in a: + if r == 'all': + ns.use_resources[:] = RESOURCE_NAMES + continue + if r == 'none': + del ns.use_resources[:] + continue + remove = False + if r[0] == '-': + remove = True + r = r[1:] + if remove: + if r in ns.use_resources: + ns.use_resources.remove(r) + elif r not in ns.use_resources: + ns.use_resources.append(r) + if ns.random_seed is not None: + ns.randomize = True + + return ns diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py new file mode 100644 index 0000000..ba9e48b --- /dev/null +++ b/Lib/test/libregrtest/main.py @@ -0,0 +1,526 @@ +import datetime +import faulthandler +import os +import platform +import random +import re +import sys +import sysconfig +import tempfile +import textwrap +import time +from test.libregrtest.cmdline import _parse_args +from test.libregrtest.runtest import ( + findtests, runtest, + STDTESTS, NOTTESTS, PASSED, FAILED, ENV_CHANGED, SKIPPED, RESOURCE_DENIED, + INTERRUPTED, CHILD_ERROR, + PROGRESS_MIN_TIME, format_test_result) +from test.libregrtest.setup import setup_tests +from test import support +try: + import gc +except ImportError: + gc = None + + +# When tests are run from the Python build directory, it is best practice +# to keep the test files in a subfolder. This eases the cleanup of leftover +# files using the "make distclean" command. +if sysconfig.is_python_build(): + TEMPDIR = os.path.join(sysconfig.get_config_var('srcdir'), 'build') +else: + TEMPDIR = tempfile.gettempdir() +TEMPDIR = os.path.abspath(TEMPDIR) + + +def format_duration(seconds): + if seconds < 1.0: + return '%.0f ms' % (seconds * 1e3) + if seconds < 60.0: + return '%.0f sec' % seconds + + minutes, seconds = divmod(seconds, 60.0) + return '%.0f min %.0f sec' % (minutes, seconds) + + +class Regrtest: + """Execute a test suite. + + This also parses command-line options and modifies its behavior + accordingly. + + tests -- a list of strings containing test names (optional) + testdir -- the directory in which to look for tests (optional) + + Users other than the Python test suite will certainly want to + specify testdir; if it's omitted, the directory containing the + Python test suite is searched for. + + If the tests argument is omitted, the tests listed on the + command-line will be used. If that's empty, too, then all *.py + files beginning with test_ will be used. + + The other default arguments (verbose, quiet, exclude, + single, randomize, findleaks, use_resources, trace, coverdir, + print_slow, and random_seed) allow programmers calling main() + directly to set the values that would normally be set by flags + on the command line. + """ + def __init__(self): + # Namespace of command line options + self.ns = None + + # tests + self.tests = [] + self.selected = [] + + # test results + self.good = [] + self.bad = [] + self.skipped = [] + self.resource_denieds = [] + self.environment_changed = [] + self.interrupted = False + + # used by --slow + self.test_times = [] + + # used by --coverage, trace.Trace instance + self.tracer = None + + # used by --findleaks, store for gc.garbage + self.found_garbage = [] + + # used to display the progress bar "[ 3/100]" + self.start_time = time.monotonic() + self.test_count = '' + self.test_count_width = 1 + + # used by --single + self.next_single_test = None + self.next_single_filename = None + + def accumulate_result(self, test, result): + ok, test_time = result + if ok not in (CHILD_ERROR, INTERRUPTED): + self.test_times.append((test_time, test)) + if ok == PASSED: + self.good.append(test) + elif ok == FAILED: + self.bad.append(test) + elif ok == ENV_CHANGED: + self.environment_changed.append(test) + elif ok == SKIPPED: + self.skipped.append(test) + elif ok == RESOURCE_DENIED: + self.skipped.append(test) + self.resource_denieds.append(test) + + def display_progress(self, test_index, test): + if self.ns.quiet: + return + if self.bad and not self.ns.pgo: + fmt = "{time} [{test_index:{count_width}}{test_count}/{nbad}] {test_name}" + else: + fmt = "{time} [{test_index:{count_width}}{test_count}] {test_name}" + test_time = time.monotonic() - self.start_time + test_time = datetime.timedelta(seconds=int(test_time)) + line = fmt.format(count_width=self.test_count_width, + test_index=test_index, + test_count=self.test_count, + nbad=len(self.bad), + test_name=test, + time=test_time) + print(line, flush=True) + + def parse_args(self, kwargs): + ns = _parse_args(sys.argv[1:], **kwargs) + + if ns.timeout and not hasattr(faulthandler, 'dump_traceback_later'): + print("Warning: The timeout option requires " + "faulthandler.dump_traceback_later", file=sys.stderr) + ns.timeout = None + + if ns.threshold is not None and gc is None: + print('No GC available, ignore --threshold.', file=sys.stderr) + ns.threshold = None + + if ns.findleaks: + if gc is not None: + # Uncomment the line below to report garbage that is not + # freeable by reference counting alone. By default only + # garbage that is not collectable by the GC is reported. + pass + #gc.set_debug(gc.DEBUG_SAVEALL) + else: + print('No GC available, disabling --findleaks', + file=sys.stderr) + ns.findleaks = False + + # Strip .py extensions. + removepy(ns.args) + + return ns + + def find_tests(self, tests): + self.tests = tests + + if self.ns.single: + self.next_single_filename = os.path.join(TEMPDIR, 'pynexttest') + try: + with open(self.next_single_filename, 'r') as fp: + next_test = fp.read().strip() + self.tests = [next_test] + except OSError: + pass + + if self.ns.fromfile: + self.tests = [] + # regex to match 'test_builtin' in line: + # '0:00:00 [ 4/400] test_builtin -- test_dict took 1 sec' + regex = (r'^(?:[0-9]+:[0-9]+:[0-9]+ *)?' + r'(?:\[[0-9/ ]+\] *)?' + r'(test_[a-zA-Z0-9_]+)') + regex = re.compile(regex) + with open(os.path.join(support.SAVEDCWD, self.ns.fromfile)) as fp: + for line in fp: + line = line.strip() + if line.startswith('#'): + continue + match = regex.match(line) + if match is None: + continue + self.tests.append(match.group(1)) + + removepy(self.tests) + + stdtests = STDTESTS[:] + nottests = NOTTESTS.copy() + if self.ns.exclude: + for arg in self.ns.args: + if arg in stdtests: + stdtests.remove(arg) + nottests.add(arg) + self.ns.args = [] + + # if testdir is set, then we are not running the python tests suite, so + # don't add default tests to be executed or skipped (pass empty values) + if self.ns.testdir: + alltests = findtests(self.ns.testdir, list(), set()) + else: + alltests = findtests(self.ns.testdir, stdtests, nottests) + + if not self.ns.fromfile: + self.selected = self.tests or self.ns.args or alltests + else: + self.selected = self.tests + if self.ns.single: + self.selected = self.selected[:1] + try: + pos = alltests.index(self.selected[0]) + self.next_single_test = alltests[pos + 1] + except IndexError: + pass + + # Remove all the selected tests that precede start if it's set. + if self.ns.start: + try: + del self.selected[:self.selected.index(self.ns.start)] + except ValueError: + print("Couldn't find starting test (%s), using all tests" + % self.ns.start, file=sys.stderr) + + if self.ns.randomize: + if self.ns.random_seed is None: + self.ns.random_seed = random.randrange(10000000) + random.seed(self.ns.random_seed) + random.shuffle(self.selected) + + def list_tests(self): + for name in self.selected: + print(name) + + def rerun_failed_tests(self): + self.ns.verbose = True + self.ns.failfast = False + self.ns.verbose3 = False + self.ns.match_tests = None + + print("Re-running failed tests in verbose mode") + for test in self.bad[:]: + print("Re-running test %r in verbose mode" % test, flush=True) + try: + self.ns.verbose = True + ok = runtest(self.ns, test) + except KeyboardInterrupt: + self.interrupted = True + # print a newline separate from the ^C + print() + break + else: + if ok[0] in {PASSED, ENV_CHANGED, SKIPPED, RESOURCE_DENIED}: + self.bad.remove(test) + else: + if self.bad: + print(count(len(self.bad), 'test'), "failed again:") + printlist(self.bad) + + def display_result(self): + if self.interrupted: + # print a newline after ^C + print() + print("Test suite interrupted by signal SIGINT.") + executed = set(self.good) | set(self.bad) | set(self.skipped) + omitted = set(self.selected) - executed + print(count(len(omitted), "test"), "omitted:") + printlist(omitted) + + # If running the test suite for PGO then no one cares about + # results. + if self.ns.pgo: + return + + if self.good and not self.ns.quiet: + if (not self.bad + and not self.skipped + and not self.interrupted + and len(self.good) > 1): + print("All", end=' ') + print(count(len(self.good), "test"), "OK.") + + if self.ns.print_slow: + self.test_times.sort(reverse=True) + print() + print("10 slowest tests:") + for time, test in self.test_times[:10]: + print("- %s: %s" % (test, format_duration(time))) + + if self.bad: + print() + print(count(len(self.bad), "test"), "failed:") + printlist(self.bad) + + if self.environment_changed: + print() + print("{} altered the execution environment:".format( + count(len(self.environment_changed), "test"))) + printlist(self.environment_changed) + + if self.skipped and not self.ns.quiet: + print() + print(count(len(self.skipped), "test"), "skipped:") + printlist(self.skipped) + + def run_tests_sequential(self): + if self.ns.trace: + import trace + self.tracer = trace.Trace(trace=False, count=True) + + save_modules = sys.modules.keys() + + print("Run tests sequentially") + + previous_test = None + for test_index, test in enumerate(self.tests, 1): + start_time = time.monotonic() + + text = test + if previous_test: + text = '%s -- %s' % (text, previous_test) + self.display_progress(test_index, text) + + if self.tracer: + # If we're tracing code coverage, then we don't exit with status + # if on a false return value from main. + cmd = ('result = runtest(self.ns, test); ' + 'self.accumulate_result(test, result)') + ns = dict(locals()) + self.tracer.runctx(cmd, globals=globals(), locals=ns) + result = ns['result'] + else: + try: + result = runtest(self.ns, test) + except KeyboardInterrupt: + self.interrupted = True + self.accumulate_result(test, (INTERRUPTED, None)) + break + else: + self.accumulate_result(test, result) + + previous_test = format_test_result(test, result[0]) + test_time = time.monotonic() - start_time + if test_time >= PROGRESS_MIN_TIME: + previous_test = "%s in %s" % (previous_test, format_duration(test_time)) + elif result[0] == PASSED: + # be quiet: say nothing if the test passed shortly + previous_test = None + + if self.ns.findleaks: + gc.collect() + if gc.garbage: + print("Warning: test created", len(gc.garbage), end=' ') + print("uncollectable object(s).") + # move the uncollectable objects somewhere so we don't see + # them again + self.found_garbage.extend(gc.garbage) + del gc.garbage[:] + + # Unload the newly imported modules (best effort finalization) + for module in sys.modules.keys(): + if module not in save_modules and module.startswith("test."): + support.unload(module) + + if previous_test: + print(previous_test) + + def _test_forever(self, tests): + while True: + for test in tests: + yield test + if self.bad: + return + + def run_tests(self): + # For a partial run, we do not need to clutter the output. + if (self.ns.verbose + or self.ns.header + or not (self.ns.pgo or self.ns.quiet or self.ns.single + or self.tests or self.ns.args)): + # Print basic platform information + print("==", platform.python_implementation(), *sys.version.split()) + print("== ", platform.platform(aliased=True), + "%s-endian" % sys.byteorder) + print("== ", "hash algorithm:", sys.hash_info.algorithm, + "64bit" if sys.maxsize > 2**32 else "32bit") + print("== ", os.getcwd()) + print("Testing with flags:", sys.flags) + + if self.ns.randomize: + print("Using random seed", self.ns.random_seed) + + if self.ns.forever: + self.tests = self._test_forever(list(self.selected)) + self.test_count = '' + self.test_count_width = 3 + else: + self.tests = iter(self.selected) + self.test_count = '/{}'.format(len(self.selected)) + self.test_count_width = len(self.test_count) - 1 + + if self.ns.use_mp: + from test.libregrtest.runtest_mp import run_tests_multiprocess + run_tests_multiprocess(self) + else: + self.run_tests_sequential() + + def finalize(self): + if self.next_single_filename: + if self.next_single_test: + with open(self.next_single_filename, 'w') as fp: + fp.write(self.next_single_test + '\n') + else: + os.unlink(self.next_single_filename) + + if self.tracer: + r = self.tracer.results() + r.write_results(show_missing=True, summary=True, + coverdir=self.ns.coverdir) + + print() + duration = time.monotonic() - self.start_time + print("Total duration: %s" % format_duration(duration)) + + if self.bad: + result = "FAILURE" + elif self.interrupted: + result = "INTERRUPTED" + else: + result = "SUCCESS" + print("Tests result: %s" % result) + + if self.ns.runleaks: + os.system("leaks %d" % os.getpid()) + + def main(self, tests=None, **kwargs): + global TEMPDIR + + if sysconfig.is_python_build(): + try: + os.mkdir(TEMPDIR) + except FileExistsError: + pass + + # Define a writable temp dir that will be used as cwd while running + # the tests. The name of the dir includes the pid to allow parallel + # testing (see the -j option). + test_cwd = 'test_python_{}'.format(os.getpid()) + test_cwd = os.path.join(TEMPDIR, test_cwd) + + # Run the tests in a context manager that temporarily changes the CWD to a + # temporary and writable directory. If it's not possible to create or + # change the CWD, the original CWD will be used. The original CWD is + # available from support.SAVEDCWD. + with support.temp_cwd(test_cwd, quiet=True): + self._main(tests, kwargs) + + def _main(self, tests, kwargs): + self.ns = self.parse_args(kwargs) + + if self.ns.slaveargs is not None: + from test.libregrtest.runtest_mp import run_tests_slave + run_tests_slave(self.ns.slaveargs) + + if self.ns.wait: + input("Press any key to continue...") + + setup_tests(self.ns) + + self.find_tests(tests) + + if self.ns.list_tests: + self.list_tests() + sys.exit(0) + + self.run_tests() + self.display_result() + + if self.ns.verbose2 and self.bad: + self.rerun_failed_tests() + + self.finalize() + sys.exit(len(self.bad) > 0 or self.interrupted) + + +def removepy(names): + if not names: + return + for idx, name in enumerate(names): + basename, ext = os.path.splitext(name) + if ext == '.py': + names[idx] = basename + + +def count(n, word): + if n == 1: + return "%d %s" % (n, word) + else: + return "%d %ss" % (n, word) + + +def printlist(x, width=70, indent=4): + """Print the elements of iterable x to stdout. + + Optional arg width (default 70) is the maximum line length. + Optional arg indent (default 4) is the number of blanks with which to + begin each line. + """ + + blanks = ' ' * indent + # Print the sorted list: 'x' may be a '--random' list or a set() + print(textwrap.fill(' '.join(str(elt) for elt in sorted(x)), width, + initial_indent=blanks, subsequent_indent=blanks)) + + +def main(tests=None, **kwargs): + """Run the Python suite.""" + Regrtest().main(tests=tests, **kwargs) diff --git a/Lib/test/libregrtest/refleak.py b/Lib/test/libregrtest/refleak.py new file mode 100644 index 0000000..59dc49f --- /dev/null +++ b/Lib/test/libregrtest/refleak.py @@ -0,0 +1,202 @@ +import errno +import os +import re +import sys +import warnings +from inspect import isabstract +from test import support + + +try: + MAXFD = os.sysconf("SC_OPEN_MAX") +except Exception: + MAXFD = 256 + + +def fd_count(): + """Count the number of open file descriptors""" + if sys.platform.startswith(('linux', 'freebsd')): + try: + names = os.listdir("/proc/self/fd") + return len(names) + except FileNotFoundError: + pass + + count = 0 + for fd in range(MAXFD): + try: + # Prefer dup() over fstat(). fstat() can require input/output + # whereas dup() doesn't. + fd2 = os.dup(fd) + except OSError as e: + if e.errno != errno.EBADF: + raise + else: + os.close(fd2) + count += 1 + return count + + +def dash_R(the_module, test, indirect_test, huntrleaks): + """Run a test multiple times, looking for reference leaks. + + Returns: + False if the test didn't leak references; True if we detected refleaks. + """ + # This code is hackish and inelegant, but it seems to do the job. + import copyreg + import collections.abc + + if not hasattr(sys, 'gettotalrefcount'): + raise Exception("Tracking reference leaks requires a debug build " + "of Python") + + # Save current values for dash_R_cleanup() to restore. + fs = warnings.filters[:] + ps = copyreg.dispatch_table.copy() + pic = sys.path_importer_cache.copy() + try: + import zipimport + except ImportError: + zdc = None # Run unmodified on platforms without zipimport support + else: + zdc = zipimport._zip_directory_cache.copy() + abcs = {} + for abc in [getattr(collections.abc, a) for a in collections.abc.__all__]: + if not isabstract(abc): + continue + for obj in abc.__subclasses__() + [abc]: + abcs[obj] = obj._abc_registry.copy() + + nwarmup, ntracked, fname = huntrleaks + fname = os.path.join(support.SAVEDCWD, fname) + repcount = nwarmup + ntracked + rc_deltas = [0] * repcount + alloc_deltas = [0] * repcount + fd_deltas = [0] * repcount + + print("beginning", repcount, "repetitions", file=sys.stderr) + print(("1234567890"*(repcount//10 + 1))[:repcount], file=sys.stderr, + flush=True) + # initialize variables to make pyflakes quiet + rc_before = alloc_before = fd_before = 0 + for i in range(repcount): + indirect_test() + alloc_after, rc_after, fd_after = dash_R_cleanup(fs, ps, pic, zdc, + abcs) + print('.', end='', flush=True) + if i >= nwarmup: + rc_deltas[i] = rc_after - rc_before + alloc_deltas[i] = alloc_after - alloc_before + fd_deltas[i] = fd_after - fd_before + alloc_before = alloc_after + rc_before = rc_after + fd_before = fd_after + print(file=sys.stderr) + # These checkers return False on success, True on failure + def check_rc_deltas(deltas): + return any(deltas) + def check_alloc_deltas(deltas): + # At least 1/3rd of 0s + if 3 * deltas.count(0) < len(deltas): + return True + # Nothing else than 1s, 0s and -1s + if not set(deltas) <= {1,0,-1}: + return True + return False + failed = False + for deltas, item_name, checker in [ + (rc_deltas, 'references', check_rc_deltas), + (alloc_deltas, 'memory blocks', check_alloc_deltas), + (fd_deltas, 'file descriptors', check_rc_deltas)]: + if checker(deltas): + msg = '%s leaked %s %s, sum=%s' % ( + test, deltas[nwarmup:], item_name, sum(deltas)) + print(msg, file=sys.stderr, flush=True) + with open(fname, "a") as refrep: + print(msg, file=refrep) + refrep.flush() + failed = True + return failed + + +def dash_R_cleanup(fs, ps, pic, zdc, abcs): + import gc, copyreg + import _strptime, linecache + import urllib.parse, urllib.request, mimetypes, doctest + import struct, filecmp, collections.abc + from distutils.dir_util import _path_created + from weakref import WeakSet + + # Clear the warnings registry, so they can be displayed again + for mod in sys.modules.values(): + if hasattr(mod, '__warningregistry__'): + del mod.__warningregistry__ + + # Restore some original values. + warnings.filters[:] = fs + copyreg.dispatch_table.clear() + copyreg.dispatch_table.update(ps) + sys.path_importer_cache.clear() + sys.path_importer_cache.update(pic) + try: + import zipimport + except ImportError: + pass # Run unmodified on platforms without zipimport support + else: + zipimport._zip_directory_cache.clear() + zipimport._zip_directory_cache.update(zdc) + + # clear type cache + sys._clear_type_cache() + + # Clear ABC registries, restoring previously saved ABC registries. + for abc in [getattr(collections.abc, a) for a in collections.abc.__all__]: + if not isabstract(abc): + continue + for obj in abc.__subclasses__() + [abc]: + obj._abc_registry = abcs.get(obj, WeakSet()).copy() + obj._abc_cache.clear() + obj._abc_negative_cache.clear() + + # Flush standard output, so that buffered data is sent to the OS and + # associated Python objects are reclaimed. + for stream in (sys.stdout, sys.stderr, sys.__stdout__, sys.__stderr__): + if stream is not None: + stream.flush() + + # Clear assorted module caches. + _path_created.clear() + re.purge() + _strptime._regex_cache.clear() + urllib.parse.clear_cache() + urllib.request.urlcleanup() + linecache.clearcache() + mimetypes._default_mime_types() + filecmp._cache.clear() + struct._clearcache() + doctest.master = None + try: + import ctypes + except ImportError: + # Don't worry about resetting the cache if ctypes is not supported + pass + else: + ctypes._reset_cache() + + # Collect cyclic trash and read memory statistics immediately after. + func1 = sys.getallocatedblocks + func2 = sys.gettotalrefcount + gc.collect() + return func1(), func2(), fd_count() + + +def warm_caches(): + # char cache + s = bytes(range(256)) + for i in range(256): + s[i:i+1] + # unicode cache + [chr(i) for i in range(256)] + # int cache + list(range(-5, 257)) diff --git a/Lib/test/libregrtest/runtest.py b/Lib/test/libregrtest/runtest.py new file mode 100644 index 0000000..7d5290e --- /dev/null +++ b/Lib/test/libregrtest/runtest.py @@ -0,0 +1,244 @@ +import faulthandler +import importlib +import io +import os +import sys +import time +import traceback +import unittest +from test import support +from test.libregrtest.refleak import dash_R +from test.libregrtest.save_env import saved_test_environment + + +# Test result constants. +PASSED = 1 +FAILED = 0 +ENV_CHANGED = -1 +SKIPPED = -2 +RESOURCE_DENIED = -3 +INTERRUPTED = -4 +CHILD_ERROR = -5 # error in a child process + +_FORMAT_TEST_RESULT = { + PASSED: '%s passed', + FAILED: '%s failed', + ENV_CHANGED: '%s failed (env changed)', + SKIPPED: '%s skipped', + RESOURCE_DENIED: '%s skipped (resource denied)', + INTERRUPTED: '%s interrupted', + CHILD_ERROR: '%s crashed', +} + +# Minimum duration of a test to display its duration or to mention that +# the test is running in background +PROGRESS_MIN_TIME = 30.0 # seconds + +# small set of tests to determine if we have a basically functioning interpreter +# (i.e. if any of these fail, then anything else is likely to follow) +STDTESTS = [ + 'test_grammar', + 'test_opcodes', + 'test_dict', + 'test_builtin', + 'test_exceptions', + 'test_types', + 'test_unittest', + 'test_doctest', + 'test_doctest2', + 'test_support' +] + +# set of tests that we don't want to be executed when using regrtest +NOTTESTS = set() + + +def format_test_result(test_name, result): + fmt = _FORMAT_TEST_RESULT.get(result, "%s") + return fmt % test_name + + +def findtests(testdir=None, stdtests=STDTESTS, nottests=NOTTESTS): + """Return a list of all applicable test modules.""" + testdir = findtestdir(testdir) + names = os.listdir(testdir) + tests = [] + others = set(stdtests) | nottests + for name in names: + mod, ext = os.path.splitext(name) + if mod[:5] == "test_" and ext in (".py", "") and mod not in others: + tests.append(mod) + return stdtests + sorted(tests) + + +def runtest(ns, test): + """Run a single test. + + ns -- regrtest namespace of options + test -- the name of the test + + Returns the tuple (result, test_time), where result is one of the + constants: + + INTERRUPTED KeyboardInterrupt when run under -j + RESOURCE_DENIED test skipped because resource denied + SKIPPED test skipped for some other reason + ENV_CHANGED test failed because it changed the execution environment + FAILED test failed + PASSED test passed + """ + + output_on_failure = ns.verbose3 + + use_timeout = (ns.timeout is not None) + if use_timeout: + faulthandler.dump_traceback_later(ns.timeout, exit=True) + try: + support.match_tests = ns.match_tests + if ns.failfast: + support.failfast = True + if output_on_failure: + support.verbose = True + + # Reuse the same instance to all calls to runtest(). Some + # tests keep a reference to sys.stdout or sys.stderr + # (eg. test_argparse). + if runtest.stringio is None: + stream = io.StringIO() + runtest.stringio = stream + else: + stream = runtest.stringio + stream.seek(0) + stream.truncate() + + orig_stdout = sys.stdout + orig_stderr = sys.stderr + try: + sys.stdout = stream + sys.stderr = stream + result = runtest_inner(ns, test, display_failure=False) + if result[0] == FAILED: + output = stream.getvalue() + orig_stderr.write(output) + orig_stderr.flush() + finally: + sys.stdout = orig_stdout + sys.stderr = orig_stderr + else: + support.verbose = ns.verbose # Tell tests to be moderately quiet + result = runtest_inner(ns, test, display_failure=not ns.verbose) + return result + finally: + if use_timeout: + faulthandler.cancel_dump_traceback_later() + cleanup_test_droppings(test, ns.verbose) +runtest.stringio = None + + +def runtest_inner(ns, test, display_failure=True): + support.unload(test) + + test_time = 0.0 + refleak = False # True if the test leaked references. + try: + if test.startswith('test.') or ns.testdir: + abstest = test + else: + # Always import it from the test package + abstest = 'test.' + test + with saved_test_environment(test, ns.verbose, ns.quiet, pgo=ns.pgo) as environment: + start_time = time.time() + the_module = importlib.import_module(abstest) + # If the test has a test_main, that will run the appropriate + # tests. If not, use normal unittest test loading. + test_runner = getattr(the_module, "test_main", None) + if test_runner is None: + 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 ns.huntrleaks: + refleak = dash_R(the_module, test, test_runner, ns.huntrleaks) + test_time = time.time() - start_time + except support.ResourceDenied as msg: + if not ns.quiet and not ns.pgo: + print(test, "skipped --", msg, flush=True) + return RESOURCE_DENIED, test_time + except unittest.SkipTest as msg: + if not ns.quiet and not ns.pgo: + print(test, "skipped --", msg, flush=True) + return SKIPPED, test_time + except KeyboardInterrupt: + raise + except support.TestFailed as msg: + if not ns.pgo: + if display_failure: + print("test", test, "failed --", msg, file=sys.stderr, + flush=True) + else: + print("test", test, "failed", file=sys.stderr, flush=True) + return FAILED, test_time + except: + msg = traceback.format_exc() + if not ns.pgo: + print("test", test, "crashed --", msg, file=sys.stderr, + flush=True) + return FAILED, test_time + else: + if refleak: + return FAILED, test_time + if environment.changed: + return ENV_CHANGED, test_time + return PASSED, test_time + + +def cleanup_test_droppings(testname, verbose): + import shutil + import stat + import gc + + # First kill any dangling references to open files etc. + # This can also issue some ResourceWarnings which would otherwise get + # triggered during the following test run, and possibly produce failures. + gc.collect() + + # Try to clean up junk commonly left behind. While tests shouldn't leave + # any files or directories behind, when a test fails that can be tedious + # for it to arrange. The consequences can be especially nasty on Windows, + # since if a test leaves a file open, it cannot be deleted by name (while + # there's nothing we can do about that here either, we can display the + # name of the offending test, which is a real help). + for name in (support.TESTFN, + "db_home", + ): + if not os.path.exists(name): + continue + + if os.path.isdir(name): + kind, nuker = "directory", shutil.rmtree + elif os.path.isfile(name): + kind, nuker = "file", os.unlink + else: + raise SystemError("os.path says %r exists but is neither " + "directory nor file" % name) + + if verbose: + print("%r left behind %s %r" % (testname, kind, name)) + try: + # if we have chmod, fix possible permissions problems + # that might prevent cleanup + if (hasattr(os, 'chmod')): + os.chmod(name, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + nuker(name) + except Exception as msg: + print(("%r left behind %s %r and it couldn't be " + "removed: %s" % (testname, kind, name, msg)), file=sys.stderr) + + +def findtestdir(path=None): + return path or os.path.dirname(os.path.dirname(__file__)) or os.curdir diff --git a/Lib/test/libregrtest/runtest_mp.py b/Lib/test/libregrtest/runtest_mp.py new file mode 100644 index 0000000..9604c16 --- /dev/null +++ b/Lib/test/libregrtest/runtest_mp.py @@ -0,0 +1,245 @@ +import faulthandler +import json +import os +import queue +import sys +import time +import traceback +import types +from test import support +try: + import threading +except ImportError: + print("Multiprocess option requires thread support") + sys.exit(2) + +from test.libregrtest.runtest import ( + runtest, INTERRUPTED, CHILD_ERROR, PROGRESS_MIN_TIME, + format_test_result) +from test.libregrtest.setup import setup_tests + + +# Display the running tests if nothing happened last N seconds +PROGRESS_UPDATE = 30.0 # seconds + +# If interrupted, display the wait progress every N seconds +WAIT_PROGRESS = 2.0 # seconds + + +def run_test_in_subprocess(testname, ns): + """Run the given test in a subprocess with --slaveargs. + + ns is the option Namespace parsed from command-line arguments. regrtest + is invoked in a subprocess with the --slaveargs argument; when the + subprocess exits, its return code, stdout and stderr are returned as a + 3-tuple. + """ + from subprocess import Popen, PIPE + + ns_dict = vars(ns) + slaveargs = (ns_dict, testname) + slaveargs = json.dumps(slaveargs) + + cmd = [sys.executable, *support.args_from_interpreter_flags(), + '-X', 'faulthandler', + '-m', 'test.regrtest', + '--slaveargs', slaveargs] + if ns.pgo: + cmd += ['--pgo'] + + # Running the child from the same working directory as regrtest's original + # invocation ensures that TEMPDIR for the child is the same when + # sysconfig.is_python_build() is true. See issue 15300. + popen = Popen(cmd, + stdout=PIPE, stderr=PIPE, + universal_newlines=True, + close_fds=(os.name != 'nt'), + cwd=support.SAVEDCWD) + with popen: + stdout, stderr = popen.communicate() + retcode = popen.wait() + return retcode, stdout, stderr + + +def run_tests_slave(slaveargs): + ns_dict, testname = json.loads(slaveargs) + ns = types.SimpleNamespace(**ns_dict) + + setup_tests(ns) + + try: + result = runtest(ns, testname) + except KeyboardInterrupt: + result = INTERRUPTED, '' + except BaseException as e: + traceback.print_exc() + result = CHILD_ERROR, str(e) + + print() # Force a newline (just in case) + print(json.dumps(result), flush=True) + sys.exit(0) + + +# We do not use a generator so multiple threads can call next(). +class MultiprocessIterator: + + """A thread-safe iterator over tests for multiprocess mode.""" + + def __init__(self, tests): + self.interrupted = False + self.lock = threading.Lock() + self.tests = tests + + def __iter__(self): + return self + + def __next__(self): + with self.lock: + if self.interrupted: + raise StopIteration('tests interrupted') + return next(self.tests) + + +class MultiprocessThread(threading.Thread): + def __init__(self, pending, output, ns): + super().__init__() + self.pending = pending + self.output = output + self.ns = ns + self.current_test = None + self.start_time = None + + def _runtest(self): + try: + test = next(self.pending) + except StopIteration: + self.output.put((None, None, None, None)) + return True + + try: + self.start_time = time.monotonic() + self.current_test = test + + retcode, stdout, stderr = run_test_in_subprocess(test, self.ns) + finally: + self.current_test = None + + stdout, _, result = stdout.strip().rpartition("\n") + if retcode != 0: + result = (CHILD_ERROR, "Exit code %s" % retcode) + self.output.put((test, stdout.rstrip(), stderr.rstrip(), + result)) + return True + + if not result: + self.output.put((None, None, None, None)) + return True + + result = json.loads(result) + self.output.put((test, stdout.rstrip(), stderr.rstrip(), + result)) + return False + + def run(self): + try: + stop = False + while not stop: + stop = self._runtest() + except BaseException: + self.output.put((None, None, None, None)) + raise + + +def run_tests_multiprocess(regrtest): + output = queue.Queue() + pending = MultiprocessIterator(regrtest.tests) + test_timeout = regrtest.ns.timeout + use_timeout = (test_timeout is not None) + + workers = [MultiprocessThread(pending, output, regrtest.ns) + for i in range(regrtest.ns.use_mp)] + print("Run tests in parallel using %s child processes" + % len(workers)) + for worker in workers: + worker.start() + + def get_running(workers): + running = [] + for worker in workers: + current_test = worker.current_test + if not current_test: + continue + dt = time.monotonic() - worker.start_time + if dt >= PROGRESS_MIN_TIME: + running.append('%s (%.0f sec)' % (current_test, dt)) + return running + + finished = 0 + test_index = 1 + get_timeout = max(PROGRESS_UPDATE, PROGRESS_MIN_TIME) + try: + while finished < regrtest.ns.use_mp: + if use_timeout: + faulthandler.dump_traceback_later(test_timeout, exit=True) + + try: + item = output.get(timeout=get_timeout) + except queue.Empty: + running = get_running(workers) + if running and not regrtest.ns.pgo: + print('running: %s' % ', '.join(running)) + continue + + test, stdout, stderr, result = item + if test is None: + finished += 1 + continue + regrtest.accumulate_result(test, result) + + # Display progress + ok, test_time = result + text = format_test_result(test, ok) + if (ok not in (CHILD_ERROR, INTERRUPTED) + and test_time >= PROGRESS_MIN_TIME + and not regrtest.ns.pgo): + text += ' (%.0f sec)' % test_time + running = get_running(workers) + if running and not regrtest.ns.pgo: + text += ' -- running: %s' % ', '.join(running) + regrtest.display_progress(test_index, text) + + # Copy stdout and stderr from the child process + if stdout: + print(stdout, flush=True) + if stderr and not regrtest.ns.pgo: + print(stderr, file=sys.stderr, flush=True) + + if result[0] == INTERRUPTED: + raise KeyboardInterrupt + if result[0] == CHILD_ERROR: + msg = "Child error on {}: {}".format(test, result[1]) + raise Exception(msg) + test_index += 1 + except KeyboardInterrupt: + regrtest.interrupted = True + pending.interrupted = True + print() + finally: + if use_timeout: + faulthandler.cancel_dump_traceback_later() + + # If tests are interrupted, wait until tests complete + wait_start = time.monotonic() + while True: + running = [worker.current_test for worker in workers] + running = list(filter(bool, running)) + if not running: + break + + dt = time.monotonic() - wait_start + line = "Waiting for %s (%s tests)" % (', '.join(running), len(running)) + if dt >= WAIT_PROGRESS: + line = "%s since %.0f sec" % (line, dt) + print(line) + for worker in workers: + worker.join(WAIT_PROGRESS) diff --git a/Lib/test/libregrtest/save_env.py b/Lib/test/libregrtest/save_env.py new file mode 100644 index 0000000..eefbc14 --- /dev/null +++ b/Lib/test/libregrtest/save_env.py @@ -0,0 +1,285 @@ +import builtins +import locale +import logging +import os +import shutil +import sys +import sysconfig +import warnings +from test import support +try: + import threading +except ImportError: + threading = None +try: + import _multiprocessing, multiprocessing.process +except ImportError: + multiprocessing = None + + +# Unit tests are supposed to leave the execution environment unchanged +# once they complete. But sometimes tests have bugs, especially when +# tests fail, and the changes to environment go on to mess up other +# tests. This can cause issues with buildbot stability, since tests +# are run in random order and so problems may appear to come and go. +# There are a few things we can save and restore to mitigate this, and +# the following context manager handles this task. + +class saved_test_environment: + """Save bits of the test environment and restore them at block exit. + + with saved_test_environment(testname, verbose, quiet): + #stuff + + Unless quiet is True, a warning is printed to stderr if any of + the saved items was changed by the test. The attribute 'changed' + is initially False, but is set to True if a change is detected. + + If verbose is more than 1, the before and after state of changed + items is also printed. + """ + + changed = False + + def __init__(self, testname, verbose=0, quiet=False, *, pgo=False): + self.testname = testname + self.verbose = verbose + self.quiet = quiet + self.pgo = pgo + + # To add things to save and restore, add a name XXX to the resources list + # and add corresponding get_XXX/restore_XXX functions. get_XXX should + # return the value to be saved and compared against a second call to the + # get function when test execution completes. restore_XXX should accept + # the saved value and restore the resource using it. It will be called if + # and only if a change in the value is detected. + # + # Note: XXX will have any '.' replaced with '_' characters when determining + # the corresponding method names. + + resources = ('sys.argv', 'cwd', 'sys.stdin', 'sys.stdout', 'sys.stderr', + 'os.environ', 'sys.path', 'sys.path_hooks', '__import__', + 'warnings.filters', 'asyncore.socket_map', + 'logging._handlers', 'logging._handlerList', 'sys.gettrace', + 'sys.warnoptions', + # multiprocessing.process._cleanup() may release ref + # to a thread, so check processes first. + 'multiprocessing.process._dangling', 'threading._dangling', + 'sysconfig._CONFIG_VARS', 'sysconfig._INSTALL_SCHEMES', + 'files', 'locale', 'warnings.showwarning', + 'shutil_archive_formats', 'shutil_unpack_formats', + ) + + def get_sys_argv(self): + return id(sys.argv), sys.argv, sys.argv[:] + def restore_sys_argv(self, saved_argv): + sys.argv = saved_argv[1] + sys.argv[:] = saved_argv[2] + + def get_cwd(self): + return os.getcwd() + def restore_cwd(self, saved_cwd): + os.chdir(saved_cwd) + + def get_sys_stdout(self): + return sys.stdout + def restore_sys_stdout(self, saved_stdout): + sys.stdout = saved_stdout + + def get_sys_stderr(self): + return sys.stderr + def restore_sys_stderr(self, saved_stderr): + sys.stderr = saved_stderr + + def get_sys_stdin(self): + return sys.stdin + def restore_sys_stdin(self, saved_stdin): + sys.stdin = saved_stdin + + def get_os_environ(self): + return id(os.environ), os.environ, dict(os.environ) + def restore_os_environ(self, saved_environ): + os.environ = saved_environ[1] + os.environ.clear() + os.environ.update(saved_environ[2]) + + def get_sys_path(self): + return id(sys.path), sys.path, sys.path[:] + def restore_sys_path(self, saved_path): + sys.path = saved_path[1] + sys.path[:] = saved_path[2] + + def get_sys_path_hooks(self): + return id(sys.path_hooks), sys.path_hooks, sys.path_hooks[:] + def restore_sys_path_hooks(self, saved_hooks): + sys.path_hooks = saved_hooks[1] + sys.path_hooks[:] = saved_hooks[2] + + def get_sys_gettrace(self): + return sys.gettrace() + def restore_sys_gettrace(self, trace_fxn): + sys.settrace(trace_fxn) + + def get___import__(self): + return builtins.__import__ + def restore___import__(self, import_): + builtins.__import__ = import_ + + def get_warnings_filters(self): + return id(warnings.filters), warnings.filters, warnings.filters[:] + def restore_warnings_filters(self, saved_filters): + warnings.filters = saved_filters[1] + warnings.filters[:] = saved_filters[2] + + def get_asyncore_socket_map(self): + asyncore = sys.modules.get('asyncore') + # XXX Making a copy keeps objects alive until __exit__ gets called. + return asyncore and asyncore.socket_map.copy() or {} + def restore_asyncore_socket_map(self, saved_map): + asyncore = sys.modules.get('asyncore') + if asyncore is not None: + asyncore.close_all(ignore_all=True) + asyncore.socket_map.update(saved_map) + + def get_shutil_archive_formats(self): + # we could call get_archives_formats() but that only returns the + # registry keys; we want to check the values too (the functions that + # are registered) + return shutil._ARCHIVE_FORMATS, shutil._ARCHIVE_FORMATS.copy() + def restore_shutil_archive_formats(self, saved): + shutil._ARCHIVE_FORMATS = saved[0] + shutil._ARCHIVE_FORMATS.clear() + shutil._ARCHIVE_FORMATS.update(saved[1]) + + def get_shutil_unpack_formats(self): + return shutil._UNPACK_FORMATS, shutil._UNPACK_FORMATS.copy() + def restore_shutil_unpack_formats(self, saved): + shutil._UNPACK_FORMATS = saved[0] + shutil._UNPACK_FORMATS.clear() + shutil._UNPACK_FORMATS.update(saved[1]) + + def get_logging__handlers(self): + # _handlers is a WeakValueDictionary + return id(logging._handlers), logging._handlers, logging._handlers.copy() + def restore_logging__handlers(self, saved_handlers): + # Can't easily revert the logging state + pass + + def get_logging__handlerList(self): + # _handlerList is a list of weakrefs to handlers + return id(logging._handlerList), logging._handlerList, logging._handlerList[:] + def restore_logging__handlerList(self, saved_handlerList): + # Can't easily revert the logging state + pass + + def get_sys_warnoptions(self): + return id(sys.warnoptions), sys.warnoptions, sys.warnoptions[:] + def restore_sys_warnoptions(self, saved_options): + sys.warnoptions = saved_options[1] + sys.warnoptions[:] = saved_options[2] + + # Controlling dangling references to Thread objects can make it easier + # to track reference leaks. + def get_threading__dangling(self): + if not threading: + return None + # This copies the weakrefs without making any strong reference + return threading._dangling.copy() + def restore_threading__dangling(self, saved): + if not threading: + return + threading._dangling.clear() + threading._dangling.update(saved) + + # Same for Process objects + def get_multiprocessing_process__dangling(self): + if not multiprocessing: + return None + # Unjoined process objects can survive after process exits + multiprocessing.process._cleanup() + # This copies the weakrefs without making any strong reference + return multiprocessing.process._dangling.copy() + def restore_multiprocessing_process__dangling(self, saved): + if not multiprocessing: + return + multiprocessing.process._dangling.clear() + multiprocessing.process._dangling.update(saved) + + def get_sysconfig__CONFIG_VARS(self): + # make sure the dict is initialized + sysconfig.get_config_var('prefix') + return (id(sysconfig._CONFIG_VARS), sysconfig._CONFIG_VARS, + dict(sysconfig._CONFIG_VARS)) + def restore_sysconfig__CONFIG_VARS(self, saved): + sysconfig._CONFIG_VARS = saved[1] + sysconfig._CONFIG_VARS.clear() + sysconfig._CONFIG_VARS.update(saved[2]) + + def get_sysconfig__INSTALL_SCHEMES(self): + return (id(sysconfig._INSTALL_SCHEMES), sysconfig._INSTALL_SCHEMES, + sysconfig._INSTALL_SCHEMES.copy()) + def restore_sysconfig__INSTALL_SCHEMES(self, saved): + sysconfig._INSTALL_SCHEMES = saved[1] + sysconfig._INSTALL_SCHEMES.clear() + sysconfig._INSTALL_SCHEMES.update(saved[2]) + + def get_files(self): + return sorted(fn + ('/' if os.path.isdir(fn) else '') + for fn in os.listdir()) + def restore_files(self, saved_value): + fn = support.TESTFN + if fn not in saved_value and (fn + '/') not in saved_value: + if os.path.isfile(fn): + support.unlink(fn) + elif os.path.isdir(fn): + support.rmtree(fn) + + _lc = [getattr(locale, lc) for lc in dir(locale) + if lc.startswith('LC_')] + def get_locale(self): + pairings = [] + for lc in self._lc: + try: + pairings.append((lc, locale.setlocale(lc, None))) + except (TypeError, ValueError): + continue + return pairings + def restore_locale(self, saved): + for lc, setting in saved: + locale.setlocale(lc, setting) + + def get_warnings_showwarning(self): + return warnings.showwarning + def restore_warnings_showwarning(self, fxn): + warnings.showwarning = fxn + + def resource_info(self): + for name in self.resources: + method_suffix = name.replace('.', '_') + get_name = 'get_' + method_suffix + restore_name = 'restore_' + method_suffix + yield name, getattr(self, get_name), getattr(self, restore_name) + + def __enter__(self): + self.saved_values = dict((name, get()) for name, get, restore + in self.resource_info()) + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + saved_values = self.saved_values + del self.saved_values + support.gc_collect() # Some resources use weak references + for name, get, restore in self.resource_info(): + current = get() + original = saved_values.pop(name) + # Check for changes to the resource's value + if current != original: + self.changed = True + restore(original) + if not self.quiet and not self.pgo: + print(f"Warning -- {name} was modified by {self.testname}", + file=sys.stderr, flush=True) + if self.verbose > 1: + print(f" Before: {original}""\n"f" After: {current} ", + file=sys.stderr, flush=True) + return False diff --git a/Lib/test/libregrtest/setup.py b/Lib/test/libregrtest/setup.py new file mode 100644 index 0000000..1d24531 --- /dev/null +++ b/Lib/test/libregrtest/setup.py @@ -0,0 +1,121 @@ +import atexit +import faulthandler +import os +import signal +import sys +import unittest +from test import support +try: + import gc +except ImportError: + gc = None + +from test.libregrtest.refleak import warm_caches + + +def setup_tests(ns): + # Display the Python traceback on fatal errors (e.g. segfault) + faulthandler.enable(all_threads=True) + + # Display the Python traceback on SIGALRM or SIGUSR1 signal + signals = [] + if hasattr(signal, 'SIGALRM'): + signals.append(signal.SIGALRM) + if hasattr(signal, 'SIGUSR1'): + signals.append(signal.SIGUSR1) + for signum in signals: + faulthandler.register(signum, chain=True) + + replace_stdout() + support.record_original_stdout(sys.stdout) + + if ns.testdir: + # Prepend test directory to sys.path, so runtest() will be able + # to locate tests + sys.path.insert(0, os.path.abspath(ns.testdir)) + + # Some times __path__ and __file__ are not absolute (e.g. while running from + # Lib/) and, if we change the CWD to run the tests in a temporary dir, some + # imports might fail. This affects only the modules imported before os.chdir(). + # These modules are searched first in sys.path[0] (so '' -- the CWD) and if + # they are found in the CWD their __file__ and __path__ will be relative (this + # happens before the chdir). All the modules imported after the chdir, are + # not found in the CWD, and since the other paths in sys.path[1:] are absolute + # (site.py absolutize them), the __file__ and __path__ will be absolute too. + # Therefore it is necessary to absolutize manually the __file__ and __path__ of + # the packages to prevent later imports to fail when the CWD is different. + for module in sys.modules.values(): + if hasattr(module, '__path__'): + for index, path in enumerate(module.__path__): + module.__path__[index] = os.path.abspath(path) + if hasattr(module, '__file__'): + module.__file__ = os.path.abspath(module.__file__) + + # MacOSX (a.k.a. Darwin) has a default stack size that is too small + # for deeply recursive regular expressions. We see this as crashes in + # the Python test suite when running test_re.py and test_sre.py. The + # fix is to set the stack limit to 2048. + # This approach may also be useful for other Unixy platforms that + # suffer from small default stack limits. + if sys.platform == 'darwin': + try: + import resource + except ImportError: + pass + else: + soft, hard = resource.getrlimit(resource.RLIMIT_STACK) + newsoft = min(hard, max(soft, 1024*2048)) + resource.setrlimit(resource.RLIMIT_STACK, (newsoft, hard)) + + if ns.huntrleaks: + unittest.BaseTestSuite._cleanup = False + + # Avoid false positives due to various caches + # filling slowly with random data: + warm_caches() + + if ns.memlimit is not None: + support.set_memlimit(ns.memlimit) + + if ns.threshold is not None: + gc.set_threshold(ns.threshold) + + try: + import msvcrt + except ImportError: + pass + else: + msvcrt.SetErrorMode(msvcrt.SEM_FAILCRITICALERRORS| + msvcrt.SEM_NOALIGNMENTFAULTEXCEPT| + msvcrt.SEM_NOGPFAULTERRORBOX| + msvcrt.SEM_NOOPENFILEERRORBOX) + try: + msvcrt.CrtSetReportMode + except AttributeError: + # release build + pass + else: + for m in [msvcrt.CRT_WARN, msvcrt.CRT_ERROR, msvcrt.CRT_ASSERT]: + if ns.verbose and ns.verbose >= 2: + msvcrt.CrtSetReportMode(m, msvcrt.CRTDBG_MODE_FILE) + msvcrt.CrtSetReportFile(m, msvcrt.CRTDBG_FILE_STDERR) + else: + msvcrt.CrtSetReportMode(m, 0) + + support.use_resources = ns.use_resources + + +def replace_stdout(): + """Set stdout encoder error handler to backslashreplace (as stderr error + handler) to avoid UnicodeEncodeError when printing a traceback""" + stdout = sys.stdout + sys.stdout = open(stdout.fileno(), 'w', + encoding=stdout.encoding, + errors="backslashreplace", + closefd=False, + newline='\n') + + def restore_stdout(): + sys.stdout.close() + sys.stdout = stdout + atexit.register(restore_stdout) diff --git a/Lib/test/list_tests.py b/Lib/test/list_tests.py index f20fdc0..26e9368 100644 --- a/Lib/test/list_tests.py +++ b/Lib/test/list_tests.py @@ -266,9 +266,21 @@ class CommonTest(seq_tests.CommonTest): self.assertEqual(a, list("spameggs")) self.assertRaises(TypeError, a.extend, None) - self.assertRaises(TypeError, a.extend) + # overflow test. issue1621 + class CustomIter: + def __iter__(self): + return self + def __next__(self): + raise StopIteration + def __length_hint__(self): + return sys.maxsize + a = self.type2test([1,2,3,4]) + a.extend(CustomIter()) + self.assertEqual(a, [1,2,3,4]) + + def test_insert(self): a = self.type2test([0, 1, 2]) a.insert(0, -2) diff --git a/Lib/test/lock_tests.py b/Lib/test/lock_tests.py index a64aa18..a6cb3b1 100644 --- a/Lib/test/lock_tests.py +++ b/Lib/test/lock_tests.py @@ -7,6 +7,7 @@ import time from _thread import start_new_thread, TIMEOUT_MAX import threading import unittest +import weakref from test import support @@ -198,6 +199,17 @@ class BaseLockTests(BaseTestCase): self.assertFalse(results[0]) self.assertTimeout(results[1], 0.5) + def test_weakref_exists(self): + lock = self.locktype() + ref = weakref.ref(lock) + self.assertIsNotNone(ref()) + + def test_weakref_deleted(self): + lock = self.locktype() + ref = weakref.ref(lock) + del lock + self.assertIsNone(ref()) + class LockTests(BaseLockTests): """ diff --git a/Lib/test/make_ssl_certs.py b/Lib/test/make_ssl_certs.py index a1f298d..4d9f01b 100644 --- a/Lib/test/make_ssl_certs.py +++ b/Lib/test/make_ssl_certs.py @@ -3,7 +3,6 @@ and friends.""" import os import shutil -import sys import tempfile from subprocess import * diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py index 7922b54..5c83361 100644 --- a/Lib/test/pickletester.py +++ b/Lib/test/pickletester.py @@ -1000,7 +1000,7 @@ class AbstractUnpickleTests(unittest.TestCase): b'0', # POP b'1', # POP_MARK b'2', # DUP - # b'(2', # PyUnpickler doesn't raise + b'(2', b'R', # REDUCE b')R', b'a', # APPEND @@ -1009,7 +1009,7 @@ class AbstractUnpickleTests(unittest.TestCase): b'Nb', b'd', # DICT b'e', # APPENDS - # b'(e', # PyUnpickler raises AttributeError + b'(e', b'ibuiltins\nlist\n', # INST b'l', # LIST b'o', # OBJ @@ -1022,7 +1022,7 @@ class AbstractUnpickleTests(unittest.TestCase): b'NNs', b't', # TUPLE b'u', # SETITEMS - # b'(u', # PyUnpickler doesn't raise + b'(u', b'}(Nu', b'\x81', # NEWOBJ b')\x81', @@ -1033,7 +1033,7 @@ class AbstractUnpickleTests(unittest.TestCase): b'N\x87', b'NN\x87', b'\x90', # ADDITEMS - # b'(\x90', # PyUnpickler raises AttributeError + b'(\x90', b'\x91', # FROZENSET b'\x92', # NEWOBJ_EX b')}\x92', @@ -1046,7 +1046,7 @@ class AbstractUnpickleTests(unittest.TestCase): def test_bad_mark(self): badpickles = [ - # b'N(.', # STOP + b'N(.', # STOP b'N(2', # DUP b'cbuiltins\nlist\n)(R', # REDUCE b'cbuiltins\nlist\n()R', @@ -1081,7 +1081,7 @@ class AbstractUnpickleTests(unittest.TestCase): b'N(\x94', # MEMOIZE ] for p in badpickles: - self.check_unpickling_error(self.bad_mark_errors, p) + self.check_unpickling_error(self.bad_stack_errors, p) def test_truncated_data(self): self.check_unpickling_error(EOFError, b'') @@ -1855,16 +1855,14 @@ class AbstractPickleTests(unittest.TestCase): 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 + elif proto < 4: + self.assertIn(b'X\x04\x00\x00\x00FACE', s) # BINUNICODE 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), @@ -2583,11 +2581,6 @@ class AbstractPickleModuleTests(unittest.TestCase): self.assertRaises(pickle.PicklingError, BadPickler().dump, 0) self.assertRaises(pickle.UnpicklingError, BadUnpickler().load) - def test_bad_input(self): - # Test issue4298 - s = bytes([0x58, 0, 0, 0, 0x54]) - self.assertRaises(EOFError, pickle.loads, s) - class AbstractPersistentPicklerTests(unittest.TestCase): diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py index ba8a780..21b0edf 100755..100644 --- a/Lib/test/regrtest.py +++ b/Lib/test/regrtest.py @@ -6,1605 +6,33 @@ Script to run Python regression tests. Run this script with -h or --help for documentation. """ -USAGE = """\ -python -m test [options] [test_name1 [test_name2 ...]] -python path/to/Lib/test/regrtest.py [options] [test_name1 [test_name2 ...]] -""" - -DESCRIPTION = """\ -Run Python regression tests. - -If no arguments or options are provided, finds all files matching -the pattern "test_*" in the Lib/test subdirectory and runs -them in alphabetical order (but see -M and -u, below, for exceptions). - -For more rigorous testing, it is useful to use the following -command line: - -python -E -Wd -m test [options] [test_name1 ...] -""" - -EPILOG = """\ -Additional option details: - --r randomizes test execution order. You can use --randseed=int to provide an -int seed value for the randomizer; this is useful for reproducing troublesome -test orders. - --s On the first invocation of regrtest using -s, the first test file found -or the first test file given on the command line is run, and the name of -the next test is recorded in a file named pynexttest. If run from the -Python build directory, pynexttest is located in the 'build' subdirectory, -otherwise it is located in tempfile.gettempdir(). On subsequent runs, -the test in pynexttest is run, and the next test is written to pynexttest. -When the last test has been run, pynexttest is deleted. In this way it -is possible to single step through the test files. This is useful when -doing memory analysis on the Python interpreter, which process tends to -consume too many resources to run the full regression test non-stop. - --S is used to continue running tests after an aborted run. It will -maintain the order a standard run (ie, this assumes -r is not used). -This is useful after the tests have prematurely stopped for some external -reason and you want to start running from where you left off rather -than starting from the beginning. - --f reads the names of tests from the file given as f's argument, one -or more test names per line. Whitespace is ignored. Blank lines and -lines beginning with '#' are ignored. This is especially useful for -whittling down failures involving interactions among tests. - --L causes the leaks(1) command to be run just before exit if it exists. -leaks(1) is available on Mac OS X and presumably on some other -FreeBSD-derived systems. - --R runs each test several times and examines sys.gettotalrefcount() to -see if the test appears to be leaking references. The argument should -be of the form stab:run:fname where 'stab' is the number of times the -test is run to let gettotalrefcount settle down, 'run' is the number -of times further it is run and 'fname' is the name of the file the -reports are written to. These parameters all have defaults (5, 4 and -"reflog.txt" respectively), and the minimal invocation is '-R :'. - --M runs tests that require an exorbitant amount of memory. These tests -typically try to ascertain containers keep working when containing more than -2 billion objects, which only works on 64-bit systems. There are also some -tests that try to exhaust the address space of the process, which only makes -sense on 32-bit systems with at least 2Gb of memory. The passed-in memlimit, -which is a string in the form of '2.5Gb', determines howmuch memory the -tests will limit themselves to (but they may go slightly over.) The number -shouldn't be more memory than the machine has (including swap memory). You -should also keep in mind that swap memory is generally much, much slower -than RAM, and setting memlimit to all available RAM or higher will heavily -tax the machine. On the other hand, it is no use running these tests with a -limit of less than 2.5Gb, and many require more than 20Gb. Tests that expect -to use more than memlimit memory will be skipped. The big-memory tests -generally run very, very long. - --u is used to specify which special resource intensive tests to run, -such as those requiring large file support or network connectivity. -The argument is a comma-separated list of words indicating the -resources to test. Currently only the following are defined: - - all - Enable all special resources. - - none - Disable all special resources (this is the default). - - audio - Tests that use the audio device. (There are known - cases of broken audio drivers that can crash Python or - even the Linux kernel.) - - curses - Tests that use curses and will modify the terminal's - state and output modes. - - largefile - It is okay to run some test that may create huge - files. These tests can take a long time and may - consume >2GB of disk space temporarily. - - network - It is okay to run tests that use external network - resource, e.g. testing SSL support for sockets. - - decimal - Test the decimal module against a large suite that - verifies compliance with standards. - - cpu - Used for certain CPU-heavy tests. - - subprocess Run all tests for the subprocess module. - - urlfetch - It is okay to download files required on testing. - - gui - Run tests that require a running GUI. - -To enable all resources except one, use '-uall,-<resource>'. For -example, to run all the tests except for the gui tests, give the -option '-uall,-gui'. -""" - # We import importlib *ASAP* in order to test #15386 import importlib -import argparse -import builtins -import faulthandler -import io -import json -import locale -import logging import os -import platform -import random -import re -import shutil -import signal import sys -import sysconfig -import tempfile -import time -import traceback -import unittest -import warnings -from inspect import isabstract - -try: - import threading -except ImportError: - threading = None -try: - import _multiprocessing, multiprocessing.process -except ImportError: - multiprocessing = None - - -# Some times __path__ and __file__ are not absolute (e.g. while running from -# Lib/) and, if we change the CWD to run the tests in a temporary dir, some -# imports might fail. This affects only the modules imported before os.chdir(). -# These modules are searched first in sys.path[0] (so '' -- the CWD) and if -# they are found in the CWD their __file__ and __path__ will be relative (this -# happens before the chdir). All the modules imported after the chdir, are -# not found in the CWD, and since the other paths in sys.path[1:] are absolute -# (site.py absolutize them), the __file__ and __path__ will be absolute too. -# Therefore it is necessary to absolutize manually the __file__ and __path__ of -# the packages to prevent later imports to fail when the CWD is different. -for module in sys.modules.values(): - if hasattr(module, '__path__'): - module.__path__ = [os.path.abspath(path) for path in module.__path__] - if hasattr(module, '__file__'): - module.__file__ = os.path.abspath(module.__file__) - - -# MacOSX (a.k.a. Darwin) has a default stack size that is too small -# for deeply recursive regular expressions. We see this as crashes in -# the Python test suite when running test_re.py and test_sre.py. The -# fix is to set the stack limit to 2048. -# This approach may also be useful for other Unixy platforms that -# suffer from small default stack limits. -if sys.platform == 'darwin': - try: - import resource - except ImportError: - pass - else: - soft, hard = resource.getrlimit(resource.RLIMIT_STACK) - newsoft = min(hard, max(soft, 1024*2048)) - resource.setrlimit(resource.RLIMIT_STACK, (newsoft, hard)) - -# Test result constants. -PASSED = 1 -FAILED = 0 -ENV_CHANGED = -1 -SKIPPED = -2 -RESOURCE_DENIED = -3 -INTERRUPTED = -4 -CHILD_ERROR = -5 # error in a child process - -from test import support - -RESOURCE_NAMES = ('audio', 'curses', 'largefile', 'network', - 'decimal', 'cpu', 'subprocess', 'urlfetch', 'gui') - -# When tests are run from the Python build directory, it is best practice -# to keep the test files in a subfolder. This eases the cleanup of leftover -# files using the "make distclean" command. -if sysconfig.is_python_build(): - TEMPDIR = os.path.join(sysconfig.get_config_var('srcdir'), 'build') -else: - TEMPDIR = tempfile.gettempdir() -TEMPDIR = os.path.abspath(TEMPDIR) - -class _ArgParser(argparse.ArgumentParser): - - def error(self, message): - super().error(message + "\nPass -h or --help for complete help.") - -def _create_parser(): - # Set prog to prevent the uninformative "__main__.py" from displaying in - # error messages when using "python -m test ...". - parser = _ArgParser(prog='regrtest.py', - usage=USAGE, - description=DESCRIPTION, - epilog=EPILOG, - add_help=False, - formatter_class=argparse.RawDescriptionHelpFormatter) - - # Arguments with this clause added to its help are described further in - # the epilog's "Additional option details" section. - more_details = ' See the section at bottom for more details.' - - group = parser.add_argument_group('General options') - # We add help explicitly to control what argument group it renders under. - group.add_argument('-h', '--help', action='help', - help='show this help message and exit') - group.add_argument('--timeout', metavar='TIMEOUT', type=float, - help='dump the traceback and exit if a test takes ' - 'more than TIMEOUT seconds; disabled if TIMEOUT ' - 'is negative or equals to zero') - group.add_argument('--wait', action='store_true', - help='wait for user input, e.g., allow a debugger ' - 'to be attached') - group.add_argument('--slaveargs', metavar='ARGS') - group.add_argument('-S', '--start', metavar='START', - help='the name of the test at which to start.' + - more_details) - - group = parser.add_argument_group('Verbosity') - group.add_argument('-v', '--verbose', action='count', - help='run tests in verbose mode with output to stdout') - group.add_argument('-w', '--verbose2', action='store_true', - help='re-run failed tests in verbose mode') - group.add_argument('-W', '--verbose3', action='store_true', - help='display test output on failure') - group.add_argument('-q', '--quiet', action='store_true', - help='no output unless one or more tests fail') - group.add_argument('-o', '--slow', action='store_true', dest='print_slow', - help='print the slowest 10 tests') - group.add_argument('--header', action='store_true', - help='print header with interpreter info') - - group = parser.add_argument_group('Selecting tests') - group.add_argument('-r', '--randomize', action='store_true', - help='randomize test execution order.' + more_details) - group.add_argument('--randseed', metavar='SEED', - dest='random_seed', type=int, - help='pass a random seed to reproduce a previous ' - 'random run') - group.add_argument('-f', '--fromfile', metavar='FILE', - help='read names of tests to run from a file.' + - more_details) - group.add_argument('-x', '--exclude', action='store_true', - help='arguments are tests to *exclude*') - group.add_argument('-s', '--single', action='store_true', - help='single step through a set of tests.' + - more_details) - group.add_argument('-m', '--match', metavar='PAT', - dest='match_tests', - help='match test cases and methods with glob pattern PAT') - group.add_argument('-G', '--failfast', action='store_true', - help='fail as soon as a test fails (only with -v or -W)') - group.add_argument('-u', '--use', metavar='RES1,RES2,...', - action='append', type=resources_list, - help='specify which special resource intensive tests ' - 'to run.' + more_details) - group.add_argument('-M', '--memlimit', metavar='LIMIT', - help='run very large memory-consuming tests.' + - more_details) - group.add_argument('--testdir', metavar='DIR', - type=relative_filename, - help='execute test files in the specified directory ' - '(instead of the Python stdlib test suite)') - - group = parser.add_argument_group('Special runs') - group.add_argument('-l', '--findleaks', action='store_true', - help='if GC is available detect tests that leak memory') - group.add_argument('-L', '--runleaks', action='store_true', - help='run the leaks(1) command just before exit.' + - more_details) - group.add_argument('-R', '--huntrleaks', metavar='RUNCOUNTS', - type=huntrleaks, - help='search for reference leaks (needs debug build, ' - 'very slow).' + more_details) - group.add_argument('-j', '--multiprocess', metavar='PROCESSES', - dest='use_mp', type=int, - help='run PROCESSES processes at once') - group.add_argument('-T', '--coverage', action='store_true', - dest='trace', - help='turn on code coverage tracing using the trace ' - 'module') - group.add_argument('-D', '--coverdir', metavar='DIR', - type=relative_filename, - help='directory where coverage files are put') - group.add_argument('-N', '--nocoverdir', - action='store_const', const=None, dest='coverdir', - help='put coverage files alongside modules') - group.add_argument('-t', '--threshold', metavar='THRESHOLD', - type=int, - help='call gc.set_threshold(THRESHOLD)') - group.add_argument('-n', '--nowindows', action='store_true', - help='suppress error message boxes on Windows') - group.add_argument('-F', '--forever', action='store_true', - help='run the specified tests in a loop, until an ' - 'error happens') - group.add_argument('-P', '--pgo', dest='pgo', action='store_true', - help='enable Profile Guided Optimization training') - - parser.add_argument('args', nargs=argparse.REMAINDER, - help=argparse.SUPPRESS) - - return parser - -def relative_filename(string): - # CWD is replaced with a temporary dir before calling main(), so we - # join it with the saved CWD so it ends up where the user expects. - return os.path.join(support.SAVEDCWD, string) - -def huntrleaks(string): - args = string.split(':') - if len(args) not in (2, 3): - raise argparse.ArgumentTypeError( - 'needs 2 or 3 colon-separated arguments') - nwarmup = int(args[0]) if args[0] else 5 - ntracked = int(args[1]) if args[1] else 4 - fname = args[2] if len(args) > 2 and args[2] else 'reflog.txt' - return nwarmup, ntracked, fname - -def resources_list(string): - u = [x.lower() for x in string.split(',')] - for r in u: - if r == 'all' or r == 'none': - continue - if r[0] == '-': - r = r[1:] - if r not in RESOURCE_NAMES: - raise argparse.ArgumentTypeError('invalid resource: ' + r) - return u - -def _parse_args(args, **kwargs): - # Defaults - ns = argparse.Namespace(testdir=None, verbose=0, quiet=False, - exclude=False, single=False, randomize=False, fromfile=None, - findleaks=False, use_resources=None, trace=False, coverdir='coverage', - runleaks=False, huntrleaks=False, verbose2=False, print_slow=False, - random_seed=None, use_mp=None, verbose3=False, forever=False, - header=False, failfast=False, match_tests=None, pgo=False) - for k, v in kwargs.items(): - if not hasattr(ns, k): - raise TypeError('%r is an invalid keyword argument ' - 'for this function' % k) - setattr(ns, k, v) - if ns.use_resources is None: - ns.use_resources = [] - - parser = _create_parser() - parser.parse_args(args=args, namespace=ns) - - if ns.single and ns.fromfile: - parser.error("-s and -f don't go together!") - if ns.use_mp and ns.trace: - parser.error("-T and -j don't go together!") - if ns.use_mp and ns.findleaks: - parser.error("-l and -j don't go together!") - if ns.use_mp and ns.memlimit: - parser.error("-M and -j don't go together!") - if ns.failfast and not (ns.verbose or ns.verbose3): - parser.error("-G/--failfast needs either -v or -W") - - if ns.quiet: - ns.verbose = 0 - if ns.timeout is not None: - if hasattr(faulthandler, 'dump_traceback_later'): - if ns.timeout <= 0: - ns.timeout = None - else: - print("Warning: The timeout option requires " - "faulthandler.dump_traceback_later") - ns.timeout = None - if ns.use_mp is not None: - if ns.use_mp <= 0: - # Use all cores + extras for tests that like to sleep - ns.use_mp = 2 + (os.cpu_count() or 1) - if ns.use_mp == 1: - ns.use_mp = None - if ns.use: - for a in ns.use: - for r in a: - if r == 'all': - ns.use_resources[:] = RESOURCE_NAMES - continue - if r == 'none': - del ns.use_resources[:] - continue - remove = False - if r[0] == '-': - remove = True - r = r[1:] - if remove: - if r in ns.use_resources: - ns.use_resources.remove(r) - elif r not in ns.use_resources: - ns.use_resources.append(r) - if ns.random_seed is not None: - ns.randomize = True - - return ns - - -def run_test_in_subprocess(testname, ns): - """Run the given test in a subprocess with --slaveargs. - - ns is the option Namespace parsed from command-line arguments. regrtest - is invoked in a subprocess with the --slaveargs argument; when the - subprocess exits, its return code, stdout and stderr are returned as a - 3-tuple. - """ - from subprocess import Popen, PIPE - base_cmd = ([sys.executable] + support.args_from_interpreter_flags() + - ['-X', 'faulthandler', '-m', 'test.regrtest']) - # required to spawn a new process with PGO flag on/off - if ns.pgo: - base_cmd = base_cmd + ['--pgo'] - slaveargs = ( - (testname, ns.verbose, ns.quiet), - dict(huntrleaks=ns.huntrleaks, - use_resources=ns.use_resources, - output_on_failure=ns.verbose3, - timeout=ns.timeout, failfast=ns.failfast, - match_tests=ns.match_tests, pgo=ns.pgo)) - # Running the child from the same working directory as regrtest's original - # invocation ensures that TEMPDIR for the child is the same when - # sysconfig.is_python_build() is true. See issue 15300. - popen = Popen(base_cmd + ['--slaveargs', json.dumps(slaveargs)], - stdout=PIPE, stderr=PIPE, - universal_newlines=True, - close_fds=(os.name != 'nt'), - cwd=support.SAVEDCWD) - stdout, stderr = popen.communicate() - retcode = popen.wait() - return retcode, stdout, stderr - - -def main(tests=None, **kwargs): - """Execute a test suite. - - This also parses command-line options and modifies its behavior - accordingly. - - tests -- a list of strings containing test names (optional) - testdir -- the directory in which to look for tests (optional) - - Users other than the Python test suite will certainly want to - specify testdir; if it's omitted, the directory containing the - Python test suite is searched for. - - If the tests argument is omitted, the tests listed on the - command-line will be used. If that's empty, too, then all *.py - files beginning with test_ will be used. - - The other default arguments (verbose, quiet, exclude, - single, randomize, findleaks, use_resources, trace, coverdir, - print_slow, and random_seed) allow programmers calling main() - directly to set the values that would normally be set by flags - on the command line. - """ - # Display the Python traceback on fatal errors (e.g. segfault) - faulthandler.enable(all_threads=True) - - # Display the Python traceback on SIGALRM or SIGUSR1 signal - signals = [] - if hasattr(signal, 'SIGALRM'): - signals.append(signal.SIGALRM) - if hasattr(signal, 'SIGUSR1'): - signals.append(signal.SIGUSR1) - for signum in signals: - faulthandler.register(signum, chain=True) - - replace_stdout() - - support.record_original_stdout(sys.stdout) - - ns = _parse_args(sys.argv[1:], **kwargs) - - if ns.huntrleaks: - # Avoid false positives due to various caches - # filling slowly with random data: - warm_caches() - if ns.memlimit is not None: - support.set_memlimit(ns.memlimit) - if ns.threshold is not None: - import gc - gc.set_threshold(ns.threshold) - if ns.nowindows: - print('The --nowindows (-n) option is deprecated. ' - 'Use -vv to display assertions in stderr.') - try: - import msvcrt - except ImportError: - pass - else: - msvcrt.SetErrorMode(msvcrt.SEM_FAILCRITICALERRORS| - msvcrt.SEM_NOALIGNMENTFAULTEXCEPT| - msvcrt.SEM_NOGPFAULTERRORBOX| - msvcrt.SEM_NOOPENFILEERRORBOX) - try: - msvcrt.CrtSetReportMode - except AttributeError: - # release build - pass - else: - for m in [msvcrt.CRT_WARN, msvcrt.CRT_ERROR, msvcrt.CRT_ASSERT]: - if ns.verbose and ns.verbose >= 2: - msvcrt.CrtSetReportMode(m, msvcrt.CRTDBG_MODE_FILE) - msvcrt.CrtSetReportFile(m, msvcrt.CRTDBG_FILE_STDERR) - else: - msvcrt.CrtSetReportMode(m, 0) - if ns.wait: - input("Press any key to continue...") - - if ns.slaveargs is not None: - args, kwargs = json.loads(ns.slaveargs) - if kwargs.get('huntrleaks'): - unittest.BaseTestSuite._cleanup = False - try: - result = runtest(*args, **kwargs) - except KeyboardInterrupt: - result = INTERRUPTED, '' - except BaseException as e: - traceback.print_exc() - result = CHILD_ERROR, str(e) - sys.stdout.flush() - print() # Force a newline (just in case) - print(json.dumps(result)) - sys.exit(0) - - good = [] - bad = [] - skipped = [] - resource_denieds = [] - environment_changed = [] - interrupted = False - - if ns.findleaks: - try: - import gc - except ImportError: - print('No GC available, disabling findleaks.') - ns.findleaks = False - else: - # Uncomment the line below to report garbage that is not - # freeable by reference counting alone. By default only - # garbage that is not collectable by the GC is reported. - #gc.set_debug(gc.DEBUG_SAVEALL) - found_garbage = [] - - if ns.huntrleaks: - unittest.BaseTestSuite._cleanup = False - - if ns.single: - filename = os.path.join(TEMPDIR, 'pynexttest') - try: - with open(filename, 'r') as fp: - next_test = fp.read().strip() - tests = [next_test] - except OSError: - pass - - if ns.fromfile: - tests = [] - with open(os.path.join(support.SAVEDCWD, ns.fromfile)) as fp: - count_pat = re.compile(r'\[\s*\d+/\s*\d+\]') - for line in fp: - line = count_pat.sub('', line) - guts = line.split() # assuming no test has whitespace in its name - if guts and not guts[0].startswith('#'): - tests.extend(guts) - - # Strip .py extensions. - removepy(ns.args) - removepy(tests) - - stdtests = STDTESTS[:] - nottests = NOTTESTS.copy() - if ns.exclude: - for arg in ns.args: - if arg in stdtests: - stdtests.remove(arg) - nottests.add(arg) - ns.args = [] - - # For a partial run, we do not need to clutter the output. - if (ns.verbose or ns.header or - not (ns.pgo or ns.quiet or ns.single or tests or ns.args)): - # Print basic platform information - print("==", platform.python_implementation(), *sys.version.split()) - print("== ", platform.platform(aliased=True), - "%s-endian" % sys.byteorder) - print("== ", "hash algorithm:", sys.hash_info.algorithm, - "64bit" if sys.maxsize > 2**32 else "32bit") - print("== ", os.getcwd()) - print("Testing with flags:", sys.flags) - - # if testdir is set, then we are not running the python tests suite, so - # don't add default tests to be executed or skipped (pass empty values) - if ns.testdir: - alltests = findtests(ns.testdir, list(), set()) - else: - alltests = findtests(ns.testdir, stdtests, nottests) - - selected = tests or ns.args or alltests - if ns.single: - selected = selected[:1] - try: - next_single_test = alltests[alltests.index(selected[0])+1] - except IndexError: - next_single_test = None - # Remove all the selected tests that precede start if it's set. - if ns.start: - try: - del selected[:selected.index(ns.start)] - except ValueError: - print("Couldn't find starting test (%s), using all tests" % ns.start) - if ns.randomize: - if ns.random_seed is None: - ns.random_seed = random.randrange(10000000) - random.seed(ns.random_seed) - print("Using random seed", ns.random_seed) - random.shuffle(selected) - if ns.trace: - import trace, tempfile - tracer = trace.Trace(ignoredirs=[sys.base_prefix, sys.base_exec_prefix, - tempfile.gettempdir()], - trace=False, count=True) - - test_times = [] - support.verbose = ns.verbose # Tell tests to be moderately quiet - support.use_resources = ns.use_resources - save_modules = sys.modules.keys() - - def accumulate_result(test, result): - ok, test_time = result - if ok not in (CHILD_ERROR, INTERRUPTED): - test_times.append((test_time, test)) - if ok == PASSED: - good.append(test) - elif ok == FAILED: - bad.append(test) - elif ok == ENV_CHANGED: - environment_changed.append(test) - elif ok == SKIPPED: - skipped.append(test) - elif ok == RESOURCE_DENIED: - skipped.append(test) - resource_denieds.append(test) - - if ns.forever: - def test_forever(tests=list(selected)): - while True: - for test in tests: - yield test - if bad: - return - tests = test_forever() - test_count = '' - test_count_width = 3 - else: - tests = iter(selected) - test_count = '/{}'.format(len(selected)) - test_count_width = len(test_count) - 1 - - if ns.use_mp: - try: - from threading import Thread - except ImportError: - print("Multiprocess option requires thread support") - sys.exit(2) - from queue import Queue - debug_output_pat = re.compile(r"\[\d+ refs, \d+ blocks\]$") - output = Queue() - pending = MultiprocessTests(tests) - def work(): - # A worker thread. - try: - while True: - try: - test = next(pending) - except StopIteration: - output.put((None, None, None, None)) - return - retcode, stdout, stderr = run_test_in_subprocess(test, ns) - # Strip last refcount output line if it exists, since it - # comes from the shutdown of the interpreter in the subcommand. - stderr = debug_output_pat.sub("", stderr) - stdout, _, result = stdout.strip().rpartition("\n") - if retcode != 0: - result = (CHILD_ERROR, "Exit code %s" % retcode) - output.put((test, stdout.rstrip(), stderr.rstrip(), result)) - return - if not result: - output.put((None, None, None, None)) - return - result = json.loads(result) - output.put((test, stdout.rstrip(), stderr.rstrip(), result)) - except BaseException: - output.put((None, None, None, None)) - raise - workers = [Thread(target=work) for i in range(ns.use_mp)] - for worker in workers: - worker.start() - finished = 0 - test_index = 1 - try: - while finished < ns.use_mp: - test, stdout, stderr, result = output.get() - if test is None: - finished += 1 - continue - accumulate_result(test, result) - if not ns.quiet: - if bad and not ns.pgo: - fmt = "[{1:{0}}{2}/{3}] {4}" - else: - fmt = "[{1:{0}}{2}] {4}" - print(fmt.format( - test_count_width, test_index, test_count, - len(bad), test)) - if stdout: - print(stdout) - if stderr and not ns.pgo: - print(stderr, file=sys.stderr) - sys.stdout.flush() - sys.stderr.flush() - if result[0] == INTERRUPTED: - raise KeyboardInterrupt - if result[0] == CHILD_ERROR: - raise Exception("Child error on {}: {}".format(test, result[1])) - test_index += 1 - except KeyboardInterrupt: - interrupted = True - pending.interrupted = True - for worker in workers: - worker.join() - else: - for test_index, test in enumerate(tests, 1): - if not ns.quiet: - if bad and not ns.pgo: - fmt = "[{1:{0}}{2}/{3}] {4}" - else: - fmt = "[{1:{0}}{2}] {4}" - print(fmt.format( - test_count_width, test_index, test_count, len(bad), test)) - sys.stdout.flush() - if ns.trace: - # If we're tracing code coverage, then we don't exit with status - # if on a false return value from main. - tracer.runctx('runtest(test, ns.verbose, ns.quiet, timeout=ns.timeout)', - globals=globals(), locals=vars()) - else: - try: - result = runtest(test, ns.verbose, ns.quiet, - ns.huntrleaks, - output_on_failure=ns.verbose3, - timeout=ns.timeout, failfast=ns.failfast, - match_tests=ns.match_tests, pgo=ns.pgo) - accumulate_result(test, result) - except KeyboardInterrupt: - interrupted = True - break - if ns.findleaks: - gc.collect() - if gc.garbage: - print("Warning: test created", len(gc.garbage), end=' ') - print("uncollectable object(s).") - # move the uncollectable objects somewhere so we don't see - # them again - found_garbage.extend(gc.garbage) - del gc.garbage[:] - # Unload the newly imported modules (best effort finalization) - for module in sys.modules.keys(): - if module not in save_modules and module.startswith("test."): - support.unload(module) - - if interrupted and not ns.pgo: - # print a newline after ^C - print() - print("Test suite interrupted by signal SIGINT.") - omitted = set(selected) - set(good) - set(bad) - set(skipped) - print(count(len(omitted), "test"), "omitted:") - printlist(omitted) - if good and not ns.quiet and not ns.pgo: - if not bad and not skipped and not interrupted and len(good) > 1: - print("All", end=' ') - print(count(len(good), "test"), "OK.") - if ns.print_slow: - test_times.sort(reverse=True) - print("10 slowest tests:") - for time, test in test_times[:10]: - print("%s: %.1fs" % (test, time)) - if bad and not ns.pgo: - print(count(len(bad), "test"), "failed:") - printlist(bad) - if environment_changed and not ns.pgo: - print("{} altered the execution environment:".format( - count(len(environment_changed), "test"))) - printlist(environment_changed) - if skipped and not ns.quiet and not ns.pgo: - print(count(len(skipped), "test"), "skipped:") - printlist(skipped) - - if ns.verbose2 and bad: - print("Re-running failed tests in verbose mode") - for test in bad[:]: - if not ns.pgo: - print("Re-running test %r in verbose mode" % test) - sys.stdout.flush() - try: - ns.verbose = True - ok = runtest(test, True, ns.quiet, ns.huntrleaks, - timeout=ns.timeout, pgo=ns.pgo) - except KeyboardInterrupt: - # print a newline separate from the ^C - print() - break - else: - if ok[0] in {PASSED, ENV_CHANGED, SKIPPED, RESOURCE_DENIED}: - bad.remove(test) - else: - if bad: - print(count(len(bad), 'test'), "failed again:") - printlist(bad) - - if ns.single: - if next_single_test: - with open(filename, 'w') as fp: - fp.write(next_single_test + '\n') - else: - os.unlink(filename) +from test.libregrtest import main - if ns.trace: - r = tracer.results() - r.write_results(show_missing=True, summary=True, coverdir=ns.coverdir) - if ns.runleaks: - os.system("leaks %d" % os.getpid()) +# Alias for backward compatibility (just in case) +main_in_temp_cwd = main - sys.exit(len(bad) > 0 or interrupted) +def _main(): + global __file__ -# small set of tests to determine if we have a basically functioning interpreter -# (i.e. if any of these fail, then anything else is likely to follow) -STDTESTS = [ - 'test_grammar', - 'test_opcodes', - 'test_dict', - 'test_builtin', - 'test_exceptions', - 'test_types', - 'test_unittest', - 'test_doctest', - 'test_doctest2', - 'test_support' -] - -# set of tests that we don't want to be executed when using regrtest -NOTTESTS = set() - -def findtests(testdir=None, stdtests=STDTESTS, nottests=NOTTESTS): - """Return a list of all applicable test modules.""" - testdir = findtestdir(testdir) - names = os.listdir(testdir) - tests = [] - others = set(stdtests) | nottests - for name in names: - mod, ext = os.path.splitext(name) - if mod[:5] == "test_" and ext in (".py", "") and mod not in others: - tests.append(mod) - return stdtests + sorted(tests) - -# We do not use a generator so multiple threads can call next(). -class MultiprocessTests(object): - - """A thread-safe iterator over tests for multiprocess mode.""" - - def __init__(self, tests): - self.interrupted = False - self.lock = threading.Lock() - self.tests = tests - - def __iter__(self): - return self - - def __next__(self): - with self.lock: - if self.interrupted: - raise StopIteration('tests interrupted') - return next(self.tests) - -def replace_stdout(): - """Set stdout encoder error handler to backslashreplace (as stderr error - handler) to avoid UnicodeEncodeError when printing a traceback""" - import atexit - - stdout = sys.stdout - sys.stdout = open(stdout.fileno(), 'w', - encoding=stdout.encoding, - errors="backslashreplace", - closefd=False, - newline='\n') - - def restore_stdout(): - sys.stdout.close() - sys.stdout = stdout - atexit.register(restore_stdout) - -def runtest(test, verbose, quiet, - huntrleaks=False, use_resources=None, - output_on_failure=False, failfast=False, match_tests=None, - timeout=None, *, pgo=False): - """Run a single test. - - test -- the name of the test - verbose -- if true, print more messages - quiet -- if true, don't print 'skipped' messages (probably redundant) - huntrleaks -- run multiple times to test for leaks; requires a debug - build; a triple corresponding to -R's three arguments - use_resources -- list of extra resources to use - output_on_failure -- if true, display test output on failure - timeout -- dump the traceback and exit if a test takes more than - timeout seconds - failfast, match_tests -- See regrtest command-line flags for these. - pgo -- if true, do not print unnecessary info when running the test - for Profile Guided Optimization build - - Returns the tuple result, test_time, where result is one of the constants: - INTERRUPTED KeyboardInterrupt when run under -j - RESOURCE_DENIED test skipped because resource denied - SKIPPED test skipped for some other reason - ENV_CHANGED test failed because it changed the execution environment - FAILED test failed - PASSED test passed - """ - if use_resources is not None: - support.use_resources = use_resources - use_timeout = (timeout is not None) - if use_timeout: - faulthandler.dump_traceback_later(timeout, exit=True) - try: - support.match_tests = match_tests - if failfast: - support.failfast = True - if output_on_failure: - support.verbose = True - - # Reuse the same instance to all calls to runtest(). Some - # tests keep a reference to sys.stdout or sys.stderr - # (eg. test_argparse). - if runtest.stringio is None: - stream = io.StringIO() - runtest.stringio = stream - else: - stream = runtest.stringio - stream.seek(0) - stream.truncate() - - orig_stdout = sys.stdout - orig_stderr = sys.stderr - try: - sys.stdout = stream - sys.stderr = stream - result = runtest_inner(test, verbose, quiet, huntrleaks, - display_failure=False, pgo=pgo) - if result[0] == FAILED and not pgo: - output = stream.getvalue() - orig_stderr.write(output) - orig_stderr.flush() - finally: - sys.stdout = orig_stdout - sys.stderr = orig_stderr - else: - support.verbose = verbose # Tell tests to be moderately quiet - result = runtest_inner(test, verbose, quiet, huntrleaks, - display_failure=not verbose, pgo=pgo) - return result - finally: - if use_timeout: - faulthandler.cancel_dump_traceback_later() - cleanup_test_droppings(test, verbose) -runtest.stringio = None - -# Unit tests are supposed to leave the execution environment unchanged -# once they complete. But sometimes tests have bugs, especially when -# tests fail, and the changes to environment go on to mess up other -# tests. This can cause issues with buildbot stability, since tests -# are run in random order and so problems may appear to come and go. -# There are a few things we can save and restore to mitigate this, and -# the following context manager handles this task. - -class saved_test_environment: - """Save bits of the test environment and restore them at block exit. - - with saved_test_environment(testname, verbose, quiet): - #stuff - - Unless quiet is True, a warning is printed to stderr if any of - the saved items was changed by the test. The attribute 'changed' - is initially False, but is set to True if a change is detected. - - If verbose is more than 1, the before and after state of changed - items is also printed. - """ - - changed = False - - def __init__(self, testname, verbose=0, quiet=False, *, pgo=False): - self.testname = testname - self.verbose = verbose - self.quiet = quiet - self.pgo = pgo - - # To add things to save and restore, add a name XXX to the resources list - # and add corresponding get_XXX/restore_XXX functions. get_XXX should - # return the value to be saved and compared against a second call to the - # get function when test execution completes. restore_XXX should accept - # the saved value and restore the resource using it. It will be called if - # and only if a change in the value is detected. - # - # Note: XXX will have any '.' replaced with '_' characters when determining - # the corresponding method names. - - resources = ('sys.argv', 'cwd', 'sys.stdin', 'sys.stdout', 'sys.stderr', - 'os.environ', 'sys.path', 'sys.path_hooks', '__import__', - 'warnings.filters', 'asyncore.socket_map', - 'logging._handlers', 'logging._handlerList', 'sys.gettrace', - 'sys.warnoptions', - # multiprocessing.process._cleanup() may release ref - # to a thread, so check processes first. - 'multiprocessing.process._dangling', 'threading._dangling', - 'sysconfig._CONFIG_VARS', 'sysconfig._INSTALL_SCHEMES', - 'files', 'locale', 'warnings.showwarning', - 'shutil_archive_formats', 'shutil_unpack_formats', - ) - - def get_sys_argv(self): - return id(sys.argv), sys.argv, sys.argv[:] - def restore_sys_argv(self, saved_argv): - sys.argv = saved_argv[1] - sys.argv[:] = saved_argv[2] - - def get_cwd(self): - return os.getcwd() - def restore_cwd(self, saved_cwd): - os.chdir(saved_cwd) - - def get_sys_stdout(self): - return sys.stdout - def restore_sys_stdout(self, saved_stdout): - sys.stdout = saved_stdout - - def get_sys_stderr(self): - return sys.stderr - def restore_sys_stderr(self, saved_stderr): - sys.stderr = saved_stderr - - def get_sys_stdin(self): - return sys.stdin - def restore_sys_stdin(self, saved_stdin): - sys.stdin = saved_stdin - - def get_os_environ(self): - return id(os.environ), os.environ, dict(os.environ) - def restore_os_environ(self, saved_environ): - os.environ = saved_environ[1] - os.environ.clear() - os.environ.update(saved_environ[2]) - - def get_sys_path(self): - return id(sys.path), sys.path, sys.path[:] - def restore_sys_path(self, saved_path): - sys.path = saved_path[1] - sys.path[:] = saved_path[2] - - def get_sys_path_hooks(self): - return id(sys.path_hooks), sys.path_hooks, sys.path_hooks[:] - def restore_sys_path_hooks(self, saved_hooks): - sys.path_hooks = saved_hooks[1] - sys.path_hooks[:] = saved_hooks[2] - - def get_sys_gettrace(self): - return sys.gettrace() - def restore_sys_gettrace(self, trace_fxn): - sys.settrace(trace_fxn) - - def get___import__(self): - return builtins.__import__ - def restore___import__(self, import_): - builtins.__import__ = import_ - - def get_warnings_filters(self): - return id(warnings.filters), warnings.filters, warnings.filters[:] - def restore_warnings_filters(self, saved_filters): - warnings.filters = saved_filters[1] - warnings.filters[:] = saved_filters[2] - - def get_asyncore_socket_map(self): - asyncore = sys.modules.get('asyncore') - # XXX Making a copy keeps objects alive until __exit__ gets called. - return asyncore and asyncore.socket_map.copy() or {} - def restore_asyncore_socket_map(self, saved_map): - asyncore = sys.modules.get('asyncore') - if asyncore is not None: - asyncore.close_all(ignore_all=True) - asyncore.socket_map.update(saved_map) - - def get_shutil_archive_formats(self): - # we could call get_archives_formats() but that only returns the - # registry keys; we want to check the values too (the functions that - # are registered) - return shutil._ARCHIVE_FORMATS, shutil._ARCHIVE_FORMATS.copy() - def restore_shutil_archive_formats(self, saved): - shutil._ARCHIVE_FORMATS = saved[0] - shutil._ARCHIVE_FORMATS.clear() - shutil._ARCHIVE_FORMATS.update(saved[1]) - - def get_shutil_unpack_formats(self): - return shutil._UNPACK_FORMATS, shutil._UNPACK_FORMATS.copy() - def restore_shutil_unpack_formats(self, saved): - shutil._UNPACK_FORMATS = saved[0] - shutil._UNPACK_FORMATS.clear() - shutil._UNPACK_FORMATS.update(saved[1]) - - def get_logging__handlers(self): - # _handlers is a WeakValueDictionary - return id(logging._handlers), logging._handlers, logging._handlers.copy() - def restore_logging__handlers(self, saved_handlers): - # Can't easily revert the logging state - pass - - def get_logging__handlerList(self): - # _handlerList is a list of weakrefs to handlers - return id(logging._handlerList), logging._handlerList, logging._handlerList[:] - def restore_logging__handlerList(self, saved_handlerList): - # Can't easily revert the logging state - pass - - def get_sys_warnoptions(self): - return id(sys.warnoptions), sys.warnoptions, sys.warnoptions[:] - def restore_sys_warnoptions(self, saved_options): - sys.warnoptions = saved_options[1] - sys.warnoptions[:] = saved_options[2] - - # Controlling dangling references to Thread objects can make it easier - # to track reference leaks. - def get_threading__dangling(self): - if not threading: - return None - # This copies the weakrefs without making any strong reference - return threading._dangling.copy() - def restore_threading__dangling(self, saved): - if not threading: - return - threading._dangling.clear() - threading._dangling.update(saved) - - # Same for Process objects - def get_multiprocessing_process__dangling(self): - if not multiprocessing: - return None - # Unjoined process objects can survive after process exits - multiprocessing.process._cleanup() - # This copies the weakrefs without making any strong reference - return multiprocessing.process._dangling.copy() - def restore_multiprocessing_process__dangling(self, saved): - if not multiprocessing: - return - multiprocessing.process._dangling.clear() - multiprocessing.process._dangling.update(saved) - - def get_sysconfig__CONFIG_VARS(self): - # make sure the dict is initialized - sysconfig.get_config_var('prefix') - return (id(sysconfig._CONFIG_VARS), sysconfig._CONFIG_VARS, - dict(sysconfig._CONFIG_VARS)) - def restore_sysconfig__CONFIG_VARS(self, saved): - sysconfig._CONFIG_VARS = saved[1] - sysconfig._CONFIG_VARS.clear() - sysconfig._CONFIG_VARS.update(saved[2]) - - def get_sysconfig__INSTALL_SCHEMES(self): - return (id(sysconfig._INSTALL_SCHEMES), sysconfig._INSTALL_SCHEMES, - sysconfig._INSTALL_SCHEMES.copy()) - def restore_sysconfig__INSTALL_SCHEMES(self, saved): - sysconfig._INSTALL_SCHEMES = saved[1] - sysconfig._INSTALL_SCHEMES.clear() - sysconfig._INSTALL_SCHEMES.update(saved[2]) - - def get_files(self): - return sorted(fn + ('/' if os.path.isdir(fn) else '') - for fn in os.listdir()) - def restore_files(self, saved_value): - fn = support.TESTFN - if fn not in saved_value and (fn + '/') not in saved_value: - if os.path.isfile(fn): - support.unlink(fn) - elif os.path.isdir(fn): - support.rmtree(fn) - - _lc = [getattr(locale, lc) for lc in dir(locale) - if lc.startswith('LC_')] - def get_locale(self): - pairings = [] - for lc in self._lc: - try: - pairings.append((lc, locale.setlocale(lc, None))) - except (TypeError, ValueError): - continue - return pairings - def restore_locale(self, saved): - for lc, setting in saved: - locale.setlocale(lc, setting) - - def get_warnings_showwarning(self): - return warnings.showwarning - def restore_warnings_showwarning(self, fxn): - warnings.showwarning = fxn - - def resource_info(self): - for name in self.resources: - method_suffix = name.replace('.', '_') - get_name = 'get_' + method_suffix - restore_name = 'restore_' + method_suffix - yield name, getattr(self, get_name), getattr(self, restore_name) - - def __enter__(self): - self.saved_values = dict((name, get()) for name, get, restore - in self.resource_info()) - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - saved_values = self.saved_values - del self.saved_values - support.gc_collect() # Some resources use weak references - for name, get, restore in self.resource_info(): - current = get() - original = saved_values.pop(name) - # Check for changes to the resource's value - if current != original: - self.changed = True - restore(original) - if not self.quiet and not self.pgo: - print("Warning -- {} was modified by {}".format( - name, self.testname), - file=sys.stderr) - if self.verbose > 1 and not self.pgo: - print(" Before: {}\n After: {} ".format( - original, current), - file=sys.stderr) - return False - - -def runtest_inner(test, verbose, quiet, - huntrleaks=False, display_failure=True, pgo=False): - support.unload(test) - - test_time = 0.0 - refleak = False # True if the test leaked references. - try: - if test.startswith('test.'): - abstest = test - else: - # Always import it from the test package - abstest = 'test.' + test - with saved_test_environment(test, verbose, quiet, pgo=pgo) as environment: - start_time = time.time() - the_module = importlib.import_module(abstest) - # If the test has a test_main, that will run the appropriate - # tests. If not, use normal unittest test loading. - test_runner = getattr(the_module, "test_main", None) - if test_runner is None: - 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: - refleak = dash_R(the_module, test, test_runner, huntrleaks) - test_time = time.time() - start_time - except support.ResourceDenied as msg: - if not quiet and not pgo: - print(test, "skipped --", msg) - sys.stdout.flush() - return RESOURCE_DENIED, test_time - except unittest.SkipTest as msg: - if not quiet and not pgo: - print(test, "skipped --", msg) - sys.stdout.flush() - return SKIPPED, test_time - except KeyboardInterrupt: - raise - except support.TestFailed as msg: - if not pgo: - if display_failure: - print("test", test, "failed --", msg, file=sys.stderr) - else: - print("test", test, "failed", file=sys.stderr) - sys.stderr.flush() - return FAILED, test_time - except: - msg = traceback.format_exc() - if not pgo: - print("test", test, "crashed --", msg, file=sys.stderr) - sys.stderr.flush() - return FAILED, test_time - else: - if refleak: - return FAILED, test_time - if environment.changed: - return ENV_CHANGED, test_time - return PASSED, test_time - -def cleanup_test_droppings(testname, verbose): - import shutil - import stat - import gc - - # First kill any dangling references to open files etc. - # This can also issue some ResourceWarnings which would otherwise get - # triggered during the following test run, and possibly produce failures. - gc.collect() - - # Try to clean up junk commonly left behind. While tests shouldn't leave - # any files or directories behind, when a test fails that can be tedious - # for it to arrange. The consequences can be especially nasty on Windows, - # since if a test leaves a file open, it cannot be deleted by name (while - # there's nothing we can do about that here either, we can display the - # name of the offending test, which is a real help). - for name in (support.TESTFN, - "db_home", - ): - if not os.path.exists(name): - continue - - if os.path.isdir(name): - kind, nuker = "directory", shutil.rmtree - elif os.path.isfile(name): - kind, nuker = "file", os.unlink - else: - raise SystemError("os.path says %r exists but is neither " - "directory nor file" % name) - - if verbose: - print("%r left behind %s %r" % (testname, kind, name)) - try: - # if we have chmod, fix possible permissions problems - # that might prevent cleanup - if (hasattr(os, 'chmod')): - os.chmod(name, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) - nuker(name) - except Exception as msg: - print(("%r left behind %s %r and it couldn't be " - "removed: %s" % (testname, kind, name, msg)), file=sys.stderr) - -def dash_R(the_module, test, indirect_test, huntrleaks): - """Run a test multiple times, looking for reference leaks. - - Returns: - False if the test didn't leak references; True if we detected refleaks. - """ - # This code is hackish and inelegant, but it seems to do the job. - import copyreg - import collections.abc - - if not hasattr(sys, 'gettotalrefcount'): - raise Exception("Tracking reference leaks requires a debug build " - "of Python") - - # Save current values for dash_R_cleanup() to restore. - fs = warnings.filters[:] - ps = copyreg.dispatch_table.copy() - pic = sys.path_importer_cache.copy() - try: - import zipimport - except ImportError: - zdc = None # Run unmodified on platforms without zipimport support - else: - zdc = zipimport._zip_directory_cache.copy() - abcs = {} - for abc in [getattr(collections.abc, a) for a in collections.abc.__all__]: - if not isabstract(abc): - continue - for obj in abc.__subclasses__() + [abc]: - abcs[obj] = obj._abc_registry.copy() - - nwarmup, ntracked, fname = huntrleaks - fname = os.path.join(support.SAVEDCWD, fname) - repcount = nwarmup + ntracked - rc_deltas = [0] * repcount - alloc_deltas = [0] * repcount - - print("beginning", repcount, "repetitions", file=sys.stderr) - print(("1234567890"*(repcount//10 + 1))[:repcount], file=sys.stderr) - sys.stderr.flush() - for i in range(repcount): - indirect_test() - alloc_after, rc_after = dash_R_cleanup(fs, ps, pic, zdc, abcs) - sys.stderr.write('.') - sys.stderr.flush() - if i >= nwarmup: - rc_deltas[i] = rc_after - rc_before - alloc_deltas[i] = alloc_after - alloc_before - alloc_before, rc_before = alloc_after, rc_after - print(file=sys.stderr) - # These checkers return False on success, True on failure - def check_rc_deltas(deltas): - return any(deltas) - def check_alloc_deltas(deltas): - # At least 1/3rd of 0s - if 3 * deltas.count(0) < len(deltas): - return True - # Nothing else than 1s, 0s and -1s - if not set(deltas) <= {1,0,-1}: - return True - return False - failed = False - for deltas, item_name, checker in [ - (rc_deltas, 'references', check_rc_deltas), - (alloc_deltas, 'memory blocks', check_alloc_deltas)]: - if checker(deltas): - msg = '%s leaked %s %s, sum=%s' % ( - test, deltas[nwarmup:], item_name, sum(deltas)) - print(msg, file=sys.stderr) - sys.stderr.flush() - with open(fname, "a") as refrep: - print(msg, file=refrep) - refrep.flush() - failed = True - return failed - -def dash_R_cleanup(fs, ps, pic, zdc, abcs): - import gc, copyreg - import _strptime, linecache - import urllib.parse, urllib.request, mimetypes, doctest - import struct, filecmp, collections.abc - from distutils.dir_util import _path_created - from weakref import WeakSet - - # Clear the warnings registry, so they can be displayed again - for mod in sys.modules.values(): - if hasattr(mod, '__warningregistry__'): - del mod.__warningregistry__ - - # Restore some original values. - warnings.filters[:] = fs - copyreg.dispatch_table.clear() - copyreg.dispatch_table.update(ps) - sys.path_importer_cache.clear() - sys.path_importer_cache.update(pic) - try: - import zipimport - except ImportError: - pass # Run unmodified on platforms without zipimport support - else: - zipimport._zip_directory_cache.clear() - zipimport._zip_directory_cache.update(zdc) - - # clear type cache - sys._clear_type_cache() - - # Clear ABC registries, restoring previously saved ABC registries. - for abc in [getattr(collections.abc, a) for a in collections.abc.__all__]: - if not isabstract(abc): - continue - for obj in abc.__subclasses__() + [abc]: - obj._abc_registry = abcs.get(obj, WeakSet()).copy() - obj._abc_cache.clear() - obj._abc_negative_cache.clear() - - # Flush standard output, so that buffered data is sent to the OS and - # associated Python objects are reclaimed. - for stream in (sys.stdout, sys.stderr, sys.__stdout__, sys.__stderr__): - if stream is not None: - stream.flush() - - # Clear assorted module caches. - _path_created.clear() - re.purge() - _strptime._regex_cache.clear() - urllib.parse.clear_cache() - urllib.request.urlcleanup() - linecache.clearcache() - mimetypes._default_mime_types() - filecmp._cache.clear() - struct._clearcache() - doctest.master = None - try: - import ctypes - except ImportError: - # Don't worry about resetting the cache if ctypes is not supported - pass - else: - ctypes._reset_cache() - - # Collect cyclic trash and read memory statistics immediately after. - func1 = sys.getallocatedblocks - func2 = sys.gettotalrefcount - gc.collect() - return func1(), func2() - -def warm_caches(): - # char cache - s = bytes(range(256)) - for i in range(256): - s[i:i+1] - # unicode cache - x = [chr(i) for i in range(256)] - # int cache - x = list(range(-5, 257)) - -def findtestdir(path=None): - return path or os.path.dirname(__file__) or os.curdir - -def removepy(names): - if not names: - return - for idx, name in enumerate(names): - basename, ext = os.path.splitext(name) - if ext == '.py': - names[idx] = basename - -def count(n, word): - if n == 1: - return "%d %s" % (n, word) - else: - return "%d %ss" % (n, word) - -def printlist(x, width=70, indent=4): - """Print the elements of iterable x to stdout. - - Optional arg width (default 70) is the maximum line length. - Optional arg indent (default 4) is the number of blanks with which to - begin each line. - """ - - from textwrap import fill - blanks = ' ' * indent - # Print the sorted list: 'x' may be a '--random' list or a set() - print(fill(' '.join(str(elt) for elt in sorted(x)), width, - initial_indent=blanks, subsequent_indent=blanks)) - - -def main_in_temp_cwd(): - """Run main() in a temporary working directory.""" - if sysconfig.is_python_build(): - try: - os.mkdir(TEMPDIR) - except FileExistsError: - pass - - # Define a writable temp dir that will be used as cwd while running - # the tests. The name of the dir includes the pid to allow parallel - # testing (see the -j option). - test_cwd = 'test_python_{}'.format(os.getpid()) - test_cwd = os.path.join(TEMPDIR, test_cwd) - - # Run the tests in a context manager that temporarily changes the CWD to a - # temporary and writable directory. If it's not possible to create or - # change the CWD, the original CWD will be used. The original CWD is - # available from support.SAVEDCWD. - with support.temp_cwd(test_cwd, quiet=True): - main() - - -if __name__ == '__main__': # Remove regrtest.py's own directory from the module search path. Despite # the elimination of implicit relative imports, this is still needed to # ensure that submodules of the test package do not inappropriately appear # as top-level modules even when people (or buildbots!) invoke regrtest.py # directly instead of using the -m switch mydir = os.path.abspath(os.path.normpath(os.path.dirname(sys.argv[0]))) - i = len(sys.path) + i = len(sys.path) - 1 while i >= 0: - i -= 1 if os.path.abspath(os.path.normpath(sys.path[i])) == mydir: del sys.path[i] + else: + i -= 1 # findtestdir() gets the dirname out of __file__, so we have to make it # absolute before changing the working directory. @@ -1615,4 +43,8 @@ if __name__ == '__main__': # sanity check assert __file__ == os.path.abspath(sys.argv[0]) - main_in_temp_cwd() + main() + + +if __name__ == '__main__': + _main() diff --git a/Lib/test/seq_tests.py b/Lib/test/seq_tests.py index 72f4845..1e7a6f6 100644 --- a/Lib/test/seq_tests.py +++ b/Lib/test/seq_tests.py @@ -318,7 +318,6 @@ class CommonTest(unittest.TestCase): self.assertEqual(id(s), id(s*1)) def test_bigrepeat(self): - import sys if sys.maxsize <= 2147483647: x = self.type2test([0]) x *= 2**16 diff --git a/Lib/test/signalinterproctester.py b/Lib/test/signalinterproctester.py new file mode 100644 index 0000000..d3ae170 --- /dev/null +++ b/Lib/test/signalinterproctester.py @@ -0,0 +1,84 @@ +import os +import signal +import subprocess +import sys +import time +import unittest + + +class SIGUSR1Exception(Exception): + pass + + +class InterProcessSignalTests(unittest.TestCase): + def setUp(self): + self.got_signals = {'SIGHUP': 0, 'SIGUSR1': 0, 'SIGALRM': 0} + + def sighup_handler(self, signum, frame): + self.got_signals['SIGHUP'] += 1 + + def sigusr1_handler(self, signum, frame): + self.got_signals['SIGUSR1'] += 1 + raise SIGUSR1Exception + + def wait_signal(self, child, signame, exc_class=None): + try: + if child is not None: + # This wait should be interrupted by exc_class + # (if set) + child.wait() + + timeout = 10.0 + deadline = time.monotonic() + timeout + + while time.monotonic() < deadline: + if self.got_signals[signame]: + return + signal.pause() + except BaseException as exc: + if exc_class is not None and isinstance(exc, exc_class): + # got the expected exception + return + raise + + self.fail('signal %s not received after %s seconds' + % (signame, timeout)) + + def subprocess_send_signal(self, pid, signame): + code = 'import os, signal; os.kill(%s, signal.%s)' % (pid, signame) + args = [sys.executable, '-I', '-c', code] + return subprocess.Popen(args) + + def test_interprocess_signal(self): + # Install handlers. This function runs in a sub-process, so we + # don't worry about re-setting the default handlers. + signal.signal(signal.SIGHUP, self.sighup_handler) + signal.signal(signal.SIGUSR1, self.sigusr1_handler) + signal.signal(signal.SIGUSR2, signal.SIG_IGN) + signal.signal(signal.SIGALRM, signal.default_int_handler) + + # Let the sub-processes know who to send signals to. + pid = str(os.getpid()) + + with self.subprocess_send_signal(pid, "SIGHUP") as child: + self.wait_signal(child, 'SIGHUP') + self.assertEqual(self.got_signals, {'SIGHUP': 1, 'SIGUSR1': 0, + 'SIGALRM': 0}) + + with self.subprocess_send_signal(pid, "SIGUSR1") as child: + self.wait_signal(child, 'SIGUSR1', SIGUSR1Exception) + self.assertEqual(self.got_signals, {'SIGHUP': 1, 'SIGUSR1': 1, + 'SIGALRM': 0}) + + with self.subprocess_send_signal(pid, "SIGUSR2") as child: + # Nothing should happen: SIGUSR2 is ignored + child.wait() + + signal.alarm(1) + self.wait_signal(None, 'SIGALRM', KeyboardInterrupt) + self.assertEqual(self.got_signals, {'SIGHUP': 1, 'SIGUSR1': 1, + 'SIGALRM': 0}) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 007f3bc..4a7cd40 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -26,6 +26,7 @@ import sys import sysconfig import tempfile import time +import types import unittest import urllib.error import warnings @@ -89,8 +90,9 @@ __all__ = [ "bigmemtest", "bigaddrspacetest", "cpython_only", "get_attribute", "requires_IEEE_754", "skip_unless_xattr", "requires_zlib", "anticipate_failure", "load_package_tests", "detect_api_mismatch", + "check__all__", # sys - "is_jython", "check_impl_detail", + "is_jython", "is_android", "check_impl_detail", "unix_shell", # network "HOST", "IPV6_ENABLED", "find_unused_port", "bind_port", "open_urlresource", # processes @@ -733,6 +735,13 @@ requires_lzma = unittest.skipUnless(lzma, 'requires lzma') is_jython = sys.platform.startswith('java') +is_android = bool(sysconfig.get_config_var('ANDROID_API_LEVEL')) + +if sys.platform != 'win32': + unix_shell = '/system/bin/sh' if is_android else '/bin/sh' +else: + unix_shell = None + # Filename used for testing if os.name == 'java': # Jython disallows @ in module names @@ -802,7 +811,7 @@ TESTFN_ENCODING = sys.getfilesystemencoding() # encoded by the filesystem encoding (in strict mode). It can be None if we # cannot generate such filename. TESTFN_UNENCODABLE = None -if os.name in ('nt', 'ce'): +if os.name == 'nt': # skip win32s (0) or Windows 9x/ME (1) if sys.getwindowsversion().platform >= 2: # Different kinds of characters from various languages to minimize the @@ -901,7 +910,7 @@ def temp_dir(path=None, quiet=False): yield path finally: if dir_created: - shutil.rmtree(path) + rmtree(path) @contextlib.contextmanager def change_cwd(path, quiet=False): @@ -2078,6 +2087,11 @@ def args_from_interpreter_flags(): settings in sys.flags and sys.warnoptions.""" return subprocess._args_from_interpreter_flags() +def optim_args_from_interpreter_flags(): + """Return a list of command-line arguments reproducing the current + optimization settings in sys.flags.""" + return subprocess._optim_args_from_interpreter_flags() + #============================================================ # Support for assertions about logging. #============================================================ @@ -2229,6 +2243,65 @@ def detect_api_mismatch(ref_api, other_api, *, ignore=()): return missing_items +def check__all__(test_case, module, name_of_module=None, extra=(), + blacklist=()): + """Assert that the __all__ variable of 'module' contains all public names. + + The module's public names (its API) are detected automatically based on + whether they match the public name convention and were defined in + 'module'. + + The 'name_of_module' argument can specify (as a string or tuple thereof) + what module(s) an API could be defined in in order to be detected as a + public API. One case for this is when 'module' imports part of its public + API from other modules, possibly a C backend (like 'csv' and its '_csv'). + + The 'extra' argument can be a set of names that wouldn't otherwise be + automatically detected as "public", like objects without a proper + '__module__' attriubute. If provided, it will be added to the + automatically detected ones. + + The 'blacklist' argument can be a set of names that must not be treated + as part of the public API even though their names indicate otherwise. + + Usage: + import bar + import foo + import unittest + from test import support + + class MiscTestCase(unittest.TestCase): + def test__all__(self): + support.check__all__(self, foo) + + class OtherTestCase(unittest.TestCase): + def test__all__(self): + extra = {'BAR_CONST', 'FOO_CONST'} + blacklist = {'baz'} # Undocumented name. + # bar imports part of its API from _bar. + support.check__all__(self, bar, ('bar', '_bar'), + extra=extra, blacklist=blacklist) + + """ + + if name_of_module is None: + name_of_module = (module.__name__, ) + elif isinstance(name_of_module, str): + name_of_module = (name_of_module, ) + + expected = set(extra) + + for name in dir(module): + if name.startswith('_') or name in blacklist: + continue + obj = getattr(module, name) + if (getattr(obj, '__module__', None) in name_of_module or + (not hasattr(obj, '__module__') and + not isinstance(obj, types.ModuleType))): + expected.add(name) + test_case.assertCountEqual(module.__all__, expected) + + class SuppressCrashReport: """Try to prevent a crash report from popping up. diff --git a/Lib/test/test__locale.py b/Lib/test/test__locale.py index 58f2f04..ab4e247 100644 --- a/Lib/test/test__locale.py +++ b/Lib/test/test__locale.py @@ -4,7 +4,6 @@ try: except ImportError: nl_langinfo = None -import codecs import locale import sys import unittest diff --git a/Lib/test/test__osx_support.py b/Lib/test/test__osx_support.py index ac6325a..bcba8ca 100644 --- a/Lib/test/test__osx_support.py +++ b/Lib/test/test__osx_support.py @@ -4,7 +4,6 @@ Test suite for _osx_support: shared OS X support functions. import os import platform -import shutil import stat import sys import unittest diff --git a/Lib/test/test_aifc.py b/Lib/test/test_aifc.py index ab51437..1bd1f89 100644 --- a/Lib/test/test_aifc.py +++ b/Lib/test/test_aifc.py @@ -2,7 +2,6 @@ from test.support import findfile, TESTFN, unlink import unittest from test import audiotests from audioop import byteswap -import os import io import sys import struct diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index f9ee398..52c6247 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -4512,6 +4512,21 @@ class TestStrings(TestCase): string = "Namespace(bar='spam', foo=42)" self.assertStringEqual(ns, string) + def test_namespace_starkwargs_notidentifier(self): + ns = argparse.Namespace(**{'"': 'quote'}) + string = """Namespace(**{'"': 'quote'})""" + self.assertStringEqual(ns, string) + + def test_namespace_kwargs_and_starkwargs_notidentifier(self): + ns = argparse.Namespace(a=1, **{'"': 'quote'}) + string = """Namespace(a=1, **{'"': 'quote'})""" + self.assertStringEqual(ns, string) + + def test_namespace_starkwargs_identifier(self): + ns = argparse.Namespace(**{'valid': True}) + string = "Namespace(valid=True)" + self.assertStringEqual(ns, string) + def test_parser(self): parser = argparse.ArgumentParser(prog='PROG') string = ( diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index 482526e..8fd54cc 100644 --- a/Lib/test/test_array.py +++ b/Lib/test/test_array.py @@ -7,8 +7,6 @@ from test import support import weakref import pickle import operator -import io -import math import struct import sys import warnings @@ -318,8 +316,19 @@ class BaseTest: d = pickle.dumps((itorig, orig), proto) it, a = pickle.loads(d) a.fromlist(data2) - self.assertEqual(type(it), type(itorig)) - self.assertEqual(list(it), data2) + self.assertEqual(list(it), []) + + def test_exhausted_iterator(self): + a = array.array(self.typecode, self.example) + self.assertEqual(list(a), list(self.example)) + exhit = iter(a) + empit = iter(a) + for x in exhit: # exhaust the iterator + next(empit) # not exhausted + a.append(self.outside) + self.assertEqual(list(exhit), []) + self.assertEqual(list(empit), [self.outside]) + self.assertEqual(list(a), list(self.example) + [self.outside]) def test_insert(self): a = array.array(self.typecode, self.example) @@ -1070,6 +1079,12 @@ class BaseTest: a = array.array('B', b"") self.assertRaises(BufferError, getbuffer_with_null_view, a) + def test_free_after_iterating(self): + support.check_free_after_iterating(self, iter, array.array, + (self.typecode,)) + support.check_free_after_iterating(self, reversed, array.array, + (self.typecode,)) + class StringTest(BaseTest): def test_setitem(self): diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index d3e6d35..e032f6d 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -1,7 +1,8 @@ +import ast +import dis import os import sys import unittest -import ast import weakref from test import support @@ -238,7 +239,7 @@ class AST_Tests(unittest.TestCase): ast_tree = compile(i, "?", kind, ast.PyCF_ONLY_AST) self.assertEqual(to_tuple(ast_tree), o) self._assertTrueorder(ast_tree, (0, 0)) - with self.subTest(action="compiling", input=i): + with self.subTest(action="compiling", input=i, kind=kind): compile(ast_tree, "?", kind) def test_slice(self): @@ -547,6 +548,17 @@ class ASTHelpers_Test(unittest.TestCase): compile(mod, 'test', 'exec') self.assertIn("invalid integer value: None", str(cm.exception)) + def test_level_as_none(self): + body = [ast.ImportFrom(module='time', + names=[ast.alias(name='sleep')], + level=None, + lineno=0, col_offset=0)] + mod = ast.Module(body) + code = compile(mod, 'test', 'exec') + ns = {} + exec(code, ns) + self.assertIn('sleep', ns) + class ASTValidatorTests(unittest.TestCase): @@ -742,7 +754,7 @@ class ASTValidatorTests(unittest.TestCase): def test_importfrom(self): imp = ast.ImportFrom(None, [ast.alias("x", None)], -42) - self.stmt(imp, "level less than -1") + self.stmt(imp, "Negative ImportFrom level") self.stmt(ast.ImportFrom(None, [], 0), "empty names on ImportFrom") def test_global(self): @@ -933,6 +945,125 @@ class ASTValidatorTests(unittest.TestCase): compile(mod, fn, "exec") +class ConstantTests(unittest.TestCase): + """Tests on the ast.Constant node type.""" + + def compile_constant(self, value): + tree = ast.parse("x = 123") + + node = tree.body[0].value + new_node = ast.Constant(value=value) + ast.copy_location(new_node, node) + tree.body[0].value = new_node + + code = compile(tree, "<string>", "exec") + + ns = {} + exec(code, ns) + return ns['x'] + + def test_validation(self): + with self.assertRaises(TypeError) as cm: + self.compile_constant([1, 2, 3]) + self.assertEqual(str(cm.exception), + "got an invalid type in Constant: list") + + def test_singletons(self): + for const in (None, False, True, Ellipsis, b'', frozenset()): + with self.subTest(const=const): + value = self.compile_constant(const) + self.assertIs(value, const) + + def test_values(self): + nested_tuple = (1,) + nested_frozenset = frozenset({1}) + for level in range(3): + nested_tuple = (nested_tuple, 2) + nested_frozenset = frozenset({nested_frozenset, 2}) + values = (123, 123.0, 123j, + "unicode", b'bytes', + tuple("tuple"), frozenset("frozenset"), + nested_tuple, nested_frozenset) + for value in values: + with self.subTest(value=value): + result = self.compile_constant(value) + self.assertEqual(result, value) + + def test_assign_to_constant(self): + tree = ast.parse("x = 1") + + target = tree.body[0].targets[0] + new_target = ast.Constant(value=1) + ast.copy_location(new_target, target) + tree.body[0].targets[0] = new_target + + with self.assertRaises(ValueError) as cm: + compile(tree, "string", "exec") + self.assertEqual(str(cm.exception), + "expression which can't be assigned " + "to in Store context") + + def test_get_docstring(self): + tree = ast.parse("'docstring'\nx = 1") + self.assertEqual(ast.get_docstring(tree), 'docstring') + + tree.body[0].value = ast.Constant(value='constant docstring') + self.assertEqual(ast.get_docstring(tree), 'constant docstring') + + def get_load_const(self, tree): + # Compile to bytecode, disassemble and get parameter of LOAD_CONST + # instructions + co = compile(tree, '<string>', 'exec') + consts = [] + for instr in dis.get_instructions(co): + if instr.opname == 'LOAD_CONST': + consts.append(instr.argval) + return consts + + @support.cpython_only + def test_load_const(self): + consts = [None, + True, False, + 124, + 2.0, + 3j, + "unicode", + b'bytes', + (1, 2, 3)] + + code = '\n'.join(['x={!r}'.format(const) for const in consts]) + code += '\nx = ...' + consts.extend((Ellipsis, None)) + + tree = ast.parse(code) + self.assertEqual(self.get_load_const(tree), + consts) + + # Replace expression nodes with constants + for assign, const in zip(tree.body, consts): + assert isinstance(assign, ast.Assign), ast.dump(assign) + new_node = ast.Constant(value=const) + ast.copy_location(new_node, assign.value) + assign.value = new_node + + self.assertEqual(self.get_load_const(tree), + consts) + + def test_literal_eval(self): + tree = ast.parse("1 + 2") + binop = tree.body[0].value + + new_left = ast.Constant(value=10) + ast.copy_location(new_left, binop.left) + binop.left = new_left + + new_right = ast.Constant(value=20) + ast.copy_location(new_right, binop.right) + binop.right = new_right + + self.assertEqual(ast.literal_eval(binop), 30) + + def main(): if __name__ != '__main__': return @@ -940,8 +1071,9 @@ def main(): for statements, kind in ((exec_tests, "exec"), (single_tests, "single"), (eval_tests, "eval")): print(kind+"_results = [") - for s in statements: - print(repr(to_tuple(compile(s, "?", kind, 0x400)))+",") + for statement in statements: + tree = ast.parse(statement, "?", kind) + print("%r," % (to_tuple(tree),)) print("]") print("main()") raise SystemExit diff --git a/Lib/test/test_asynchat.py b/Lib/test/test_asynchat.py index 3a33fc8..0eba76d 100644 --- a/Lib/test/test_asynchat.py +++ b/Lib/test/test_asynchat.py @@ -12,7 +12,6 @@ import socket import sys import time import unittest -import warnings import unittest.mock try: import threading @@ -297,37 +296,6 @@ class TestHelperFunctions(unittest.TestCase): self.assertEqual(asynchat.find_prefix_at_end("qwertydkjf", "\r\n"), 0) -class TestFifo(unittest.TestCase): - def test_basic(self): - 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) - self.assertEqual(f.first(), 7) - self.assertEqual(f.pop(), (1, 7)) - self.assertEqual(len(f), 1) - self.assertEqual(f.first(), b'a') - self.assertEqual(f.is_empty(), False) - self.assertEqual(f.pop(), (1, b'a')) - self.assertEqual(len(f), 0) - self.assertEqual(f.is_empty(), True) - self.assertEqual(f.pop(), (0, None)) - - def test_given_list(self): - 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)) - self.assertEqual(f.pop(), (1, 3)) - self.assertEqual(f.pop(), (0, None)) - - class TestNotConnected(unittest.TestCase): def test_disallow_negative_terminator(self): # Issue #11259 diff --git a/Lib/test/test_asyncio/test_locks.py b/Lib/test/test_asyncio/test_locks.py index d3bdc51..e557212 100644 --- a/Lib/test/test_asyncio/test_locks.py +++ b/Lib/test/test_asyncio/test_locks.py @@ -130,8 +130,8 @@ class LockTests(test_utils.TestCase): def test_cancel_race(self): # Several tasks: # - A acquires the lock - # - B is blocked in aqcuire() - # - C is blocked in aqcuire() + # - B is blocked in acquire() + # - C is blocked in acquire() # # Now, concurrently: # - B is cancelled diff --git a/Lib/test/test_augassign.py b/Lib/test/test_augassign.py index 5093e9d..5930d9e 100644 --- a/Lib/test/test_augassign.py +++ b/Lib/test/test_augassign.py @@ -83,6 +83,10 @@ class AugAssignTest(unittest.TestCase): def __iadd__(self, val): return aug_test3(self.val + val) + class aug_test4(aug_test3): + """Blocks inheritance, and fallback to __add__""" + __iadd__ = None + x = aug_test(1) y = x x += 10 @@ -106,6 +110,10 @@ class AugAssignTest(unittest.TestCase): self.assertTrue(y is not x) self.assertEqual(x.val, 13) + x = aug_test4(4) + with self.assertRaises(TypeError): + x += 10 + def testCustomMethods2(test_self): output = [] diff --git a/Lib/test/test_bigmem.py b/Lib/test/test_bigmem.py index 40ac9bf..6133bbc 100644 --- a/Lib/test/test_bigmem.py +++ b/Lib/test/test_bigmem.py @@ -14,7 +14,6 @@ from test.support import bigmemtest, _1G, _2G, _4G import unittest import operator import sys -import functools # These tests all use one of the bigmemtest decorators to indicate how much # memory they use and how much memory they need to be even meaningful. The diff --git a/Lib/test/test_binascii.py b/Lib/test/test_binascii.py index 8367afe..fbc933e4 100644 --- a/Lib/test/test_binascii.py +++ b/Lib/test/test_binascii.py @@ -159,11 +159,25 @@ class BinASCIITest(unittest.TestCase): # Then calculate the hexbin4 binary-to-ASCII translation rle = binascii.rlecode_hqx(self.data) a = binascii.b2a_hqx(self.type2test(rle)) + b, _ = binascii.a2b_hqx(self.type2test(a)) res = binascii.rledecode_hqx(b) - self.assertEqual(res, self.rawdata) + def test_rle(self): + # test repetition with a repetition longer than the limit of 255 + data = (b'a' * 100 + b'b' + b'c' * 300) + + encoded = binascii.rlecode_hqx(data) + self.assertEqual(encoded, + (b'a\x90d' # 'a' * 100 + b'b' # 'b' + b'c\x90\xff' # 'c' * 255 + b'c\x90-')) # 'c' * 45 + + decoded = binascii.rledecode_hqx(encoded) + self.assertEqual(decoded, data) + def test_hex(self): # test hexlification s = b'{s\005\000\000\000worldi\002\000\000\000s\005\000\000\000helloi\001\000\000\0000' @@ -262,6 +276,16 @@ class BinASCIITest(unittest.TestCase): # non-ASCII string self.assertRaises(ValueError, a2b, "\x80") + def test_b2a_base64_newline(self): + # Issue #25357: test newline parameter + b = self.type2test(b'hello') + self.assertEqual(binascii.b2a_base64(b), + b'aGVsbG8=\n') + self.assertEqual(binascii.b2a_base64(b, newline=True), + b'aGVsbG8=\n') + self.assertEqual(binascii.b2a_base64(b, newline=False), + b'aGVsbG8=') + class ArrayBinASCIITest(BinASCIITest): def type2test(self, s): diff --git a/Lib/test/test_binhex.py b/Lib/test/test_binhex.py index 9d4c85a..21f4463 100644 --- a/Lib/test/test_binhex.py +++ b/Lib/test/test_binhex.py @@ -4,7 +4,6 @@ Based on an original test by Roger E. Masse. """ import binhex -import os import unittest from test import support diff --git a/Lib/test/test_binop.py b/Lib/test/test_binop.py index e9dbddc..3ed018e 100644 --- a/Lib/test/test_binop.py +++ b/Lib/test/test_binop.py @@ -2,7 +2,7 @@ import unittest from test import support -from operator import eq, ne, lt, gt, le, ge +from operator import eq, le, ne from abc import ABCMeta def gcd(a, b): @@ -388,6 +388,54 @@ class OperationOrderTests(unittest.TestCase): self.assertEqual(op_sequence(eq, B, V), ['B.__eq__', 'V.__eq__']) self.assertEqual(op_sequence(le, B, V), ['B.__le__', 'V.__ge__']) +class SupEq(object): + """Class that can test equality""" + def __eq__(self, other): + return True + +class S(SupEq): + """Subclass of SupEq that should fail""" + __eq__ = None + +class F(object): + """Independent class that should fall back""" + +class X(object): + """Independent class that should fail""" + __eq__ = None + +class SN(SupEq): + """Subclass of SupEq that can test equality, but not non-equality""" + __ne__ = None + +class XN: + """Independent class that can test equality, but not non-equality""" + def __eq__(self, other): + return True + __ne__ = None + +class FallbackBlockingTests(unittest.TestCase): + """Unit tests for None method blocking""" + + def test_fallback_rmethod_blocking(self): + e, f, s, x = SupEq(), F(), S(), X() + self.assertEqual(e, e) + self.assertEqual(e, f) + self.assertEqual(f, e) + # left operand is checked first + self.assertEqual(e, x) + self.assertRaises(TypeError, eq, x, e) + # S is a subclass, so it's always checked first + self.assertRaises(TypeError, eq, e, s) + self.assertRaises(TypeError, eq, s, e) + + def test_fallback_ne_blocking(self): + e, sn, xn = SupEq(), SN(), XN() + self.assertFalse(e != e) + self.assertRaises(TypeError, ne, e, sn) + self.assertRaises(TypeError, ne, sn, e) + self.assertFalse(e != xn) + self.assertRaises(TypeError, ne, xn, e) if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_bool.py b/Lib/test/test_bool.py index d30a3b9..9f8f0e1 100644 --- a/Lib/test/test_bool.py +++ b/Lib/test/test_bool.py @@ -96,6 +96,13 @@ class BoolTest(unittest.TestCase): self.assertEqual(False/1, 0) self.assertIsNot(False/1, False) + self.assertEqual(True%1, 0) + self.assertIsNot(True%1, False) + self.assertEqual(True%2, 1) + self.assertIsNot(True%2, True) + self.assertEqual(False%1, 0) + self.assertIsNot(False%1, False) + for b in False, True: for i in 0, 1, 2: self.assertEqual(b**i, int(b)**i) @@ -333,6 +340,17 @@ class BoolTest(unittest.TestCase): except (Exception) as e_len: self.assertEqual(str(e_bool), str(e_len)) + def test_blocked(self): + class A: + __bool__ = None + self.assertRaises(TypeError, bool, A()) + + class B: + def __len__(self): + return 10 + __bool__ = None + self.assertRaises(TypeError, bool, B()) + def test_real_and_imag(self): self.assertEqual(True.real, 1) self.assertEqual(True.imag, 0) diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py index 2eef9fc..b83f2f1 100644 --- a/Lib/test/test_buffer.py +++ b/Lib/test/test_buffer.py @@ -16,7 +16,6 @@ import unittest from test import support from itertools import permutations, product from random import randrange, sample, choice -from sysconfig import get_config_var import warnings import sys, array, io from decimal import Decimal diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 39c80b0..486f445 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -16,8 +16,10 @@ import traceback import types import unittest import warnings +from collections import OrderedDict from operator import neg -from test.support import TESTFN, unlink, run_unittest, check_warnings +from test.support import (TESTFN, unlink, run_unittest, check_warnings, + cpython_only) from test.support.script_helper import assert_python_ok try: import pty, signal @@ -1710,21 +1712,11 @@ class TestType(unittest.TestCase): self.assertEqual(x.spam(), 'spam42') self.assertEqual(x.to_bytes(2, 'little'), b'\x2a\x00') - def test_type_new_keywords(self): - class B: - def ham(self): - return 'ham%d' % self - C = type.__new__(type, - name='C', - bases=(B, int), - dict={'spam': lambda self: 'spam%s' % self}) - self.assertEqual(C.__name__, 'C') - self.assertEqual(C.__qualname__, 'C') - self.assertEqual(C.__module__, __name__) - self.assertEqual(C.__bases__, (B, int)) - self.assertIs(C.__base__, int) - self.assertIn('spam', C.__dict__) - self.assertNotIn('ham', C.__dict__) + def test_type_nokwargs(self): + with self.assertRaises(TypeError): + type('a', (), {}, x=5) + with self.assertRaises(TypeError): + type('a', (), dict={}) def test_type_name(self): for name in 'A', '\xc4', '\U0001f40d', 'B.A', '42', '': @@ -1788,6 +1780,194 @@ class TestType(unittest.TestCase): A.__doc__ = doc self.assertEqual(A.__doc__, doc) + def test_type_definition_order_nonempty(self): + class Spam: + b = 1 + c = 3 + a = 2 + d = 4 + eggs = 2 + e = 5 + b = 42 + + self.assertEqual(Spam.__definition_order__, + ('__module__', '__qualname__', + 'b', 'c', 'a', 'd', 'eggs', 'e')) + + def test_type_definition_order_empty(self): + class Empty: + pass + + self.assertEqual(Empty.__definition_order__, + ('__module__', '__qualname__')) + + def test_type_definition_order_on_instance(self): + class Spam: + a = 2 + b = 1 + c = 3 + with self.assertRaises(AttributeError): + Spam().__definition_order__ + + def test_type_definition_order_set_to_None(self): + class Spam: + a = 2 + b = 1 + c = 3 + Spam.__definition_order__ = None + self.assertEqual(Spam.__definition_order__, None) + + def test_type_definition_order_set_to_tuple(self): + class Spam: + a = 2 + b = 1 + c = 3 + Spam.__definition_order__ = ('x', 'y', 'z') + self.assertEqual(Spam.__definition_order__, ('x', 'y', 'z')) + + def test_type_definition_order_deleted(self): + class Spam: + a = 2 + b = 1 + c = 3 + del Spam.__definition_order__ + self.assertEqual(Spam.__definition_order__, None) + + def test_type_definition_order_set_to_bad_type(self): + class Spam: + a = 2 + b = 1 + c = 3 + Spam.__definition_order__ = 42 + self.assertEqual(Spam.__definition_order__, 42) + + def test_type_definition_order_builtins(self): + self.assertEqual(object.__definition_order__, None) + self.assertEqual(type.__definition_order__, None) + self.assertEqual(dict.__definition_order__, None) + self.assertEqual(type(None).__definition_order__, None) + + def test_type_definition_order_dunder_names_included(self): + class Dunder: + VAR = 3 + def __init__(self): + pass + + self.assertEqual(Dunder.__definition_order__, + ('__module__', '__qualname__', + 'VAR', '__init__')) + + def test_type_definition_order_only_dunder_names(self): + class DunderOnly: + __xyz__ = None + def __init__(self): + pass + + self.assertEqual(DunderOnly.__definition_order__, + ('__module__', '__qualname__', + '__xyz__', '__init__')) + + def test_type_definition_order_underscore_names(self): + class HalfDunder: + __whether_to_be = True + or_not_to_be__ = False + + self.assertEqual(HalfDunder.__definition_order__, + ('__module__', '__qualname__', + '_HalfDunder__whether_to_be', 'or_not_to_be__')) + + def test_type_definition_order_with_slots(self): + class Slots: + __slots__ = ('x', 'y') + a = 1 + b = 2 + + self.assertEqual(Slots.__definition_order__, + ('__module__', '__qualname__', + '__slots__', 'a', 'b')) + + def test_type_definition_order_overwritten_None(self): + class OverwrittenNone: + __definition_order__ = None + a = 1 + b = 2 + c = 3 + + self.assertEqual(OverwrittenNone.__definition_order__, None) + + def test_type_definition_order_overwritten_tuple(self): + class OverwrittenTuple: + __definition_order__ = ('x', 'y', 'z') + a = 1 + b = 2 + c = 3 + + self.assertEqual(OverwrittenTuple.__definition_order__, + ('x', 'y', 'z')) + + def test_type_definition_order_overwritten_bad_item(self): + with self.assertRaises(TypeError): + class PoorlyOverwritten: + __definition_order__ = ('a', 2, 'c') + a = 1 + b = 2 + c = 3 + + def test_type_definition_order_overwritten_bad_type(self): + with self.assertRaises(TypeError): + class PoorlyOverwritten: + __definition_order__ = ['a', 2, 'c'] + a = 1 + b = 2 + c = 3 + + def test_type_definition_order_metaclass(self): + class Meta(type): + SPAM = 42 + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.assertEqual(Meta.__definition_order__, + ('__module__', '__qualname__', + 'SPAM', '__init__')) + + def test_type_definition_order_OrderedDict(self): + class Meta(type): + def __prepare__(self, *args, **kwargs): + return OrderedDict() + + class WithODict(metaclass=Meta): + x='y' + + self.assertEqual(WithODict.__definition_order__, + ('__module__', '__qualname__', 'x')) + + class Meta(type): + def __prepare__(self, *args, **kwargs): + class ODictSub(OrderedDict): + pass + return ODictSub() + + class WithODictSub(metaclass=Meta): + x='y' + + self.assertEqual(WithODictSub.__definition_order__, + ('__module__', '__qualname__', 'x')) + + @cpython_only + def test_type_definition_order_cpython(self): + # some implementations will have an ordered-by-default dict. + + class Meta(type): + def __prepare__(self, *args, **kwargs): + return {} + + class NotOrdered(metaclass=Meta): + x='y' + + self.assertEqual(NotOrdered.__definition_order__, None) + def test_bad_args(self): with self.assertRaises(TypeError): type() diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index cc312b1..8bbd669 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -269,6 +269,7 @@ class BaseBytesTest: self.assertNotIn(200, b) self.assertRaises(ValueError, lambda: 300 in b) self.assertRaises(ValueError, lambda: -1 in b) + self.assertRaises(ValueError, lambda: sys.maxsize+1 in b) self.assertRaises(TypeError, lambda: None in b) self.assertRaises(TypeError, lambda: float(ord('a')) in b) self.assertRaises(TypeError, lambda: "a" in b) @@ -300,6 +301,20 @@ class BaseBytesTest: self.assertRaises(ValueError, self.type2test.fromhex, '\x00') self.assertRaises(ValueError, self.type2test.fromhex, '12 \x00 34') + for data, pos in ( + # invalid first hexadecimal character + ('12 x4 56', 3), + # invalid second hexadecimal character + ('12 3x 56', 4), + # two invalid hexadecimal characters + ('12 xy 56', 3), + # test non-ASCII string + ('12 3\xff 56', 4), + ): + with self.assertRaises(ValueError) as cm: + self.type2test.fromhex(data) + self.assertIn('at position %s' % pos, str(cm.exception)) + def test_hex(self): self.assertRaises(TypeError, self.type2test.hex) self.assertRaises(TypeError, self.type2test.hex, 1) @@ -674,6 +689,37 @@ class BaseBytesTest: test.support.check_free_after_iterating(self, iter, self.type2test) test.support.check_free_after_iterating(self, reversed, self.type2test) + def test_translate(self): + b = self.type2test(b'hello') + rosetta = bytearray(range(256)) + rosetta[ord('o')] = ord('e') + + self.assertRaises(TypeError, b.translate) + self.assertRaises(TypeError, b.translate, None, None) + self.assertRaises(ValueError, b.translate, bytes(range(255))) + + c = b.translate(rosetta, b'hello') + self.assertEqual(b, b'hello') + self.assertIsInstance(c, self.type2test) + + c = b.translate(rosetta) + d = b.translate(rosetta, b'') + self.assertEqual(c, d) + self.assertEqual(c, b'helle') + + c = b.translate(rosetta, b'l') + self.assertEqual(c, b'hee') + c = b.translate(None, b'e') + self.assertEqual(c, b'hllo') + + # test delete as a keyword argument + c = b.translate(rosetta, delete=b'') + self.assertEqual(c, b'helle') + c = b.translate(rosetta, delete=b'l') + self.assertEqual(c, b'hee') + c = b.translate(None, delete=b'e') + self.assertEqual(c, b'hllo') + class BytesTest(BaseBytesTest, unittest.TestCase): type2test = bytes @@ -722,31 +768,142 @@ class BytesTest(BaseBytesTest, unittest.TestCase): # Test PyBytes_FromFormat() def test_from_format(self): - test.support.import_module('ctypes') - from ctypes import pythonapi, py_object, c_int, c_char_p + ctypes = test.support.import_module('ctypes') + _testcapi = test.support.import_module('_testcapi') + from ctypes import pythonapi, py_object + from ctypes import ( + c_int, c_uint, + c_long, c_ulong, + c_size_t, c_ssize_t, + c_char_p) + PyBytes_FromFormat = pythonapi.PyBytes_FromFormat PyBytes_FromFormat.restype = py_object + # basic tests self.assertEqual(PyBytes_FromFormat(b'format'), b'format') - + self.assertEqual(PyBytes_FromFormat(b'Hello %s !', b'world'), + b'Hello world !') + + # test formatters + self.assertEqual(PyBytes_FromFormat(b'c=%c', c_int(0)), + b'c=\0') + self.assertEqual(PyBytes_FromFormat(b'c=%c', c_int(ord('@'))), + b'c=@') + self.assertEqual(PyBytes_FromFormat(b'c=%c', c_int(255)), + b'c=\xff') + self.assertEqual(PyBytes_FromFormat(b'd=%d ld=%ld zd=%zd', + c_int(1), c_long(2), + c_size_t(3)), + b'd=1 ld=2 zd=3') + self.assertEqual(PyBytes_FromFormat(b'd=%d ld=%ld zd=%zd', + c_int(-1), c_long(-2), + c_size_t(-3)), + b'd=-1 ld=-2 zd=-3') + self.assertEqual(PyBytes_FromFormat(b'u=%u lu=%lu zu=%zu', + c_uint(123), c_ulong(456), + c_size_t(789)), + b'u=123 lu=456 zu=789') + self.assertEqual(PyBytes_FromFormat(b'i=%i', c_int(123)), + b'i=123') + self.assertEqual(PyBytes_FromFormat(b'i=%i', c_int(-123)), + b'i=-123') + self.assertEqual(PyBytes_FromFormat(b'x=%x', c_int(0xabc)), + b'x=abc') + + sizeof_ptr = ctypes.sizeof(c_char_p) + + if os.name == 'nt': + # Windows (MSCRT) + ptr_format = '0x%0{}X'.format(2 * sizeof_ptr) + def ptr_formatter(ptr): + return (ptr_format % ptr) + else: + # UNIX (glibc) + def ptr_formatter(ptr): + return '%#x' % ptr + + ptr = 0xabcdef + self.assertEqual(PyBytes_FromFormat(b'ptr=%p', c_char_p(ptr)), + ('ptr=' + ptr_formatter(ptr)).encode('ascii')) + self.assertEqual(PyBytes_FromFormat(b's=%s', c_char_p(b'cstr')), + b's=cstr') + + # test minimum and maximum integer values + size_max = c_size_t(-1).value + for formatstr, ctypes_type, value, py_formatter in ( + (b'%d', c_int, _testcapi.INT_MIN, str), + (b'%d', c_int, _testcapi.INT_MAX, str), + (b'%ld', c_long, _testcapi.LONG_MIN, str), + (b'%ld', c_long, _testcapi.LONG_MAX, str), + (b'%lu', c_ulong, _testcapi.ULONG_MAX, str), + (b'%zd', c_ssize_t, _testcapi.PY_SSIZE_T_MIN, str), + (b'%zd', c_ssize_t, _testcapi.PY_SSIZE_T_MAX, str), + (b'%zu', c_size_t, size_max, str), + (b'%p', c_char_p, size_max, ptr_formatter), + ): + self.assertEqual(PyBytes_FromFormat(formatstr, ctypes_type(value)), + py_formatter(value).encode('ascii')), + + # width and precision (width is currently ignored) + self.assertEqual(PyBytes_FromFormat(b'%5s', b'a'), + b'a') + self.assertEqual(PyBytes_FromFormat(b'%.3s', b'abcdef'), + b'abc') + + # '%%' formatter + self.assertEqual(PyBytes_FromFormat(b'%%'), + b'%') + self.assertEqual(PyBytes_FromFormat(b'[%%]'), + b'[%]') + self.assertEqual(PyBytes_FromFormat(b'%%%c', c_int(ord('_'))), + b'%_') + self.assertEqual(PyBytes_FromFormat(b'%%s'), + b'%s') + + # Invalid formats and partial formatting self.assertEqual(PyBytes_FromFormat(b'%'), b'%') - self.assertEqual(PyBytes_FromFormat(b'%%'), b'%') - self.assertEqual(PyBytes_FromFormat(b'%%s'), b'%s') - self.assertEqual(PyBytes_FromFormat(b'[%%]'), b'[%]') - self.assertEqual(PyBytes_FromFormat(b'%%%c', c_int(ord('_'))), b'%_') - - self.assertEqual(PyBytes_FromFormat(b'c:%c', c_int(255)), - b'c:\xff') - self.assertEqual(PyBytes_FromFormat(b's:%s', c_char_p(b'cstr')), - b's:cstr') + self.assertEqual(PyBytes_FromFormat(b'x=%i y=%', c_int(2), c_int(3)), + b'x=2 y=%') - # Issue #19969 + # Issue #19969: %c must raise OverflowError for values + # not in the range [0; 255] self.assertRaises(OverflowError, PyBytes_FromFormat, b'%c', c_int(-1)) self.assertRaises(OverflowError, PyBytes_FromFormat, b'%c', c_int(256)) + def test_bytes_blocking(self): + class IterationBlocked(list): + __bytes__ = None + i = [0, 1, 2, 3] + self.assertEqual(bytes(i), b'\x00\x01\x02\x03') + self.assertRaises(TypeError, bytes, IterationBlocked(i)) + + # At least in CPython, because bytes.__new__ and the C API + # PyBytes_FromObject have different fallback rules, integer + # fallback is handled specially, so test separately. + class IntBlocked(int): + __bytes__ = None + self.assertEqual(bytes(3), b'\0\0\0') + self.assertRaises(TypeError, bytes, IntBlocked(3)) + + # While there is no separately-defined rule for handling bytes + # subclasses differently from other buffer-interface classes, + # an implementation may well special-case them (as CPython 2.x + # str did), so test them separately. + class BytesSubclassBlocked(bytes): + __bytes__ = None + self.assertEqual(bytes(b'ab'), b'ab') + self.assertRaises(TypeError, bytes, BytesSubclassBlocked(b'ab')) + + class BufferBlocked(bytearray): + __bytes__ = None + ba, bb = bytearray(b'ab'), BufferBlocked(b'ab') + self.assertEqual(bytes(ba), b'ab') + self.assertRaises(TypeError, bytes, bb) + class ByteArrayTest(BaseBytesTest, unittest.TestCase): type2test = bytearray @@ -1323,24 +1480,6 @@ class AssortedBytesTest(unittest.TestCase): self.assertRaises(SyntaxError, eval, 'b"%s"' % chr(c)) - def test_translate(self): - b = b'hello' - ba = bytearray(b) - rosetta = bytearray(range(0, 256)) - rosetta[ord('o')] = ord('e') - c = b.translate(rosetta, b'l') - self.assertEqual(b, b'hello') - self.assertEqual(c, b'hee') - c = ba.translate(rosetta, b'l') - self.assertEqual(ba, b'hello') - self.assertEqual(c, b'hee') - c = b.translate(None, b'e') - self.assertEqual(c, b'hllo') - c = ba.translate(None, b'e') - self.assertEqual(c, b'hllo') - self.assertRaises(TypeError, b.translate, None, None) - self.assertRaises(TypeError, ba.translate, None, None) - def test_split_bytearray(self): self.assertEqual(b'a b'.split(memoryview(b' ')), [b'a', b'b']) @@ -1504,7 +1643,32 @@ class SubclassTest: self.assertEqual(type(a), type(b)) self.assertEqual(type(a.y), type(b.y)) - test_fromhex = BaseBytesTest.test_fromhex + def test_fromhex(self): + b = self.type2test.fromhex('1a2B30') + self.assertEqual(b, b'\x1a\x2b\x30') + self.assertIs(type(b), self.type2test) + + class B1(self.basetype): + def __new__(cls, value): + me = self.basetype.__new__(cls, value) + me.foo = 'bar' + return me + + b = B1.fromhex('1a2B30') + self.assertEqual(b, b'\x1a\x2b\x30') + self.assertIs(type(b), B1) + self.assertEqual(b.foo, 'bar') + + class B2(self.basetype): + def __init__(me, *args, **kwargs): + if self.basetype is not bytes: + self.basetype.__init__(me, *args, **kwargs) + me.foo = 'bar' + + b = B2.fromhex('1a2B30') + self.assertEqual(b, b'\x1a\x2b\x30') + self.assertIs(type(b), B2) + self.assertEqual(b.foo, 'bar') class ByteArraySubclass(bytearray): diff --git a/Lib/test/test_calendar.py b/Lib/test/test_calendar.py index 80ed632..6dad058 100644 --- a/Lib/test/test_calendar.py +++ b/Lib/test/test_calendar.py @@ -702,19 +702,19 @@ class CommandLineTestCase(unittest.TestCase): def assertFailure(self, *args): rc, stdout, stderr = assert_python_failure('-m', 'calendar', *args) - self.assertIn(b'Usage:', stderr) + self.assertIn(b'usage:', stderr) self.assertEqual(rc, 2) def test_help(self): stdout = self.run_ok('-h') - self.assertIn(b'Usage:', stdout) + self.assertIn(b'usage:', stdout) self.assertIn(b'calendar.py', stdout) self.assertIn(b'--help', stdout) def test_illegal_arguments(self): self.assertFailure('-z') - #self.assertFailure('spam') - #self.assertFailure('2004', 'spam') + self.assertFailure('spam') + self.assertFailure('2004', 'spam') self.assertFailure('-t', 'html', '2004', '1') def test_output_current_year(self): @@ -815,5 +815,14 @@ class CommandLineTestCase(unittest.TestCase): b'href="custom.css" />', stdout) +class MiscTestCase(unittest.TestCase): + def test__all__(self): + blacklist = {'mdays', 'January', 'February', 'EPOCH', + 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', + 'SATURDAY', 'SUNDAY', 'different_locale', 'c', + 'prweek', 'week', 'format', 'formatstring', 'main'} + support.check__all__(self, calendar, blacklist=blacklist) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index 1eadd22..6852381 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -4,8 +4,10 @@ import os import pickle import random +import re import subprocess import sys +import sysconfig import textwrap import time import unittest @@ -442,6 +444,7 @@ class EmbeddingTests(unittest.TestCase): self.maxDiff = None self.assertEqual(out.strip(), expected_output) + class SkipitemTest(unittest.TestCase): def test_skipitem(self): @@ -491,10 +494,10 @@ class SkipitemTest(unittest.TestCase): _testcapi.parse_tuple_and_keywords(tuple_1, dict_b, format.encode("ascii"), keywords) when_not_skipped = False - except TypeError as e: + except SystemError as e: s = "argument 1 (impossible<bad format char>)" when_not_skipped = (str(e) == s) - except RuntimeError as e: + except TypeError: when_not_skipped = False # test the format unit when skipped @@ -503,7 +506,7 @@ class SkipitemTest(unittest.TestCase): _testcapi.parse_tuple_and_keywords(empty_tuple, dict_b, optional_format.encode("ascii"), keywords) when_skipped = False - except RuntimeError as e: + except SystemError as e: s = "impossible<bad format char>: '{}'".format(format) when_skipped = (str(e) == s) @@ -524,6 +527,32 @@ class SkipitemTest(unittest.TestCase): self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords, (), {}, b'', [42]) + def test_positional_only(self): + parse = _testcapi.parse_tuple_and_keywords + + parse((1, 2, 3), {}, b'OOO', ['', '', 'a']) + parse((1, 2), {'a': 3}, b'OOO', ['', '', 'a']) + with self.assertRaisesRegex(TypeError, + 'Function takes at least 2 positional arguments \(1 given\)'): + parse((1,), {'a': 3}, b'OOO', ['', '', 'a']) + parse((1,), {}, b'O|OO', ['', '', 'a']) + with self.assertRaisesRegex(TypeError, + 'Function takes at least 1 positional arguments \(0 given\)'): + parse((), {}, b'O|OO', ['', '', 'a']) + parse((1, 2), {'a': 3}, b'OO$O', ['', '', 'a']) + with self.assertRaisesRegex(TypeError, + 'Function takes exactly 2 positional arguments \(1 given\)'): + parse((1,), {'a': 3}, b'OO$O', ['', '', 'a']) + parse((1,), {}, b'O|O$O', ['', '', 'a']) + with self.assertRaisesRegex(TypeError, + 'Function takes at least 1 positional arguments \(0 given\)'): + parse((), {}, b'O|O$O', ['', '', 'a']) + with self.assertRaisesRegex(SystemError, 'Empty parameter name after \$'): + parse((1,), {}, b'O|$OO', ['', '', 'a']) + with self.assertRaisesRegex(SystemError, 'Empty keyword'): + parse((1,), {}, b'O|OO', ['', 'a', '']) + + @unittest.skipUnless(threading, 'Threading required for this test.') class TestThreadState(unittest.TestCase): @@ -548,6 +577,7 @@ class TestThreadState(unittest.TestCase): t.start() t.join() + class Test_testcapi(unittest.TestCase): def test__testcapi(self): for name in dir(_testcapi): @@ -556,5 +586,84 @@ class Test_testcapi(unittest.TestCase): test = getattr(_testcapi, name) test() + +class PyMemDebugTests(unittest.TestCase): + PYTHONMALLOC = 'debug' + # '0x04c06e0' or '04C06E0' + PTR_REGEX = r'(?:0x)?[0-9a-fA-F]+' + + def check(self, code): + with support.SuppressCrashReport(): + out = assert_python_failure('-c', code, + PYTHONMALLOC=self.PYTHONMALLOC) + stderr = out.err + return stderr.decode('ascii', 'replace') + + def test_buffer_overflow(self): + out = self.check('import _testcapi; _testcapi.pymem_buffer_overflow()') + regex = (r"Debug memory block at address p={ptr}: API 'm'\n" + r" 16 bytes originally requested\n" + r" The [0-9] pad bytes at p-[0-9] are FORBIDDENBYTE, as expected.\n" + r" The [0-9] pad bytes at tail={ptr} are not all FORBIDDENBYTE \(0x[0-9a-f]{{2}}\):\n" + r" at tail\+0: 0x78 \*\*\* OUCH\n" + r" at tail\+1: 0xfb\n" + r" at tail\+2: 0xfb\n" + r" .*\n" + r" The block was made by call #[0-9]+ to debug malloc/realloc.\n" + r" Data at p: cb cb cb .*\n" + r"\n" + r"Fatal Python error: bad trailing pad byte") + regex = regex.format(ptr=self.PTR_REGEX) + regex = re.compile(regex, flags=re.DOTALL) + self.assertRegex(out, regex) + + def test_api_misuse(self): + out = self.check('import _testcapi; _testcapi.pymem_api_misuse()') + regex = (r"Debug memory block at address p={ptr}: API 'm'\n" + r" 16 bytes originally requested\n" + r" The [0-9] pad bytes at p-[0-9] are FORBIDDENBYTE, as expected.\n" + r" The [0-9] pad bytes at tail={ptr} are FORBIDDENBYTE, as expected.\n" + r" The block was made by call #[0-9]+ to debug malloc/realloc.\n" + r" Data at p: cb cb cb .*\n" + r"\n" + r"Fatal Python error: bad ID: Allocated using API 'm', verified using API 'r'\n") + regex = regex.format(ptr=self.PTR_REGEX) + self.assertRegex(out, regex) + + def check_malloc_without_gil(self, code): + out = self.check(code) + expected = ('Fatal Python error: Python memory allocator called ' + 'without holding the GIL') + self.assertIn(expected, out) + + def test_pymem_malloc_without_gil(self): + # Debug hooks must raise an error if PyMem_Malloc() is called + # without holding the GIL + code = 'import _testcapi; _testcapi.pymem_malloc_without_gil()' + self.check_malloc_without_gil(code) + + def test_pyobject_malloc_without_gil(self): + # Debug hooks must raise an error if PyObject_Malloc() is called + # without holding the GIL + code = 'import _testcapi; _testcapi.pyobject_malloc_without_gil()' + self.check_malloc_without_gil(code) + + +class PyMemMallocDebugTests(PyMemDebugTests): + PYTHONMALLOC = 'malloc_debug' + + +@unittest.skipUnless(sysconfig.get_config_var('WITH_PYMALLOC') == 1, + 'need pymalloc') +class PyMemPymallocDebugTests(PyMemDebugTests): + PYTHONMALLOC = 'pymalloc_debug' + + +@unittest.skipUnless(Py_DEBUG, 'need Py_DEBUG') +class PyMemDefaultTests(PyMemDebugTests): + # test default allocator of Python compiled in debug mode + PYTHONMALLOC = '' + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_cgi.py b/Lib/test/test_cgi.py index ab9f6ab..1647849 100644 --- a/Lib/test/test_cgi.py +++ b/Lib/test/test_cgi.py @@ -7,6 +7,7 @@ import unittest import warnings from collections import namedtuple from io import StringIO, BytesIO +from test import support class HackedSysModule: # The regression test will have real values in sys.argv, which @@ -473,6 +474,11 @@ this is the content of the fake file cgi.parse_header('form-data; name="files"; filename="fo\\"o;bar"'), ("form-data", {"name": "files", "filename": 'fo"o;bar'})) + def test_all(self): + blacklist = {"logfile", "logfp", "initlog", "dolog", "nolog", + "closelog", "log", "maxlen", "valid_boundary"} + support.check__all__(self, cgi, blacklist=blacklist) + BOUNDARY = "---------------------------721837373350705526688164684" diff --git a/Lib/test/test_charmapcodec.py b/Lib/test/test_charmapcodec.py index 4064aef..0d4594d 100644 --- a/Lib/test/test_charmapcodec.py +++ b/Lib/test/test_charmapcodec.py @@ -9,7 +9,7 @@ Written by Marc-Andre Lemburg (mal@lemburg.com). """#" -import test.support, unittest +import unittest import codecs diff --git a/Lib/test/test_cmath.py b/Lib/test/test_cmath.py index 1f884e5..0451fb0 100644 --- a/Lib/test/test_cmath.py +++ b/Lib/test/test_cmath.py @@ -4,6 +4,8 @@ import test.test_math as test_math import unittest import cmath, math from cmath import phase, polar, rect, pi +import platform +import sys import sysconfig INF = float('inf') @@ -154,6 +156,23 @@ class CMathTests(unittest.TestCase): self.assertAlmostEqual(cmath.e, e_expected, places=9, msg="cmath.e is {}; should be {}".format(cmath.e, e_expected)) + def test_infinity_and_nan_constants(self): + self.assertEqual(cmath.inf.real, math.inf) + self.assertEqual(cmath.inf.imag, 0.0) + self.assertEqual(cmath.infj.real, 0.0) + self.assertEqual(cmath.infj.imag, math.inf) + + self.assertTrue(math.isnan(cmath.nan.real)) + self.assertEqual(cmath.nan.imag, 0.0) + self.assertEqual(cmath.nanj.real, 0.0) + self.assertTrue(math.isnan(cmath.nanj.imag)) + + # Check consistency with reprs. + self.assertEqual(repr(cmath.inf), "inf") + self.assertEqual(repr(cmath.infj), "infj") + self.assertEqual(repr(cmath.nan), "nan") + self.assertEqual(repr(cmath.nanj), "nanj") + def test_user_object(self): # Test automatic calling of __complex__ and __float__ by cmath # functions @@ -315,6 +334,18 @@ class CMathTests(unittest.TestCase): @requires_IEEE_754 def test_specific_values(self): + # Some tests need to be skipped on ancient OS X versions. + # See issue #27953. + SKIP_ON_TIGER = {'tan0064'} + + osx_version = None + if sys.platform == 'darwin': + version_txt = platform.mac_ver()[0] + try: + osx_version = tuple(map(int, version_txt.split('.'))) + except ValueError: + pass + def rect_complex(z): """Wrapped version of rect that accepts a complex number instead of two float arguments.""" @@ -328,6 +359,12 @@ class CMathTests(unittest.TestCase): for id, fn, ar, ai, er, ei, flags in parse_testfile(test_file): arg = complex(ar, ai) expected = complex(er, ei) + + # Skip certain tests on OS X 10.4. + if osx_version is not None and osx_version < (10, 5): + if id in SKIP_ON_TIGER: + continue + if fn == 'rect': function = rect_complex elif fn == 'polar': diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index 3d2f769..87571d3 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -348,8 +348,9 @@ class CmdLineTest(unittest.TestCase): test.support.SuppressCrashReport().__enter__() sys.stdout.write('x') os.close(sys.stdout.fileno())""" - rc, out, err = assert_python_ok('-c', code) + rc, out, err = assert_python_failure('-c', code) self.assertEqual(b'', out) + self.assertEqual(120, rc) self.assertRegex(err.decode('ascii', 'ignore'), 'Exception ignored in.*\nOSError: .*') diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py index befe0e4..6e0b669 100644 --- a/Lib/test/test_cmd_line_script.py +++ b/Lib/test/test_cmd_line_script.py @@ -138,9 +138,8 @@ class CmdLineTest(unittest.TestCase): expected_argv0, expected_path0, expected_package, expected_loader, *cmd_line_switches): - if not __debug__: - cmd_line_switches += ('-' + 'O' * sys.flags.optimize,) - run_args = cmd_line_switches + (script_name,) + tuple(example_args) + run_args = [*support.optim_args_from_interpreter_flags(), + *cmd_line_switches, script_name, *example_args] rc, out, err = assert_python_ok(*run_args, __isolated=False) self._check_output(script_name, rc, out + err, expected_file, expected_argv0, expected_path0, diff --git a/Lib/test/test_code_module.py b/Lib/test/test_code_module.py index 3394b39..1a8f699 100644 --- a/Lib/test/test_code_module.py +++ b/Lib/test/test_code_module.py @@ -69,7 +69,7 @@ class TestInteractiveConsole(unittest.TestCase): # with banner self.infunc.side_effect = EOFError('Finished') self.console.interact(banner='Foo') - self.assertEqual(len(self.stderr.method_calls), 2) + self.assertEqual(len(self.stderr.method_calls), 3) banner_call = self.stderr.method_calls[0] self.assertEqual(banner_call, ['write', ('Foo\n',), {}]) @@ -77,8 +77,36 @@ class TestInteractiveConsole(unittest.TestCase): self.stderr.reset_mock() self.infunc.side_effect = EOFError('Finished') self.console.interact(banner='') + self.assertEqual(len(self.stderr.method_calls), 2) + + def test_exit_msg(self): + # default exit message + self.infunc.side_effect = EOFError('Finished') + self.console.interact(banner='') + self.assertEqual(len(self.stderr.method_calls), 2) + err_msg = self.stderr.method_calls[1] + expected = 'now exiting InteractiveConsole...\n' + self.assertEqual(err_msg, ['write', (expected,), {}]) + + # no exit message + self.stderr.reset_mock() + self.infunc.side_effect = EOFError('Finished') + self.console.interact(banner='', exitmsg='') self.assertEqual(len(self.stderr.method_calls), 1) + # custom exit message + self.stderr.reset_mock() + message = ( + 'bye! \N{GREEK SMALL LETTER ZETA}\N{CYRILLIC SMALL LETTER ZHE}' + ) + self.infunc.side_effect = EOFError('Finished') + self.console.interact(banner='', exitmsg=message) + self.assertEqual(len(self.stderr.method_calls), 2) + err_msg = self.stderr.method_calls[1] + expected = message + '\n' + self.assertEqual(err_msg, ['write', (expected,), {}]) + + def test_cause_tb(self): self.infunc.side_effect = ["raise ValueError('') from AttributeError", EOFError('Finished')] diff --git a/Lib/test/test_codeccallbacks.py b/Lib/test/test_codeccallbacks.py index ee1e28a..c8cdacf 100644 --- a/Lib/test/test_codeccallbacks.py +++ b/Lib/test/test_codeccallbacks.py @@ -4,7 +4,6 @@ import sys import test.support import unicodedata import unittest -import warnings class PosReturn: # this can be used for configurable callbacks diff --git a/Lib/test/test_codecencodings_cn.py b/Lib/test/test_codecencodings_cn.py index d0e3a15..3bdf7d0 100644 --- a/Lib/test/test_codecencodings_cn.py +++ b/Lib/test/test_codecencodings_cn.py @@ -3,7 +3,6 @@ # Codec encoding tests for PRC encodings. # -from test import support from test import multibytecodec_support import unittest diff --git a/Lib/test/test_codecencodings_hk.py b/Lib/test/test_codecencodings_hk.py index bb9be11..c5e2f99 100644 --- a/Lib/test/test_codecencodings_hk.py +++ b/Lib/test/test_codecencodings_hk.py @@ -3,7 +3,6 @@ # Codec encoding tests for HongKong encodings. # -from test import support from test import multibytecodec_support import unittest diff --git a/Lib/test/test_codecencodings_iso2022.py b/Lib/test/test_codecencodings_iso2022.py index 8a3ca70..00ea1c3 100644 --- a/Lib/test/test_codecencodings_iso2022.py +++ b/Lib/test/test_codecencodings_iso2022.py @@ -1,6 +1,5 @@ # Codec encoding tests for ISO 2022 encodings. -from test import support from test import multibytecodec_support import unittest diff --git a/Lib/test/test_codecencodings_jp.py b/Lib/test/test_codecencodings_jp.py index 44b63a0..94378d1 100644 --- a/Lib/test/test_codecencodings_jp.py +++ b/Lib/test/test_codecencodings_jp.py @@ -3,7 +3,6 @@ # Codec encoding tests for Japanese encodings. # -from test import support from test import multibytecodec_support import unittest diff --git a/Lib/test/test_codecencodings_kr.py b/Lib/test/test_codecencodings_kr.py index b6a74fb..863d16b 100644 --- a/Lib/test/test_codecencodings_kr.py +++ b/Lib/test/test_codecencodings_kr.py @@ -3,7 +3,6 @@ # Codec encoding tests for ROK encodings. # -from test import support from test import multibytecodec_support import unittest diff --git a/Lib/test/test_codecencodings_tw.py b/Lib/test/test_codecencodings_tw.py index 9174296..bb1d133 100644 --- a/Lib/test/test_codecencodings_tw.py +++ b/Lib/test/test_codecencodings_tw.py @@ -3,7 +3,6 @@ # Codec encoding tests for ROC encodings. # -from test import support from test import multibytecodec_support import unittest diff --git a/Lib/test/test_codecmaps_cn.py b/Lib/test/test_codecmaps_cn.py index f1bd384..89e51c6 100644 --- a/Lib/test/test_codecmaps_cn.py +++ b/Lib/test/test_codecmaps_cn.py @@ -3,7 +3,6 @@ # Codec mapping tests for PRC encodings # -from test import support from test import multibytecodec_support import unittest diff --git a/Lib/test/test_codecmaps_hk.py b/Lib/test/test_codecmaps_hk.py index 4c0c415..7a48d24 100644 --- a/Lib/test/test_codecmaps_hk.py +++ b/Lib/test/test_codecmaps_hk.py @@ -3,7 +3,6 @@ # Codec mapping tests for HongKong encodings # -from test import support from test import multibytecodec_support import unittest diff --git a/Lib/test/test_codecmaps_jp.py b/Lib/test/test_codecmaps_jp.py index 5773823..fdfec80 100644 --- a/Lib/test/test_codecmaps_jp.py +++ b/Lib/test/test_codecmaps_jp.py @@ -3,7 +3,6 @@ # Codec mapping tests for Japanese encodings # -from test import support from test import multibytecodec_support import unittest diff --git a/Lib/test/test_codecmaps_kr.py b/Lib/test/test_codecmaps_kr.py index 6cb41c8..471cd74 100644 --- a/Lib/test/test_codecmaps_kr.py +++ b/Lib/test/test_codecmaps_kr.py @@ -3,7 +3,6 @@ # Codec mapping tests for ROK encodings # -from test import support from test import multibytecodec_support import unittest diff --git a/Lib/test/test_codecmaps_tw.py b/Lib/test/test_codecmaps_tw.py index 2ea44b5..145a97d 100644 --- a/Lib/test/test_codecmaps_tw.py +++ b/Lib/test/test_codecmaps_tw.py @@ -3,7 +3,6 @@ # Codec mapping tests for ROC encodings # -from test import support from test import multibytecodec_support import unittest diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index 0479542..d875340 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -4,7 +4,6 @@ import io import locale import sys import unittest -import warnings import encodings from test import support @@ -27,6 +26,7 @@ def coding_checker(self, coder): self.assertEqual(coder(input), (expect, len(input))) return check + class Queue(object): """ queue: write bytes at one end, read bytes from the other end @@ -47,6 +47,7 @@ class Queue(object): self._buffer = self._buffer[size:] return s + class MixInCheckStateHandling: def check_state_handling_decode(self, encoding, u, s): for i in range(len(s)+1): @@ -80,6 +81,7 @@ class MixInCheckStateHandling: part2 = d.encode(u[i:], True) self.assertEqual(s, part1+part2) + class ReadTest(MixInCheckStateHandling): def check_partial(self, input, partialresults): # get a StreamReader for the encoding and feed the bytestring version @@ -358,6 +360,12 @@ class ReadTest(MixInCheckStateHandling): self.assertEqual("[\uDC80]".encode(self.encoding, "replace"), "[?]".encode(self.encoding)) + # sequential surrogate characters + self.assertEqual("[\uD800\uDC80]".encode(self.encoding, "ignore"), + "[]".encode(self.encoding)) + self.assertEqual("[\uD800\uDC80]".encode(self.encoding, "replace"), + "[??]".encode(self.encoding)) + bom = "".encode(self.encoding) for before, after in [("\U00010fff", "A"), ("[", "]"), ("A", "\U00010fff")]: @@ -383,6 +391,7 @@ class ReadTest(MixInCheckStateHandling): self.assertEqual(test_sequence.decode(self.encoding, "backslashreplace"), before + backslashreplace + after) + class UTF32Test(ReadTest, unittest.TestCase): encoding = "utf-32" if sys.byteorder == 'little': @@ -478,6 +487,7 @@ class UTF32Test(ReadTest, unittest.TestCase): self.assertEqual('\U00010000' * 1024, codecs.utf_32_decode(encoded_be)[0]) + class UTF32LETest(ReadTest, unittest.TestCase): encoding = "utf-32-le" ill_formed_sequence = b"\x80\xdc\x00\x00" @@ -523,6 +533,7 @@ class UTF32LETest(ReadTest, unittest.TestCase): self.assertEqual('\U00010000' * 1024, codecs.utf_32_le_decode(encoded)[0]) + class UTF32BETest(ReadTest, unittest.TestCase): encoding = "utf-32-be" ill_formed_sequence = b"\x00\x00\xdc\x80" @@ -747,6 +758,7 @@ class UTF8Test(ReadTest, unittest.TestCase): encoding = "utf-8" ill_formed_sequence = b"\xed\xb2\x80" ill_formed_sequence_replace = "\ufffd" * 3 + BOM = b'' def test_partial(self): self.check_partial( @@ -775,27 +787,49 @@ class UTF8Test(ReadTest, unittest.TestCase): self.check_state_handling_decode(self.encoding, u, u.encode(self.encoding)) + def test_decode_error(self): + for data, error_handler, expected in ( + (b'[\x80\xff]', 'ignore', '[]'), + (b'[\x80\xff]', 'replace', '[\ufffd\ufffd]'), + (b'[\x80\xff]', 'surrogateescape', '[\udc80\udcff]'), + (b'[\x80\xff]', 'backslashreplace', '[\\x80\\xff]'), + ): + with self.subTest(data=data, error_handler=error_handler, + expected=expected): + self.assertEqual(data.decode(self.encoding, error_handler), + expected) + def test_lone_surrogates(self): super().test_lone_surrogates() # not sure if this is making sense for # UTF-16 and UTF-32 - self.assertEqual("[\uDC80]".encode('utf-8', "surrogateescape"), - b'[\x80]') + self.assertEqual("[\uDC80]".encode(self.encoding, "surrogateescape"), + self.BOM + b'[\x80]') + + with self.assertRaises(UnicodeEncodeError) as cm: + "[\uDC80\uD800\uDFFF]".encode(self.encoding, "surrogateescape") + exc = cm.exception + self.assertEqual(exc.object[exc.start:exc.end], '\uD800\uDFFF') def test_surrogatepass_handler(self): - self.assertEqual("abc\ud800def".encode("utf-8", "surrogatepass"), - b"abc\xed\xa0\x80def") - self.assertEqual(b"abc\xed\xa0\x80def".decode("utf-8", "surrogatepass"), + self.assertEqual("abc\ud800def".encode(self.encoding, "surrogatepass"), + self.BOM + b"abc\xed\xa0\x80def") + self.assertEqual("\U00010fff\uD800".encode(self.encoding, "surrogatepass"), + self.BOM + b"\xf0\x90\xbf\xbf\xed\xa0\x80") + self.assertEqual("[\uD800\uDC80]".encode(self.encoding, "surrogatepass"), + self.BOM + b'[\xed\xa0\x80\xed\xb2\x80]') + + self.assertEqual(b"abc\xed\xa0\x80def".decode(self.encoding, "surrogatepass"), "abc\ud800def") - self.assertEqual("\U00010fff\uD800".encode("utf-8", "surrogatepass"), - b"\xf0\x90\xbf\xbf\xed\xa0\x80") - self.assertEqual(b"\xf0\x90\xbf\xbf\xed\xa0\x80".decode("utf-8", "surrogatepass"), + self.assertEqual(b"\xf0\x90\xbf\xbf\xed\xa0\x80".decode(self.encoding, "surrogatepass"), "\U00010fff\uD800") + self.assertTrue(codecs.lookup_error("surrogatepass")) with self.assertRaises(UnicodeDecodeError): - b"abc\xed\xa0".decode("utf-8", "surrogatepass") + b"abc\xed\xa0".decode(self.encoding, "surrogatepass") with self.assertRaises(UnicodeDecodeError): - b"abc\xed\xa0z".decode("utf-8", "surrogatepass") + b"abc\xed\xa0z".decode(self.encoding, "surrogatepass") + @unittest.skipUnless(sys.platform == 'win32', 'cp65001 is a Windows-only codec') @@ -1059,6 +1093,7 @@ class ReadBufferTest(unittest.TestCase): class UTF8SigTest(UTF8Test, unittest.TestCase): encoding = "utf-8-sig" + BOM = codecs.BOM_UTF8 def test_partial(self): self.check_partial( @@ -1194,6 +1229,7 @@ class EscapeDecodeTest(unittest.TestCase): self.assertEqual(decode(br"[\x0]\x0", "ignore"), (b"[]", 8)) self.assertEqual(decode(br"[\x0]\x0", "replace"), (b"[?]?", 8)) + class RecodingTest(unittest.TestCase): def test_recoding(self): f = io.BytesIO() @@ -1313,6 +1349,7 @@ for i in punycode_testcases: if len(i)!=2: print(repr(i)) + class PunycodeTest(unittest.TestCase): def test_encode(self): for uni, puny in punycode_testcases: @@ -1332,6 +1369,7 @@ class PunycodeTest(unittest.TestCase): puny = puny.decode("ascii").encode("ascii") self.assertEqual(uni, puny.decode("punycode")) + class UnicodeInternalTest(unittest.TestCase): @unittest.skipUnless(SIZEOF_WCHAR_T == 4, 'specific to 32-bit wchar_t') def test_bug1251300(self): @@ -1586,6 +1624,7 @@ class NameprepTest(unittest.TestCase): except Exception as e: raise support.TestFailed("Test 3.%d: %s" % (pos+1, str(e))) + class IDNACodecTest(unittest.TestCase): def test_builtin_decode(self): self.assertEqual(str(b"python.org", "idna"), "python.org") @@ -1672,6 +1711,7 @@ class IDNACodecTest(unittest.TestCase): self.assertRaises(Exception, b"python.org".decode, "idna", errors) + class CodecsModuleTest(unittest.TestCase): def test_decode(self): @@ -1780,6 +1820,7 @@ class CodecsModuleTest(unittest.TestCase): self.assertRaises(UnicodeError, codecs.decode, b'abc', 'undefined', errors) + class StreamReaderTest(unittest.TestCase): def setUp(self): @@ -1790,6 +1831,7 @@ class StreamReaderTest(unittest.TestCase): f = self.reader(self.stream) self.assertEqual(f.readlines(), ['\ud55c\n', '\uae00']) + class EncodedFileTest(unittest.TestCase): def test_basic(self): @@ -1920,6 +1962,7 @@ broken_unicode_with_stateful = [ "unicode_internal" ] + class BasicUnicodeTest(unittest.TestCase, MixInCheckStateHandling): def test_basics(self): s = "abc123" # all codecs should be able to encode these @@ -2082,6 +2125,7 @@ class BasicUnicodeTest(unittest.TestCase, MixInCheckStateHandling): self.check_state_handling_decode(encoding, u, u.encode(encoding)) self.check_state_handling_encode(encoding, u, u.encode(encoding)) + class CharmapTest(unittest.TestCase): def test_decode_with_string_map(self): self.assertEqual( @@ -2332,6 +2376,7 @@ class WithStmtTest(unittest.TestCase): info.streamwriter, 'strict') as srw: self.assertEqual(srw.read(), "\xfc") + class TypesTest(unittest.TestCase): def test_decode_unicode(self): # Most decoders don't accept unicode input @@ -2622,6 +2667,7 @@ else: bytes_transform_encodings.append("bz2_codec") transform_aliases["bz2_codec"] = ["bz2"] + class TransformCodecTest(unittest.TestCase): def test_basics(self): @@ -3099,5 +3145,81 @@ class CodePageTest(unittest.TestCase): self.assertEqual(decoded, ('abc', 3)) +class ASCIITest(unittest.TestCase): + def test_encode(self): + self.assertEqual('abc123'.encode('ascii'), b'abc123') + + def test_encode_error(self): + for data, error_handler, expected in ( + ('[\x80\xff\u20ac]', 'ignore', b'[]'), + ('[\x80\xff\u20ac]', 'replace', b'[???]'), + ('[\x80\xff\u20ac]', 'xmlcharrefreplace', b'[€ÿ€]'), + ('[\x80\xff\u20ac\U000abcde]', 'backslashreplace', + b'[\\x80\\xff\\u20ac\\U000abcde]'), + ('[\udc80\udcff]', 'surrogateescape', b'[\x80\xff]'), + ): + with self.subTest(data=data, error_handler=error_handler, + expected=expected): + self.assertEqual(data.encode('ascii', error_handler), + expected) + + def test_encode_surrogateescape_error(self): + with self.assertRaises(UnicodeEncodeError): + # the first character can be decoded, but not the second + '\udc80\xff'.encode('ascii', 'surrogateescape') + + def test_decode(self): + self.assertEqual(b'abc'.decode('ascii'), 'abc') + + def test_decode_error(self): + for data, error_handler, expected in ( + (b'[\x80\xff]', 'ignore', '[]'), + (b'[\x80\xff]', 'replace', '[\ufffd\ufffd]'), + (b'[\x80\xff]', 'surrogateescape', '[\udc80\udcff]'), + (b'[\x80\xff]', 'backslashreplace', '[\\x80\\xff]'), + ): + with self.subTest(data=data, error_handler=error_handler, + expected=expected): + self.assertEqual(data.decode('ascii', error_handler), + expected) + + +class Latin1Test(unittest.TestCase): + def test_encode(self): + for data, expected in ( + ('abc', b'abc'), + ('\x80\xe9\xff', b'\x80\xe9\xff'), + ): + with self.subTest(data=data, expected=expected): + self.assertEqual(data.encode('latin1'), expected) + + def test_encode_errors(self): + for data, error_handler, expected in ( + ('[\u20ac\udc80]', 'ignore', b'[]'), + ('[\u20ac\udc80]', 'replace', b'[??]'), + ('[\u20ac\U000abcde]', 'backslashreplace', + b'[\\u20ac\\U000abcde]'), + ('[\u20ac\udc80]', 'xmlcharrefreplace', b'[€�]'), + ('[\udc80\udcff]', 'surrogateescape', b'[\x80\xff]'), + ): + with self.subTest(data=data, error_handler=error_handler, + expected=expected): + self.assertEqual(data.encode('latin1', error_handler), + expected) + + def test_encode_surrogateescape_error(self): + with self.assertRaises(UnicodeEncodeError): + # the first character can be decoded, but not the second + '\udc80\u20ac'.encode('latin1', 'surrogateescape') + + def test_decode(self): + for data, expected in ( + (b'abc', 'abc'), + (b'[\x80\xff]', '[\x80\xff]'), + ): + with self.subTest(data=data, expected=expected): + self.assertEqual(data.decode('latin1'), expected) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_codeop.py b/Lib/test/test_codeop.py index 509bf5d..98da26f 100644 --- a/Lib/test/test_codeop.py +++ b/Lib/test/test_codeop.py @@ -282,7 +282,6 @@ class CodeopTests(unittest.TestCase): ai("if (a == 1 and b = 2): pass") ai("del 1") - ai("del ()") ai("del (1,)") ai("del [1]") ai("del '1'") diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index 5238382..f1fb011 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -20,10 +20,10 @@ from collections import UserDict, UserString, UserList from collections import ChainMap from collections import deque from collections.abc import Awaitable, Coroutine, AsyncIterator, AsyncIterable -from collections.abc import Hashable, Iterable, Iterator, Generator -from collections.abc import Sized, Container, Callable +from collections.abc import Hashable, Iterable, Iterator, Generator, Reversible +from collections.abc import Sized, Container, Callable, Collection from collections.abc import Set, MutableSet -from collections.abc import Mapping, MutableMapping, KeysView, ItemsView +from collections.abc import Mapping, MutableMapping, KeysView, ItemsView, ValuesView from collections.abc import Sequence, MutableSequence from collections.abc import ByteString @@ -412,6 +412,18 @@ class TestNamedTuple(unittest.TestCase): self.assertEqual(NTColor._fields, ('red', 'green', 'blue')) globals().pop('NTColor', None) # clean-up after this test + def test_keyword_only_arguments(self): + # See issue 25628 + with support.captured_stdout() as template: + NT = namedtuple('NT', ['x', 'y'], verbose=True) + self.assertIn('class NT', NT._source) + with self.assertRaises(TypeError): + NT = namedtuple('NT', ['x', 'y'], True) + + NT = namedtuple('NT', ['abc', 'def'], rename=True) + self.assertEqual(NT._fields, ('abc', '_1')) + with self.assertRaises(TypeError): + NT = namedtuple('NT', ['abc', 'def'], False, True) def test_namedtuple_subclass_issue_24931(self): class Point(namedtuple('_Point', ['x', 'y'])): @@ -487,6 +499,9 @@ class ABCTestCase(unittest.TestCase): self.assertTrue(other.right_side,'Right side not called for %s.%s' % (type(instance), name)) +def _test_gen(): + yield + class TestOneTrickPonyABCs(ABCTestCase): def test_Awaitable(self): @@ -674,7 +689,7 @@ class TestOneTrickPonyABCs(ABCTestCase): samples = [bytes(), str(), tuple(), list(), set(), frozenset(), dict(), dict().keys(), dict().items(), dict().values(), - (lambda: (yield))(), + _test_gen(), (x for x in []), ] for x in samples: @@ -688,6 +703,161 @@ class TestOneTrickPonyABCs(ABCTestCase): self.assertFalse(issubclass(str, I)) self.validate_abstract_methods(Iterable, '__iter__') self.validate_isinstance(Iterable, '__iter__') + # Check None blocking + class It: + def __iter__(self): return iter([]) + class ItBlocked(It): + __iter__ = None + self.assertTrue(issubclass(It, Iterable)) + self.assertTrue(isinstance(It(), Iterable)) + self.assertFalse(issubclass(ItBlocked, Iterable)) + self.assertFalse(isinstance(ItBlocked(), Iterable)) + + def test_Reversible(self): + # Check some non-reversibles + non_samples = [None, 42, 3.14, 1j, dict(), set(), frozenset()] + for x in non_samples: + self.assertNotIsInstance(x, Reversible) + self.assertFalse(issubclass(type(x), Reversible), repr(type(x))) + # Check some non-reversible iterables + non_reversibles = [dict().keys(), dict().items(), dict().values(), + Counter(), Counter().keys(), Counter().items(), + Counter().values(), _test_gen(), + (x for x in []), iter([]), reversed([])] + for x in non_reversibles: + self.assertNotIsInstance(x, Reversible) + self.assertFalse(issubclass(type(x), Reversible), repr(type(x))) + # Check some reversible iterables + samples = [bytes(), str(), tuple(), list(), OrderedDict(), + OrderedDict().keys(), OrderedDict().items(), + OrderedDict().values()] + for x in samples: + self.assertIsInstance(x, Reversible) + self.assertTrue(issubclass(type(x), Reversible), repr(type(x))) + # Check also Mapping, MutableMapping, and Sequence + self.assertTrue(issubclass(Sequence, Reversible), repr(Sequence)) + self.assertFalse(issubclass(Mapping, Reversible), repr(Mapping)) + self.assertFalse(issubclass(MutableMapping, Reversible), repr(MutableMapping)) + # Check direct subclassing + class R(Reversible): + def __iter__(self): + return iter(list()) + def __reversed__(self): + return iter(list()) + self.assertEqual(list(reversed(R())), []) + self.assertFalse(issubclass(float, R)) + self.validate_abstract_methods(Reversible, '__reversed__', '__iter__') + # Check reversible non-iterable (which is not Reversible) + class RevNoIter: + def __reversed__(self): return reversed([]) + class RevPlusIter(RevNoIter): + def __iter__(self): return iter([]) + self.assertFalse(issubclass(RevNoIter, Reversible)) + self.assertFalse(isinstance(RevNoIter(), Reversible)) + self.assertTrue(issubclass(RevPlusIter, Reversible)) + self.assertTrue(isinstance(RevPlusIter(), Reversible)) + # Check None blocking + class Rev: + def __iter__(self): return iter([]) + def __reversed__(self): return reversed([]) + class RevItBlocked(Rev): + __iter__ = None + class RevRevBlocked(Rev): + __reversed__ = None + self.assertTrue(issubclass(Rev, Reversible)) + self.assertTrue(isinstance(Rev(), Reversible)) + self.assertFalse(issubclass(RevItBlocked, Reversible)) + self.assertFalse(isinstance(RevItBlocked(), Reversible)) + self.assertFalse(issubclass(RevRevBlocked, Reversible)) + self.assertFalse(isinstance(RevRevBlocked(), Reversible)) + + def test_Collection(self): + # Check some non-collections + non_collections = [None, 42, 3.14, 1j, lambda x: 2*x] + for x in non_collections: + self.assertNotIsInstance(x, Collection) + self.assertFalse(issubclass(type(x), Collection), repr(type(x))) + # Check some non-collection iterables + non_col_iterables = [_test_gen(), iter(b''), iter(bytearray()), + (x for x in []), dict().values()] + for x in non_col_iterables: + self.assertNotIsInstance(x, Collection) + self.assertFalse(issubclass(type(x), Collection), repr(type(x))) + # Check some collections + samples = [set(), frozenset(), dict(), bytes(), str(), tuple(), + list(), dict().keys(), dict().items()] + for x in samples: + self.assertIsInstance(x, Collection) + self.assertTrue(issubclass(type(x), Collection), repr(type(x))) + # Check also Mapping, MutableMapping, etc. + self.assertTrue(issubclass(Sequence, Collection), repr(Sequence)) + self.assertTrue(issubclass(Mapping, Collection), repr(Mapping)) + self.assertTrue(issubclass(MutableMapping, Collection), + repr(MutableMapping)) + self.assertTrue(issubclass(Set, Collection), repr(Set)) + self.assertTrue(issubclass(MutableSet, Collection), repr(MutableSet)) + self.assertTrue(issubclass(Sequence, Collection), repr(MutableSet)) + # Check direct subclassing + class Col(Collection): + def __iter__(self): + return iter(list()) + def __len__(self): + return 0 + def __contains__(self, item): + return False + class DerCol(Col): pass + self.assertEqual(list(iter(Col())), []) + self.assertFalse(issubclass(list, Col)) + self.assertFalse(issubclass(set, Col)) + self.assertFalse(issubclass(float, Col)) + self.assertEqual(list(iter(DerCol())), []) + self.assertFalse(issubclass(list, DerCol)) + self.assertFalse(issubclass(set, DerCol)) + self.assertFalse(issubclass(float, DerCol)) + self.validate_abstract_methods(Collection, '__len__', '__iter__', + '__contains__') + # Check sized container non-iterable (which is not Collection) etc. + class ColNoIter: + def __len__(self): return 0 + def __contains__(self, item): return False + class ColNoSize: + def __iter__(self): return iter([]) + def __contains__(self, item): return False + class ColNoCont: + def __iter__(self): return iter([]) + def __len__(self): return 0 + self.assertFalse(issubclass(ColNoIter, Collection)) + self.assertFalse(isinstance(ColNoIter(), Collection)) + self.assertFalse(issubclass(ColNoSize, Collection)) + self.assertFalse(isinstance(ColNoSize(), Collection)) + self.assertFalse(issubclass(ColNoCont, Collection)) + self.assertFalse(isinstance(ColNoCont(), Collection)) + # Check None blocking + class SizeBlock: + def __iter__(self): return iter([]) + def __contains__(self): return False + __len__ = None + class IterBlock: + def __len__(self): return 0 + def __contains__(self): return True + __iter__ = None + self.assertFalse(issubclass(SizeBlock, Collection)) + self.assertFalse(isinstance(SizeBlock(), Collection)) + self.assertFalse(issubclass(IterBlock, Collection)) + self.assertFalse(isinstance(IterBlock(), Collection)) + # Check None blocking in subclass + class ColImpl: + def __iter__(self): + return iter(list()) + def __len__(self): + return 0 + def __contains__(self, item): + return False + class NonCol(ColImpl): + __contains__ = None + self.assertFalse(issubclass(NonCol, Collection)) + self.assertFalse(isinstance(NonCol(), Collection)) + def test_Iterator(self): non_samples = [None, 42, 3.14, 1j, b"", "", (), [], {}, set()] @@ -699,7 +869,7 @@ class TestOneTrickPonyABCs(ABCTestCase): iter(set()), iter(frozenset()), iter(dict().keys()), iter(dict().items()), iter(dict().values()), - (lambda: (yield))(), + _test_gen(), (x for x in []), ] for x in samples: @@ -787,7 +957,7 @@ class TestOneTrickPonyABCs(ABCTestCase): def test_Sized(self): non_samples = [None, 42, 3.14, 1j, - (lambda: (yield))(), + _test_gen(), (x for x in []), ] for x in non_samples: @@ -805,7 +975,7 @@ class TestOneTrickPonyABCs(ABCTestCase): def test_Container(self): non_samples = [None, 42, 3.14, 1j, - (lambda: (yield))(), + _test_gen(), (x for x in []), ] for x in non_samples: @@ -824,7 +994,7 @@ class TestOneTrickPonyABCs(ABCTestCase): def test_Callable(self): non_samples = [None, 42, 3.14, 1j, "", b"", (), [], {}, set(), - (lambda: (yield))(), + _test_gen(), (x for x in []), ] for x in non_samples: @@ -842,14 +1012,14 @@ class TestOneTrickPonyABCs(ABCTestCase): self.validate_isinstance(Callable, '__call__') def test_direct_subclassing(self): - for B in Hashable, Iterable, Iterator, Sized, Container, Callable: + for B in Hashable, Iterable, Iterator, Reversible, Sized, Container, Callable: class C(B): pass self.assertTrue(issubclass(C, B)) self.assertFalse(issubclass(int, C)) def test_registration(self): - for B in Hashable, Iterable, Iterator, Sized, Container, Callable: + for B in Hashable, Iterable, Iterator, Reversible, Sized, Container, Callable: class C: __hash__ = None # Make sure it isn't hashable by default self.assertFalse(issubclass(C, B), B.__name__) @@ -1049,6 +1219,26 @@ class TestCollectionABCs(ABCTestCase): self.assertFalse(ncs > cs) self.assertTrue(ncs >= cs) + def test_issue26915(self): + # Container membership test should check identity first + class CustomEqualObject: + def __eq__(self, other): + return False + class CustomSequence(list): + def __contains__(self, value): + return Sequence.__contains__(self, value) + + nan = float('nan') + obj = CustomEqualObject() + containers = [ + CustomSequence([nan, obj]), + ItemsView({1: nan, 2: obj}), + ValuesView({1: nan, 2: obj}) + ] + for container in containers: + for elem in container: + self.assertIn(elem, container) + def assertSameSet(self, s1, s2): # coerce both to a real set then check equality self.assertSetEqual(set(s1), set(s2)) @@ -1219,6 +1409,7 @@ class TestCollectionABCs(ABCTestCase): def __iter__(self): return iter(()) self.validate_comparison(MyMapping()) + self.assertRaises(TypeError, reversed, MyMapping()) def test_MutableMapping(self): for sample in [dict]: diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 824e843..9638e69 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -473,10 +473,13 @@ if 1: self.assertEqual(d, {1: 2, 3: 4}) def test_compile_filename(self): - for filename in ('file.py', b'file.py', - bytearray(b'file.py'), memoryview(b'file.py')): + for filename in 'file.py', b'file.py': code = compile('pass', filename, 'exec') self.assertEqual(code.co_filename, 'file.py') + for filename in bytearray(b'file.py'), memoryview(b'file.py'): + with self.assertWarns(DeprecationWarning): + code = compile('pass', filename, 'exec') + self.assertEqual(code.co_filename, 'file.py') self.assertRaises(TypeError, compile, 'pass', list(b'file.py'), 'exec') @support.cpython_only diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py index 2ce8a61..9b424a7 100644 --- a/Lib/test/test_compileall.py +++ b/Lib/test/test_compileall.py @@ -1,6 +1,7 @@ import sys import compileall import importlib.util +import test.test_importlib.util import os import pathlib import py_compile @@ -40,6 +41,11 @@ class CompileallTests(unittest.TestCase): def tearDown(self): shutil.rmtree(self.directory) + def add_bad_source_file(self): + self.bad_source_path = os.path.join(self.directory, '_test_bad.py') + with open(self.bad_source_path, 'w') as file: + file.write('x (\n') + def data(self): with open(self.bc_path, 'rb') as file: data = file.read(8) @@ -78,15 +84,43 @@ class CompileallTests(unittest.TestCase): os.unlink(fn) except: pass - compileall.compile_file(self.source_path, force=False, quiet=True) + self.assertTrue(compileall.compile_file(self.source_path, + force=False, quiet=True)) self.assertTrue(os.path.isfile(self.bc_path) and not os.path.isfile(self.bc_path2)) os.unlink(self.bc_path) - compileall.compile_dir(self.directory, force=False, quiet=True) + self.assertTrue(compileall.compile_dir(self.directory, force=False, + quiet=True)) self.assertTrue(os.path.isfile(self.bc_path) and os.path.isfile(self.bc_path2)) os.unlink(self.bc_path) os.unlink(self.bc_path2) + # Test against bad files + self.add_bad_source_file() + self.assertFalse(compileall.compile_file(self.bad_source_path, + force=False, quiet=2)) + self.assertFalse(compileall.compile_dir(self.directory, + force=False, quiet=2)) + + def test_compile_path(self): + # Exclude Lib/test/ which contains invalid Python files like + # Lib/test/badsyntax_pep3120.py + testdir = os.path.realpath(os.path.dirname(__file__)) + if testdir in sys.path: + self.addCleanup(setattr, sys, 'path', sys.path) + + sys.path = list(sys.path) + try: + sys.path.remove(testdir) + except ValueError: + pass + + self.assertTrue(compileall.compile_path(quiet=2)) + + with test.test_importlib.util.import_state(path=[self.directory]): + self.add_bad_source_file() + self.assertFalse(compileall.compile_path(skip_curdir=False, + force=True, quiet=2)) def test_no_pycache_in_non_package(self): # Bug 8563 reported that __pycache__ directories got created by @@ -197,10 +231,9 @@ class CommandLineTests(unittest.TestCase): raise unittest.SkipTest('not all entries on sys.path are writable') def _get_run_args(self, args): - interp_args = ['-S'] - if sys.flags.optimize: - interp_args.append({1 : '-O', 2 : '-OO'}[sys.flags.optimize]) - return interp_args + ['-m', 'compileall'] + list(args) + return [*support.optim_args_from_interpreter_flags(), + '-S', '-m', 'compileall', + *args] def assertRunOK(self, *args, **env_vars): rc, out, err = script_helper.assert_python_ok( diff --git a/Lib/test/test_concurrent_futures.py b/Lib/test/test_concurrent_futures.py index cdb9308..23e95b2 100644 --- a/Lib/test/test_concurrent_futures.py +++ b/Lib/test/test_concurrent_futures.py @@ -4,7 +4,7 @@ import test.support test.support.import_module('_multiprocessing') # Skip tests if sem_open implementation is broken. test.support.import_module('multiprocessing.synchronize') -# import threading after _multiprocessing to raise a more revelant error +# import threading after _multiprocessing to raise a more relevant error # message: "No module named _multiprocessing". _multiprocessing is not compiled # without thread support. test.support.import_module('threading') @@ -154,6 +154,30 @@ class ThreadPoolShutdownTest(ThreadPoolMixin, ExecutorShutdownTest, unittest.Tes for t in threads: t.join() + def test_thread_names_assigned(self): + executor = futures.ThreadPoolExecutor( + max_workers=5, thread_name_prefix='SpecialPool') + executor.map(abs, range(-5, 5)) + threads = executor._threads + del executor + + for t in threads: + self.assertRegex(t.name, r'^SpecialPool_[0-4]$') + t.join() + + def test_thread_names_default(self): + executor = futures.ThreadPoolExecutor(max_workers=5) + executor.map(abs, range(-5, 5)) + threads = executor._threads + del executor + + for t in threads: + # We don't particularly care what the default name is, just that + # it has a default name implying that it is a ThreadPoolExecutor + # followed by what looks like a thread number. + self.assertRegex(t.name, r'^.*ThreadPoolExecutor.*_[0-4]$') + t.join() + class ProcessPoolShutdownTest(ProcessPoolMixin, ExecutorShutdownTest, unittest.TestCase): def _prime_executor(self): diff --git a/Lib/test/test_contains.py b/Lib/test/test_contains.py index 3c6bdef..036a1d0 100644 --- a/Lib/test/test_contains.py +++ b/Lib/test/test_contains.py @@ -84,6 +84,31 @@ class TestContains(unittest.TestCase): self.assertTrue(container == constructor(values)) self.assertTrue(container == container) + def test_block_fallback(self): + # blocking fallback with __contains__ = None + class ByContains(object): + def __contains__(self, other): + return False + c = ByContains() + class BlockContains(ByContains): + """Is not a container + + This class is a perfectly good iterable (as tested by + list(bc)), as well as inheriting from a perfectly good + container, but __contains__ = None prevents the usual + fallback to iteration in the container protocol. That + is, normally, 0 in bc would fall back to the equivalent + of any(x==0 for x in bc), but here it's blocked from + doing so. + """ + def __iter__(self): + while False: + yield None + __contains__ = None + bc = BlockContains() + self.assertFalse(0 in c) + self.assertFalse(0 in list(bc)) + self.assertRaises(TypeError, lambda: 0 in bc) if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_contextlib.py b/Lib/test/test_contextlib.py index 516403e..c04c804 100644 --- a/Lib/test/test_contextlib.py +++ b/Lib/test/test_contextlib.py @@ -12,6 +12,39 @@ except ImportError: threading = None +class TestAbstractContextManager(unittest.TestCase): + + def test_enter(self): + class DefaultEnter(AbstractContextManager): + def __exit__(self, *args): + super().__exit__(*args) + + manager = DefaultEnter() + self.assertIs(manager.__enter__(), manager) + + def test_exit_is_abstract(self): + class MissingExit(AbstractContextManager): + pass + + with self.assertRaises(TypeError): + MissingExit() + + def test_structural_subclassing(self): + class ManagerFromScratch: + def __enter__(self): + return self + def __exit__(self, exc_type, exc_value, traceback): + return None + + self.assertTrue(issubclass(ManagerFromScratch, AbstractContextManager)) + + class DefaultEnter(AbstractContextManager): + def __exit__(self, *args): + super().__exit__(*args) + + self.assertTrue(issubclass(DefaultEnter, AbstractContextManager)) + + class ContextManagerTestCase(unittest.TestCase): def test_contextmanager_plain(self): @@ -89,7 +122,7 @@ class ContextManagerTestCase(unittest.TestCase): def woohoo(): yield try: - with self.assertWarnsRegex(PendingDeprecationWarning, + with self.assertWarnsRegex(DeprecationWarning, "StopIteration"): with woohoo(): raise stop_exc diff --git a/Lib/test/test_copy.py b/Lib/test/test_copy.py index 0e1f670..45a6920 100644 --- a/Lib/test/test_copy.py +++ b/Lib/test/test_copy.py @@ -98,7 +98,7 @@ class TestCopy(unittest.TestCase): tests = [None, ..., NotImplemented, 42, 2**100, 3.14, True, False, 1j, "hello", "hello\u1234", f.__code__, - b"world", bytes(range(256)), range(10), + b"world", bytes(range(256)), range(10), slice(1, 10, 2), NewStyle, Classic, max, WithMetaclass] for x in tests: self.assertIs(copy.copy(x), x) diff --git a/Lib/test/test_cprofile.py b/Lib/test/test_cprofile.py index f18983f..53f8917 100644 --- a/Lib/test/test_cprofile.py +++ b/Lib/test/test_cprofile.py @@ -6,7 +6,7 @@ from test.support import run_unittest, TESTFN, unlink # rip off all interesting stuff from test_profile import cProfile from test.test_profile import ProfileTest, regenerate_expected_output -from test.profilee import testfunc + class CProfileTest(ProfileTest): profilerclass = cProfile.Profile diff --git a/Lib/test/test_csv.py b/Lib/test/test_csv.py index 77c315e..7dcea9c 100644 --- a/Lib/test/test_csv.py +++ b/Lib/test/test_csv.py @@ -2,9 +2,7 @@ # csv package unit tests import copy -import io import sys -import os import unittest from io import StringIO from tempfile import TemporaryFile @@ -12,6 +10,9 @@ import csv import gc import pickle from test import support +from itertools import permutations +from textwrap import dedent +from collections import OrderedDict class Test_Csv(unittest.TestCase): """ @@ -426,17 +427,16 @@ class TestDialectRegistry(unittest.TestCase): self.assertRaises(TypeError, csv.reader, [], quoting = -1) self.assertRaises(TypeError, csv.reader, [], quoting = 100) - # See issue #22995 - ## def test_copy(self): - ## for name in csv.list_dialects(): - ## dialect = csv.get_dialect(name) - ## self.assertRaises(TypeError, copy.copy, dialect) + def test_copy(self): + for name in csv.list_dialects(): + dialect = csv.get_dialect(name) + self.assertRaises(TypeError, copy.copy, dialect) - ## def test_pickle(self): - ## for name in csv.list_dialects(): - ## dialect = csv.get_dialect(name) - ## for proto in range(pickle.HIGHEST_PROTOCOL + 1): - ## self.assertRaises(TypeError, pickle.dumps, dialect, proto) + def test_pickle(self): + for name in csv.list_dialects(): + dialect = csv.get_dialect(name) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + self.assertRaises(TypeError, pickle.dumps, dialect, proto) class TestCsvBase(unittest.TestCase): def readerAssertEqual(self, input, expected_result): @@ -1080,7 +1080,6 @@ class TestUnicode(unittest.TestCase): "François Pinard"] def test_unicode_read(self): - import io with TemporaryFile("w+", newline='', encoding="utf-8") as fileobj: fileobj.write(",".join(self.names) + "\r\n") fileobj.seek(0) @@ -1089,7 +1088,6 @@ class TestUnicode(unittest.TestCase): def test_unicode_write(self): - import io with TemporaryFile("w+", newline='', encoding="utf-8") as fileobj: writer = csv.writer(fileobj) writer.writerow(self.names) @@ -1097,6 +1095,63 @@ class TestUnicode(unittest.TestCase): fileobj.seek(0) self.assertEqual(fileobj.read(), expected) +class KeyOrderingTest(unittest.TestCase): + + def test_ordering_for_the_dict_reader_and_writer(self): + resultset = set() + for keys in permutations("abcde"): + with TemporaryFile('w+', newline='', encoding="utf-8") as fileobject: + dw = csv.DictWriter(fileobject, keys) + dw.writeheader() + fileobject.seek(0) + dr = csv.DictReader(fileobject) + kt = tuple(dr.fieldnames) + self.assertEqual(keys, kt) + resultset.add(kt) + # Final sanity check: were all permutations unique? + self.assertEqual(len(resultset), 120, "Key ordering: some key permutations not collected (expected 120)") + + def test_ordered_dict_reader(self): + data = dedent('''\ + FirstName,LastName + Eric,Idle + Graham,Chapman,Over1,Over2 + + Under1 + John,Cleese + ''').splitlines() + + self.assertEqual(list(csv.DictReader(data)), + [OrderedDict([('FirstName', 'Eric'), ('LastName', 'Idle')]), + OrderedDict([('FirstName', 'Graham'), ('LastName', 'Chapman'), + (None, ['Over1', 'Over2'])]), + OrderedDict([('FirstName', 'Under1'), ('LastName', None)]), + OrderedDict([('FirstName', 'John'), ('LastName', 'Cleese')]), + ]) + + self.assertEqual(list(csv.DictReader(data, restkey='OtherInfo')), + [OrderedDict([('FirstName', 'Eric'), ('LastName', 'Idle')]), + OrderedDict([('FirstName', 'Graham'), ('LastName', 'Chapman'), + ('OtherInfo', ['Over1', 'Over2'])]), + OrderedDict([('FirstName', 'Under1'), ('LastName', None)]), + OrderedDict([('FirstName', 'John'), ('LastName', 'Cleese')]), + ]) + + del data[0] # Remove the header row + self.assertEqual(list(csv.DictReader(data, fieldnames=['fname', 'lname'])), + [OrderedDict([('fname', 'Eric'), ('lname', 'Idle')]), + OrderedDict([('fname', 'Graham'), ('lname', 'Chapman'), + (None, ['Over1', 'Over2'])]), + OrderedDict([('fname', 'Under1'), ('lname', None)]), + OrderedDict([('fname', 'John'), ('lname', 'Cleese')]), + ]) + + +class MiscTestCase(unittest.TestCase): + def test__all__(self): + extra = {'__doc__', '__version__'} + support.check__all__(self, csv, ('csv', '_csv'), extra=extra) + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_datetime.py b/Lib/test/test_datetime.py index 2d4eb52..242e1bb 100644 --- a/Lib/test/test_datetime.py +++ b/Lib/test/test_datetime.py @@ -23,9 +23,16 @@ test_suffixes = ["_Pure", "_Fast"] test_classes = [] for module, suffix in zip(test_modules, test_suffixes): + test_classes = [] for name, cls in module.__dict__.items(): - if not (isinstance(cls, type) and issubclass(cls, unittest.TestCase)): + if not isinstance(cls, type): continue + if issubclass(cls, unittest.TestCase): + test_classes.append(cls) + elif issubclass(cls, unittest.TestSuite): + suit = cls() + test_classes.extend(type(test) for test in suit) + for cls in test_classes: cls.__name__ = name + suffix @classmethod def setUpClass(cls_, module=module): @@ -39,7 +46,6 @@ for module, suffix in zip(test_modules, test_suffixes): sys.modules.update(cls_._save_sys_modules) cls.setUpClass = setUpClass cls.tearDownClass = tearDownClass - test_classes.append(cls) def test_main(): run_unittest(*test_classes) diff --git a/Lib/test/test_dbm.py b/Lib/test/test_dbm.py index 623d992..f0a428d 100644 --- a/Lib/test/test_dbm.py +++ b/Lib/test/test_dbm.py @@ -1,6 +1,5 @@ """Test script for the dbm.open function based on testdumbdbm.py""" -import os import unittest import glob import test.support diff --git a/Lib/test/test_dbm_dumb.py b/Lib/test/test_dbm_dumb.py index ff63c88..2d77f07 100644 --- a/Lib/test/test_dbm_dumb.py +++ b/Lib/test/test_dbm_dumb.py @@ -6,6 +6,7 @@ import io import operator import os import unittest +import warnings import dbm.dumb as dumbdbm from test import support from functools import partial @@ -78,6 +79,12 @@ class DumbDBMTestCase(unittest.TestCase): self.init_db() f = dumbdbm.open(_fname, 'r') self.read_helper(f) + with self.assertWarnsRegex(DeprecationWarning, + 'The database is opened for reading only'): + f[b'g'] = b'x' + with self.assertWarnsRegex(DeprecationWarning, + 'The database is opened for reading only'): + del f[b'a'] f.close() def test_dumbdbm_keys(self): @@ -148,7 +155,7 @@ class DumbDBMTestCase(unittest.TestCase): self.assertEqual(self._dict[key], f[key]) def init_db(self): - f = dumbdbm.open(_fname, 'w') + f = dumbdbm.open(_fname, 'n') for k in self._dict: f[k] = self._dict[k] f.close() @@ -234,6 +241,24 @@ class DumbDBMTestCase(unittest.TestCase): pass self.assertEqual(stdout.getvalue(), '') + def test_warn_on_ignored_flags(self): + for value in ('r', 'w'): + _delete_files() + with self.assertWarnsRegex(DeprecationWarning, + "The database file is missing, the " + "semantics of the 'c' flag will " + "be used."): + f = dumbdbm.open(_fname, value) + f.close() + + def test_invalid_flag(self): + for flag in ('x', 'rf', None): + with self.assertWarnsRegex(DeprecationWarning, + "Flag must be one of " + "'r', 'w', 'c', or 'n'"): + f = dumbdbm.open(_fname, flag) + f.close() + def tearDown(self): _delete_files() diff --git a/Lib/test/test_dbm_gnu.py b/Lib/test/test_dbm_gnu.py index a7808f5..304b332 100644 --- a/Lib/test/test_dbm_gnu.py +++ b/Lib/test/test_dbm_gnu.py @@ -2,7 +2,7 @@ from test import support gdbm = support.import_module("dbm.gnu") #skip if not supported import unittest import os -from test.support import verbose, TESTFN, unlink +from test.support import TESTFN, unlink filename = TESTFN diff --git a/Lib/test/test_dbm_ndbm.py b/Lib/test/test_dbm_ndbm.py index 2291561..49f4426 100644 --- a/Lib/test/test_dbm_ndbm.py +++ b/Lib/test/test_dbm_ndbm.py @@ -1,8 +1,6 @@ from test import support support.import_module("dbm.ndbm") #skip if not supported import unittest -import os -import random import dbm.ndbm from dbm.ndbm import error diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index 1aa0bf8..7492f54 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -2047,6 +2047,39 @@ class UsabilityTest(unittest.TestCase): d = Decimal( (1, (0, 2, 7, 1), 'F') ) self.assertEqual(d.as_tuple(), (1, (0,), 'F')) + def test_as_integer_ratio(self): + Decimal = self.decimal.Decimal + + # exceptional cases + self.assertRaises(OverflowError, + Decimal.as_integer_ratio, Decimal('inf')) + self.assertRaises(OverflowError, + Decimal.as_integer_ratio, Decimal('-inf')) + self.assertRaises(ValueError, + Decimal.as_integer_ratio, Decimal('-nan')) + self.assertRaises(ValueError, + Decimal.as_integer_ratio, Decimal('snan123')) + + for exp in range(-4, 2): + for coeff in range(1000): + for sign in '+', '-': + d = Decimal('%s%dE%d' % (sign, coeff, exp)) + pq = d.as_integer_ratio() + p, q = pq + + # check return type + self.assertIsInstance(pq, tuple) + self.assertIsInstance(p, int) + self.assertIsInstance(q, int) + + # check normalization: q should be positive; + # p should be relatively prime to q. + self.assertGreater(q, 0) + self.assertEqual(math.gcd(p, q), 1) + + # check that p/q actually gives the correct value + self.assertEqual(Decimal(p) / Decimal(q), d) + def test_subclassing(self): # Different behaviours when subclassing Decimal Decimal = self.decimal.Decimal diff --git a/Lib/test/test_deque.py b/Lib/test/test_deque.py index 18e1df0..ce517b5 100644 --- a/Lib/test/test_deque.py +++ b/Lib/test/test_deque.py @@ -289,7 +289,7 @@ class TestBasic(unittest.TestCase): else: self.assertEqual(d.index(element, start, stop), target) - def test_insert_bug_24913(self): + def test_index_bug_24913(self): d = deque('A' * 3) with self.assertRaises(ValueError): i = d.index("Hello world", 0, 4) @@ -622,20 +622,22 @@ class TestBasic(unittest.TestCase): self.assertEqual(list(d), list(e)) def test_pickle(self): - d = deque(range(200)) - for i in range(pickle.HIGHEST_PROTOCOL + 1): - s = pickle.dumps(d, i) - e = pickle.loads(s) - self.assertNotEqual(id(d), id(e)) - self.assertEqual(list(d), list(e)) - -## def test_pickle_recursive(self): -## d = deque('abc') -## d.append(d) -## for i in range(pickle.HIGHEST_PROTOCOL + 1): -## e = pickle.loads(pickle.dumps(d, i)) -## self.assertNotEqual(id(d), id(e)) -## self.assertEqual(id(e), id(e[-1])) + for d in deque(range(200)), deque(range(200), 100): + for i in range(pickle.HIGHEST_PROTOCOL + 1): + s = pickle.dumps(d, i) + e = pickle.loads(s) + self.assertNotEqual(id(e), id(d)) + self.assertEqual(list(e), list(d)) + self.assertEqual(e.maxlen, d.maxlen) + + def test_pickle_recursive(self): + for d in deque('abc'), deque('abc', 3): + d.append(d) + for i in range(pickle.HIGHEST_PROTOCOL + 1): + e = pickle.loads(pickle.dumps(d, i)) + self.assertNotEqual(id(e), id(d)) + self.assertEqual(id(e[-1]), id(e)) + self.assertEqual(e.maxlen, d.maxlen) def test_iterator_pickle(self): orig = deque(range(200)) @@ -696,6 +698,15 @@ class TestBasic(unittest.TestCase): self.assertNotEqual(id(d), id(e)) self.assertEqual(list(d), list(e)) + for i in range(5): + for maxlen in range(-1, 6): + s = [random.random() for j in range(i)] + d = deque(s) if maxlen == -1 else deque(s, maxlen) + e = d.copy() + self.assertEqual(d, e) + self.assertEqual(d.maxlen, e.maxlen) + self.assertTrue(all(x is y for x, y in zip(d, e))) + def test_copy_method(self): mut = [10] d = deque([mut]) @@ -845,24 +856,26 @@ class TestSubclass(unittest.TestCase): self.assertEqual(type(d), type(e)) self.assertEqual(list(d), list(e)) -## def test_pickle(self): -## d = Deque('abc') -## d.append(d) -## -## e = pickle.loads(pickle.dumps(d)) -## self.assertNotEqual(id(d), id(e)) -## self.assertEqual(type(d), type(e)) -## dd = d.pop() -## ee = e.pop() -## self.assertEqual(id(e), id(ee)) -## self.assertEqual(d, e) -## -## d.x = d -## e = pickle.loads(pickle.dumps(d)) -## self.assertEqual(id(e), id(e.x)) -## -## d = DequeWithBadIter('abc') -## self.assertRaises(TypeError, pickle.dumps, d) + def test_pickle_recursive(self): + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + for d in Deque('abc'), Deque('abc', 3): + d.append(d) + + e = pickle.loads(pickle.dumps(d, proto)) + self.assertNotEqual(id(e), id(d)) + self.assertEqual(type(e), type(d)) + self.assertEqual(e.maxlen, d.maxlen) + dd = d.pop() + ee = e.pop() + self.assertEqual(id(ee), id(e)) + self.assertEqual(e, d) + + d.x = d + e = pickle.loads(pickle.dumps(d, proto)) + self.assertEqual(id(e.x), id(e)) + + for d in DequeWithBadIter('abc'), DequeWithBadIter('abc', 2): + self.assertRaises(TypeError, pickle.dumps, d, proto) def test_weakref(self): d = deque('gallahad') diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index a130cec..0950b8e 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -876,7 +876,7 @@ class ClassPropertiesAndMethods(unittest.TestCase): self.assertEqual(Frag().__int__(), 42) self.assertEqual(int(Frag()), 42) - def test_diamond_inheritence(self): + def test_diamond_inheritance(self): # Testing multiple inheritance special cases... class A(object): def spam(self): return "A" @@ -4738,11 +4738,8 @@ class PicklingTests(unittest.TestCase): return (args, kwargs) obj = C3() for proto in protocols: - if proto >= 4: + if proto >= 2: self._check_reduce(proto, obj, args, kwargs) - elif proto >= 2: - with self.assertRaises(ValueError): - obj.__reduce_ex__(proto) class C4: def __getnewargs_ex__(self): @@ -5061,10 +5058,6 @@ class PicklingTests(unittest.TestCase): kwargs = getattr(cls, 'KWARGS', {}) obj = cls(*cls.ARGS, **kwargs) proto = pickle_copier.proto - if 2 <= proto < 4 and hasattr(cls, '__getnewargs_ex__'): - with self.assertRaises(ValueError): - pickle_copier.dumps(obj, proto) - continue objcopy = pickle_copier.copy(obj) self._assert_is_copy(obj, objcopy) # For test classes that supports this, make sure we didn't go diff --git a/Lib/test/test_descrtut.py b/Lib/test/test_descrtut.py index 506d1ab..b84d644 100644 --- a/Lib/test/test_descrtut.py +++ b/Lib/test/test_descrtut.py @@ -182,6 +182,7 @@ You can get the information from the list type: '__iadd__', '__imul__', '__init__', + '__init_subclass__', '__iter__', '__le__', '__len__', diff --git a/Lib/test/test_devpoll.py b/Lib/test/test_devpoll.py index 955618a..c133c81 100644 --- a/Lib/test/test_devpoll.py +++ b/Lib/test/test_devpoll.py @@ -5,9 +5,8 @@ import os import random import select -import sys import unittest -from test.support import TESTFN, run_unittest, cpython_only +from test.support import run_unittest, cpython_only if not hasattr(select, 'devpoll') : raise unittest.SkipTest('test works only on Solaris OS family') diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py index 6d68e76..6c47066 100644 --- a/Lib/test/test_dict.py +++ b/Lib/test/test_dict.py @@ -1,10 +1,12 @@ -import unittest -from test import support - -import collections, random, string +import collections import collections.abc -import gc, weakref +import gc import pickle +import random +import string +import unittest +import weakref +from test import support class DictTest(unittest.TestCase): @@ -969,5 +971,6 @@ class Dict(dict): class SubclassMappingTests(mapping_tests.BasicTestMappingProtocol): type2test = Dict + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_dictcomps.py b/Lib/test/test_dictcomps.py index 3c8b95c..0873071 100644 --- a/Lib/test/test_dictcomps.py +++ b/Lib/test/test_dictcomps.py @@ -1,7 +1,5 @@ import unittest -from test import support - # For scope testing. g = "Global variable" diff --git a/Lib/test/test_dictviews.py b/Lib/test/test_dictviews.py index ab23ca1..4c290ff 100644 --- a/Lib/test/test_dictviews.py +++ b/Lib/test/test_dictviews.py @@ -1,3 +1,4 @@ +import collections import copy import pickle import unittest @@ -219,6 +220,27 @@ class DictSetTest(unittest.TestCase): self.assertRaises((TypeError, pickle.PicklingError), pickle.dumps, d.items(), proto) + def test_abc_registry(self): + d = dict(a=1) + + self.assertIsInstance(d.keys(), collections.KeysView) + self.assertIsInstance(d.keys(), collections.MappingView) + self.assertIsInstance(d.keys(), collections.Set) + self.assertIsInstance(d.keys(), collections.Sized) + self.assertIsInstance(d.keys(), collections.Iterable) + self.assertIsInstance(d.keys(), collections.Container) + + self.assertIsInstance(d.values(), collections.ValuesView) + self.assertIsInstance(d.values(), collections.MappingView) + self.assertIsInstance(d.values(), collections.Sized) + + self.assertIsInstance(d.items(), collections.ItemsView) + self.assertIsInstance(d.items(), collections.MappingView) + self.assertIsInstance(d.items(), collections.Set) + self.assertIsInstance(d.items(), collections.Sized) + self.assertIsInstance(d.items(), collections.Iterable) + self.assertIsInstance(d.items(), collections.Container) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_difflib.py b/Lib/test/test_difflib.py index ab9debf..156b523 100644 --- a/Lib/test/test_difflib.py +++ b/Lib/test/test_difflib.py @@ -122,17 +122,17 @@ patch914575_nonascii_to1 = """ """ patch914575_from2 = """ -\t\tLine 1: preceeded by from:[tt] to:[ssss] - \t\tLine 2: preceeded by from:[sstt] to:[sssst] - \t \tLine 3: preceeded by from:[sstst] to:[ssssss] +\t\tLine 1: preceded by from:[tt] to:[ssss] + \t\tLine 2: preceded by from:[sstt] to:[sssst] + \t \tLine 3: preceded by from:[sstst] to:[ssssss] Line 4: \thas from:[sst] to:[sss] after : Line 5: has from:[t] to:[ss] at end\t """ patch914575_to2 = """ - Line 1: preceeded by from:[tt] to:[ssss] - \tLine 2: preceeded by from:[sstt] to:[sssst] - Line 3: preceeded by from:[sstst] to:[ssssss] + Line 1: preceded by from:[tt] to:[ssss] + \tLine 2: preceded by from:[sstt] to:[sssst] + Line 3: preceded by from:[sstst] to:[ssssss] Line 4: has from:[sst] to:[sss] after : Line 5: has from:[t] to:[ss] at end """ diff --git a/Lib/test/test_difflib_expect.html b/Lib/test/test_difflib_expect.html index ea7a24e..3e6a7b7 100644 --- a/Lib/test/test_difflib_expect.html +++ b/Lib/test/test_difflib_expect.html @@ -387,9 +387,9 @@ <tbody> <tr><td class="diff_next" id="difflib_chg_to9__0"><a href="#difflib_chg_to9__0">f</a></td><td class="diff_header" id="from9_1">1</td><td nowrap="nowrap"></td><td class="diff_next"><a href="#difflib_chg_to9__0">f</a></td><td class="diff_header" id="to9_1">1</td><td nowrap="nowrap"></td></tr> - <tr><td class="diff_next"><a href="#difflib_chg_to9__top">t</a></td><td class="diff_header" id="from9_2">2</td><td nowrap="nowrap"><span class="diff_chg"> </span>Line 1: preceeded by from:[tt] to:[ssss]</td><td class="diff_next"><a href="#difflib_chg_to9__top">t</a></td><td class="diff_header" id="to9_2">2</td><td nowrap="nowrap"><span class="diff_chg"> </span>Line 1: preceeded by from:[tt] to:[ssss]</td></tr> - <tr><td class="diff_next"></td><td class="diff_header" id="from9_3">3</td><td nowrap="nowrap"> <span class="diff_chg"> </span> Line 2: preceeded by from:[sstt] to:[sssst]</td><td class="diff_next"></td><td class="diff_header" id="to9_3">3</td><td nowrap="nowrap"> <span class="diff_chg"> </span> Line 2: preceeded by from:[sstt] to:[sssst]</td></tr> - <tr><td class="diff_next"></td><td class="diff_header" id="from9_4">4</td><td nowrap="nowrap"> <span class="diff_chg"> </span>Line 3: preceeded by from:[sstst] to:[ssssss]</td><td class="diff_next"></td><td class="diff_header" id="to9_4">4</td><td nowrap="nowrap"> <span class="diff_chg"> </span>Line 3: preceeded by from:[sstst] to:[ssssss]</td></tr> + <tr><td class="diff_next"><a href="#difflib_chg_to9__top">t</a></td><td class="diff_header" id="from9_2">2</td><td nowrap="nowrap"><span class="diff_chg"> </span>Line 1: preceded by from:[tt] to:[ssss]</td><td class="diff_next"><a href="#difflib_chg_to9__top">t</a></td><td class="diff_header" id="to9_2">2</td><td nowrap="nowrap"><span class="diff_chg"> </span>Line 1: preceded by from:[tt] to:[ssss]</td></tr> + <tr><td class="diff_next"></td><td class="diff_header" id="from9_3">3</td><td nowrap="nowrap"> <span class="diff_chg"> </span> Line 2: preceded by from:[sstt] to:[sssst]</td><td class="diff_next"></td><td class="diff_header" id="to9_3">3</td><td nowrap="nowrap"> <span class="diff_chg"> </span> Line 2: preceded by from:[sstt] to:[sssst]</td></tr> + <tr><td class="diff_next"></td><td class="diff_header" id="from9_4">4</td><td nowrap="nowrap"> <span class="diff_chg"> </span>Line 3: preceded by from:[sstst] to:[ssssss]</td><td class="diff_next"></td><td class="diff_header" id="to9_4">4</td><td nowrap="nowrap"> <span class="diff_chg"> </span>Line 3: preceded by from:[sstst] to:[ssssss]</td></tr> <tr><td class="diff_next"></td><td class="diff_header" id="from9_5">5</td><td nowrap="nowrap">Line 4: <span class="diff_chg"> </span>has from:[sst] to:[sss] after :</td><td class="diff_next"></td><td class="diff_header" id="to9_5">5</td><td nowrap="nowrap">Line 4: <span class="diff_chg"> </span>has from:[sst] to:[sss] after :</td></tr> <tr><td class="diff_next"></td><td class="diff_header" id="from9_6">6</td><td nowrap="nowrap">Line 5: has from:[t] to:[ss] at end<span class="diff_sub"> </span></td><td class="diff_next"></td><td class="diff_header" id="to9_6">6</td><td nowrap="nowrap">Line 5: has from:[t] to:[ss] at end</td></tr> </tbody> @@ -403,9 +403,9 @@ <tbody> <tr><td class="diff_next" id="difflib_chg_to10__0"><a href="#difflib_chg_to10__0">f</a></td><td class="diff_header" id="from10_1">1</td><td nowrap="nowrap"></td><td class="diff_next"><a href="#difflib_chg_to10__0">f</a></td><td class="diff_header" id="to10_1">1</td><td nowrap="nowrap"></td></tr> - <tr><td class="diff_next"><a href="#difflib_chg_to10__top">t</a></td><td class="diff_header" id="from10_2">2</td><td nowrap="nowrap"><span class="diff_chg"> </span>Line 1: preceeded by from:[tt] to:[ssss]</td><td class="diff_next"><a href="#difflib_chg_to10__top">t</a></td><td class="diff_header" id="to10_2">2</td><td nowrap="nowrap"><span class="diff_chg"> </span>Line 1: preceeded by from:[tt] to:[ssss]</td></tr> - <tr><td class="diff_next"></td><td class="diff_header" id="from10_3">3</td><td nowrap="nowrap"> <span class="diff_chg"> </span> Line 2: preceeded by from:[sstt] to:[sssst]</td><td class="diff_next"></td><td class="diff_header" id="to10_3">3</td><td nowrap="nowrap"> <span class="diff_chg"> </span> Line 2: preceeded by from:[sstt] to:[sssst]</td></tr> - <tr><td class="diff_next"></td><td class="diff_header" id="from10_4">4</td><td nowrap="nowrap"> <span class="diff_chg"> </span>Line 3: preceeded by from:[sstst] to:[ssssss]</td><td class="diff_next"></td><td class="diff_header" id="to10_4">4</td><td nowrap="nowrap"> <span class="diff_chg"> </span>Line 3: preceeded by from:[sstst] to:[ssssss]</td></tr> + <tr><td class="diff_next"><a href="#difflib_chg_to10__top">t</a></td><td class="diff_header" id="from10_2">2</td><td nowrap="nowrap"><span class="diff_chg"> </span>Line 1: preceded by from:[tt] to:[ssss]</td><td class="diff_next"><a href="#difflib_chg_to10__top">t</a></td><td class="diff_header" id="to10_2">2</td><td nowrap="nowrap"><span class="diff_chg"> </span>Line 1: preceded by from:[tt] to:[ssss]</td></tr> + <tr><td class="diff_next"></td><td class="diff_header" id="from10_3">3</td><td nowrap="nowrap"> <span class="diff_chg"> </span> Line 2: preceded by from:[sstt] to:[sssst]</td><td class="diff_next"></td><td class="diff_header" id="to10_3">3</td><td nowrap="nowrap"> <span class="diff_chg"> </span> Line 2: preceded by from:[sstt] to:[sssst]</td></tr> + <tr><td class="diff_next"></td><td class="diff_header" id="from10_4">4</td><td nowrap="nowrap"> <span class="diff_chg"> </span>Line 3: preceded by from:[sstst] to:[ssssss]</td><td class="diff_next"></td><td class="diff_header" id="to10_4">4</td><td nowrap="nowrap"> <span class="diff_chg"> </span>Line 3: preceded by from:[sstst] to:[ssssss]</td></tr> <tr><td class="diff_next"></td><td class="diff_header" id="from10_5">5</td><td nowrap="nowrap">Line 4: <span class="diff_chg"> </span>has from:[sst] to:[sss] after :</td><td class="diff_next"></td><td class="diff_header" id="to10_5">5</td><td nowrap="nowrap">Line 4: <span class="diff_chg"> </span>has from:[sst] to:[sss] after :</td></tr> <tr><td class="diff_next"></td><td class="diff_header" id="from10_6">6</td><td nowrap="nowrap">Line 5: has from:[t] to:[ss] at end<span class="diff_sub"> </span></td><td class="diff_next"></td><td class="diff_header" id="to10_6">6</td><td nowrap="nowrap">Line 5: has from:[t] to:[ss] at end</td></tr> </tbody> diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 0fd1348..09e68ce 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -40,41 +40,41 @@ class _C: dis_c_instance_method = """\ %3d 0 LOAD_FAST 1 (x) - 3 LOAD_CONST 1 (1) - 6 COMPARE_OP 2 (==) - 9 LOAD_FAST 0 (self) - 12 STORE_ATTR 0 (x) - 15 LOAD_CONST 0 (None) - 18 RETURN_VALUE + 2 LOAD_CONST 1 (1) + 4 COMPARE_OP 2 (==) + 6 LOAD_FAST 0 (self) + 8 STORE_ATTR 0 (x) + 10 LOAD_CONST 0 (None) + 12 RETURN_VALUE """ % (_C.__init__.__code__.co_firstlineno + 1,) dis_c_instance_method_bytes = """\ 0 LOAD_FAST 1 (1) - 3 LOAD_CONST 1 (1) - 6 COMPARE_OP 2 (==) - 9 LOAD_FAST 0 (0) - 12 STORE_ATTR 0 (0) - 15 LOAD_CONST 0 (0) - 18 RETURN_VALUE + 2 LOAD_CONST 1 (1) + 4 COMPARE_OP 2 (==) + 6 LOAD_FAST 0 (0) + 8 STORE_ATTR 0 (0) + 10 LOAD_CONST 0 (0) + 12 RETURN_VALUE """ dis_c_class_method = """\ %3d 0 LOAD_FAST 1 (x) - 3 LOAD_CONST 1 (1) - 6 COMPARE_OP 2 (==) - 9 LOAD_FAST 0 (cls) - 12 STORE_ATTR 0 (x) - 15 LOAD_CONST 0 (None) - 18 RETURN_VALUE + 2 LOAD_CONST 1 (1) + 4 COMPARE_OP 2 (==) + 6 LOAD_FAST 0 (cls) + 8 STORE_ATTR 0 (x) + 10 LOAD_CONST 0 (None) + 12 RETURN_VALUE """ % (_C.cm.__code__.co_firstlineno + 2,) dis_c_static_method = """\ %3d 0 LOAD_FAST 0 (x) - 3 LOAD_CONST 1 (1) - 6 COMPARE_OP 2 (==) - 9 STORE_FAST 0 (x) - 12 LOAD_CONST 0 (None) - 15 RETURN_VALUE + 2 LOAD_CONST 1 (1) + 4 COMPARE_OP 2 (==) + 6 STORE_FAST 0 (x) + 8 LOAD_CONST 0 (None) + 10 RETURN_VALUE """ % (_C.sm.__code__.co_firstlineno + 2,) # Class disassembling info has an extra newline at end. @@ -95,23 +95,23 @@ def _f(a): dis_f = """\ %3d 0 LOAD_GLOBAL 0 (print) - 3 LOAD_FAST 0 (a) - 6 CALL_FUNCTION 1 (1 positional, 0 keyword pair) - 9 POP_TOP + 2 LOAD_FAST 0 (a) + 4 CALL_FUNCTION 1 (1 positional, 0 keyword pair) + 6 POP_TOP -%3d 10 LOAD_CONST 1 (1) - 13 RETURN_VALUE +%3d 8 LOAD_CONST 1 (1) + 10 RETURN_VALUE """ % (_f.__code__.co_firstlineno + 1, _f.__code__.co_firstlineno + 2) dis_f_co_code = """\ 0 LOAD_GLOBAL 0 (0) - 3 LOAD_FAST 0 (0) - 6 CALL_FUNCTION 1 (1 positional, 0 keyword pair) - 9 POP_TOP - 10 LOAD_CONST 1 (1) - 13 RETURN_VALUE + 2 LOAD_FAST 0 (0) + 4 CALL_FUNCTION 1 (1 positional, 0 keyword pair) + 6 POP_TOP + 8 LOAD_CONST 1 (1) + 10 RETURN_VALUE """ @@ -121,20 +121,20 @@ def bug708901(): pass dis_bug708901 = """\ -%3d 0 SETUP_LOOP 23 (to 26) - 3 LOAD_GLOBAL 0 (range) - 6 LOAD_CONST 1 (1) - -%3d 9 LOAD_CONST 2 (10) - 12 CALL_FUNCTION 2 (2 positional, 0 keyword pair) - 15 GET_ITER - >> 16 FOR_ITER 6 (to 25) - 19 STORE_FAST 0 (res) - -%3d 22 JUMP_ABSOLUTE 16 - >> 25 POP_BLOCK - >> 26 LOAD_CONST 0 (None) - 29 RETURN_VALUE +%3d 0 SETUP_LOOP 18 (to 20) + 2 LOAD_GLOBAL 0 (range) + 4 LOAD_CONST 1 (1) + +%3d 6 LOAD_CONST 2 (10) + 8 CALL_FUNCTION 2 (2 positional, 0 keyword pair) + 10 GET_ITER + >> 12 FOR_ITER 4 (to 18) + 14 STORE_FAST 0 (res) + +%3d 16 JUMP_ABSOLUTE 12 + >> 18 POP_BLOCK + >> 20 LOAD_CONST 0 (None) + 22 RETURN_VALUE """ % (bug708901.__code__.co_firstlineno + 1, bug708901.__code__.co_firstlineno + 2, bug708901.__code__.co_firstlineno + 3) @@ -147,22 +147,22 @@ def bug1333982(x=[]): dis_bug1333982 = """\ %3d 0 LOAD_CONST 1 (0) - 3 POP_JUMP_IF_TRUE 35 - 6 LOAD_GLOBAL 0 (AssertionError) - 9 LOAD_CONST 2 (<code object <listcomp> at 0x..., file "%s", line %d>) - 12 LOAD_CONST 3 ('bug1333982.<locals>.<listcomp>') - 15 MAKE_FUNCTION 0 - 18 LOAD_FAST 0 (x) - 21 GET_ITER + 2 POP_JUMP_IF_TRUE 26 + 4 LOAD_GLOBAL 0 (AssertionError) + 6 LOAD_CONST 2 (<code object <listcomp> at 0x..., file "%s", line %d>) + 8 LOAD_CONST 3 ('bug1333982.<locals>.<listcomp>') + 10 MAKE_FUNCTION 0 + 12 LOAD_FAST 0 (x) + 14 GET_ITER + 16 CALL_FUNCTION 1 (1 positional, 0 keyword pair) + +%3d 18 LOAD_CONST 4 (1) + 20 BINARY_ADD 22 CALL_FUNCTION 1 (1 positional, 0 keyword pair) + 24 RAISE_VARARGS 1 -%3d 25 LOAD_CONST 4 (1) - 28 BINARY_ADD - 29 CALL_FUNCTION 1 (1 positional, 0 keyword pair) - 32 RAISE_VARARGS 1 - -%3d >> 35 LOAD_CONST 0 (None) - 38 RETURN_VALUE +%3d >> 26 LOAD_CONST 0 (None) + 28 RETURN_VALUE """ % (bug1333982.__code__.co_firstlineno + 1, __file__, bug1333982.__code__.co_firstlineno + 1, @@ -171,19 +171,19 @@ dis_bug1333982 = """\ _BIG_LINENO_FORMAT = """\ %3d 0 LOAD_GLOBAL 0 (spam) - 3 POP_TOP + 2 POP_TOP 4 LOAD_CONST 0 (None) - 7 RETURN_VALUE + 6 RETURN_VALUE """ dis_module_expected_results = """\ Disassembly of f: 4 0 LOAD_CONST 0 (None) - 3 RETURN_VALUE + 2 RETURN_VALUE Disassembly of g: 5 0 LOAD_CONST 0 (None) - 3 RETURN_VALUE + 2 RETURN_VALUE """ @@ -191,20 +191,20 @@ expr_str = "x + 1" dis_expr_str = """\ 1 0 LOAD_NAME 0 (x) - 3 LOAD_CONST 0 (1) - 6 BINARY_ADD - 7 RETURN_VALUE + 2 LOAD_CONST 0 (1) + 4 BINARY_ADD + 6 RETURN_VALUE """ simple_stmt_str = "x = x + 1" dis_simple_stmt_str = """\ 1 0 LOAD_NAME 0 (x) - 3 LOAD_CONST 0 (1) - 6 BINARY_ADD - 7 STORE_NAME 0 (x) - 10 LOAD_CONST 1 (None) - 13 RETURN_VALUE + 2 LOAD_CONST 0 (1) + 4 BINARY_ADD + 6 STORE_NAME 0 (x) + 8 LOAD_CONST 1 (None) + 10 RETURN_VALUE """ compound_stmt_str = """\ @@ -215,54 +215,54 @@ while 1: dis_compound_stmt_str = """\ 1 0 LOAD_CONST 0 (0) - 3 STORE_NAME 0 (x) - - 2 6 SETUP_LOOP 14 (to 23) - - 3 >> 9 LOAD_NAME 0 (x) - 12 LOAD_CONST 1 (1) - 15 INPLACE_ADD - 16 STORE_NAME 0 (x) - 19 JUMP_ABSOLUTE 9 - 22 POP_BLOCK - >> 23 LOAD_CONST 2 (None) - 26 RETURN_VALUE + 2 STORE_NAME 0 (x) + + 2 4 SETUP_LOOP 12 (to 18) + + 3 >> 6 LOAD_NAME 0 (x) + 8 LOAD_CONST 1 (1) + 10 INPLACE_ADD + 12 STORE_NAME 0 (x) + 14 JUMP_ABSOLUTE 6 + 16 POP_BLOCK + >> 18 LOAD_CONST 2 (None) + 20 RETURN_VALUE """ dis_traceback = """\ -%3d 0 SETUP_EXCEPT 12 (to 15) +%3d 0 SETUP_EXCEPT 12 (to 14) -%3d 3 LOAD_CONST 1 (1) - 6 LOAD_CONST 2 (0) - --> 9 BINARY_TRUE_DIVIDE - 10 POP_TOP - 11 POP_BLOCK - 12 JUMP_FORWARD 46 (to 61) +%3d 2 LOAD_CONST 1 (1) + 4 LOAD_CONST 2 (0) + --> 6 BINARY_TRUE_DIVIDE + 8 POP_TOP + 10 POP_BLOCK + 12 JUMP_FORWARD 40 (to 54) -%3d >> 15 DUP_TOP +%3d >> 14 DUP_TOP 16 LOAD_GLOBAL 0 (Exception) - 19 COMPARE_OP 10 (exception match) - 22 POP_JUMP_IF_FALSE 60 - 25 POP_TOP - 26 STORE_FAST 0 (e) - 29 POP_TOP - 30 SETUP_FINALLY 14 (to 47) - -%3d 33 LOAD_FAST 0 (e) - 36 LOAD_ATTR 1 (__traceback__) - 39 STORE_FAST 1 (tb) - 42 POP_BLOCK - 43 POP_EXCEPT - 44 LOAD_CONST 0 (None) - >> 47 LOAD_CONST 0 (None) - 50 STORE_FAST 0 (e) - 53 DELETE_FAST 0 (e) - 56 END_FINALLY - 57 JUMP_FORWARD 1 (to 61) - >> 60 END_FINALLY - -%3d >> 61 LOAD_FAST 1 (tb) - 64 RETURN_VALUE + 18 COMPARE_OP 10 (exception match) + 20 POP_JUMP_IF_FALSE 52 + 22 POP_TOP + 24 STORE_FAST 0 (e) + 26 POP_TOP + 28 SETUP_FINALLY 12 (to 42) + +%3d 30 LOAD_FAST 0 (e) + 32 LOAD_ATTR 1 (__traceback__) + 34 STORE_FAST 1 (tb) + 36 POP_BLOCK + 38 POP_EXCEPT + 40 LOAD_CONST 0 (None) + >> 42 LOAD_CONST 0 (None) + 44 STORE_FAST 0 (e) + 46 DELETE_FAST 0 (e) + 48 END_FINALLY + 50 JUMP_FORWARD 2 (to 54) + >> 52 END_FINALLY + +%3d >> 54 LOAD_FAST 1 (tb) + 56 RETURN_VALUE """ % (TRACEBACK_CODE.co_firstlineno + 1, TRACEBACK_CODE.co_firstlineno + 2, TRACEBACK_CODE.co_firstlineno + 3, @@ -649,171 +649,169 @@ expected_jumpy_line = 1 Instruction = dis.Instruction expected_opinfo_outer = [ - Instruction(opname='LOAD_CONST', opcode=100, arg=1, argval=3, argrepr='3', offset=0, starts_line=2, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=2, argval=4, argrepr='4', offset=3, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CLOSURE', opcode=135, arg=0, argval='a', argrepr='a', offset=6, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CLOSURE', opcode=135, arg=1, argval='b', argrepr='b', offset=9, starts_line=None, is_jump_target=False), - Instruction(opname='BUILD_TUPLE', opcode=102, arg=2, argval=2, argrepr='', offset=12, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=code_object_f, argrepr=repr(code_object_f), offset=15, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='outer.<locals>.f', argrepr="'outer.<locals>.f'", offset=18, starts_line=None, is_jump_target=False), - Instruction(opname='MAKE_CLOSURE', opcode=134, arg=2, argval=2, argrepr='', offset=21, starts_line=None, is_jump_target=False), - Instruction(opname='STORE_FAST', opcode=125, arg=2, argval='f', argrepr='f', offset=24, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=27, starts_line=7, is_jump_target=False), - Instruction(opname='LOAD_DEREF', opcode=136, arg=0, argval='a', argrepr='a', offset=30, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_DEREF', opcode=136, arg=1, argval='b', argrepr='b', offset=33, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval='', argrepr="''", offset=36, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=6, argval=1, argrepr='1', offset=39, starts_line=None, is_jump_target=False), - Instruction(opname='BUILD_LIST', opcode=103, arg=0, argval=0, argrepr='', offset=42, starts_line=None, is_jump_target=False), - Instruction(opname='BUILD_MAP', opcode=105, arg=0, argval=0, argrepr='', offset=45, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=7, argval='Hello world!', argrepr="'Hello world!'", offset=48, starts_line=None, is_jump_target=False), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=7, argval=7, argrepr='7 positional, 0 keyword pair', offset=51, starts_line=None, is_jump_target=False), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=54, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_FAST', opcode=124, arg=2, argval='f', argrepr='f', offset=55, starts_line=8, is_jump_target=False), - Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=58, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=8, argval=(3, 4), argrepr='(3, 4)', offset=0, starts_line=2, is_jump_target=False), + Instruction(opname='LOAD_CLOSURE', opcode=135, arg=0, argval='a', argrepr='a', offset=2, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CLOSURE', opcode=135, arg=1, argval='b', argrepr='b', offset=4, starts_line=None, is_jump_target=False), + Instruction(opname='BUILD_TUPLE', opcode=102, arg=2, argval=2, argrepr='', offset=6, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=code_object_f, argrepr=repr(code_object_f), offset=8, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='outer.<locals>.f', argrepr="'outer.<locals>.f'", offset=10, starts_line=None, is_jump_target=False), + Instruction(opname='MAKE_FUNCTION', opcode=132, arg=9, argval=9, argrepr='', offset=12, starts_line=None, is_jump_target=False), + Instruction(opname='STORE_FAST', opcode=125, arg=2, argval='f', argrepr='f', offset=14, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=16, starts_line=7, is_jump_target=False), + Instruction(opname='LOAD_DEREF', opcode=136, arg=0, argval='a', argrepr='a', offset=18, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_DEREF', opcode=136, arg=1, argval='b', argrepr='b', offset=20, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval='', argrepr="''", offset=22, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=6, argval=1, argrepr='1', offset=24, starts_line=None, is_jump_target=False), + Instruction(opname='BUILD_LIST', opcode=103, arg=0, argval=0, argrepr='', offset=26, starts_line=None, is_jump_target=False), + Instruction(opname='BUILD_MAP', opcode=105, arg=0, argval=0, argrepr='', offset=28, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=7, argval='Hello world!', argrepr="'Hello world!'", offset=30, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=7, argval=7, argrepr='7 positional, 0 keyword pair', offset=32, starts_line=None, is_jump_target=False), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=34, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_FAST', opcode=124, arg=2, argval='f', argrepr='f', offset=36, starts_line=8, is_jump_target=False), + Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=38, starts_line=None, is_jump_target=False), ] expected_opinfo_f = [ - Instruction(opname='LOAD_CONST', opcode=100, arg=1, argval=5, argrepr='5', offset=0, starts_line=3, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=2, argval=6, argrepr='6', offset=3, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CLOSURE', opcode=135, arg=2, argval='a', argrepr='a', offset=6, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CLOSURE', opcode=135, arg=3, argval='b', argrepr='b', offset=9, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CLOSURE', opcode=135, arg=0, argval='c', argrepr='c', offset=12, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CLOSURE', opcode=135, arg=1, argval='d', argrepr='d', offset=15, starts_line=None, is_jump_target=False), - Instruction(opname='BUILD_TUPLE', opcode=102, arg=4, argval=4, argrepr='', offset=18, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=code_object_inner, argrepr=repr(code_object_inner), offset=21, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='outer.<locals>.f.<locals>.inner', argrepr="'outer.<locals>.f.<locals>.inner'", offset=24, starts_line=None, is_jump_target=False), - Instruction(opname='MAKE_CLOSURE', opcode=134, arg=2, argval=2, argrepr='', offset=27, starts_line=None, is_jump_target=False), - Instruction(opname='STORE_FAST', opcode=125, arg=2, argval='inner', argrepr='inner', offset=30, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=33, starts_line=5, is_jump_target=False), - Instruction(opname='LOAD_DEREF', opcode=136, arg=2, argval='a', argrepr='a', offset=36, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_DEREF', opcode=136, arg=3, argval='b', argrepr='b', offset=39, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_DEREF', opcode=136, arg=0, argval='c', argrepr='c', offset=42, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_DEREF', opcode=136, arg=1, argval='d', argrepr='d', offset=45, starts_line=None, is_jump_target=False), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=4, argval=4, argrepr='4 positional, 0 keyword pair', offset=48, starts_line=None, is_jump_target=False), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=51, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_FAST', opcode=124, arg=2, argval='inner', argrepr='inner', offset=52, starts_line=6, is_jump_target=False), - Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=55, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=(5, 6), argrepr='(5, 6)', offset=0, starts_line=3, is_jump_target=False), + Instruction(opname='LOAD_CLOSURE', opcode=135, arg=2, argval='a', argrepr='a', offset=2, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CLOSURE', opcode=135, arg=3, argval='b', argrepr='b', offset=4, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CLOSURE', opcode=135, arg=0, argval='c', argrepr='c', offset=6, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CLOSURE', opcode=135, arg=1, argval='d', argrepr='d', offset=8, starts_line=None, is_jump_target=False), + Instruction(opname='BUILD_TUPLE', opcode=102, arg=4, argval=4, argrepr='', offset=10, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=code_object_inner, argrepr=repr(code_object_inner), offset=12, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='outer.<locals>.f.<locals>.inner', argrepr="'outer.<locals>.f.<locals>.inner'", offset=14, starts_line=None, is_jump_target=False), + Instruction(opname='MAKE_FUNCTION', opcode=132, arg=9, argval=9, argrepr='', offset=16, starts_line=None, is_jump_target=False), + Instruction(opname='STORE_FAST', opcode=125, arg=2, argval='inner', argrepr='inner', offset=18, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=20, starts_line=5, is_jump_target=False), + Instruction(opname='LOAD_DEREF', opcode=136, arg=2, argval='a', argrepr='a', offset=22, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_DEREF', opcode=136, arg=3, argval='b', argrepr='b', offset=24, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_DEREF', opcode=136, arg=0, argval='c', argrepr='c', offset=26, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_DEREF', opcode=136, arg=1, argval='d', argrepr='d', offset=28, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=4, argval=4, argrepr='4 positional, 0 keyword pair', offset=30, starts_line=None, is_jump_target=False), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=32, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_FAST', opcode=124, arg=2, argval='inner', argrepr='inner', offset=34, starts_line=6, is_jump_target=False), + Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=36, starts_line=None, is_jump_target=False), ] expected_opinfo_inner = [ Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=0, starts_line=4, is_jump_target=False), - Instruction(opname='LOAD_DEREF', opcode=136, arg=0, argval='a', argrepr='a', offset=3, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_DEREF', opcode=136, arg=1, argval='b', argrepr='b', offset=6, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_DEREF', opcode=136, arg=2, argval='c', argrepr='c', offset=9, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_DEREF', opcode=136, arg=3, argval='d', argrepr='d', offset=12, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='e', argrepr='e', offset=15, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_FAST', opcode=124, arg=1, argval='f', argrepr='f', offset=18, starts_line=None, is_jump_target=False), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=6, argval=6, argrepr='6 positional, 0 keyword pair', offset=21, starts_line=None, is_jump_target=False), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=24, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=25, starts_line=None, is_jump_target=False), - Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=28, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_DEREF', opcode=136, arg=0, argval='a', argrepr='a', offset=2, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_DEREF', opcode=136, arg=1, argval='b', argrepr='b', offset=4, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_DEREF', opcode=136, arg=2, argval='c', argrepr='c', offset=6, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_DEREF', opcode=136, arg=3, argval='d', argrepr='d', offset=8, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='e', argrepr='e', offset=10, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_FAST', opcode=124, arg=1, argval='f', argrepr='f', offset=12, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=6, argval=6, argrepr='6 positional, 0 keyword pair', offset=14, starts_line=None, is_jump_target=False), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=16, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=18, starts_line=None, is_jump_target=False), + Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=20, starts_line=None, is_jump_target=False), ] expected_opinfo_jumpy = [ - 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=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), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=25, starts_line=None, is_jump_target=False), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=28, starts_line=None, is_jump_target=False), - 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=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='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=73, argval=218, argrepr='to 218', 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_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=183, starts_line=None, is_jump_target=False), - Instruction(opname='JUMP_FORWARD', opcode=110, arg=27, argval=214, argrepr='to 214', 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_START', opcode=81, arg=None, argval=None, argrepr='', offset=211, starts_line=None, is_jump_target=True), - Instruction(opname='WITH_CLEANUP_FINISH', opcode=82, arg=None, argval=None, argrepr='', offset=212, starts_line=None, is_jump_target=False), - Instruction(opname='END_FINALLY', opcode=88, arg=None, argval=None, argrepr='', offset=213, starts_line=None, is_jump_target=False), - Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=214, starts_line=None, is_jump_target=True), - Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=215, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=218, 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=221, starts_line=None, is_jump_target=False), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=224, starts_line=None, is_jump_target=False), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=227, starts_line=None, is_jump_target=False), - Instruction(opname='END_FINALLY', opcode=88, arg=None, argval=None, argrepr='', offset=228, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=229, starts_line=None, is_jump_target=False), - Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=232, starts_line=None, is_jump_target=False), + Instruction(opname='SETUP_LOOP', opcode=120, arg=52, argval=54, argrepr='to 54', offset=0, starts_line=3, is_jump_target=False), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='range', argrepr='range', offset=2, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=1, argval=10, argrepr='10', offset=4, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=6, starts_line=None, is_jump_target=False), + Instruction(opname='GET_ITER', opcode=68, arg=None, argval=None, argrepr='', offset=8, starts_line=None, is_jump_target=False), + Instruction(opname='FOR_ITER', opcode=93, arg=32, argval=44, argrepr='to 44', offset=10, starts_line=None, is_jump_target=True), + Instruction(opname='STORE_FAST', opcode=125, arg=0, argval='i', argrepr='i', offset=12, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=14, starts_line=4, is_jump_target=False), + Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=16, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=18, starts_line=None, is_jump_target=False), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=20, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=22, starts_line=5, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=2, argval=4, argrepr='4', offset=24, starts_line=None, is_jump_target=False), + Instruction(opname='COMPARE_OP', opcode=107, arg=0, argval='<', argrepr='<', offset=26, starts_line=None, is_jump_target=False), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=32, argval=32, argrepr='', offset=28, starts_line=None, is_jump_target=False), + Instruction(opname='JUMP_ABSOLUTE', opcode=113, arg=10, argval=10, argrepr='', offset=30, starts_line=6, is_jump_target=False), + Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=32, starts_line=7, is_jump_target=True), + Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=6, argrepr='6', offset=34, starts_line=None, is_jump_target=False), + Instruction(opname='COMPARE_OP', opcode=107, arg=4, argval='>', argrepr='>', offset=36, starts_line=None, is_jump_target=False), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=10, argval=10, argrepr='', offset=38, starts_line=None, is_jump_target=False), + Instruction(opname='BREAK_LOOP', opcode=80, arg=None, argval=None, argrepr='', offset=40, starts_line=8, is_jump_target=False), + Instruction(opname='JUMP_ABSOLUTE', opcode=113, arg=10, argval=10, argrepr='', offset=42, starts_line=None, is_jump_target=False), + Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=44, starts_line=None, is_jump_target=True), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=46, 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=48, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=50, starts_line=None, is_jump_target=False), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=52, starts_line=None, is_jump_target=False), + Instruction(opname='SETUP_LOOP', opcode=120, arg=52, argval=108, argrepr='to 108', offset=54, starts_line=11, is_jump_target=True), + Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=56, starts_line=None, is_jump_target=True), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=98, argval=98, argrepr='', offset=58, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=60, starts_line=12, is_jump_target=False), + Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=62, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=64, starts_line=None, is_jump_target=False), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=66, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=68, starts_line=13, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=1, argrepr='1', offset=70, starts_line=None, is_jump_target=False), + Instruction(opname='INPLACE_SUBTRACT', opcode=56, arg=None, argval=None, argrepr='', offset=72, starts_line=None, is_jump_target=False), + Instruction(opname='STORE_FAST', opcode=125, arg=0, argval='i', argrepr='i', offset=74, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=76, starts_line=14, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=6, argrepr='6', offset=78, starts_line=None, is_jump_target=False), + Instruction(opname='COMPARE_OP', opcode=107, arg=4, argval='>', argrepr='>', offset=80, starts_line=None, is_jump_target=False), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=86, argval=86, argrepr='', offset=82, starts_line=None, is_jump_target=False), + Instruction(opname='JUMP_ABSOLUTE', opcode=113, arg=56, argval=56, argrepr='', offset=84, starts_line=15, is_jump_target=False), + Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=86, starts_line=16, is_jump_target=True), + Instruction(opname='LOAD_CONST', opcode=100, arg=2, argval=4, argrepr='4', offset=88, starts_line=None, is_jump_target=False), + Instruction(opname='COMPARE_OP', opcode=107, arg=0, argval='<', argrepr='<', offset=90, starts_line=None, is_jump_target=False), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=56, argval=56, argrepr='', offset=92, starts_line=None, is_jump_target=False), + Instruction(opname='BREAK_LOOP', opcode=80, arg=None, argval=None, argrepr='', offset=94, starts_line=17, is_jump_target=False), + Instruction(opname='JUMP_ABSOLUTE', opcode=113, arg=56, argval=56, argrepr='', offset=96, starts_line=None, is_jump_target=False), + Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=98, starts_line=None, is_jump_target=True), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=100, 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=102, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=104, starts_line=None, is_jump_target=False), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=106, starts_line=None, is_jump_target=False), + Instruction(opname='SETUP_FINALLY', opcode=122, arg=70, argval=180, argrepr='to 180', offset=108, starts_line=20, is_jump_target=True), + Instruction(opname='SETUP_EXCEPT', opcode=121, arg=12, argval=124, argrepr='to 124', offset=110, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=1, argrepr='1', offset=112, starts_line=21, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=7, argval=0, argrepr='0', offset=114, starts_line=None, is_jump_target=False), + Instruction(opname='BINARY_TRUE_DIVIDE', opcode=27, arg=None, argval=None, argrepr='', offset=116, starts_line=None, is_jump_target=False), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=118, starts_line=None, is_jump_target=False), + Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=120, starts_line=None, is_jump_target=False), + Instruction(opname='JUMP_FORWARD', opcode=110, arg=28, argval=152, argrepr='to 152', offset=122, starts_line=None, is_jump_target=False), + Instruction(opname='DUP_TOP', opcode=4, arg=None, argval=None, argrepr='', offset=124, starts_line=22, is_jump_target=True), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=2, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=126, starts_line=None, is_jump_target=False), + Instruction(opname='COMPARE_OP', opcode=107, arg=10, argval='exception match', argrepr='exception match', offset=128, starts_line=None, is_jump_target=False), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=150, argval=150, argrepr='', offset=130, starts_line=None, is_jump_target=False), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=132, starts_line=None, is_jump_target=False), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=134, starts_line=None, is_jump_target=False), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=136, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=138, 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=140, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=142, starts_line=None, is_jump_target=False), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=144, starts_line=None, is_jump_target=False), + Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=146, starts_line=None, is_jump_target=False), + Instruction(opname='JUMP_FORWARD', opcode=110, arg=26, argval=176, argrepr='to 176', offset=148, starts_line=None, is_jump_target=False), + Instruction(opname='END_FINALLY', opcode=88, arg=None, argval=None, argrepr='', offset=150, starts_line=None, is_jump_target=True), + Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=152, starts_line=25, is_jump_target=True), + Instruction(opname='SETUP_WITH', opcode=143, arg=14, argval=170, argrepr='to 170', offset=154, starts_line=None, is_jump_target=False), + Instruction(opname='STORE_FAST', opcode=125, arg=1, argval='dodgy', argrepr='dodgy', offset=156, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=158, starts_line=26, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=9, argval='Never reach this', argrepr="'Never reach this'", offset=160, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=162, starts_line=None, is_jump_target=False), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=164, starts_line=None, is_jump_target=False), + Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=166, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=168, starts_line=None, is_jump_target=False), + Instruction(opname='WITH_CLEANUP_START', opcode=81, arg=None, argval=None, argrepr='', offset=170, starts_line=None, is_jump_target=True), + Instruction(opname='WITH_CLEANUP_FINISH', opcode=82, arg=None, argval=None, argrepr='', offset=172, starts_line=None, is_jump_target=False), + Instruction(opname='END_FINALLY', opcode=88, arg=None, argval=None, argrepr='', offset=174, starts_line=None, is_jump_target=False), + Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=176, starts_line=None, is_jump_target=True), + Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=178, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=180, 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=182, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=184, starts_line=None, is_jump_target=False), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=186, starts_line=None, is_jump_target=False), + Instruction(opname='END_FINALLY', opcode=88, arg=None, argval=None, argrepr='', offset=188, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=190, starts_line=None, is_jump_target=False), + Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=192, starts_line=None, is_jump_target=False), ] # One last piece of inspect fodder to check the default line number handling def simple(): pass expected_opinfo_simple = [ Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=0, starts_line=simple.__code__.co_firstlineno, is_jump_target=False), - Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=3, starts_line=None, is_jump_target=False) + Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=2, starts_line=None, is_jump_target=False) ] diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index 33163b9..a9520a5 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -1876,7 +1876,6 @@ if not hasattr(sys, 'gettrace') or not sys.gettrace(): To demonstrate this, we'll create a fake standard input that captures our debugger input: - >>> import tempfile >>> real_stdin = sys.stdin >>> sys.stdin = _FakeInput([ ... 'print(x)', # print data defined by the example @@ -1917,7 +1916,7 @@ if not hasattr(sys, 'gettrace') or not sys.gettrace(): ... finally: ... sys.stdin = real_stdin --Return-- - > <doctest test.test_doctest.test_pdb_set_trace[8]>(3)calls_set_trace()->None + > <doctest test.test_doctest.test_pdb_set_trace[7]>(3)calls_set_trace()->None -> import pdb; pdb.set_trace() (Pdb) print(y) 2 @@ -2798,7 +2797,6 @@ text files). ... _ = f.write(" 'abc def'\n") ... _ = f.write("\n") ... _ = f.write(' \"\"\"\n') - ... import shutil ... rc1, out1, err1 = script_helper.assert_python_failure( ... '-m', 'doctest', fn, fn2) ... rc2, out2, err2 = script_helper.assert_python_ok( @@ -2942,12 +2940,11 @@ Invalid doctest option: def test_main(): # Check the doctest cases in doctest itself: 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) -import sys, re, io - def test_coverage(coverdir): trace = support.import_module('trace') tracer = trace.Trace(ignoredirs=[sys.base_prefix, sys.base_exec_prefix,], diff --git a/Lib/test/test_dynamic.py b/Lib/test/test_dynamic.py index 5080ec9..3ae090f 100644 --- a/Lib/test/test_dynamic.py +++ b/Lib/test/test_dynamic.py @@ -1,7 +1,6 @@ # Test the most dynamic corner cases of Python's runtime semantics. import builtins -import contextlib import unittest from test.support import swap_item, swap_attr diff --git a/Lib/test/test_eintr.py b/Lib/test/test_eintr.py index aabad83..25f86d3 100644 --- a/Lib/test/test_eintr.py +++ b/Lib/test/test_eintr.py @@ -1,7 +1,5 @@ import os import signal -import subprocess -import sys import unittest from test import support @@ -16,14 +14,8 @@ class EINTRTests(unittest.TestCase): # Run the tester in a sub-process, to make sure there is only one # thread (for reliable signal delivery). tester = support.findfile("eintr_tester.py", subdir="eintrdata") - - if support.verbose: - args = [sys.executable, tester] - with subprocess.Popen(args) as proc: - exitcode = proc.wait() - self.assertEqual(exitcode, 0) - else: - script_helper.assert_python_ok(tester) + # use -u to try to get the full output if the test hangs or crash + script_helper.assert_python_ok("-u", tester) if __name__ == "__main__": diff --git a/Lib/test/test_email/__init__.py b/Lib/test/test_email/__init__.py index d2f7d31..1600159 100644 --- a/Lib/test/test_email/__init__.py +++ b/Lib/test/test_email/__init__.py @@ -1,5 +1,4 @@ import os -import sys import unittest import collections import email diff --git a/Lib/test/test_email/test_asian_codecs.py b/Lib/test/test_email/test_asian_codecs.py index f9cd7d9..1e0caee 100644 --- a/Lib/test/test_email/test_asian_codecs.py +++ b/Lib/test/test_email/test_asian_codecs.py @@ -3,7 +3,6 @@ # email package unit tests for (optional) Asian codecs import unittest -from test.support import run_unittest from test.test_email import TestEmailBase from email.charset import Charset diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py index 90fd9e1..8aaca01 100644 --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -723,12 +723,12 @@ class TestMessageAPI(TestEmailBase): # Issue 5871: reject an attempt to embed a header inside a header value # (header injection attack). - def test_embeded_header_via_Header_rejected(self): + def test_embedded_header_via_Header_rejected(self): msg = Message() msg['Dummy'] = Header('dummy\nX-Injected-Header: test') self.assertRaises(errors.HeaderParseError, msg.as_string) - def test_embeded_header_via_string_rejected(self): + def test_embedded_header_via_string_rejected(self): msg = Message() msg['Dummy'] = 'dummy\nX-Injected-Header: test' self.assertRaises(errors.HeaderParseError, msg.as_string) @@ -3421,7 +3421,6 @@ Do you like this message? class TestFeedParsers(TestEmailBase): def parse(self, chunks): - from email.feedparser import FeedParser feedparser = FeedParser() for chunk in chunks: feedparser.feed(chunk) diff --git a/Lib/test/test_email/test_generator.py b/Lib/test/test_email/test_generator.py index b1cbce2..7c8877f 100644 --- a/Lib/test/test_email/test_generator.py +++ b/Lib/test/test_email/test_generator.py @@ -143,7 +143,7 @@ class TestGeneratorBase: def test_set_mangle_from_via_policy(self): source = textwrap.dedent("""\ Subject: test that - from is mangeld in the body! + from is mangled in the body! From time to time I write a rhyme. """) diff --git a/Lib/test/test_email/test_headerregistry.py b/Lib/test/test_email/test_headerregistry.py index 55ecdea..af836dc 100644 --- a/Lib/test/test_email/test_headerregistry.py +++ b/Lib/test/test_email/test_headerregistry.py @@ -1,7 +1,6 @@ import datetime import textwrap import unittest -import types from email import errors from email import policy from email.message import Message diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index 630b155..698fd30 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -3,9 +3,10 @@ import inspect import pydoc import unittest from collections import OrderedDict -from enum import Enum, IntEnum, EnumMeta, unique +from enum import Enum, IntEnum, EnumMeta, Flag, IntFlag, unique from io import StringIO from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL +from test import support # for pickle tests try: @@ -32,6 +33,14 @@ try: except Exception as exc: FloatStooges = exc +try: + class FlagStooges(Flag): + LARRY = 1 + CURLY = 2 + MOE = 3 +except Exception as exc: + FlagStooges = exc + # for pickle test and subclass tests try: class StrEnum(str, Enum): @@ -283,6 +292,28 @@ class TestEnum(unittest.TestCase): class Wrong(Enum): _any_name_ = 9 + def test_bool(self): + # plain Enum members are always True + class Logic(Enum): + true = True + false = False + self.assertTrue(Logic.true) + self.assertTrue(Logic.false) + # unless overridden + class RealLogic(Enum): + true = True + false = False + def __bool__(self): + return bool(self._value_) + self.assertTrue(RealLogic.true) + self.assertFalse(RealLogic.false) + # mixed Enums depend on mixed-in type + class IntLogic(int, Enum): + true = 1 + false = 0 + self.assertTrue(IntLogic.true) + self.assertFalse(IntLogic.false) + def test_contains(self): Season = self.Season self.assertIn(Season.AUTUMN, Season) @@ -1548,6 +1579,580 @@ class TestEnum(unittest.TestCase): self.assertEqual(LabelledList(1), LabelledList.unprocessed) +class TestOrder(unittest.TestCase): + + def test_same_members(self): + class Color(Enum): + _order_ = 'red green blue' + red = 1 + green = 2 + blue = 3 + + def test_same_members_with_aliases(self): + class Color(Enum): + _order_ = 'red green blue' + red = 1 + green = 2 + blue = 3 + verde = green + + def test_same_members_wrong_order(self): + with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): + class Color(Enum): + _order_ = 'red green blue' + red = 1 + blue = 3 + green = 2 + + def test_order_has_extra_members(self): + with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): + class Color(Enum): + _order_ = 'red green blue purple' + red = 1 + green = 2 + blue = 3 + + def test_order_has_extra_members_with_aliases(self): + with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): + class Color(Enum): + _order_ = 'red green blue purple' + red = 1 + green = 2 + blue = 3 + verde = green + + def test_enum_has_extra_members(self): + with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): + class Color(Enum): + _order_ = 'red green blue' + red = 1 + green = 2 + blue = 3 + purple = 4 + + def test_enum_has_extra_members_with_aliases(self): + with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): + class Color(Enum): + _order_ = 'red green blue' + red = 1 + green = 2 + blue = 3 + purple = 4 + verde = green + + +class TestFlag(unittest.TestCase): + """Tests of the Flags.""" + + class Perm(Flag): + R, W, X = 4, 2, 1 + + class Open(Flag): + RO = 0 + WO = 1 + RW = 2 + AC = 3 + CE = 1<<19 + + def test_str(self): + Perm = self.Perm + self.assertEqual(str(Perm.R), 'Perm.R') + self.assertEqual(str(Perm.W), 'Perm.W') + self.assertEqual(str(Perm.X), 'Perm.X') + self.assertEqual(str(Perm.R | Perm.W), 'Perm.R|W') + self.assertEqual(str(Perm.R | Perm.W | Perm.X), 'Perm.R|W|X') + self.assertEqual(str(Perm(0)), 'Perm.0') + self.assertEqual(str(~Perm.R), 'Perm.W|X') + self.assertEqual(str(~Perm.W), 'Perm.R|X') + self.assertEqual(str(~Perm.X), 'Perm.R|W') + self.assertEqual(str(~(Perm.R | Perm.W)), 'Perm.X') + self.assertEqual(str(~(Perm.R | Perm.W | Perm.X)), 'Perm.0') + self.assertEqual(str(Perm(~0)), 'Perm.R|W|X') + + Open = self.Open + self.assertEqual(str(Open.RO), 'Open.RO') + self.assertEqual(str(Open.WO), 'Open.WO') + self.assertEqual(str(Open.AC), 'Open.AC') + self.assertEqual(str(Open.RO | Open.CE), 'Open.CE') + self.assertEqual(str(Open.WO | Open.CE), 'Open.CE|WO') + self.assertEqual(str(~Open.RO), 'Open.CE|AC') + self.assertEqual(str(~Open.WO), 'Open.CE|RW') + self.assertEqual(str(~Open.AC), 'Open.CE') + self.assertEqual(str(~(Open.RO | Open.CE)), 'Open.AC') + self.assertEqual(str(~(Open.WO | Open.CE)), 'Open.RW') + + def test_repr(self): + Perm = self.Perm + self.assertEqual(repr(Perm.R), '<Perm.R: 4>') + self.assertEqual(repr(Perm.W), '<Perm.W: 2>') + self.assertEqual(repr(Perm.X), '<Perm.X: 1>') + self.assertEqual(repr(Perm.R | Perm.W), '<Perm.R|W: 6>') + self.assertEqual(repr(Perm.R | Perm.W | Perm.X), '<Perm.R|W|X: 7>') + self.assertEqual(repr(Perm(0)), '<Perm.0: 0>') + self.assertEqual(repr(~Perm.R), '<Perm.W|X: 3>') + self.assertEqual(repr(~Perm.W), '<Perm.R|X: 5>') + self.assertEqual(repr(~Perm.X), '<Perm.R|W: 6>') + self.assertEqual(repr(~(Perm.R | Perm.W)), '<Perm.X: 1>') + self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '<Perm.0: 0>') + self.assertEqual(repr(Perm(~0)), '<Perm.R|W|X: 7>') + + Open = self.Open + self.assertEqual(repr(Open.RO), '<Open.RO: 0>') + self.assertEqual(repr(Open.WO), '<Open.WO: 1>') + self.assertEqual(repr(Open.AC), '<Open.AC: 3>') + self.assertEqual(repr(Open.RO | Open.CE), '<Open.CE: 524288>') + self.assertEqual(repr(Open.WO | Open.CE), '<Open.CE|WO: 524289>') + self.assertEqual(repr(~Open.RO), '<Open.CE|AC: 524291>') + self.assertEqual(repr(~Open.WO), '<Open.CE|RW: 524290>') + self.assertEqual(repr(~Open.AC), '<Open.CE: 524288>') + self.assertEqual(repr(~(Open.RO | Open.CE)), '<Open.AC: 3>') + self.assertEqual(repr(~(Open.WO | Open.CE)), '<Open.RW: 2>') + + def test_or(self): + Perm = self.Perm + for i in Perm: + for j in Perm: + self.assertEqual((i | j), Perm(i.value | j.value)) + self.assertEqual((i | j).value, i.value | j.value) + self.assertIs(type(i | j), Perm) + for i in Perm: + self.assertIs(i | i, i) + Open = self.Open + self.assertIs(Open.RO | Open.CE, Open.CE) + + def test_and(self): + Perm = self.Perm + RW = Perm.R | Perm.W + RX = Perm.R | Perm.X + WX = Perm.W | Perm.X + RWX = Perm.R | Perm.W | Perm.X + values = list(Perm) + [RW, RX, WX, RWX, Perm(0)] + for i in values: + for j in values: + self.assertEqual((i & j).value, i.value & j.value) + self.assertIs(type(i & j), Perm) + for i in Perm: + self.assertIs(i & i, i) + self.assertIs(i & RWX, i) + self.assertIs(RWX & i, i) + Open = self.Open + self.assertIs(Open.RO & Open.CE, Open.RO) + + def test_xor(self): + Perm = self.Perm + for i in Perm: + for j in Perm: + self.assertEqual((i ^ j).value, i.value ^ j.value) + self.assertIs(type(i ^ j), Perm) + for i in Perm: + self.assertIs(i ^ Perm(0), i) + self.assertIs(Perm(0) ^ i, i) + Open = self.Open + self.assertIs(Open.RO ^ Open.CE, Open.CE) + self.assertIs(Open.CE ^ Open.CE, Open.RO) + + def test_invert(self): + Perm = self.Perm + RW = Perm.R | Perm.W + RX = Perm.R | Perm.X + WX = Perm.W | Perm.X + RWX = Perm.R | Perm.W | Perm.X + values = list(Perm) + [RW, RX, WX, RWX, Perm(0)] + for i in values: + self.assertIs(type(~i), Perm) + self.assertEqual(~~i, i) + for i in Perm: + self.assertIs(~~i, i) + Open = self.Open + self.assertIs(Open.WO & ~Open.WO, Open.RO) + self.assertIs((Open.WO|Open.CE) & ~Open.WO, Open.CE) + + def test_bool(self): + Perm = self.Perm + for f in Perm: + self.assertTrue(f) + Open = self.Open + for f in Open: + self.assertEqual(bool(f.value), bool(f)) + + def test_programatic_function_string(self): + Perm = Flag('Perm', 'R W X') + lst = list(Perm) + self.assertEqual(len(lst), len(Perm)) + self.assertEqual(len(Perm), 3, Perm) + self.assertEqual(lst, [Perm.R, Perm.W, Perm.X]) + for i, n in enumerate('R W X'.split()): + v = 1<<i + e = Perm(v) + self.assertEqual(e.value, v) + self.assertEqual(type(e.value), int) + self.assertEqual(e.name, n) + self.assertIn(e, Perm) + self.assertIs(type(e), Perm) + + def test_programatic_function_string_with_start(self): + Perm = Flag('Perm', 'R W X', start=8) + lst = list(Perm) + self.assertEqual(len(lst), len(Perm)) + self.assertEqual(len(Perm), 3, Perm) + self.assertEqual(lst, [Perm.R, Perm.W, Perm.X]) + for i, n in enumerate('R W X'.split()): + v = 8<<i + e = Perm(v) + self.assertEqual(e.value, v) + self.assertEqual(type(e.value), int) + self.assertEqual(e.name, n) + self.assertIn(e, Perm) + self.assertIs(type(e), Perm) + + def test_programatic_function_string_list(self): + Perm = Flag('Perm', ['R', 'W', 'X']) + lst = list(Perm) + self.assertEqual(len(lst), len(Perm)) + self.assertEqual(len(Perm), 3, Perm) + self.assertEqual(lst, [Perm.R, Perm.W, Perm.X]) + for i, n in enumerate('R W X'.split()): + v = 1<<i + e = Perm(v) + self.assertEqual(e.value, v) + self.assertEqual(type(e.value), int) + self.assertEqual(e.name, n) + self.assertIn(e, Perm) + self.assertIs(type(e), Perm) + + def test_programatic_function_iterable(self): + Perm = Flag('Perm', (('R', 2), ('W', 8), ('X', 32))) + lst = list(Perm) + self.assertEqual(len(lst), len(Perm)) + self.assertEqual(len(Perm), 3, Perm) + self.assertEqual(lst, [Perm.R, Perm.W, Perm.X]) + for i, n in enumerate('R W X'.split()): + v = 1<<(2*i+1) + e = Perm(v) + self.assertEqual(e.value, v) + self.assertEqual(type(e.value), int) + self.assertEqual(e.name, n) + self.assertIn(e, Perm) + self.assertIs(type(e), Perm) + + def test_programatic_function_from_dict(self): + Perm = Flag('Perm', OrderedDict((('R', 2), ('W', 8), ('X', 32)))) + lst = list(Perm) + self.assertEqual(len(lst), len(Perm)) + self.assertEqual(len(Perm), 3, Perm) + self.assertEqual(lst, [Perm.R, Perm.W, Perm.X]) + for i, n in enumerate('R W X'.split()): + v = 1<<(2*i+1) + e = Perm(v) + self.assertEqual(e.value, v) + self.assertEqual(type(e.value), int) + self.assertEqual(e.name, n) + self.assertIn(e, Perm) + self.assertIs(type(e), Perm) + + def test_pickle(self): + if isinstance(FlagStooges, Exception): + raise FlagStooges + test_pickle_dump_load(self.assertIs, FlagStooges.CURLY|FlagStooges.MOE) + test_pickle_dump_load(self.assertIs, FlagStooges) + + + def test_containment(self): + Perm = self.Perm + R, W, X = Perm + RW = R | W + RX = R | X + WX = W | X + RWX = R | W | X + self.assertTrue(R in RW) + self.assertTrue(R in RX) + self.assertTrue(R in RWX) + self.assertTrue(W in RW) + self.assertTrue(W in WX) + self.assertTrue(W in RWX) + self.assertTrue(X in RX) + self.assertTrue(X in WX) + self.assertTrue(X in RWX) + self.assertFalse(R in WX) + self.assertFalse(W in RX) + self.assertFalse(X in RW) + +class TestIntFlag(unittest.TestCase): + """Tests of the IntFlags.""" + + class Perm(IntFlag): + X = 1 << 0 + W = 1 << 1 + R = 1 << 2 + + class Open(IntFlag): + RO = 0 + WO = 1 + RW = 2 + AC = 3 + CE = 1<<19 + + def test_str(self): + Perm = self.Perm + self.assertEqual(str(Perm.R), 'Perm.R') + self.assertEqual(str(Perm.W), 'Perm.W') + self.assertEqual(str(Perm.X), 'Perm.X') + self.assertEqual(str(Perm.R | Perm.W), 'Perm.R|W') + self.assertEqual(str(Perm.R | Perm.W | Perm.X), 'Perm.R|W|X') + self.assertEqual(str(Perm.R | 8), 'Perm.8|R') + self.assertEqual(str(Perm(0)), 'Perm.0') + self.assertEqual(str(Perm(8)), 'Perm.8') + self.assertEqual(str(~Perm.R), 'Perm.W|X|-8') + self.assertEqual(str(~Perm.W), 'Perm.R|X|-8') + self.assertEqual(str(~Perm.X), 'Perm.R|W|-8') + self.assertEqual(str(~(Perm.R | Perm.W)), 'Perm.X|-8') + self.assertEqual(str(~(Perm.R | Perm.W | Perm.X)), 'Perm.-8') + self.assertEqual(str(~(Perm.R | 8)), 'Perm.W|X|-16') + self.assertEqual(str(Perm(~0)), 'Perm.R|W|X|-8') + self.assertEqual(str(Perm(~8)), 'Perm.R|W|X|-16') + + Open = self.Open + self.assertEqual(str(Open.RO), 'Open.RO') + self.assertEqual(str(Open.WO), 'Open.WO') + self.assertEqual(str(Open.AC), 'Open.AC') + self.assertEqual(str(Open.RO | Open.CE), 'Open.CE') + self.assertEqual(str(Open.WO | Open.CE), 'Open.CE|WO') + self.assertEqual(str(Open(4)), 'Open.4') + self.assertEqual(str(~Open.RO), 'Open.CE|AC|-524292') + self.assertEqual(str(~Open.WO), 'Open.CE|RW|-524292') + self.assertEqual(str(~Open.AC), 'Open.CE|-524292') + self.assertEqual(str(~(Open.RO | Open.CE)), 'Open.AC|-524292') + self.assertEqual(str(~(Open.WO | Open.CE)), 'Open.RW|-524292') + self.assertEqual(str(Open(~4)), 'Open.CE|AC|-524296') + + def test_repr(self): + Perm = self.Perm + self.assertEqual(repr(Perm.R), '<Perm.R: 4>') + self.assertEqual(repr(Perm.W), '<Perm.W: 2>') + self.assertEqual(repr(Perm.X), '<Perm.X: 1>') + self.assertEqual(repr(Perm.R | Perm.W), '<Perm.R|W: 6>') + self.assertEqual(repr(Perm.R | Perm.W | Perm.X), '<Perm.R|W|X: 7>') + self.assertEqual(repr(Perm.R | 8), '<Perm.8|R: 12>') + self.assertEqual(repr(Perm(0)), '<Perm.0: 0>') + self.assertEqual(repr(Perm(8)), '<Perm.8: 8>') + self.assertEqual(repr(~Perm.R), '<Perm.W|X|-8: -5>') + self.assertEqual(repr(~Perm.W), '<Perm.R|X|-8: -3>') + self.assertEqual(repr(~Perm.X), '<Perm.R|W|-8: -2>') + self.assertEqual(repr(~(Perm.R | Perm.W)), '<Perm.X|-8: -7>') + self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '<Perm.-8: -8>') + self.assertEqual(repr(~(Perm.R | 8)), '<Perm.W|X|-16: -13>') + self.assertEqual(repr(Perm(~0)), '<Perm.R|W|X|-8: -1>') + self.assertEqual(repr(Perm(~8)), '<Perm.R|W|X|-16: -9>') + + Open = self.Open + self.assertEqual(repr(Open.RO), '<Open.RO: 0>') + self.assertEqual(repr(Open.WO), '<Open.WO: 1>') + self.assertEqual(repr(Open.AC), '<Open.AC: 3>') + self.assertEqual(repr(Open.RO | Open.CE), '<Open.CE: 524288>') + self.assertEqual(repr(Open.WO | Open.CE), '<Open.CE|WO: 524289>') + self.assertEqual(repr(Open(4)), '<Open.4: 4>') + self.assertEqual(repr(~Open.RO), '<Open.CE|AC|-524292: -1>') + self.assertEqual(repr(~Open.WO), '<Open.CE|RW|-524292: -2>') + self.assertEqual(repr(~Open.AC), '<Open.CE|-524292: -4>') + self.assertEqual(repr(~(Open.RO | Open.CE)), '<Open.AC|-524292: -524289>') + self.assertEqual(repr(~(Open.WO | Open.CE)), '<Open.RW|-524292: -524290>') + self.assertEqual(repr(Open(~4)), '<Open.CE|AC|-524296: -5>') + + def test_or(self): + Perm = self.Perm + for i in Perm: + for j in Perm: + self.assertEqual(i | j, i.value | j.value) + self.assertEqual((i | j).value, i.value | j.value) + self.assertIs(type(i | j), Perm) + for j in range(8): + self.assertEqual(i | j, i.value | j) + self.assertEqual((i | j).value, i.value | j) + self.assertIs(type(i | j), Perm) + self.assertEqual(j | i, j | i.value) + self.assertEqual((j | i).value, j | i.value) + self.assertIs(type(j | i), Perm) + for i in Perm: + self.assertIs(i | i, i) + self.assertIs(i | 0, i) + self.assertIs(0 | i, i) + Open = self.Open + self.assertIs(Open.RO | Open.CE, Open.CE) + + def test_and(self): + Perm = self.Perm + RW = Perm.R | Perm.W + RX = Perm.R | Perm.X + WX = Perm.W | Perm.X + RWX = Perm.R | Perm.W | Perm.X + values = list(Perm) + [RW, RX, WX, RWX, Perm(0)] + for i in values: + for j in values: + self.assertEqual(i & j, i.value & j.value, 'i is %r, j is %r' % (i, j)) + self.assertEqual((i & j).value, i.value & j.value, 'i is %r, j is %r' % (i, j)) + self.assertIs(type(i & j), Perm, 'i is %r, j is %r' % (i, j)) + for j in range(8): + self.assertEqual(i & j, i.value & j) + self.assertEqual((i & j).value, i.value & j) + self.assertIs(type(i & j), Perm) + self.assertEqual(j & i, j & i.value) + self.assertEqual((j & i).value, j & i.value) + self.assertIs(type(j & i), Perm) + for i in Perm: + self.assertIs(i & i, i) + self.assertIs(i & 7, i) + self.assertIs(7 & i, i) + Open = self.Open + self.assertIs(Open.RO & Open.CE, Open.RO) + + def test_xor(self): + Perm = self.Perm + for i in Perm: + for j in Perm: + self.assertEqual(i ^ j, i.value ^ j.value) + self.assertEqual((i ^ j).value, i.value ^ j.value) + self.assertIs(type(i ^ j), Perm) + for j in range(8): + self.assertEqual(i ^ j, i.value ^ j) + self.assertEqual((i ^ j).value, i.value ^ j) + self.assertIs(type(i ^ j), Perm) + self.assertEqual(j ^ i, j ^ i.value) + self.assertEqual((j ^ i).value, j ^ i.value) + self.assertIs(type(j ^ i), Perm) + for i in Perm: + self.assertIs(i ^ 0, i) + self.assertIs(0 ^ i, i) + Open = self.Open + self.assertIs(Open.RO ^ Open.CE, Open.CE) + self.assertIs(Open.CE ^ Open.CE, Open.RO) + + def test_invert(self): + Perm = self.Perm + RW = Perm.R | Perm.W + RX = Perm.R | Perm.X + WX = Perm.W | Perm.X + RWX = Perm.R | Perm.W | Perm.X + values = list(Perm) + [RW, RX, WX, RWX, Perm(0)] + for i in values: + self.assertEqual(~i, ~i.value) + self.assertEqual((~i).value, ~i.value) + self.assertIs(type(~i), Perm) + self.assertEqual(~~i, i) + for i in Perm: + self.assertIs(~~i, i) + Open = self.Open + self.assertIs(Open.WO & ~Open.WO, Open.RO) + self.assertIs((Open.WO|Open.CE) & ~Open.WO, Open.CE) + + def test_programatic_function_string(self): + Perm = IntFlag('Perm', 'R W X') + lst = list(Perm) + self.assertEqual(len(lst), len(Perm)) + self.assertEqual(len(Perm), 3, Perm) + self.assertEqual(lst, [Perm.R, Perm.W, Perm.X]) + for i, n in enumerate('R W X'.split()): + v = 1<<i + e = Perm(v) + self.assertEqual(e.value, v) + self.assertEqual(type(e.value), int) + self.assertEqual(e, v) + self.assertEqual(e.name, n) + self.assertIn(e, Perm) + self.assertIs(type(e), Perm) + + def test_programatic_function_string_with_start(self): + Perm = IntFlag('Perm', 'R W X', start=8) + lst = list(Perm) + self.assertEqual(len(lst), len(Perm)) + self.assertEqual(len(Perm), 3, Perm) + self.assertEqual(lst, [Perm.R, Perm.W, Perm.X]) + for i, n in enumerate('R W X'.split()): + v = 8<<i + e = Perm(v) + self.assertEqual(e.value, v) + self.assertEqual(type(e.value), int) + self.assertEqual(e, v) + self.assertEqual(e.name, n) + self.assertIn(e, Perm) + self.assertIs(type(e), Perm) + + def test_programatic_function_string_list(self): + Perm = IntFlag('Perm', ['R', 'W', 'X']) + lst = list(Perm) + self.assertEqual(len(lst), len(Perm)) + self.assertEqual(len(Perm), 3, Perm) + self.assertEqual(lst, [Perm.R, Perm.W, Perm.X]) + for i, n in enumerate('R W X'.split()): + v = 1<<i + e = Perm(v) + self.assertEqual(e.value, v) + self.assertEqual(type(e.value), int) + self.assertEqual(e, v) + self.assertEqual(e.name, n) + self.assertIn(e, Perm) + self.assertIs(type(e), Perm) + + def test_programatic_function_iterable(self): + Perm = IntFlag('Perm', (('R', 2), ('W', 8), ('X', 32))) + lst = list(Perm) + self.assertEqual(len(lst), len(Perm)) + self.assertEqual(len(Perm), 3, Perm) + self.assertEqual(lst, [Perm.R, Perm.W, Perm.X]) + for i, n in enumerate('R W X'.split()): + v = 1<<(2*i+1) + e = Perm(v) + self.assertEqual(e.value, v) + self.assertEqual(type(e.value), int) + self.assertEqual(e, v) + self.assertEqual(e.name, n) + self.assertIn(e, Perm) + self.assertIs(type(e), Perm) + + def test_programatic_function_from_dict(self): + Perm = IntFlag('Perm', OrderedDict((('R', 2), ('W', 8), ('X', 32)))) + lst = list(Perm) + self.assertEqual(len(lst), len(Perm)) + self.assertEqual(len(Perm), 3, Perm) + self.assertEqual(lst, [Perm.R, Perm.W, Perm.X]) + for i, n in enumerate('R W X'.split()): + v = 1<<(2*i+1) + e = Perm(v) + self.assertEqual(e.value, v) + self.assertEqual(type(e.value), int) + self.assertEqual(e, v) + self.assertEqual(e.name, n) + self.assertIn(e, Perm) + self.assertIs(type(e), Perm) + + + def test_containment(self): + Perm = self.Perm + R, W, X = Perm + RW = R | W + RX = R | X + WX = W | X + RWX = R | W | X + self.assertTrue(R in RW) + self.assertTrue(R in RX) + self.assertTrue(R in RWX) + self.assertTrue(W in RW) + self.assertTrue(W in WX) + self.assertTrue(W in RWX) + self.assertTrue(X in RX) + self.assertTrue(X in WX) + self.assertTrue(X in RWX) + self.assertFalse(R in WX) + self.assertFalse(W in RX) + self.assertFalse(X in RW) + + def test_bool(self): + Perm = self.Perm + for f in Perm: + self.assertTrue(f) + Open = self.Open + for f in Open: + self.assertEqual(bool(f.value), bool(f)) + class TestUnique(unittest.TestCase): def test_unique_clean(self): @@ -1739,5 +2344,47 @@ class TestStdLib(unittest.TestCase): if failed: self.fail("result does not equal expected, see print above") + +class MiscTestCase(unittest.TestCase): + def test__all__(self): + support.check__all__(self, enum) + + +# These are unordered here on purpose to ensure that declaration order +# makes no difference. +CONVERT_TEST_NAME_D = 5 +CONVERT_TEST_NAME_C = 5 +CONVERT_TEST_NAME_B = 5 +CONVERT_TEST_NAME_A = 5 # This one should sort first. +CONVERT_TEST_NAME_E = 5 +CONVERT_TEST_NAME_F = 5 + +class TestIntEnumConvert(unittest.TestCase): + def test_convert_value_lookup_priority(self): + test_type = enum.IntEnum._convert( + 'UnittestConvert', 'test.test_enum', + filter=lambda x: x.startswith('CONVERT_TEST_')) + # We don't want the reverse lookup value to vary when there are + # multiple possible names for a given value. It should always + # report the first lexigraphical name in that case. + self.assertEqual(test_type(5).name, 'CONVERT_TEST_NAME_A') + + def test_convert(self): + test_type = enum.IntEnum._convert( + 'UnittestConvert', 'test.test_enum', + filter=lambda x: x.startswith('CONVERT_TEST_')) + # Ensure that test_type has all of the desired names and values. + self.assertEqual(test_type.CONVERT_TEST_NAME_F, + test_type.CONVERT_TEST_NAME_A) + self.assertEqual(test_type.CONVERT_TEST_NAME_B, 5) + self.assertEqual(test_type.CONVERT_TEST_NAME_C, 5) + self.assertEqual(test_type.CONVERT_TEST_NAME_D, 5) + self.assertEqual(test_type.CONVERT_TEST_NAME_E, 5) + # Ensure that test_type only picked up names matching the filter. + self.assertEqual([name for name in dir(test_type) + if name[0:2] not in ('CO', '__')], + [], msg='Names other than CONVERT_TEST_* found.') + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_enumerate.py b/Lib/test/test_enumerate.py index 2630cf2..e455ade 100644 --- a/Lib/test/test_enumerate.py +++ b/Lib/test/test_enumerate.py @@ -223,7 +223,7 @@ class TestReversed(unittest.TestCase, PickleTest): def test_objmethods(self): # Objects must have __len__() and __getitem__() implemented. class NoLen(object): - def __getitem__(self): return 1 + def __getitem__(self, i): return 1 nl = NoLen() self.assertRaises(TypeError, reversed, nl) @@ -232,6 +232,13 @@ class TestReversed(unittest.TestCase, PickleTest): ngi = NoGetItem() self.assertRaises(TypeError, reversed, ngi) + class Blocked(object): + def __getitem__(self, i): return 1 + def __len__(self): return 2 + __reversed__ = None + b = Blocked() + self.assertRaises(TypeError, reversed, b) + def test_pickle(self): for data in 'abc', range(5), tuple(enumerate('abc')), range(1,17,5): self.check_pickle(reversed(data), list(data)[::-1]) diff --git a/Lib/test/test_epoll.py b/Lib/test/test_epoll.py index a7359e9..a7aff8a 100644 --- a/Lib/test/test_epoll.py +++ b/Lib/test/test_epoll.py @@ -28,7 +28,6 @@ import socket import time import unittest -from test import support if not hasattr(select, "epoll"): raise unittest.SkipTest("test works only on Linux 2.6") diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index 1562eec..1ff17bb 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -23,6 +23,7 @@ except ImportError: _testcapi = None TIMEOUT = 0.5 +MS_WINDOWS = (os.name == 'nt') def expected_traceback(lineno1, lineno2, header, min_count=1): regex = header @@ -58,8 +59,9 @@ class FaultHandlerTests(unittest.TestCase): pass_fds.append(fd) with support.SuppressCrashReport(): process = script_helper.spawn_python('-c', code, pass_fds=pass_fds) - stdout, stderr = process.communicate() - exitcode = process.wait() + with process: + stdout, stderr = process.communicate() + exitcode = process.wait() output = support.strip_python_stderr(stdout) output = output.decode('ascii', 'backslashreplace') if filename: @@ -73,14 +75,11 @@ class FaultHandlerTests(unittest.TestCase): with open(fd, "rb", closefd=False) as fp: output = fp.read() output = output.decode('ascii', 'backslashreplace') - output = re.sub('Current thread 0x[0-9a-f]+', - 'Current thread XXX', - output) return output.splitlines(), exitcode - def check_fatal_error(self, code, line_number, name_regex, - filename=None, all_threads=True, other_regex=None, - fd=None): + def check_error(self, code, line_number, fatal_error, *, + filename=None, all_threads=True, other_regex=None, + fd=None, know_current_thread=True): """ Check that the fault handler for fatal errors is enabled and check the traceback from the child process output. @@ -88,19 +87,22 @@ class FaultHandlerTests(unittest.TestCase): Raise an error if the output doesn't match the expected format. """ if all_threads: - header = 'Current thread XXX (most recent call first)' + if know_current_thread: + header = 'Current thread 0x[0-9a-f]+' + else: + header = 'Thread 0x[0-9a-f]+' else: - header = 'Stack (most recent call first)' + header = 'Stack' regex = """ - ^Fatal Python error: {name} + ^{fatal_error} - {header}: + {header} \(most recent call first\): File "<string>", line {lineno} in <module> """ regex = dedent(regex.format( lineno=line_number, - name=name_regex, - header=re.escape(header))).strip() + fatal_error=fatal_error, + header=header)).strip() if other_regex: regex += '|' + other_regex output, exitcode = self.get_output(code, filename=filename, fd=fd) @@ -108,26 +110,56 @@ class FaultHandlerTests(unittest.TestCase): self.assertRegex(output, regex) self.assertNotEqual(exitcode, 0) + def check_fatal_error(self, code, line_number, name_regex, **kw): + fatal_error = 'Fatal Python error: %s' % name_regex + self.check_error(code, line_number, fatal_error, **kw) + + def check_windows_exception(self, code, line_number, name_regex, **kw): + fatal_error = 'Windows fatal exception: %s' % name_regex + self.check_error(code, line_number, fatal_error, **kw) + @unittest.skipIf(sys.platform.startswith('aix'), "the first page of memory is a mapped read-only on AIX") def test_read_null(self): + if not MS_WINDOWS: + self.check_fatal_error(""" + import faulthandler + faulthandler.enable() + faulthandler._read_null() + """, + 3, + # Issue #12700: Read NULL raises SIGILL on Mac OS X Lion + '(?:Segmentation fault' + '|Bus error' + '|Illegal instruction)') + else: + self.check_windows_exception(""" + import faulthandler + faulthandler.enable() + faulthandler._read_null() + """, + 3, + 'access violation') + + def test_sigsegv(self): self.check_fatal_error(""" import faulthandler faulthandler.enable() - faulthandler._read_null() + faulthandler._sigsegv() """, 3, - # Issue #12700: Read NULL raises SIGILL on Mac OS X Lion - '(?:Segmentation fault|Bus error|Illegal instruction)') + 'Segmentation fault') - def test_sigsegv(self): + @unittest.skipIf(not HAVE_THREADS, 'need threads') + def test_fatal_error_c_thread(self): self.check_fatal_error(""" import faulthandler faulthandler.enable() - faulthandler._sigsegv() + faulthandler._fatal_error_c_thread() """, 3, - 'Segmentation fault') + 'in new thread', + know_current_thread=False) def test_sigabrt(self): self.check_fatal_error(""" @@ -465,7 +497,7 @@ class FaultHandlerTests(unittest.TestCase): File ".*threading.py", line [0-9]+ in _bootstrap_inner File ".*threading.py", line [0-9]+ in _bootstrap - Current thread XXX \(most recent call first\): + Current thread 0x[0-9a-f]+ \(most recent call first\): File "<string>", line {lineno} in dump File "<string>", line 28 in <module>$ """ @@ -637,7 +669,7 @@ class FaultHandlerTests(unittest.TestCase): trace = '\n'.join(trace) if not unregister: if all_threads: - regex = 'Current thread XXX \(most recent call first\):\n' + regex = 'Current thread 0x[0-9a-f]+ \(most recent call first\):\n' else: regex = 'Stack \(most recent call first\):\n' regex = expected_traceback(14, 32, regex) @@ -696,6 +728,22 @@ class FaultHandlerTests(unittest.TestCase): with self.check_stderr_none(): faulthandler.register(signal.SIGUSR1) + @unittest.skipUnless(MS_WINDOWS, 'specific to Windows') + def test_raise_exception(self): + for exc, name in ( + ('EXCEPTION_ACCESS_VIOLATION', 'access violation'), + ('EXCEPTION_INT_DIVIDE_BY_ZERO', 'int divide by zero'), + ('EXCEPTION_STACK_OVERFLOW', 'stack overflow'), + ): + self.check_windows_exception(""" + import faulthandler + faulthandler.enable() + faulthandler._raise_exception(faulthandler._{exc}) + """.format(exc=exc), + 3, + name) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_file.py b/Lib/test/test_file.py index 4e392b7..65be30f 100644 --- a/Lib/test/test_file.py +++ b/Lib/test/test_file.py @@ -7,7 +7,7 @@ from weakref import proxy import io import _pyio as pyio -from test.support import TESTFN, run_unittest +from test.support import TESTFN from collections import UserList class AutoFileTests: @@ -139,7 +139,7 @@ class OtherFileTests: def testModeStrings(self): # check invalid mode strings - for mode in ("", "aU", "wU+"): + for mode in ("", "aU", "wU+", "U+", "+U", "rU+"): try: f = self.open(TESTFN, mode) except ValueError: diff --git a/Lib/test/test_fileinput.py b/Lib/test/test_fileinput.py index 784bc92..565633f 100644 --- a/Lib/test/test_fileinput.py +++ b/Lib/test/test_fileinput.py @@ -22,8 +22,9 @@ except ImportError: from io import BytesIO, StringIO from fileinput import FileInput, hook_encoded -from test.support import verbose, TESTFN, run_unittest, check_warnings +from test.support import verbose, TESTFN, check_warnings from test.support import unlink as safe_unlink +from test import support from unittest import mock @@ -92,7 +93,11 @@ class BufferSizesTests(unittest.TestCase): t2 = writeTmp(2, ["Line %s of file 2\n" % (i+1) for i in range(10)]) t3 = writeTmp(3, ["Line %s of file 3\n" % (i+1) for i in range(5)]) t4 = writeTmp(4, ["Line %s of file 4\n" % (i+1) for i in range(1)]) - self.buffer_size_test(t1, t2, t3, t4, bs, round) + if bs: + with self.assertWarns(DeprecationWarning): + self.buffer_size_test(t1, t2, t3, t4, bs, round) + else: + self.buffer_size_test(t1, t2, t3, t4, bs, round) finally: remove_tempfiles(t1, t2, t3, t4) @@ -940,7 +945,8 @@ class Test_hook_encoded(unittest.TestCase): def test(self): encoding = object() - result = fileinput.hook_encoded(encoding) + errors = object() + result = fileinput.hook_encoded(encoding, errors=errors) fake_open = InvocationRecorder() original_open = builtins.open @@ -958,8 +964,26 @@ class Test_hook_encoded(unittest.TestCase): self.assertIs(args[0], filename) self.assertIs(args[1], mode) self.assertIs(kwargs.pop('encoding'), encoding) + self.assertIs(kwargs.pop('errors'), errors) self.assertFalse(kwargs) + def test_errors(self): + with open(TESTFN, 'wb') as f: + f.write(b'\x80abc') + self.addCleanup(safe_unlink, TESTFN) + + def check(errors, expected_lines): + with FileInput(files=TESTFN, mode='r', + openhook=hook_encoded('utf-8', errors=errors)) as fi: + lines = list(fi) + self.assertEqual(lines, expected_lines) + + check('ignore', ['abc']) + with self.assertRaises(UnicodeDecodeError): + check('strict', ['abc']) + check('replace', ['\ufffdabc']) + check('backslashreplace', ['\\x80abc']) + def test_modes(self): with open(TESTFN, 'wb') as f: # UTF-7 is a convenient, seldom used encoding @@ -981,5 +1005,11 @@ class Test_hook_encoded(unittest.TestCase): check('rb', ['A\n', 'B\r\n', 'C\r', 'D\u20ac']) +class MiscTest(unittest.TestCase): + + def test_all(self): + support.check__all__(self, fileinput) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py index cb1f6db..68b212e 100644 --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -1,6 +1,5 @@ import fractions -import math import operator import os import random @@ -162,11 +161,12 @@ class GeneralFloatCases(unittest.TestCase): def __float__(self): return float(str(self)) + 1 - self.assertAlmostEqual(float(Foo1()), 42.) - self.assertAlmostEqual(float(Foo2()), 42.) - self.assertAlmostEqual(float(Foo3(21)), 42.) + self.assertEqual(float(Foo1()), 42.) + self.assertEqual(float(Foo2()), 42.) + with self.assertWarns(DeprecationWarning): + self.assertEqual(float(Foo3(21)), 42.) self.assertRaises(TypeError, float, Foo4(42)) - self.assertAlmostEqual(float(FooStr('8')), 9.) + self.assertEqual(float(FooStr('8')), 9.) class Foo5: def __float__(self): @@ -177,10 +177,14 @@ class GeneralFloatCases(unittest.TestCase): class F: def __float__(self): return OtherFloatSubclass(42.) - self.assertAlmostEqual(float(F()), 42.) - self.assertIs(type(float(F())), OtherFloatSubclass) - self.assertAlmostEqual(FloatSubclass(F()), 42.) - self.assertIs(type(FloatSubclass(F())), FloatSubclass) + with self.assertWarns(DeprecationWarning): + self.assertEqual(float(F()), 42.) + with self.assertWarns(DeprecationWarning): + self.assertIs(type(float(F())), float) + with self.assertWarns(DeprecationWarning): + self.assertEqual(FloatSubclass(F()), 42.) + with self.assertWarns(DeprecationWarning): + self.assertIs(type(FloatSubclass(F())), FloatSubclass) def test_is_integer(self): self.assertFalse((1.1).is_integer()) diff --git a/Lib/test/test_format.py b/Lib/test/test_format.py index 9b13632..8afd5b8 100644 --- a/Lib/test/test_format.py +++ b/Lib/test/test_format.py @@ -274,7 +274,7 @@ class FormatTest(unittest.TestCase): test_exc('%d', '1', TypeError, "%d format: a number is required, not str") test_exc('%x', '1', TypeError, "%x format: an integer is required, not str") test_exc('%x', 3.14, TypeError, "%x format: an integer is required, not float") - test_exc('%g', '1', TypeError, "a float is required") + test_exc('%g', '1', TypeError, "must be real number, not str") test_exc('no format', '1', TypeError, "not all arguments converted during string formatting") test_exc('%c', -1, OverflowError, "%c arg not in range(0x110000)") @@ -300,6 +300,8 @@ class FormatTest(unittest.TestCase): testcommon(b"%c", 7, b"\x07") testcommon(b"%c", b"Z", b"Z") testcommon(b"%c", bytearray(b"Z"), b"Z") + testcommon(b"%5c", 65, b" A") + testcommon(b"%-5c", 65, b"A ") # %b will insert a series of bytes, either from a type that supports # the Py_buffer protocol, or something that has a __bytes__ method class FakeBytes(object): diff --git a/Lib/test/test_fractions.py b/Lib/test/test_fractions.py index 9df4a54..7905c36 100644 --- a/Lib/test/test_fractions.py +++ b/Lib/test/test_fractions.py @@ -150,6 +150,7 @@ class FractionTest(unittest.TestCase): self.assertRaises(TypeError, F, "3/2", 3) self.assertRaises(TypeError, F, 3, 0j) self.assertRaises(TypeError, F, 3, 1j) + self.assertRaises(TypeError, F, 1, 2, 3) @requires_IEEE_754 def testInitFromFloat(self): @@ -263,13 +264,13 @@ class FractionTest(unittest.TestCase): nan = inf - inf # bug 16469: error types should be consistent with float -> int self.assertRaisesMessage( - OverflowError, "Cannot convert inf to Fraction.", + OverflowError, "cannot convert Infinity to integer ratio", F.from_float, inf) self.assertRaisesMessage( - OverflowError, "Cannot convert -inf to Fraction.", + OverflowError, "cannot convert Infinity to integer ratio", F.from_float, -inf) self.assertRaisesMessage( - ValueError, "Cannot convert nan to Fraction.", + ValueError, "cannot convert NaN to integer ratio", F.from_float, nan) def testFromDecimal(self): @@ -284,16 +285,16 @@ class FractionTest(unittest.TestCase): # bug 16469: error types should be consistent with decimal -> int self.assertRaisesMessage( - OverflowError, "Cannot convert Infinity to Fraction.", + OverflowError, "cannot convert Infinity to integer ratio", F.from_decimal, Decimal("inf")) self.assertRaisesMessage( - OverflowError, "Cannot convert -Infinity to Fraction.", + OverflowError, "cannot convert Infinity to integer ratio", F.from_decimal, Decimal("-inf")) self.assertRaisesMessage( - ValueError, "Cannot convert NaN to Fraction.", + ValueError, "cannot convert NaN to integer ratio", F.from_decimal, Decimal("nan")) self.assertRaisesMessage( - ValueError, "Cannot convert sNaN to Fraction.", + ValueError, "cannot convert NaN to integer ratio", F.from_decimal, Decimal("snan")) def testLimitDenominator(self): diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py new file mode 100644 index 0000000..2ba1b21 --- /dev/null +++ b/Lib/test/test_fstring.py @@ -0,0 +1,705 @@ +import ast +import types +import decimal +import unittest + +a_global = 'global variable' + +# You could argue that I'm too strict in looking for specific error +# values with assertRaisesRegex, but without it it's way too easy to +# make a syntax error in the test strings. Especially with all of the +# triple quotes, raw strings, backslashes, etc. I think it's a +# worthwhile tradeoff. When I switched to this method, I found many +# examples where I wasn't testing what I thought I was. + +class TestCase(unittest.TestCase): + def assertAllRaise(self, exception_type, regex, error_strings): + for str in error_strings: + with self.subTest(str=str): + with self.assertRaisesRegex(exception_type, regex): + eval(str) + + def test__format__lookup(self): + # Make sure __format__ is looked up on the type, not the instance. + class X: + def __format__(self, spec): + return 'class' + + x = X() + + # Add a bound __format__ method to the 'y' instance, but not + # the 'x' instance. + y = X() + y.__format__ = types.MethodType(lambda self, spec: 'instance', y) + + self.assertEqual(f'{y}', format(y)) + self.assertEqual(f'{y}', 'class') + self.assertEqual(format(x), format(y)) + + # __format__ is not called this way, but still make sure it + # returns what we expect (so we can make sure we're bypassing + # it). + self.assertEqual(x.__format__(''), 'class') + self.assertEqual(y.__format__(''), 'instance') + + # This is how __format__ is actually called. + self.assertEqual(type(x).__format__(x, ''), 'class') + self.assertEqual(type(y).__format__(y, ''), 'class') + + def test_ast(self): + # Inspired by http://bugs.python.org/issue24975 + class X: + def __init__(self): + self.called = False + def __call__(self): + self.called = True + return 4 + x = X() + expr = """ +a = 10 +f'{a * x()}'""" + t = ast.parse(expr) + c = compile(t, '', 'exec') + + # Make sure x was not called. + self.assertFalse(x.called) + + # Actually run the code. + exec(c) + + # Make sure x was called. + self.assertTrue(x.called) + + def test_literal_eval(self): + # With no expressions, an f-string is okay. + self.assertEqual(ast.literal_eval("f'x'"), 'x') + self.assertEqual(ast.literal_eval("f'x' 'y'"), 'xy') + + # But this should raise an error. + with self.assertRaisesRegex(ValueError, 'malformed node or string'): + ast.literal_eval("f'x{3}'") + + # As should this, which uses a different ast node + with self.assertRaisesRegex(ValueError, 'malformed node or string'): + ast.literal_eval("f'{3}'") + + def test_ast_compile_time_concat(self): + x = [''] + + expr = """x[0] = 'foo' f'{3}'""" + t = ast.parse(expr) + c = compile(t, '', 'exec') + exec(c) + self.assertEqual(x[0], 'foo3') + + def test_literal(self): + self.assertEqual(f'', '') + self.assertEqual(f'a', 'a') + self.assertEqual(f' ', ' ') + + def test_unterminated_string(self): + self.assertAllRaise(SyntaxError, 'f-string: unterminated string', + [r"""f'{"x'""", + r"""f'{"x}'""", + r"""f'{("x'""", + r"""f'{("x}'""", + ]) + + def test_mismatched_parens(self): + self.assertAllRaise(SyntaxError, 'f-string: mismatched', + ["f'{((}'", + ]) + + def test_double_braces(self): + self.assertEqual(f'{{', '{') + self.assertEqual(f'a{{', 'a{') + self.assertEqual(f'{{b', '{b') + self.assertEqual(f'a{{b', 'a{b') + self.assertEqual(f'}}', '}') + self.assertEqual(f'a}}', 'a}') + self.assertEqual(f'}}b', '}b') + self.assertEqual(f'a}}b', 'a}b') + + self.assertEqual(f'{{{10}', '{10') + self.assertEqual(f'}}{10}', '}10') + self.assertEqual(f'}}{{{10}', '}{10') + self.assertEqual(f'}}a{{{10}', '}a{10') + + self.assertEqual(f'{10}{{', '10{') + self.assertEqual(f'{10}}}', '10}') + self.assertEqual(f'{10}}}{{', '10}{') + self.assertEqual(f'{10}}}a{{' '}', '10}a{}') + + # Inside of strings, don't interpret doubled brackets. + self.assertEqual(f'{"{{}}"}', '{{}}') + + self.assertAllRaise(TypeError, 'unhashable type', + ["f'{ {{}} }'", # dict in a set + ]) + + def test_compile_time_concat(self): + x = 'def' + self.assertEqual('abc' f'## {x}ghi', 'abc## defghi') + self.assertEqual('abc' f'{x}' 'ghi', 'abcdefghi') + self.assertEqual('abc' f'{x}' 'gh' f'i{x:4}', 'abcdefghidef ') + self.assertEqual('{x}' f'{x}', '{x}def') + self.assertEqual('{x' f'{x}', '{xdef') + self.assertEqual('{x}' f'{x}', '{x}def') + self.assertEqual('{{x}}' f'{x}', '{{x}}def') + self.assertEqual('{{x' f'{x}', '{{xdef') + self.assertEqual('x}}' f'{x}', 'x}}def') + self.assertEqual(f'{x}' 'x}}', 'defx}}') + self.assertEqual(f'{x}' '', 'def') + self.assertEqual('' f'{x}' '', 'def') + self.assertEqual('' f'{x}', 'def') + self.assertEqual(f'{x}' '2', 'def2') + self.assertEqual('1' f'{x}' '2', '1def2') + self.assertEqual('1' f'{x}', '1def') + self.assertEqual(f'{x}' f'-{x}', 'def-def') + self.assertEqual('' f'', '') + self.assertEqual('' f'' '', '') + self.assertEqual('' f'' '' f'', '') + self.assertEqual(f'', '') + self.assertEqual(f'' '', '') + self.assertEqual(f'' '' f'', '') + self.assertEqual(f'' '' f'' '', '') + + self.assertAllRaise(SyntaxError, "f-string: expecting '}'", + ["f'{3' f'}'", # can't concat to get a valid f-string + ]) + + def test_comments(self): + # These aren't comments, since they're in strings. + d = {'#': 'hash'} + self.assertEqual(f'{"#"}', '#') + self.assertEqual(f'{d["#"]}', 'hash') + + self.assertAllRaise(SyntaxError, "f-string cannot include '#'", + ["f'{1#}'", # error because the expression becomes "(1#)" + "f'{3(#)}'", + ]) + + def test_many_expressions(self): + # Create a string with many expressions in it. Note that + # because we have a space in here as a literal, we're actually + # going to use twice as many ast nodes: one for each literal + # plus one for each expression. + def build_fstr(n, extra=''): + return "f'" + ('{x} ' * n) + extra + "'" + + x = 'X' + width = 1 + + # Test around 256. + for i in range(250, 260): + self.assertEqual(eval(build_fstr(i)), (x+' ')*i) + + # Test concatenating 2 largs fstrings. + self.assertEqual(eval(build_fstr(255)*256), (x+' ')*(255*256)) + + s = build_fstr(253, '{x:{width}} ') + self.assertEqual(eval(s), (x+' ')*254) + + # Test lots of expressions and constants, concatenated. + s = "f'{1}' 'x' 'y'" * 1024 + self.assertEqual(eval(s), '1xy' * 1024) + + def test_format_specifier_expressions(self): + width = 10 + precision = 4 + value = decimal.Decimal('12.34567') + self.assertEqual(f'result: {value:{width}.{precision}}', 'result: 12.35') + self.assertEqual(f'result: {value:{width!r}.{precision}}', 'result: 12.35') + self.assertEqual(f'result: {value:{width:0}.{precision:1}}', 'result: 12.35') + self.assertEqual(f'result: {value:{1}{0:0}.{precision:1}}', 'result: 12.35') + self.assertEqual(f'result: {value:{ 1}{ 0:0}.{ precision:1}}', 'result: 12.35') + self.assertEqual(f'{10:#{1}0x}', ' 0xa') + self.assertEqual(f'{10:{"#"}1{0}{"x"}}', ' 0xa') + self.assertEqual(f'{-10:-{"#"}1{0}x}', ' -0xa') + self.assertEqual(f'{-10:{"-"}#{1}0{"x"}}', ' -0xa') + self.assertEqual(f'{10:#{3 != {4:5} and width}x}', ' 0xa') + + self.assertAllRaise(SyntaxError, "f-string: expecting '}'", + ["""f'{"s"!r{":10"}}'""", + + # This looks like a nested format spec. + ]) + + self.assertAllRaise(SyntaxError, "invalid syntax", + [# Invalid syntax inside a nested spec. + "f'{4:{/5}}'", + ]) + + self.assertAllRaise(SyntaxError, "f-string: expressions nested too deeply", + [# Can't nest format specifiers. + "f'result: {value:{width:{0}}.{precision:1}}'", + ]) + + self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character', + [# No expansion inside conversion or for + # the : or ! itself. + """f'{"s"!{"r"}}'""", + ]) + + def test_side_effect_order(self): + class X: + def __init__(self): + self.i = 0 + def __format__(self, spec): + self.i += 1 + return str(self.i) + + x = X() + self.assertEqual(f'{x} {x}', '1 2') + + def test_missing_expression(self): + self.assertAllRaise(SyntaxError, 'f-string: empty expression not allowed', + ["f'{}'", + "f'{ }'" + "f' {} '", + "f'{!r}'", + "f'{ !r}'", + "f'{10:{ }}'", + "f' { } '", + + # Catch the empty expression before the + # invalid conversion. + "f'{!x}'", + "f'{ !xr}'", + "f'{!x:}'", + "f'{!x:a}'", + "f'{ !xr:}'", + "f'{ !xr:a}'", + + "f'{!}'", + "f'{:}'", + + # We find the empty expression before the + # missing closing brace. + "f'{!'", + "f'{!s:'", + "f'{:'", + "f'{:x'", + ]) + + def test_parens_in_expressions(self): + self.assertEqual(f'{3,}', '(3,)') + + # Add these because when an expression is evaluated, parens + # are added around it. But we shouldn't go from an invalid + # expression to a valid one. The added parens are just + # supposed to allow whitespace (including newlines). + self.assertAllRaise(SyntaxError, 'invalid syntax', + ["f'{,}'", + "f'{,}'", # this is (,), which is an error + ]) + + self.assertAllRaise(SyntaxError, "f-string: expecting '}'", + ["f'{3)+(4}'", + ]) + + self.assertAllRaise(SyntaxError, 'EOL while scanning string literal', + ["f'{\n}'", + ]) + + def test_no_backslashes(self): + # See issue 27921 + + # These should work, but currently don't + self.assertAllRaise(SyntaxError, 'backslashes not allowed', + [r"f'\t'", + r"f'{2}\t'", + r"f'{2}\t{3}'", + r"f'\t{3}'", + + r"f'\N{GREEK CAPITAL LETTER DELTA}'", + r"f'{2}\N{GREEK CAPITAL LETTER DELTA}'", + r"f'{2}\N{GREEK CAPITAL LETTER DELTA}{3}'", + r"f'\N{GREEK CAPITAL LETTER DELTA}{3}'", + + r"f'\u0394'", + r"f'{2}\u0394'", + r"f'{2}\u0394{3}'", + r"f'\u0394{3}'", + + r"f'\U00000394'", + r"f'{2}\U00000394'", + r"f'{2}\U00000394{3}'", + r"f'\U00000394{3}'", + + r"f'\x20'", + r"f'{2}\x20'", + r"f'{2}\x20{3}'", + r"f'\x20{3}'", + + r"f'2\x20'", + r"f'2\x203'", + r"f'2\x203'", + ]) + + # And these don't work now, and shouldn't work in the future. + self.assertAllRaise(SyntaxError, 'backslashes not allowed', + [r"f'{\'a\'}'", + r"f'{\t3}'", + ]) + + # add this when backslashes are allowed again. see issue 27921 + # these test will be needed because unicode names will be parsed + # differently once backslashes are allowed inside expressions + ## def test_misformed_unicode_character_name(self): + ## self.assertAllRaise(SyntaxError, 'xx', + ## [r"f'\N'", + ## [r"f'\N{'", + ## [r"f'\N{GREEK CAPITAL LETTER DELTA'", + ## ]) + + def test_newlines_in_expressions(self): + self.assertEqual(f'{0}', '0') + self.assertEqual(rf'''{3+ +4}''', '7') + + def test_lambda(self): + x = 5 + self.assertEqual(f'{(lambda y:x*y)("8")!r}', "'88888'") + self.assertEqual(f'{(lambda y:x*y)("8")!r:10}', "'88888' ") + self.assertEqual(f'{(lambda y:x*y)("8"):10}', "88888 ") + + # lambda doesn't work without parens, because the colon + # makes the parser think it's a format_spec + self.assertAllRaise(SyntaxError, 'unexpected EOF while parsing', + ["f'{lambda x:x}'", + ]) + + def test_yield(self): + # Not terribly useful, but make sure the yield turns + # a function into a generator + def fn(y): + f'y:{yield y*2}' + + g = fn(4) + self.assertEqual(next(g), 8) + + def test_yield_send(self): + def fn(x): + yield f'x:{yield (lambda i: x * i)}' + + g = fn(10) + the_lambda = next(g) + self.assertEqual(the_lambda(4), 40) + self.assertEqual(g.send('string'), 'x:string') + + def test_expressions_with_triple_quoted_strings(self): + self.assertEqual(f"{'''x'''}", 'x') + self.assertEqual(f"{'''eric's'''}", "eric's") + + # Test concatenation within an expression + self.assertEqual(f'{"x" """eric"s""" "y"}', 'xeric"sy') + self.assertEqual(f'{"x" """eric"s"""}', 'xeric"s') + self.assertEqual(f'{"""eric"s""" "y"}', 'eric"sy') + self.assertEqual(f'{"""x""" """eric"s""" "y"}', 'xeric"sy') + self.assertEqual(f'{"""x""" """eric"s""" """y"""}', 'xeric"sy') + self.assertEqual(f'{r"""x""" """eric"s""" """y"""}', 'xeric"sy') + + def test_multiple_vars(self): + x = 98 + y = 'abc' + self.assertEqual(f'{x}{y}', '98abc') + + self.assertEqual(f'X{x}{y}', 'X98abc') + self.assertEqual(f'{x}X{y}', '98Xabc') + self.assertEqual(f'{x}{y}X', '98abcX') + + self.assertEqual(f'X{x}Y{y}', 'X98Yabc') + self.assertEqual(f'X{x}{y}Y', 'X98abcY') + self.assertEqual(f'{x}X{y}Y', '98XabcY') + + self.assertEqual(f'X{x}Y{y}Z', 'X98YabcZ') + + def test_closure(self): + def outer(x): + def inner(): + return f'x:{x}' + return inner + + self.assertEqual(outer('987')(), 'x:987') + self.assertEqual(outer(7)(), 'x:7') + + def test_arguments(self): + y = 2 + def f(x, width): + return f'x={x*y:{width}}' + + self.assertEqual(f('foo', 10), 'x=foofoo ') + x = 'bar' + self.assertEqual(f(10, 10), 'x= 20') + + def test_locals(self): + value = 123 + self.assertEqual(f'v:{value}', 'v:123') + + def test_missing_variable(self): + with self.assertRaises(NameError): + f'v:{value}' + + def test_missing_format_spec(self): + class O: + def __format__(self, spec): + if not spec: + return '*' + return spec + + self.assertEqual(f'{O():x}', 'x') + self.assertEqual(f'{O()}', '*') + self.assertEqual(f'{O():}', '*') + + self.assertEqual(f'{3:}', '3') + self.assertEqual(f'{3!s:}', '3') + + def test_global(self): + self.assertEqual(f'g:{a_global}', 'g:global variable') + self.assertEqual(f'g:{a_global!r}', "g:'global variable'") + + a_local = 'local variable' + self.assertEqual(f'g:{a_global} l:{a_local}', + 'g:global variable l:local variable') + self.assertEqual(f'g:{a_global!r}', + "g:'global variable'") + self.assertEqual(f'g:{a_global} l:{a_local!r}', + "g:global variable l:'local variable'") + + self.assertIn("module 'unittest' from", f'{unittest}') + + def test_shadowed_global(self): + a_global = 'really a local' + self.assertEqual(f'g:{a_global}', 'g:really a local') + self.assertEqual(f'g:{a_global!r}', "g:'really a local'") + + a_local = 'local variable' + self.assertEqual(f'g:{a_global} l:{a_local}', + 'g:really a local l:local variable') + self.assertEqual(f'g:{a_global!r}', + "g:'really a local'") + self.assertEqual(f'g:{a_global} l:{a_local!r}', + "g:really a local l:'local variable'") + + def test_call(self): + def foo(x): + return 'x=' + str(x) + + self.assertEqual(f'{foo(10)}', 'x=10') + + def test_nested_fstrings(self): + y = 5 + self.assertEqual(f'{f"{0}"*3}', '000') + self.assertEqual(f'{f"{y}"*3}', '555') + + def test_invalid_string_prefixes(self): + self.assertAllRaise(SyntaxError, 'unexpected EOF while parsing', + ["fu''", + "uf''", + "Fu''", + "fU''", + "Uf''", + "uF''", + "ufr''", + "urf''", + "fur''", + "fru''", + "rfu''", + "ruf''", + "FUR''", + "Fur''", + ]) + + def test_leading_trailing_spaces(self): + self.assertEqual(f'{ 3}', '3') + self.assertEqual(f'{ 3}', '3') + self.assertEqual(f'{3 }', '3') + self.assertEqual(f'{3 }', '3') + + self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]}}', + 'expr={1: 2}') + self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]} }', + 'expr={1: 2}') + + def test_not_equal(self): + # There's a special test for this because there's a special + # case in the f-string parser to look for != as not ending an + # expression. Normally it would, while looking for !s or !r. + + self.assertEqual(f'{3!=4}', 'True') + self.assertEqual(f'{3!=4:}', 'True') + self.assertEqual(f'{3!=4!s}', 'True') + self.assertEqual(f'{3!=4!s:.3}', 'Tru') + + def test_conversions(self): + self.assertEqual(f'{3.14:10.10}', ' 3.14') + self.assertEqual(f'{3.14!s:10.10}', '3.14 ') + self.assertEqual(f'{3.14!r:10.10}', '3.14 ') + self.assertEqual(f'{3.14!a:10.10}', '3.14 ') + + self.assertEqual(f'{"a"}', 'a') + self.assertEqual(f'{"a"!r}', "'a'") + self.assertEqual(f'{"a"!a}', "'a'") + + # Not a conversion. + self.assertEqual(f'{"a!r"}', "a!r") + + # Not a conversion, but show that ! is allowed in a format spec. + self.assertEqual(f'{3.14:!<10.10}', '3.14!!!!!!') + + self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character', + ["f'{3!g}'", + "f'{3!A}'", + "f'{3!A}'", + "f'{3!A}'", + "f'{3!!}'", + "f'{3!:}'", + "f'{3! s}'", # no space before conversion char + ]) + + self.assertAllRaise(SyntaxError, "f-string: expecting '}'", + ["f'{x!s{y}}'", + "f'{3!ss}'", + "f'{3!ss:}'", + "f'{3!ss:s}'", + ]) + + def test_assignment(self): + self.assertAllRaise(SyntaxError, 'invalid syntax', + ["f'' = 3", + "f'{0}' = x", + "f'{x}' = x", + ]) + + def test_del(self): + self.assertAllRaise(SyntaxError, 'invalid syntax', + ["del f''", + "del '' f''", + ]) + + def test_mismatched_braces(self): + self.assertAllRaise(SyntaxError, "f-string: single '}' is not allowed", + ["f'{{}'", + "f'{{}}}'", + "f'}'", + "f'x}'", + "f'x}x'", + + # Can't have { or } in a format spec. + "f'{3:}>10}'", + "f'{3:}}>10}'", + ]) + + self.assertAllRaise(SyntaxError, "f-string: expecting '}'", + ["f'{3:{{>10}'", + "f'{3'", + "f'{3!'", + "f'{3:'", + "f'{3!s'", + "f'{3!s:'", + "f'{3!s:3'", + "f'x{'", + "f'x{x'", + "f'{3:s'", + "f'{{{'", + "f'{{}}{'", + "f'{'", + ]) + + # But these are just normal strings. + self.assertEqual(f'{"{"}', '{') + self.assertEqual(f'{"}"}', '}') + self.assertEqual(f'{3:{"}"}>10}', '}}}}}}}}}3') + self.assertEqual(f'{2:{"{"}>10}', '{{{{{{{{{2') + + def test_if_conditional(self): + # There's special logic in compile.c to test if the + # conditional for an if (and while) are constants. Exercise + # that code. + + def test_fstring(x, expected): + flag = 0 + if f'{x}': + flag = 1 + else: + flag = 2 + self.assertEqual(flag, expected) + + def test_concat_empty(x, expected): + flag = 0 + if '' f'{x}': + flag = 1 + else: + flag = 2 + self.assertEqual(flag, expected) + + def test_concat_non_empty(x, expected): + flag = 0 + if ' ' f'{x}': + flag = 1 + else: + flag = 2 + self.assertEqual(flag, expected) + + test_fstring('', 2) + test_fstring(' ', 1) + + test_concat_empty('', 2) + test_concat_empty(' ', 1) + + test_concat_non_empty('', 1) + test_concat_non_empty(' ', 1) + + def test_empty_format_specifier(self): + x = 'test' + self.assertEqual(f'{x}', 'test') + self.assertEqual(f'{x:}', 'test') + self.assertEqual(f'{x!s:}', 'test') + self.assertEqual(f'{x!r:}', "'test'") + + def test_str_format_differences(self): + d = {'a': 'string', + 0: 'integer', + } + a = 0 + self.assertEqual(f'{d[0]}', 'integer') + self.assertEqual(f'{d["a"]}', 'string') + self.assertEqual(f'{d[a]}', 'integer') + self.assertEqual('{d[a]}'.format(d=d), 'string') + self.assertEqual('{d[0]}'.format(d=d), 'integer') + + def test_invalid_expressions(self): + self.assertAllRaise(SyntaxError, 'invalid syntax', + [r"f'{a[4)}'", + r"f'{a(4]}'", + ]) + + def test_errors(self): + # see issue 26287 + self.assertAllRaise(TypeError, 'non-empty', + [r"f'{(lambda: 0):x}'", + r"f'{(0,):x}'", + ]) + self.assertAllRaise(ValueError, 'Unknown format code', + [r"f'{1000:j}'", + r"f'{1000:j}'", + ]) + + def test_loop(self): + for i in range(1000): + self.assertEqual(f'i:{i}', 'i:' + str(i)) + + def test_dict(self): + d = {'"': 'dquote', + "'": 'squote', + 'foo': 'bar', + } + self.assertEqual(f'''{d["'"]}''', 'squote') + self.assertEqual(f"""{d['"']}""", 'dquote') + + self.assertEqual(f'{d["foo"]}', 'bar') + self.assertEqual(f"{d['foo']}", 'bar') + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py index aef66da..9d8de21 100644 --- a/Lib/test/test_ftplib.py +++ b/Lib/test/test_ftplib.py @@ -1049,10 +1049,19 @@ class TestTimeouts(TestCase): ftp.close() +class MiscTestCase(TestCase): + def test__all__(self): + blacklist = {'MSG_OOB', 'FTP_PORT', 'MAXLINE', 'CRLF', 'B_CRLF', + 'Error', 'parse150', 'parse227', 'parse229', 'parse257', + 'print_line', 'ftpcp', 'test'} + support.check__all__(self, ftplib, blacklist=blacklist) + + def test_main(): tests = [TestFTPClass, TestTimeouts, TestIPv6Environment, - TestTLS_FTPClassMixin, TestTLS_FTPClass] + TestTLS_FTPClassMixin, TestTLS_FTPClass, + MiscTestCase] thread_info = support.threading_setup() try: diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index ab51a35..40f2234 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -1548,13 +1548,15 @@ class TestSingleDispatch(unittest.TestCase): bases = [c.Sequence, c.MutableMapping, c.Mapping, c.Set] for haystack in permutations(bases): m = mro(dict, haystack) - self.assertEqual(m, [dict, c.MutableMapping, c.Mapping, c.Sized, - c.Iterable, c.Container, object]) + self.assertEqual(m, [dict, c.MutableMapping, c.Mapping, + c.Collection, c.Sized, c.Iterable, + c.Container, object]) bases = [c.Container, c.Mapping, c.MutableMapping, c.OrderedDict] for haystack in permutations(bases): m = mro(c.ChainMap, haystack) self.assertEqual(m, [c.ChainMap, c.MutableMapping, c.Mapping, - c.Sized, c.Iterable, c.Container, object]) + c.Collection, c.Sized, c.Iterable, + c.Container, object]) # If there's a generic function with implementations registered for # both Sized and Container, passing a defaultdict to it results in an @@ -1575,9 +1577,9 @@ class TestSingleDispatch(unittest.TestCase): bases = [c.MutableSequence, c.MutableMapping] for haystack in permutations(bases): m = mro(D, bases) - self.assertEqual(m, [D, c.MutableSequence, c.Sequence, - c.defaultdict, dict, c.MutableMapping, - c.Mapping, c.Sized, c.Iterable, c.Container, + self.assertEqual(m, [D, c.MutableSequence, c.Sequence, c.Reversible, + c.defaultdict, dict, c.MutableMapping, c.Mapping, + c.Collection, c.Sized, c.Iterable, c.Container, object]) # Container and Callable are registered on different base classes and @@ -1590,7 +1592,8 @@ class TestSingleDispatch(unittest.TestCase): for haystack in permutations(bases): m = mro(C, haystack) self.assertEqual(m, [C, c.Callable, c.defaultdict, dict, c.Mapping, - c.Sized, c.Iterable, c.Container, object]) + c.Collection, c.Sized, c.Iterable, + c.Container, object]) def test_register_abc(self): c = collections diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py index a4d684b..e727499 100644 --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py @@ -684,7 +684,6 @@ class GCTests(unittest.TestCase): # Create a reference cycle through the __main__ module and check # it gets collected at interpreter shutdown. code = """if 1: - import weakref class C: def __del__(self): print('__del__ called') @@ -699,7 +698,6 @@ class GCTests(unittest.TestCase): # Same as above, but with a non-__main__ module. with temp_dir() as script_dir: module = """if 1: - import weakref class C: def __del__(self): print('__del__ called') diff --git a/Lib/test/test_gdb.py b/Lib/test/test_gdb.py index 3db10b6..09bafbd 100644 --- a/Lib/test/test_gdb.py +++ b/Lib/test/test_gdb.py @@ -5,7 +5,6 @@ import os import re -import pprint import subprocess import sys import sysconfig @@ -177,6 +176,7 @@ class DebuggerTests(unittest.TestCase): args = ['--eval-command=%s' % cmd for cmd in commands] args += ["--args", sys.executable] + args.extend(subprocess._args_from_interpreter_flags()) if not import_site: # -S suppresses the default 'import site' @@ -292,7 +292,9 @@ class PrettyPrintTests(DebuggerTests): 'Verify the pretty-printing of dictionaries' self.assertGdbRepr({}) self.assertGdbRepr({'foo': 'bar'}, "{'foo': 'bar'}") - self.assertGdbRepr({'foo': 'bar', 'douglas': 42}, "{'douglas': 42, 'foo': 'bar'}") + # PYTHONHASHSEED is need to get the exact item order + if not sys.flags.ignore_environment: + self.assertGdbRepr({'foo': 'bar', 'douglas': 42}, "{'douglas': 42, 'foo': 'bar'}") def test_lists(self): 'Verify the pretty-printing of lists' @@ -355,9 +357,12 @@ class PrettyPrintTests(DebuggerTests): 'Verify the pretty-printing of sets' if (gdb_major_version, gdb_minor_version) < (7, 3): self.skipTest("pretty-printing of sets needs gdb 7.3 or later") - self.assertGdbRepr(set(), 'set()') - self.assertGdbRepr(set(['a', 'b']), "{'a', 'b'}") - self.assertGdbRepr(set([4, 5, 6]), "{4, 5, 6}") + self.assertGdbRepr(set(), "set()") + self.assertGdbRepr(set(['a']), "{'a'}") + # PYTHONHASHSEED is need to get the exact frozenset item order + if not sys.flags.ignore_environment: + self.assertGdbRepr(set(['a', 'b']), "{'a', 'b'}") + self.assertGdbRepr(set([4, 5, 6]), "{4, 5, 6}") # Ensure that we handle sets containing the "dummy" key value, # which happens on deletion: @@ -370,9 +375,12 @@ id(s)''') 'Verify the pretty-printing of frozensets' if (gdb_major_version, gdb_minor_version) < (7, 3): self.skipTest("pretty-printing of frozensets needs gdb 7.3 or later") - self.assertGdbRepr(frozenset(), 'frozenset()') - self.assertGdbRepr(frozenset(['a', 'b']), "frozenset({'a', 'b'})") - self.assertGdbRepr(frozenset([4, 5, 6]), "frozenset({4, 5, 6})") + self.assertGdbRepr(frozenset(), "frozenset()") + self.assertGdbRepr(frozenset(['a']), "frozenset({'a'})") + # PYTHONHASHSEED is need to get the exact frozenset item order + if not sys.flags.ignore_environment: + self.assertGdbRepr(frozenset(['a', 'b']), "frozenset({'a', 'b'})") + self.assertGdbRepr(frozenset([4, 5, 6]), "frozenset({4, 5, 6})") def test_exceptions(self): # Test a RuntimeError @@ -501,6 +509,10 @@ id(foo)''') def test_builtins_help(self): 'Ensure that the new-style class _Helper in site.py can be handled' + + if sys.flags.no_site: + self.skipTest("need site module, but -S option was used") + # (this was the issue causing tracebacks in # http://bugs.python.org/issue8032#msg100537 ) gdb_repr, gdb_output = self.get_gdb_repr('id(__builtins__.help)', import_site=True) diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index 3f82462..f4b33af 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -245,11 +245,11 @@ class ExceptionTest(unittest.TestCase): yield with self.assertRaises(StopIteration), \ - self.assertWarnsRegex(PendingDeprecationWarning, "StopIteration"): + self.assertWarnsRegex(DeprecationWarning, "StopIteration"): next(gen()) - with self.assertRaisesRegex(PendingDeprecationWarning, + with self.assertRaisesRegex(DeprecationWarning, "generator .* raised StopIteration"), \ warnings.catch_warnings(): @@ -268,7 +268,7 @@ class ExceptionTest(unittest.TestCase): g = f() self.assertEqual(next(g), 1) - with self.assertWarnsRegex(PendingDeprecationWarning, "StopIteration"): + with self.assertWarnsRegex(DeprecationWarning, "StopIteration"): with self.assertRaises(StopIteration): next(g) diff --git a/Lib/test/test_genericpath.py b/Lib/test/test_genericpath.py index b77d1d7..c8f158d 100644 --- a/Lib/test/test_genericpath.py +++ b/Lib/test/test_genericpath.py @@ -10,11 +10,9 @@ import warnings from test import support -def safe_rmdir(dirname): - try: - os.rmdir(dirname) - except OSError: - pass +def create_file(filename, data=b'foo'): + with open(filename, 'xb', 0) as fp: + fp.write(data) class GenericTest: @@ -97,52 +95,47 @@ class GenericTest: self.assertNotEqual(s1[n:n+1], s2[n:n+1]) def test_getsize(self): - f = open(support.TESTFN, "wb") - try: - f.write(b"foo") - f.close() - self.assertEqual(self.pathmodule.getsize(support.TESTFN), 3) - finally: - if not f.closed: - f.close() - support.unlink(support.TESTFN) + filename = support.TESTFN + self.addCleanup(support.unlink, filename) - def test_time(self): - f = open(support.TESTFN, "wb") - try: - f.write(b"foo") - f.close() - f = open(support.TESTFN, "ab") + create_file(filename, b'Hello') + self.assertEqual(self.pathmodule.getsize(filename), 5) + os.remove(filename) + + create_file(filename, b'Hello World!') + self.assertEqual(self.pathmodule.getsize(filename), 12) + + def test_filetime(self): + filename = support.TESTFN + self.addCleanup(support.unlink, filename) + + create_file(filename, b'foo') + + with open(filename, "ab", 0) as f: f.write(b"bar") - f.close() - f = open(support.TESTFN, "rb") - d = f.read() - f.close() - self.assertEqual(d, b"foobar") - - self.assertLessEqual( - self.pathmodule.getctime(support.TESTFN), - self.pathmodule.getmtime(support.TESTFN) - ) - finally: - if not f.closed: - f.close() - support.unlink(support.TESTFN) + + with open(filename, "rb", 0) as f: + data = f.read() + self.assertEqual(data, b"foobar") + + self.assertLessEqual( + self.pathmodule.getctime(filename), + self.pathmodule.getmtime(filename) + ) def test_exists(self): - self.assertIs(self.pathmodule.exists(support.TESTFN), False) - f = open(support.TESTFN, "wb") - try: + filename = support.TESTFN + self.addCleanup(support.unlink, filename) + + self.assertIs(self.pathmodule.exists(filename), False) + + with open(filename, "xb") as f: f.write(b"foo") - f.close() - self.assertIs(self.pathmodule.exists(support.TESTFN), True) - if not self.pathmodule == genericpath: - self.assertIs(self.pathmodule.lexists(support.TESTFN), - True) - finally: - if not f.close(): - f.close() - support.unlink(support.TESTFN) + + self.assertIs(self.pathmodule.exists(filename), True) + + if not self.pathmodule == genericpath: + self.assertIs(self.pathmodule.lexists(filename), True) @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") def test_exists_fd(self): @@ -154,53 +147,66 @@ class GenericTest: os.close(w) self.assertFalse(self.pathmodule.exists(r)) - def test_isdir(self): - self.assertIs(self.pathmodule.isdir(support.TESTFN), False) - f = open(support.TESTFN, "wb") - try: - f.write(b"foo") - f.close() - self.assertIs(self.pathmodule.isdir(support.TESTFN), False) - os.remove(support.TESTFN) - os.mkdir(support.TESTFN) - self.assertIs(self.pathmodule.isdir(support.TESTFN), True) - os.rmdir(support.TESTFN) - finally: - if not f.close(): - f.close() - support.unlink(support.TESTFN) - safe_rmdir(support.TESTFN) - - def test_isfile(self): - self.assertIs(self.pathmodule.isfile(support.TESTFN), False) - f = open(support.TESTFN, "wb") - try: - f.write(b"foo") - f.close() - self.assertIs(self.pathmodule.isfile(support.TESTFN), True) - os.remove(support.TESTFN) - os.mkdir(support.TESTFN) - self.assertIs(self.pathmodule.isfile(support.TESTFN), False) - os.rmdir(support.TESTFN) - finally: - if not f.close(): - f.close() - support.unlink(support.TESTFN) - safe_rmdir(support.TESTFN) + def test_isdir_file(self): + filename = support.TESTFN + self.addCleanup(support.unlink, filename) + self.assertIs(self.pathmodule.isdir(filename), False) + + create_file(filename) + self.assertIs(self.pathmodule.isdir(filename), False) + + def test_isdir_dir(self): + filename = support.TESTFN + self.addCleanup(support.rmdir, filename) + self.assertIs(self.pathmodule.isdir(filename), False) - @staticmethod - def _create_file(filename): - with open(filename, 'wb') as f: - f.write(b'foo') + os.mkdir(filename) + self.assertIs(self.pathmodule.isdir(filename), True) + + def test_isfile_file(self): + filename = support.TESTFN + self.addCleanup(support.unlink, filename) + self.assertIs(self.pathmodule.isfile(filename), False) + + create_file(filename) + self.assertIs(self.pathmodule.isfile(filename), True) + + def test_isfile_dir(self): + filename = support.TESTFN + self.addCleanup(support.rmdir, filename) + self.assertIs(self.pathmodule.isfile(filename), False) + + os.mkdir(filename) + self.assertIs(self.pathmodule.isfile(filename), False) def test_samefile(self): - try: - test_fn = support.TESTFN + "1" - self._create_file(test_fn) - self.assertTrue(self.pathmodule.samefile(test_fn, test_fn)) - self.assertRaises(TypeError, self.pathmodule.samefile) - finally: - os.remove(test_fn) + file1 = support.TESTFN + file2 = support.TESTFN + "2" + self.addCleanup(support.unlink, file1) + self.addCleanup(support.unlink, file2) + + create_file(file1) + self.assertTrue(self.pathmodule.samefile(file1, file1)) + + create_file(file2) + self.assertFalse(self.pathmodule.samefile(file1, file2)) + + self.assertRaises(TypeError, self.pathmodule.samefile) + + def _test_samefile_on_link_func(self, func): + test_fn1 = support.TESTFN + test_fn2 = support.TESTFN + "2" + self.addCleanup(support.unlink, test_fn1) + self.addCleanup(support.unlink, test_fn2) + + create_file(test_fn1) + + func(test_fn1, test_fn2) + self.assertTrue(self.pathmodule.samefile(test_fn1, test_fn2)) + os.remove(test_fn2) + + create_file(test_fn2) + self.assertFalse(self.pathmodule.samefile(test_fn1, test_fn2)) @support.skip_unless_symlink def test_samefile_on_symlink(self): @@ -209,31 +215,37 @@ class GenericTest: def test_samefile_on_link(self): self._test_samefile_on_link_func(os.link) - def _test_samefile_on_link_func(self, func): - try: - test_fn1 = support.TESTFN + "1" - test_fn2 = support.TESTFN + "2" - self._create_file(test_fn1) + def test_samestat(self): + test_fn1 = support.TESTFN + test_fn2 = support.TESTFN + "2" + self.addCleanup(support.unlink, test_fn1) + self.addCleanup(support.unlink, test_fn2) - func(test_fn1, test_fn2) - self.assertTrue(self.pathmodule.samefile(test_fn1, test_fn2)) - os.remove(test_fn2) + create_file(test_fn1) + stat1 = os.stat(test_fn1) + self.assertTrue(self.pathmodule.samestat(stat1, os.stat(test_fn1))) - self._create_file(test_fn2) - self.assertFalse(self.pathmodule.samefile(test_fn1, test_fn2)) - finally: - os.remove(test_fn1) - os.remove(test_fn2) + create_file(test_fn2) + stat2 = os.stat(test_fn2) + self.assertFalse(self.pathmodule.samestat(stat1, stat2)) - def test_samestat(self): - try: - test_fn = support.TESTFN + "1" - self._create_file(test_fn) - test_fns = [test_fn]*2 - stats = map(os.stat, test_fns) - self.assertTrue(self.pathmodule.samestat(*stats)) - finally: - os.remove(test_fn) + self.assertRaises(TypeError, self.pathmodule.samestat) + + def _test_samestat_on_link_func(self, func): + test_fn1 = support.TESTFN + "1" + test_fn2 = support.TESTFN + "2" + self.addCleanup(support.unlink, test_fn1) + self.addCleanup(support.unlink, test_fn2) + + create_file(test_fn1) + func(test_fn1, test_fn2) + self.assertTrue(self.pathmodule.samestat(os.stat(test_fn1), + os.stat(test_fn2))) + os.remove(test_fn2) + + create_file(test_fn2) + self.assertFalse(self.pathmodule.samestat(os.stat(test_fn1), + os.stat(test_fn2))) @support.skip_unless_symlink def test_samestat_on_symlink(self): @@ -242,31 +254,17 @@ class GenericTest: def test_samestat_on_link(self): self._test_samestat_on_link_func(os.link) - def _test_samestat_on_link_func(self, func): - try: - test_fn1 = support.TESTFN + "1" - test_fn2 = support.TESTFN + "2" - self._create_file(test_fn1) - test_fns = (test_fn1, test_fn2) - func(*test_fns) - stats = map(os.stat, test_fns) - self.assertTrue(self.pathmodule.samestat(*stats)) - os.remove(test_fn2) - - self._create_file(test_fn2) - stats = map(os.stat, test_fns) - self.assertFalse(self.pathmodule.samestat(*stats)) - - self.assertRaises(TypeError, self.pathmodule.samestat) - finally: - os.remove(test_fn1) - os.remove(test_fn2) - def test_sameopenfile(self): - fname = support.TESTFN + "1" - with open(fname, "wb") as a, open(fname, "wb") as b: - self.assertTrue(self.pathmodule.sameopenfile( - a.fileno(), b.fileno())) + filename = support.TESTFN + self.addCleanup(support.unlink, filename) + create_file(filename) + + with open(filename, "rb", 0) as fp1: + fd1 = fp1.fileno() + with open(filename, "rb", 0) as fp2: + fd2 = fp2.fileno() + self.assertTrue(self.pathmodule.sameopenfile(fd1, fd2)) + class TestGenericTest(GenericTest, unittest.TestCase): # Issue 16852: GenericTest can't inherit from unittest.TestCase @@ -452,16 +450,15 @@ class CommonTest(GenericTest): 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'): + with self.assertRaisesRegex(TypeError, 'int'): self.pathmodule.join(42, 'str') - with self.assertRaisesRegex(TypeError, errmsg % 'int'): + with self.assertRaisesRegex(TypeError, 'int'): self.pathmodule.join('str', 42) - with self.assertRaisesRegex(TypeError, errmsg % 'int'): + with self.assertRaisesRegex(TypeError, 'int'): self.pathmodule.join(42) - with self.assertRaisesRegex(TypeError, errmsg % 'list'): + with self.assertRaisesRegex(TypeError, 'list'): self.pathmodule.join([]) - with self.assertRaisesRegex(TypeError, errmsg % 'bytearray'): + with self.assertRaisesRegex(TypeError, 'bytearray'): self.pathmodule.join(bytearray(b'foo'), bytearray(b'bar')) def test_relpath_errors(self): @@ -473,14 +470,59 @@ class CommonTest(GenericTest): 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'): + with self.assertRaisesRegex(TypeError, 'int'): self.pathmodule.relpath(42, 'str') - with self.assertRaisesRegex(TypeError, errmsg % 'int'): + with self.assertRaisesRegex(TypeError, 'int'): self.pathmodule.relpath('str', 42) - with self.assertRaisesRegex(TypeError, errmsg % 'bytearray'): + with self.assertRaisesRegex(TypeError, 'bytearray'): self.pathmodule.relpath(bytearray(b'foo'), bytearray(b'bar')) +class PathLikeTests(unittest.TestCase): + + class PathLike: + def __init__(self, path=''): + self.path = path + def __fspath__(self): + if isinstance(self.path, BaseException): + raise self.path + else: + return self.path + + def setUp(self): + self.file_name = support.TESTFN.lower() + self.file_path = self.PathLike(support.TESTFN) + self.addCleanup(support.unlink, self.file_name) + create_file(self.file_name, b"test_genericpath.PathLikeTests") + + def assertPathEqual(self, func): + self.assertEqual(func(self.file_path), func(self.file_name)) + + def test_path_exists(self): + self.assertPathEqual(os.path.exists) + + def test_path_isfile(self): + self.assertPathEqual(os.path.isfile) + + def test_path_isdir(self): + self.assertPathEqual(os.path.isdir) + + def test_path_commonprefix(self): + self.assertEqual(os.path.commonprefix([self.file_path, self.file_name]), + self.file_name) + + def test_path_getsize(self): + self.assertPathEqual(os.path.getsize) + + def test_path_getmtime(self): + self.assertPathEqual(os.path.getatime) + + def test_path_getctime(self): + self.assertPathEqual(os.path.getctime) + + def test_path_samefile(self): + self.assertTrue(os.path.samefile(self.file_path, self.file_name)) + + if __name__=="__main__": unittest.main() diff --git a/Lib/test/test_getargs2.py b/Lib/test/test_getargs2.py index 984aac7..16e163a 100644 --- a/Lib/test/test_getargs2.py +++ b/Lib/test/test_getargs2.py @@ -365,7 +365,8 @@ class Float_TestCase(unittest.TestCase): self.assertEqual(getargs_f(FloatSubclass(7.5)), 7.5) self.assertEqual(getargs_f(FloatSubclass2(7.5)), 7.5) self.assertRaises(TypeError, getargs_f, BadFloat()) - self.assertEqual(getargs_f(BadFloat2()), 4.25) + with self.assertWarns(DeprecationWarning): + self.assertEqual(getargs_f(BadFloat2()), 4.25) self.assertEqual(getargs_f(BadFloat3(7.5)), 7.5) for x in (FLT_MIN, -FLT_MIN, FLT_MAX, -FLT_MAX, INF, -INF): @@ -390,7 +391,8 @@ class Float_TestCase(unittest.TestCase): self.assertEqual(getargs_d(FloatSubclass(7.5)), 7.5) self.assertEqual(getargs_d(FloatSubclass2(7.5)), 7.5) self.assertRaises(TypeError, getargs_d, BadFloat()) - self.assertEqual(getargs_d(BadFloat2()), 4.25) + with self.assertWarns(DeprecationWarning): + self.assertEqual(getargs_d(BadFloat2()), 4.25) self.assertEqual(getargs_d(BadFloat3(7.5)), 7.5) for x in (DBL_MIN, -DBL_MIN, DBL_MAX, -DBL_MAX, INF, -INF): @@ -474,7 +476,7 @@ class Tuple_TestCase(unittest.TestCase): ret = get_args(*TupleSubclass([1, 2])) self.assertEqual(ret, (1, 2)) - self.assertIsInstance(ret, tuple) + self.assertIs(type(ret), tuple) ret = get_args() self.assertIn(ret, ((), None)) @@ -512,7 +514,7 @@ class Keywords_TestCase(unittest.TestCase): ret = get_kwargs(**DictSubclass({'a': 1, 'b': 2})) self.assertEqual(ret, {'a': 1, 'b': 2}) - self.assertIsInstance(ret, dict) + self.assertIs(type(ret), dict) ret = get_kwargs() self.assertIn(ret, ({}, None)) @@ -656,6 +658,39 @@ class KeywordOnly_TestCase(unittest.TestCase): getargs_keyword_only(1, 2, **{'\uDC80': 10}) +class PositionalOnlyAndKeywords_TestCase(unittest.TestCase): + from _testcapi import getargs_positional_only_and_keywords as getargs + + def test_positional_args(self): + # using all possible positional args + self.assertEqual(self.getargs(1, 2, 3), (1, 2, 3)) + + def test_mixed_args(self): + # positional and keyword args + self.assertEqual(self.getargs(1, 2, keyword=3), (1, 2, 3)) + + def test_optional_args(self): + # missing optional args + self.assertEqual(self.getargs(1, 2), (1, 2, -1)) + self.assertEqual(self.getargs(1, keyword=3), (1, -1, 3)) + + def test_required_args(self): + self.assertEqual(self.getargs(1), (1, -1, -1)) + # required positional arg missing + with self.assertRaisesRegex(TypeError, + "Function takes at least 1 positional arguments \(0 given\)"): + self.getargs() + + with self.assertRaisesRegex(TypeError, + "Function takes at least 1 positional arguments \(0 given\)"): + self.getargs(keyword=3) + + def test_empty_keyword(self): + with self.assertRaisesRegex(TypeError, + "'' is an invalid keyword argument for this function"): + self.getargs(1, 2, **{'': 666}) + + class Bytes_TestCase(unittest.TestCase): def test_c(self): from _testcapi import getargs_c @@ -822,10 +857,10 @@ class String_TestCase(unittest.TestCase): self.assertEqual(getargs_es_hash('abc\xe9', 'latin1', buf), b'abc\xe9') self.assertEqual(buf, bytearray(b'abc\xe9\x00')) buf = bytearray(b'x'*4) - self.assertRaises(TypeError, getargs_es_hash, 'abc\xe9', 'latin1', buf) + self.assertRaises(ValueError, getargs_es_hash, 'abc\xe9', 'latin1', buf) self.assertEqual(buf, bytearray(b'x'*4)) buf = bytearray() - self.assertRaises(TypeError, getargs_es_hash, 'abc\xe9', 'latin1', buf) + self.assertRaises(ValueError, getargs_es_hash, 'abc\xe9', 'latin1', buf) def test_et_hash(self): from _testcapi import getargs_et_hash @@ -848,10 +883,10 @@ class String_TestCase(unittest.TestCase): self.assertEqual(getargs_et_hash('abc\xe9', 'latin1', buf), b'abc\xe9') self.assertEqual(buf, bytearray(b'abc\xe9\x00')) buf = bytearray(b'x'*4) - self.assertRaises(TypeError, getargs_et_hash, 'abc\xe9', 'latin1', buf) + self.assertRaises(ValueError, getargs_et_hash, 'abc\xe9', 'latin1', buf) self.assertEqual(buf, bytearray(b'x'*4)) buf = bytearray() - self.assertRaises(TypeError, getargs_et_hash, 'abc\xe9', 'latin1', buf) + self.assertRaises(ValueError, getargs_et_hash, 'abc\xe9', 'latin1', buf) def test_u(self): from _testcapi import getargs_u diff --git a/Lib/test/test_gettext.py b/Lib/test/test_gettext.py index de610c7..d345baa 100644 --- a/Lib/test/test_gettext.py +++ b/Lib/test/test_gettext.py @@ -1,6 +1,5 @@ import os import base64 -import shutil import gettext import unittest @@ -440,6 +439,12 @@ class GettextCacheTestCase(GettextBaseTest): self.assertEqual(t.__class__, DummyGNUTranslations) +class MiscTestCase(unittest.TestCase): + def test__all__(self): + blacklist = {'c2py', 'ENOENT'} + support.check__all__(self, gettext, blacklist=blacklist) + + def test_main(): support.run_unittest(__name__) diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index 154e3b6..bfe5225 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -295,6 +295,10 @@ class GrammarTests(unittest.TestCase): pos2key2dict(1,2,k2=100,tokwarg1=100,tokwarg2=200) pos2key2dict(1,2,tokwarg1=100,tokwarg2=200, k2=100) + self.assertRaises(SyntaxError, eval, "def f(*): pass") + self.assertRaises(SyntaxError, eval, "def f(*,): pass") + self.assertRaises(SyntaxError, eval, "def f(*, **kwds): pass") + # keyword arguments after *arglist def f(*args, **kwargs): return args, kwargs @@ -341,7 +345,7 @@ class GrammarTests(unittest.TestCase): def f(x) -> list: pass self.assertEqual(f.__annotations__, {'return': list}) - # test MAKE_CLOSURE with a variety of oparg's + # test closures with a variety of opargs closure = 1 def f(): return closure def f(x=1): return closure @@ -352,6 +356,23 @@ class GrammarTests(unittest.TestCase): check_syntax_error(self, "f(*g(1=2))") check_syntax_error(self, "f(**g(1=2))") + # Check trailing commas are permitted in funcdef argument list + def f(a,): pass + def f(*args,): pass + def f(**kwds,): pass + def f(a, *args,): pass + def f(a, **kwds,): pass + def f(*args, b,): pass + def f(*, b,): pass + def f(*args, **kwds,): pass + def f(a, *args, b,): pass + def f(a, *, b,): pass + def f(a, *args, **kwds,): pass + def f(*args, b, **kwds,): pass + def f(*, b, **kwds,): pass + def f(a, *args, b, **kwds,): pass + def f(a, *, b, **kwds,): pass + def test_lambdef(self): ### lambdef: 'lambda' [varargslist] ':' test l1 = lambda : 0 @@ -370,6 +391,23 @@ class GrammarTests(unittest.TestCase): self.assertEqual(l6(1,2), 1+2+20) self.assertEqual(l6(1,2,k=10), 1+2+10) + # check that trailing commas are permitted + l10 = lambda a,: 0 + l11 = lambda *args,: 0 + l12 = lambda **kwds,: 0 + l13 = lambda a, *args,: 0 + l14 = lambda a, **kwds,: 0 + l15 = lambda *args, b,: 0 + l16 = lambda *, b,: 0 + l17 = lambda *args, **kwds,: 0 + l18 = lambda a, *args, b,: 0 + l19 = lambda a, *, b,: 0 + l20 = lambda a, *args, **kwds,: 0 + l21 = lambda *args, b, **kwds,: 0 + l22 = lambda *, b, **kwds,: 0 + l23 = lambda a, *args, b, **kwds,: 0 + l24 = lambda a, *, b, **kwds,: 0 + ### stmt: simple_stmt | compound_stmt # Tested below diff --git a/Lib/test/test_grp.py b/Lib/test/test_grp.py index 272b086..69095a3 100644 --- a/Lib/test/test_grp.py +++ b/Lib/test/test_grp.py @@ -92,5 +92,15 @@ class GroupDatabaseTestCase(unittest.TestCase): self.assertRaises(KeyError, grp.getgrgid, fakegid) + def test_noninteger_gid(self): + entries = grp.getgrall() + if not entries: + self.skipTest('no groups') + # Choose an existent gid. + gid = entries[0][2] + self.assertWarns(DeprecationWarning, grp.getgrgid, float(gid)) + self.assertWarns(DeprecationWarning, grp.getgrgid, str(gid)) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py index c9b113e..e6df09c 100644 --- a/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py @@ -7,6 +7,7 @@ # import array +from binascii import unhexlify import hashlib import itertools import os @@ -26,6 +27,14 @@ COMPILED_WITH_PYDEBUG = hasattr(sys, 'gettotalrefcount') c_hashlib = import_fresh_module('hashlib', fresh=['_hashlib']) py_hashlib = import_fresh_module('hashlib', blocked=['_hashlib']) +try: + import _blake2 +except ImportError: + _blake2 = None + +requires_blake2 = unittest.skipUnless(_blake2, 'requires _blake2') + + def hexstr(s): assert isinstance(s, bytes), repr(s) h = "0123456789abcdef" @@ -35,10 +44,24 @@ def hexstr(s): return r +URL = "https://raw.githubusercontent.com/tiran/python_vectors/master/{}.txt" + +def read_vectors(hash_name): + with support.open_urlresource(URL.format(hash_name)) as f: + for line in f: + line = line.strip() + if line.startswith('#') or not line: + continue + parts = line.split(',') + parts[0] = bytes.fromhex(parts[0]) + yield parts + + class HashLibTestCase(unittest.TestCase): supported_hash_names = ( 'md5', 'MD5', 'sha1', 'SHA1', 'sha224', 'SHA224', 'sha256', 'SHA256', - 'sha384', 'SHA384', 'sha512', 'SHA512') + 'sha384', 'SHA384', 'sha512', 'SHA512', + 'blake2b', 'blake2s') # Issue #14693: fallback modules are always compiled under POSIX _warn_on_extension_import = os.name == 'posix' or COMPILED_WITH_PYDEBUG @@ -56,6 +79,11 @@ class HashLibTestCase(unittest.TestCase): algorithms = set() for algorithm in self.supported_hash_names: algorithms.add(algorithm.lower()) + + _blake2 = self._conditional_import_module('_blake2') + if _blake2: + algorithms.update({'blake2b', 'blake2s'}) + self.constructors_to_test = {} for algorithm in algorithms: self.constructors_to_test[algorithm] = set() @@ -64,10 +92,10 @@ class HashLibTestCase(unittest.TestCase): # of hashlib.new given the algorithm name. for algorithm, constructors in self.constructors_to_test.items(): constructors.add(getattr(hashlib, algorithm)) - def _test_algorithm_via_hashlib_new(data=None, _alg=algorithm): + def _test_algorithm_via_hashlib_new(data=None, _alg=algorithm, **kwargs): if data is None: - return hashlib.new(_alg) - return hashlib.new(_alg, data) + return hashlib.new(_alg, **kwargs) + return hashlib.new(_alg, data, **kwargs) constructors.add(_test_algorithm_via_hashlib_new) _hashlib = self._conditional_import_module('_hashlib') @@ -99,6 +127,9 @@ class HashLibTestCase(unittest.TestCase): if _sha512: add_builtin_constructor('sha384') add_builtin_constructor('sha512') + if _blake2: + add_builtin_constructor('blake2s') + add_builtin_constructor('blake2b') super(HashLibTestCase, self).__init__(*args, **kwargs) @@ -193,13 +224,13 @@ class HashLibTestCase(unittest.TestCase): self.assertEqual(m1.digest(), m4_copy.digest()) self.assertEqual(m4.digest(), m4_digest) - def check(self, name, data, hexdigest): + def check(self, name, data, hexdigest, **kwargs): hexdigest = hexdigest.lower() constructors = self.constructors_to_test[name] # 2 is for hashlib.name(...) and hashlib.new(name, ...) self.assertGreaterEqual(len(constructors), 2) for hash_object_constructor in constructors: - m = hash_object_constructor(data) + m = hash_object_constructor(data, **kwargs) computed = m.hexdigest() self.assertEqual( computed, hexdigest, @@ -226,6 +257,11 @@ class HashLibTestCase(unittest.TestCase): self.check_no_unicode('sha384') self.check_no_unicode('sha512') + @requires_blake2 + def test_no_unicode_blake2(self): + self.check_no_unicode('blake2b') + self.check_no_unicode('blake2s') + def check_blocksize_name(self, name, block_size=0, digest_size=0): constructors = self.constructors_to_test[name] for hash_object_constructor in constructors: @@ -245,6 +281,11 @@ class HashLibTestCase(unittest.TestCase): self.check_blocksize_name('sha384', 128, 48) self.check_blocksize_name('sha512', 128, 64) + @requires_blake2 + def test_blocksize_name_blake2(self): + self.check_blocksize_name('blake2b', 128, 64) + self.check_blocksize_name('blake2s', 64, 32) + def test_case_md5_0(self): self.check('md5', b'', 'd41d8cd98f00b204e9800998ecf8427e') @@ -373,6 +414,155 @@ class HashLibTestCase(unittest.TestCase): "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973eb"+ "de0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b") + def check_blake2(self, constructor, salt_size, person_size, key_size, + digest_size, max_offset): + self.assertEqual(constructor.SALT_SIZE, salt_size) + for i in range(salt_size + 1): + constructor(salt=b'a' * i) + salt = b'a' * (salt_size + 1) + self.assertRaises(ValueError, constructor, salt=salt) + + self.assertEqual(constructor.PERSON_SIZE, person_size) + for i in range(person_size+1): + constructor(person=b'a' * i) + person = b'a' * (person_size + 1) + self.assertRaises(ValueError, constructor, person=person) + + self.assertEqual(constructor.MAX_DIGEST_SIZE, digest_size) + for i in range(1, digest_size + 1): + constructor(digest_size=i) + self.assertRaises(ValueError, constructor, digest_size=-1) + self.assertRaises(ValueError, constructor, digest_size=0) + self.assertRaises(ValueError, constructor, digest_size=digest_size+1) + + self.assertEqual(constructor.MAX_KEY_SIZE, key_size) + for i in range(key_size+1): + constructor(key=b'a' * i) + key = b'a' * (key_size + 1) + self.assertRaises(ValueError, constructor, key=key) + self.assertEqual(constructor().hexdigest(), + constructor(key=b'').hexdigest()) + + for i in range(0, 256): + constructor(fanout=i) + self.assertRaises(ValueError, constructor, fanout=-1) + self.assertRaises(ValueError, constructor, fanout=256) + + for i in range(1, 256): + constructor(depth=i) + self.assertRaises(ValueError, constructor, depth=-1) + self.assertRaises(ValueError, constructor, depth=0) + self.assertRaises(ValueError, constructor, depth=256) + + for i in range(0, 256): + constructor(node_depth=i) + self.assertRaises(ValueError, constructor, node_depth=-1) + self.assertRaises(ValueError, constructor, node_depth=256) + + for i in range(0, digest_size + 1): + constructor(inner_size=i) + self.assertRaises(ValueError, constructor, inner_size=-1) + self.assertRaises(ValueError, constructor, inner_size=digest_size+1) + + constructor(leaf_size=0) + constructor(leaf_size=(1<<32)-1) + self.assertRaises(OverflowError, constructor, leaf_size=-1) + self.assertRaises(OverflowError, constructor, leaf_size=1<<32) + + constructor(node_offset=0) + constructor(node_offset=max_offset) + self.assertRaises(OverflowError, constructor, node_offset=-1) + self.assertRaises(OverflowError, constructor, node_offset=max_offset+1) + + constructor( + string=b'', + key=b'', + salt=b'', + person=b'', + digest_size=17, + fanout=1, + depth=1, + leaf_size=256, + node_offset=512, + node_depth=1, + inner_size=7, + last_node=True + ) + + def blake2_rfc7693(self, constructor, md_len, in_len): + def selftest_seq(length, seed): + mask = (1<<32)-1 + a = (0xDEAD4BAD * seed) & mask + b = 1 + out = bytearray(length) + for i in range(length): + t = (a + b) & mask + a, b = b, t + out[i] = (t >> 24) & 0xFF + return out + outer = constructor(digest_size=32) + for outlen in md_len: + for inlen in in_len: + indata = selftest_seq(inlen, inlen) + key = selftest_seq(outlen, outlen) + unkeyed = constructor(indata, digest_size=outlen) + outer.update(unkeyed.digest()) + keyed = constructor(indata, key=key, digest_size=outlen) + outer.update(keyed.digest()) + return outer.hexdigest() + + @requires_blake2 + def test_blake2b(self): + self.check_blake2(hashlib.blake2b, 16, 16, 64, 64, (1<<64)-1) + b2b_md_len = [20, 32, 48, 64] + b2b_in_len = [0, 3, 128, 129, 255, 1024] + self.assertEqual( + self.blake2_rfc7693(hashlib.blake2b, b2b_md_len, b2b_in_len), + "c23a7800d98123bd10f506c61e29da5603d763b8bbad2e737f5e765a7bccd475") + + @requires_blake2 + def test_case_blake2b_0(self): + self.check('blake2b', b"", + "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419"+ + "d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce") + + @requires_blake2 + def test_case_blake2b_1(self): + self.check('blake2b', b"abc", + "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1"+ + "7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923") + + @requires_blake2 + def test_blake2b_vectors(self): + for msg, key, md in read_vectors('blake2b'): + key = bytes.fromhex(key) + self.check('blake2b', msg, md, key=key) + + @requires_blake2 + def test_blake2s(self): + self.check_blake2(hashlib.blake2s, 8, 8, 32, 32, (1<<48)-1) + b2s_md_len = [16, 20, 28, 32] + b2s_in_len = [0, 3, 64, 65, 255, 1024] + self.assertEqual( + self.blake2_rfc7693(hashlib.blake2s, b2s_md_len, b2s_in_len), + "6a411f08ce25adcdfb02aba641451cec53c598b24f4fc787fbdc88797f4c1dfe") + + @requires_blake2 + def test_case_blake2s_0(self): + self.check('blake2s', b"", + "69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9") + + @requires_blake2 + def test_case_blake2s_1(self): + self.check('blake2s', b"abc", + "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982") + + @requires_blake2 + def test_blake2s_vectors(self): + for msg, key, md in read_vectors('blake2s'): + key = bytes.fromhex(key) + self.check('blake2s', msg, md, key=key) + def test_gil(self): # Check things work fine with an input larger than the size required # for multithreaded operation (which is hardwired to 2048). @@ -447,6 +637,12 @@ class KDFTests(unittest.TestCase): (b'pass\0word', b'sa\0lt', 4096, 16), ] + scrypt_test_vectors = [ + (b'', b'', 16, 1, 1, unhexlify('77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906')), + (b'password', b'NaCl', 1024, 8, 16, unhexlify('fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b3731622eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640')), + (b'pleaseletmein', b'SodiumChloride', 16384, 8, 1, unhexlify('7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887')), + ] + pbkdf2_results = { "sha1": [ # official test vectors from RFC 6070 @@ -526,5 +722,45 @@ class KDFTests(unittest.TestCase): self._test_pbkdf2_hmac(c_hashlib.pbkdf2_hmac) + @unittest.skipUnless(hasattr(c_hashlib, 'scrypt'), + ' test requires OpenSSL > 1.1') + def test_scrypt(self): + for password, salt, n, r, p, expected in self.scrypt_test_vectors: + result = hashlib.scrypt(password, salt=salt, n=n, r=r, p=p) + self.assertEqual(result, expected) + + # this values should work + hashlib.scrypt(b'password', salt=b'salt', n=2, r=8, p=1) + # password and salt must be bytes-like + with self.assertRaises(TypeError): + hashlib.scrypt('password', salt=b'salt', n=2, r=8, p=1) + with self.assertRaises(TypeError): + hashlib.scrypt(b'password', salt='salt', n=2, r=8, p=1) + # require keyword args + with self.assertRaises(TypeError): + hashlib.scrypt(b'password') + with self.assertRaises(TypeError): + hashlib.scrypt(b'password', b'salt') + with self.assertRaises(TypeError): + hashlib.scrypt(b'password', 2, 8, 1, salt=b'salt') + for n in [-1, 0, 1, None]: + with self.assertRaises((ValueError, OverflowError, TypeError)): + hashlib.scrypt(b'password', salt=b'salt', n=n, r=8, p=1) + for r in [-1, 0, None]: + with self.assertRaises((ValueError, OverflowError, TypeError)): + hashlib.scrypt(b'password', salt=b'salt', n=2, r=r, p=1) + for p in [-1, 0, None]: + with self.assertRaises((ValueError, OverflowError, TypeError)): + hashlib.scrypt(b'password', salt=b'salt', n=2, r=8, p=p) + for maxmem in [-1, None]: + with self.assertRaises((ValueError, OverflowError, TypeError)): + hashlib.scrypt(b'password', salt=b'salt', n=2, r=8, p=1, + maxmem=maxmem) + for dklen in [-1, None]: + with self.assertRaises((ValueError, OverflowError, TypeError)): + hashlib.scrypt(b'password', salt=b'salt', n=2, r=8, p=1, + dklen=dklen) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_heapq.py b/Lib/test/test_heapq.py index b7e8259..2f8c648 100644 --- a/Lib/test/test_heapq.py +++ b/Lib/test/test_heapq.py @@ -1,6 +1,5 @@ """Unittests for heapq.""" -import sys import random import unittest diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py index 98826b5..067e13f 100644 --- a/Lib/test/test_hmac.py +++ b/Lib/test/test_hmac.py @@ -3,7 +3,6 @@ import hmac import hashlib import unittest import warnings -from test import support def ignore_warning(func): diff --git a/Lib/test/test_htmlparser.py b/Lib/test/test_htmlparser.py index 11420b2c..a7f53d3 100644 --- a/Lib/test/test_htmlparser.py +++ b/Lib/test/test_htmlparser.py @@ -3,7 +3,6 @@ import html.parser import pprint import unittest -from test import support class EventCollector(html.parser.HTMLParser): diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index f45e352..359e0bb 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -314,6 +314,134 @@ class HeaderTests(TestCase): conn.putheader(name, value) +class TransferEncodingTest(TestCase): + expected_body = b"It's just a flesh wound" + + def test_endheaders_chunked(self): + conn = client.HTTPConnection('example.com') + conn.sock = FakeSocket(b'') + conn.putrequest('POST', '/') + conn.endheaders(self._make_body(), encode_chunked=True) + + _, _, body = self._parse_request(conn.sock.data) + body = self._parse_chunked(body) + self.assertEqual(body, self.expected_body) + + def test_explicit_headers(self): + # explicit chunked + conn = client.HTTPConnection('example.com') + conn.sock = FakeSocket(b'') + # this shouldn't actually be automatically chunk-encoded because the + # calling code has explicitly stated that it's taking care of it + conn.request( + 'POST', '/', self._make_body(), {'Transfer-Encoding': 'chunked'}) + + _, headers, body = self._parse_request(conn.sock.data) + self.assertNotIn('content-length', [k.lower() for k in headers.keys()]) + self.assertEqual(headers['Transfer-Encoding'], 'chunked') + self.assertEqual(body, self.expected_body) + + # explicit chunked, string body + conn = client.HTTPConnection('example.com') + conn.sock = FakeSocket(b'') + conn.request( + 'POST', '/', self.expected_body.decode('latin-1'), + {'Transfer-Encoding': 'chunked'}) + + _, headers, body = self._parse_request(conn.sock.data) + self.assertNotIn('content-length', [k.lower() for k in headers.keys()]) + self.assertEqual(headers['Transfer-Encoding'], 'chunked') + self.assertEqual(body, self.expected_body) + + # User-specified TE, but request() does the chunk encoding + conn = client.HTTPConnection('example.com') + conn.sock = FakeSocket(b'') + conn.request('POST', '/', + headers={'Transfer-Encoding': 'gzip, chunked'}, + encode_chunked=True, + body=self._make_body()) + _, headers, body = self._parse_request(conn.sock.data) + self.assertNotIn('content-length', [k.lower() for k in headers]) + self.assertEqual(headers['Transfer-Encoding'], 'gzip, chunked') + self.assertEqual(self._parse_chunked(body), self.expected_body) + + def test_request(self): + for empty_lines in (False, True,): + conn = client.HTTPConnection('example.com') + conn.sock = FakeSocket(b'') + conn.request( + 'POST', '/', self._make_body(empty_lines=empty_lines)) + + _, headers, body = self._parse_request(conn.sock.data) + body = self._parse_chunked(body) + self.assertEqual(body, self.expected_body) + self.assertEqual(headers['Transfer-Encoding'], 'chunked') + + # Content-Length and Transfer-Encoding SHOULD not be sent in the + # same request + self.assertNotIn('content-length', [k.lower() for k in headers]) + + def test_empty_body(self): + # Zero-length iterable should be treated like any other iterable + conn = client.HTTPConnection('example.com') + conn.sock = FakeSocket(b'') + conn.request('POST', '/', ()) + _, headers, body = self._parse_request(conn.sock.data) + self.assertEqual(headers['Transfer-Encoding'], 'chunked') + self.assertNotIn('content-length', [k.lower() for k in headers]) + self.assertEqual(body, b"0\r\n\r\n") + + def _make_body(self, empty_lines=False): + lines = self.expected_body.split(b' ') + for idx, line in enumerate(lines): + # for testing handling empty lines + if empty_lines and idx % 2: + yield b'' + if idx < len(lines) - 1: + yield line + b' ' + else: + yield line + + def _parse_request(self, data): + lines = data.split(b'\r\n') + request = lines[0] + headers = {} + n = 1 + while n < len(lines) and len(lines[n]) > 0: + key, val = lines[n].split(b':') + key = key.decode('latin-1').strip() + headers[key] = val.decode('latin-1').strip() + n += 1 + + return request, headers, b'\r\n'.join(lines[n + 1:]) + + def _parse_chunked(self, data): + body = [] + trailers = {} + n = 0 + lines = data.split(b'\r\n') + # parse body + while True: + size, chunk = lines[n:n+2] + size = int(size, 16) + + if size == 0: + n += 1 + break + + self.assertEqual(size, len(chunk)) + body.append(chunk) + + n += 2 + # we /should/ hit the end chunk, but check against the size of + # lines so we're not stuck in an infinite loop should we get + # malformed data + if n > len(lines): + break + + return b''.join(body) + + class BasicTest(TestCase): def test_status_lines(self): # Test HTTP status lines @@ -534,7 +662,9 @@ class BasicTest(TestCase): def test_send_file(self): expected = (b'GET /foo HTTP/1.1\r\nHost: example.com\r\n' - b'Accept-Encoding: identity\r\nContent-Length:') + b'Accept-Encoding: identity\r\n' + b'Transfer-Encoding: chunked\r\n' + b'\r\n') with open(__file__, 'rb') as body: conn = client.HTTPConnection('example.com') @@ -564,11 +694,11 @@ class BasicTest(TestCase): yield None yield 'data_two' - class UpdatingFile(): + class UpdatingFile(io.TextIOBase): mode = 'r' d = data() def read(self, blocksize=-1): - return self.d.__next__() + return next(self.d) expected = b'data' @@ -940,6 +1070,7 @@ class BasicTest(TestCase): thread = threading.Thread(target=run_server) thread.start() + self.addCleanup(thread.join, float(1)) conn = client.HTTPConnection(*serv.getsockname()) conn.request("CONNECT", "dummy:1234") response = conn.getresponse() @@ -953,7 +1084,7 @@ class BasicTest(TestCase): finally: response.close() conn.close() - thread.join() + thread.join() self.assertEqual(result, b"proxied data\n") class ExtendedReadTest(TestCase): @@ -1545,6 +1676,26 @@ class RequestBodyTest(TestCase): message = client.parse_headers(f) return message, f + def test_list_body(self): + # Note that no content-length is automatically calculated for + # an iterable. The request will fall back to send chunked + # transfer encoding. + cases = ( + ([b'foo', b'bar'], b'3\r\nfoo\r\n3\r\nbar\r\n0\r\n\r\n'), + ((b'foo', b'bar'), b'3\r\nfoo\r\n3\r\nbar\r\n0\r\n\r\n'), + ) + for body, expected in cases: + with self.subTest(body): + self.conn = client.HTTPConnection('example.com') + self.conn.sock = self.sock = FakeSocket('') + + self.conn.request('PUT', '/url', body) + msg, f = self.get_headers_and_fp() + self.assertNotIn('Content-Type', msg) + self.assertNotIn('Content-Length', msg) + self.assertEqual(msg.get('Transfer-Encoding'), 'chunked') + self.assertEqual(expected, f.read()) + def test_manual_content_length(self): # Set an incorrect content-length so that we can verify that # it will not be over-ridden by the library. @@ -1578,7 +1729,7 @@ class RequestBodyTest(TestCase): self.assertEqual("5", message.get("content-length")) self.assertEqual(b'body\xc1', f.read()) - def test_file_body(self): + def test_text_file_body(self): self.addCleanup(support.unlink, support.TESTFN) with open(support.TESTFN, "w") as f: f.write("body") @@ -1587,8 +1738,11 @@ class RequestBodyTest(TestCase): message, f = self.get_headers_and_fp() self.assertEqual("text/plain", message.get_content_type()) self.assertIsNone(message.get_charset()) - self.assertEqual("4", message.get("content-length")) - self.assertEqual(b'body', f.read()) + # No content-length will be determined for files; the body + # will be sent using chunked transfer encoding instead. + self.assertIsNone(message.get("content-length")) + self.assertEqual("chunked", message.get("transfer-encoding")) + self.assertEqual(b'4\r\nbody\r\n0\r\n\r\n', f.read()) def test_binary_file_body(self): self.addCleanup(support.unlink, support.TESTFN) @@ -1599,8 +1753,9 @@ class RequestBodyTest(TestCase): message, f = self.get_headers_and_fp() self.assertEqual("text/plain", message.get_content_type()) self.assertIsNone(message.get_charset()) - self.assertEqual("5", message.get("content-length")) - self.assertEqual(b'body\xc1', f.read()) + self.assertEqual("chunked", message.get("Transfer-Encoding")) + self.assertNotIn("Content-Length", message) + self.assertEqual(b'5\r\nbody\xc1\r\n0\r\n\r\n', f.read()) class HTTPResponseTest(TestCase): @@ -1711,13 +1866,5 @@ class TunnelTests(TestCase): self.assertIn('header: {}'.format(expected_header), lines) -@support.reap_threads -def test_main(verbose=None): - support.run_unittest(HeaderTests, OfflineTest, BasicTest, TimeoutTest, - PersistenceTest, - HTTPSTest, RequestBodyTest, SourceAddressTest, - HTTPResponseTest, ExtendedReadTest, - ExtendedReadTestChunked, TunnelTests) - if __name__ == '__main__': - test_main() + unittest.main(verbosity=2) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 72e6e08..75044cb 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -18,6 +18,7 @@ import urllib.parse import html import http.client import tempfile +import time from io import BytesIO import unittest @@ -189,7 +190,7 @@ class BaseHTTPServerTestCase(BaseTestCase): res = self.con.getresponse() self.assertEqual(res.status, HTTPStatus.NOT_IMPLEMENTED) - def test_head_keep_alive(self): + def test_header_keep_alive(self): self.con._http_vsn_str = 'HTTP/1.1' self.con.putrequest('GET', '/') self.con.putheader('Connection', 'keep-alive') @@ -388,7 +389,7 @@ class SimpleHTTPServerTestCase(BaseTestCase): quotedname = urllib.parse.quote(filename, errors='surrogatepass') self.assertIn(('href="%s"' % quotedname) .encode(enc, 'surrogateescape'), body) - self.assertIn(('>%s<' % html.escape(filename)) + self.assertIn(('>%s<' % html.escape(filename, quote=False)) .encode(enc, 'surrogateescape'), body) response = self.request(self.base_url + '/' + quotedname) self.check_status_and_reason(response, HTTPStatus.OK, @@ -466,6 +467,27 @@ class SimpleHTTPServerTestCase(BaseTestCase): self.assertEqual(response.getheader("Location"), self.tempdir_name + "/?hi=1") + def test_html_escape_filename(self): + filename = '<test&>.txt' + fullpath = os.path.join(self.tempdir, filename) + + try: + open(fullpath, 'w').close() + except OSError: + raise unittest.SkipTest('Can not create file %s on current file ' + 'system' % filename) + + try: + response = self.request(self.base_url + '/') + body = self.check_status_and_reason(response, HTTPStatus.OK) + enc = response.headers.get_content_charset() + finally: + os.unlink(fullpath) # avoid affecting test_undecodable_filename + + self.assertIsNotNone(enc) + html_text = '>%s<' % html.escape(filename, quote=False) + self.assertIn(html_text.encode(enc), body) + cgi_file1 = """\ #!%s @@ -916,7 +938,7 @@ class BaseHTTPRequestHandlerTestCase(unittest.TestCase): # Issue #6791: same for headers result = self.send_typical_request( b'GET / HTTP/1.1\r\nX-Foo: bar' + b'r' * 65537 + b'\r\n\r\n') - self.assertEqual(result[0], b'HTTP/1.1 400 Line too long\r\n') + self.assertEqual(result[0], b'HTTP/1.1 431 Line too long\r\n') self.assertFalse(self.handler.get_called) self.assertEqual(self.handler.requestline, 'GET / HTTP/1.1') @@ -927,6 +949,13 @@ class BaseHTTPRequestHandlerTestCase(unittest.TestCase): self.assertFalse(self.handler.get_called) self.assertEqual(self.handler.requestline, 'GET / HTTP/1.1') + def test_html_escape_on_error(self): + result = self.send_typical_request( + b'<script>alert("hello")</script> / HTTP/1.1') + result = b''.join(result) + text = '<script>alert("hello")</script>' + self.assertIn(html.escape(text, quote=False).encode('ascii'), result) + def test_close_connection(self): # handle_one_request() should be repeatedly called until # it sets close_connection @@ -942,6 +971,19 @@ class BaseHTTPRequestHandlerTestCase(unittest.TestCase): self.handler.handle() self.assertRaises(StopIteration, next, close_values) + def test_date_time_string(self): + now = time.time() + # this is the old code that formats the timestamp + year, month, day, hh, mm, ss, wd, y, z = time.gmtime(now) + expected = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % ( + self.handler.weekdayname[wd], + day, + self.handler.monthname[month], + year, hh, mm, ss + ) + self.assertEqual(self.handler.date_time_string(timestamp=now), expected) + + class SimpleHTTPRequestHandlerTestCase(unittest.TestCase): """ Test url parsing """ def setUp(self): diff --git a/Lib/test/test_idle.py b/Lib/test/test_idle.py index 141e89e..da05da5 100644 --- a/Lib/test/test_idle.py +++ b/Lib/test/test_idle.py @@ -1,16 +1,23 @@ import unittest -from test import support from test.support import import_module -# Skip test if _thread or _tkinter wasn't built or idlelib was deleted. +# Skip test if _thread or _tkinter wasn't built, if idlelib is missing, +# or if tcl/tk is not the 8.5+ needed for ttk widgets. import_module('threading') # imported by PyShell, imports _thread tk = import_module('tkinter') # imports _tkinter -idletest = import_module('idlelib.idle_test') +if tk.TkVersion < 8.5: + raise unittest.SkipTest("IDLE requires tk 8.5 or later.") +idlelib = import_module('idlelib') -# Without test_main present, regrtest.runtest_inner (line1219) calls -# unittest.TestLoader().loadTestsFromModule(this_module) which calls -# load_tests() if it finds it. (Unittest.main does the same.) -load_tests = idletest.load_tests +# Before test imports, tell IDLE to avoid changing the environment. +idlelib.testing = True + +# unittest.main and test.libregrtest.runtest.runtest_inner +# call load_tests, when present, to discover tests to run. +from idlelib.idle_test import load_tests if __name__ == '__main__': - unittest.main(verbosity=2, exit=False) + tk.NoDefaultRoot() + unittest.main(exit=False) + tk._support_default_root = 1 + tk._default_root = None diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py index 07157f5..8e4990b 100644 --- a/Lib/test/test_imaplib.py +++ b/Lib/test/test_imaplib.py @@ -243,6 +243,55 @@ class ThreadedNetworkedTests(unittest.TestCase): client.shutdown() @reap_threads + def test_bracket_flags(self): + + # This violates RFC 3501, which disallows ']' characters in tag names, + # but imaplib has allowed producing such tags forever, other programs + # also produce them (eg: OtherInbox's Organizer app as of 20140716), + # and Gmail, for example, accepts them and produces them. So we + # support them. See issue #21815. + + class BracketFlagHandler(SimpleIMAPHandler): + + def handle(self): + self.flags = ['Answered', 'Flagged', 'Deleted', 'Seen', 'Draft'] + super().handle() + + def cmd_AUTHENTICATE(self, tag, args): + self._send_textline('+') + self.server.response = yield + self._send_tagged(tag, 'OK', 'FAKEAUTH successful') + + def cmd_SELECT(self, tag, args): + flag_msg = ' \\'.join(self.flags) + self._send_line(('* FLAGS (%s)' % flag_msg).encode('ascii')) + self._send_line(b'* 2 EXISTS') + self._send_line(b'* 0 RECENT') + msg = ('* OK [PERMANENTFLAGS %s \\*)] Flags permitted.' + % flag_msg) + self._send_line(msg.encode('ascii')) + self._send_tagged(tag, 'OK', '[READ-WRITE] SELECT completed.') + + def cmd_STORE(self, tag, args): + new_flags = args[2].strip('(').strip(')').split() + self.flags.extend(new_flags) + flags_msg = '(FLAGS (%s))' % ' \\'.join(self.flags) + msg = '* %s FETCH %s' % (args[0], flags_msg) + self._send_line(msg.encode('ascii')) + self._send_tagged(tag, 'OK', 'STORE completed.') + + with self.reaped_pair(BracketFlagHandler) as (server, client): + code, data = client.authenticate('MYAUTH', lambda x: b'fake') + self.assertEqual(code, 'OK') + self.assertEqual(server.response, b'ZmFrZQ==\r\n') + client.select('test') + typ, [data] = client.store(b'1', "+FLAGS", "[test]") + self.assertIn(b'[test]', data) + client.select('test') + typ, [data] = client.response('PERMANENTFLAGS') + self.assertIn(b'[test]', data) + + @reap_threads def test_issue5949(self): class EOFHandler(socketserver.StreamRequestHandler): diff --git a/Lib/test/test_imp.py b/Lib/test/test_imp.py index ee9ee1a..4ece365 100644 --- a/Lib/test/test_imp.py +++ b/Lib/test/test_imp.py @@ -6,13 +6,12 @@ import importlib import importlib.util import os import os.path -import shutil import sys from test import support import unittest import warnings with warnings.catch_warnings(): - warnings.simplefilter('ignore', PendingDeprecationWarning) + warnings.simplefilter('ignore', DeprecationWarning) import imp diff --git a/Lib/test/test_importlib/extension/test_case_sensitivity.py b/Lib/test/test_importlib/extension/test_case_sensitivity.py index c112ca7..0dd9c86 100644 --- a/Lib/test/test_importlib/extension/test_case_sensitivity.py +++ b/Lib/test/test_importlib/extension/test_case_sensitivity.py @@ -1,5 +1,4 @@ from importlib import _bootstrap_external -import sys from test import support import unittest @@ -9,8 +8,6 @@ importlib = util.import_importlib('importlib') machinery = util.import_importlib('importlib.machinery') -# XXX find_spec tests - @unittest.skipIf(util.EXTENSIONS.filename is None, '_testcapi not available') @util.case_insensitive_tests class ExtensionModuleCaseSensitivityTest(util.CASEOKTestBase): diff --git a/Lib/test/test_importlib/extension/test_finder.py b/Lib/test/test_importlib/extension/test_finder.py index 71bf67f..c9b4a37 100644 --- a/Lib/test/test_importlib/extension/test_finder.py +++ b/Lib/test/test_importlib/extension/test_finder.py @@ -6,7 +6,6 @@ machinery = util.import_importlib('importlib.machinery') import unittest import warnings -# XXX find_spec tests class FinderTests(abc.FinderTests): diff --git a/Lib/test/test_importlib/extension/test_path_hook.py b/Lib/test/test_importlib/extension/test_path_hook.py index 8f4b8bb..a4b5a64 100644 --- a/Lib/test/test_importlib/extension/test_path_hook.py +++ b/Lib/test/test_importlib/extension/test_path_hook.py @@ -2,8 +2,6 @@ from .. import util machinery = util.import_importlib('importlib.machinery') -import collections -import sys import unittest diff --git a/Lib/test/test_importlib/frozen/test_loader.py b/Lib/test/test_importlib/frozen/test_loader.py index 603c7d7..29ecff1 100644 --- a/Lib/test/test_importlib/frozen/test_loader.py +++ b/Lib/test/test_importlib/frozen/test_loader.py @@ -3,8 +3,6 @@ from .. import util machinery = util.import_importlib('importlib.machinery') - -import sys from test.support import captured_stdout import types import unittest diff --git a/Lib/test/test_importlib/import_/test___package__.py b/Lib/test/test_importlib/import_/test___package__.py index c7d3a2a..7f64548 100644 --- a/Lib/test/test_importlib/import_/test___package__.py +++ b/Lib/test/test_importlib/import_/test___package__.py @@ -5,6 +5,7 @@ of using the typical __path__/__name__ test). """ import unittest +import warnings from .. import util @@ -33,31 +34,50 @@ class Using__package__: """ - def test_using___package__(self): - # [__package__] + def import_module(self, globals_): with self.mock_modules('pkg.__init__', 'pkg.fake') as importer: with util.import_state(meta_path=[importer]): self.__import__('pkg.fake') module = self.__import__('', - globals={'__package__': 'pkg.fake'}, - fromlist=['attr'], level=2) + globals=globals_, + fromlist=['attr'], level=2) + return module + + def test_using___package__(self): + # [__package__] + module = self.import_module({'__package__': 'pkg.fake'}) self.assertEqual(module.__name__, 'pkg') - def test_using___name__(self, package_as_None=False): + def test_using___name__(self): # [__name__] - globals_ = {'__name__': 'pkg.fake', '__path__': []} - if package_as_None: - globals_['__package__'] = None - with self.mock_modules('pkg.__init__', 'pkg.fake') as importer: - with util.import_state(meta_path=[importer]): - self.__import__('pkg.fake') - module = self.__import__('', globals= globals_, - fromlist=['attr'], level=2) - self.assertEqual(module.__name__, 'pkg') + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + module = self.import_module({'__name__': 'pkg.fake', + '__path__': []}) + self.assertEqual(module.__name__, 'pkg') + + def test_warn_when_using___name__(self): + with self.assertWarns(ImportWarning): + self.import_module({'__name__': 'pkg.fake', '__path__': []}) def test_None_as___package__(self): # [None] - self.test_using___name__(package_as_None=True) + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + module = self.import_module({ + '__name__': 'pkg.fake', '__path__': [], '__package__': None }) + self.assertEqual(module.__name__, 'pkg') + + def test_spec_fallback(self): + # If __package__ isn't defined, fall back on __spec__.parent. + module = self.import_module({'__spec__': FakeSpec('pkg.fake')}) + self.assertEqual(module.__name__, 'pkg') + + def test_warn_when_package_and_spec_disagree(self): + # Raise an ImportWarning if __package__ != __spec__.parent. + with self.assertWarns(ImportWarning): + self.import_module({'__package__': 'pkg.fake', + '__spec__': FakeSpec('pkg.fakefake')}) def test_bad__package__(self): globals = {'__package__': '<not real>'} @@ -70,6 +90,11 @@ class Using__package__: self.__import__('', globals, {}, ['relimport'], 1) +class FakeSpec: + def __init__(self, parent): + self.parent = parent + + class Using__package__PEP302(Using__package__): mock_modules = util.mock_modules diff --git a/Lib/test/test_importlib/import_/test_meta_path.py b/Lib/test/test_importlib/import_/test_meta_path.py index c452cdd..5a41e89 100644 --- a/Lib/test/test_importlib/import_/test_meta_path.py +++ b/Lib/test/test_importlib/import_/test_meta_path.py @@ -76,7 +76,6 @@ class CallSignature: self.__import__(mod_name) assert len(log) == 1 args = log[0][0] - kwargs = log[0][1] # Assuming all arguments are positional. self.assertEqual(args[0], mod_name) self.assertIsNone(args[1]) diff --git a/Lib/test/test_importlib/import_/test_packages.py b/Lib/test/test_importlib/import_/test_packages.py index 3755b84..2439604 100644 --- a/Lib/test/test_importlib/import_/test_packages.py +++ b/Lib/test/test_importlib/import_/test_packages.py @@ -1,7 +1,6 @@ from .. import util import sys import unittest -import importlib from test import support diff --git a/Lib/test/test_importlib/import_/test_path.py b/Lib/test/test_importlib/import_/test_path.py index b32a876..7aa26b0 100644 --- a/Lib/test/test_importlib/import_/test_path.py +++ b/Lib/test/test_importlib/import_/test_path.py @@ -16,11 +16,14 @@ class FinderTests: """Tests for PathFinder.""" + find = None + check_found = None + def test_failure(self): # Test None returned upon not finding a suitable loader. module = '<test module>' with util.import_state(): - self.assertIsNone(self.machinery.PathFinder.find_module(module)) + self.assertIsNone(self.find(module)) def test_sys_path(self): # Test that sys.path is used when 'path' is None. @@ -30,8 +33,8 @@ class FinderTests: importer = util.mock_spec(module) with util.import_state(path_importer_cache={path: importer}, path=[path]): - loader = self.machinery.PathFinder.find_module(module) - self.assertIs(loader, importer) + found = self.find(module) + self.check_found(found, importer) def test_path(self): # Test that 'path' is used when set. @@ -40,8 +43,8 @@ class FinderTests: path = '<test path>' importer = util.mock_spec(module) with util.import_state(path_importer_cache={path: importer}): - loader = self.machinery.PathFinder.find_module(module, [path]) - self.assertIs(loader, importer) + found = self.find(module, [path]) + self.check_found(found, importer) def test_empty_list(self): # An empty list should not count as asking for sys.path. @@ -50,7 +53,7 @@ class FinderTests: importer = util.mock_spec(module) with util.import_state(path_importer_cache={path: importer}, path=[path]): - self.assertIsNone(self.machinery.PathFinder.find_module('module', [])) + self.assertIsNone(self.find('module', [])) def test_path_hooks(self): # Test that sys.path_hooks is used. @@ -60,8 +63,8 @@ class FinderTests: importer = util.mock_spec(module) 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) + found = self.find(module, [path]) + self.check_found(found, importer) self.assertIn(path, sys.path_importer_cache) self.assertIs(sys.path_importer_cache[path], importer) @@ -73,7 +76,7 @@ class FinderTests: path=[path_entry]): with warnings.catch_warnings(record=True) as w: warnings.simplefilter('always') - self.assertIsNone(self.machinery.PathFinder.find_module('os')) + self.assertIsNone(self.find('os')) self.assertIsNone(sys.path_importer_cache[path_entry]) self.assertEqual(len(w), 1) self.assertTrue(issubclass(w[-1].category, ImportWarning)) @@ -85,8 +88,8 @@ class FinderTests: importer = util.mock_spec(module) 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) + found = self.find(module) + self.check_found(found, importer) self.assertIn(os.getcwd(), sys.path_importer_cache) def test_None_on_sys_path(self): @@ -182,16 +185,33 @@ class FinderTests: self.assertIsNone(self.machinery.PathFinder.find_spec('whatever')) +class FindModuleTests(FinderTests): + def find(self, *args, **kwargs): + return self.machinery.PathFinder.find_module(*args, **kwargs) + def check_found(self, found, importer): + self.assertIs(found, importer) + + +(Frozen_FindModuleTests, + Source_FindModuleTests +) = util.test_both(FindModuleTests, importlib=importlib, machinery=machinery) -(Frozen_FinderTests, - Source_FinderTests - ) = util.test_both(FinderTests, importlib=importlib, machinery=machinery) +class FindSpecTests(FinderTests): + def find(self, *args, **kwargs): + return self.machinery.PathFinder.find_spec(*args, **kwargs) + def check_found(self, found, importer): + self.assertIs(found.loader, importer) + + +(Frozen_FindSpecTests, + Source_FindSpecTests + ) = util.test_both(FindSpecTests, importlib=importlib, machinery=machinery) class PathEntryFinderTests: - def test_finder_with_failing_find_module(self): + def test_finder_with_failing_find_spec(self): # PathEntryFinder with find_module() defined should work. # Issue #20763. class Finder: @@ -209,6 +229,24 @@ class PathEntryFinderTests: path_hooks=[Finder]): self.machinery.PathFinder.find_spec('importlib') + def test_finder_with_failing_find_module(self): + # PathEntryFinder with find_module() defined should work. + # Issue #20763. + class Finder: + path_location = 'test_finder_with_find_module' + def __init__(self, path): + if path != self.path_location: + raise ImportError + + @staticmethod + def find_module(fullname): + return None + + + with util.import_state(path=[Finder.path_location]+sys.path[:], + path_hooks=[Finder]): + self.machinery.PathFinder.find_module('importlib') + (Frozen_PEFTests, Source_PEFTests diff --git a/Lib/test/test_importlib/import_/test_relative_imports.py b/Lib/test/test_importlib/import_/test_relative_imports.py index 3bb819f..8a95a32 100644 --- a/Lib/test/test_importlib/import_/test_relative_imports.py +++ b/Lib/test/test_importlib/import_/test_relative_imports.py @@ -1,7 +1,8 @@ """Test relative imports (PEP 328).""" from .. import util -import sys import unittest +import warnings + class RelativeImports: @@ -65,9 +66,11 @@ class RelativeImports: uncache_names.append(name[:-len('.__init__')]) with util.mock_spec(*create) as importer: with util.import_state(meta_path=[importer]): - for global_ in globals_: - with util.uncache(*uncache_names): - callback(global_) + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + for global_ in globals_: + with util.uncache(*uncache_names): + callback(global_) def test_module_from_module(self): @@ -204,11 +207,18 @@ class RelativeImports: def test_relative_import_no_globals(self): # No globals for a relative import is an error. - with self.assertRaises(KeyError): - self.__import__('sys', level=1) + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + with self.assertRaises(KeyError): + self.__import__('sys', level=1) + + def test_relative_import_no_package(self): + with self.assertRaises(ImportError): + self.__import__('a', {'__package__': '', '__spec__': None}, + level=1) def test_relative_import_no_package_exists_absolute(self): - with self.assertRaises(SystemError): + with self.assertRaises(ImportError): self.__import__('sys', {'__package__': '', '__spec__': None}, level=1) diff --git a/Lib/test/test_importlib/regrtest.py b/Lib/test/test_importlib/regrtest.py deleted file mode 100644 index a5be11f..0000000 --- a/Lib/test/test_importlib/regrtest.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Run Python's standard test suite using importlib.__import__. - -Tests known to fail because of assumptions that importlib (properly) -invalidates are automatically skipped if the entire test suite is run. -Otherwise all command-line options valid for test.regrtest are also valid for -this script. - -""" -import importlib -import sys -from test import regrtest - -if __name__ == '__main__': - __builtins__.__import__ = importlib.__import__ - sys.path_importer_cache.clear() - - regrtest.main(quiet=True, verbose2=True) diff --git a/Lib/test/test_importlib/source/test_case_sensitivity.py b/Lib/test/test_importlib/source/test_case_sensitivity.py index 34b86cd..12ce0cb 100644 --- a/Lib/test/test_importlib/source/test_case_sensitivity.py +++ b/Lib/test/test_importlib/source/test_case_sensitivity.py @@ -5,7 +5,6 @@ importlib = util.import_importlib('importlib') machinery = util.import_importlib('importlib.machinery') import os -import sys from test import support as test_support import unittest diff --git a/Lib/test/test_importlib/source/test_file_loader.py b/Lib/test/test_importlib/source/test_file_loader.py index 73f4c62..a151149 100644 --- a/Lib/test/test_importlib/source/test_file_loader.py +++ b/Lib/test/test_importlib/source/test_file_loader.py @@ -217,7 +217,7 @@ class SimpleTest(abc.LoaderTests): # PEP 302 with warnings.catch_warnings(): warnings.simplefilter('ignore', DeprecationWarning) - mod = loader.load_module('_temp') # XXX + mod = loader.load_module('_temp') # Sanity checks. self.assertEqual(mod.__cached__, compiled) self.assertEqual(mod.x, 5) @@ -245,12 +245,7 @@ class SimpleTest(abc.LoaderTests): class BadBytecodeTest: def import_(self, file, module_name): - loader = self.loader(module_name, file) - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - # XXX Change to use exec_module(). - module = loader.load_module(module_name) - self.assertIn(module_name, sys.modules) + raise NotImplementedError def manipulate_bytecode(self, name, mapping, manipulator, *, del_source=False): diff --git a/Lib/test/test_importlib/source/test_path_hook.py b/Lib/test/test_importlib/source/test_path_hook.py index e6a2415..795d436 100644 --- a/Lib/test/test_importlib/source/test_path_hook.py +++ b/Lib/test/test_importlib/source/test_path_hook.py @@ -16,10 +16,19 @@ class PathHookTest: def test_success(self): with util.create_modules('dummy') as mapping: self.assertTrue(hasattr(self.path_hook()(mapping['.root']), - 'find_module')) + 'find_spec')) + + def test_success_legacy(self): + with util.create_modules('dummy') as mapping: + self.assertTrue(hasattr(self.path_hook()(mapping['.root']), + 'find_module')) def test_empty_string(self): # The empty string represents the cwd. + self.assertTrue(hasattr(self.path_hook()(''), 'find_spec')) + + def test_empty_string_legacy(self): + # The empty string represents the cwd. self.assertTrue(hasattr(self.path_hook()(''), 'find_module')) diff --git a/Lib/test/test_importlib/source/test_source_encoding.py b/Lib/test/test_importlib/source/test_source_encoding.py index 1e0771b..980855f 100644 --- a/Lib/test/test_importlib/source/test_source_encoding.py +++ b/Lib/test/test_importlib/source/test_source_encoding.py @@ -5,7 +5,6 @@ machinery = util.import_importlib('importlib.machinery') import codecs import importlib.util import re -import sys import types # Because sys.path gets essentially blanked, need to have unicodedata already # imported for the parser to use. diff --git a/Lib/test/test_importlib/test_abc.py b/Lib/test/test_importlib/test_abc.py index d4bf915..c862480 100644 --- a/Lib/test/test_importlib/test_abc.py +++ b/Lib/test/test_importlib/test_abc.py @@ -207,6 +207,10 @@ class LoaderDefaultsTests(ABCTestHarness): SPLIT = make_abc_subclasses(Loader) + def test_create_module(self): + spec = 'a spec' + self.assertIsNone(self.ins.create_module(spec)) + def test_load_module(self): with self.assertRaises(ImportError): self.ins.load_module('something') @@ -519,6 +523,12 @@ class InspectLoaderLoadModuleTests: support.unload(self.module_name) self.addCleanup(support.unload, self.module_name) + def load(self, loader): + spec = self.util.spec_from_loader(self.module_name, loader) + with warnings.catch_warnings(): + warnings.simplefilter('ignore', DeprecationWarning) + return self.init._bootstrap._load_unlocked(spec) + def mock_get_code(self): return mock.patch.object(self.InspectLoaderSubclass, 'get_code') @@ -528,9 +538,7 @@ class InspectLoaderLoadModuleTests: mocked_get_code.side_effect = ImportError with self.assertRaises(ImportError): loader = self.InspectLoaderSubclass() - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - loader.load_module(self.module_name) + self.load(loader) def test_get_code_None(self): # If get_code() returns None, raise ImportError. @@ -538,7 +546,7 @@ class InspectLoaderLoadModuleTests: mocked_get_code.return_value = None with self.assertRaises(ImportError): loader = self.InspectLoaderSubclass() - loader.load_module(self.module_name) + self.load(loader) def test_module_returned(self): # The loaded module should be returned. @@ -546,14 +554,16 @@ class InspectLoaderLoadModuleTests: with self.mock_get_code() as mocked_get_code: mocked_get_code.return_value = code loader = self.InspectLoaderSubclass() - module = loader.load_module(self.module_name) + module = self.load(loader) self.assertEqual(module, sys.modules[self.module_name]) (Frozen_ILLoadModuleTests, Source_ILLoadModuleTests ) = test_util.test_both(InspectLoaderLoadModuleTests, - InspectLoaderSubclass=SPLIT_IL) + InspectLoaderSubclass=SPLIT_IL, + init=init, + util=util) ##### ExecutionLoader concrete methods ######################################### diff --git a/Lib/test/test_importlib/test_api.py b/Lib/test/test_importlib/test_api.py index 6bc3c56..b0a94aa 100644 --- a/Lib/test/test_importlib/test_api.py +++ b/Lib/test/test_importlib/test_api.py @@ -99,9 +99,7 @@ class ImportModuleTests: class FindLoaderTests: - class FakeMetaFinder: - @staticmethod - def find_module(name, path=None): return name, path + FakeMetaFinder = None def test_sys_modules(self): # If a module with __loader__ is in sys.modules, then return it. @@ -171,9 +169,30 @@ class FindLoaderTests: self.assertIsNone(self.init.find_loader('nevergoingtofindthismodule')) -(Frozen_FindLoaderTests, - Source_FindLoaderTests - ) = test_util.test_both(FindLoaderTests, init=init) +class FindLoaderPEP451Tests(FindLoaderTests): + + class FakeMetaFinder: + @staticmethod + def find_spec(name, path=None, target=None): + return machinery['Source'].ModuleSpec(name, (name, path)) + + +(Frozen_FindLoaderPEP451Tests, + Source_FindLoaderPEP451Tests + ) = test_util.test_both(FindLoaderPEP451Tests, init=init) + + +class FindLoaderPEP302Tests(FindLoaderTests): + + class FakeMetaFinder: + @staticmethod + def find_module(name, path=None): + return name, path + + +(Frozen_FindLoaderPEP302Tests, + Source_FindLoaderPEP302Tests + ) = test_util.test_both(FindLoaderPEP302Tests, init=init) class ReloadTests: diff --git a/Lib/test/test_importlib/test_lazy.py b/Lib/test/test_importlib/test_lazy.py index cc383c2..ffd8dc6 100644 --- a/Lib/test/test_importlib/test_lazy.py +++ b/Lib/test/test_importlib/test_lazy.py @@ -66,6 +66,8 @@ class LazyLoaderTests(unittest.TestCase): spec = util.spec_from_loader(TestingImporter.module_name, util.LazyLoader(loader)) module = spec.loader.create_module(spec) + if module is None: + module = types.ModuleType(TestingImporter.module_name) module.__spec__ = spec module.__loader__ = spec.loader spec.loader.exec_module(module) diff --git a/Lib/test/test_importlib/test_locks.py b/Lib/test/test_importlib/test_locks.py index df0af12..b2aadff 100644 --- a/Lib/test/test_importlib/test_locks.py +++ b/Lib/test/test_importlib/test_locks.py @@ -3,7 +3,6 @@ from . import util as test_util init = test_util.import_importlib('importlib') import sys -import time import unittest import weakref diff --git a/Lib/test/test_importlib/test_namespace_pkgs.py b/Lib/test/test_importlib/test_namespace_pkgs.py index 6639612..e37d8a1 100644 --- a/Lib/test/test_importlib/test_namespace_pkgs.py +++ b/Lib/test/test_importlib/test_namespace_pkgs.py @@ -1,13 +1,10 @@ import contextlib -import importlib.abc -import importlib.machinery +import importlib import os import sys -import types import unittest from test.test_importlib import util -from test.support import run_unittest # needed tests: # @@ -71,6 +68,7 @@ class NamespacePackageTest(unittest.TestCase): # TODO: will we ever want to pass exc_info to __exit__? self.ctx.__exit__(None, None, None) + class SingleNamespacePackage(NamespacePackageTest): paths = ['portion1'] @@ -87,7 +85,7 @@ class SingleNamespacePackage(NamespacePackageTest): self.assertEqual(repr(foo), "<module 'foo' (namespace)>") -class DynamicPatheNamespacePackage(NamespacePackageTest): +class DynamicPathNamespacePackage(NamespacePackageTest): paths = ['portion1'] def test_dynamic_path(self): @@ -289,5 +287,35 @@ class ModuleAndNamespacePackageInSameDir(NamespacePackageTest): self.assertEqual(a_test.attr, 'in module') +class ReloadTests(NamespacePackageTest): + paths = ['portion1'] + + def test_simple_package(self): + import foo.one + foo = importlib.reload(foo) + self.assertEqual(foo.one.attr, 'portion1 foo one') + + def test_cant_import_other(self): + import foo + with self.assertRaises(ImportError): + import foo.two + foo = importlib.reload(foo) + with self.assertRaises(ImportError): + import foo.two + + def test_dynamic_path(self): + import foo.one + with self.assertRaises(ImportError): + import foo.two + + # Now modify sys.path and reload. + sys.path.append(os.path.join(self.root, 'portion2')) + foo = importlib.reload(foo) + + # And make sure foo.two is now importable + import foo.two + self.assertEqual(foo.two.attr, 'portion2 foo two') + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py index 69466b2..41ca333 100644 --- a/Lib/test/test_importlib/test_util.py +++ b/Lib/test/test_importlib/test_util.py @@ -372,7 +372,7 @@ class ResolveNameTests: # bacon self.assertEqual('bacon', self.util.resolve_name('bacon', None)) - def test_aboslute_within_package(self): + def test_absolute_within_package(self): # bacon in spam self.assertEqual('bacon', self.util.resolve_name('bacon', 'spam')) diff --git a/Lib/test/test_importlib/test_windows.py b/Lib/test/test_importlib/test_windows.py index c893bcf..005b685 100644 --- a/Lib/test/test_importlib/test_windows.py +++ b/Lib/test/test_importlib/test_windows.py @@ -40,7 +40,7 @@ def setup_module(machinery, name, path=None): else: root = machinery.WindowsRegistryFinder.REGISTRY_KEY key = root.format(fullname=name, - sys_version=sys.version[:3]) + sys_version='%d.%d' % sys.version_info[:2]) try: with temp_module(name, "a = 1") as location: subkey = CreateKey(HKEY_CURRENT_USER, key) diff --git a/Lib/test/test_importlib/util.py b/Lib/test/test_importlib/util.py index 43896c5..64e039e 100644 --- a/Lib/test/test_importlib/util.py +++ b/Lib/test/test_importlib/util.py @@ -266,7 +266,6 @@ class mock_spec(_ImporterMock): module = self.modules[fullname] except KeyError: return None - is_package = hasattr(module, '__path__') spec = util.spec_from_file_location( fullname, module.__file__, loader=self, submodule_search_locations=getattr(module, '__path__', None)) diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 671e05a..47244ae 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -30,6 +30,7 @@ from test.support import MISSING_C_DOCSTRINGS, cpython_only from test.support.script_helper import assert_python_ok, assert_python_failure from test import inspect_fodder as mod from test import inspect_fodder2 as mod2 +from test import support from test.test_import import _ready_to_import @@ -38,7 +39,7 @@ from test.test_import import _ready_to_import # ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode, # isbuiltin, isroutine, isgenerator, isgeneratorfunction, getmembers, # getdoc, getfile, getmodule, getsourcefile, getcomments, getsource, -# getclasstree, getargspec, getargvalues, formatargspec, formatargvalues, +# getclasstree, getargvalues, formatargspec, formatargvalues, # currentframe, stack, trace, isdatadescriptor # NOTE: There are some additional tests relating to interaction with @@ -400,7 +401,7 @@ class TestRetrievingSourceCode(GetSourceBase): self.assertEqual(normcase(inspect.getsourcefile(mod.spam)), modfile) self.assertEqual(normcase(inspect.getsourcefile(git.abuse)), modfile) fn = "_non_existing_filename_used_for_sourcefile_test.py" - co = compile("None", fn, "exec") + co = compile("x=1", fn, "exec") self.assertEqual(inspect.getsourcefile(co), None) linecache.cache[co.co_filename] = (1, None, "None", co.co_filename) try: @@ -2902,6 +2903,10 @@ class TestParameterObject(unittest.TestCase): 'is not a valid parameter name'): inspect.Parameter('$', kind=inspect.Parameter.VAR_KEYWORD) + with self.assertRaisesRegex(ValueError, + 'is not a valid parameter name'): + inspect.Parameter('.a', kind=inspect.Parameter.VAR_KEYWORD) + with self.assertRaisesRegex(ValueError, 'cannot have default values'): inspect.Parameter('a', default=42, kind=inspect.Parameter.VAR_KEYWORD) @@ -2985,6 +2990,17 @@ class TestParameterObject(unittest.TestCase): with self.assertRaisesRegex(TypeError, 'name must be a str'): inspect.Parameter(None, kind=inspect.Parameter.POSITIONAL_ONLY) + @cpython_only + def test_signature_parameter_implicit(self): + with self.assertRaisesRegex(ValueError, + 'implicit arguments must be passed in as'): + inspect.Parameter('.0', kind=inspect.Parameter.POSITIONAL_ONLY) + + param = inspect.Parameter( + '.0', kind=inspect.Parameter.POSITIONAL_OR_KEYWORD) + self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_ONLY) + self.assertEqual(param.name, 'implicit0') + def test_signature_parameter_immutability(self): p = inspect.Parameter('spam', kind=inspect.Parameter.KEYWORD_ONLY) @@ -3233,6 +3249,17 @@ class TestSignatureBind(unittest.TestCase): ba = sig.bind(args=1) self.assertEqual(ba.arguments, {'kwargs': {'args': 1}}) + @cpython_only + def test_signature_bind_implicit_arg(self): + # Issue #19611: getcallargs should work with set comprehensions + def make_set(): + return {z * z for z in range(5)} + setcomp_code = make_set.__code__.co_consts[1] + setcomp_func = types.FunctionType(setcomp_code, {}) + + iterator = iter(range(5)) + self.assertEqual(self.call(setcomp_func, iterator), {0, 1, 4, 9, 16}) + class TestBoundArguments(unittest.TestCase): def test_signature_bound_arguments_unhashable(self): @@ -3543,14 +3570,14 @@ class TestMain(unittest.TestCase): def test_details(self): module = importlib.import_module('unittest') - rc, out, err = assert_python_ok('-m', 'inspect', + args = support.optim_args_from_interpreter_flags() + rc, out, err = assert_python_ok(*args, '-m', 'inspect', 'unittest', '--details') output = out.decode() # Just a quick sanity check on the output self.assertIn(module.__name__, output) self.assertIn(module.__file__, output) - if not sys.flags.optimize: - self.assertIn(module.__cached__, output) + self.assertIn(module.__cached__, output) self.assertEqual(err, b'') diff --git a/Lib/test/test_int.py b/Lib/test/test_int.py index b66c5d6..8847f4c 100644 --- a/Lib/test/test_int.py +++ b/Lib/test/test_int.py @@ -430,21 +430,24 @@ class IntTestCases(unittest.TestCase): with self.assertWarns(DeprecationWarning): n = int(bad_int) self.assertEqual(n, 1) + self.assertIs(type(n), int) bad_int = BadInt2() with self.assertWarns(DeprecationWarning): n = int(bad_int) self.assertEqual(n, 1) + self.assertIs(type(n), int) bad_int = TruncReturnsBadInt() with self.assertWarns(DeprecationWarning): n = int(bad_int) self.assertEqual(n, 1) + self.assertIs(type(n), int) good_int = TruncReturnsIntSubclass() n = int(good_int) self.assertEqual(n, 1) - self.assertIs(type(n), bool) + self.assertIs(type(n), int) n = IntSubclass(good_int) self.assertEqual(n, 1) self.assertIs(type(n), IntSubclass) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 5111882..c48ec3a 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -496,7 +496,11 @@ class IOTest(unittest.TestCase): def test_open_handles_NUL_chars(self): fn_with_NUL = 'foo\0bar' self.assertRaises(ValueError, self.open, fn_with_NUL, 'w') - self.assertRaises(ValueError, self.open, bytes(fn_with_NUL, 'ascii'), 'w') + + bytes_fn = bytes(fn_with_NUL, 'ascii') + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + self.assertRaises(ValueError, self.open, bytes_fn, 'w') def test_raw_file_io(self): with self.open(support.TESTFN, "wb", buffering=0) as f: @@ -856,6 +860,32 @@ class IOTest(unittest.TestCase): self.assertEqual(getattr(stream, method)(buffer), 5) self.assertEqual(bytes(buffer), b"12345") + def test_fspath_support(self): + class PathLike: + def __init__(self, path): + self.path = path + + def __fspath__(self): + return self.path + + def check_path_succeeds(path): + with self.open(path, "w") as f: + f.write("egg\n") + + with self.open(path, "r") as f: + self.assertEqual(f.read(), "egg\n") + + check_path_succeeds(PathLike(support.TESTFN)) + check_path_succeeds(PathLike(support.TESTFN.encode('utf-8'))) + + bad_path = PathLike(TypeError) + with self.assertRaises(TypeError): + self.open(bad_path, 'w') + + # ensure that refcounting is correct with some error conditions + with self.assertRaisesRegex(ValueError, 'read/write/append mode'): + self.open(PathLike(support.TESTFN), 'rwxa') + class CIOTest(IOTest): @@ -3246,8 +3276,7 @@ class CTextIOWrapperTest(TextIOWrapperTest): class PyTextIOWrapperTest(TextIOWrapperTest): io = pyio - #shutdown_error = "LookupError: unknown encoding: ascii" - shutdown_error = "TypeError: 'NoneType' object is not iterable" + shutdown_error = "LookupError: unknown encoding: ascii" class IncrementalNewlineDecoderTest(unittest.TestCase): diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py index 94bf4cd..5f08f0c 100644 --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -1176,6 +1176,7 @@ class IpaddrUnitTest(unittest.TestCase): self.assertEqual(str(self.ipv6_network[5]), '2001:658:22a:cafe::5') + self.assertRaises(IndexError, self.ipv6_network.__getitem__, 1 << 64) def testGetitem(self): # http://code.google.com/p/ipaddr-py/issues/detail?id=15 @@ -1262,7 +1263,7 @@ class IpaddrUnitTest(unittest.TestCase): ip4 = ipaddress.IPv4Address('1.1.1.3') ip5 = ipaddress.IPv4Address('1.1.1.4') ip6 = ipaddress.IPv4Address('1.1.1.0') - # check that addreses are subsumed properly. + # check that addresses are subsumed properly. collapsed = ipaddress.collapse_addresses( [ip1, ip2, ip3, ip4, ip5, ip6]) self.assertEqual(list(collapsed), @@ -1276,7 +1277,7 @@ class IpaddrUnitTest(unittest.TestCase): ip4 = ipaddress.IPv4Address('1.1.1.3') #ip5 = ipaddress.IPv4Interface('1.1.1.4/30') #ip6 = ipaddress.IPv4Interface('1.1.1.4/30') - # check that addreses are subsumed properly. + # check that addresses are subsumed properly. collapsed = ipaddress.collapse_addresses([ip1, ip2, ip3, ip4]) self.assertEqual(list(collapsed), [ipaddress.IPv4Network('1.1.1.0/30')]) @@ -1290,7 +1291,7 @@ class IpaddrUnitTest(unittest.TestCase): # stored in no particular order b/c we want CollapseAddr to call # [].sort ip6 = ipaddress.IPv4Network('1.1.0.0/22') - # check that addreses are subsumed properly. + # check that addresses are subsumed properly. collapsed = ipaddress.collapse_addresses([ip1, ip2, ip3, ip4, ip5, ip6]) self.assertEqual(list(collapsed), diff --git a/Lib/test/test_iter.py b/Lib/test/test_iter.py index a91670b..542b284 100644 --- a/Lib/test/test_iter.py +++ b/Lib/test/test_iter.py @@ -54,6 +54,14 @@ class UnlimitedSequenceClass: def __getitem__(self, i): return i +class DefaultIterClass: + pass + +class NoIterClass: + def __getitem__(self, i): + return i + __iter__ = None + # Main test suite class TestCase(unittest.TestCase): @@ -995,6 +1003,10 @@ class TestCase(unittest.TestCase): def test_free_after_iterating(self): check_free_after_iterating(self, iter, SequenceClass, (0,)) + def test_error_iter(self): + for typ in (DefaultIterClass, NoIterClass): + self.assertRaises(TypeError, iter, typ()) + def test_main(): run_unittest(TestCase) diff --git a/Lib/test/test_iterlen.py b/Lib/test/test_iterlen.py index 152f5fc..41c9752 100644 --- a/Lib/test/test_iterlen.py +++ b/Lib/test/test_iterlen.py @@ -42,7 +42,6 @@ enumerate(iter('abc')). """ import unittest -from test import support from itertools import repeat from collections import deque from operator import length_hint diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py index f940852..945c58d 100644 --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -4,7 +4,6 @@ from itertools import * import weakref from decimal import Decimal from fractions import Fraction -import sys import operator import random import copy @@ -613,6 +612,56 @@ class TestBasicOps(unittest.TestCase): for proto in range(pickle.HIGHEST_PROTOCOL + 1): self.pickletest(proto, cycle('abc')) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + # test with partial consumed input iterable + it = iter('abcde') + c = cycle(it) + _ = [next(c) for i in range(2)] # consume 2 of 5 inputs + p = pickle.dumps(c, proto) + d = pickle.loads(p) # rebuild the cycle object + self.assertEqual(take(20, d), list('cdeabcdeabcdeabcdeab')) + + # test with completely consumed input iterable + it = iter('abcde') + c = cycle(it) + _ = [next(c) for i in range(7)] # consume 7 of 5 inputs + p = pickle.dumps(c, proto) + d = pickle.loads(p) # rebuild the cycle object + self.assertEqual(take(20, d), list('cdeabcdeabcdeabcdeab')) + + def test_cycle_setstate(self): + # Verify both modes for restoring state + + # Mode 0 is efficient. It uses an incompletely consumed input + # iterator to build a cycle object and then passes in state with + # a list of previously consumed values. There is no data + # overlap between the two. + c = cycle('defg') + c.__setstate__((list('abc'), 0)) + self.assertEqual(take(20, c), list('defgabcdefgabcdefgab')) + + # Mode 1 is inefficient. It starts with a cycle object built + # from an iterator over the remaining elements in a partial + # cycle and then passes in state with all of the previously + # seen values (this overlaps values included in the iterator). + c = cycle('defg') + c.__setstate__((list('abcdefg'), 1)) + self.assertEqual(take(20, c), list('defgabcdefgabcdefgab')) + + # The first argument to setstate needs to be a tuple + with self.assertRaises(SystemError): + cycle('defg').__setstate__([list('abcdefg'), 0]) + + # The first argument in the setstate tuple must be a list + with self.assertRaises(TypeError): + c = cycle('defg') + c.__setstate__((dict.fromkeys('defg'), 0)) + take(20, c) + + # The first argument in the setstate tuple must be a list + with self.assertRaises(TypeError): + cycle('defg').__setstate__((list('abcdefg'), 'x')) + def test_groupby(self): # Check whether it accepts arguments correctly self.assertEqual([], list(groupby([]))) diff --git a/Lib/test/test_json/__init__.py b/Lib/test/test_json/__init__.py index 0807e6f..bac370d 100644 --- a/Lib/test/test_json/__init__.py +++ b/Lib/test/test_json/__init__.py @@ -1,5 +1,4 @@ import os -import sys import json import doctest import unittest diff --git a/Lib/test/test_json/test_fail.py b/Lib/test/test_json/test_fail.py index 95ff5b8..7910521 100644 --- a/Lib/test/test_json/test_fail.py +++ b/Lib/test/test_json/test_fail.py @@ -1,5 +1,4 @@ from test.test_json import PyTest, CTest -import re # 2007-10-05 JSONDOCS = [ diff --git a/Lib/test/test_kqueue.py b/Lib/test/test_kqueue.py index f822024..9f49886 100644 --- a/Lib/test/test_kqueue.py +++ b/Lib/test/test_kqueue.py @@ -5,7 +5,6 @@ import errno import os import select import socket -import sys import time import unittest diff --git a/Lib/test/test_linecache.py b/Lib/test/test_linecache.py index 47e2edd..375d9c4 100644 --- a/Lib/test/test_linecache.py +++ b/Lib/test/test_linecache.py @@ -3,6 +3,8 @@ import linecache import unittest import os.path +import tempfile +import tokenize from test import support @@ -10,8 +12,6 @@ FILENAME = linecache.__file__ NONEXISTENT_FILENAME = FILENAME + '.missing' INVALID_NAME = '!@$)(!@#_1' EMPTY = '' -TESTS = 'inspect_fodder inspect_fodder2 mapping_tests' -TESTS = TESTS.split() TEST_PATH = os.path.dirname(__file__) MODULES = "linecache abc".split() MODULE_PATH = os.path.dirname(FILENAME) @@ -37,6 +37,65 @@ def f(): return 3''' # No ending newline +class TempFile: + + def setUp(self): + super().setUp() + with tempfile.NamedTemporaryFile(delete=False) as fp: + self.file_name = fp.name + fp.write(self.file_byte_string) + self.addCleanup(support.unlink, self.file_name) + + +class GetLineTestsGoodData(TempFile): + # file_list = ['list\n', 'of\n', 'good\n', 'strings\n'] + + def setUp(self): + self.file_byte_string = ''.join(self.file_list).encode('utf-8') + super().setUp() + + def test_getline(self): + with tokenize.open(self.file_name) as fp: + for index, line in enumerate(fp): + if not line.endswith('\n'): + line += '\n' + + cached_line = linecache.getline(self.file_name, index + 1) + self.assertEqual(line, cached_line) + + def test_getlines(self): + lines = linecache.getlines(self.file_name) + self.assertEqual(lines, self.file_list) + + +class GetLineTestsBadData(TempFile): + # file_byte_string = b'Bad data goes here' + + def test_getline(self): + self.assertRaises((SyntaxError, UnicodeDecodeError), + linecache.getline, self.file_name, 1) + + def test_getlines(self): + self.assertRaises((SyntaxError, UnicodeDecodeError), + linecache.getlines, self.file_name) + + +class EmptyFile(GetLineTestsGoodData, unittest.TestCase): + file_list = [] + + +class SingleEmptyLine(GetLineTestsGoodData, unittest.TestCase): + file_list = ['\n'] + + +class GoodUnicode(GetLineTestsGoodData, unittest.TestCase): + file_list = ['á\n', 'b\n', 'abcdef\n', 'ááááá\n'] + + +class BadUnicode(GetLineTestsBadData, unittest.TestCase): + file_byte_string = b'\x80abc' + + class LineCacheTests(unittest.TestCase): def test_getline(self): @@ -53,13 +112,6 @@ class LineCacheTests(unittest.TestCase): self.assertEqual(getline(EMPTY, 1), EMPTY) self.assertEqual(getline(INVALID_NAME, 1), EMPTY) - # Check whether lines correspond to those from file iteration - for entry in TESTS: - filename = os.path.join(TEST_PATH, entry) + '.py' - with open(filename) as file: - for index, line in enumerate(file): - self.assertEqual(line, getline(filename, index + 1)) - # Check module loading for entry in MODULES: filename = os.path.join(MODULE_PATH, entry) + '.py' @@ -80,12 +132,13 @@ class LineCacheTests(unittest.TestCase): def test_clearcache(self): cached = [] - for entry in TESTS: - filename = os.path.join(TEST_PATH, entry) + '.py' + for entry in MODULES: + filename = os.path.join(MODULE_PATH, entry) + '.py' cached.append(filename) linecache.getline(filename, 1) # Are all files cached? + self.assertNotEqual(cached, []) cached_empty = [fn for fn in cached if fn not in linecache.cache] self.assertEqual(cached_empty, []) diff --git a/Lib/test/test_list.py b/Lib/test/test_list.py index 8f82ab5..aee62dc 100644 --- a/Lib/test/test_list.py +++ b/Lib/test/test_list.py @@ -1,5 +1,5 @@ import sys -from test import support, list_tests +from test import list_tests import pickle import unittest diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index cb6a628..ff0012b 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -26,6 +26,7 @@ import logging.config import codecs import configparser import datetime +import pathlib import pickle import io import gc @@ -308,6 +309,10 @@ class BuiltinLevelsTest(BaseTest): self.assertEqual(logging.getLevelName('INFO'), logging.INFO) self.assertEqual(logging.getLevelName(logging.INFO), 'INFO') + def test_issue27935(self): + fatal = logging.getLevelName('FATAL') + self.assertEqual(fatal, logging.FATAL) + class BasicFilterTest(BaseTest): """Test the bundled Filter class.""" @@ -575,6 +580,29 @@ class HandlerTest(BaseTest): self.assertFalse(h.shouldFlush(r)) h.close() + def test_path_objects(self): + """ + Test that Path objects are accepted as filename arguments to handlers. + + See Issue #27493. + """ + fd, fn = tempfile.mkstemp() + os.close(fd) + os.unlink(fn) + pfn = pathlib.Path(fn) + cases = ( + (logging.FileHandler, (pfn, 'w')), + (logging.handlers.RotatingFileHandler, (pfn, 'a')), + (logging.handlers.TimedRotatingFileHandler, (pfn, 'h')), + ) + if sys.platform in ('linux', 'darwin'): + cases += ((logging.handlers.WatchedFileHandler, (pfn, 'w')),) + for cls, args in cases: + h = cls(*args) + self.assertTrue(os.path.exists(fn)) + h.close() + os.unlink(fn) + @unittest.skipIf(os.name == 'nt', 'WatchedFileHandler not appropriate for Windows.') @unittest.skipUnless(threading, 'Threading required for this test.') def test_race(self): @@ -958,7 +986,7 @@ class MemoryHandlerTest(BaseTest): def setUp(self): BaseTest.setUp(self) self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING, - self.root_hdlr) + self.root_hdlr) self.mem_logger = logging.getLogger('mem') self.mem_logger.propagate = 0 self.mem_logger.addHandler(self.mem_hdlr) @@ -995,6 +1023,36 @@ class MemoryHandlerTest(BaseTest): self.mem_logger.debug(self.next_message()) self.assert_log_lines(lines) + def test_flush_on_close(self): + """ + Test that the flush-on-close configuration works as expected. + """ + self.mem_logger.debug(self.next_message()) + self.assert_log_lines([]) + self.mem_logger.info(self.next_message()) + self.assert_log_lines([]) + self.mem_logger.removeHandler(self.mem_hdlr) + # Default behaviour is to flush on close. Check that it happens. + self.mem_hdlr.close() + lines = [ + ('DEBUG', '1'), + ('INFO', '2'), + ] + self.assert_log_lines(lines) + # Now configure for flushing not to be done on close. + self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING, + self.root_hdlr, + False) + self.mem_logger.addHandler(self.mem_hdlr) + self.mem_logger.debug(self.next_message()) + self.assert_log_lines(lines) # no change + self.mem_logger.info(self.next_message()) + self.assert_log_lines(lines) # no change + self.mem_logger.removeHandler(self.mem_hdlr) + self.mem_hdlr.close() + # assert that no new lines have been added + self.assert_log_lines(lines) # no change + class ExceptionFormatter(logging.Formatter): """A special exception formatter.""" @@ -4161,6 +4219,17 @@ class NTEventLogHandlerTest(BaseTest): msg = 'Record not found in event log, went back %d records' % GO_BACK self.assertTrue(found, msg=msg) + +class MiscTestCase(unittest.TestCase): + def test__all__(self): + blacklist = {'logThreads', 'logMultiprocessing', + 'logProcesses', 'currentframe', + 'PercentStyle', 'StrFormatStyle', 'StringTemplateStyle', + 'Filterer', 'PlaceHolder', 'Manager', 'RootLogger', + 'root'} + support.check__all__(self, logging, blacklist=blacklist) + + # 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. @@ -4177,7 +4246,8 @@ def test_main(): RotatingFileHandlerTest, LastResortTest, LogRecordTest, ExceptionTest, SysLogHandlerTest, HTTPHandlerTest, NTEventLogHandlerTest, TimedRotatingFileHandlerTest, - UnixSocketHandlerTest, UnixDatagramHandlerTest, UnixSysLogHandlerTest) + UnixSocketHandlerTest, UnixDatagramHandlerTest, UnixSysLogHandlerTest, + MiscTestCase) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py index 4b2d81c..4cc4b05 100644 --- a/Lib/test/test_long.py +++ b/Lib/test/test_long.py @@ -689,6 +689,20 @@ class LongTest(unittest.TestCase): self.assertRaises(OverflowError, int, float('-inf')) self.assertRaises(ValueError, int, float('nan')) + def test_mod_division(self): + with self.assertRaises(ZeroDivisionError): + _ = 1 % 0 + + self.assertEqual(13 % 10, 3) + self.assertEqual(-13 % 10, 7) + self.assertEqual(13 % -10, -7) + self.assertEqual(-13 % -10, -3) + + self.assertEqual(12 % 4, 0) + self.assertEqual(-12 % 4, 0) + self.assertEqual(12 % -4, 0) + self.assertEqual(-12 % -4, 0) + def test_true_division(self): huge = 1 << 40000 mhuge = -huge @@ -723,6 +737,25 @@ class LongTest(unittest.TestCase): for zero in ["huge / 0", "mhuge / 0"]: self.assertRaises(ZeroDivisionError, eval, zero, namespace) + def test_floordiv(self): + with self.assertRaises(ZeroDivisionError): + _ = 1 // 0 + + self.assertEqual(2 // 3, 0) + self.assertEqual(2 // -3, -1) + self.assertEqual(-2 // 3, -1) + self.assertEqual(-2 // -3, 0) + + self.assertEqual(-11 // -3, 3) + self.assertEqual(-11 // 3, -4) + self.assertEqual(11 // -3, -4) + self.assertEqual(11 // 3, 3) + + self.assertEqual(-12 // -3, 4) + self.assertEqual(-12 // 3, -4) + self.assertEqual(12 // -3, -4) + self.assertEqual(12 // 3, 4) + def check_truediv(self, a, b, skip_small=True): """Verify that the result of a/b is correctly rounded, by comparing it with a pure Python implementation of correctly @@ -845,6 +878,21 @@ class LongTest(unittest.TestCase): self.check_truediv(-x, y) self.check_truediv(-x, -y) + def test_lshift_of_zero(self): + self.assertEqual(0 << 0, 0) + self.assertEqual(0 << 10, 0) + with self.assertRaises(ValueError): + 0 << -1 + + @support.cpython_only + def test_huge_lshift_of_zero(self): + # Shouldn't try to allocate memory for a huge shift. See issue #27870. + # Other implementations may have a different boundary for overflow, + # or not raise at all. + self.assertEqual(0 << sys.maxsize, 0) + with self.assertRaises(OverflowError): + 0 << (sys.maxsize + 1) + def test_small_ints(self): for i in range(-5, 257): self.assertIs(i, i + 0) diff --git a/Lib/test/test_macpath.py b/Lib/test/test_macpath.py index 80bec7a..0698ff5 100644 --- a/Lib/test/test_macpath.py +++ b/Lib/test/test_macpath.py @@ -1,5 +1,5 @@ import macpath -from test import support, test_genericpath +from test import test_genericpath import unittest diff --git a/Lib/test/test_mailbox.py b/Lib/test/test_mailbox.py index 0991f74..aeabdbb 100644 --- a/Lib/test/test_mailbox.py +++ b/Lib/test/test_mailbox.py @@ -7,17 +7,12 @@ import email import email.message import re import io -import shutil import tempfile from test import support import unittest import textwrap import mailbox import glob -try: - import fcntl -except ImportError: - pass class TestBase: @@ -2273,12 +2268,18 @@ Gregory K. Johnson """) +class MiscTestCase(unittest.TestCase): + def test__all__(self): + blacklist = {"linesep", "fcntl"} + support.check__all__(self, mailbox, blacklist=blacklist) + + def test_main(): tests = (TestMailboxSuperclass, TestMaildir, TestMbox, TestMMDF, TestMH, TestBabyl, TestMessage, TestMaildirMessage, TestMboxMessage, TestMHMessage, TestBabylMessage, TestMMDFMessage, TestMessageConversion, TestProxyFile, TestPartialFile, - MaildirTestCase, TestFakeMailBox) + MaildirTestCase, TestFakeMailBox, MiscTestCase) support.run_unittest(*tests) support.reap_children() diff --git a/Lib/test/test_mailcap.py b/Lib/test/test_mailcap.py index 22b2fcc..623fadb 100644 --- a/Lib/test/test_mailcap.py +++ b/Lib/test/test_mailcap.py @@ -1,6 +1,5 @@ import mailcap import os -import shutil import test.support import unittest diff --git a/Lib/test/test_marshal.py b/Lib/test/test_marshal.py index c7def9a..b378ffe 100644 --- a/Lib/test/test_marshal.py +++ b/Lib/test/test_marshal.py @@ -135,6 +135,13 @@ class ContainerTestCase(unittest.TestCase, HelperMixin): for constructor in (set, frozenset): self.helper(constructor(self.d.keys())) + @support.cpython_only + def test_empty_frozenset_singleton(self): + # marshal.loads() must reuse the empty frozenset singleton + obj = frozenset() + obj2 = marshal.loads(marshal.dumps(obj)) + self.assertIs(obj2, obj) + class BufferTestCase(unittest.TestCase, HelperMixin): diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index a379a6a..eaa41bc 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -7,14 +7,15 @@ import unittest import math import os import platform -import sys import struct +import sys import sysconfig eps = 1E-05 NAN = float('nan') INF = float('inf') NINF = float('-inf') +FLOAT_MAX = sys.float_info.max # detect evidence of double-rounding: fsum is not always correctly # rounded on machines that suffer from double rounding. @@ -30,6 +31,7 @@ test_dir = os.path.dirname(file) or os.curdir math_testcases = os.path.join(test_dir, 'math_testcases.txt') test_file = os.path.join(test_dir, 'cmath_testcases.txt') + def to_ulps(x): """Convert a non-NaN float x to an integer, in such a way that adjacent floats are converted to adjacent integers. Then @@ -37,25 +39,39 @@ def to_ulps(x): floats. The results from this function will only make sense on platforms - where C doubles are represented in IEEE 754 binary64 format. + where native doubles are represented in IEEE 754 binary64 format. + Note: 0.0 and -0.0 are converted to 0 and -1, respectively. """ n = struct.unpack('<q', struct.pack('<d', x))[0] if n < 0: n = ~(n+2**63) return n -def ulps_check(expected, got, ulps=20): - """Given non-NaN floats `expected` and `got`, - check that they're equal to within the given number of ulps. - Returns None on success and an error message on failure.""" +def ulp(x): + """Return the value of the least significant bit of a + float x, such that the first float bigger than x is x+ulp(x). + Then, given an expected result x and a tolerance of n ulps, + the result y should be such that abs(y-x) <= n * ulp(x). + The results from this function will only make sense on platforms + where native doubles are represented in IEEE 754 binary64 format. + """ + x = abs(float(x)) + if math.isnan(x) or math.isinf(x): + return x - ulps_error = to_ulps(got) - to_ulps(expected) - if abs(ulps_error) <= ulps: - return None - return "error = {} ulps; permitted error = {} ulps".format(ulps_error, - ulps) + # Find next float up from x. + n = struct.unpack('<q', struct.pack('<d', x))[0] + x_next = struct.unpack('<d', struct.pack('<q', n + 1))[0] + if math.isinf(x_next): + # Corner case: x was the largest finite float. Then it's + # not an exact power of two, so we can take the difference + # between x and the previous float. + x_prev = struct.unpack('<d', struct.pack('<q', n - 1))[0] + return x - x_prev + else: + return x_next - x # Here's a pure Python version of the math.factorial algorithm, for # documentation and comparison purposes. @@ -107,24 +123,23 @@ def py_factorial(n): outer *= inner return outer << (n - count_set_bits(n)) -def acc_check(expected, got, rel_err=2e-15, abs_err = 5e-323): - """Determine whether non-NaN floats a and b are equal to within a - (small) rounding error. The default values for rel_err and - abs_err are chosen to be suitable for platforms where a float is - represented by an IEEE 754 double. They allow an error of between - 9 and 19 ulps.""" - - # need to special case infinities, since inf - inf gives nan - if math.isinf(expected) and got == expected: - return None +def ulp_abs_check(expected, got, ulp_tol, abs_tol): + """Given finite floats `expected` and `got`, check that they're + approximately equal to within the given number of ulps or the + given absolute tolerance, whichever is bigger. - error = got - expected + Returns None on success and an error message on failure. + """ + ulp_error = abs(to_ulps(expected) - to_ulps(got)) + abs_error = abs(expected - got) - permitted_error = max(abs_err, rel_err * abs(expected)) - if abs(error) < permitted_error: + # Succeed if either abs_error <= abs_tol or ulp_error <= ulp_tol. + if abs_error <= abs_tol or ulp_error <= ulp_tol: return None - return "error = {}; permitted error = {}".format(error, - permitted_error) + else: + fmt = ("error = {:.3g} ({:d} ulps); " + "permitted error = {:.3g} or {:d} ulps") + return fmt.format(abs_error, ulp_error, abs_tol, ulp_tol) def parse_mtestfile(fname): """Parse a file with test values @@ -151,6 +166,7 @@ def parse_mtestfile(fname): yield (id, fn, float(arg), float(exp), flags) + def parse_testfile(fname): """Parse a file with test values @@ -172,8 +188,53 @@ def parse_testfile(fname): yield (id, fn, float(arg_real), float(arg_imag), float(exp_real), float(exp_imag), - flags - ) + flags) + + +def result_check(expected, got, ulp_tol=5, abs_tol=0.0): + # Common logic of MathTests.(ftest, test_testcases, test_mtestcases) + """Compare arguments expected and got, as floats, if either + is a float, using a tolerance expressed in multiples of + ulp(expected) or absolutely (if given and greater). + + As a convenience, when neither argument is a float, and for + non-finite floats, exact equality is demanded. Also, nan==nan + as far as this function is concerned. + + Returns None on success and an error message on failure. + """ + + # Check exactly equal (applies also to strings representing exceptions) + if got == expected: + return None + + failure = "not equal" + + # Turn mixed float and int comparison (e.g. floor()) to all-float + if isinstance(expected, float) and isinstance(got, int): + got = float(got) + elif isinstance(got, float) and isinstance(expected, int): + expected = float(expected) + + if isinstance(expected, float) and isinstance(got, float): + if math.isnan(expected) and math.isnan(got): + # Pass, since both nan + failure = None + elif math.isinf(expected) or math.isinf(got): + # We already know they're not equal, drop through to failure + pass + else: + # Both are finite floats (now). Are they close enough? + failure = ulp_abs_check(expected, got, ulp_tol, abs_tol) + + # arguments are not equal, and if numeric, are too far apart + if failure is not None: + fail_fmt = "expected {!r}, got {!r}" + fail_msg = fail_fmt.format(expected, got) + fail_msg += ' ({})'.format(failure) + return fail_msg + else: + return None # Class providing an __index__ method. class MyIndexable(object): @@ -185,18 +246,24 @@ class MyIndexable(object): class MathTests(unittest.TestCase): - def ftest(self, name, value, expected): - if abs(value-expected) > eps: - # Use %r instead of %f so the error message - # displays full precision. Otherwise discrepancies - # in the last few bits will lead to very confusing - # error messages - self.fail('%s returned %r, expected %r' % - (name, value, expected)) + def ftest(self, name, got, expected, ulp_tol=5, abs_tol=0.0): + """Compare arguments expected and got, as floats, if either + is a float, using a tolerance expressed in multiples of + ulp(expected) or absolutely, whichever is greater. + + As a convenience, when neither argument is a float, and for + non-finite floats, exact equality is demanded. Also, nan==nan + in this function. + """ + failure = result_check(expected, got, ulp_tol, abs_tol) + if failure is not None: + self.fail("{}: {}".format(name, failure)) def testConstants(self): - self.ftest('pi', math.pi, 3.1415926) - self.ftest('e', math.e, 2.7182818) + # Ref: Abramowitz & Stegun (Dover, 1965) + self.ftest('pi', math.pi, 3.141592653589793238462643) + self.ftest('e', math.e, 2.718281828459045235360287) + self.assertEqual(math.tau, 2*math.pi) def testAcos(self): self.assertRaises(TypeError, math.acos) @@ -205,6 +272,8 @@ class MathTests(unittest.TestCase): self.ftest('acos(1)', math.acos(1), 0) self.assertRaises(ValueError, math.acos, INF) self.assertRaises(ValueError, math.acos, NINF) + self.assertRaises(ValueError, math.acos, 1 + eps) + self.assertRaises(ValueError, math.acos, -1 - eps) self.assertTrue(math.isnan(math.acos(NAN))) def testAcosh(self): @@ -224,6 +293,8 @@ class MathTests(unittest.TestCase): self.ftest('asin(1)', math.asin(1), math.pi/2) self.assertRaises(ValueError, math.asin, INF) self.assertRaises(ValueError, math.asin, NINF) + self.assertRaises(ValueError, math.asin, 1 + eps) + self.assertRaises(ValueError, math.asin, -1 - eps) self.assertTrue(math.isnan(math.asin(NAN))) def testAsinh(self): @@ -378,9 +449,9 @@ class MathTests(unittest.TestCase): def testCos(self): self.assertRaises(TypeError, math.cos) - self.ftest('cos(-pi/2)', math.cos(-math.pi/2), 0) + self.ftest('cos(-pi/2)', math.cos(-math.pi/2), 0, abs_tol=ulp(1)) self.ftest('cos(0)', math.cos(0), 1) - self.ftest('cos(pi/2)', math.cos(math.pi/2), 0) + self.ftest('cos(pi/2)', math.cos(math.pi/2), 0, abs_tol=ulp(1)) self.ftest('cos(pi)', math.cos(math.pi), -1) try: self.assertTrue(math.isnan(math.cos(INF))) @@ -403,6 +474,7 @@ class MathTests(unittest.TestCase): self.ftest('degrees(pi)', math.degrees(math.pi), 180.0) self.ftest('degrees(pi/2)', math.degrees(math.pi/2), 90.0) self.ftest('degrees(-pi/4)', math.degrees(-math.pi/4), -45.0) + self.ftest('degrees(0)', math.degrees(0), 0) def testExp(self): self.assertRaises(TypeError, math.exp) @@ -412,6 +484,7 @@ class MathTests(unittest.TestCase): self.assertEqual(math.exp(INF), INF) self.assertEqual(math.exp(NINF), 0.) self.assertTrue(math.isnan(math.exp(NAN))) + self.assertRaises(OverflowError, math.exp, 1000000) def testFabs(self): self.assertRaises(TypeError, math.fabs) @@ -654,6 +727,7 @@ class MathTests(unittest.TestCase): self.assertEqual(math.hypot(INF, NAN), INF) self.assertEqual(math.hypot(NAN, NINF), INF) self.assertEqual(math.hypot(NINF, NAN), INF) + self.assertRaises(OverflowError, math.hypot, FLOAT_MAX, FLOAT_MAX) self.assertTrue(math.isnan(math.hypot(1.0, NAN))) self.assertTrue(math.isnan(math.hypot(NAN, -2.0))) @@ -707,8 +781,10 @@ class MathTests(unittest.TestCase): def testLog1p(self): self.assertRaises(TypeError, math.log1p) - n= 2**90 - self.assertAlmostEqual(math.log1p(n), math.log1p(float(n))) + for n in [2, 2**90, 2**300]: + self.assertAlmostEqual(math.log1p(n), math.log1p(float(n))) + self.assertRaises(ValueError, math.log1p, -1) + self.assertEqual(math.log1p(INF), INF) @requires_IEEE_754 def testLog2(self): @@ -922,6 +998,7 @@ class MathTests(unittest.TestCase): self.ftest('radians(180)', math.radians(180), math.pi) self.ftest('radians(90)', math.radians(90), math.pi/2) self.ftest('radians(-45)', math.radians(-45), -math.pi/4) + self.ftest('radians(0)', math.radians(0), 0) def testSin(self): self.assertRaises(TypeError, math.sin) @@ -951,6 +1028,7 @@ class MathTests(unittest.TestCase): self.ftest('sqrt(1)', math.sqrt(1), 1) self.ftest('sqrt(4)', math.sqrt(4), 2) self.assertEqual(math.sqrt(INF), INF) + self.assertRaises(ValueError, math.sqrt, -1) self.assertRaises(ValueError, math.sqrt, NINF) self.assertTrue(math.isnan(math.sqrt(NAN))) @@ -970,7 +1048,8 @@ class MathTests(unittest.TestCase): def testTanh(self): self.assertRaises(TypeError, math.tanh) self.ftest('tanh(0)', math.tanh(0), 0) - self.ftest('tanh(1)+tanh(-1)', math.tanh(1)+math.tanh(-1), 0) + self.ftest('tanh(1)+tanh(-1)', math.tanh(1)+math.tanh(-1), 0, + abs_tol=ulp(1)) self.ftest('tanh(inf)', math.tanh(INF), 1) self.ftest('tanh(-inf)', math.tanh(NINF), -1) self.assertTrue(math.isnan(math.tanh(NAN))) @@ -1020,7 +1099,8 @@ class MathTests(unittest.TestCase): def testIsnan(self): self.assertTrue(math.isnan(float("nan"))) - self.assertTrue(math.isnan(float("inf")* 0.)) + self.assertTrue(math.isnan(float("-nan"))) + self.assertTrue(math.isnan(float("inf") * 0.)) self.assertFalse(math.isnan(float("inf"))) self.assertFalse(math.isnan(0.)) self.assertFalse(math.isnan(1.)) @@ -1084,30 +1164,64 @@ class MathTests(unittest.TestCase): @requires_IEEE_754 def test_testfile(self): + # Some tests need to be skipped on ancient OS X versions. + # See issue #27953. + SKIP_ON_TIGER = {'tan0064'} + + osx_version = None + if sys.platform == 'darwin': + version_txt = platform.mac_ver()[0] + try: + osx_version = tuple(map(int, version_txt.split('.'))) + except ValueError: + pass + + fail_fmt = "{}: {}({!r}): {}" + + failures = [] for id, fn, ar, ai, er, ei, flags in parse_testfile(test_file): - # Skip if either the input or result is complex, or if - # flags is nonempty - if ai != 0. or ei != 0. or flags: + # Skip if either the input or result is complex + if ai != 0.0 or ei != 0.0: continue if fn in ['rect', 'polar']: # no real versions of rect, polar continue + # Skip certain tests on OS X 10.4. + if osx_version is not None and osx_version < (10, 5): + if id in SKIP_ON_TIGER: + continue + func = getattr(math, fn) + + if 'invalid' in flags or 'divide-by-zero' in flags: + er = 'ValueError' + elif 'overflow' in flags: + er = 'OverflowError' + try: result = func(ar) - except ValueError as exc: - message = (("Unexpected ValueError: %s\n " + - "in test %s:%s(%r)\n") % (exc.args[0], id, fn, ar)) - self.fail(message) + except ValueError: + result = 'ValueError' except OverflowError: - message = ("Unexpected OverflowError in " + - "test %s:%s(%r)\n" % (id, fn, ar)) - self.fail(message) - self.ftest("%s:%s(%r)" % (id, fn, ar), result, er) + result = 'OverflowError' + + # Default tolerances + ulp_tol, abs_tol = 5, 0.0 + + failure = result_check(er, result, ulp_tol, abs_tol) + if failure is None: + continue + + msg = fail_fmt.format(id, fn, ar, failure) + failures.append(msg) + + if failures: + self.fail('Failures in test_testfile:\n ' + + '\n '.join(failures)) @requires_IEEE_754 def test_mtestfile(self): - fail_fmt = "{}:{}({!r}): expected {!r}, got {!r}" + fail_fmt = "{}: {}({!r}): {}" failures = [] for id, fn, arg, expected, flags in parse_mtestfile(math_testcases): @@ -1125,41 +1239,48 @@ class MathTests(unittest.TestCase): except OverflowError: got = 'OverflowError' - accuracy_failure = None - if isinstance(got, float) and isinstance(expected, float): - if math.isnan(expected) and math.isnan(got): - continue - if not math.isnan(expected) and not math.isnan(got): - if fn == 'lgamma': - # we use a weaker accuracy test for lgamma; - # lgamma only achieves an absolute error of - # a few multiples of the machine accuracy, in - # general. - accuracy_failure = acc_check(expected, got, - rel_err = 5e-15, - abs_err = 5e-15) - elif fn == 'erfc': - # erfc has less-than-ideal accuracy for large - # arguments (x ~ 25 or so), mainly due to the - # error involved in computing exp(-x*x). - # - # XXX Would be better to weaken this test only - # for large x, instead of for all x. - accuracy_failure = ulps_check(expected, got, 2000) - - else: - accuracy_failure = ulps_check(expected, got, 20) - if accuracy_failure is None: - continue - - if isinstance(got, str) and isinstance(expected, str): - if got == expected: - continue + # Default tolerances + ulp_tol, abs_tol = 5, 0.0 + + # Exceptions to the defaults + if fn == 'gamma': + # Experimental results on one platform gave + # an accuracy of <= 10 ulps across the entire float + # domain. We weaken that to require 20 ulp accuracy. + ulp_tol = 20 + + elif fn == 'lgamma': + # we use a weaker accuracy test for lgamma; + # lgamma only achieves an absolute error of + # a few multiples of the machine accuracy, in + # general. + abs_tol = 1e-15 + + elif fn == 'erfc' and arg >= 0.0: + # erfc has less-than-ideal accuracy for large + # arguments (x ~ 25 or so), mainly due to the + # error involved in computing exp(-x*x). + # + # Observed between CPython and mpmath at 25 dp: + # x < 0 : err <= 2 ulp + # 0 <= x < 1 : err <= 10 ulp + # 1 <= x < 10 : err <= 100 ulp + # 10 <= x < 20 : err <= 300 ulp + # 20 <= x : < 600 ulp + # + if arg < 1.0: + ulp_tol = 10 + elif arg < 10.0: + ulp_tol = 100 + else: + ulp_tol = 1000 + + failure = result_check(expected, got, ulp_tol, abs_tol) + if failure is None: + continue - fail_msg = fail_fmt.format(id, fn, arg, expected, got) - if accuracy_failure is not None: - fail_msg += ' ({})'.format(accuracy_failure) - failures.append(fail_msg) + msg = fail_fmt.format(id, fn, arg, failure) + failures.append(msg) if failures: self.fail('Failures in test_mtestfile:\n ' + @@ -1272,7 +1393,8 @@ class IsCloseTests(unittest.TestCase): decimal_examples = [(Decimal('1.00000001'), Decimal('1.0')), (Decimal('1.00000001e-20'), Decimal('1.0e-20')), - (Decimal('1.00000001e-100'), Decimal('1.0e-100'))] + (Decimal('1.00000001e-100'), Decimal('1.0e-100')), + (Decimal('1.00000001e20'), Decimal('1.0e20'))] self.assertAllClose(decimal_examples, rel_tol=1e-8) self.assertAllNotClose(decimal_examples, rel_tol=1e-9) @@ -1280,8 +1402,10 @@ class IsCloseTests(unittest.TestCase): # test with Fraction values from fractions import Fraction - # could use some more examples here! - fraction_examples = [(Fraction(1, 100000000) + 1, Fraction(1))] + fraction_examples = [ + (Fraction(1, 100000000) + 1, Fraction(1)), + (Fraction(100000001), Fraction(100000000)), + (Fraction(10**8 + 1, 10**28), Fraction(1, 10**20))] self.assertAllClose(fraction_examples, rel_tol=1e-8) self.assertAllNotClose(fraction_examples, rel_tol=1e-9) diff --git a/Lib/test/test_metaclass.py b/Lib/test/test_metaclass.py index e6fe20a..4db792e 100644 --- a/Lib/test/test_metaclass.py +++ b/Lib/test/test_metaclass.py @@ -180,7 +180,7 @@ Use a metaclass that doesn't derive from type. meta: C () ns: [('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('a', 42), ('b', 24)] kw: [] - >>> type(C) is dict + >>> type(C) is types._DefaultClassNamespaceType True >>> print(sorted(C.items())) [('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('a', 42), ('b', 24)] @@ -211,8 +211,11 @@ And again, with a __prepare__ attribute. The default metaclass must define a __prepare__() method. - >>> type.__prepare__() - {} + >>> ns = type.__prepare__() + >>> type(ns) is types._DefaultClassNamespaceType + True + >>> list(ns) == [] + True >>> Make sure it works with subclassing. @@ -248,7 +251,9 @@ Test failures in looking up the __prepare__ method work. """ +from collections import OrderedDict import sys +import types # Trace function introduces __locals__ which causes various tests to fail. if hasattr(sys, 'gettrace') and sys.gettrace(): diff --git a/Lib/test/test_mimetypes.py b/Lib/test/test_mimetypes.py index 6856593..4e2c908 100644 --- a/Lib/test/test_mimetypes.py +++ b/Lib/test/test_mimetypes.py @@ -101,5 +101,11 @@ class Win32MimeTypesTestCase(unittest.TestCase): eq(self.db.guess_type("image.jpg"), ("image/jpeg", None)) eq(self.db.guess_type("image.png"), ("image/png", None)) + +class MiscTestCase(unittest.TestCase): + def test__all__(self): + support.check__all__(self, mimetypes) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py index b365d84..bbb4070 100644 --- a/Lib/test/test_mmap.py +++ b/Lib/test/test_mmap.py @@ -713,6 +713,14 @@ class MmapTests(unittest.TestCase): gc_collect() self.assertIs(wr(), None) + def test_write_returning_the_number_of_bytes_written(self): + mm = mmap.mmap(-1, 16) + self.assertEqual(mm.write(b""), 0) + self.assertEqual(mm.write(b"x"), 1) + self.assertEqual(mm.write(b"yz"), 2) + self.assertEqual(mm.write(b"python"), 6) + + class LargeMmapTests(unittest.TestCase): def setUp(self): diff --git a/Lib/test/test_msilib.py b/Lib/test/test_msilib.py index 8ef334f..f656f72 100644 --- a/Lib/test/test_msilib.py +++ b/Lib/test/test_msilib.py @@ -1,6 +1,5 @@ """ Test suite for the code in msilib """ import unittest -import os from test.support import import_module msilib = import_module('msilib') diff --git a/Lib/test/test_multibytecodec.py b/Lib/test/test_multibytecodec.py index 8d7a213..01a1cd3 100644 --- a/Lib/test/test_multibytecodec.py +++ b/Lib/test/test_multibytecodec.py @@ -5,7 +5,7 @@ from test import support from test.support import TESTFN -import unittest, io, codecs, sys, os +import unittest, io, codecs, sys import _multibytecodec ALL_CJKENCODINGS = [ diff --git a/Lib/test/test_multiprocessing_main_handling.py b/Lib/test/test_multiprocessing_main_handling.py index 52273ea..2e15cd8 100644 --- a/Lib/test/test_multiprocessing_main_handling.py +++ b/Lib/test/test_multiprocessing_main_handling.py @@ -6,7 +6,6 @@ support.import_module('_multiprocessing') import importlib import importlib.machinery -import zipimport import unittest import sys import os @@ -15,7 +14,7 @@ import py_compile from test.support.script_helper import ( make_pkg, make_script, make_zip_pkg, make_zip_script, - assert_python_ok, assert_python_failure, spawn_python, kill_python) + assert_python_ok) # Look up which start methods are available to test import multiprocessing diff --git a/Lib/test/test_nis.py b/Lib/test/test_nis.py index 387a4e7..21074c6 100644 --- a/Lib/test/test_nis.py +++ b/Lib/test/test_nis.py @@ -1,6 +1,5 @@ from test import support import unittest -import sys # Skip test if nis module does not exist. nis = support.import_module('nis') diff --git a/Lib/test/test_normalization.py b/Lib/test/test_normalization.py index 30fa612..5b590e1 100644 --- a/Lib/test/test_normalization.py +++ b/Lib/test/test_normalization.py @@ -3,7 +3,6 @@ import unittest from http.client import HTTPException import sys -import os from unicodedata import normalize, unidata_version TESTDATAFILE = "NormalizationTest.txt" diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index 580f203..90edb6d 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -452,5 +452,88 @@ class NtCommonTest(test_genericpath.CommonTest, unittest.TestCase): attributes = ['relpath', 'splitunc'] +class PathLikeTests(unittest.TestCase): + + path = ntpath + + class PathLike: + def __init__(self, path=''): + self.path = path + def __fspath__(self): + if isinstance(self.path, BaseException): + raise self.path + else: + return self.path + + def setUp(self): + self.file_name = support.TESTFN.lower() + self.file_path = self.PathLike(support.TESTFN) + self.addCleanup(support.unlink, self.file_name) + with open(self.file_name, 'xb', 0) as file: + file.write(b"test_ntpath.PathLikeTests") + + def assertPathEqual(self, func): + self.assertEqual(func(self.file_path), func(self.file_name)) + + def test_path_normcase(self): + self.assertPathEqual(self.path.normcase) + + def test_path_isabs(self): + self.assertPathEqual(self.path.isabs) + + def test_path_join(self): + self.assertEqual(self.path.join('a', self.PathLike('b'), 'c'), + self.path.join('a', 'b', 'c')) + + def test_path_split(self): + self.assertPathEqual(self.path.split) + + def test_path_splitext(self): + self.assertPathEqual(self.path.splitext) + + def test_path_splitdrive(self): + self.assertPathEqual(self.path.splitdrive) + + def test_path_basename(self): + self.assertPathEqual(self.path.basename) + + def test_path_dirname(self): + self.assertPathEqual(self.path.dirname) + + def test_path_islink(self): + self.assertPathEqual(self.path.islink) + + def test_path_lexists(self): + self.assertPathEqual(self.path.lexists) + + def test_path_ismount(self): + self.assertPathEqual(self.path.ismount) + + def test_path_expanduser(self): + self.assertPathEqual(self.path.expanduser) + + def test_path_expandvars(self): + self.assertPathEqual(self.path.expandvars) + + def test_path_normpath(self): + self.assertPathEqual(self.path.normpath) + + def test_path_abspath(self): + self.assertPathEqual(self.path.abspath) + + def test_path_realpath(self): + self.assertPathEqual(self.path.realpath) + + def test_path_relpath(self): + self.assertPathEqual(self.path.relpath) + + def test_path_commonpath(self): + common_path = self.path.commonpath([self.file_path, self.file_name]) + self.assertEqual(common_path, self.file_name) + + def test_path_isdir(self): + self.assertPathEqual(self.path.isdir) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_operator.py b/Lib/test/test_operator.py index b5ba976..6254091 100644 --- a/Lib/test/test_operator.py +++ b/Lib/test/test_operator.py @@ -120,63 +120,63 @@ class OperatorTestCase: operator = self.module self.assertRaises(TypeError, operator.add) self.assertRaises(TypeError, operator.add, None, None) - self.assertTrue(operator.add(3, 4) == 7) + self.assertEqual(operator.add(3, 4), 7) def test_bitwise_and(self): operator = self.module self.assertRaises(TypeError, operator.and_) self.assertRaises(TypeError, operator.and_, None, None) - self.assertTrue(operator.and_(0xf, 0xa) == 0xa) + self.assertEqual(operator.and_(0xf, 0xa), 0xa) def test_concat(self): operator = self.module self.assertRaises(TypeError, operator.concat) self.assertRaises(TypeError, operator.concat, None, None) - self.assertTrue(operator.concat('py', 'thon') == 'python') - self.assertTrue(operator.concat([1, 2], [3, 4]) == [1, 2, 3, 4]) - self.assertTrue(operator.concat(Seq1([5, 6]), Seq1([7])) == [5, 6, 7]) - self.assertTrue(operator.concat(Seq2([5, 6]), Seq2([7])) == [5, 6, 7]) + self.assertEqual(operator.concat('py', 'thon'), 'python') + self.assertEqual(operator.concat([1, 2], [3, 4]), [1, 2, 3, 4]) + self.assertEqual(operator.concat(Seq1([5, 6]), Seq1([7])), [5, 6, 7]) + self.assertEqual(operator.concat(Seq2([5, 6]), Seq2([7])), [5, 6, 7]) self.assertRaises(TypeError, operator.concat, 13, 29) def test_countOf(self): operator = self.module self.assertRaises(TypeError, operator.countOf) self.assertRaises(TypeError, operator.countOf, None, None) - self.assertTrue(operator.countOf([1, 2, 1, 3, 1, 4], 3) == 1) - self.assertTrue(operator.countOf([1, 2, 1, 3, 1, 4], 5) == 0) + self.assertEqual(operator.countOf([1, 2, 1, 3, 1, 4], 3), 1) + self.assertEqual(operator.countOf([1, 2, 1, 3, 1, 4], 5), 0) def test_delitem(self): operator = self.module a = [4, 3, 2, 1] self.assertRaises(TypeError, operator.delitem, a) self.assertRaises(TypeError, operator.delitem, a, None) - self.assertTrue(operator.delitem(a, 1) is None) - self.assertTrue(a == [4, 2, 1]) + self.assertIsNone(operator.delitem(a, 1)) + self.assertEqual(a, [4, 2, 1]) def test_floordiv(self): operator = self.module self.assertRaises(TypeError, operator.floordiv, 5) self.assertRaises(TypeError, operator.floordiv, None, None) - self.assertTrue(operator.floordiv(5, 2) == 2) + self.assertEqual(operator.floordiv(5, 2), 2) def test_truediv(self): operator = self.module self.assertRaises(TypeError, operator.truediv, 5) self.assertRaises(TypeError, operator.truediv, None, None) - self.assertTrue(operator.truediv(5, 2) == 2.5) + self.assertEqual(operator.truediv(5, 2), 2.5) def test_getitem(self): operator = self.module a = range(10) self.assertRaises(TypeError, operator.getitem) self.assertRaises(TypeError, operator.getitem, a, None) - self.assertTrue(operator.getitem(a, 2) == 2) + self.assertEqual(operator.getitem(a, 2), 2) def test_indexOf(self): operator = self.module self.assertRaises(TypeError, operator.indexOf) self.assertRaises(TypeError, operator.indexOf, None, None) - self.assertTrue(operator.indexOf([4, 3, 2, 1], 3) == 1) + self.assertEqual(operator.indexOf([4, 3, 2, 1], 3), 1) self.assertRaises(ValueError, operator.indexOf, [4, 3, 2, 1], 0) def test_invert(self): @@ -189,21 +189,21 @@ class OperatorTestCase: operator = self.module self.assertRaises(TypeError, operator.lshift) self.assertRaises(TypeError, operator.lshift, None, 42) - self.assertTrue(operator.lshift(5, 1) == 10) - self.assertTrue(operator.lshift(5, 0) == 5) + self.assertEqual(operator.lshift(5, 1), 10) + self.assertEqual(operator.lshift(5, 0), 5) self.assertRaises(ValueError, operator.lshift, 2, -1) def test_mod(self): operator = self.module self.assertRaises(TypeError, operator.mod) self.assertRaises(TypeError, operator.mod, None, 42) - self.assertTrue(operator.mod(5, 2) == 1) + self.assertEqual(operator.mod(5, 2), 1) def test_mul(self): operator = self.module self.assertRaises(TypeError, operator.mul) self.assertRaises(TypeError, operator.mul, None, None) - self.assertTrue(operator.mul(5, 2) == 10) + self.assertEqual(operator.mul(5, 2), 10) def test_matmul(self): operator = self.module @@ -227,7 +227,7 @@ class OperatorTestCase: operator = self.module self.assertRaises(TypeError, operator.or_) self.assertRaises(TypeError, operator.or_, None, None) - self.assertTrue(operator.or_(0xa, 0x5) == 0xf) + self.assertEqual(operator.or_(0xa, 0x5), 0xf) def test_pos(self): operator = self.module @@ -250,8 +250,8 @@ class OperatorTestCase: operator = self.module self.assertRaises(TypeError, operator.rshift) self.assertRaises(TypeError, operator.rshift, None, 42) - self.assertTrue(operator.rshift(5, 1) == 2) - self.assertTrue(operator.rshift(5, 0) == 5) + self.assertEqual(operator.rshift(5, 1), 2) + self.assertEqual(operator.rshift(5, 0), 5) self.assertRaises(ValueError, operator.rshift, 2, -1) def test_contains(self): @@ -266,15 +266,15 @@ class OperatorTestCase: a = list(range(3)) self.assertRaises(TypeError, operator.setitem, a) self.assertRaises(TypeError, operator.setitem, a, None, None) - self.assertTrue(operator.setitem(a, 0, 2) is None) - self.assertTrue(a == [2, 1, 2]) + self.assertIsNone(operator.setitem(a, 0, 2)) + self.assertEqual(a, [2, 1, 2]) self.assertRaises(IndexError, operator.setitem, a, 4, 2) def test_sub(self): operator = self.module self.assertRaises(TypeError, operator.sub) self.assertRaises(TypeError, operator.sub, None, None) - self.assertTrue(operator.sub(5, 2) == 3) + self.assertEqual(operator.sub(5, 2), 3) def test_truth(self): operator = self.module @@ -292,7 +292,7 @@ class OperatorTestCase: operator = self.module self.assertRaises(TypeError, operator.xor) self.assertRaises(TypeError, operator.xor, None, None) - self.assertTrue(operator.xor(0xb, 0xc) == 0x7) + self.assertEqual(operator.xor(0xb, 0xc), 0x7) def test_is(self): operator = self.module diff --git a/Lib/test/test_optparse.py b/Lib/test/test_optparse.py index 7621c24..91a0319 100644 --- a/Lib/test/test_optparse.py +++ b/Lib/test/test_optparse.py @@ -16,6 +16,7 @@ from io import StringIO from test import support +import optparse from optparse import make_option, Option, \ TitledHelpFormatter, OptionParser, OptionGroup, \ SUPPRESS_USAGE, OptionError, OptionConflictError, \ @@ -1650,6 +1651,12 @@ class TestParseNumber(BaseTest): "option -l: invalid integer value: '0x12x'") +class MiscTestCase(unittest.TestCase): + def test__all__(self): + blacklist = {'check_builtin', 'AmbiguousOptionError', 'NO_DEFAULT'} + support.check__all__(self, optparse, blacklist=blacklist) + + def test_main(): support.run_unittest(__name__) diff --git a/Lib/test/test_ordered_dict.py b/Lib/test/test_ordered_dict.py index 901d4b2..6fbc1b4 100644 --- a/Lib/test/test_ordered_dict.py +++ b/Lib/test/test_ordered_dict.py @@ -298,9 +298,11 @@ class OrderedDictTests: # do not save instance dictionary if not needed pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)] od = OrderedDict(pairs) + self.assertIsInstance(od.__dict__, dict) self.assertIsNone(od.__reduce__()[2]) od.x = 10 - self.assertIsNotNone(od.__reduce__()[2]) + self.assertEqual(od.__dict__['x'], 10) + self.assertEqual(od.__reduce__()[2], {'x': 10}) def test_pickle_recursive(self): OrderedDict = self.OrderedDict @@ -403,6 +405,14 @@ class OrderedDictTests: od = OrderedDict(**d) self.assertGreater(sys.getsizeof(od), sys.getsizeof(d)) + def test_views(self): + OrderedDict = self.OrderedDict + # See http://bugs.python.org/issue24286 + s = 'the quick brown fox jumped over a lazy dog yesterday before dawn'.split() + od = OrderedDict.fromkeys(s) + self.assertEqual(od.keys(), dict(od).keys()) + self.assertEqual(od.items(), dict(od).items()) + def test_override_update(self): OrderedDict = self.OrderedDict # Verify that subclasses can override update() without breaking __init__() diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 874f9e4..c5bea85 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -15,7 +15,6 @@ import locale import mmap import os import pickle -import platform import re import shutil import signal @@ -65,6 +64,8 @@ except ImportError: INT_MAX = PY_SSIZE_T_MAX = sys.maxsize from test.support.script_helper import assert_python_ok +from test.support import unix_shell + root_in_posix = False if hasattr(os, 'geteuid'): @@ -82,6 +83,28 @@ else: # Issue #14110: Some tests fail on FreeBSD if the user is in the wheel group. HAVE_WHEEL_GROUP = sys.platform.startswith('freebsd') and os.getgid() == 0 + +@contextlib.contextmanager +def ignore_deprecation_warnings(msg_regex, quiet=False): + with support.check_warnings((msg_regex, DeprecationWarning), quiet=quiet): + yield + + +@contextlib.contextmanager +def bytes_filename_warn(expected): + msg = 'The Windows bytes API has been deprecated' + if os.name == 'nt': + with ignore_deprecation_warnings(msg, quiet=not expected): + yield + else: + yield + + +def create_file(filename, content=b'content'): + with open(filename, "xb", 0) as fp: + fp.write(content) + + # Tests creating TESTFN class FileTests(unittest.TestCase): def setUp(self): @@ -140,9 +163,8 @@ class FileTests(unittest.TestCase): "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) + create_file(support.TESTFN, b'test') # Issue #21932: Make sure that os.read() does not raise an # OverflowError for size larger than INT_MAX @@ -199,11 +221,12 @@ class FileTests(unittest.TestCase): def test_replace(self): TESTFN2 = support.TESTFN + ".2" - with open(support.TESTFN, 'w') as f: - f.write("1") - with open(TESTFN2, 'w') as f: - f.write("2") - self.addCleanup(os.unlink, TESTFN2) + self.addCleanup(support.unlink, support.TESTFN) + self.addCleanup(support.unlink, TESTFN2) + + create_file(support.TESTFN, b"1") + create_file(TESTFN2, b"2") + os.replace(support.TESTFN, TESTFN2) self.assertRaises(FileNotFoundError, os.stat, support.TESTFN) with open(TESTFN2, 'r') as f: @@ -226,15 +249,9 @@ class FileTests(unittest.TestCase): # Test attributes on return values from os.*stat* family. class StatAttributeTests(unittest.TestCase): def setUp(self): - os.mkdir(support.TESTFN) - self.fname = os.path.join(support.TESTFN, "f1") - f = open(self.fname, 'wb') - f.write(b"ABC") - f.close() - - def tearDown(self): - os.unlink(self.fname) - os.rmdir(support.TESTFN) + self.fname = support.TESTFN + self.addCleanup(support.unlink, self.fname) + create_file(self.fname, b"ABC") @unittest.skipUnless(hasattr(os, 'stat'), 'test needs os.stat()') def check_stat_attributes(self, fname): @@ -310,8 +327,7 @@ class StatAttributeTests(unittest.TestCase): fname = self.fname.encode(sys.getfilesystemencoding()) except UnicodeEncodeError: self.skipTest("cannot encode %a for the filesystem" % self.fname) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) + with bytes_filename_warn(True): self.check_stat_attributes(fname) def test_stat_result_pickle(self): @@ -426,7 +442,11 @@ class StatAttributeTests(unittest.TestCase): 0) # test directory st_file_attributes (FILE_ATTRIBUTE_DIRECTORY set) - result = os.stat(support.TESTFN) + dirname = support.TESTFN + "dir" + os.mkdir(dirname) + self.addCleanup(os.rmdir, dirname) + + result = os.stat(dirname) self.check_file_attributes(result) self.assertEqual( result.st_file_attributes & stat.FILE_ATTRIBUTE_DIRECTORY, @@ -440,19 +460,14 @@ class UtimeTests(unittest.TestCase): self.addCleanup(support.rmtree, self.dirname) os.mkdir(self.dirname) - with open(self.fname, 'wb') as fp: - fp.write(b"ABC") + create_file(self.fname) def restore_float_times(state): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - + with ignore_deprecation_warnings('stat_float_times'): os.stat_float_times(state) # ensure that st_atime and st_mtime are float - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - + with ignore_deprecation_warnings('stat_float_times'): old_float_times = os.stat_float_times(-1) self.addCleanup(restore_float_times, old_float_times) @@ -544,7 +559,7 @@ class UtimeTests(unittest.TestCase): "fd support for utime required for this test.") def test_utime_fd(self): def set_time(filename, ns): - with open(filename, 'wb') as fp: + with open(filename, 'wb', 0) as fp: # use a file descriptor to test futimens(timespec) # or futimes(timeval) os.utime(fp.fileno(), ns=ns) @@ -656,18 +671,20 @@ class EnvironTests(mapping_tests.BasicTestMappingProtocol): return os.environ # Bug 1110478 - @unittest.skipUnless(os.path.exists('/bin/sh'), 'requires /bin/sh') + @unittest.skipUnless(unix_shell and os.path.exists(unix_shell), + 'requires a shell') def test_update2(self): os.environ.clear() os.environ.update(HELLO="World") - with os.popen("/bin/sh -c 'echo $HELLO'") as popen: + with os.popen("%s -c 'echo $HELLO'" % unix_shell) as popen: value = popen.read().strip() self.assertEqual(value, "World") - @unittest.skipUnless(os.path.exists('/bin/sh'), 'requires /bin/sh') + @unittest.skipUnless(unix_shell and os.path.exists(unix_shell), + 'requires a shell') def test_os_popen_iter(self): - with os.popen( - "/bin/sh -c 'echo \"line1\nline2\nline3\"'") as popen: + with os.popen("%s -c 'echo \"line1\nline2\nline3\"'" + % unix_shell) as popen: it = iter(popen) self.assertEqual(next(it), "line1\n") self.assertEqual(next(it), "line2\n") @@ -798,6 +815,7 @@ class WalkTests(unittest.TestCase): def setUp(self): join = os.path.join + self.addCleanup(support.rmtree, support.TESTFN) # Build: # TESTFN/ @@ -830,9 +848,8 @@ class WalkTests(unittest.TestCase): os.makedirs(t2_path) for path in tmp1_path, tmp2_path, tmp3_path, tmp4_path: - f = open(path, "w") - f.write("I'm " + path + " and proud of it. Blame test_os.\n") - f.close() + with open(path, "x") as f: + f.write("I'm " + path + " and proud of it. Blame test_os.\n") if support.can_symlink(): os.symlink(os.path.abspath(t2_path), self.link_path) @@ -857,10 +874,12 @@ class WalkTests(unittest.TestCase): self.assertEqual(all[2 + flipped], (self.sub11_path, [], [])) self.assertEqual(all[3 - 2 * flipped], self.sub2_tree) - def test_walk_prune(self): + def test_walk_prune(self, walk_path=None): + if walk_path is None: + walk_path = self.walk_path # Prune the search. all = [] - for root, dirs, files in self.walk(self.walk_path): + for root, dirs, files in self.walk(walk_path): all.append((root, dirs, files)) # Don't descend into SUB1. if 'SUB1' in dirs: @@ -869,16 +888,27 @@ class WalkTests(unittest.TestCase): self.assertEqual(len(all), 2) self.assertEqual(all[0], - (self.walk_path, ["SUB2"], ["tmp1"])) + (str(walk_path), ["SUB2"], ["tmp1"])) all[1][-1].sort() self.assertEqual(all[1], self.sub2_tree) + def test_file_like_path(self): + class FileLike: + def __init__(self, path): + self._path = path + def __str__(self): + return str(self._path) + def __fspath__(self): + return self._path + + self.test_walk_prune(FileLike(self.walk_path)) + def test_walk_bottom_up(self): # Walk bottom-up. all = list(self.walk(self.walk_path, topdown=False)) - self.assertEqual(len(all), 4) + self.assertEqual(len(all), 4, all) # We can't know which order SUB1 and SUB2 will appear in. # Not flipped: SUB11, SUB1, SUB2, TESTFN # flipped: SUB2, SUB11, SUB1, TESTFN @@ -908,22 +938,6 @@ class WalkTests(unittest.TestCase): else: self.fail("Didn't follow symlink with followlinks=True") - def tearDown(self): - # Tear everything down. This is a decent use for bottom-up on - # Windows, which doesn't have a recursive delete command. The - # (not so) subtlety is that rmdir will fail unless the dir's - # kids are removed first, so bottom up is essential. - for root, dirs, files in os.walk(support.TESTFN, topdown=False): - for name in files: - os.remove(os.path.join(root, name)) - for name in dirs: - dirname = os.path.join(root, name) - if not os.path.islink(dirname): - os.rmdir(dirname) - else: - os.remove(dirname) - os.rmdir(support.TESTFN) - def test_walk_bad_dir(self): # Walk top-down. errors = [] @@ -1006,27 +1020,13 @@ class FwalkTests(WalkTests): self.addCleanup(os.close, newfd) self.assertEqual(newfd, minfd) - def tearDown(self): - # cleanup - for root, dirs, files, rootfd in os.fwalk(support.TESTFN, topdown=False): - for name in files: - os.unlink(name, dir_fd=rootfd) - for name in dirs: - st = os.stat(name, dir_fd=rootfd, follow_symlinks=False) - if stat.S_ISDIR(st.st_mode): - os.rmdir(name, dir_fd=rootfd) - else: - os.unlink(name, dir_fd=rootfd) - os.rmdir(support.TESTFN) - class BytesWalkTests(WalkTests): """Tests for os.walk() with bytes.""" def setUp(self): super().setUp() self.stack = contextlib.ExitStack() if os.name == 'nt': - self.stack.enter_context(warnings.catch_warnings()) - warnings.simplefilter("ignore", DeprecationWarning) + self.stack.enter_context(bytes_filename_warn(False)) def tearDown(self): self.stack.close() @@ -1203,8 +1203,7 @@ class RemoveDirsTests(unittest.TestCase): os.mkdir(dira) dirb = os.path.join(dira, 'dirb') os.mkdir(dirb) - with open(os.path.join(dira, 'file.txt'), 'w') as f: - f.write('text') + create_file(os.path.join(dira, 'file.txt')) os.removedirs(dirb) self.assertFalse(os.path.exists(dirb)) self.assertTrue(os.path.exists(dira)) @@ -1215,8 +1214,7 @@ class RemoveDirsTests(unittest.TestCase): os.mkdir(dira) dirb = os.path.join(dira, 'dirb') os.mkdir(dirb) - with open(os.path.join(dirb, 'file.txt'), 'w') as f: - f.write('text') + create_file(os.path.join(dirb, 'file.txt')) with self.assertRaises(OSError): os.removedirs(dirb) self.assertTrue(os.path.exists(dirb)) @@ -1226,7 +1224,7 @@ class RemoveDirsTests(unittest.TestCase): class DevNullTests(unittest.TestCase): def test_devnull(self): - with open(os.devnull, 'wb') as f: + with open(os.devnull, 'wb', 0) as f: f.write(b'hello') f.close() with open(os.devnull, 'rb') as f: @@ -1313,9 +1311,9 @@ class URandomFDTests(unittest.TestCase): def test_urandom_fd_reopened(self): # Issue #21207: urandom() should detect its fd to /dev/urandom # changed to something else, and reopen it. - with open(support.TESTFN, 'wb') as f: - f.write(b"x" * 256) - self.addCleanup(os.unlink, support.TESTFN) + self.addCleanup(support.unlink, support.TESTFN) + create_file(support.TESTFN, b"x" * 256) + code = """if 1: import os import sys @@ -1444,6 +1442,18 @@ class ExecTests(unittest.TestCase): @unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") class Win32ErrorTests(unittest.TestCase): + def setUp(self): + try: + os.stat(support.TESTFN) + except FileNotFoundError: + exists = False + except OSError as exc: + exists = True + self.fail("file %s must not exist; os.stat failed with %s" + % (support.TESTFN, exc)) + else: + self.fail("file %s must not exist" % support.TESTFN) + def test_rename(self): self.assertRaises(OSError, os.rename, support.TESTFN, support.TESTFN+".bak") @@ -1454,12 +1464,10 @@ class Win32ErrorTests(unittest.TestCase): self.assertRaises(OSError, os.chdir, support.TESTFN) def test_mkdir(self): - f = open(support.TESTFN, "w") - try: + self.addCleanup(support.unlink, support.TESTFN) + + with open(support.TESTFN, "x") as f: self.assertRaises(OSError, os.mkdir, support.TESTFN) - finally: - f.close() - os.unlink(support.TESTFN) def test_utime(self): self.assertRaises(OSError, os.utime, support.TESTFN, None) @@ -1467,6 +1475,7 @@ class Win32ErrorTests(unittest.TestCase): def test_chmod(self): self.assertRaises(OSError, os.chmod, support.TESTFN, 0) + class TestInvalidFD(unittest.TestCase): singles = ["fchdir", "dup", "fdopen", "fdatasync", "fstat", "fstatvfs", "fsync", "tcgetpgrp", "ttyname"] @@ -1578,11 +1587,9 @@ class LinkTests(unittest.TestCase): os.unlink(file) def _test_link(self, file1, file2): - with open(file1, "w") as f1: - f1.write("test") + create_file(file1) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) + with bytes_filename_warn(False): os.link(file1, file2) with open(file1, "r") as f1, open(file2, "r") as f2: self.assertTrue(os.path.sameopenfile(f1.fileno(), f2.fileno())) @@ -1874,10 +1881,12 @@ class Win32ListdirTests(unittest.TestCase): self.assertEqual( sorted(os.listdir(support.TESTFN)), self.created_paths) + # bytes - self.assertEqual( - sorted(os.listdir(os.fsencode(support.TESTFN))), - [os.fsencode(path) for path in self.created_paths]) + with bytes_filename_warn(False): + self.assertEqual( + sorted(os.listdir(os.fsencode(support.TESTFN))), + [os.fsencode(path) for path in self.created_paths]) def test_listdir_extended_path(self): """Test when the path starts with '\\\\?\\'.""" @@ -1887,11 +1896,13 @@ class Win32ListdirTests(unittest.TestCase): self.assertEqual( sorted(os.listdir(path)), self.created_paths) + # bytes - path = b'\\\\?\\' + os.fsencode(os.path.abspath(support.TESTFN)) - self.assertEqual( - sorted(os.listdir(path)), - [os.fsencode(path) for path in self.created_paths]) + with bytes_filename_warn(False): + path = b'\\\\?\\' + os.fsencode(os.path.abspath(support.TESTFN)) + self.assertEqual( + sorted(os.listdir(path)), + [os.fsencode(path) for path in self.created_paths]) @unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") @@ -1966,51 +1977,45 @@ class Win32SymlinkTests(unittest.TestCase): self.assertNotEqual(os.lstat(link), os.stat(link)) bytes_link = os.fsencode(link) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) + with bytes_filename_warn(True): self.assertEqual(os.stat(bytes_link), os.stat(target)) + with bytes_filename_warn(True): self.assertNotEqual(os.lstat(bytes_link), os.stat(bytes_link)) def test_12084(self): level1 = os.path.abspath(support.TESTFN) level2 = os.path.join(level1, "level2") level3 = os.path.join(level2, "level3") - try: - os.mkdir(level1) - os.mkdir(level2) - os.mkdir(level3) + self.addCleanup(support.rmtree, level1) - file1 = os.path.abspath(os.path.join(level1, "file1")) + os.mkdir(level1) + os.mkdir(level2) + os.mkdir(level3) - with open(file1, "w") as f: - f.write("file1") + file1 = os.path.abspath(os.path.join(level1, "file1")) + create_file(file1) - orig_dir = os.getcwd() - try: - os.chdir(level2) - link = os.path.join(level2, "link") - os.symlink(os.path.relpath(file1), "link") - self.assertIn("link", os.listdir(os.getcwd())) - - # Check os.stat calls from the same dir as the link - self.assertEqual(os.stat(file1), os.stat("link")) - - # Check os.stat calls from a dir below the link - os.chdir(level1) - self.assertEqual(os.stat(file1), - os.stat(os.path.relpath(link))) - - # Check os.stat calls from a dir above the link - os.chdir(level3) - self.assertEqual(os.stat(file1), - os.stat(os.path.relpath(link))) - finally: - os.chdir(orig_dir) - except OSError as err: - self.fail(err) + orig_dir = os.getcwd() + try: + os.chdir(level2) + link = os.path.join(level2, "link") + os.symlink(os.path.relpath(file1), "link") + self.assertIn("link", os.listdir(os.getcwd())) + + # Check os.stat calls from the same dir as the link + self.assertEqual(os.stat(file1), os.stat("link")) + + # Check os.stat calls from a dir below the link + os.chdir(level1) + self.assertEqual(os.stat(file1), + os.stat(os.path.relpath(link))) + + # Check os.stat calls from a dir above the link + os.chdir(level3) + self.assertEqual(os.stat(file1), + os.stat(os.path.relpath(link))) finally: - os.remove(file1) - shutil.rmtree(level1) + os.chdir(orig_dir) @unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") @@ -2146,8 +2151,8 @@ class ProgramPriorityTests(unittest.TestCase): try: new_prio = os.getpriority(os.PRIO_PROCESS, os.getpid()) if base >= 19 and new_prio <= 19: - raise unittest.SkipTest( - "unable to reliably test setpriority at current nice level of %s" % base) + raise unittest.SkipTest("unable to reliably test setpriority " + "at current nice level of %s" % base) else: self.assertEqual(new_prio, base + 1) finally: @@ -2257,8 +2262,7 @@ class TestSendfile(unittest.TestCase): @classmethod def setUpClass(cls): cls.key = support.threading_setup() - with open(support.TESTFN, "wb") as f: - f.write(cls.DATA) + create_file(support.TESTFN, cls.DATA) @classmethod def tearDownClass(cls): @@ -2408,10 +2412,11 @@ class TestSendfile(unittest.TestCase): def test_trailers(self): TESTFN2 = support.TESTFN + "2" file_data = b"abcdef" - with open(TESTFN2, 'wb') as f: - f.write(file_data) - with open(TESTFN2, 'rb')as f: - self.addCleanup(os.remove, TESTFN2) + + self.addCleanup(support.unlink, TESTFN2) + create_file(TESTFN2, file_data) + + with open(TESTFN2, 'rb') as f: os.sendfile(self.sockno, f.fileno(), 0, len(file_data), trailers=[b"1234"]) self.client.close() @@ -2434,35 +2439,37 @@ class TestSendfile(unittest.TestCase): def supports_extended_attributes(): if not hasattr(os, "setxattr"): return False + try: - with open(support.TESTFN, "wb") as fp: + with open(support.TESTFN, "xb", 0) as fp: try: os.setxattr(fp.fileno(), b"user.test", b"") except OSError: return False finally: support.unlink(support.TESTFN) - # Kernels < 2.6.39 don't respect setxattr flags. - kernel_version = platform.release() - m = re.match("2.6.(\d{1,2})", kernel_version) - return m is None or int(m.group(1)) >= 39 + + return True @unittest.skipUnless(supports_extended_attributes(), "no non-broken extended attribute support") +# Kernels < 2.6.39 don't respect setxattr flags. +@support.requires_linux_version(2, 6, 39) class ExtendedAttributeTests(unittest.TestCase): - def tearDown(self): - support.unlink(support.TESTFN) - def _check_xattrs_str(self, s, getxattr, setxattr, removexattr, listxattr, **kwargs): fn = support.TESTFN - open(fn, "wb").close() + self.addCleanup(support.unlink, fn) + create_file(fn) + with self.assertRaises(OSError) as cm: getxattr(fn, s("user.test"), **kwargs) self.assertEqual(cm.exception.errno, errno.ENODATA) + init_xattr = listxattr(fn) self.assertIsInstance(init_xattr, list) + setxattr(fn, s("user.test"), b"", **kwargs) xattr = set(init_xattr) xattr.add("user.test") @@ -2470,19 +2477,24 @@ class ExtendedAttributeTests(unittest.TestCase): self.assertEqual(getxattr(fn, b"user.test", **kwargs), b"") setxattr(fn, s("user.test"), b"hello", os.XATTR_REPLACE, **kwargs) self.assertEqual(getxattr(fn, b"user.test", **kwargs), b"hello") + with self.assertRaises(OSError) as cm: setxattr(fn, s("user.test"), b"bye", os.XATTR_CREATE, **kwargs) self.assertEqual(cm.exception.errno, errno.EEXIST) + with self.assertRaises(OSError) as cm: setxattr(fn, s("user.test2"), b"bye", os.XATTR_REPLACE, **kwargs) self.assertEqual(cm.exception.errno, errno.ENODATA) + setxattr(fn, s("user.test2"), b"foo", os.XATTR_CREATE, **kwargs) xattr.add("user.test2") self.assertEqual(set(listxattr(fn)), xattr) removexattr(fn, s("user.test"), **kwargs) + with self.assertRaises(OSError) as cm: getxattr(fn, s("user.test"), **kwargs) self.assertEqual(cm.exception.errno, errno.ENODATA) + xattr.remove("user.test") self.assertEqual(set(listxattr(fn)), xattr) self.assertEqual(getxattr(fn, s("user.test2"), **kwargs), b"foo") @@ -2495,11 +2507,11 @@ class ExtendedAttributeTests(unittest.TestCase): self.assertEqual(set(listxattr(fn)), set(init_xattr) | set(many)) def _check_xattrs(self, *args, **kwargs): - def make_bytes(s): - return bytes(s, "ascii") self._check_xattrs_str(str, *args, **kwargs) support.unlink(support.TESTFN) - self._check_xattrs_str(make_bytes, *args, **kwargs) + + self._check_xattrs_str(os.fsencode, *args, **kwargs) + support.unlink(support.TESTFN) def test_simple(self): self._check_xattrs(os.getxattr, os.setxattr, os.removexattr, @@ -2514,10 +2526,10 @@ class ExtendedAttributeTests(unittest.TestCase): with open(path, "rb") as fp: return os.getxattr(fp.fileno(), *args) def setxattr(path, *args): - with open(path, "wb") as fp: + with open(path, "wb", 0) as fp: os.setxattr(fp.fileno(), *args) def removexattr(path, *args): - with open(path, "wb") as fp: + with open(path, "wb", 0) as fp: os.removexattr(fp.fileno(), *args) def listxattr(path, *args): with open(path, "rb") as fp: @@ -2530,36 +2542,39 @@ class Win32DeprecatedBytesAPI(unittest.TestCase): def test_deprecated(self): import nt filename = os.fsencode(support.TESTFN) - with warnings.catch_warnings(): - warnings.simplefilter("error", DeprecationWarning) - for func, *args in ( - (nt._getfullpathname, filename), - (nt._isdir, filename), - (os.access, filename, os.R_OK), - (os.chdir, filename), - (os.chmod, filename, 0o777), - (os.getcwdb,), - (os.link, filename, filename), - (os.listdir, filename), - (os.lstat, filename), - (os.mkdir, filename), - (os.open, filename, os.O_RDONLY), - (os.rename, filename, filename), - (os.rmdir, filename), - (os.startfile, filename), - (os.stat, filename), - (os.unlink, filename), - (os.utime, filename), - ): - self.assertRaises(DeprecationWarning, func, *args) + for func, *args in ( + (nt._getfullpathname, filename), + (nt._isdir, filename), + (os.access, filename, os.R_OK), + (os.chdir, filename), + (os.chmod, filename, 0o777), + (os.getcwdb,), + (os.link, filename, filename), + (os.listdir, filename), + (os.lstat, filename), + (os.mkdir, filename), + (os.open, filename, os.O_RDONLY), + (os.rename, filename, filename), + (os.rmdir, filename), + (os.startfile, filename), + (os.stat, filename), + (os.unlink, filename), + (os.utime, filename), + ): + with bytes_filename_warn(True): + try: + func(*args) + except OSError: + # ignore OSError, we only care about DeprecationWarning + pass @support.skip_unless_symlink def test_symlink(self): + self.addCleanup(support.unlink, support.TESTFN) + filename = os.fsencode(support.TESTFN) - with warnings.catch_warnings(): - warnings.simplefilter("error", DeprecationWarning) - self.assertRaises(DeprecationWarning, - os.symlink, filename, filename) + with bytes_filename_warn(True): + os.symlink(filename, filename) @unittest.skipUnless(hasattr(os, 'get_terminal_size'), "requires os.get_terminal_size") @@ -2624,6 +2639,7 @@ class OSErrorTests(unittest.TestCase): else: encoded = os.fsencode(support.TESTFN) self.bytes_filenames.append(encoded) + self.bytes_filenames.append(bytearray(encoded)) self.bytes_filenames.append(memoryview(encoded)) self.filenames = self.bytes_filenames + self.unicode_filenames @@ -2697,7 +2713,14 @@ class OSErrorTests(unittest.TestCase): for filenames, func, *func_args in funcs: for name in filenames: try: - func(name, *func_args) + if isinstance(name, str): + func(name, *func_args) + elif isinstance(name, bytes): + with bytes_filename_warn(False): + func(name, *func_args) + else: + with self.assertWarnsRegex(DeprecationWarning, 'should be'): + func(name, *func_args) except OSError as err: self.assertIs(err.filename, name) else: @@ -2797,6 +2820,70 @@ class FDInheritanceTests(unittest.TestCase): self.assertEqual(os.get_inheritable(slave_fd), False) +class PathTConverterTests(unittest.TestCase): + # tuples of (function name, allows fd arguments, additional arguments to + # function, cleanup function) + functions = [ + ('stat', True, (), None), + ('lstat', False, (), None), + ('access', False, (os.F_OK,), None), + ('chflags', False, (0,), None), + ('lchflags', False, (0,), None), + ('open', False, (0,), getattr(os, 'close', None)), + ] + + def test_path_t_converter(self): + class PathLike: + def __init__(self, path): + self.path = path + + def __fspath__(self): + return self.path + + str_filename = support.TESTFN + if os.name == 'nt': + bytes_fspath = bytes_filename = None + else: + bytes_filename = support.TESTFN.encode('ascii') + bytes_fspath = PathLike(bytes_filename) + fd = os.open(PathLike(str_filename), os.O_WRONLY|os.O_CREAT) + self.addCleanup(support.unlink, support.TESTFN) + self.addCleanup(os.close, fd) + + int_fspath = PathLike(fd) + str_fspath = PathLike(str_filename) + + for name, allow_fd, extra_args, cleanup_fn in self.functions: + with self.subTest(name=name): + try: + fn = getattr(os, name) + except AttributeError: + continue + + for path in (str_filename, bytes_filename, str_fspath, + bytes_fspath): + if path is None: + continue + with self.subTest(name=name, path=path): + result = fn(path, *extra_args) + if cleanup_fn is not None: + cleanup_fn(result) + + with self.assertRaisesRegex( + TypeError, 'should be string, bytes'): + fn(int_fspath, *extra_args) + + if allow_fd: + result = fn(fd, *extra_args) # should not fail + if cleanup_fn is not None: + cleanup_fn(result) + else: + with self.assertRaisesRegex( + TypeError, + 'os.PathLike'): + fn(fd, *extra_args) + + @unittest.skipUnless(hasattr(os, 'get_blocking'), 'needs os.get_blocking() and os.set_blocking()') class BlockingTests(unittest.TestCase): @@ -2820,15 +2907,18 @@ class ExportsTests(unittest.TestCase): class TestScandir(unittest.TestCase): + check_no_resource_warning = support.check_no_resource_warning + def setUp(self): self.path = os.path.realpath(support.TESTFN) + self.bytes_path = os.fsencode(self.path) self.addCleanup(support.rmtree, self.path) os.mkdir(self.path) def create_file(self, name="file.txt"): - filename = os.path.join(self.path, name) - with open(filename, "wb") as fp: - fp.write(b'python') + path = self.bytes_path if isinstance(name, bytes) else self.path + filename = os.path.join(path, name) + create_file(filename, b'python') return filename def get_entries(self, names): @@ -2851,6 +2941,7 @@ class TestScandir(unittest.TestCase): self.assertEqual(stat1, stat2) def check_entry(self, entry, name, is_dir, is_file, is_symlink): + self.assertIsInstance(entry, os.DirEntry) self.assertEqual(entry.name, name) self.assertEqual(entry.path, os.path.join(self.path, name)) self.assertEqual(entry.inode(), @@ -2916,15 +3007,16 @@ class TestScandir(unittest.TestCase): self.check_entry(entry, 'symlink_file.txt', False, True, True) def get_entry(self, name): - entries = list(os.scandir(self.path)) + path = self.bytes_path if isinstance(name, bytes) else self.path + entries = list(os.scandir(path)) self.assertEqual(len(entries), 1) entry = entries[0] self.assertEqual(entry.name, name) return entry - def create_file_entry(self): - filename = self.create_file() + def create_file_entry(self, name='file.txt'): + filename = self.create_file(name=name) return self.get_entry(os.path.basename(filename)) def test_current_directory(self): @@ -2945,6 +3037,19 @@ class TestScandir(unittest.TestCase): entry = self.create_file_entry() self.assertEqual(repr(entry), "<DirEntry 'file.txt'>") + def test_fspath_protocol(self): + entry = self.create_file_entry() + self.assertEqual(os.fspath(entry), os.path.join(self.path, 'file.txt')) + + @unittest.skipIf(os.name == "nt", "test requires bytes path support") + def test_fspath_protocol_bytes(self): + bytes_filename = os.fsencode('bytesfile.txt') + bytes_entry = self.create_file_entry(name=bytes_filename) + fspath = os.fspath(bytes_entry) + self.assertIsInstance(fspath, bytes) + self.assertEqual(fspath, + os.path.join(os.fsencode(self.path),bytes_filename)) + def test_removed_dir(self): path = os.path.join(self.path, 'dir') @@ -3010,7 +3115,8 @@ class TestScandir(unittest.TestCase): def test_bytes(self): if os.name == "nt": # On Windows, os.scandir(bytes) must raise an exception - self.assertRaises(TypeError, os.scandir, b'.') + with bytes_filename_warn(True): + self.assertRaises(TypeError, os.scandir, b'.') return self.create_file("file.txt") @@ -3042,6 +3148,121 @@ class TestScandir(unittest.TestCase): for obj in [1234, 1.234, {}, []]: self.assertRaises(TypeError, os.scandir, obj) + def test_close(self): + self.create_file("file.txt") + self.create_file("file2.txt") + iterator = os.scandir(self.path) + next(iterator) + iterator.close() + # multiple closes + iterator.close() + with self.check_no_resource_warning(): + del iterator + + def test_context_manager(self): + self.create_file("file.txt") + self.create_file("file2.txt") + with os.scandir(self.path) as iterator: + next(iterator) + with self.check_no_resource_warning(): + del iterator + + def test_context_manager_close(self): + self.create_file("file.txt") + self.create_file("file2.txt") + with os.scandir(self.path) as iterator: + next(iterator) + iterator.close() + + def test_context_manager_exception(self): + self.create_file("file.txt") + self.create_file("file2.txt") + with self.assertRaises(ZeroDivisionError): + with os.scandir(self.path) as iterator: + next(iterator) + 1/0 + with self.check_no_resource_warning(): + del iterator + + def test_resource_warning(self): + self.create_file("file.txt") + self.create_file("file2.txt") + iterator = os.scandir(self.path) + next(iterator) + with self.assertWarns(ResourceWarning): + del iterator + support.gc_collect() + # exhausted iterator + iterator = os.scandir(self.path) + list(iterator) + with self.check_no_resource_warning(): + del iterator + + +class TestPEP519(unittest.TestCase): + + # Abstracted so it can be overridden to test pure Python implementation + # if a C version is provided. + fspath = staticmethod(os.fspath) + + class PathLike: + def __init__(self, path=''): + self.path = path + def __fspath__(self): + if isinstance(self.path, BaseException): + raise self.path + else: + return self.path + + def test_return_bytes(self): + for b in b'hello', b'goodbye', b'some/path/and/file': + self.assertEqual(b, self.fspath(b)) + + def test_return_string(self): + for s in 'hello', 'goodbye', 'some/path/and/file': + self.assertEqual(s, self.fspath(s)) + + def test_fsencode_fsdecode(self): + for p in "path/like/object", b"path/like/object": + pathlike = self.PathLike(p) + + self.assertEqual(p, self.fspath(pathlike)) + self.assertEqual(b"path/like/object", os.fsencode(pathlike)) + self.assertEqual("path/like/object", os.fsdecode(pathlike)) + + def test_pathlike(self): + self.assertEqual('#feelthegil', self.fspath(self.PathLike('#feelthegil'))) + self.assertTrue(issubclass(self.PathLike, os.PathLike)) + self.assertTrue(isinstance(self.PathLike(), os.PathLike)) + + def test_garbage_in_exception_out(self): + vapor = type('blah', (), {}) + for o in int, type, os, vapor(): + self.assertRaises(TypeError, self.fspath, o) + + def test_argument_required(self): + self.assertRaises(TypeError, self.fspath) + + def test_bad_pathlike(self): + # __fspath__ returns a value other than str or bytes. + self.assertRaises(TypeError, self.fspath, self.PathLike(42)) + # __fspath__ attribute that is not callable. + c = type('foo', (), {}) + c.__fspath__ = 1 + self.assertRaises(TypeError, self.fspath, c()) + # __fspath__ raises an exception. + self.assertRaises(ZeroDivisionError, self.fspath, + self.PathLike(ZeroDivisionError())) + +# Only test if the C version is provided, otherwise TestPEP519 already tested +# the pure Python implementation. +if hasattr(os, "_fspath"): + class TestPEP519PurePython(TestPEP519): + + """Explicitly test the pure Python implementation of os.fspath().""" + + fspath = staticmethod(os._fspath) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_parser.py b/Lib/test/test_parser.py index ab6577f..e2a42f9 100644 --- a/Lib/test/test_parser.py +++ b/Lib/test/test_parser.py @@ -1,6 +1,5 @@ import parser import unittest -import sys import operator import struct from test import support @@ -633,12 +632,18 @@ class CompileTestCase(unittest.TestCase): self.assertEqual(code.co_filename, '<syntax-tree>') code = st.compile() self.assertEqual(code.co_filename, '<syntax-tree>') - for filename in ('file.py', b'file.py', - bytearray(b'file.py'), memoryview(b'file.py')): + for filename in 'file.py', b'file.py': code = parser.compilest(st, filename) self.assertEqual(code.co_filename, 'file.py') code = st.compile(filename) self.assertEqual(code.co_filename, 'file.py') + for filename in bytearray(b'file.py'), memoryview(b'file.py'): + with self.assertWarns(DeprecationWarning): + code = parser.compilest(st, filename) + self.assertEqual(code.co_filename, 'file.py') + with self.assertWarns(DeprecationWarning): + code = st.compile(filename) + self.assertEqual(code.co_filename, 'file.py') self.assertRaises(TypeError, parser.compilest, st, list(b'file.py')) self.assertRaises(TypeError, st.compile, list(b'file.py')) diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index fa96d9f..2f2ba3c 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -190,13 +190,18 @@ class _BasePurePathTest(object): P = self.cls p = P('a') self.assertIsInstance(p, P) + class PathLike: + def __fspath__(self): + return "a/b/c" P('a', 'b', 'c') P('/a', 'b', 'c') P('a/b/c') P('/a/b/c') + P(PathLike()) self.assertEqual(P(P('a')), P('a')) self.assertEqual(P(P('a'), 'b'), P('a/b')) self.assertEqual(P(P('a'), P('b')), P('a/b')) + self.assertEqual(P(P('a'), P('b'), P('c')), P(PathLike())) def _check_str_subclass(self, *args): # Issue #21127: it should be possible to construct a PurePath object @@ -384,6 +389,12 @@ class _BasePurePathTest(object): parts = p.parts self.assertEqual(parts, (sep, 'a', 'b')) + def test_fspath_common(self): + P = self.cls + p = P('a/b') + self._check_str(p.__fspath__(), ('a/b',)) + self._check_str(os.fspath(p), ('a/b',)) + def test_equivalences(self): for k, tuples in self.equivalences.items(): canon = k.replace('/', self.sep) @@ -1474,14 +1485,14 @@ class _BasePathTest(object): self.assertEqual(set(p.glob("dirA/../file*")), { P(BASE, "dirA/../fileA") }) self.assertEqual(set(p.glob("../xyzzy")), set()) - def _check_resolve_relative(self, p, expected): - q = p.resolve() - self.assertEqual(q, expected) - def _check_resolve_absolute(self, p, expected): + def _check_resolve(self, p, expected): q = p.resolve() self.assertEqual(q, expected) + # this can be used to check both relative and absolute resolutions + _check_resolve_relative = _check_resolve_absolute = _check_resolve + @with_symlinks def test_resolve_common(self): P = self.cls diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 45ba5a9..a63ccd8 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -558,7 +558,6 @@ def test_pdb_continue_in_bottomframe(): def pdb_invoke(method, arg): """Run pdb.method(arg).""" - import pdb getattr(pdb.Pdb(nosigint=True), method)(arg) diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 41e5091..b033640 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -1,9 +1,8 @@ import dis import re import sys -from io import StringIO +import textwrap import unittest -from math import copysign from test.bytecode_helper import BytecodeTestCase @@ -30,22 +29,25 @@ class TestTranforms(BytecodeTestCase): def test_global_as_constant(self): # LOAD_GLOBAL None/True/False --> LOAD_CONST None/True/False - def f(x): - None - None + def f(): + x = None + x = None return x - def g(x): - True + def g(): + x = True return x - def h(x): - False + def h(): + x = False return x + for func, elem in ((f, None), (g, True), (h, False)): self.assertNotInBytecode(func, 'LOAD_GLOBAL') self.assertInBytecode(func, 'LOAD_CONST', elem) + def f(): 'Adding a docstring made this test fail in Py2.5.0' return None + self.assertNotInBytecode(f, 'LOAD_GLOBAL') self.assertInBytecode(f, 'LOAD_CONST', None) diff --git a/Lib/test/test_pep247.py b/Lib/test/test_pep247.py index ab5f4189..c17ceed 100644 --- a/Lib/test/test_pep247.py +++ b/Lib/test/test_pep247.py @@ -1,5 +1,5 @@ """ -Test suite to check compilance with PEP 247, the standard API +Test suite to check compliance with PEP 247, the standard API for hashing algorithms """ diff --git a/Lib/test/test_pep3151.py b/Lib/test/test_pep3151.py index 7b0d465..8649596 100644 --- a/Lib/test/test_pep3151.py +++ b/Lib/test/test_pep3151.py @@ -2,7 +2,6 @@ import builtins import os import select import socket -import sys import unittest import errno from errno import EEXIST diff --git a/Lib/test/test_pep352.py b/Lib/test/test_pep352.py index 7c98c46..27d514f 100644 --- a/Lib/test/test_pep352.py +++ b/Lib/test/test_pep352.py @@ -1,6 +1,5 @@ import unittest import builtins -import warnings import os from platform import system as platform_system diff --git a/Lib/test/test_pickle.py b/Lib/test/test_pickle.py index d467d52..05203e5 100644 --- a/Lib/test/test_pickle.py +++ b/Lib/test/test_pickle.py @@ -34,8 +34,6 @@ class PyUnpicklerTests(AbstractUnpickleTests): unpickler = pickle._Unpickler bad_stack_errors = (IndexError,) - bad_mark_errors = (IndexError, pickle.UnpicklingError, - TypeError, AttributeError, EOFError) truncated_errors = (pickle.UnpicklingError, EOFError, AttributeError, ValueError, struct.error, IndexError, ImportError) @@ -70,8 +68,6 @@ class InMemoryPickleTests(AbstractPickleTests, AbstractUnpickleTests, pickler = pickle._Pickler unpickler = pickle._Unpickler bad_stack_errors = (pickle.UnpicklingError, IndexError) - bad_mark_errors = (pickle.UnpicklingError, IndexError, - TypeError, AttributeError, EOFError) truncated_errors = (pickle.UnpicklingError, EOFError, AttributeError, ValueError, struct.error, IndexError, ImportError) @@ -143,9 +139,7 @@ if has_c_implementation: class CUnpicklerTests(PyUnpicklerTests): unpickler = _pickle.Unpickler bad_stack_errors = (pickle.UnpicklingError,) - bad_mark_errors = (EOFError,) - truncated_errors = (pickle.UnpicklingError, EOFError, - AttributeError, ValueError) + truncated_errors = (pickle.UnpicklingError,) class CPicklerTests(PyPicklerTests): pickler = _pickle.Pickler diff --git a/Lib/test/test_pickletools.py b/Lib/test/test_pickletools.py index bbe6875..86bebfa 100644 --- a/Lib/test/test_pickletools.py +++ b/Lib/test/test_pickletools.py @@ -1,9 +1,9 @@ -import struct import pickle import pickletools from test import support from test.pickletester import AbstractPickleTests from test.pickletester import AbstractPickleModuleTests +import unittest class OptimizedPickleTests(AbstractPickleTests, AbstractPickleModuleTests): @@ -59,8 +59,40 @@ class OptimizedPickleTests(AbstractPickleTests, AbstractPickleModuleTests): self.assertNotIn(pickle.BINPUT, pickled2) +class MiscTestCase(unittest.TestCase): + def test__all__(self): + blacklist = {'bytes_types', + 'UP_TO_NEWLINE', 'TAKEN_FROM_ARGUMENT1', + 'TAKEN_FROM_ARGUMENT4', 'TAKEN_FROM_ARGUMENT4U', + 'TAKEN_FROM_ARGUMENT8U', 'ArgumentDescriptor', + 'read_uint1', 'read_uint2', 'read_int4', 'read_uint4', + 'read_uint8', 'read_stringnl', 'read_stringnl_noescape', + 'read_stringnl_noescape_pair', 'read_string1', + 'read_string4', 'read_bytes1', 'read_bytes4', + 'read_bytes8', 'read_unicodestringnl', + 'read_unicodestring1', 'read_unicodestring4', + 'read_unicodestring8', 'read_decimalnl_short', + 'read_decimalnl_long', 'read_floatnl', 'read_float8', + 'read_long1', 'read_long4', + 'uint1', 'uint2', 'int4', 'uint4', 'uint8', 'stringnl', + 'stringnl_noescape', 'stringnl_noescape_pair', 'string1', + 'string4', 'bytes1', 'bytes4', 'bytes8', + 'unicodestringnl', 'unicodestring1', 'unicodestring4', + 'unicodestring8', 'decimalnl_short', 'decimalnl_long', + 'floatnl', 'float8', 'long1', 'long4', + 'StackObject', + 'pyint', 'pylong', 'pyinteger_or_bool', 'pybool', 'pyfloat', + 'pybytes_or_str', 'pystring', 'pybytes', 'pyunicode', + 'pynone', 'pytuple', 'pylist', 'pydict', 'pyset', + 'pyfrozenset', 'anyobject', 'markobject', 'stackslice', + 'OpcodeInfo', 'opcodes', 'code2op', + } + support.check__all__(self, pickletools, blacklist=blacklist) + + def test_main(): support.run_unittest(OptimizedPickleTests) + support.run_unittest(MiscTestCase) support.run_doctest(pickletools) diff --git a/Lib/test/test_pipes.py b/Lib/test/test_pipes.py index 6a7b45f..ad01d08 100644 --- a/Lib/test/test_pipes.py +++ b/Lib/test/test_pipes.py @@ -2,6 +2,7 @@ import pipes import os import string import unittest +import shutil from test.support import TESTFN, run_unittest, unlink, reap_children if os.name != 'posix': @@ -18,6 +19,8 @@ class SimplePipeTests(unittest.TestCase): unlink(f) def testSimplePipe1(self): + if shutil.which('tr') is None: + self.skipTest('tr is not available') t = pipes.Template() t.append(s_command, pipes.STDIN_STDOUT) f = t.open(TESTFN, 'w') @@ -27,6 +30,8 @@ class SimplePipeTests(unittest.TestCase): self.assertEqual(f.read(), 'HELLO WORLD #1') def testSimplePipe2(self): + if shutil.which('tr') is None: + self.skipTest('tr is not available') with open(TESTFN, 'w') as f: f.write('hello world #2') t = pipes.Template() @@ -36,6 +41,8 @@ class SimplePipeTests(unittest.TestCase): self.assertEqual(f.read(), 'HELLO WORLD #2') def testSimplePipe3(self): + if shutil.which('tr') is None: + self.skipTest('tr is not available') with open(TESTFN, 'w') as f: f.write('hello world #2') t = pipes.Template() diff --git a/Lib/test/test_pkgutil.py b/Lib/test/test_pkgutil.py index 9d20354..ae2aa1b 100644 --- a/Lib/test/test_pkgutil.py +++ b/Lib/test/test_pkgutil.py @@ -205,7 +205,7 @@ class PkgutilPEP302Tests(unittest.TestCase): del sys.meta_path[0] def test_getdata_pep302(self): - # Use a dummy importer/loader + # Use a dummy finder/loader self.assertEqual(pkgutil.get_data('foo', 'dummy'), "Hello, world!") del sys.modules['foo'] @@ -413,6 +413,7 @@ class ImportlibMigrationTests(unittest.TestCase): self.assertIsNotNone(pkgutil.get_loader("test.support")) self.assertEqual(len(w.warnings), 0) + @unittest.skipIf(__name__ == '__main__', 'not compatible with __main__') def test_get_loader_handles_missing_loader_attribute(self): global __loader__ this_loader = __loader__ diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py index 16114f9..60ff918 100644 --- a/Lib/test/test_plistlib.py +++ b/Lib/test/test_plistlib.py @@ -7,7 +7,6 @@ import datetime import codecs import binascii import collections -import struct from test import support from io import BytesIO @@ -527,8 +526,14 @@ class TestPlistlibDeprecated(unittest.TestCase): self.assertEqual(cur, in_data) +class MiscTestCase(unittest.TestCase): + def test__all__(self): + blacklist = {"PlistFormat", "PLISTHEADER"} + support.check__all__(self, plistlib, blacklist=blacklist) + + def test_main(): - support.run_unittest(TestPlistlib, TestPlistlibDeprecated) + support.run_unittest(TestPlistlib, TestPlistlibDeprecated, MiscTestCase) if __name__ == '__main__': diff --git a/Lib/test/test_poplib.py b/Lib/test/test_poplib.py index bceeb93..7b9606d 100644 --- a/Lib/test/test_poplib.py +++ b/Lib/test/test_poplib.py @@ -8,7 +8,6 @@ import asyncore import asynchat import socket import os -import time import errno from unittest import TestCase, skipUnless diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index 2a59c38..d2f58ba 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -11,7 +11,6 @@ import time import os import platform import pwd -import shutil import stat import tempfile import unittest @@ -398,7 +397,7 @@ class PosixTester(unittest.TestCase): self.assertTrue(posix.stat(fp.fileno())) self.assertRaisesRegex(TypeError, - 'should be string, bytes or integer, not', + 'should be string, bytes, os.PathLike or integer, not', posix.stat, float(fp.fileno())) finally: fp.close() @@ -408,16 +407,18 @@ class PosixTester(unittest.TestCase): def test_stat(self): self.assertTrue(posix.stat(support.TESTFN)) self.assertTrue(posix.stat(os.fsencode(support.TESTFN))) - self.assertTrue(posix.stat(bytearray(os.fsencode(support.TESTFN)))) + self.assertWarnsRegex(DeprecationWarning, + 'should be string, bytes, os.PathLike or integer, not', + posix.stat, bytearray(os.fsencode(support.TESTFN))) self.assertRaisesRegex(TypeError, - 'can\'t specify None for path argument', + 'should be string, bytes, os.PathLike or integer, not', posix.stat, None) self.assertRaisesRegex(TypeError, - 'should be string, bytes or integer, not', + 'should be string, bytes, os.PathLike or integer, not', posix.stat, list(support.TESTFN)) self.assertRaisesRegex(TypeError, - 'should be string, bytes or integer, not', + 'should be string, bytes, os.PathLike or integer, not', posix.stat, list(os.fsencode(support.TESTFN))) @unittest.skipUnless(hasattr(posix, 'mkfifo'), "don't have mkfifo()") @@ -863,9 +864,9 @@ class PosixTester(unittest.TestCase): self.assertEqual(s1, s2) s2 = posix.stat(support.TESTFN, dir_fd=None) self.assertEqual(s1, s2) - self.assertRaisesRegex(TypeError, 'should be integer, not', + self.assertRaisesRegex(TypeError, 'should be integer or None, not', posix.stat, support.TESTFN, dir_fd=posix.getcwd()) - self.assertRaisesRegex(TypeError, 'should be integer, not', + self.assertRaisesRegex(TypeError, 'should be integer or None, not', posix.stat, support.TESTFN, dir_fd=float(f)) self.assertRaises(OverflowError, posix.stat, support.TESTFN, dir_fd=10**20) diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py index 0783c36..8a1e33b 100644 --- a/Lib/test/test_posixpath.py +++ b/Lib/test/test_posixpath.py @@ -596,5 +596,85 @@ class PosixCommonTest(test_genericpath.CommonTest, unittest.TestCase): attributes = ['relpath', 'samefile', 'sameopenfile', 'samestat'] +class PathLikeTests(unittest.TestCase): + + path = posixpath + + class PathLike: + def __init__(self, path=''): + self.path = path + def __fspath__(self): + if isinstance(self.path, BaseException): + raise self.path + else: + return self.path + + def setUp(self): + self.file_name = support.TESTFN.lower() + self.file_path = self.PathLike(support.TESTFN) + self.addCleanup(support.unlink, self.file_name) + with open(self.file_name, 'xb', 0) as file: + file.write(b"test_posixpath.PathLikeTests") + + def assertPathEqual(self, func): + self.assertEqual(func(self.file_path), func(self.file_name)) + + def test_path_normcase(self): + self.assertPathEqual(self.path.normcase) + + def test_path_isabs(self): + self.assertPathEqual(self.path.isabs) + + def test_path_join(self): + self.assertEqual(self.path.join('a', self.PathLike('b'), 'c'), + self.path.join('a', 'b', 'c')) + + def test_path_split(self): + self.assertPathEqual(self.path.split) + + def test_path_splitext(self): + self.assertPathEqual(self.path.splitext) + + def test_path_splitdrive(self): + self.assertPathEqual(self.path.splitdrive) + + def test_path_basename(self): + self.assertPathEqual(self.path.basename) + + def test_path_dirname(self): + self.assertPathEqual(self.path.dirname) + + def test_path_islink(self): + self.assertPathEqual(self.path.islink) + + def test_path_lexists(self): + self.assertPathEqual(self.path.lexists) + + def test_path_ismount(self): + self.assertPathEqual(self.path.ismount) + + def test_path_expanduser(self): + self.assertPathEqual(self.path.expanduser) + + def test_path_expandvars(self): + self.assertPathEqual(self.path.expandvars) + + def test_path_normpath(self): + self.assertPathEqual(self.path.normpath) + + def test_path_abspath(self): + self.assertPathEqual(self.path.abspath) + + def test_path_realpath(self): + self.assertPathEqual(self.path.realpath) + + def test_path_relpath(self): + self.assertPathEqual(self.path.relpath) + + def test_path_commonpath(self): + common_path = self.path.commonpath([self.file_path, self.file_name]) + self.assertEqual(common_path, self.file_name) + + if __name__=="__main__": unittest.main() diff --git a/Lib/test/test_pow.py b/Lib/test/test_pow.py index 6feac40..ce99fe6 100644 --- a/Lib/test/test_pow.py +++ b/Lib/test/test_pow.py @@ -1,4 +1,4 @@ -import test.support, unittest +import unittest class PowTest(unittest.TestCase): diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py index ef5e99e..15f88e4 100644 --- a/Lib/test/test_pty.py +++ b/Lib/test/test_pty.py @@ -277,7 +277,6 @@ class SmallPtyTests(unittest.TestCase): socketpair = self._socketpair() masters = [s.fileno() for s in socketpair] - os.close(masters[1]) socketpair[1].close() os.close(write_to_stdin_fd) diff --git a/Lib/test/test_pulldom.py b/Lib/test/test_pulldom.py index 1932c6b..3d89e3a 100644 --- a/Lib/test/test_pulldom.py +++ b/Lib/test/test_pulldom.py @@ -1,6 +1,5 @@ import io import unittest -import sys import xml.sax from xml.sax.xmlreader import AttributesImpl diff --git a/Lib/test/test_pyclbr.py b/Lib/test/test_pyclbr.py index 6ffbbbd..06c10c1 100644 --- a/Lib/test/test_pyclbr.py +++ b/Lib/test/test_pyclbr.py @@ -156,7 +156,7 @@ class PyclbrTest(TestCase): # These were once about the 10 longest modules cm('random', ignore=('Random',)) # from _random import Random as CoreGenerator cm('cgi', ignore=('log',)) # set with = in module - cm('pickle') + cm('pickle', ignore=('partial',)) cm('aifc', ignore=('openfp', '_aifc_params')) # set with = in module cm('sre_parse', ignore=('dump', 'groups')) # from sre_constants import *; property cm('pdb') diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py index aee979b..527234b 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -427,6 +427,7 @@ class PydocDocTest(unittest.TestCase): expected_html = expected_html_pattern % ( (mod_url, mod_file, doc_loc) + expected_html_data_docstrings) + self.maxDiff = None self.assertEqual(result, expected_html) @unittest.skipIf(sys.flags.optimize >= 2, @@ -473,13 +474,18 @@ class PydocDocTest(unittest.TestCase): def test_non_str_name(self): # issue14638 # Treat illegal (non-str) name like no name + # Definition order is set to None so it looks the same in both + # cases. class A: + __definition_order__ = None __name__ = 42 class B: pass adoc = pydoc.render_doc(A()) bdoc = pydoc.render_doc(B()) - self.assertEqual(adoc.replace("A", "B"), bdoc) + self.maxDiff = None + expected = adoc.replace("A", "B") + self.assertEqual(bdoc, expected) def test_not_here(self): missing_module = "test.i_am_not_here" @@ -638,8 +644,9 @@ class PydocDocTest(unittest.TestCase): del expected['__doc__'] del expected['__class__'] # inspect resolves descriptors on type into methods, but vars doesn't, - # so we need to update __subclasshook__. + # so we need to update __subclasshook__ and __init_subclass__. expected['__subclasshook__'] = TestClass.__subclasshook__ + expected['__init_subclass__'] = TestClass.__init_subclass__ methods = pydoc.allmethods(TestClass) self.assertDictEqual(methods, expected) @@ -855,6 +862,22 @@ class TestDescriptions(unittest.TestCase): self.assertEqual(self._get_summary_line(t.wrap), "wrap(text) method of textwrap.TextWrapper instance") + def test_field_order_for_named_tuples(self): + Person = namedtuple('Person', ['nickname', 'firstname', 'agegroup']) + s = pydoc.render_doc(Person) + self.assertLess(s.index('nickname'), s.index('firstname')) + self.assertLess(s.index('firstname'), s.index('agegroup')) + + class NonIterableFields: + _fields = None + + class NonHashableFields: + _fields = [[]] + + # Make sure these doesn't fail + pydoc.render_doc(NonIterableFields) + pydoc.render_doc(NonHashableFields) + @requires_docstrings def test_bound_builtin_method(self): s = StringIO() diff --git a/Lib/test/test_quopri.py b/Lib/test/test_quopri.py index 7cac013..715544c 100644 --- a/Lib/test/test_quopri.py +++ b/Lib/test/test_quopri.py @@ -1,6 +1,6 @@ import unittest -import sys, os, io, subprocess +import sys, io, subprocess import quopri diff --git a/Lib/test/test_range.py b/Lib/test/test_range.py index 106c732..e03b570 100644 --- a/Lib/test/test_range.py +++ b/Lib/test/test_range.py @@ -1,6 +1,6 @@ # Python test set -- built-in functions -import test.support, unittest +import unittest import sys import pickle import itertools diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index 7a74141..24a0604 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -124,7 +124,7 @@ class ReTests(unittest.TestCase): (chr(9)+chr(10)+chr(11)+chr(13)+chr(12)+chr(7)+chr(8))) for c in 'cdehijklmopqsuwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ': with self.subTest(c): - with self.assertWarns(DeprecationWarning): + with self.assertRaises(re.error): self.assertEqual(re.sub('a', '\\' + c, 'a'), '\\' + c) self.assertEqual(re.sub('^\s*', 'X', 'test'), 'Xtest') @@ -414,19 +414,33 @@ class ReTests(unittest.TestCase): self.assertEqual(pat.match('bc').groups(), ('b', None, 'b', 'c')) self.assertEqual(pat.match('bc').groups(""), ('b', "", 'b', 'c')) - # A single group - m = re.match('(a)', 'a') - self.assertEqual(m.group(0), 'a') - self.assertEqual(m.group(0), 'a') - self.assertEqual(m.group(1), 'a') - self.assertEqual(m.group(1, 1), ('a', 'a')) - pat = re.compile('(?:(?P<a1>a)|(?P<b2>b))(?P<c3>c)?') self.assertEqual(pat.match('a').group(1, 2, 3), ('a', None, None)) self.assertEqual(pat.match('b').group('a1', 'b2', 'c3'), (None, 'b', None)) self.assertEqual(pat.match('ac').group(1, 'b2', 3), ('a', None, 'c')) + def test_group(self): + class Index: + def __init__(self, value): + self.value = value + def __index__(self): + return self.value + # A single group + m = re.match('(a)(b)', 'ab') + self.assertEqual(m.group(), 'ab') + self.assertEqual(m.group(0), 'ab') + self.assertEqual(m.group(1), 'a') + self.assertEqual(m.group(Index(1)), 'a') + self.assertRaises(IndexError, m.group, -1) + self.assertRaises(IndexError, m.group, 3) + self.assertRaises(IndexError, m.group, 1<<1000) + self.assertRaises(IndexError, m.group, Index(1<<1000)) + self.assertRaises(IndexError, m.group, 'x') + # Multiple groups + self.assertEqual(m.group(2, 1), ('b', 'a')) + self.assertEqual(m.group(Index(2), Index(1)), ('b', 'a')) + def test_re_fullmatch(self): # Issue 16203: Proposal: add re.fullmatch() method. self.assertEqual(re.fullmatch(r"a", "a").span(), (0, 1)) @@ -633,14 +647,10 @@ class ReTests(unittest.TestCase): re.purge() # for warnings for c in 'ceghijklmopqyzCEFGHIJKLMNOPQRTVXY': with self.subTest(c): - with self.assertWarns(DeprecationWarning): - self.assertEqual(re.fullmatch('\\%c' % c, c).group(), c) - self.assertIsNone(re.match('\\%c' % c, 'a')) + self.assertRaises(re.error, re.compile, '\\%c' % c) for c in 'ceghijklmopqyzABCEFGHIJKLMNOPQRTVXYZ': with self.subTest(c): - with self.assertWarns(DeprecationWarning): - self.assertEqual(re.fullmatch('[\\%c]' % c, c).group(), c) - self.assertIsNone(re.match('[\\%c]' % c, 'a')) + self.assertRaises(re.error, re.compile, '[\\%c]' % c) def test_string_boundaries(self): # See http://bugs.python.org/issue10713 @@ -993,10 +1003,8 @@ class ReTests(unittest.TestCase): self.assertTrue(re.match((r"\x%02x" % i).encode(), bytes([i]))) self.assertTrue(re.match((r"\x%02x0" % i).encode(), bytes([i])+b"0")) self.assertTrue(re.match((r"\x%02xz" % i).encode(), bytes([i])+b"z")) - with self.assertWarns(DeprecationWarning): - self.assertTrue(re.match(br"\u1234", b'u1234')) - with self.assertWarns(DeprecationWarning): - self.assertTrue(re.match(br"\U00012345", b'U00012345')) + self.assertRaises(re.error, re.compile, br"\u1234") + self.assertRaises(re.error, re.compile, br"\U00012345") self.assertTrue(re.match(br"\0", b"\000")) self.assertTrue(re.match(br"\08", b"\0008")) self.assertTrue(re.match(br"\01", b"\001")) @@ -1018,10 +1026,8 @@ class ReTests(unittest.TestCase): self.assertTrue(re.match((r"[\x%02x]" % i).encode(), bytes([i]))) self.assertTrue(re.match((r"[\x%02x0]" % i).encode(), bytes([i]))) self.assertTrue(re.match((r"[\x%02xz]" % i).encode(), bytes([i]))) - with self.assertWarns(DeprecationWarning): - self.assertTrue(re.match(br"[\u1234]", b'u')) - with self.assertWarns(DeprecationWarning): - self.assertTrue(re.match(br"[\U00012345]", b'U')) + self.assertRaises(re.error, re.compile, br"[\u1234]") + self.assertRaises(re.error, re.compile, br"[\U00012345]") self.checkPatternError(br"[\567]", r'octal escape value \567 outside of ' r'range 0-0o377', 1) @@ -1363,12 +1369,12 @@ class ReTests(unittest.TestCase): 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)') + self.assertRaises(ValueError, re.compile, '', re.LOCALE) + self.assertRaises(ValueError, re.compile, '(?L)') + self.assertRaises(ValueError, re.compile, b'', re.LOCALE | re.ASCII) + self.assertRaises(ValueError, re.compile, b'(?L)', re.ASCII) + self.assertRaises(ValueError, re.compile, b'(?a)', re.LOCALE) + self.assertRaises(ValueError, re.compile, b'(?aL)') def test_bug_6509(self): # Replacement strings of both types must parse properly. @@ -1419,13 +1425,6 @@ class ReTests(unittest.TestCase): # Test behaviour when not given a string or pattern as parameter self.assertRaises(TypeError, re.compile, 0) - def test_bug_13899(self): - # Issue #13899: re pattern r"[\A]" should work like "A" but matches - # nothing. Ditto B and Z. - with self.assertWarns(DeprecationWarning): - self.assertEqual(re.findall(r'[\A\B\b\C\Z]', 'AB\bCZ'), - ['A', 'B', '\b', 'C', 'Z']) - @bigmemtest(size=_2G, memuse=1) def test_large_search(self, size): # Issue #10182: indices were 32-bit-truncated. diff --git a/Lib/test/test_readline.py b/Lib/test/test_readline.py index 2c73df2..06a9149 100644 --- a/Lib/test/test_readline.py +++ b/Lib/test/test_readline.py @@ -121,6 +121,21 @@ class TestReadline(unittest.TestCase): TERM='xterm-256color') self.assertEqual(stdout, b'') + auto_history_script = """\ +import readline +readline.set_auto_history({}) +input() +print("History length:", readline.get_current_history_length()) +""" + + def test_auto_history_enabled(self): + output = run_pty(self.auto_history_script.format(True)) + self.assertIn(b"History length: 1\r\n", output) + + def test_auto_history_disabled(self): + output = run_pty(self.auto_history_script.format(False)) + self.assertIn(b"History length: 0\r\n", output) + def test_nonascii(self): try: readline.add_history("\xEB\xEF") diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index a398a4f..dc15461 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -1,21 +1,48 @@ """ Tests of regrtest.py. + +Note: test_regrtest cannot be run twice in parallel. """ -import argparse +import contextlib import faulthandler -import getopt +import io import os.path +import platform +import re +import subprocess +import sys +import sysconfig +import tempfile +import textwrap import unittest -from test import regrtest, support +from test import libregrtest +from test import support -class ParseArgsTestCase(unittest.TestCase): - """Test regrtest's argument parsing.""" +Py_DEBUG = hasattr(sys, 'getobjects') +ROOT_DIR = os.path.join(os.path.dirname(__file__), '..', '..') +ROOT_DIR = os.path.abspath(os.path.normpath(ROOT_DIR)) + +TEST_INTERRUPTED = textwrap.dedent(""" + from signal import SIGINT + try: + from _testcapi import raise_signal + raise_signal(SIGINT) + except ImportError: + import os + os.kill(os.getpid(), SIGINT) + """) + + +class ParseArgsTestCase(unittest.TestCase): + """ + Test regrtest's argument parsing, function _parse_args(). + """ def checkError(self, args, msg): with support.captured_stderr() as err, self.assertRaises(SystemExit): - regrtest._parse_args(args) + libregrtest._parse_args(args) self.assertIn(msg, err.getvalue()) def test_help(self): @@ -23,82 +50,82 @@ class ParseArgsTestCase(unittest.TestCase): with self.subTest(opt=opt): with support.captured_stdout() as out, \ self.assertRaises(SystemExit): - regrtest._parse_args([opt]) + libregrtest._parse_args([opt]) self.assertIn('Run Python regression tests.', out.getvalue()) @unittest.skipUnless(hasattr(faulthandler, 'dump_traceback_later'), "faulthandler.dump_traceback_later() required") def test_timeout(self): - ns = regrtest._parse_args(['--timeout', '4.2']) + ns = libregrtest._parse_args(['--timeout', '4.2']) self.assertEqual(ns.timeout, 4.2) self.checkError(['--timeout'], 'expected one argument') self.checkError(['--timeout', 'foo'], 'invalid float value') def test_wait(self): - ns = regrtest._parse_args(['--wait']) + ns = libregrtest._parse_args(['--wait']) self.assertTrue(ns.wait) def test_slaveargs(self): - ns = regrtest._parse_args(['--slaveargs', '[[], {}]']) + ns = libregrtest._parse_args(['--slaveargs', '[[], {}]']) self.assertEqual(ns.slaveargs, '[[], {}]') self.checkError(['--slaveargs'], 'expected one argument') def test_start(self): for opt in '-S', '--start': with self.subTest(opt=opt): - ns = regrtest._parse_args([opt, 'foo']) + ns = libregrtest._parse_args([opt, 'foo']) self.assertEqual(ns.start, 'foo') self.checkError([opt], 'expected one argument') def test_verbose(self): - ns = regrtest._parse_args(['-v']) + ns = libregrtest._parse_args(['-v']) self.assertEqual(ns.verbose, 1) - ns = regrtest._parse_args(['-vvv']) + ns = libregrtest._parse_args(['-vvv']) self.assertEqual(ns.verbose, 3) - ns = regrtest._parse_args(['--verbose']) + ns = libregrtest._parse_args(['--verbose']) self.assertEqual(ns.verbose, 1) - ns = regrtest._parse_args(['--verbose'] * 3) + ns = libregrtest._parse_args(['--verbose'] * 3) self.assertEqual(ns.verbose, 3) - ns = regrtest._parse_args([]) + ns = libregrtest._parse_args([]) self.assertEqual(ns.verbose, 0) def test_verbose2(self): for opt in '-w', '--verbose2': with self.subTest(opt=opt): - ns = regrtest._parse_args([opt]) + ns = libregrtest._parse_args([opt]) self.assertTrue(ns.verbose2) def test_verbose3(self): for opt in '-W', '--verbose3': with self.subTest(opt=opt): - ns = regrtest._parse_args([opt]) + ns = libregrtest._parse_args([opt]) self.assertTrue(ns.verbose3) def test_quiet(self): for opt in '-q', '--quiet': with self.subTest(opt=opt): - ns = regrtest._parse_args([opt]) + ns = libregrtest._parse_args([opt]) self.assertTrue(ns.quiet) self.assertEqual(ns.verbose, 0) def test_slow(self): - for opt in '-o', '--slow': + for opt in '-o', '--slowest': with self.subTest(opt=opt): - ns = regrtest._parse_args([opt]) + ns = libregrtest._parse_args([opt]) self.assertTrue(ns.print_slow) def test_header(self): - ns = regrtest._parse_args(['--header']) + ns = libregrtest._parse_args(['--header']) self.assertTrue(ns.header) def test_randomize(self): for opt in '-r', '--randomize': with self.subTest(opt=opt): - ns = regrtest._parse_args([opt]) + ns = libregrtest._parse_args([opt]) self.assertTrue(ns.randomize) def test_randseed(self): - ns = regrtest._parse_args(['--randseed', '12345']) + ns = libregrtest._parse_args(['--randseed', '12345']) self.assertEqual(ns.random_seed, 12345) self.assertTrue(ns.randomize) self.checkError(['--randseed'], 'expected one argument') @@ -107,7 +134,7 @@ class ParseArgsTestCase(unittest.TestCase): def test_fromfile(self): for opt in '-f', '--fromfile': with self.subTest(opt=opt): - ns = regrtest._parse_args([opt, 'foo']) + ns = libregrtest._parse_args([opt, 'foo']) self.assertEqual(ns.fromfile, 'foo') self.checkError([opt], 'expected one argument') self.checkError([opt, 'foo', '-s'], "don't go together") @@ -115,42 +142,42 @@ class ParseArgsTestCase(unittest.TestCase): def test_exclude(self): for opt in '-x', '--exclude': with self.subTest(opt=opt): - ns = regrtest._parse_args([opt]) + ns = libregrtest._parse_args([opt]) self.assertTrue(ns.exclude) def test_single(self): for opt in '-s', '--single': with self.subTest(opt=opt): - ns = regrtest._parse_args([opt]) + ns = libregrtest._parse_args([opt]) self.assertTrue(ns.single) self.checkError([opt, '-f', 'foo'], "don't go together") def test_match(self): for opt in '-m', '--match': with self.subTest(opt=opt): - ns = regrtest._parse_args([opt, 'pattern']) + ns = libregrtest._parse_args([opt, 'pattern']) self.assertEqual(ns.match_tests, 'pattern') self.checkError([opt], 'expected one argument') def test_failfast(self): for opt in '-G', '--failfast': with self.subTest(opt=opt): - ns = regrtest._parse_args([opt, '-v']) + ns = libregrtest._parse_args([opt, '-v']) self.assertTrue(ns.failfast) - ns = regrtest._parse_args([opt, '-W']) + ns = libregrtest._parse_args([opt, '-W']) self.assertTrue(ns.failfast) self.checkError([opt], '-G/--failfast needs either -v or -W') def test_use(self): for opt in '-u', '--use': with self.subTest(opt=opt): - ns = regrtest._parse_args([opt, 'gui,network']) + ns = libregrtest._parse_args([opt, 'gui,network']) self.assertEqual(ns.use_resources, ['gui', 'network']) - ns = regrtest._parse_args([opt, 'gui,none,network']) + ns = libregrtest._parse_args([opt, 'gui,none,network']) self.assertEqual(ns.use_resources, ['network']) - expected = list(regrtest.RESOURCE_NAMES) + expected = list(libregrtest.RESOURCE_NAMES) expected.remove('gui') - ns = regrtest._parse_args([opt, 'all,-gui']) + ns = libregrtest._parse_args([opt, 'all,-gui']) self.assertEqual(ns.use_resources, expected) self.checkError([opt], 'expected one argument') self.checkError([opt, 'foo'], 'invalid resource') @@ -158,31 +185,31 @@ class ParseArgsTestCase(unittest.TestCase): def test_memlimit(self): for opt in '-M', '--memlimit': with self.subTest(opt=opt): - ns = regrtest._parse_args([opt, '4G']) + ns = libregrtest._parse_args([opt, '4G']) self.assertEqual(ns.memlimit, '4G') self.checkError([opt], 'expected one argument') def test_testdir(self): - ns = regrtest._parse_args(['--testdir', 'foo']) + ns = libregrtest._parse_args(['--testdir', 'foo']) self.assertEqual(ns.testdir, os.path.join(support.SAVEDCWD, 'foo')) self.checkError(['--testdir'], 'expected one argument') def test_runleaks(self): for opt in '-L', '--runleaks': with self.subTest(opt=opt): - ns = regrtest._parse_args([opt]) + ns = libregrtest._parse_args([opt]) self.assertTrue(ns.runleaks) def test_huntrleaks(self): for opt in '-R', '--huntrleaks': with self.subTest(opt=opt): - ns = regrtest._parse_args([opt, ':']) + ns = libregrtest._parse_args([opt, ':']) self.assertEqual(ns.huntrleaks, (5, 4, 'reflog.txt')) - ns = regrtest._parse_args([opt, '6:']) + ns = libregrtest._parse_args([opt, '6:']) self.assertEqual(ns.huntrleaks, (6, 4, 'reflog.txt')) - ns = regrtest._parse_args([opt, ':3']) + ns = libregrtest._parse_args([opt, ':3']) self.assertEqual(ns.huntrleaks, (5, 3, 'reflog.txt')) - ns = regrtest._parse_args([opt, '6:3:leaks.log']) + ns = libregrtest._parse_args([opt, '6:3:leaks.log']) self.assertEqual(ns.huntrleaks, (6, 3, 'leaks.log')) self.checkError([opt], 'expected one argument') self.checkError([opt, '6'], @@ -193,24 +220,23 @@ class ParseArgsTestCase(unittest.TestCase): def test_multiprocess(self): for opt in '-j', '--multiprocess': with self.subTest(opt=opt): - ns = regrtest._parse_args([opt, '2']) + ns = libregrtest._parse_args([opt, '2']) self.assertEqual(ns.use_mp, 2) self.checkError([opt], 'expected one argument') self.checkError([opt, 'foo'], 'invalid int value') self.checkError([opt, '2', '-T'], "don't go together") self.checkError([opt, '2', '-l'], "don't go together") - self.checkError([opt, '2', '-M', '4G'], "don't go together") def test_coverage(self): for opt in '-T', '--coverage': with self.subTest(opt=opt): - ns = regrtest._parse_args([opt]) + ns = libregrtest._parse_args([opt]) self.assertTrue(ns.trace) def test_coverdir(self): for opt in '-D', '--coverdir': with self.subTest(opt=opt): - ns = regrtest._parse_args([opt, 'foo']) + ns = libregrtest._parse_args([opt, 'foo']) self.assertEqual(ns.coverdir, os.path.join(support.SAVEDCWD, 'foo')) self.checkError([opt], 'expected one argument') @@ -218,13 +244,13 @@ class ParseArgsTestCase(unittest.TestCase): def test_nocoverdir(self): for opt in '-N', '--nocoverdir': with self.subTest(opt=opt): - ns = regrtest._parse_args([opt]) + ns = libregrtest._parse_args([opt]) self.assertIsNone(ns.coverdir) def test_threshold(self): for opt in '-t', '--threshold': with self.subTest(opt=opt): - ns = regrtest._parse_args([opt, '1000']) + ns = libregrtest._parse_args([opt, '1000']) self.assertEqual(ns.threshold, 1000) self.checkError([opt], 'expected one argument') self.checkError([opt, 'foo'], 'invalid int value') @@ -232,13 +258,16 @@ class ParseArgsTestCase(unittest.TestCase): def test_nowindows(self): for opt in '-n', '--nowindows': with self.subTest(opt=opt): - ns = regrtest._parse_args([opt]) + with contextlib.redirect_stderr(io.StringIO()) as stderr: + ns = libregrtest._parse_args([opt]) self.assertTrue(ns.nowindows) + err = stderr.getvalue() + self.assertIn('the --nowindows (-n) option is deprecated', err) def test_forever(self): for opt in '-F', '--forever': with self.subTest(opt=opt): - ns = regrtest._parse_args([opt]) + ns = libregrtest._parse_args([opt]) self.assertTrue(ns.forever) @@ -246,30 +275,519 @@ class ParseArgsTestCase(unittest.TestCase): self.checkError(['--xxx'], 'usage:') def test_long_option__partial(self): - ns = regrtest._parse_args(['--qui']) + ns = libregrtest._parse_args(['--qui']) self.assertTrue(ns.quiet) self.assertEqual(ns.verbose, 0) def test_two_options(self): - ns = regrtest._parse_args(['--quiet', '--exclude']) + ns = libregrtest._parse_args(['--quiet', '--exclude']) self.assertTrue(ns.quiet) self.assertEqual(ns.verbose, 0) self.assertTrue(ns.exclude) def test_option_with_empty_string_value(self): - ns = regrtest._parse_args(['--start', '']) + ns = libregrtest._parse_args(['--start', '']) self.assertEqual(ns.start, '') def test_arg(self): - ns = regrtest._parse_args(['foo']) + ns = libregrtest._parse_args(['foo']) self.assertEqual(ns.args, ['foo']) def test_option_and_arg(self): - ns = regrtest._parse_args(['--quiet', 'foo']) + ns = libregrtest._parse_args(['--quiet', 'foo']) self.assertTrue(ns.quiet) self.assertEqual(ns.verbose, 0) self.assertEqual(ns.args, ['foo']) +class BaseTestCase(unittest.TestCase): + TEST_UNIQUE_ID = 1 + TESTNAME_PREFIX = 'test_regrtest_' + TESTNAME_REGEX = r'test_[a-zA-Z0-9_]+' + + def setUp(self): + self.testdir = os.path.realpath(os.path.dirname(__file__)) + + self.tmptestdir = tempfile.mkdtemp() + self.addCleanup(support.rmtree, self.tmptestdir) + + def create_test(self, name=None, code=''): + if not name: + name = 'noop%s' % BaseTestCase.TEST_UNIQUE_ID + BaseTestCase.TEST_UNIQUE_ID += 1 + + # test_regrtest cannot be run twice in parallel because + # of setUp() and create_test() + name = self.TESTNAME_PREFIX + name + path = os.path.join(self.tmptestdir, name + '.py') + + self.addCleanup(support.unlink, path) + # Use 'x' mode to ensure that we do not override existing tests + try: + with open(path, 'x', encoding='utf-8') as fp: + fp.write(code) + except PermissionError as exc: + if not sysconfig.is_python_build(): + self.skipTest("cannot write %s: %s" % (path, exc)) + raise + return name + + def regex_search(self, regex, output): + match = re.search(regex, output, re.MULTILINE) + if not match: + self.fail("%r not found in %r" % (regex, output)) + return match + + def check_line(self, output, regex): + regex = re.compile(r'^' + regex, re.MULTILINE) + self.assertRegex(output, regex) + + def parse_executed_tests(self, output): + regex = (r'^[0-9]+:[0-9]+:[0-9]+ \[ *[0-9]+(?:/ *[0-9]+)?\] (%s)' + % self.TESTNAME_REGEX) + parser = re.finditer(regex, output, re.MULTILINE) + return list(match.group(1) for match in parser) + + def check_executed_tests(self, output, tests, skipped=(), failed=(), + omitted=(), randomize=False, interrupted=False): + if isinstance(tests, str): + tests = [tests] + if isinstance(skipped, str): + skipped = [skipped] + if isinstance(failed, str): + failed = [failed] + if isinstance(omitted, str): + omitted = [omitted] + ntest = len(tests) + nskipped = len(skipped) + nfailed = len(failed) + nomitted = len(omitted) + + executed = self.parse_executed_tests(output) + if randomize: + self.assertEqual(set(executed), set(tests), output) + else: + self.assertEqual(executed, tests, output) + + def plural(count): + return 's' if count != 1 else '' + + def list_regex(line_format, tests): + count = len(tests) + names = ' '.join(sorted(tests)) + regex = line_format % (count, plural(count)) + regex = r'%s:\n %s$' % (regex, names) + return regex + + if skipped: + regex = list_regex('%s test%s skipped', skipped) + self.check_line(output, regex) + + if failed: + regex = list_regex('%s test%s failed', failed) + self.check_line(output, regex) + + if omitted: + regex = list_regex('%s test%s omitted', omitted) + self.check_line(output, regex) + + good = ntest - nskipped - nfailed - nomitted + if good: + regex = r'%s test%s OK\.$' % (good, plural(good)) + if not skipped and not failed and good > 1: + regex = 'All %s' % regex + self.check_line(output, regex) + + if interrupted: + self.check_line(output, 'Test suite interrupted by signal SIGINT.') + + if nfailed: + result = 'FAILURE' + elif interrupted: + result = 'INTERRUPTED' + else: + result = 'SUCCESS' + self.check_line(output, 'Tests result: %s' % result) + + def parse_random_seed(self, output): + match = self.regex_search(r'Using random seed ([0-9]+)', output) + randseed = int(match.group(1)) + self.assertTrue(0 <= randseed <= 10000000, randseed) + return randseed + + def run_command(self, args, input=None, exitcode=0, **kw): + if not input: + input = '' + if 'stderr' not in kw: + kw['stderr'] = subprocess.PIPE + proc = subprocess.run(args, + universal_newlines=True, + input=input, + stdout=subprocess.PIPE, + **kw) + if proc.returncode != exitcode: + msg = ("Command %s failed with exit code %s\n" + "\n" + "stdout:\n" + "---\n" + "%s\n" + "---\n" + % (str(args), proc.returncode, proc.stdout)) + if proc.stderr: + msg += ("\n" + "stderr:\n" + "---\n" + "%s" + "---\n" + % proc.stderr) + self.fail(msg) + return proc + + + def run_python(self, args, **kw): + args = [sys.executable, '-X', 'faulthandler', '-I', *args] + proc = self.run_command(args, **kw) + return proc.stdout + + +class ProgramsTestCase(BaseTestCase): + """ + Test various ways to run the Python test suite. Use options close + to options used on the buildbot. + """ + + NTEST = 4 + + def setUp(self): + super().setUp() + + # Create NTEST tests doing nothing + self.tests = [self.create_test() for index in range(self.NTEST)] + + self.python_args = ['-Wd', '-E', '-bb'] + self.regrtest_args = ['-uall', '-rwW', + '--testdir=%s' % self.tmptestdir] + if hasattr(faulthandler, 'dump_traceback_later'): + self.regrtest_args.extend(('--timeout', '3600', '-j4')) + if sys.platform == 'win32': + self.regrtest_args.append('-n') + + def check_output(self, output): + self.parse_random_seed(output) + self.check_executed_tests(output, self.tests, randomize=True) + + def run_tests(self, args): + output = self.run_python(args) + self.check_output(output) + + def test_script_regrtest(self): + # Lib/test/regrtest.py + script = os.path.join(self.testdir, 'regrtest.py') + + args = [*self.python_args, script, *self.regrtest_args, *self.tests] + self.run_tests(args) + + def test_module_test(self): + # -m test + args = [*self.python_args, '-m', 'test', + *self.regrtest_args, *self.tests] + self.run_tests(args) + + def test_module_regrtest(self): + # -m test.regrtest + args = [*self.python_args, '-m', 'test.regrtest', + *self.regrtest_args, *self.tests] + self.run_tests(args) + + def test_module_autotest(self): + # -m test.autotest + args = [*self.python_args, '-m', 'test.autotest', + *self.regrtest_args, *self.tests] + self.run_tests(args) + + def test_module_from_test_autotest(self): + # from test import autotest + code = 'from test import autotest' + args = [*self.python_args, '-c', code, + *self.regrtest_args, *self.tests] + self.run_tests(args) + + def test_script_autotest(self): + # Lib/test/autotest.py + script = os.path.join(self.testdir, 'autotest.py') + args = [*self.python_args, script, *self.regrtest_args, *self.tests] + self.run_tests(args) + + @unittest.skipUnless(sysconfig.is_python_build(), + 'run_tests.py script is not installed') + def test_tools_script_run_tests(self): + # Tools/scripts/run_tests.py + script = os.path.join(ROOT_DIR, 'Tools', 'scripts', 'run_tests.py') + args = [script, *self.regrtest_args, *self.tests] + self.run_tests(args) + + def run_batch(self, *args): + proc = self.run_command(args) + self.check_output(proc.stdout) + + @unittest.skipUnless(sysconfig.is_python_build(), + 'test.bat script is not installed') + @unittest.skipUnless(sys.platform == 'win32', 'Windows only') + def test_tools_buildbot_test(self): + # Tools\buildbot\test.bat + script = os.path.join(ROOT_DIR, 'Tools', 'buildbot', 'test.bat') + test_args = ['--testdir=%s' % self.tmptestdir] + if platform.architecture()[0] == '64bit': + test_args.append('-x64') # 64-bit build + if not Py_DEBUG: + test_args.append('+d') # Release build, use python.exe + self.run_batch(script, *test_args, *self.tests) + + @unittest.skipUnless(sys.platform == 'win32', 'Windows only') + def test_pcbuild_rt(self): + # PCbuild\rt.bat + script = os.path.join(ROOT_DIR, r'PCbuild\rt.bat') + rt_args = ["-q"] # Quick, don't run tests twice + if platform.architecture()[0] == '64bit': + rt_args.append('-x64') # 64-bit build + if Py_DEBUG: + rt_args.append('-d') # Debug build, use python_d.exe + self.run_batch(script, *rt_args, *self.regrtest_args, *self.tests) + + +class ArgsTestCase(BaseTestCase): + """ + Test arguments of the Python test suite. + """ + + def run_tests(self, *testargs, **kw): + cmdargs = ['-m', 'test', '--testdir=%s' % self.tmptestdir, *testargs] + return self.run_python(cmdargs, **kw) + + def test_failing_test(self): + # test a failing test + code = textwrap.dedent(""" + import unittest + + class FailingTest(unittest.TestCase): + def test_failing(self): + self.fail("bug") + """) + test_ok = self.create_test('ok') + test_failing = self.create_test('failing', code=code) + tests = [test_ok, test_failing] + + output = self.run_tests(*tests, exitcode=1) + self.check_executed_tests(output, tests, failed=test_failing) + + def test_resources(self): + # test -u command line option + tests = {} + for resource in ('audio', 'network'): + code = 'from test import support\nsupport.requires(%r)' % resource + tests[resource] = self.create_test(resource, code) + test_names = sorted(tests.values()) + + # -u all: 2 resources enabled + output = self.run_tests('-u', 'all', *test_names) + self.check_executed_tests(output, test_names) + + # -u audio: 1 resource enabled + output = self.run_tests('-uaudio', *test_names) + self.check_executed_tests(output, test_names, + skipped=tests['network']) + + # no option: 0 resources enabled + output = self.run_tests(*test_names) + self.check_executed_tests(output, test_names, + skipped=test_names) + + def test_random(self): + # test -r and --randseed command line option + code = textwrap.dedent(""" + import random + print("TESTRANDOM: %s" % random.randint(1, 1000)) + """) + test = self.create_test('random', code) + + # first run to get the output with the random seed + output = self.run_tests('-r', test) + randseed = self.parse_random_seed(output) + match = self.regex_search(r'TESTRANDOM: ([0-9]+)', output) + test_random = int(match.group(1)) + + # try to reproduce with the random seed + output = self.run_tests('-r', '--randseed=%s' % randseed, test) + randseed2 = self.parse_random_seed(output) + self.assertEqual(randseed2, randseed) + + match = self.regex_search(r'TESTRANDOM: ([0-9]+)', output) + test_random2 = int(match.group(1)) + self.assertEqual(test_random2, test_random) + + def test_fromfile(self): + # test --fromfile + tests = [self.create_test() for index in range(5)] + + # Write the list of files using a format similar to regrtest output: + # [1/2] test_1 + # [2/2] test_2 + filename = support.TESTFN + self.addCleanup(support.unlink, filename) + + # test format '0:00:00 [2/7] test_opcodes -- test_grammar took 0 sec' + with open(filename, "w") as fp: + previous = None + for index, name in enumerate(tests, 1): + line = ("00:00:%02i [%s/%s] %s" + % (index, index, len(tests), name)) + if previous: + line += " -- %s took 0 sec" % previous + print(line, file=fp) + previous = name + + output = self.run_tests('--fromfile', filename) + self.check_executed_tests(output, tests) + + # test format '[2/7] test_opcodes' + with open(filename, "w") as fp: + for index, name in enumerate(tests, 1): + print("[%s/%s] %s" % (index, len(tests), name), file=fp) + + output = self.run_tests('--fromfile', filename) + self.check_executed_tests(output, tests) + + # test format 'test_opcodes' + with open(filename, "w") as fp: + for name in tests: + print(name, file=fp) + + output = self.run_tests('--fromfile', filename) + self.check_executed_tests(output, tests) + + def test_interrupted(self): + code = TEST_INTERRUPTED + test = self.create_test('sigint', code=code) + output = self.run_tests(test, exitcode=1) + self.check_executed_tests(output, test, omitted=test, + interrupted=True) + + def test_slowest(self): + # test --slowest + tests = [self.create_test() for index in range(3)] + output = self.run_tests("--slowest", *tests) + self.check_executed_tests(output, tests) + regex = ('10 slowest tests:\n' + '(?:- %s: .*\n){%s}' + % (self.TESTNAME_REGEX, len(tests))) + self.check_line(output, regex) + + def test_slow_interrupted(self): + # Issue #25373: test --slowest with an interrupted test + code = TEST_INTERRUPTED + test = self.create_test("sigint", code=code) + + for multiprocessing in (False, True): + if multiprocessing: + args = ("--slowest", "-j2", test) + else: + args = ("--slowest", test) + output = self.run_tests(*args, exitcode=1) + self.check_executed_tests(output, test, + omitted=test, interrupted=True) + + regex = ('10 slowest tests:\n') + self.check_line(output, regex) + + def test_coverage(self): + # test --coverage + test = self.create_test('coverage') + output = self.run_tests("--coverage", test) + self.check_executed_tests(output, [test]) + regex = ('lines +cov% +module +\(path\)\n' + '(?: *[0-9]+ *[0-9]{1,2}% *[^ ]+ +\([^)]+\)+)+') + self.check_line(output, regex) + + def test_wait(self): + # test --wait + test = self.create_test('wait') + output = self.run_tests("--wait", test, input='key') + self.check_line(output, 'Press any key to continue') + + def test_forever(self): + # test --forever + code = textwrap.dedent(""" + import builtins + import unittest + + class ForeverTester(unittest.TestCase): + def test_run(self): + # Store the state in the builtins module, because the test + # module is reload at each run + if 'RUN' in builtins.__dict__: + builtins.__dict__['RUN'] += 1 + if builtins.__dict__['RUN'] >= 3: + self.fail("fail at the 3rd runs") + else: + builtins.__dict__['RUN'] = 1 + """) + test = self.create_test('forever', code=code) + output = self.run_tests('--forever', test, exitcode=1) + self.check_executed_tests(output, [test]*3, failed=test) + + @unittest.skipUnless(Py_DEBUG, 'need a debug build') + def test_huntrleaks_fd_leak(self): + # test --huntrleaks for file descriptor leak + code = textwrap.dedent(""" + import os + import unittest + + # Issue #25306: Disable popups and logs to stderr on assertion + # failures in MSCRT + try: + import msvcrt + msvcrt.CrtSetReportMode + except (ImportError, AttributeError): + # no Windows, o release build + pass + else: + for m in [msvcrt.CRT_WARN, msvcrt.CRT_ERROR, msvcrt.CRT_ASSERT]: + msvcrt.CrtSetReportMode(m, 0) + + class FDLeakTest(unittest.TestCase): + def test_leak(self): + fd = os.open(__file__, os.O_RDONLY) + # bug: never cloes the file descriptor + """) + test = self.create_test('huntrleaks', code=code) + + filename = 'reflog.txt' + self.addCleanup(support.unlink, filename) + output = self.run_tests('--huntrleaks', '3:3:', test, + exitcode=1, + stderr=subprocess.STDOUT) + self.check_executed_tests(output, [test], failed=test) + + line = 'beginning 6 repetitions\n123456\n......\n' + self.check_line(output, re.escape(line)) + + line2 = '%s leaked [1, 1, 1] file descriptors, sum=3\n' % test + self.check_line(output, re.escape(line2)) + + with open(filename) as fp: + reflog = fp.read() + if hasattr(sys, 'getcounts'): + # Types are immportal if COUNT_ALLOCS is defined + reflog = reflog.splitlines(True)[-1] + self.assertEqual(reflog, line2) + + def test_list_tests(self): + # test --list-tests + tests = [self.create_test() for i in range(5)] + output = self.run_tests('--list-tests', *tests) + self.assertEqual(output.rstrip().splitlines(), + tests) + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_richcmp.py b/Lib/test/test_richcmp.py index 1582caa..58729a9 100644 --- a/Lib/test/test_richcmp.py +++ b/Lib/test/test_richcmp.py @@ -253,6 +253,31 @@ class MiscTest(unittest.TestCase): self.assertTrue(a != b) self.assertTrue(a < b) + def test_exception_message(self): + class Spam: + pass + + tests = [ + (lambda: 42 < None, r"'<' .* of 'int' and 'NoneType'"), + (lambda: None < 42, r"'<' .* of 'NoneType' and 'int'"), + (lambda: 42 > None, r"'>' .* of 'int' and 'NoneType'"), + (lambda: "foo" < None, r"'<' .* of 'str' and 'NoneType'"), + (lambda: "foo" >= 666, r"'>=' .* of 'str' and 'int'"), + (lambda: 42 <= None, r"'<=' .* of 'int' and 'NoneType'"), + (lambda: 42 >= None, r"'>=' .* of 'int' and 'NoneType'"), + (lambda: 42 < [], r"'<' .* of 'int' and 'list'"), + (lambda: () > [], r"'>' .* of 'tuple' and 'list'"), + (lambda: None >= None, r"'>=' .* of 'NoneType' and 'NoneType'"), + (lambda: Spam() < 42, r"'<' .* of 'Spam' and 'int'"), + (lambda: 42 < Spam(), r"'<' .* of 'int' and 'Spam'"), + (lambda: Spam() <= Spam(), r"'<=' .* of 'Spam' and 'Spam'"), + ] + for i, test in enumerate(tests): + with self.subTest(test=i): + with self.assertRaisesRegex(TypeError, test[1]): + test[0]() + + class DictTest(unittest.TestCase): def test_dicts(self): diff --git a/Lib/test/test_rlcompleter.py b/Lib/test/test_rlcompleter.py index 853e773..0dc1080 100644 --- a/Lib/test/test_rlcompleter.py +++ b/Lib/test/test_rlcompleter.py @@ -1,11 +1,12 @@ import unittest -import unittest.mock +from unittest.mock import patch import builtins import rlcompleter class CompleteMe: """ Trivial class used in testing rlcompleter.Completer. """ spam = 1 + _ham = 2 class TestRlcompleter(unittest.TestCase): @@ -52,18 +53,32 @@ class TestRlcompleter(unittest.TestCase): ['str.{}('.format(x) for x in dir(str) if x.startswith('s')]) self.assertEqual(self.stdcompleter.attr_matches('tuple.foospamegg'), []) + expected = sorted({'None.%s%s' % (x, '(' if x != '__doc__' else '') + for x in dir(None)}) + self.assertEqual(self.stdcompleter.attr_matches('None.'), expected) + self.assertEqual(self.stdcompleter.attr_matches('None._'), expected) + self.assertEqual(self.stdcompleter.attr_matches('None.__'), expected) # test with a customized namespace self.assertEqual(self.completer.attr_matches('CompleteMe.sp'), ['CompleteMe.spam']) self.assertEqual(self.completer.attr_matches('Completeme.egg'), []) + self.assertEqual(self.completer.attr_matches('CompleteMe.'), + ['CompleteMe.mro(', 'CompleteMe.spam']) + self.assertEqual(self.completer.attr_matches('CompleteMe._'), + ['CompleteMe._ham']) + matches = self.completer.attr_matches('CompleteMe.__') + for x in matches: + self.assertTrue(x.startswith('CompleteMe.__'), x) + self.assertIn('CompleteMe.__name__', matches) + self.assertIn('CompleteMe.__new__(', matches) - CompleteMe.me = CompleteMe - self.assertEqual(self.completer.attr_matches('CompleteMe.me.me.sp'), - ['CompleteMe.me.me.spam']) - self.assertEqual(self.completer.attr_matches('egg.s'), - ['egg.{}('.format(x) for x in dir(str) - if x.startswith('s')]) + with patch.object(CompleteMe, "me", CompleteMe, create=True): + self.assertEqual(self.completer.attr_matches('CompleteMe.me.me.sp'), + ['CompleteMe.me.me.spam']) + self.assertEqual(self.completer.attr_matches('egg.s'), + ['egg.{}('.format(x) for x in dir(str) + if x.startswith('s')]) def test_excessive_getattr(self): # Ensure getattr() is invoked no more than once per attribute @@ -78,14 +93,27 @@ class TestRlcompleter(unittest.TestCase): self.assertEqual(completer.complete('f.b', 0), 'f.bar') self.assertEqual(f.calls, 1) + def test_uncreated_attr(self): + # Attributes like properties and slots should be completed even when + # they haven't been created on an instance + class Foo: + __slots__ = ("bar",) + completer = rlcompleter.Completer(dict(f=Foo())) + self.assertEqual(completer.complete('f.', 0), 'f.bar') + @unittest.mock.patch('rlcompleter._readline_available', False) def test_complete(self): completer = rlcompleter.Completer() self.assertEqual(completer.complete('', 0), '\t') - self.assertEqual(completer.complete('a', 0), 'and') - self.assertEqual(completer.complete('a', 1), 'as') - self.assertEqual(completer.complete('as', 2), 'assert') - self.assertEqual(completer.complete('an', 0), 'and') + self.assertEqual(completer.complete('a', 0), 'and ') + self.assertEqual(completer.complete('a', 1), 'as ') + self.assertEqual(completer.complete('as', 2), 'assert ') + self.assertEqual(completer.complete('an', 0), 'and ') + self.assertEqual(completer.complete('pa', 0), 'pass') + self.assertEqual(completer.complete('Fa', 0), 'False') + self.assertEqual(completer.complete('el', 0), 'elif ') + self.assertEqual(completer.complete('el', 1), 'else') + self.assertEqual(completer.complete('tr', 0), 'try:') def test_duplicate_globals(self): namespace = { @@ -98,9 +126,10 @@ class TestRlcompleter(unittest.TestCase): completer = rlcompleter.Completer(namespace) self.assertEqual(completer.complete('False', 0), 'False') self.assertIsNone(completer.complete('False', 1)) # No duplicates - self.assertEqual(completer.complete('assert', 0), 'assert') + # Space or colon added due to being a reserved keyword + self.assertEqual(completer.complete('assert', 0), 'assert ') self.assertIsNone(completer.complete('assert', 1)) - self.assertEqual(completer.complete('try', 0), 'try') + self.assertEqual(completer.complete('try', 0), 'try:') self.assertIsNone(completer.complete('try', 1)) # No opening bracket "(" because we overrode the built-in class self.assertEqual(completer.complete('memoryview', 0), 'memoryview') diff --git a/Lib/test/test_robotparser.py b/Lib/test/test_robotparser.py index d01266f..76f4f7c 100644 --- a/Lib/test/test_robotparser.py +++ b/Lib/test/test_robotparser.py @@ -1,8 +1,7 @@ import io import unittest import urllib.robotparser -from urllib.error import URLError, HTTPError -from urllib.request import urlopen +from collections import namedtuple from test import support from http.server import BaseHTTPRequestHandler, HTTPServer try: @@ -12,7 +11,8 @@ except ImportError: class RobotTestCase(unittest.TestCase): - def __init__(self, index=None, parser=None, url=None, good=None, agent=None): + def __init__(self, index=None, parser=None, url=None, good=None, + agent=None, request_rate=None, crawl_delay=None): # workaround to make unittest discovery work (see #17066) if not isinstance(index, int): return @@ -25,6 +25,8 @@ class RobotTestCase(unittest.TestCase): self.url = url self.good = good self.agent = agent + self.request_rate = request_rate + self.crawl_delay = crawl_delay def runTest(self): if isinstance(self.url, tuple): @@ -34,6 +36,18 @@ class RobotTestCase(unittest.TestCase): agent = self.agent if self.good: self.assertTrue(self.parser.can_fetch(agent, url)) + self.assertEqual(self.parser.crawl_delay(agent), self.crawl_delay) + # if we have actual values for request rate + if self.request_rate and self.parser.request_rate(agent): + self.assertEqual( + self.parser.request_rate(agent).requests, + self.request_rate.requests + ) + self.assertEqual( + self.parser.request_rate(agent).seconds, + self.request_rate.seconds + ) + self.assertEqual(self.parser.request_rate(agent), self.request_rate) else: self.assertFalse(self.parser.can_fetch(agent, url)) @@ -43,15 +57,17 @@ class RobotTestCase(unittest.TestCase): tests = unittest.TestSuite() def RobotTest(index, robots_txt, good_urls, bad_urls, - agent="test_robotparser"): + request_rate, crawl_delay, agent="test_robotparser"): lines = io.StringIO(robots_txt).readlines() parser = urllib.robotparser.RobotFileParser() parser.parse(lines) for url in good_urls: - tests.addTest(RobotTestCase(index, parser, url, 1, agent)) + tests.addTest(RobotTestCase(index, parser, url, 1, agent, + request_rate, crawl_delay)) for url in bad_urls: - tests.addTest(RobotTestCase(index, parser, url, 0, agent)) + tests.addTest(RobotTestCase(index, parser, url, 0, agent, + request_rate, crawl_delay)) # Examples from http://www.robotstxt.org/wc/norobots.html (fetched 2002) @@ -65,14 +81,18 @@ Disallow: /foo.html good = ['/','/test.html'] bad = ['/cyberworld/map/index.html','/tmp/xxx','/foo.html'] +request_rate = None +crawl_delay = None -RobotTest(1, doc, good, bad) +RobotTest(1, doc, good, bad, request_rate, crawl_delay) # 2. doc = """ # robots.txt for http://www.example.com/ User-agent: * +Crawl-delay: 1 +Request-rate: 3/15 Disallow: /cyberworld/map/ # This is an infinite virtual URL space # Cybermapper knows where to go. @@ -83,8 +103,10 @@ Disallow: good = ['/','/test.html',('cybermapper','/cyberworld/map/index.html')] bad = ['/cyberworld/map/index.html'] +request_rate = None # The parameters should be equal to None since they +crawl_delay = None # don't apply to the cybermapper user agent -RobotTest(2, doc, good, bad) +RobotTest(2, doc, good, bad, request_rate, crawl_delay) # 3. doc = """ @@ -95,14 +117,18 @@ Disallow: / good = [] bad = ['/cyberworld/map/index.html','/','/tmp/'] +request_rate = None +crawl_delay = None -RobotTest(3, doc, good, bad) +RobotTest(3, doc, good, bad, request_rate, crawl_delay) # Examples from http://www.robotstxt.org/wc/norobots-rfc.html (fetched 2002) # 4. doc = """ User-agent: figtree +Crawl-delay: 3 +Request-rate: 9/30 Disallow: /tmp Disallow: /a%3cd.html Disallow: /a%2fb.html @@ -115,8 +141,17 @@ bad = ['/tmp','/tmp.html','/tmp/a.html', '/~joe/index.html' ] -RobotTest(4, doc, good, bad, 'figtree') -RobotTest(5, doc, good, bad, 'FigTree Robot libwww-perl/5.04') +request_rate = namedtuple('req_rate', 'requests seconds') +request_rate.requests = 9 +request_rate.seconds = 30 +crawl_delay = 3 +request_rate_bad = None # not actually tested, but we still need to parse it +crawl_delay_bad = None # in order to accommodate the input parameters + + +RobotTest(4, doc, good, bad, request_rate, crawl_delay, 'figtree' ) +RobotTest(5, doc, good, bad, request_rate_bad, crawl_delay_bad, + 'FigTree Robot libwww-perl/5.04') # 6. doc = """ @@ -125,14 +160,18 @@ Disallow: /tmp/ Disallow: /a%3Cd.html Disallow: /a/b.html Disallow: /%7ejoe/index.html +Crawl-delay: 3 +Request-rate: 9/banana """ good = ['/tmp',] # XFAIL: '/a%2fb.html' bad = ['/tmp/','/tmp/a.html', '/a%3cd.html','/a%3Cd.html',"/a/b.html", '/%7Ejoe/index.html'] +crawl_delay = 3 +request_rate = None # since request rate has invalid syntax, return None -RobotTest(6, doc, good, bad) +RobotTest(6, doc, good, bad, None, None) # From bug report #523041 @@ -140,12 +179,16 @@ RobotTest(6, doc, good, bad) doc = """ User-Agent: * Disallow: /. +Crawl-delay: pears """ good = ['/foo.html'] -bad = [] # Bug report says "/" should be denied, but that is not in the RFC +bad = [] # bug report says "/" should be denied, but that is not in the RFC + +crawl_delay = None # since crawl delay has invalid syntax, return None +request_rate = None -RobotTest(7, doc, good, bad) +RobotTest(7, doc, good, bad, crawl_delay, request_rate) # From Google: http://www.google.com/support/webmasters/bin/answer.py?hl=en&answer=40364 @@ -154,12 +197,15 @@ doc = """ User-agent: Googlebot Allow: /folder1/myfile.html Disallow: /folder1/ +Request-rate: whale/banana """ good = ['/folder1/myfile.html'] bad = ['/folder1/anotherfile.html'] +crawl_delay = None +request_rate = None # invalid syntax, return none -RobotTest(8, doc, good, bad, agent="Googlebot") +RobotTest(8, doc, good, bad, crawl_delay, request_rate, agent="Googlebot") # 9. This file is incorrect because "Googlebot" is a substring of # "Googlebot-Mobile", so test 10 works just like test 9. @@ -174,12 +220,12 @@ Allow: / good = [] bad = ['/something.jpg'] -RobotTest(9, doc, good, bad, agent="Googlebot") +RobotTest(9, doc, good, bad, None, None, agent="Googlebot") good = [] bad = ['/something.jpg'] -RobotTest(10, doc, good, bad, agent="Googlebot-Mobile") +RobotTest(10, doc, good, bad, None, None, agent="Googlebot-Mobile") # 11. Get the order correct. doc = """ @@ -193,12 +239,12 @@ Disallow: / good = [] bad = ['/something.jpg'] -RobotTest(11, doc, good, bad, agent="Googlebot") +RobotTest(11, doc, good, bad, None, None, agent="Googlebot") good = ['/something.jpg'] bad = [] -RobotTest(12, doc, good, bad, agent="Googlebot-Mobile") +RobotTest(12, doc, good, bad, None, None, agent="Googlebot-Mobile") # 13. Google also got the order wrong in #8. You need to specify the @@ -212,7 +258,7 @@ Disallow: /folder1/ good = ['/folder1/myfile.html'] bad = ['/folder1/anotherfile.html'] -RobotTest(13, doc, good, bad, agent="googlebot") +RobotTest(13, doc, good, bad, None, None, agent="googlebot") # 14. For issue #6325 (query string support) @@ -224,7 +270,7 @@ Disallow: /some/path?name=value good = ['/some/path'] bad = ['/some/path?name=value'] -RobotTest(14, doc, good, bad) +RobotTest(14, doc, good, bad, None, None) # 15. For issue #4108 (obey first * entry) doc = """ @@ -238,7 +284,7 @@ Disallow: /another/path good = ['/another/path'] bad = ['/some/path'] -RobotTest(15, doc, good, bad) +RobotTest(15, doc, good, bad, None, None) # 16. Empty query (issue #17403). Normalizing the url first. doc = """ @@ -250,7 +296,7 @@ Disallow: /another/path? good = ['/some/path?'] bad = ['/another/path?'] -RobotTest(16, doc, good, bad) +RobotTest(16, doc, good, bad, None, None) class RobotHandler(BaseHTTPRequestHandler): diff --git a/Lib/test/test_sched.py b/Lib/test/test_sched.py index fe8e785..f86f599 100644 --- a/Lib/test/test_sched.py +++ b/Lib/test/test_sched.py @@ -2,7 +2,6 @@ import queue import sched import time import unittest -from test import support try: import threading except ImportError: diff --git a/Lib/test/test_secrets.py b/Lib/test/test_secrets.py new file mode 100644 index 0000000..4c65cf0 --- /dev/null +++ b/Lib/test/test_secrets.py @@ -0,0 +1,123 @@ +"""Test the secrets module. + +As most of the functions in secrets are thin wrappers around functions +defined elsewhere, we don't need to test them exhaustively. +""" + + +import secrets +import unittest +import string + + +# === Unit tests === + +class Compare_Digest_Tests(unittest.TestCase): + """Test secrets.compare_digest function.""" + + def test_equal(self): + # Test compare_digest functionality with equal (byte/text) strings. + for s in ("a", "bcd", "xyz123"): + a = s*100 + b = s*100 + self.assertTrue(secrets.compare_digest(a, b)) + self.assertTrue(secrets.compare_digest(a.encode('utf-8'), b.encode('utf-8'))) + + def test_unequal(self): + # Test compare_digest functionality with unequal (byte/text) strings. + self.assertFalse(secrets.compare_digest("abc", "abcd")) + self.assertFalse(secrets.compare_digest(b"abc", b"abcd")) + for s in ("x", "mn", "a1b2c3"): + a = s*100 + "q" + b = s*100 + "k" + self.assertFalse(secrets.compare_digest(a, b)) + self.assertFalse(secrets.compare_digest(a.encode('utf-8'), b.encode('utf-8'))) + + def test_bad_types(self): + # Test that compare_digest raises with mixed types. + a = 'abcde' + b = a.encode('utf-8') + assert isinstance(a, str) + assert isinstance(b, bytes) + self.assertRaises(TypeError, secrets.compare_digest, a, b) + self.assertRaises(TypeError, secrets.compare_digest, b, a) + + def test_bool(self): + # Test that compare_digest returns a bool. + self.assertIsInstance(secrets.compare_digest("abc", "abc"), bool) + self.assertIsInstance(secrets.compare_digest("abc", "xyz"), bool) + + +class Random_Tests(unittest.TestCase): + """Test wrappers around SystemRandom methods.""" + + def test_randbits(self): + # Test randbits. + errmsg = "randbits(%d) returned %d" + for numbits in (3, 12, 30): + for i in range(6): + n = secrets.randbits(numbits) + self.assertTrue(0 <= n < 2**numbits, errmsg % (numbits, n)) + + def test_choice(self): + # Test choice. + items = [1, 2, 4, 8, 16, 32, 64] + for i in range(10): + self.assertTrue(secrets.choice(items) in items) + + def test_randbelow(self): + # Test randbelow. + for i in range(2, 10): + self.assertIn(secrets.randbelow(i), range(i)) + self.assertRaises(ValueError, secrets.randbelow, 0) + + +class Token_Tests(unittest.TestCase): + """Test token functions.""" + + def test_token_defaults(self): + # Test that token_* functions handle default size correctly. + for func in (secrets.token_bytes, secrets.token_hex, + secrets.token_urlsafe): + with self.subTest(func=func): + name = func.__name__ + try: + func() + except TypeError: + self.fail("%s cannot be called with no argument" % name) + try: + func(None) + except TypeError: + self.fail("%s cannot be called with None" % name) + size = secrets.DEFAULT_ENTROPY + self.assertEqual(len(secrets.token_bytes(None)), size) + self.assertEqual(len(secrets.token_hex(None)), 2*size) + + def test_token_bytes(self): + # Test token_bytes. + for n in (1, 8, 17, 100): + with self.subTest(n=n): + self.assertIsInstance(secrets.token_bytes(n), bytes) + self.assertEqual(len(secrets.token_bytes(n)), n) + + def test_token_hex(self): + # Test token_hex. + for n in (1, 12, 25, 90): + with self.subTest(n=n): + s = secrets.token_hex(n) + self.assertIsInstance(s, str) + self.assertEqual(len(s), 2*n) + self.assertTrue(all(c in string.hexdigits for c in s)) + + def test_token_urlsafe(self): + # Test token_urlsafe. + legal = string.ascii_letters + string.digits + '-_' + for n in (1, 11, 28, 76): + with self.subTest(n=n): + s = secrets.token_urlsafe(n) + self.assertIsInstance(s, str) + self.assertTrue(all(c in legal for c in s)) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py index 1a49edf..49abfb3 100644 --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -6,10 +6,11 @@ import operator import copy import pickle from random import randrange, shuffle -import sys import warnings import collections import collections.abc +import itertools +import string class PassThru(Exception): pass @@ -714,6 +715,28 @@ class TestFrozenSet(TestJointOps, unittest.TestCase): addhashvalue(hash(frozenset([e for e, m in elemmasks if m&i]))) self.assertEqual(len(hashvalues), 2**n) + def letter_range(n): + return string.ascii_letters[:n] + + def zf_range(n): + # https://en.wikipedia.org/wiki/Set-theoretic_definition_of_natural_numbers + nums = [frozenset()] + for i in range(n-1): + num = frozenset(nums) + nums.append(num) + return nums[:n] + + def powerset(s): + for i in range(len(s)+1): + yield from map(frozenset, itertools.combinations(s, i)) + + for n in range(18): + t = 2 ** n + mask = t - 1 + for nums in (range, letter_range, zf_range): + u = len({h & mask for h in map(hash, powerset(nums(n)))}) + self.assertGreater(4*u, t) + class FrozenSetSubclass(frozenset): pass diff --git a/Lib/test/test_shlex.py b/Lib/test/test_shlex.py index 55b533d..3936c97 100644 --- a/Lib/test/test_shlex.py +++ b/Lib/test/test_shlex.py @@ -173,16 +173,116 @@ class ShlexTest(unittest.TestCase): "%s: %s != %s" % (self.data[i][0], l, self.data[i][1:])) + def testSyntaxSplitAmpersandAndPipe(self): + """Test handling of syntax splitting of &, |""" + # Could take these forms: &&, &, |&, ;&, ;;& + # of course, the same applies to | and || + # these should all parse to the same output + for delimiter in ('&&', '&', '|&', ';&', ';;&', + '||', '|', '&|', ';|', ';;|'): + src = ['echo hi %s echo bye' % delimiter, + 'echo hi%secho bye' % delimiter] + ref = ['echo', 'hi', delimiter, 'echo', 'bye'] + for ss in src: + s = shlex.shlex(ss, punctuation_chars=True) + result = list(s) + self.assertEqual(ref, result, "While splitting '%s'" % ss) + + def testSyntaxSplitSemicolon(self): + """Test handling of syntax splitting of ;""" + # Could take these forms: ;, ;;, ;&, ;;& + # these should all parse to the same output + for delimiter in (';', ';;', ';&', ';;&'): + src = ['echo hi %s echo bye' % delimiter, + 'echo hi%s echo bye' % delimiter, + 'echo hi%secho bye' % delimiter] + ref = ['echo', 'hi', delimiter, 'echo', 'bye'] + for ss in src: + s = shlex.shlex(ss, punctuation_chars=True) + result = list(s) + self.assertEqual(ref, result, "While splitting '%s'" % ss) + + def testSyntaxSplitRedirect(self): + """Test handling of syntax splitting of >""" + # of course, the same applies to <, | + # these should all parse to the same output + for delimiter in ('<', '|'): + src = ['echo hi %s out' % delimiter, + 'echo hi%s out' % delimiter, + 'echo hi%sout' % delimiter] + ref = ['echo', 'hi', delimiter, 'out'] + for ss in src: + s = shlex.shlex(ss, punctuation_chars=True) + result = list(s) + self.assertEqual(ref, result, "While splitting '%s'" % ss) + + def testSyntaxSplitParen(self): + """Test handling of syntax splitting of ()""" + # these should all parse to the same output + src = ['( echo hi )', + '(echo hi)'] + ref = ['(', 'echo', 'hi', ')'] + for ss in src: + s = shlex.shlex(ss, punctuation_chars=True) + result = list(s) + self.assertEqual(ref, result, "While splitting '%s'" % ss) + + def testSyntaxSplitCustom(self): + """Test handling of syntax splitting with custom chars""" + ref = ['~/a', '&', '&', 'b-c', '--color=auto', '||', 'd', '*.py?'] + ss = "~/a && b-c --color=auto || d *.py?" + s = shlex.shlex(ss, punctuation_chars="|") + result = list(s) + self.assertEqual(ref, result, "While splitting '%s'" % ss) + + def testTokenTypes(self): + """Test that tokens are split with types as expected.""" + for source, expected in ( + ('a && b || c', + [('a', 'a'), ('&&', 'c'), ('b', 'a'), + ('||', 'c'), ('c', 'a')]), + ): + s = shlex.shlex(source, punctuation_chars=True) + observed = [] + while True: + t = s.get_token() + if t == s.eof: + break + if t[0] in s.punctuation_chars: + tt = 'c' + else: + tt = 'a' + observed.append((t, tt)) + self.assertEqual(observed, expected) + + def testPunctuationInWordChars(self): + """Test that any punctuation chars are removed from wordchars""" + s = shlex.shlex('a_b__c', punctuation_chars='_') + self.assertNotIn('_', s.wordchars) + self.assertEqual(list(s), ['a', '_', 'b', '__', 'c']) + + def testPunctuationWithWhitespaceSplit(self): + """Test that with whitespace_split, behaviour is as expected""" + s = shlex.shlex('a && b || c', punctuation_chars='&') + # whitespace_split is False, so splitting will be based on + # punctuation_chars + self.assertEqual(list(s), ['a', '&&', 'b', '|', '|', 'c']) + s = shlex.shlex('a && b || c', punctuation_chars='&') + s.whitespace_split = True + # whitespace_split is True, so splitting will be based on + # white space + self.assertEqual(list(s), ['a', '&&', 'b', '||', 'c']) + def testEmptyStringHandling(self): """Test that parsing of empty strings is correctly handled.""" # see Issue #21999 expected = ['', ')', 'abc'] - - s = shlex.shlex("'')abc", posix=True) - slist = list(s) - self.assertEqual(slist, expected) + for punct in (False, True): + s = shlex.shlex("'')abc", posix=True, punctuation_chars=punct) + slist = list(s) + self.assertEqual(slist, expected) expected = ["''", ')', 'abc'] - s = shlex.shlex("'')abc") + s = shlex.shlex("'')abc", punctuation_chars=True) self.assertEqual(list(s), expected) def testQuote(self): diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index 1d5e01a..90a31d7 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -1306,10 +1306,10 @@ class TestShutil(unittest.TestCase): shutil.chown(filename) with self.assertRaises(LookupError): - shutil.chown(filename, user='non-exising username') + shutil.chown(filename, user='non-existing username') with self.assertRaises(LookupError): - shutil.chown(filename, group='non-exising groupname') + shutil.chown(filename, group='non-existing groupname') with self.assertRaises(TypeError): shutil.chown(filename, b'spam') diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py index 1b80ff0..ab42ed7 100644 --- a/Lib/test/test_signal.py +++ b/Lib/test/test_signal.py @@ -22,29 +22,6 @@ except ImportError: _testcapi = None -class HandlerBCalled(Exception): - pass - - -def exit_subprocess(): - """Use os._exit(0) to exit the current subprocess. - - Otherwise, the test catches the SystemExit and continues executing - in parallel with the original test, so you wind up with an - exponential number of tests running concurrently. - """ - os._exit(0) - - -def ignoring_eintr(__func, *args, **kwargs): - try: - return __func(*args, **kwargs) - except OSError as e: - if e.errno != errno.EINTR: - raise - return None - - class GenericTests(unittest.TestCase): @unittest.skipIf(threading is None, "test needs threading module") @@ -63,145 +40,6 @@ class GenericTests(unittest.TestCase): @unittest.skipIf(sys.platform == "win32", "Not valid on Windows") -class InterProcessSignalTests(unittest.TestCase): - MAX_DURATION = 20 # Entire test should last at most 20 sec. - - def setUp(self): - self.using_gc = gc.isenabled() - gc.disable() - - def tearDown(self): - if self.using_gc: - gc.enable() - - def format_frame(self, frame, limit=None): - return ''.join(traceback.format_stack(frame, limit=limit)) - - def handlerA(self, signum, frame): - self.a_called = True - - def handlerB(self, signum, frame): - self.b_called = True - raise HandlerBCalled(signum, self.format_frame(frame)) - - def wait(self, child): - """Wait for child to finish, ignoring EINTR.""" - while True: - try: - child.wait() - return - except OSError as e: - if e.errno != errno.EINTR: - raise - - def run_test(self): - # Install handlers. This function runs in a sub-process, so we - # don't worry about re-setting the default handlers. - signal.signal(signal.SIGHUP, self.handlerA) - signal.signal(signal.SIGUSR1, self.handlerB) - signal.signal(signal.SIGUSR2, signal.SIG_IGN) - signal.signal(signal.SIGALRM, signal.default_int_handler) - - # Variables the signals will modify: - self.a_called = False - self.b_called = False - - # Let the sub-processes know who to send signals to. - pid = os.getpid() - - child = ignoring_eintr(subprocess.Popen, ['kill', '-HUP', str(pid)]) - if child: - self.wait(child) - if not self.a_called: - time.sleep(1) # Give the signal time to be delivered. - self.assertTrue(self.a_called) - self.assertFalse(self.b_called) - self.a_called = False - - # Make sure the signal isn't delivered while the previous - # Popen object is being destroyed, because __del__ swallows - # exceptions. - del child - try: - child = subprocess.Popen(['kill', '-USR1', str(pid)]) - # This wait should be interrupted by the signal's exception. - self.wait(child) - time.sleep(1) # Give the signal time to be delivered. - self.fail('HandlerBCalled exception not raised') - except HandlerBCalled: - self.assertTrue(self.b_called) - self.assertFalse(self.a_called) - - child = ignoring_eintr(subprocess.Popen, ['kill', '-USR2', str(pid)]) - if child: - self.wait(child) # Nothing should happen. - - try: - signal.alarm(1) - # The race condition in pause doesn't matter in this case, - # since alarm is going to raise a KeyboardException, which - # will skip the call. - signal.pause() - # But if another signal arrives before the alarm, pause - # may return early. - time.sleep(1) - except KeyboardInterrupt: - pass - except: - self.fail("Some other exception woke us from pause: %s" % - traceback.format_exc()) - else: - self.fail("pause returned of its own accord, and the signal" - " didn't arrive after another second.") - - # Issue 3864, unknown if this affects earlier versions of freebsd also - @unittest.skipIf(sys.platform=='freebsd6', - 'inter process signals not reliable (do not mix well with threading) ' - 'on freebsd6') - def test_main(self): - # This function spawns a child process to insulate the main - # test-running process from all the signals. It then - # communicates with that child process over a pipe and - # re-raises information about any exceptions the child - # raises. The real work happens in self.run_test(). - os_done_r, os_done_w = os.pipe() - with closing(os.fdopen(os_done_r, 'rb')) as done_r, \ - closing(os.fdopen(os_done_w, 'wb')) as done_w: - child = os.fork() - if child == 0: - # In the child process; run the test and report results - # through the pipe. - try: - done_r.close() - # Have to close done_w again here because - # exit_subprocess() will skip the enclosing with block. - with closing(done_w): - try: - self.run_test() - except: - pickle.dump(traceback.format_exc(), done_w) - else: - pickle.dump(None, done_w) - except: - print('Uh oh, raised from pickle.') - traceback.print_exc() - finally: - exit_subprocess() - - done_w.close() - # Block for up to MAX_DURATION seconds for the test to finish. - r, w, x = select.select([done_r], [], [], self.MAX_DURATION) - if done_r in r: - tb = pickle.load(done_r) - if tb: - self.fail(tb) - else: - os.kill(child, signal.SIGKILL) - self.fail('Test deadlocked after %d seconds.' % - self.MAX_DURATION) - - -@unittest.skipIf(sys.platform == "win32", "Not valid on Windows") class PosixTests(unittest.TestCase): def trivial_signal_handler(self, *args): pass @@ -224,6 +62,15 @@ class PosixTests(unittest.TestCase): signal.signal(signal.SIGHUP, hup) self.assertEqual(signal.getsignal(signal.SIGHUP), hup) + # Issue 3864, unknown if this affects earlier versions of freebsd also + @unittest.skipIf(sys.platform=='freebsd6', + 'inter process signals not reliable (do not mix well with threading) ' + 'on freebsd6') + def test_interprocess_signal(self): + dirname = os.path.dirname(__file__) + script = os.path.join(dirname, 'signalinterproctester.py') + assert_python_ok(script) + @unittest.skipUnless(sys.platform == "win32", "Windows specific") class WindowsSignalTests(unittest.TestCase): diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index da20a3d..f698927 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -75,7 +75,7 @@ class HelperFunctionsTests(unittest.TestCase): def test_init_pathinfo(self): dir_set = site._init_pathinfo() for entry in [site.makepath(path)[1] for path in sys.path - if path and os.path.isdir(path)]: + if path and os.path.exists(path)]: self.assertIn(entry, dir_set, "%s from sys.path not found in set returned " "by _init_pathinfo(): %s" % (entry, dir_set)) @@ -243,13 +243,14 @@ class HelperFunctionsTests(unittest.TestCase): self.assertEqual(len(dirs), 2) wanted = os.path.join('/Library', sysconfig.get_config_var("PYTHONFRAMEWORK"), - sys.version[:3], + '%d.%d' % sys.version_info[:2], 'site-packages') self.assertEqual(dirs[1], wanted) elif os.sep == '/': # OS X non-framwework builds, Linux, FreeBSD, etc self.assertEqual(len(dirs), 1) - wanted = os.path.join('xoxo', 'lib', 'python' + sys.version[:3], + wanted = os.path.join('xoxo', 'lib', + 'python%d.%d' % sys.version_info[:2], 'site-packages') self.assertEqual(dirs[0], wanted) else: diff --git a/Lib/test/test_smtpd.py b/Lib/test/test_smtpd.py index 88dbfdf..3eebe94 100644 --- a/Lib/test/test_smtpd.py +++ b/Lib/test/test_smtpd.py @@ -53,10 +53,6 @@ class SMTPDServerTest(unittest.TestCase): 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, @@ -108,10 +104,9 @@ class DebuggingServerTest(unittest.TestCase): """)) def test_process_message_with_decode_data_false(self): - server = smtpd.DebuggingServer((support.HOST, 0), ('b', 0), - decode_data=False) + server = smtpd.DebuggingServer((support.HOST, 0), ('b', 0)) conn, addr = server.accept() - channel = smtpd.SMTPChannel(server, conn, addr, decode_data=False) + channel = smtpd.SMTPChannel(server, conn, addr) with support.captured_stdout() as s: self.send_data(channel, b'From: test\n\nh\xc3\xa9llo\xff\n') stdout = s.getvalue() @@ -175,13 +170,11 @@ class TestFamilyDetection(unittest.TestCase): @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) + server = smtpd.SMTPServer((support.HOSTv6, 0), (support.HOST, 0)) 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) + server = smtpd.SMTPServer((support.HOST, 0), (support.HOSTv6, 0)) self.assertEqual(server.socket.family, socket.AF_INET) @@ -204,18 +197,18 @@ class TestRcptOptionParsing(unittest.TestCase): channel.handle_read() def test_params_rejected(self): - server = DummyServer((support.HOST, 0), ('b', 0), decode_data=False) + server = DummyServer((support.HOST, 0), ('b', 0)) conn, addr = server.accept() - channel = smtpd.SMTPChannel(server, conn, addr, decode_data=False) + channel = smtpd.SMTPChannel(server, conn, addr) self.write_line(channel, b'EHLO example') self.write_line(channel, b'MAIL from: <foo@example.com> size=20') self.write_line(channel, b'RCPT to: <foo@example.com> foo=bar') self.assertEqual(channel.socket.last, self.error_response) def test_nothing_accepted(self): - server = DummyServer((support.HOST, 0), ('b', 0), decode_data=False) + server = DummyServer((support.HOST, 0), ('b', 0)) conn, addr = server.accept() - channel = smtpd.SMTPChannel(server, conn, addr, decode_data=False) + channel = smtpd.SMTPChannel(server, conn, addr) self.write_line(channel, b'EHLO example') self.write_line(channel, b'MAIL from: <foo@example.com> size=20') self.write_line(channel, b'RCPT to: <foo@example.com>') @@ -257,9 +250,9 @@ class TestMailOptionParsing(unittest.TestCase): self.assertEqual(channel.socket.last, b'250 OK\r\n') def test_with_decode_data_false(self): - server = DummyServer((support.HOST, 0), ('b', 0), decode_data=False) + server = DummyServer((support.HOST, 0), ('b', 0)) conn, addr = server.accept() - channel = smtpd.SMTPChannel(server, conn, addr, decode_data=False) + channel = smtpd.SMTPChannel(server, conn, addr) self.write_line(channel, b'EHLO example') for line in [ b'MAIL from: <foo@example.com> size=20 SMTPUTF8', @@ -765,13 +758,6 @@ 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): @@ -845,12 +831,9 @@ class SMTPDChannelWithDecodeDataFalse(unittest.TestCase): 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) + self.server = DummyServer((support.HOST, 0), ('b', 0)) conn, addr = self.server.accept() - # Set decode_data to False - self.channel = smtpd.SMTPChannel(self.server, conn, addr, - decode_data=False) + self.channel = smtpd.SMTPChannel(self.server, conn, addr) def tearDown(self): asyncore.close_all() @@ -1015,5 +998,16 @@ class SMTPDChannelTestWithEnableSMTPUTF8True(unittest.TestCase): self.write_line(b'test\r\n.') self.assertEqual(self.channel.socket.last[0:3], b'250') + +class MiscTestCase(unittest.TestCase): + def test__all__(self): + blacklist = { + "program", "Devnull", "DEBUGSTREAM", "NEWLINE", "COMMASPACE", + "DATA_SIZE_DEFAULT", "usage", "Options", "parseargs", + + } + support.check__all__(self, smtpd, blacklist=blacklist) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 4b047ee..b3632e9 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -13,7 +13,6 @@ import queue import sys import os import array -import platform import contextlib from weakref import proxy import signal @@ -66,10 +65,22 @@ def _have_socket_rds(): s.close() return True +def _have_socket_alg(): + """Check whether AF_ALG sockets are supported on this host.""" + try: + s = socket.socket(socket.AF_ALG, socket.SOCK_SEQPACKET, 0) + except (AttributeError, OSError): + return False + else: + s.close() + return True + HAVE_SOCKET_CAN = _have_socket_can() HAVE_SOCKET_RDS = _have_socket_rds() +HAVE_SOCKET_ALG = _have_socket_alg() + # Size in bytes of the int type SIZEOF_INT = array.array("i").itemsize @@ -1161,6 +1172,17 @@ class GeneralModuleTests(unittest.TestCase): sock.close() self.assertRaises(OSError, sock.send, b"spam") + def testCloseException(self): + sock = socket.socket() + socket.socket(fileno=sock.fileno()).close() + try: + sock.close() + except OSError as err: + # Winsock apparently raises ENOTSOCK + self.assertIn(err.errno, (errno.EBADF, errno.ENOTSOCK)) + else: + self.fail("close() should raise EBADF/ENOTSOCK") + def testNewAttributes(self): # testing .family, .type and .protocol @@ -1207,6 +1229,22 @@ class GeneralModuleTests(unittest.TestCase): self.assertRaises(ValueError, s.ioctl, -1, None) s.ioctl(socket.SIO_KEEPALIVE_VALS, (1, 100, 100)) + @unittest.skipUnless(os.name == "nt", "Windows specific") + @unittest.skipUnless(hasattr(socket, 'SIO_LOOPBACK_FAST_PATH'), + 'Loopback fast path support required for this test') + def test_sio_loopback_fast_path(self): + s = socket.socket() + self.addCleanup(s.close) + try: + s.ioctl(socket.SIO_LOOPBACK_FAST_PATH, True) + except OSError as exc: + WSAEOPNOTSUPP = 10045 + if exc.winerror == WSAEOPNOTSUPP: + self.skipTest("SIO_LOOPBACK_FAST_PATH is defined but " + "doesn't implemented in this Windows version") + raise + self.assertRaises(TypeError, s.ioctl, socket.SIO_LOOPBACK_FAST_PATH, None) + def testGetaddrinfo(self): try: socket.getaddrinfo('localhost', 80) @@ -2823,6 +2861,7 @@ class SCMRightsTest(SendrecvmsgServerTimeoutBase): nbytes = self.sendmsgToServer([msg]) self.assertEqual(nbytes, len(msg)) + @unittest.skipIf(sys.platform == "darwin", "see issue #24725") def testFDPassEmpty(self): # Try to pass an empty FD array. Can receive either no array # or an empty array. @@ -5299,6 +5338,177 @@ class SendfileUsingSendfileTest(SendfileUsingSendTest): return getattr(sock, "_sendfile_use_sendfile") +@unittest.skipUnless(HAVE_SOCKET_ALG, 'AF_ALG required') +class LinuxKernelCryptoAPI(unittest.TestCase): + # tests for AF_ALG + def create_alg(self, typ, name): + sock = socket.socket(socket.AF_ALG, socket.SOCK_SEQPACKET, 0) + try: + sock.bind((typ, name)) + except FileNotFoundError as e: + # type / algorithm is not available + raise unittest.SkipTest(str(e), typ, name) + return sock + + def test_sha256(self): + expected = bytes.fromhex("ba7816bf8f01cfea414140de5dae2223b00361a396" + "177a9cb410ff61f20015ad") + with self.create_alg('hash', 'sha256') as algo: + op, _ = algo.accept() + with op: + op.sendall(b"abc") + self.assertEqual(op.recv(512), expected) + + op, _ = algo.accept() + with op: + op.send(b'a', socket.MSG_MORE) + op.send(b'b', socket.MSG_MORE) + op.send(b'c', socket.MSG_MORE) + op.send(b'') + self.assertEqual(op.recv(512), expected) + + def test_hmac_sha1(self): + expected = bytes.fromhex("effcdf6ae5eb2fa2d27416d5f184df9c259a7c79") + with self.create_alg('hash', 'hmac(sha1)') as algo: + algo.setsockopt(socket.SOL_ALG, socket.ALG_SET_KEY, b"Jefe") + op, _ = algo.accept() + with op: + op.sendall(b"what do ya want for nothing?") + self.assertEqual(op.recv(512), expected) + + @support.requires_linux_version(3, 19) + def test_aes_cbc(self): + key = bytes.fromhex('06a9214036b8a15b512e03d534120006') + iv = bytes.fromhex('3dafba429d9eb430b422da802c9fac41') + msg = b"Single block msg" + ciphertext = bytes.fromhex('e353779c1079aeb82708942dbe77181a') + msglen = len(msg) + with self.create_alg('skcipher', 'cbc(aes)') as algo: + algo.setsockopt(socket.SOL_ALG, socket.ALG_SET_KEY, key) + op, _ = algo.accept() + with op: + op.sendmsg_afalg(op=socket.ALG_OP_ENCRYPT, iv=iv, + flags=socket.MSG_MORE) + op.sendall(msg) + self.assertEqual(op.recv(msglen), ciphertext) + + op, _ = algo.accept() + with op: + op.sendmsg_afalg([ciphertext], + op=socket.ALG_OP_DECRYPT, iv=iv) + self.assertEqual(op.recv(msglen), msg) + + # long message + multiplier = 1024 + longmsg = [msg] * multiplier + op, _ = algo.accept() + with op: + op.sendmsg_afalg(longmsg, + op=socket.ALG_OP_ENCRYPT, iv=iv) + enc = op.recv(msglen * multiplier) + self.assertEqual(len(enc), msglen * multiplier) + self.assertTrue(enc[:msglen], ciphertext) + + op, _ = algo.accept() + with op: + op.sendmsg_afalg([enc], + op=socket.ALG_OP_DECRYPT, iv=iv) + dec = op.recv(msglen * multiplier) + self.assertEqual(len(dec), msglen * multiplier) + self.assertEqual(dec, msg * multiplier) + + @support.requires_linux_version(3, 19) + def test_aead_aes_gcm(self): + key = bytes.fromhex('c939cc13397c1d37de6ae0e1cb7c423c') + iv = bytes.fromhex('b3d8cc017cbb89b39e0f67e2') + plain = bytes.fromhex('c3b3c41f113a31b73d9a5cd432103069') + assoc = bytes.fromhex('24825602bd12a984e0092d3e448eda5f') + expected_ct = bytes.fromhex('93fe7d9e9bfd10348a5606e5cafa7354') + expected_tag = bytes.fromhex('0032a1dc85f1c9786925a2e71d8272dd') + + taglen = len(expected_tag) + assoclen = len(assoc) + + with self.create_alg('aead', 'gcm(aes)') as algo: + algo.setsockopt(socket.SOL_ALG, socket.ALG_SET_KEY, key) + algo.setsockopt(socket.SOL_ALG, socket.ALG_SET_AEAD_AUTHSIZE, + None, taglen) + + # send assoc, plain and tag buffer in separate steps + op, _ = algo.accept() + with op: + op.sendmsg_afalg(op=socket.ALG_OP_ENCRYPT, iv=iv, + assoclen=assoclen, flags=socket.MSG_MORE) + op.sendall(assoc, socket.MSG_MORE) + op.sendall(plain, socket.MSG_MORE) + op.sendall(b'\x00' * taglen) + res = op.recv(assoclen + len(plain) + taglen) + self.assertEqual(expected_ct, res[assoclen:-taglen]) + self.assertEqual(expected_tag, res[-taglen:]) + + # now with msg + op, _ = algo.accept() + with op: + msg = assoc + plain + b'\x00' * taglen + op.sendmsg_afalg([msg], op=socket.ALG_OP_ENCRYPT, iv=iv, + assoclen=assoclen) + res = op.recv(assoclen + len(plain) + taglen) + self.assertEqual(expected_ct, res[assoclen:-taglen]) + self.assertEqual(expected_tag, res[-taglen:]) + + # create anc data manually + pack_uint32 = struct.Struct('I').pack + op, _ = algo.accept() + with op: + msg = assoc + plain + b'\x00' * taglen + op.sendmsg( + [msg], + ([socket.SOL_ALG, socket.ALG_SET_OP, pack_uint32(socket.ALG_OP_ENCRYPT)], + [socket.SOL_ALG, socket.ALG_SET_IV, pack_uint32(len(iv)) + iv], + [socket.SOL_ALG, socket.ALG_SET_AEAD_ASSOCLEN, pack_uint32(assoclen)], + ) + ) + res = op.recv(len(msg)) + self.assertEqual(expected_ct, res[assoclen:-taglen]) + self.assertEqual(expected_tag, res[-taglen:]) + + # decrypt and verify + op, _ = algo.accept() + with op: + msg = assoc + expected_ct + expected_tag + op.sendmsg_afalg([msg], op=socket.ALG_OP_DECRYPT, iv=iv, + assoclen=assoclen) + res = op.recv(len(msg)) + self.assertEqual(plain, res[assoclen:-taglen]) + + @support.requires_linux_version(3, 19) + def test_drbg_pr_sha256(self): + # deterministic random bit generator, prediction resistance, sha256 + with self.create_alg('rng', 'drbg_pr_sha256') as algo: + extra_seed = os.urandom(32) + algo.setsockopt(socket.SOL_ALG, socket.ALG_SET_KEY, extra_seed) + op, _ = algo.accept() + with op: + rn = op.recv(32) + self.assertEqual(len(rn), 32) + + def test_sendmsg_afalg_args(self): + sock = socket.socket(socket.AF_ALG, socket.SOCK_SEQPACKET, 0) + with self.assertRaises(TypeError): + sock.sendmsg_afalg() + + with self.assertRaises(TypeError): + sock.sendmsg_afalg(op=None) + + with self.assertRaises(TypeError): + sock.sendmsg_afalg(1) + + with self.assertRaises(TypeError): + sock.sendmsg_afalg(op=socket.ALG_OP_ENCRYPT, assoclen=None) + + with self.assertRaises(TypeError): + sock.sendmsg_afalg(op=socket.ALG_OP_ENCRYPT, assoclen=-1) + def test_main(): tests = [GeneralModuleTests, BasicTCPTest, TCPCloserTest, TCPTimeoutTest, TestExceptions, BufferIOTest, BasicTCPTest2, BasicUDPTest, UDPTimeoutTest ] @@ -5325,6 +5535,7 @@ def test_main(): tests.extend([TIPCTest, TIPCThreadableTest]) tests.extend([BasicCANTest, CANTest]) tests.extend([BasicRDSTest, RDSTest]) + tests.append(LinuxKernelCryptoAPI) tests.extend([ CmsgMacroTests, SendmsgUDPTest, diff --git a/Lib/test/test_socketserver.py b/Lib/test/test_socketserver.py index 0d0f86f..3f4dfa1 100644 --- a/Lib/test/test_socketserver.py +++ b/Lib/test/test_socketserver.py @@ -3,12 +3,11 @@ Test suite for socketserver. """ import contextlib +import io import os import select import signal import socket -import select -import errno import tempfile import unittest import socketserver @@ -46,7 +45,7 @@ def receive(sock, n, timeout=20): else: raise RuntimeError("timed out on %r" % (sock,)) -if HAVE_UNIX_SOCKETS: +if HAVE_UNIX_SOCKETS and HAVE_FORKING: class ForkingUnixStreamServer(socketserver.ForkingMixIn, socketserver.UnixStreamServer): pass @@ -58,6 +57,7 @@ if HAVE_UNIX_SOCKETS: @contextlib.contextmanager def simple_subprocess(testcase): + """Tests that a custom child process is not waited on (Issue 1540386)""" pid = os.fork() if pid == 0: # Don't raise an exception; it would be caught by the test harness. @@ -103,7 +103,6 @@ class SocketServerTest(unittest.TestCase): class MyServer(svrcls): def handle_error(self, request, client_address): self.close_request(request) - self.server_close() raise class MyHandler(hdlrbase): @@ -279,6 +278,182 @@ class SocketServerTest(unittest.TestCase): socketserver.TCPServer((HOST, -1), socketserver.StreamRequestHandler) + def test_context_manager(self): + with socketserver.TCPServer((HOST, 0), + socketserver.StreamRequestHandler) as server: + pass + self.assertEqual(-1, server.socket.fileno()) + + +class ErrorHandlerTest(unittest.TestCase): + """Test that the servers pass normal exceptions from the handler to + handle_error(), and that exiting exceptions like SystemExit and + KeyboardInterrupt are not passed.""" + + def tearDown(self): + test.support.unlink(test.support.TESTFN) + + def test_sync_handled(self): + BaseErrorTestServer(ValueError) + self.check_result(handled=True) + + def test_sync_not_handled(self): + with self.assertRaises(SystemExit): + BaseErrorTestServer(SystemExit) + self.check_result(handled=False) + + @unittest.skipUnless(threading, 'Threading required for this test.') + def test_threading_handled(self): + ThreadingErrorTestServer(ValueError) + self.check_result(handled=True) + + @unittest.skipUnless(threading, 'Threading required for this test.') + def test_threading_not_handled(self): + ThreadingErrorTestServer(SystemExit) + self.check_result(handled=False) + + @requires_forking + def test_forking_handled(self): + ForkingErrorTestServer(ValueError) + self.check_result(handled=True) + + @requires_forking + def test_forking_not_handled(self): + ForkingErrorTestServer(SystemExit) + self.check_result(handled=False) + + def check_result(self, handled): + with open(test.support.TESTFN) as log: + expected = 'Handler called\n' + 'Error handled\n' * handled + self.assertEqual(log.read(), expected) + + +class BaseErrorTestServer(socketserver.TCPServer): + def __init__(self, exception): + self.exception = exception + super().__init__((HOST, 0), BadHandler) + with socket.create_connection(self.server_address): + pass + try: + self.handle_request() + finally: + self.server_close() + self.wait_done() + + def handle_error(self, request, client_address): + with open(test.support.TESTFN, 'a') as log: + log.write('Error handled\n') + + def wait_done(self): + pass + + +class BadHandler(socketserver.BaseRequestHandler): + def handle(self): + with open(test.support.TESTFN, 'a') as log: + log.write('Handler called\n') + raise self.server.exception('Test error') + + +class ThreadingErrorTestServer(socketserver.ThreadingMixIn, + BaseErrorTestServer): + def __init__(self, *pos, **kw): + self.done = threading.Event() + super().__init__(*pos, **kw) + + def shutdown_request(self, *pos, **kw): + super().shutdown_request(*pos, **kw) + self.done.set() + + def wait_done(self): + self.done.wait() + + +if HAVE_FORKING: + class ForkingErrorTestServer(socketserver.ForkingMixIn, BaseErrorTestServer): + def wait_done(self): + [child] = self.active_children + os.waitpid(child, 0) + self.active_children.clear() + + +class SocketWriterTest(unittest.TestCase): + def test_basics(self): + class Handler(socketserver.StreamRequestHandler): + def handle(self): + self.server.wfile = self.wfile + self.server.wfile_fileno = self.wfile.fileno() + self.server.request_fileno = self.request.fileno() + + server = socketserver.TCPServer((HOST, 0), Handler) + self.addCleanup(server.server_close) + s = socket.socket( + server.address_family, socket.SOCK_STREAM, socket.IPPROTO_TCP) + with s: + s.connect(server.server_address) + server.handle_request() + self.assertIsInstance(server.wfile, io.BufferedIOBase) + self.assertEqual(server.wfile_fileno, server.request_fileno) + + @unittest.skipUnless(threading, 'Threading required for this test.') + def test_write(self): + # Test that wfile.write() sends data immediately, and that it does + # not truncate sends when interrupted by a Unix signal + pthread_kill = test.support.get_attribute(signal, 'pthread_kill') + + class Handler(socketserver.StreamRequestHandler): + def handle(self): + self.server.sent1 = self.wfile.write(b'write data\n') + # Should be sent immediately, without requiring flush() + self.server.received = self.rfile.readline() + big_chunk = bytes(test.support.SOCK_MAX_SIZE) + self.server.sent2 = self.wfile.write(big_chunk) + + server = socketserver.TCPServer((HOST, 0), Handler) + self.addCleanup(server.server_close) + interrupted = threading.Event() + + def signal_handler(signum, frame): + interrupted.set() + + original = signal.signal(signal.SIGUSR1, signal_handler) + self.addCleanup(signal.signal, signal.SIGUSR1, original) + response1 = None + received2 = None + main_thread = threading.get_ident() + + def run_client(): + s = socket.socket(server.address_family, socket.SOCK_STREAM, + socket.IPPROTO_TCP) + with s, s.makefile('rb') as reader: + s.connect(server.server_address) + nonlocal response1 + response1 = reader.readline() + s.sendall(b'client response\n') + + reader.read(100) + # The main thread should now be blocking in a send() syscall. + # But in theory, it could get interrupted by other signals, + # and then retried. So keep sending the signal in a loop, in + # case an earlier signal happens to be delivered at an + # inconvenient moment. + while True: + pthread_kill(main_thread, signal.SIGUSR1) + if interrupted.wait(timeout=float(1)): + break + nonlocal received2 + received2 = len(reader.read()) + + background = threading.Thread(target=run_client) + background.start() + server.handle_request() + background.join() + self.assertEqual(server.sent1, len(response1)) + self.assertEqual(response1, b'write data\n') + self.assertEqual(server.received, b'client response\n') + self.assertEqual(server.sent2, test.support.SOCK_MAX_SIZE) + self.assertEqual(received2, test.support.SOCK_MAX_SIZE - 100) + class MiscTestCase(unittest.TestCase): diff --git a/Lib/test/test_sort.py b/Lib/test/test_sort.py index a5d0ebf..98ccab5 100644 --- a/Lib/test/test_sort.py +++ b/Lib/test/test_sort.py @@ -1,6 +1,5 @@ from test import support import random -import sys import unittest from functools import cmp_to_key diff --git a/Lib/test/test_spwd.py b/Lib/test/test_spwd.py index bea7ab1..e893f3a 100644 --- a/Lib/test/test_spwd.py +++ b/Lib/test/test_spwd.py @@ -56,5 +56,20 @@ class TestSpwdRoot(unittest.TestCase): self.assertRaises(TypeError, spwd.getspnam, bytes_name) +@unittest.skipUnless(hasattr(os, 'geteuid') and os.geteuid() != 0, + 'non-root user required') +class TestSpwdNonRoot(unittest.TestCase): + + def test_getspnam_exception(self): + name = 'bin' + try: + with self.assertRaises(PermissionError) as cm: + spwd.getspnam(name) + except KeyError as exc: + self.skipTest("spwd entry %r doesn't exist: %s" % (name, exc)) + else: + self.assertEqual(str(cm.exception), '[Errno 13] Permission denied') + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 0f4faa0..d8d53af 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -21,6 +21,13 @@ import functools ssl = support.import_module("ssl") +try: + import threading +except ImportError: + _have_threads = False +else: + _have_threads = True + PROTOCOLS = sorted(ssl._PROTOCOL_NAMES) HOST = support.HOST IS_LIBRESSL = ssl.OPENSSL_VERSION.startswith('LibreSSL') @@ -56,12 +63,12 @@ CRLFILE = data_file("revocation.crl") # Two keys and certs signed by the same CA (for SNI tests) SIGNED_CERTFILE = data_file("keycert3.pem") SIGNED_CERTFILE2 = data_file("keycert4.pem") -SIGNING_CA = data_file("pycacert.pem") +# Same certificate as pycacert.pem, but without extra text in file +SIGNING_CA = data_file("capath", "ceff1710.0") # cert with all kinds of subject alt names ALLSANFILE = data_file("allsans.pem") REMOTE_HOST = "self-signed.pythontest.net" -REMOTE_ROOT_CERT = data_file("selfsigned_pythontestdotnet.pem") EMPTYCERT = data_file("nullcert.pem") BADCERT = data_file("badcert.pem") @@ -354,7 +361,7 @@ class BasicSocketTests(unittest.TestCase): wr = weakref.ref(ss) with support.check_warnings(("", ResourceWarning)): del ss - self.assertEqual(wr(), None) + self.assertEqual(wr(), None) def test_wrapped_unconnected(self): # Methods on an unconnected SSLSocket propagate the original @@ -809,6 +816,22 @@ class BasicSocketTests(unittest.TestCase): 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") + def test_connect_ex_error(self): + server = socket.socket(socket.AF_INET) + self.addCleanup(server.close) + port = support.bind_port(server) # Reserve port but don't listen + s = ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_REQUIRED) + self.addCleanup(s.close) + rc = s.connect_ex((HOST, port)) + # Issue #19919: Windows machines or VMs hosted on Windows + # machines sometimes return EWOULDBLOCK. + errors = ( + errno.ECONNREFUSED, errno.EHOSTUNREACH, errno.ETIMEDOUT, + errno.EWOULDBLOCK, + ) + self.assertIn(rc, errors) + class ContextTests(unittest.TestCase): @@ -834,6 +857,14 @@ class ContextTests(unittest.TestCase): with self.assertRaisesRegex(ssl.SSLError, "No cipher can be selected"): ctx.set_ciphers("^$:,;?*'dorothyx") + @unittest.skipIf(ssl.OPENSSL_VERSION_INFO < (1, 0, 2, 0, 0), 'OpenSSL too old') + def test_get_ciphers(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + ctx.set_ciphers('AESGCM') + names = set(d['name'] for d in ctx.get_ciphers()) + self.assertIn('AES256-GCM-SHA384', names) + self.assertIn('AES128-GCM-SHA256', names) + @skip_if_broken_ubuntu_ssl def test_options(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) @@ -1397,140 +1428,103 @@ class MemoryBIOTests(unittest.TestCase): self.assertRaises(TypeError, bio.write, 1) -class NetworkedTests(unittest.TestCase): +@unittest.skipUnless(_have_threads, "Needs threading module") +class SimpleBackgroundTests(unittest.TestCase): - def test_connect(self): - with support.transient_internet(REMOTE_HOST): - s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_NONE) - try: - s.connect((REMOTE_HOST, 443)) - self.assertEqual({}, s.getpeercert()) - finally: - s.close() + """Tests that connect to a simple server running in the background""" - # this should fail because we have no verification certs - s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_REQUIRED) - self.assertRaisesRegex(ssl.SSLError, "certificate verify failed", - s.connect, (REMOTE_HOST, 443)) - s.close() + def setUp(self): + server = ThreadedEchoServer(SIGNED_CERTFILE) + self.server_addr = (HOST, server.port) + server.__enter__() + self.addCleanup(server.__exit__, None, None, None) - # this should succeed because we specify the root cert - s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_REQUIRED, - ca_certs=REMOTE_ROOT_CERT) - try: - s.connect((REMOTE_HOST, 443)) - self.assertTrue(s.getpeercert()) - finally: - s.close() + def test_connect(self): + with ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_NONE) as s: + s.connect(self.server_addr) + self.assertEqual({}, s.getpeercert()) + + # this should succeed because we specify the root cert + with ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_REQUIRED, + ca_certs=SIGNING_CA) as s: + s.connect(self.server_addr) + self.assertTrue(s.getpeercert()) + + def test_connect_fail(self): + # This should fail because we have no verification certs. Connection + # failure crashes ThreadedEchoServer, so run this in an independent + # test method. + s = ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_REQUIRED) + self.addCleanup(s.close) + self.assertRaisesRegex(ssl.SSLError, "certificate verify failed", + s.connect, self.server_addr) def test_connect_ex(self): # Issue #11326: check connect_ex() implementation - with support.transient_internet(REMOTE_HOST): - s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_REQUIRED, - ca_certs=REMOTE_ROOT_CERT) - try: - self.assertEqual(0, s.connect_ex((REMOTE_HOST, 443))) - self.assertTrue(s.getpeercert()) - finally: - s.close() + s = ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_REQUIRED, + ca_certs=SIGNING_CA) + self.addCleanup(s.close) + self.assertEqual(0, s.connect_ex(self.server_addr)) + self.assertTrue(s.getpeercert()) def test_non_blocking_connect_ex(self): # Issue #11326: non-blocking connect_ex() should allow handshake # to proceed after the socket gets ready. - with support.transient_internet(REMOTE_HOST): - s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_REQUIRED, - ca_certs=REMOTE_ROOT_CERT, - do_handshake_on_connect=False) + s = ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_REQUIRED, + ca_certs=SIGNING_CA, + do_handshake_on_connect=False) + self.addCleanup(s.close) + s.setblocking(False) + rc = s.connect_ex(self.server_addr) + # EWOULDBLOCK under Windows, EINPROGRESS elsewhere + self.assertIn(rc, (0, errno.EINPROGRESS, errno.EWOULDBLOCK)) + # Wait for connect to finish + select.select([], [s], [], 5.0) + # Non-blocking handshake + while True: try: - s.setblocking(False) - rc = s.connect_ex((REMOTE_HOST, 443)) - # EWOULDBLOCK under Windows, EINPROGRESS elsewhere - self.assertIn(rc, (0, errno.EINPROGRESS, errno.EWOULDBLOCK)) - # Wait for connect to finish + s.do_handshake() + break + except ssl.SSLWantReadError: + select.select([s], [], [], 5.0) + except ssl.SSLWantWriteError: select.select([], [s], [], 5.0) - # Non-blocking handshake - while True: - try: - s.do_handshake() - break - except ssl.SSLWantReadError: - select.select([s], [], [], 5.0) - except ssl.SSLWantWriteError: - select.select([], [s], [], 5.0) - # SSL established - self.assertTrue(s.getpeercert()) - finally: - s.close() - - def test_timeout_connect_ex(self): - # Issue #12065: on a timeout, connect_ex() should return the original - # errno (mimicking the behaviour of non-SSL sockets). - with support.transient_internet(REMOTE_HOST): - s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_REQUIRED, - ca_certs=REMOTE_ROOT_CERT, - do_handshake_on_connect=False) - try: - s.settimeout(0.0000001) - rc = s.connect_ex((REMOTE_HOST, 443)) - if rc == 0: - self.skipTest("REMOTE_HOST responded too quickly") - self.assertIn(rc, (errno.EAGAIN, errno.EWOULDBLOCK)) - finally: - s.close() - - def test_connect_ex_error(self): - with support.transient_internet(REMOTE_HOST): - s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_REQUIRED, - ca_certs=REMOTE_ROOT_CERT) - try: - rc = s.connect_ex((REMOTE_HOST, 444)) - # Issue #19919: Windows machines or VMs hosted on Windows - # machines sometimes return EWOULDBLOCK. - errors = ( - errno.ECONNREFUSED, errno.EHOSTUNREACH, errno.ETIMEDOUT, - errno.EWOULDBLOCK, - ) - self.assertIn(rc, errors) - finally: - s.close() + # SSL established + self.assertTrue(s.getpeercert()) def test_connect_with_context(self): - with support.transient_internet(REMOTE_HOST): - # Same as test_connect, but with a separately created context - ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - s = ctx.wrap_socket(socket.socket(socket.AF_INET)) - s.connect((REMOTE_HOST, 443)) - try: - self.assertEqual({}, s.getpeercert()) - finally: - s.close() - # Same with a server hostname - s = ctx.wrap_socket(socket.socket(socket.AF_INET), - server_hostname=REMOTE_HOST) - s.connect((REMOTE_HOST, 443)) - s.close() - # This should fail because we have no verification certs - ctx.verify_mode = ssl.CERT_REQUIRED - s = ctx.wrap_socket(socket.socket(socket.AF_INET)) - self.assertRaisesRegex(ssl.SSLError, "certificate verify failed", - s.connect, (REMOTE_HOST, 443)) - s.close() - # This should succeed because we specify the root cert - ctx.load_verify_locations(REMOTE_ROOT_CERT) - s = ctx.wrap_socket(socket.socket(socket.AF_INET)) - s.connect((REMOTE_HOST, 443)) - try: - cert = s.getpeercert() - self.assertTrue(cert) - finally: - s.close() + # Same as test_connect, but with a separately created context + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + with ctx.wrap_socket(socket.socket(socket.AF_INET)) as s: + s.connect(self.server_addr) + self.assertEqual({}, s.getpeercert()) + # Same with a server hostname + with ctx.wrap_socket(socket.socket(socket.AF_INET), + server_hostname="dummy") as s: + s.connect(self.server_addr) + ctx.verify_mode = ssl.CERT_REQUIRED + # This should succeed because we specify the root cert + ctx.load_verify_locations(SIGNING_CA) + with ctx.wrap_socket(socket.socket(socket.AF_INET)) as s: + s.connect(self.server_addr) + cert = s.getpeercert() + self.assertTrue(cert) + + def test_connect_with_context_fail(self): + # This should fail because we have no verification certs. Connection + # failure crashes ThreadedEchoServer, so run this in an independent + # test method. + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.verify_mode = ssl.CERT_REQUIRED + s = ctx.wrap_socket(socket.socket(socket.AF_INET)) + self.addCleanup(s.close) + self.assertRaisesRegex(ssl.SSLError, "certificate verify failed", + s.connect, self.server_addr) def test_connect_capath(self): # Verify server certificates using the `capath` argument @@ -1538,198 +1532,130 @@ class NetworkedTests(unittest.TestCase): # OpenSSL 0.9.8n and 1.0.0, as a result the capath directory must # contain both versions of each certificate (same content, different # filename) for this test to be portable across OpenSSL releases. - with support.transient_internet(REMOTE_HOST): - ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - ctx.verify_mode = ssl.CERT_REQUIRED - ctx.load_verify_locations(capath=CAPATH) - s = ctx.wrap_socket(socket.socket(socket.AF_INET)) - s.connect((REMOTE_HOST, 443)) - try: - cert = s.getpeercert() - self.assertTrue(cert) - finally: - s.close() - # Same with a bytes `capath` argument - ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - ctx.verify_mode = ssl.CERT_REQUIRED - ctx.load_verify_locations(capath=BYTES_CAPATH) - s = ctx.wrap_socket(socket.socket(socket.AF_INET)) - s.connect((REMOTE_HOST, 443)) - try: - cert = s.getpeercert() - self.assertTrue(cert) - finally: - s.close() + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.verify_mode = ssl.CERT_REQUIRED + ctx.load_verify_locations(capath=CAPATH) + with ctx.wrap_socket(socket.socket(socket.AF_INET)) as s: + s.connect(self.server_addr) + cert = s.getpeercert() + self.assertTrue(cert) + # Same with a bytes `capath` argument + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.verify_mode = ssl.CERT_REQUIRED + ctx.load_verify_locations(capath=BYTES_CAPATH) + with ctx.wrap_socket(socket.socket(socket.AF_INET)) as s: + s.connect(self.server_addr) + cert = s.getpeercert() + self.assertTrue(cert) def test_connect_cadata(self): - with open(REMOTE_ROOT_CERT) as f: + with open(SIGNING_CA) as f: pem = f.read() der = ssl.PEM_cert_to_DER_cert(pem) - with support.transient_internet(REMOTE_HOST): - ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - ctx.verify_mode = ssl.CERT_REQUIRED - ctx.load_verify_locations(cadata=pem) - with ctx.wrap_socket(socket.socket(socket.AF_INET)) as s: - s.connect((REMOTE_HOST, 443)) - cert = s.getpeercert() - self.assertTrue(cert) + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.verify_mode = ssl.CERT_REQUIRED + ctx.load_verify_locations(cadata=pem) + with ctx.wrap_socket(socket.socket(socket.AF_INET)) as s: + s.connect(self.server_addr) + cert = s.getpeercert() + self.assertTrue(cert) - # same with DER - ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - ctx.verify_mode = ssl.CERT_REQUIRED - ctx.load_verify_locations(cadata=der) - with ctx.wrap_socket(socket.socket(socket.AF_INET)) as s: - s.connect((REMOTE_HOST, 443)) - cert = s.getpeercert() - self.assertTrue(cert) + # same with DER + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.verify_mode = ssl.CERT_REQUIRED + ctx.load_verify_locations(cadata=der) + with ctx.wrap_socket(socket.socket(socket.AF_INET)) as s: + s.connect(self.server_addr) + cert = s.getpeercert() + self.assertTrue(cert) @unittest.skipIf(os.name == "nt", "Can't use a socket as a file under Windows") def test_makefile_close(self): # Issue #5238: creating a file-like object with makefile() shouldn't # delay closing the underlying "real socket" (here tested with its # file descriptor, hence skipping the test under Windows). - with support.transient_internet(REMOTE_HOST): - ss = ssl.wrap_socket(socket.socket(socket.AF_INET)) - ss.connect((REMOTE_HOST, 443)) - fd = ss.fileno() - f = ss.makefile() - f.close() - # The fd is still open + ss = ssl.wrap_socket(socket.socket(socket.AF_INET)) + ss.connect(self.server_addr) + fd = ss.fileno() + f = ss.makefile() + f.close() + # The fd is still open + os.read(fd, 0) + # Closing the SSL socket should close the fd too + ss.close() + gc.collect() + with self.assertRaises(OSError) as e: os.read(fd, 0) - # Closing the SSL socket should close the fd too - ss.close() - gc.collect() - with self.assertRaises(OSError) as e: - os.read(fd, 0) - self.assertEqual(e.exception.errno, errno.EBADF) + self.assertEqual(e.exception.errno, errno.EBADF) def test_non_blocking_handshake(self): - with support.transient_internet(REMOTE_HOST): - s = socket.socket(socket.AF_INET) - s.connect((REMOTE_HOST, 443)) - s.setblocking(False) - s = ssl.wrap_socket(s, - cert_reqs=ssl.CERT_NONE, - do_handshake_on_connect=False) - count = 0 - while True: - try: - count += 1 - s.do_handshake() - break - except ssl.SSLWantReadError: - select.select([s], [], []) - except ssl.SSLWantWriteError: - select.select([], [s], []) - s.close() - if support.verbose: - sys.stdout.write("\nNeeded %d calls to do_handshake() to establish session.\n" % count) + s = socket.socket(socket.AF_INET) + s.connect(self.server_addr) + s.setblocking(False) + s = ssl.wrap_socket(s, + cert_reqs=ssl.CERT_NONE, + do_handshake_on_connect=False) + self.addCleanup(s.close) + count = 0 + while True: + try: + count += 1 + s.do_handshake() + break + except ssl.SSLWantReadError: + select.select([s], [], []) + except ssl.SSLWantWriteError: + select.select([], [s], []) + if support.verbose: + sys.stdout.write("\nNeeded %d calls to do_handshake() to establish session.\n" % count) 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)) - if not pem: - self.fail("No server certificate on %s:%s!" % (host, port)) - - try: - pem = ssl.get_server_certificate((host, port), - ca_certs=CERTFILE) - except ssl.SSLError as x: - #should fail - if support.verbose: - sys.stdout.write("%s\n" % x) - else: - self.fail("Got server certificate %s for %s:%s!" % (pem, host, port)) - - pem = ssl.get_server_certificate((host, port), - ca_certs=cert) - if not pem: - self.fail("No server certificate on %s:%s!" % (host, port)) - if support.verbose: - sys.stdout.write("\nVerified certificate for %s:%s is\n%s\n" % (host, port ,pem)) + _test_get_server_certificate(self, *self.server_addr, cert=SIGNING_CA) - _test_get_server_certificate(REMOTE_HOST, 443, REMOTE_ROOT_CERT) - if support.IPV6_ENABLED: - _test_get_server_certificate('ipv6.google.com', 443) + def test_get_server_certificate_fail(self): + # Connection failure crashes ThreadedEchoServer, so run this in an + # independent test method + _test_get_server_certificate_fail(self, *self.server_addr) def test_ciphers(self): - remote = (REMOTE_HOST, 443) - with support.transient_internet(remote[0]): - with ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_NONE, ciphers="ALL") as s: - s.connect(remote) - with ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_NONE, ciphers="DEFAULT") as s: - s.connect(remote) - # Error checking can happen at instantiation or when connecting - with self.assertRaisesRegex(ssl.SSLError, "No cipher can be selected"): - with socket.socket(socket.AF_INET) as sock: - s = ssl.wrap_socket(sock, - cert_reqs=ssl.CERT_NONE, ciphers="^$:,;?*'dorothyx") - s.connect(remote) - - def test_algorithms(self): - # Issue #8484: all algorithms should be available when verifying a - # certificate. - # SHA256 was added in OpenSSL 0.9.8 - if ssl.OPENSSL_VERSION_INFO < (0, 9, 8, 0, 15): - self.skipTest("SHA256 not available on %r" % ssl.OPENSSL_VERSION) - # sha256.tbs-internet.com needs SNI to use the correct certificate - if not ssl.HAS_SNI: - self.skipTest("SNI needed for this test") - # https://sha2.hboeck.de/ was used until 2011-01-08 (no route to host) - remote = ("sha256.tbs-internet.com", 443) - sha256_cert = os.path.join(os.path.dirname(__file__), "sha256.pem") - with support.transient_internet("sha256.tbs-internet.com"): - ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) - ctx.verify_mode = ssl.CERT_REQUIRED - ctx.load_verify_locations(sha256_cert) - s = ctx.wrap_socket(socket.socket(socket.AF_INET), - server_hostname="sha256.tbs-internet.com") - try: - s.connect(remote) - if support.verbose: - sys.stdout.write("\nCipher with %r is %r\n" % - (remote, s.cipher())) - sys.stdout.write("Certificate is:\n%s\n" % - pprint.pformat(s.getpeercert())) - finally: - s.close() + with ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_NONE, ciphers="ALL") as s: + s.connect(self.server_addr) + with ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_NONE, ciphers="DEFAULT") as s: + s.connect(self.server_addr) + # Error checking can happen at instantiation or when connecting + with self.assertRaisesRegex(ssl.SSLError, "No cipher can be selected"): + with socket.socket(socket.AF_INET) as sock: + s = ssl.wrap_socket(sock, + cert_reqs=ssl.CERT_NONE, ciphers="^$:,;?*'dorothyx") + s.connect(self.server_addr) def test_get_ca_certs_capath(self): # capath certs are loaded on request - with support.transient_internet(REMOTE_HOST): - ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - ctx.verify_mode = ssl.CERT_REQUIRED - ctx.load_verify_locations(capath=CAPATH) - self.assertEqual(ctx.get_ca_certs(), []) - s = ctx.wrap_socket(socket.socket(socket.AF_INET)) - s.connect((REMOTE_HOST, 443)) - try: - cert = s.getpeercert() - self.assertTrue(cert) - finally: - s.close() - self.assertEqual(len(ctx.get_ca_certs()), 1) + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.verify_mode = ssl.CERT_REQUIRED + ctx.load_verify_locations(capath=CAPATH) + self.assertEqual(ctx.get_ca_certs(), []) + with ctx.wrap_socket(socket.socket(socket.AF_INET)) as s: + s.connect(self.server_addr) + cert = s.getpeercert() + self.assertTrue(cert) + self.assertEqual(len(ctx.get_ca_certs()), 1) @needs_sni def test_context_setget(self): # Check that the context of a connected socket can be replaced. - with support.transient_internet(REMOTE_HOST): - ctx1 = ssl.SSLContext(ssl.PROTOCOL_TLSv1) - ctx2 = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - s = socket.socket(socket.AF_INET) - with ctx1.wrap_socket(s) as ss: - ss.connect((REMOTE_HOST, 443)) - self.assertIs(ss.context, ctx1) - self.assertIs(ss._sslobj.context, ctx1) - ss.context = ctx2 - self.assertIs(ss.context, ctx2) - self.assertIs(ss._sslobj.context, ctx2) - - -class NetworkedBIOTests(unittest.TestCase): + ctx1 = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + ctx2 = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + s = socket.socket(socket.AF_INET) + with ctx1.wrap_socket(s) as ss: + ss.connect(self.server_addr) + self.assertIs(ss.context, ctx1) + self.assertIs(ss._sslobj.context, ctx1) + ss.context = ctx2 + self.assertIs(ss.context, ctx2) + self.assertIs(ss._sslobj.context, ctx2) def ssl_io_loop(self, sock, incoming, outgoing, func, *args, **kwargs): # A simple IO loop. Call func(*args) depending on the error we get @@ -1765,64 +1691,128 @@ class NetworkedBIOTests(unittest.TestCase): % (count, func.__name__)) return ret - def test_handshake(self): + def test_bio_handshake(self): + sock = socket.socket(socket.AF_INET) + self.addCleanup(sock.close) + sock.connect(self.server_addr) + incoming = ssl.MemoryBIO() + outgoing = ssl.MemoryBIO() + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.verify_mode = ssl.CERT_REQUIRED + ctx.load_verify_locations(SIGNING_CA) + ctx.check_hostname = True + sslobj = ctx.wrap_bio(incoming, outgoing, False, 'localhost') + self.assertIs(sslobj._sslobj.owner, sslobj) + self.assertIsNone(sslobj.cipher()) + self.assertIsNotNone(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.assertIsNotNone(sslobj.shared_ciphers()) + self.assertTrue(sslobj.getpeercert()) + if 'tls-unique' in ssl.CHANNEL_BINDING_TYPES: + self.assertTrue(sslobj.get_channel_binding('tls-unique')) + try: + self.ssl_io_loop(sock, incoming, outgoing, sslobj.unwrap) + except ssl.SSLSyscallError: + # If the server shuts down the TCP connection without sending a + # secure shutdown message, this is reported as SSL_ERROR_SYSCALL + pass + self.assertRaises(ssl.SSLError, sslobj.write, b'foo') + + def test_bio_read_write_data(self): + sock = socket.socket(socket.AF_INET) + self.addCleanup(sock.close) + sock.connect(self.server_addr) + 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'FOO\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, b'foo\n') + self.ssl_io_loop(sock, incoming, outgoing, sslobj.unwrap) + + +class NetworkedTests(unittest.TestCase): + + def test_timeout_connect_ex(self): + # Issue #12065: on a timeout, connect_ex() should return the original + # errno (mimicking the behaviour of non-SSL sockets). with support.transient_internet(REMOTE_HOST): - sock = socket.socket(socket.AF_INET) - sock.connect((REMOTE_HOST, 443)) - incoming = ssl.MemoryBIO() - outgoing = ssl.MemoryBIO() - ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + s = ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_REQUIRED, + do_handshake_on_connect=False) + self.addCleanup(s.close) + s.settimeout(0.0000001) + rc = s.connect_ex((REMOTE_HOST, 443)) + if rc == 0: + self.skipTest("REMOTE_HOST responded too quickly") + self.assertIn(rc, (errno.EAGAIN, errno.EWOULDBLOCK)) + + @unittest.skipUnless(support.IPV6_ENABLED, 'Needs IPv6') + def test_get_server_certificate_ipv6(self): + with support.transient_internet('ipv6.google.com'): + _test_get_server_certificate(self, 'ipv6.google.com', 443) + _test_get_server_certificate_fail(self, 'ipv6.google.com', 443) + + def test_algorithms(self): + # Issue #8484: all algorithms should be available when verifying a + # certificate. + # SHA256 was added in OpenSSL 0.9.8 + if ssl.OPENSSL_VERSION_INFO < (0, 9, 8, 0, 15): + self.skipTest("SHA256 not available on %r" % ssl.OPENSSL_VERSION) + # sha256.tbs-internet.com needs SNI to use the correct certificate + if not ssl.HAS_SNI: + self.skipTest("SNI needed for this test") + # https://sha2.hboeck.de/ was used until 2011-01-08 (no route to host) + remote = ("sha256.tbs-internet.com", 443) + sha256_cert = os.path.join(os.path.dirname(__file__), "sha256.pem") + with support.transient_internet("sha256.tbs-internet.com"): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) ctx.verify_mode = ssl.CERT_REQUIRED - ctx.load_verify_locations(REMOTE_ROOT_CERT) - ctx.check_hostname = True - sslobj = ctx.wrap_bio(incoming, outgoing, False, REMOTE_HOST) - self.assertIs(sslobj._sslobj.owner, sslobj) - self.assertIsNone(sslobj.cipher()) - self.assertIsNotNone(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.assertIsNotNone(sslobj.shared_ciphers()) - self.assertTrue(sslobj.getpeercert()) - if 'tls-unique' in ssl.CHANNEL_BINDING_TYPES: - self.assertTrue(sslobj.get_channel_binding('tls-unique')) + ctx.load_verify_locations(sha256_cert) + s = ctx.wrap_socket(socket.socket(socket.AF_INET), + server_hostname="sha256.tbs-internet.com") try: - self.ssl_io_loop(sock, incoming, outgoing, sslobj.unwrap) - except ssl.SSLSyscallError: - # self-signed.pythontest.net probably shuts down the TCP - # connection without sending a secure shutdown message, and - # this is reported as SSL_ERROR_SYSCALL - pass - self.assertRaises(ssl.SSLError, sslobj.write, b'foo') - sock.close() + s.connect(remote) + if support.verbose: + sys.stdout.write("\nCipher with %r is %r\n" % + (remote, s.cipher())) + sys.stdout.write("Certificate is:\n%s\n" % + pprint.pformat(s.getpeercert())) + finally: + s.close() - def test_read_write_data(self): - with support.transient_internet(REMOTE_HOST): - sock = socket.socket(socket.AF_INET) - sock.connect((REMOTE_HOST, 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() +def _test_get_server_certificate(test, host, port, cert=None): + pem = ssl.get_server_certificate((host, port)) + if not pem: + test.fail("No server certificate on %s:%s!" % (host, port)) -try: - import threading -except ImportError: - _have_threads = False -else: - _have_threads = True + pem = ssl.get_server_certificate((host, port), ca_certs=cert) + if not pem: + test.fail("No server certificate on %s:%s!" % (host, port)) + if support.verbose: + sys.stdout.write("\nVerified certificate for %s:%s is\n%s\n" % (host, port ,pem)) +def _test_get_server_certificate_fail(test, host, port): + try: + pem = ssl.get_server_certificate((host, port), ca_certs=CERTFILE) + except ssl.SSLError as x: + #should fail + if support.verbose: + sys.stdout.write("%s\n" % x) + else: + test.fail("Got server certificate %s for %s:%s!" % (pem, host, port)) + + +if _have_threads: from test.ssl_servers import make_https_server class ThreadedEchoServer(threading.Thread): @@ -1910,6 +1900,15 @@ else: if not stripped: # eof, so quit this handler self.running = False + try: + self.sock = self.sslconn.unwrap() + except OSError: + # Many tests shut the TCP connection down + # without an SSL shutdown. This causes + # unwrap() to raise OSError with errno=0! + pass + else: + self.sslconn = None self.close() elif stripped == b'over': if support.verbose and self.server.connectionchatty: @@ -2747,12 +2746,13 @@ else: count, addr = s.recvfrom_into(b) return b[:count] - # (name, method, whether to expect success, *args) + # (name, method, expect success?, *args, return value func) send_methods = [ - ('send', s.send, True, []), - ('sendto', s.sendto, False, ["some.address"]), - ('sendall', s.sendall, True, []), + ('send', s.send, True, [], len), + ('sendto', s.sendto, False, ["some.address"], len), + ('sendall', s.sendall, True, [], lambda x: None), ] + # (name, method, whether to expect success, *args) recv_methods = [ ('recv', s.recv, True, []), ('recvfrom', s.recvfrom, False, ["some.address"]), @@ -2761,10 +2761,13 @@ else: ] data_prefix = "PREFIX_" - for meth_name, send_meth, expect_success, args in send_methods: + for (meth_name, send_meth, expect_success, args, + ret_val_meth) in send_methods: indata = (data_prefix + meth_name).encode('ascii') try: - send_meth(indata, *args) + ret = send_meth(indata, *args) + msg = "sending with {}".format(meth_name) + self.assertEqual(ret, ret_val_meth(indata), msg=msg) outdata = s.read() if outdata != indata.lower(): self.fail( @@ -3424,18 +3427,20 @@ def test_main(verbose=False): pass for filename in [ - CERTFILE, REMOTE_ROOT_CERT, BYTES_CERTFILE, + CERTFILE, BYTES_CERTFILE, ONLYCERT, ONLYKEY, BYTES_ONLYCERT, BYTES_ONLYKEY, SIGNED_CERTFILE, SIGNED_CERTFILE2, SIGNING_CA, BADCERT, BADKEY, EMPTYCERT]: if not os.path.exists(filename): raise support.TestFailed("Can't read certificate file %r" % filename) - tests = [ContextTests, BasicSocketTests, SSLErrorTests, MemoryBIOTests] + tests = [ + ContextTests, BasicSocketTests, SSLErrorTests, MemoryBIOTests, + SimpleBackgroundTests, + ] 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_statistics.py b/Lib/test/test_statistics.py index 0089ae8..6cac709 100644 --- a/Lib/test/test_statistics.py +++ b/Lib/test/test_statistics.py @@ -21,6 +21,10 @@ import statistics # === Helper functions and class === +def sign(x): + """Return -1.0 for negatives, including -0.0, otherwise +1.0.""" + return math.copysign(1, x) + def _nan_equal(a, b): """Return True if a and b are both the same kind of NAN. @@ -264,6 +268,13 @@ class NumericTestCase(unittest.TestCase): # === Test the helpers === # ======================== +class TestSign(unittest.TestCase): + """Test that the helper function sign() works correctly.""" + def testZeroes(self): + # Test that signed zeroes report their sign correctly. + self.assertEqual(sign(0.0), +1) + self.assertEqual(sign(-0.0), -1) + # --- Tests for approx_equal --- @@ -659,7 +670,7 @@ class DocTests(unittest.TestCase): @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -OO and above") def test_doc_tests(self): - failed, tried = doctest.testmod(statistics) + failed, tried = doctest.testmod(statistics, optionflags=doctest.ELLIPSIS) self.assertGreater(tried, 0) self.assertEqual(failed, 0) @@ -702,9 +713,9 @@ class ExactRatioTest(unittest.TestCase): def test_decimal(self): D = Decimal _exact_ratio = statistics._exact_ratio - self.assertEqual(_exact_ratio(D("0.125")), (125, 1000)) - self.assertEqual(_exact_ratio(D("12.345")), (12345, 1000)) - self.assertEqual(_exact_ratio(D("-1.98")), (-198, 100)) + self.assertEqual(_exact_ratio(D("0.125")), (1, 8)) + self.assertEqual(_exact_ratio(D("12.345")), (2469, 200)) + self.assertEqual(_exact_ratio(D("-1.98")), (-99, 50)) def test_inf(self): INF = float("INF") @@ -743,18 +754,18 @@ class ExactRatioTest(unittest.TestCase): class DecimalToRatioTest(unittest.TestCase): - # Test _decimal_to_ratio private function. + # Test _exact_ratio private function. def test_infinity(self): # Test that INFs are handled correctly. inf = Decimal('INF') - self.assertEqual(statistics._decimal_to_ratio(inf), (inf, None)) - self.assertEqual(statistics._decimal_to_ratio(-inf), (-inf, None)) + self.assertEqual(statistics._exact_ratio(inf), (inf, None)) + self.assertEqual(statistics._exact_ratio(-inf), (-inf, None)) def test_nan(self): # Test that NANs are handled correctly. for nan in (Decimal('NAN'), Decimal('sNAN')): - num, den = statistics._decimal_to_ratio(nan) + num, den = statistics._exact_ratio(nan) # Because NANs always compare non-equal, we cannot use assertEqual. # Nor can we use an identity test, as we don't guarantee anything # about the object identity. @@ -767,30 +778,30 @@ class DecimalToRatioTest(unittest.TestCase): for d in numbers: # First test positive decimals. assert d > 0 - num, den = statistics._decimal_to_ratio(d) + num, den = statistics._exact_ratio(d) self.assertGreaterEqual(num, 0) self.assertGreater(den, 0) # Then test negative decimals. - num, den = statistics._decimal_to_ratio(-d) + num, den = statistics._exact_ratio(-d) self.assertLessEqual(num, 0) self.assertGreater(den, 0) def test_negative_exponent(self): # Test result when the exponent is negative. - t = statistics._decimal_to_ratio(Decimal("0.1234")) - self.assertEqual(t, (1234, 10000)) + t = statistics._exact_ratio(Decimal("0.1234")) + self.assertEqual(t, (617, 5000)) def test_positive_exponent(self): # Test results when the exponent is positive. - t = statistics._decimal_to_ratio(Decimal("1.234e7")) + t = statistics._exact_ratio(Decimal("1.234e7")) self.assertEqual(t, (12340000, 1)) def test_regression_20536(self): # Regression test for issue 20536. # See http://bugs.python.org/issue20536 - t = statistics._decimal_to_ratio(Decimal("1e2")) + t = statistics._exact_ratio(Decimal("1e2")) self.assertEqual(t, (100, 1)) - t = statistics._decimal_to_ratio(Decimal("1.47e5")) + t = statistics._exact_ratio(Decimal("1.47e5")) self.assertEqual(t, (147000, 1)) @@ -971,6 +982,301 @@ class ConvertTest(unittest.TestCase): self.assertTrue(_nan_equal(x, nan)) +class FailNegTest(unittest.TestCase): + """Test _fail_neg private function.""" + + def test_pass_through(self): + # Test that values are passed through unchanged. + values = [1, 2.0, Fraction(3), Decimal(4)] + new = list(statistics._fail_neg(values)) + self.assertEqual(values, new) + + def test_negatives_raise(self): + # Test that negatives raise an exception. + for x in [1, 2.0, Fraction(3), Decimal(4)]: + seq = [-x] + it = statistics._fail_neg(seq) + self.assertRaises(statistics.StatisticsError, next, it) + + def test_error_msg(self): + # Test that a given error message is used. + msg = "badness #%d" % random.randint(10000, 99999) + try: + next(statistics._fail_neg([-1], msg)) + except statistics.StatisticsError as e: + errmsg = e.args[0] + else: + self.fail("expected exception, but it didn't happen") + self.assertEqual(errmsg, msg) + + +class Test_Product(NumericTestCase): + """Test the private _product function.""" + + def test_ints(self): + data = [1, 2, 5, 7, 9] + self.assertEqual(statistics._product(data), (0, 630)) + self.assertEqual(statistics._product(data*100), (0, 630**100)) + + def test_floats(self): + data = [1.0, 2.0, 4.0, 8.0] + self.assertEqual(statistics._product(data), (8, 0.25)) + + def test_overflow(self): + # Test with floats that overflow. + data = [1e300]*5 + self.assertEqual(statistics._product(data), (5980, 0.6928287951283193)) + + def test_fractions(self): + F = Fraction + data = [F(14, 23), F(69, 1), F(665, 529), F(299, 105), F(1683, 39)] + exp, mant = statistics._product(data) + self.assertEqual(exp, 0) + self.assertEqual(mant, F(2*3*7*11*17*19, 23)) + self.assertTrue(isinstance(mant, F)) + # Mixed Fraction and int. + data = [3, 25, F(2, 15)] + exp, mant = statistics._product(data) + self.assertEqual(exp, 0) + self.assertEqual(mant, F(10)) + self.assertTrue(isinstance(mant, F)) + + def test_decimal(self): + D = Decimal + data = [D('24.5'), D('17.6'), D('0.025'), D('1.3')] + expected = D('14.014000') + self.assertEqual(statistics._product(data), (0, expected)) + + def test_mixed_decimal_float(self): + # Test that mixed Decimal and float raises. + self.assertRaises(TypeError, statistics._product, [1.0, Decimal(1)]) + self.assertRaises(TypeError, statistics._product, [Decimal(1), 1.0]) + + +@unittest.skipIf(True, "FIXME: tests known to fail, see issue #27181") +class Test_Nth_Root(NumericTestCase): + """Test the functionality of the private _nth_root function.""" + + def setUp(self): + self.nroot = statistics._nth_root + + # --- Special values (infinities, NANs, zeroes) --- + + def test_float_NAN(self): + # Test that the root of a float NAN is a float NAN. + NAN = float('nan') + for n in range(2, 9): + with self.subTest(n=n): + result = self.nroot(NAN, n) + self.assertTrue(math.isnan(result)) + + def test_decimal_QNAN(self): + # Test the behaviour when taking the root of a Decimal quiet NAN. + NAN = decimal.Decimal('nan') + with decimal.localcontext() as ctx: + ctx.traps[decimal.InvalidOperation] = 1 + self.assertRaises(decimal.InvalidOperation, self.nroot, NAN, 5) + ctx.traps[decimal.InvalidOperation] = 0 + self.assertTrue(self.nroot(NAN, 5).is_qnan()) + + def test_decimal_SNAN(self): + # Test that taking the root of a Decimal sNAN always raises. + sNAN = decimal.Decimal('snan') + with decimal.localcontext() as ctx: + ctx.traps[decimal.InvalidOperation] = 1 + self.assertRaises(decimal.InvalidOperation, self.nroot, sNAN, 5) + ctx.traps[decimal.InvalidOperation] = 0 + self.assertRaises(decimal.InvalidOperation, self.nroot, sNAN, 5) + + def test_inf(self): + # Test that the root of infinity is infinity. + for INF in (float('inf'), decimal.Decimal('inf')): + for n in range(2, 9): + with self.subTest(n=n, inf=INF): + self.assertEqual(self.nroot(INF, n), INF) + + # FIXME: need to check Decimal zeroes too. + def test_zero(self): + # Test that the root of +0.0 is +0.0. + for n in range(2, 11): + with self.subTest(n=n): + result = self.nroot(+0.0, n) + self.assertEqual(result, 0.0) + self.assertEqual(sign(result), +1) + + # FIXME: need to check Decimal zeroes too. + def test_neg_zero(self): + # Test that the root of -0.0 is -0.0. + for n in range(2, 11): + with self.subTest(n=n): + result = self.nroot(-0.0, n) + self.assertEqual(result, 0.0) + self.assertEqual(sign(result), -1) + + # --- Test return types --- + + def check_result_type(self, x, n, outtype): + self.assertIsInstance(self.nroot(x, n), outtype) + class MySubclass(type(x)): + pass + self.assertIsInstance(self.nroot(MySubclass(x), n), outtype) + + def testDecimal(self): + # Test that Decimal arguments return Decimal results. + self.check_result_type(decimal.Decimal('33.3'), 3, decimal.Decimal) + + def testFloat(self): + # Test that other arguments return float results. + for x in (0.2, Fraction(11, 7), 91): + self.check_result_type(x, 6, float) + + # --- Test bad input --- + + def testBadOrderTypes(self): + # Test that nroot raises correctly when n has the wrong type. + for n in (5.0, 2j, None, 'x', b'x', [], {}, set(), sign): + with self.subTest(n=n): + self.assertRaises(TypeError, self.nroot, 2.5, n) + + def testBadOrderValues(self): + # Test that nroot raises correctly when n has a wrong value. + for n in (1, 0, -1, -2, -87): + with self.subTest(n=n): + self.assertRaises(ValueError, self.nroot, 2.5, n) + + def testBadTypes(self): + # Test that nroot raises correctly when x has the wrong type. + for x in (None, 'x', b'x', [], {}, set(), sign): + with self.subTest(x=x): + self.assertRaises(TypeError, self.nroot, x, 3) + + def testNegativeError(self): + # Test negative x raises correctly. + x = random.uniform(-20.0, -0.1) + assert x < 0 + for n in range(3, 7): + with self.subTest(x=x, n=n): + self.assertRaises(ValueError, self.nroot, x, n) + # And Decimal. + self.assertRaises(ValueError, self.nroot, Decimal(-27), 3) + + # --- Test that nroot is never worse than calling math.pow() --- + + def check_error_is_no_worse(self, x, n): + y = math.pow(x, n) + with self.subTest(x=x, n=n, y=y): + err1 = abs(self.nroot(y, n) - x) + err2 = abs(math.pow(y, 1.0/n) - x) + self.assertLessEqual(err1, err2) + + def testCompareWithPowSmall(self): + # Compare nroot with pow for small values of x. + for i in range(200): + x = random.uniform(1e-9, 1.0-1e-9) + n = random.choice(range(2, 16)) + self.check_error_is_no_worse(x, n) + + def testCompareWithPowMedium(self): + # Compare nroot with pow for medium-sized values of x. + for i in range(200): + x = random.uniform(1.0, 100.0) + n = random.choice(range(2, 16)) + self.check_error_is_no_worse(x, n) + + def testCompareWithPowLarge(self): + # Compare nroot with pow for largish values of x. + for i in range(200): + x = random.uniform(100.0, 10000.0) + n = random.choice(range(2, 16)) + self.check_error_is_no_worse(x, n) + + def testCompareWithPowHuge(self): + # Compare nroot with pow for huge values of x. + for i in range(200): + x = random.uniform(1e20, 1e50) + # We restrict the order here to avoid an Overflow error. + n = random.choice(range(2, 7)) + self.check_error_is_no_worse(x, n) + + # --- Test for numerically correct answers --- + + def testExactPowers(self): + # Test that small integer powers are calculated exactly. + for i in range(1, 51): + for n in range(2, 16): + if (i, n) == (35, 13): + # See testExpectedFailure35p13 + continue + with self.subTest(i=i, n=n): + x = i**n + self.assertEqual(self.nroot(x, n), i) + + def testExpectedFailure35p13(self): + # Test the expected failure 35**13 is almost exact. + x = 35**13 + err = abs(self.nroot(x, 13) - 35) + self.assertLessEqual(err, 0.000000001) + + def testOne(self): + # Test that the root of 1.0 is 1.0. + for n in range(2, 11): + with self.subTest(n=n): + self.assertEqual(self.nroot(1.0, n), 1.0) + + def testFraction(self): + # Test Fraction results. + x = Fraction(89, 75) + self.assertEqual(self.nroot(x**12, 12), float(x)) + + def testInt(self): + # Test int results. + x = 276 + self.assertEqual(self.nroot(x**24, 24), x) + + def testBigInt(self): + # Test that ints too big to convert to floats work. + bignum = 10**20 # That's not that big... + self.assertEqual(self.nroot(bignum**280, 280), bignum) + # Can we make it bigger? + hugenum = bignum**50 + # Make sure that it is too big to convert to a float. + try: + y = float(hugenum) + except OverflowError: + pass + else: + raise AssertionError('hugenum is not big enough') + self.assertEqual(self.nroot(hugenum, 50), float(bignum)) + + def testDecimal(self): + # Test Decimal results. + for s in '3.759 64.027 5234.338'.split(): + x = decimal.Decimal(s) + with self.subTest(x=x): + a = self.nroot(x**5, 5) + self.assertEqual(a, x) + a = self.nroot(x**17, 17) + self.assertEqual(a, x) + + def testFloat(self): + # Test float results. + for x in (3.04e-16, 18.25, 461.3, 1.9e17): + with self.subTest(x=x): + self.assertEqual(self.nroot(x**3, 3), x) + self.assertEqual(self.nroot(x**8, 8), x) + self.assertEqual(self.nroot(x**11, 11), x) + + +class Test_NthRoot_NS(unittest.TestCase): + """Test internals of the nth_root function, hidden in _nroot_NS.""" + + def test_class_cannot_be_instantiated(self): + # Test that _nroot_NS cannot be instantiated. + # It should be a namespace, like in C++ or C#, but Python + # lacks that feature and so we have to make do with a class. + self.assertRaises(TypeError, statistics._nroot_NS) + + # === Tests for public functions === class UnivariateCommonMixin: @@ -1082,13 +1388,13 @@ class UnivariateTypeMixin: Not all tests to do with types need go in this class. Only those that rely on the function returning the same type as its input data. """ - def test_types_conserved(self): - # Test that functions keeps the same type as their data points. - # (Excludes mixed data types.) This only tests the type of the return - # result, not the value. + def prepare_types_for_conservation_test(self): + """Return the types which are expected to be conserved.""" class MyFloat(float): def __truediv__(self, other): return type(self)(super().__truediv__(other)) + def __rtruediv__(self, other): + return type(self)(super().__rtruediv__(other)) def __sub__(self, other): return type(self)(super().__sub__(other)) def __rsub__(self, other): @@ -1098,9 +1404,14 @@ class UnivariateTypeMixin: def __add__(self, other): return type(self)(super().__add__(other)) __radd__ = __add__ + return (float, Decimal, Fraction, MyFloat) + def test_types_conserved(self): + # Test that functions keeps the same type as their data points. + # (Excludes mixed data types.) This only tests the type of the return + # result, not the value. data = self.prepare_data() - for kind in (float, Decimal, Fraction, MyFloat): + for kind in self.prepare_types_for_conservation_test(): d = [kind(x) for x in data] result = self.func(d) self.assertIs(type(result), kind) @@ -1275,12 +1586,16 @@ class AverageMixin(UnivariateCommonMixin): for x in (23, 42.5, 1.3e15, Fraction(15, 19), Decimal('0.28')): self.assertEqual(self.func([x]), x) + def prepare_values_for_repeated_single_test(self): + return (3.5, 17, 2.5e15, Fraction(61, 67), Decimal('4.9712')) + def test_repeated_single_value(self): # The average of a single repeated value is the value itself. - for x in (3.5, 17, 2.5e15, Fraction(61, 67), Decimal('4.9712')): + for x in self.prepare_values_for_repeated_single_test(): for count in (2, 5, 10, 20): - data = [x]*count - self.assertEqual(self.func(data), x) + with self.subTest(x=x, count=count): + data = [x]*count + self.assertEqual(self.func(data), x) class TestMean(NumericTestCase, AverageMixin, UnivariateTypeMixin): @@ -1304,7 +1619,7 @@ class TestMean(NumericTestCase, AverageMixin, UnivariateTypeMixin): self.assertEqual(self.func(data), 22.015625) def test_decimals(self): - # Test mean with ints. + # Test mean with Decimals. D = Decimal data = [D("1.634"), D("2.517"), D("3.912"), D("4.072"), D("5.813")] random.shuffle(data) @@ -1379,6 +1694,94 @@ class TestMean(NumericTestCase, AverageMixin, UnivariateTypeMixin): self.assertEqual(statistics.mean([tiny]*n), tiny) +class TestHarmonicMean(NumericTestCase, AverageMixin, UnivariateTypeMixin): + def setUp(self): + self.func = statistics.harmonic_mean + + def prepare_data(self): + # Override mixin method. + values = super().prepare_data() + values.remove(0) + return values + + def prepare_values_for_repeated_single_test(self): + # Override mixin method. + return (3.5, 17, 2.5e15, Fraction(61, 67), Decimal('4.125')) + + def test_zero(self): + # Test that harmonic mean returns zero when given zero. + values = [1, 0, 2] + self.assertEqual(self.func(values), 0) + + def test_negative_error(self): + # Test that harmonic mean raises when given a negative value. + exc = statistics.StatisticsError + for values in ([-1], [1, -2, 3]): + with self.subTest(values=values): + self.assertRaises(exc, self.func, values) + + def test_ints(self): + # Test harmonic mean with ints. + data = [2, 4, 4, 8, 16, 16] + random.shuffle(data) + self.assertEqual(self.func(data), 6*4/5) + + def test_floats_exact(self): + # Test harmonic mean with some carefully chosen floats. + data = [1/8, 1/4, 1/4, 1/2, 1/2] + random.shuffle(data) + self.assertEqual(self.func(data), 1/4) + self.assertEqual(self.func([0.25, 0.5, 1.0, 1.0]), 0.5) + + def test_singleton_lists(self): + # Test that harmonic mean([x]) returns (approximately) x. + for x in range(1, 101): + self.assertEqual(self.func([x]), x) + + def test_decimals_exact(self): + # Test harmonic mean with some carefully chosen Decimals. + D = Decimal + self.assertEqual(self.func([D(15), D(30), D(60), D(60)]), D(30)) + data = [D("0.05"), D("0.10"), D("0.20"), D("0.20")] + random.shuffle(data) + self.assertEqual(self.func(data), D("0.10")) + data = [D("1.68"), D("0.32"), D("5.94"), D("2.75")] + random.shuffle(data) + self.assertEqual(self.func(data), D(66528)/70723) + + def test_fractions(self): + # Test harmonic mean with Fractions. + F = Fraction + data = [F(1, 2), F(2, 3), F(3, 4), F(4, 5), F(5, 6), F(6, 7), F(7, 8)] + random.shuffle(data) + self.assertEqual(self.func(data), F(7*420, 4029)) + + def test_inf(self): + # Test harmonic mean with infinity. + values = [2.0, float('inf'), 1.0] + self.assertEqual(self.func(values), 2.0) + + def test_nan(self): + # Test harmonic mean with NANs. + values = [2.0, float('nan'), 1.0] + self.assertTrue(math.isnan(self.func(values))) + + def test_multiply_data_points(self): + # Test multiplying every data point by a constant. + c = 111 + data = [3.4, 4.5, 4.9, 6.7, 6.8, 7.2, 8.0, 8.1, 9.4] + expected = self.func(data)*c + result = self.func([x*c for x in data]) + self.assertEqual(result, expected) + + def test_doubled_data(self): + # Harmonic mean of [a,b...z] should be same as for [a,a,b,b...z,z]. + data = [random.uniform(1, 5) for _ in range(1000)] + expected = self.func(data) + actual = self.func(data*2) + self.assertApproxEqual(actual, expected) + + class TestMedian(NumericTestCase, AverageMixin): # Common tests for median and all median.* functions. def setUp(self): @@ -1600,6 +2003,22 @@ class TestMedianGrouped(TestMedian): data = [220, 220, 240, 260, 260, 260, 260, 280, 280, 300, 320, 340] self.assertEqual(self.func(data, 20), 265.0) + def test_data_type_error(self): + # Test median_grouped with str, bytes data types for data and interval + data = ["", "", ""] + self.assertRaises(TypeError, self.func, data) + #--- + data = [b"", b"", b""] + self.assertRaises(TypeError, self.func, data) + #--- + data = [1, 2, 3] + interval = "" + self.assertRaises(TypeError, self.func, data, interval) + #--- + data = [1, 2, 3] + interval = b"" + self.assertRaises(TypeError, self.func, data, interval) + class TestMode(NumericTestCase, AverageMixin, UnivariateTypeMixin): # Test cases for the discrete version of mode. diff --git a/Lib/test/test_strptime.py b/Lib/test/test_strptime.py index 85126e6..8c8f97b 100644 --- a/Lib/test/test_strptime.py +++ b/Lib/test/test_strptime.py @@ -5,7 +5,6 @@ import time import locale import re import os -import sys from test import support from datetime import date as datetime_date @@ -153,8 +152,8 @@ class TimeRETests(unittest.TestCase): "'%s' using '%s'; group 'a' = '%s', group 'b' = %s'" % (found.string, found.re.pattern, found.group('a'), found.group('b'))) - for directive in ('a','A','b','B','c','d','H','I','j','m','M','p','S', - 'U','w','W','x','X','y','Y','Z','%'): + for directive in ('a','A','b','B','c','d','G','H','I','j','m','M','p', + 'S','u','U','V','w','W','x','X','y','Y','Z','%'): compiled = self.time_re.compile("%" + directive) found = compiled.match(time.strftime("%" + directive)) self.assertTrue(found, "Matching failed on '%s' using '%s' regex" % @@ -219,6 +218,26 @@ class StrptimeTests(unittest.TestCase): else: self.fail("'%s' did not raise ValueError" % bad_format) + # Ambiguous or incomplete cases using ISO year/week/weekday directives + # 1. ISO week (%V) is specified, but the year is specified with %Y + # instead of %G + with self.assertRaises(ValueError): + _strptime._strptime("1999 50", "%Y %V") + # 2. ISO year (%G) and ISO week (%V) are specified, but weekday is not + with self.assertRaises(ValueError): + _strptime._strptime("1999 51", "%G %V") + # 3. ISO year (%G) and weekday are specified, but ISO week (%V) is not + for w in ('A', 'a', 'w', 'u'): + with self.assertRaises(ValueError): + _strptime._strptime("1999 51","%G %{}".format(w)) + # 4. ISO year is specified alone (e.g. time.strptime('2015', '%G')) + with self.assertRaises(ValueError): + _strptime._strptime("2015", "%G") + # 5. Julian/ordinal day (%j) is specified with %G, but not %Y + with self.assertRaises(ValueError): + _strptime._strptime("1999 256", "%G %j") + + def test_strptime_exception_context(self): # check that this doesn't chain exceptions needlessly (see #17572) with self.assertRaises(ValueError) as e: @@ -290,7 +309,7 @@ class StrptimeTests(unittest.TestCase): def test_weekday(self): # Test weekday directives - for directive in ('A', 'a', 'w'): + for directive in ('A', 'a', 'w', 'u'): self.helper(directive,6) def test_julian(self): @@ -457,16 +476,20 @@ class CalculationTests(unittest.TestCase): # Should be able to infer date if given year, week of year (%U or %W) # and day of the week def test_helper(ymd_tuple, test_reason): - for directive in ('W', 'U'): - format_string = "%%Y %%%s %%w" % directive - dt_date = datetime_date(*ymd_tuple) - strp_input = dt_date.strftime(format_string) - strp_output = _strptime._strptime_time(strp_input, format_string) - self.assertTrue(strp_output[:3] == ymd_tuple, - "%s(%s) test failed w/ '%s': %s != %s (%s != %s)" % - (test_reason, directive, strp_input, - strp_output[:3], ymd_tuple, - strp_output[7], dt_date.timetuple()[7])) + for year_week_format in ('%Y %W', '%Y %U', '%G %V'): + for weekday_format in ('%w', '%u', '%a', '%A'): + format_string = year_week_format + ' ' + weekday_format + with self.subTest(test_reason, + date=ymd_tuple, + format=format_string): + dt_date = datetime_date(*ymd_tuple) + strp_input = dt_date.strftime(format_string) + strp_output = _strptime._strptime_time(strp_input, + format_string) + msg = "%r: %s != %s" % (strp_input, + strp_output[7], + dt_date.timetuple()[7]) + self.assertEqual(strp_output[:3], ymd_tuple, msg) test_helper((1901, 1, 3), "week 0") test_helper((1901, 1, 8), "common case") test_helper((1901, 1, 13), "day on Sunday") @@ -498,33 +521,48 @@ class CalculationTests(unittest.TestCase): self.assertEqual(_strptime._strptime_time(value, format)[:-1], expected) check('2015 0 0', '%Y %U %w', 2014, 12, 28, 0, 0, 0, 6, 362) check('2015 0 0', '%Y %W %w', 2015, 1, 4, 0, 0, 0, 6, 4) + check('2015 1 1', '%G %V %u', 2014, 12, 29, 0, 0, 0, 0, 363) check('2015 0 1', '%Y %U %w', 2014, 12, 29, 0, 0, 0, 0, 363) check('2015 0 1', '%Y %W %w', 2014, 12, 29, 0, 0, 0, 0, 363) + check('2015 1 2', '%G %V %u', 2014, 12, 30, 0, 0, 0, 1, 364) check('2015 0 2', '%Y %U %w', 2014, 12, 30, 0, 0, 0, 1, 364) check('2015 0 2', '%Y %W %w', 2014, 12, 30, 0, 0, 0, 1, 364) + check('2015 1 3', '%G %V %u', 2014, 12, 31, 0, 0, 0, 2, 365) check('2015 0 3', '%Y %U %w', 2014, 12, 31, 0, 0, 0, 2, 365) check('2015 0 3', '%Y %W %w', 2014, 12, 31, 0, 0, 0, 2, 365) + check('2015 1 4', '%G %V %u', 2015, 1, 1, 0, 0, 0, 3, 1) check('2015 0 4', '%Y %U %w', 2015, 1, 1, 0, 0, 0, 3, 1) check('2015 0 4', '%Y %W %w', 2015, 1, 1, 0, 0, 0, 3, 1) + check('2015 1 5', '%G %V %u', 2015, 1, 2, 0, 0, 0, 4, 2) check('2015 0 5', '%Y %U %w', 2015, 1, 2, 0, 0, 0, 4, 2) check('2015 0 5', '%Y %W %w', 2015, 1, 2, 0, 0, 0, 4, 2) + check('2015 1 6', '%G %V %u', 2015, 1, 3, 0, 0, 0, 5, 3) check('2015 0 6', '%Y %U %w', 2015, 1, 3, 0, 0, 0, 5, 3) check('2015 0 6', '%Y %W %w', 2015, 1, 3, 0, 0, 0, 5, 3) + check('2015 1 7', '%G %V %u', 2015, 1, 4, 0, 0, 0, 6, 4) check('2009 0 0', '%Y %U %w', 2008, 12, 28, 0, 0, 0, 6, 363) check('2009 0 0', '%Y %W %w', 2009, 1, 4, 0, 0, 0, 6, 4) + check('2009 1 1', '%G %V %u', 2008, 12, 29, 0, 0, 0, 0, 364) check('2009 0 1', '%Y %U %w', 2008, 12, 29, 0, 0, 0, 0, 364) check('2009 0 1', '%Y %W %w', 2008, 12, 29, 0, 0, 0, 0, 364) + check('2009 1 2', '%G %V %u', 2008, 12, 30, 0, 0, 0, 1, 365) check('2009 0 2', '%Y %U %w', 2008, 12, 30, 0, 0, 0, 1, 365) check('2009 0 2', '%Y %W %w', 2008, 12, 30, 0, 0, 0, 1, 365) + check('2009 1 3', '%G %V %u', 2008, 12, 31, 0, 0, 0, 2, 366) check('2009 0 3', '%Y %U %w', 2008, 12, 31, 0, 0, 0, 2, 366) check('2009 0 3', '%Y %W %w', 2008, 12, 31, 0, 0, 0, 2, 366) + check('2009 1 4', '%G %V %u', 2009, 1, 1, 0, 0, 0, 3, 1) check('2009 0 4', '%Y %U %w', 2009, 1, 1, 0, 0, 0, 3, 1) check('2009 0 4', '%Y %W %w', 2009, 1, 1, 0, 0, 0, 3, 1) + check('2009 1 5', '%G %V %u', 2009, 1, 2, 0, 0, 0, 4, 2) check('2009 0 5', '%Y %U %w', 2009, 1, 2, 0, 0, 0, 4, 2) check('2009 0 5', '%Y %W %w', 2009, 1, 2, 0, 0, 0, 4, 2) + check('2009 1 6', '%G %V %u', 2009, 1, 3, 0, 0, 0, 5, 3) check('2009 0 6', '%Y %U %w', 2009, 1, 3, 0, 0, 0, 5, 3) check('2009 0 6', '%Y %W %w', 2009, 1, 3, 0, 0, 0, 5, 3) + check('2009 1 7', '%G %V %u', 2009, 1, 4, 0, 0, 0, 6, 4) + class CacheTests(unittest.TestCase): """Test that caching works properly.""" diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py index efbdbfc..4d9d601 100644 --- a/Lib/test/test_struct.py +++ b/Lib/test/test_struct.py @@ -1,5 +1,6 @@ from collections import abc import array +import math import operator import unittest import struct @@ -15,22 +16,10 @@ byteorders = '', '@', '=', '<', '>', '!' def iter_integer_formats(byteorders=byteorders): for code in integer_codes: for byteorder in byteorders: - if (byteorder in ('', '@') and code in ('q', 'Q') and - not HAVE_LONG_LONG): - continue if (byteorder not in ('', '@') and code in ('n', 'N')): continue yield code, byteorder -# Native 'q' packing isn't available on systems that don't have the C -# long long type. -try: - struct.pack('q', 5) -except struct.error: - HAVE_LONG_LONG = False -else: - HAVE_LONG_LONG = True - def string_reverse(s): return s[::-1] @@ -158,9 +147,7 @@ class StructTest(unittest.TestCase): self.assertEqual(size, expected_size[code]) # native integer sizes - native_pairs = 'bB', 'hH', 'iI', 'lL', 'nN' - if HAVE_LONG_LONG: - native_pairs += 'qQ', + native_pairs = 'bB', 'hH', 'iI', 'lL', 'nN', 'qQ' for format_pair in native_pairs: for byteorder in '', '@': signed_size = struct.calcsize(byteorder + format_pair[0]) @@ -173,9 +160,8 @@ class StructTest(unittest.TestCase): self.assertLessEqual(4, struct.calcsize('l')) self.assertLessEqual(struct.calcsize('h'), struct.calcsize('i')) self.assertLessEqual(struct.calcsize('i'), struct.calcsize('l')) - if HAVE_LONG_LONG: - self.assertLessEqual(8, struct.calcsize('q')) - self.assertLessEqual(struct.calcsize('l'), struct.calcsize('q')) + self.assertLessEqual(8, struct.calcsize('q')) + self.assertLessEqual(struct.calcsize('l'), struct.calcsize('q')) self.assertGreaterEqual(struct.calcsize('n'), struct.calcsize('i')) self.assertGreaterEqual(struct.calcsize('n'), struct.calcsize('P')) @@ -366,8 +352,6 @@ class StructTest(unittest.TestCase): # SF bug 705836. "<f" and ">f" had a severe rounding bug, where a carry # from the low-order discarded bits could propagate into the exponent # field, causing the result to be wrong by a factor of 2. - import math - for base in range(1, 33): # smaller <- largest representable float less than base. delta = 0.5 @@ -659,6 +643,110 @@ class UnpackIteratorTest(unittest.TestCase): self.assertRaises(StopIteration, next, it) self.assertRaises(StopIteration, next, it) + def test_half_float(self): + # Little-endian examples from: + # http://en.wikipedia.org/wiki/Half_precision_floating-point_format + format_bits_float__cleanRoundtrip_list = [ + (b'\x00\x3c', 1.0), + (b'\x00\xc0', -2.0), + (b'\xff\x7b', 65504.0), # (max half precision) + (b'\x00\x04', 2**-14), # ~= 6.10352 * 10**-5 (min pos normal) + (b'\x01\x00', 2**-24), # ~= 5.96046 * 10**-8 (min pos subnormal) + (b'\x00\x00', 0.0), + (b'\x00\x80', -0.0), + (b'\x00\x7c', float('+inf')), + (b'\x00\xfc', float('-inf')), + (b'\x55\x35', 0.333251953125), # ~= 1/3 + ] + + for le_bits, f in format_bits_float__cleanRoundtrip_list: + be_bits = le_bits[::-1] + self.assertEqual(f, struct.unpack('<e', le_bits)[0]) + self.assertEqual(le_bits, struct.pack('<e', f)) + self.assertEqual(f, struct.unpack('>e', be_bits)[0]) + self.assertEqual(be_bits, struct.pack('>e', f)) + if sys.byteorder == 'little': + self.assertEqual(f, struct.unpack('e', le_bits)[0]) + self.assertEqual(le_bits, struct.pack('e', f)) + else: + self.assertEqual(f, struct.unpack('e', be_bits)[0]) + self.assertEqual(be_bits, struct.pack('e', f)) + + # Check for NaN handling: + format_bits__nan_list = [ + ('<e', b'\x01\xfc'), + ('<e', b'\x00\xfe'), + ('<e', b'\xff\xff'), + ('<e', b'\x01\x7c'), + ('<e', b'\x00\x7e'), + ('<e', b'\xff\x7f'), + ] + + for formatcode, bits in format_bits__nan_list: + self.assertTrue(math.isnan(struct.unpack('<e', bits)[0])) + self.assertTrue(math.isnan(struct.unpack('>e', bits[::-1])[0])) + + # Check that packing produces a bit pattern representing a quiet NaN: + # all exponent bits and the msb of the fraction should all be 1. + packed = struct.pack('<e', math.nan) + self.assertEqual(packed[1] & 0x7e, 0x7e) + packed = struct.pack('<e', -math.nan) + self.assertEqual(packed[1] & 0x7e, 0x7e) + + # Checks for round-to-even behavior + format_bits_float__rounding_list = [ + ('>e', b'\x00\x01', 2.0**-25 + 2.0**-35), # Rounds to minimum subnormal + ('>e', b'\x00\x00', 2.0**-25), # Underflows to zero (nearest even mode) + ('>e', b'\x00\x00', 2.0**-26), # Underflows to zero + ('>e', b'\x03\xff', 2.0**-14 - 2.0**-24), # Largest subnormal. + ('>e', b'\x03\xff', 2.0**-14 - 2.0**-25 - 2.0**-65), + ('>e', b'\x04\x00', 2.0**-14 - 2.0**-25), + ('>e', b'\x04\x00', 2.0**-14), # Smallest normal. + ('>e', b'\x3c\x01', 1.0+2.0**-11 + 2.0**-16), # rounds to 1.0+2**(-10) + ('>e', b'\x3c\x00', 1.0+2.0**-11), # rounds to 1.0 (nearest even mode) + ('>e', b'\x3c\x00', 1.0+2.0**-12), # rounds to 1.0 + ('>e', b'\x7b\xff', 65504), # largest normal + ('>e', b'\x7b\xff', 65519), # rounds to 65504 + ('>e', b'\x80\x01', -2.0**-25 - 2.0**-35), # Rounds to minimum subnormal + ('>e', b'\x80\x00', -2.0**-25), # Underflows to zero (nearest even mode) + ('>e', b'\x80\x00', -2.0**-26), # Underflows to zero + ('>e', b'\xbc\x01', -1.0-2.0**-11 - 2.0**-16), # rounds to 1.0+2**(-10) + ('>e', b'\xbc\x00', -1.0-2.0**-11), # rounds to 1.0 (nearest even mode) + ('>e', b'\xbc\x00', -1.0-2.0**-12), # rounds to 1.0 + ('>e', b'\xfb\xff', -65519), # rounds to 65504 + ] + + for formatcode, bits, f in format_bits_float__rounding_list: + self.assertEqual(bits, struct.pack(formatcode, f)) + + # This overflows, and so raises an error + format_bits_float__roundingError_list = [ + # Values that round to infinity. + ('>e', 65520.0), + ('>e', 65536.0), + ('>e', 1e300), + ('>e', -65520.0), + ('>e', -65536.0), + ('>e', -1e300), + ('<e', 65520.0), + ('<e', 65536.0), + ('<e', 1e300), + ('<e', -65520.0), + ('<e', -65536.0), + ('<e', -1e300), + ] + + for formatcode, f in format_bits_float__roundingError_list: + self.assertRaises(OverflowError, struct.pack, formatcode, f) + + # Double rounding + format_bits_float__doubleRoundingError_list = [ + ('>e', b'\x67\xff', 0x1ffdffffff * 2**-26), # should be 2047, if double-rounded 64>32>16, becomes 2048 + ] + + for formatcode, bits, f in format_bits_float__doubleRoundingError_list: + self.assertEqual(bits, struct.pack(formatcode, f)) + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_subclassinit.py b/Lib/test/test_subclassinit.py new file mode 100644 index 0000000..ea6de75 --- /dev/null +++ b/Lib/test/test_subclassinit.py @@ -0,0 +1,244 @@ +import sys +import types +import unittest + + +class Test(unittest.TestCase): + def test_init_subclass(self): + class A: + initialized = False + + def __init_subclass__(cls): + super().__init_subclass__() + cls.initialized = True + + class B(A): + pass + + self.assertFalse(A.initialized) + self.assertTrue(B.initialized) + + def test_init_subclass_dict(self): + class A(dict): + initialized = False + + def __init_subclass__(cls): + super().__init_subclass__() + cls.initialized = True + + class B(A): + pass + + self.assertFalse(A.initialized) + self.assertTrue(B.initialized) + + def test_init_subclass_kwargs(self): + class A: + def __init_subclass__(cls, **kwargs): + cls.kwargs = kwargs + + class B(A, x=3): + pass + + self.assertEqual(B.kwargs, dict(x=3)) + + def test_init_subclass_error(self): + class A: + def __init_subclass__(cls): + raise RuntimeError + + with self.assertRaises(RuntimeError): + class B(A): + pass + + def test_init_subclass_wrong(self): + class A: + def __init_subclass__(cls, whatever): + pass + + with self.assertRaises(TypeError): + class B(A): + pass + + def test_init_subclass_skipped(self): + class BaseWithInit: + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + cls.initialized = cls + + class BaseWithoutInit(BaseWithInit): + pass + + class A(BaseWithoutInit): + pass + + self.assertIs(A.initialized, A) + self.assertIs(BaseWithoutInit.initialized, BaseWithoutInit) + + def test_init_subclass_diamond(self): + class Base: + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + cls.calls = [] + + class Left(Base): + pass + + class Middle: + def __init_subclass__(cls, middle, **kwargs): + super().__init_subclass__(**kwargs) + cls.calls += [middle] + + class Right(Base): + def __init_subclass__(cls, right="right", **kwargs): + super().__init_subclass__(**kwargs) + cls.calls += [right] + + class A(Left, Middle, Right, middle="middle"): + pass + + self.assertEqual(A.calls, ["right", "middle"]) + self.assertEqual(Left.calls, []) + self.assertEqual(Right.calls, []) + + def test_set_name(self): + class Descriptor: + def __set_name__(self, owner, name): + self.owner = owner + self.name = name + + class A: + d = Descriptor() + + self.assertEqual(A.d.name, "d") + self.assertIs(A.d.owner, A) + + def test_set_name_metaclass(self): + class Meta(type): + def __new__(cls, name, bases, ns): + ret = super().__new__(cls, name, bases, ns) + self.assertEqual(ret.d.name, "d") + self.assertIs(ret.d.owner, ret) + return 0 + + class Descriptor: + def __set_name__(self, owner, name): + self.owner = owner + self.name = name + + class A(metaclass=Meta): + d = Descriptor() + self.assertEqual(A, 0) + + def test_set_name_error(self): + class Descriptor: + def __set_name__(self, owner, name): + raise RuntimeError + + with self.assertRaises(RuntimeError): + class A: + d = Descriptor() + + def test_set_name_wrong(self): + class Descriptor: + def __set_name__(self): + pass + + with self.assertRaises(TypeError): + class A: + d = Descriptor() + + def test_set_name_init_subclass(self): + class Descriptor: + def __set_name__(self, owner, name): + self.owner = owner + self.name = name + + class Meta(type): + def __new__(cls, name, bases, ns): + self = super().__new__(cls, name, bases, ns) + self.meta_owner = self.owner + self.meta_name = self.name + return self + + class A: + def __init_subclass__(cls): + cls.owner = cls.d.owner + cls.name = cls.d.name + + class B(A, metaclass=Meta): + d = Descriptor() + + self.assertIs(B.owner, B) + self.assertEqual(B.name, 'd') + self.assertIs(B.meta_owner, B) + self.assertEqual(B.name, 'd') + + def test_errors(self): + class MyMeta(type): + pass + + with self.assertRaises(TypeError): + class MyClass(metaclass=MyMeta, otherarg=1): + pass + + with self.assertRaises(TypeError): + types.new_class("MyClass", (object,), + dict(metaclass=MyMeta, otherarg=1)) + types.prepare_class("MyClass", (object,), + dict(metaclass=MyMeta, otherarg=1)) + + class MyMeta(type): + def __init__(self, name, bases, namespace, otherarg): + super().__init__(name, bases, namespace) + + with self.assertRaises(TypeError): + class MyClass(metaclass=MyMeta, otherarg=1): + pass + + class MyMeta(type): + def __new__(cls, name, bases, namespace, otherarg): + return super().__new__(cls, name, bases, namespace) + + def __init__(self, name, bases, namespace, otherarg): + super().__init__(name, bases, namespace) + self.otherarg = otherarg + + class MyClass(metaclass=MyMeta, otherarg=1): + pass + + self.assertEqual(MyClass.otherarg, 1) + + def test_errors_changed_pep487(self): + # These tests failed before Python 3.6, PEP 487 + class MyMeta(type): + def __new__(cls, name, bases, namespace): + return super().__new__(cls, name=name, bases=bases, + dict=namespace) + + with self.assertRaises(TypeError): + class MyClass(metaclass=MyMeta): + pass + + class MyMeta(type): + def __new__(cls, name, bases, namespace, otherarg): + self = super().__new__(cls, name, bases, namespace) + self.otherarg = otherarg + return self + + class MyClass(metaclass=MyMeta, otherarg=1): + pass + + self.assertEqual(MyClass.otherarg, 1) + + def test_type(self): + t = type('NewClass', (object,), {}) + self.assertIsInstance(t, type) + self.assertEqual(t.__name__, 'NewClass') + + with self.assertRaises(TypeError): + type(name='NewClass', bases=(object,), dict={}) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 4b409b7..2bfb69c 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1,20 +1,16 @@ import unittest from unittest import mock -from test.support import script_helper from test import support import subprocess import sys import signal import io -import locale import os import errno import tempfile import time -import re import selectors import sysconfig -import warnings import select import shutil import gc @@ -448,8 +444,8 @@ class ProcessTestCase(BaseTestCase): p = subprocess.Popen([sys.executable, "-c", 'import sys; sys.stdout.write("orange")'], stdout=subprocess.PIPE) - self.addCleanup(p.stdout.close) - self.assertEqual(p.stdout.read(), b"orange") + with p: + self.assertEqual(p.stdout.read(), b"orange") def test_stdout_filedes(self): # stdout is set to open file descriptor @@ -479,8 +475,8 @@ class ProcessTestCase(BaseTestCase): p = subprocess.Popen([sys.executable, "-c", 'import sys; sys.stderr.write("strawberry")'], stderr=subprocess.PIPE) - self.addCleanup(p.stderr.close) - self.assertStderrEqual(p.stderr.read(), b"strawberry") + with p: + self.assertStderrEqual(p.stderr.read(), b"strawberry") def test_stderr_filedes(self): # stderr is set to open file descriptor @@ -535,8 +531,8 @@ class ProcessTestCase(BaseTestCase): 'sys.stderr.write("orange")'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - self.addCleanup(p.stdout.close) - self.assertStderrEqual(p.stdout.read(), b"appleorange") + with p: + self.assertStderrEqual(p.stdout.read(), b"appleorange") def test_stdout_stderr_file(self): # capture stdout and stderr to the same open file @@ -686,7 +682,7 @@ class ProcessTestCase(BaseTestCase): self.assertEqual(stdout, "banana") self.assertStderrEqual(stderr.encode(), b"pineapple\npear\n") - def test_communicate_timeout_large_ouput(self): + def test_communicate_timeout_large_output(self): # Test an expiring timeout while the child is outputting lots of data. p = subprocess.Popen([sys.executable, "-c", 'import sys,os,time;' @@ -794,18 +790,19 @@ class ProcessTestCase(BaseTestCase): stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=1) - p.stdin.write("line1\n") - p.stdin.flush() - self.assertEqual(p.stdout.readline(), "line1\n") - p.stdin.write("line3\n") - p.stdin.close() - self.addCleanup(p.stdout.close) - self.assertEqual(p.stdout.readline(), - "line2\n") - self.assertEqual(p.stdout.read(6), - "line3\n") - self.assertEqual(p.stdout.read(), - "line4\nline5\nline6\nline7\nline8") + with p: + p.stdin.write("line1\n") + p.stdin.flush() + self.assertEqual(p.stdout.readline(), "line1\n") + p.stdin.write("line3\n") + p.stdin.close() + self.addCleanup(p.stdout.close) + self.assertEqual(p.stdout.readline(), + "line2\n") + self.assertEqual(p.stdout.read(6), + "line3\n") + self.assertEqual(p.stdout.read(), + "line4\nline5\nline6\nline7\nline8") def test_universal_newlines_communicate(self): # universal newlines through communicate() @@ -1431,6 +1428,27 @@ class POSIXProcessTestCase(BaseTestCase): p.wait() self.assertEqual(-p.returncode, signal.SIGABRT) + def test_CalledProcessError_str_signal(self): + err = subprocess.CalledProcessError(-int(signal.SIGABRT), "fake cmd") + error_string = str(err) + # We're relying on the repr() of the signal.Signals intenum to provide + # the word signal, the signal name and the numeric value. + self.assertIn("signal", error_string.lower()) + # We're not being specific about the signal name as some signals have + # multiple names and which name is revealed can vary. + self.assertIn("SIG", error_string) + self.assertIn(str(signal.SIGABRT), error_string) + + def test_CalledProcessError_str_unknown_signal(self): + err = subprocess.CalledProcessError(-9876543, "fake cmd") + error_string = str(err) + self.assertIn("unknown signal 9876543.", error_string) + + def test_CalledProcessError_str_non_zero(self): + err = subprocess.CalledProcessError(2, "fake cmd") + error_string = str(err) + self.assertIn("non-zero exit status 2.", error_string) + def test_preexec(self): # DISCLAIMER: Setting environment variables is *not* a good use # of a preexec_fn. This is merely a test. @@ -1439,8 +1457,8 @@ class POSIXProcessTestCase(BaseTestCase): 'sys.stdout.write(os.getenv("FRUIT"))'], stdout=subprocess.PIPE, preexec_fn=lambda: os.putenv("FRUIT", "apple")) - self.addCleanup(p.stdout.close) - self.assertEqual(p.stdout.read(), b"apple") + with p: + self.assertEqual(p.stdout.read(), b"apple") def test_preexec_exception(self): def raise_it(): @@ -1561,7 +1579,7 @@ class POSIXProcessTestCase(BaseTestCase): fd, fname = tempfile.mkstemp() # reopen in text mode with open(fd, "w", errors="surrogateescape") as fobj: - fobj.write("#!/bin/sh\n") + fobj.write("#!%s\n" % support.unix_shell) fobj.write("exec '%s' -c 'import sys; sys.exit(47)'\n" % sys.executable) os.chmod(fname, 0o700) @@ -1588,8 +1606,8 @@ class POSIXProcessTestCase(BaseTestCase): p = subprocess.Popen(["echo $FRUIT"], shell=1, stdout=subprocess.PIPE, env=newenv) - self.addCleanup(p.stdout.close) - self.assertEqual(p.stdout.read().strip(b" \t\r\n\f"), b"apple") + with p: + self.assertEqual(p.stdout.read().strip(b" \t\r\n\f"), b"apple") def test_shell_string(self): # Run command through the shell (string) @@ -1598,15 +1616,15 @@ class POSIXProcessTestCase(BaseTestCase): p = subprocess.Popen("echo $FRUIT", shell=1, stdout=subprocess.PIPE, env=newenv) - self.addCleanup(p.stdout.close) - self.assertEqual(p.stdout.read().strip(b" \t\r\n\f"), b"apple") + with p: + self.assertEqual(p.stdout.read().strip(b" \t\r\n\f"), b"apple") def test_call_string(self): # call() function with string argument on UNIX fd, fname = tempfile.mkstemp() # reopen in text mode with open(fd, "w", errors="surrogateescape") as fobj: - fobj.write("#!/bin/sh\n") + fobj.write("#!%s\n" % support.unix_shell) fobj.write("exec '%s' -c 'import sys; sys.exit(47)'\n" % sys.executable) os.chmod(fname, 0o700) @@ -1631,8 +1649,8 @@ class POSIXProcessTestCase(BaseTestCase): for sh in shells: p = subprocess.Popen("echo $0", executable=sh, shell=True, stdout=subprocess.PIPE) - self.addCleanup(p.stdout.close) - self.assertEqual(p.stdout.read().strip(), bytes(sh, 'ascii')) + with p: + self.assertEqual(p.stdout.read().strip(), bytes(sh, 'ascii')) def _kill_process(self, method, *args): # Do not inherit file handles from the parent. @@ -2290,7 +2308,9 @@ class POSIXProcessTestCase(BaseTestCase): self.addCleanup(p.stderr.close) ident = id(p) pid = p.pid - del p + with support.check_warnings(('', ResourceWarning)): + p = None + # check that p is in the active processes list self.assertIn(ident, [id(o) for o in subprocess._active]) @@ -2309,7 +2329,9 @@ class POSIXProcessTestCase(BaseTestCase): self.addCleanup(p.stderr.close) ident = id(p) pid = p.pid - del p + with support.check_warnings(('', ResourceWarning)): + p = None + os.kill(pid, signal.SIGKILL) # check that p is in the active processes list self.assertIn(ident, [id(o) for o in subprocess._active]) @@ -2501,8 +2523,8 @@ class Win32ProcessTestCase(BaseTestCase): p = subprocess.Popen(["set"], shell=1, stdout=subprocess.PIPE, env=newenv) - self.addCleanup(p.stdout.close) - self.assertIn(b"physalis", p.stdout.read()) + with p: + self.assertIn(b"physalis", p.stdout.read()) def test_shell_string(self): # Run command through the shell (string) @@ -2511,8 +2533,8 @@ class Win32ProcessTestCase(BaseTestCase): p = subprocess.Popen("set", shell=1, stdout=subprocess.PIPE, env=newenv) - self.addCleanup(p.stdout.close) - self.assertIn(b"physalis", p.stdout.read()) + with p: + self.assertIn(b"physalis", p.stdout.read()) def test_call_string(self): # call() function with string argument on Windows @@ -2531,16 +2553,14 @@ class Win32ProcessTestCase(BaseTestCase): stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - self.addCleanup(p.stdout.close) - self.addCleanup(p.stderr.close) - self.addCleanup(p.stdin.close) - # Wait for the interpreter to be completely initialized before - # sending any signal. - p.stdout.read(1) - getattr(p, method)(*args) - _, stderr = p.communicate() - self.assertStderrEqual(stderr, b'') - returncode = p.wait() + with p: + # Wait for the interpreter to be completely initialized before + # sending any signal. + p.stdout.read(1) + getattr(p, method)(*args) + _, stderr = p.communicate() + self.assertStderrEqual(stderr, b'') + returncode = p.wait() self.assertNotEqual(returncode, 0) def _kill_dead_process(self, method, *args): @@ -2553,19 +2573,17 @@ class Win32ProcessTestCase(BaseTestCase): stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - self.addCleanup(p.stdout.close) - self.addCleanup(p.stderr.close) - self.addCleanup(p.stdin.close) - # Wait for the interpreter to be completely initialized before - # sending any signal. - p.stdout.read(1) - # The process should end after this - time.sleep(1) - # This shouldn't raise even though the child is now dead - getattr(p, method)(*args) - _, stderr = p.communicate() - self.assertStderrEqual(stderr, b'') - rc = p.wait() + with p: + # Wait for the interpreter to be completely initialized before + # sending any signal. + p.stdout.read(1) + # The process should end after this + time.sleep(1) + # This shouldn't raise even though the child is now dead + getattr(p, method)(*args) + _, stderr = p.communicate() + self.assertStderrEqual(stderr, b'') + rc = p.wait() self.assertEqual(rc, 42) def test_send_signal(self): @@ -2608,8 +2626,7 @@ class MiscTests(unittest.TestCase): def test__all__(self): """Ensure that __all__ is populated properly.""" - # STARTUPINFO added to __all__ in 3.6 - intentionally_excluded = {"list2cmdline", "STARTUPINFO", "Handle"} + intentionally_excluded = {"list2cmdline", "Handle"} exported = set(subprocess.__all__) possible_exports = set() import types @@ -2654,11 +2671,11 @@ class CommandsWithSpaces (BaseTestCase): def with_spaces(self, *args, **kwargs): kwargs['stdout'] = subprocess.PIPE p = subprocess.Popen(*args, **kwargs) - self.addCleanup(p.stdout.close) - self.assertEqual( - p.stdout.read ().decode("mbcs"), - "2 [%r, 'ab cd']" % self.fname - ) + with p: + self.assertEqual( + p.stdout.read ().decode("mbcs"), + "2 [%r, 'ab cd']" % self.fname + ) def test_shell_string_with_spaces(self): # call() function with string argument with spaces on Windows diff --git a/Lib/test/test_sunau.py b/Lib/test/test_sunau.py index 0f4134e..bc1f46c 100644 --- a/Lib/test/test_sunau.py +++ b/Lib/test/test_sunau.py @@ -1,4 +1,3 @@ -from test.support import TESTFN import unittest from test import audiotests from audioop import byteswap diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 2c00417..269d9bf 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -9,13 +9,11 @@ import errno from test import support TESTFN = support.TESTFN -TESTDIRN = os.path.basename(tempfile.mkdtemp(dir='.')) class TestSupport(unittest.TestCase): def setUp(self): support.unlink(TESTFN) - support.rmtree(TESTDIRN) tearDown = setUp def test_import_module(self): @@ -48,6 +46,10 @@ class TestSupport(unittest.TestCase): support.unlink(TESTFN) def test_rmtree(self): + TESTDIRN = os.path.basename(tempfile.mkdtemp(dir='.')) + self.addCleanup(support.rmtree, TESTDIRN) + support.rmtree(TESTDIRN) + os.mkdir(TESTDIRN) os.mkdir(os.path.join(TESTDIRN, TESTDIRN)) support.rmtree(TESTDIRN) @@ -228,7 +230,8 @@ class TestSupport(unittest.TestCase): def test_check_syntax_error(self): support.check_syntax_error(self, "def class") - self.assertRaises(AssertionError, support.check_syntax_error, self, "1") + with self.assertRaises(AssertionError): + support.check_syntax_error(self, "x=1") def test_CleanImport(self): import importlib @@ -312,6 +315,28 @@ class TestSupport(unittest.TestCase): self.OtherClass, self.RefClass, ignore=ignore) self.assertEqual(set(), missing_items) + def test_check__all__(self): + extra = {'tempdir'} + blacklist = {'template'} + support.check__all__(self, + tempfile, + extra=extra, + blacklist=blacklist) + + extra = {'TextTestResult', 'installHandler'} + blacklist = {'load_tests', "TestProgram", "BaseTestSuite"} + + support.check__all__(self, + unittest, + ("unittest.result", "unittest.case", + "unittest.suite", "unittest.loader", + "unittest.main", "unittest.runner", + "unittest.signals"), + extra=extra, + blacklist=blacklist) + + self.assertRaises(AssertionError, support.check__all__, self, unittest) + # XXX -follows a list of untested API # make_legacy_pyc # is_resource_enabled diff --git a/Lib/test/test_symbol.py b/Lib/test/test_symbol.py new file mode 100644 index 0000000..c1306f5 --- /dev/null +++ b/Lib/test/test_symbol.py @@ -0,0 +1,54 @@ +import unittest +from test import support +import os +import sys +import subprocess + + +SYMBOL_FILE = support.findfile('symbol.py') +GRAMMAR_FILE = os.path.join(os.path.dirname(__file__), + '..', '..', 'Include', 'graminit.h') +TEST_PY_FILE = 'symbol_test.py' + + +class TestSymbolGeneration(unittest.TestCase): + + def _copy_file_without_generated_symbols(self, source_file, dest_file): + with open(source_file) as fp: + lines = fp.readlines() + with open(dest_file, 'w') as fp: + fp.writelines(lines[:lines.index("#--start constants--\n") + 1]) + fp.writelines(lines[lines.index("#--end constants--\n"):]) + + def _generate_symbols(self, grammar_file, target_symbol_py_file): + proc = subprocess.Popen([sys.executable, + SYMBOL_FILE, + grammar_file, + target_symbol_py_file], stderr=subprocess.PIPE) + stderr = proc.communicate()[1] + return proc.returncode, stderr + + def compare_files(self, file1, file2): + with open(file1) as fp: + lines1 = fp.readlines() + with open(file2) as fp: + lines2 = fp.readlines() + self.assertEqual(lines1, lines2) + + @unittest.skipIf(not os.path.exists(GRAMMAR_FILE), + 'test only works from source build directory') + def test_real_grammar_and_symbol_file(self): + output = support.TESTFN + self.addCleanup(support.unlink, output) + + self._copy_file_without_generated_symbols(SYMBOL_FILE, output) + + exitcode, stderr = self._generate_symbols(GRAMMAR_FILE, output) + self.assertEqual(b'', stderr) + self.assertEqual(0, exitcode) + + self.compare_files(SYMBOL_FILE, output) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_symtable.py b/Lib/test/test_symtable.py index c5d7fac..bf99505 100644 --- a/Lib/test/test_symtable.py +++ b/Lib/test/test_symtable.py @@ -158,9 +158,11 @@ class SymtableTest(unittest.TestCase): checkfilename("def f(x): foo)(") # parse-time checkfilename("def f(x): global x") # symtable-build-time symtable.symtable("pass", b"spam", "exec") - with self.assertRaises(TypeError): + with self.assertWarns(DeprecationWarning), \ + self.assertRaises(TypeError): symtable.symtable("pass", bytearray(b"spam"), "exec") - symtable.symtable("pass", memoryview(b"spam"), "exec") + with self.assertWarns(DeprecationWarning): + symtable.symtable("pass", memoryview(b"spam"), "exec") with self.assertRaises(TypeError): symtable.symtable("pass", list(b"spam"), "exec") diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index 83f49f6..f47bdf9 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -35,14 +35,6 @@ SyntaxError: invalid syntax Traceback (most recent call last): SyntaxError: can't assign to keyword -It's a syntax error to assign to the empty tuple. Why isn't it an -error to assign to the empty list? It will always raise some error at -runtime. - ->>> () = 1 -Traceback (most recent call last): -SyntaxError: can't assign to () - >>> f() = 1 Traceback (most recent call last): SyntaxError: can't assign to function call @@ -493,10 +485,6 @@ Traceback (most recent call last): ... SyntaxError: keyword argument repeated ->>> del () -Traceback (most recent call last): -SyntaxError: can't delete () - >>> {1, 2, 3} = 42 Traceback (most recent call last): SyntaxError: can't assign to literal diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 4435d69..3131f36 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1084,7 +1084,7 @@ class SizeofTest(unittest.TestCase): check((1,2,3), vsize('') + 3*self.P) # type # static type: PyTypeObject - fmt = 'P2n15Pl4Pn9Pn11PIP' + fmt = 'P2n15Pl4Pn9Pn11PIPP' if hasattr(sys, 'getcounts'): fmt += '3n2P' s = vsize(fmt) diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py index 509bc3e..25c5835 100644 --- a/Lib/test/test_sys_settrace.py +++ b/Lib/test/test_sys_settrace.py @@ -338,8 +338,8 @@ class TraceTestCase(unittest.TestCase): def test_14_onliner_if(self): def onliners(): - if True: False - else: True + if True: x=False + else: x=True return 0 self.run_and_compare( onliners, diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index abfb34d..d7785ce 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -2074,6 +2074,24 @@ class MiscTest(unittest.TestCase): with self.assertRaises(ValueError): tarfile.itn(0x10000000000, 6, tarfile.GNU_FORMAT) + def test__all__(self): + blacklist = {'version', 'grp', 'pwd', 'symlink_exception', + 'NUL', 'BLOCKSIZE', 'RECORDSIZE', 'GNU_MAGIC', + 'POSIX_MAGIC', 'LENGTH_NAME', 'LENGTH_LINK', + 'LENGTH_PREFIX', 'REGTYPE', 'AREGTYPE', 'LNKTYPE', + 'SYMTYPE', 'CHRTYPE', 'BLKTYPE', 'DIRTYPE', 'FIFOTYPE', + 'CONTTYPE', 'GNUTYPE_LONGNAME', 'GNUTYPE_LONGLINK', + 'GNUTYPE_SPARSE', 'XHDTYPE', 'XGLTYPE', 'SOLARIS_XHDTYPE', + 'SUPPORTED_TYPES', 'REGULAR_TYPES', 'GNU_TYPES', + 'PAX_FIELDS', 'PAX_NAME_FIELDS', 'PAX_NUMBER_FIELDS', + 'stn', 'nts', 'nti', 'itn', 'calc_chksums', 'copyfileobj', + 'filemode', + 'EmptyHeaderError', 'TruncatedHeaderError', + 'EOFHeaderError', 'InvalidHeaderError', + 'SubsequentHeaderError', 'ExFileObject', + 'main'} + support.check__all__(self, tarfile, blacklist=blacklist) + class CommandLineTest(unittest.TestCase): diff --git a/Lib/test/test_telnetlib.py b/Lib/test/test_telnetlib.py index 8e219f4..51d82e1 100644 --- a/Lib/test/test_telnetlib.py +++ b/Lib/test/test_telnetlib.py @@ -1,7 +1,6 @@ import socket import selectors import telnetlib -import time import contextlib from test import support @@ -42,6 +41,11 @@ class GeneralTests(unittest.TestCase): telnet = telnetlib.Telnet(HOST, self.port) telnet.sock.close() + def testContextManager(self): + with telnetlib.Telnet(HOST, self.port) as tn: + self.assertIsNotNone(tn.get_socket()) + self.assertIsNone(tn.get_socket()) + def testTimeoutDefault(self): self.assertTrue(socket.getdefaulttimeout() is None) socket.setdefaulttimeout(30) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index b630509..845e7d4 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -8,7 +8,6 @@ from test.support import (verbose, import_module, cpython_only, from test.support.script_helper import assert_python_ok, assert_python_failure import random -import re import sys _thread = import_module('_thread') threading = import_module('threading') @@ -19,6 +18,7 @@ import os import subprocess from test import lock_tests +from test import support # Between fork() and exec(), only async-safe functions are allowed (issues @@ -1117,5 +1117,12 @@ class BoundedSemaphoreTests(lock_tests.BoundedSemaphoreTests): class BarrierTests(lock_tests.BarrierTests): barriertype = staticmethod(threading.Barrier) +class MiscTestCase(unittest.TestCase): + def test__all__(self): + extra = {"ThreadError"} + blacklist = {'currentThread', 'activeCount'} + support.check__all__(self, threading, ('threading', '_thread'), + extra=extra, blacklist=blacklist) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py index 76b894e..f224212 100644 --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -1,6 +1,8 @@ from test import support +import decimal import enum import locale +import math import platform import sys import sysconfig @@ -21,17 +23,27 @@ SIZEOF_INT = sysconfig.get_config_var('SIZEOF_INT') or 4 TIME_MAXYEAR = (1 << 8 * SIZEOF_INT - 1) - 1 TIME_MINYEAR = -TIME_MAXYEAR - 1 +SEC_TO_US = 10 ** 6 US_TO_NS = 10 ** 3 MS_TO_NS = 10 ** 6 SEC_TO_NS = 10 ** 9 +NS_TO_SEC = 10 ** 9 class _PyTime(enum.IntEnum): # Round towards minus infinity (-inf) ROUND_FLOOR = 0 # Round towards infinity (+inf) ROUND_CEILING = 1 + # Round to nearest with ties going to nearest even integer + ROUND_HALF_EVEN = 2 -ALL_ROUNDING_METHODS = (_PyTime.ROUND_FLOOR, _PyTime.ROUND_CEILING) +# Rounding modes supported by PyTime +ROUNDING_MODES = ( + # (PyTime rounding method, decimal rounding method) + (_PyTime.ROUND_FLOOR, decimal.ROUND_FLOOR), + (_PyTime.ROUND_CEILING, decimal.ROUND_CEILING), + (_PyTime.ROUND_HALF_EVEN, decimal.ROUND_HALF_EVEN), +) class TimeTestCase(unittest.TestCase): @@ -607,79 +619,6 @@ class TestStrftime4dyear(_TestStrftimeYear, _Test4dYear, unittest.TestCase): class TestPytime(unittest.TestCase): - def setUp(self): - self.invalid_values = ( - -(2 ** 100), 2 ** 100, - -(2.0 ** 100.0), 2.0 ** 100.0, - ) - - @support.cpython_only - def test_time_t(self): - from _testcapi import pytime_object_to_time_t - for obj, time_t, rnd in ( - # Round towards minus infinity (-inf) - (0, 0, _PyTime.ROUND_FLOOR), - (-1, -1, _PyTime.ROUND_FLOOR), - (-1.0, -1, _PyTime.ROUND_FLOOR), - (-1.9, -2, _PyTime.ROUND_FLOOR), - (1.0, 1, _PyTime.ROUND_FLOOR), - (1.9, 1, _PyTime.ROUND_FLOOR), - # Round towards infinity (+inf) - (0, 0, _PyTime.ROUND_CEILING), - (-1, -1, _PyTime.ROUND_CEILING), - (-1.0, -1, _PyTime.ROUND_CEILING), - (-1.9, -1, _PyTime.ROUND_CEILING), - (1.0, 1, _PyTime.ROUND_CEILING), - (1.9, 2, _PyTime.ROUND_CEILING), - ): - self.assertEqual(pytime_object_to_time_t(obj, rnd), time_t) - - rnd = _PyTime.ROUND_FLOOR - for invalid in self.invalid_values: - self.assertRaises(OverflowError, - pytime_object_to_time_t, invalid, rnd) - - @support.cpython_only - def test_timespec(self): - from _testcapi import pytime_object_to_timespec - for obj, timespec, rnd in ( - # Round towards minus infinity (-inf) - (0, (0, 0), _PyTime.ROUND_FLOOR), - (-1, (-1, 0), _PyTime.ROUND_FLOOR), - (-1.0, (-1, 0), _PyTime.ROUND_FLOOR), - (1e-9, (0, 1), _PyTime.ROUND_FLOOR), - (1e-10, (0, 0), _PyTime.ROUND_FLOOR), - (-1e-9, (-1, 999999999), _PyTime.ROUND_FLOOR), - (-1e-10, (-1, 999999999), _PyTime.ROUND_FLOOR), - (-1.2, (-2, 800000000), _PyTime.ROUND_FLOOR), - (0.9999999999, (0, 999999999), _PyTime.ROUND_FLOOR), - (1.1234567890, (1, 123456789), _PyTime.ROUND_FLOOR), - (1.1234567899, (1, 123456789), _PyTime.ROUND_FLOOR), - (-1.1234567890, (-2, 876543211), _PyTime.ROUND_FLOOR), - (-1.1234567891, (-2, 876543210), _PyTime.ROUND_FLOOR), - # Round towards infinity (+inf) - (0, (0, 0), _PyTime.ROUND_CEILING), - (-1, (-1, 0), _PyTime.ROUND_CEILING), - (-1.0, (-1, 0), _PyTime.ROUND_CEILING), - (1e-9, (0, 1), _PyTime.ROUND_CEILING), - (1e-10, (0, 1), _PyTime.ROUND_CEILING), - (-1e-9, (-1, 999999999), _PyTime.ROUND_CEILING), - (-1e-10, (0, 0), _PyTime.ROUND_CEILING), - (-1.2, (-2, 800000000), _PyTime.ROUND_CEILING), - (0.9999999999, (1, 0), _PyTime.ROUND_CEILING), - (1.1234567890, (1, 123456790), _PyTime.ROUND_CEILING), - (1.1234567899, (1, 123456790), _PyTime.ROUND_CEILING), - (-1.1234567890, (-2, 876543211), _PyTime.ROUND_CEILING), - (-1.1234567891, (-2, 876543211), _PyTime.ROUND_CEILING), - ): - with self.subTest(obj=obj, round=rnd, timespec=timespec): - self.assertEqual(pytime_object_to_timespec(obj, rnd), timespec) - - rnd = _PyTime.ROUND_FLOOR - for invalid in self.invalid_values: - self.assertRaises(OverflowError, - pytime_object_to_timespec, invalid, rnd) - @unittest.skipUnless(time._STRUCT_TM_ITEMS == 11, "needs tm_zone support") def test_localtime_timezone(self): @@ -734,266 +673,291 @@ class TestPytime(unittest.TestCase): self.assertIs(lt.tm_zone, None) -@unittest.skipUnless(_testcapi is not None, - 'need the _testcapi module') -class TestPyTime_t(unittest.TestCase): +@unittest.skipIf(_testcapi is None, 'need the _testcapi module') +class CPyTimeTestCase: + """ + Base class to test the C _PyTime_t API. + """ + OVERFLOW_SECONDS = None + + def setUp(self): + from _testcapi import SIZEOF_TIME_T + bits = SIZEOF_TIME_T * 8 - 1 + self.time_t_min = -2 ** bits + self.time_t_max = 2 ** bits - 1 + + def time_t_filter(self, seconds): + return (self.time_t_min <= seconds <= self.time_t_max) + + def _rounding_values(self, use_float): + "Build timestamps used to test rounding." + + units = [1, US_TO_NS, MS_TO_NS, SEC_TO_NS] + if use_float: + # picoseconds are only tested to pytime_converter accepting floats + units.append(1e-3) + + values = ( + # small values + 1, 2, 5, 7, 123, 456, 1234, + # 10^k - 1 + 9, + 99, + 999, + 9999, + 99999, + 999999, + # test half even rounding near 0.5, 1.5, 2.5, 3.5, 4.5 + 499, 500, 501, + 1499, 1500, 1501, + 2500, + 3500, + 4500, + ) + + ns_timestamps = [0] + for unit in units: + for value in values: + ns = value * unit + ns_timestamps.extend((-ns, ns)) + for pow2 in (0, 5, 10, 15, 22, 23, 24, 30, 33): + ns = (2 ** pow2) * SEC_TO_NS + ns_timestamps.extend(( + -ns-1, -ns, -ns+1, + ns-1, ns, ns+1 + )) + for seconds in (_testcapi.INT_MIN, _testcapi.INT_MAX): + ns_timestamps.append(seconds * SEC_TO_NS) + if use_float: + # numbers with an exact representation in IEEE 754 (base 2) + for pow2 in (3, 7, 10, 15): + ns = 2.0 ** (-pow2) + ns_timestamps.extend((-ns, ns)) + + # seconds close to _PyTime_t type limit + ns = (2 ** 63 // SEC_TO_NS) * SEC_TO_NS + ns_timestamps.extend((-ns, ns)) + + return ns_timestamps + + def _check_rounding(self, pytime_converter, expected_func, + use_float, unit_to_sec, value_filter=None): + + def convert_values(ns_timestamps): + if use_float: + unit_to_ns = SEC_TO_NS / float(unit_to_sec) + values = [ns / unit_to_ns for ns in ns_timestamps] + else: + unit_to_ns = SEC_TO_NS // unit_to_sec + values = [ns // unit_to_ns for ns in ns_timestamps] + + if value_filter: + values = filter(value_filter, values) + + # remove duplicates and sort + return sorted(set(values)) + + # test rounding + ns_timestamps = self._rounding_values(use_float) + valid_values = convert_values(ns_timestamps) + for time_rnd, decimal_rnd in ROUNDING_MODES : + context = decimal.getcontext() + context.rounding = decimal_rnd + + for value in valid_values: + debug_info = {'value': value, 'rounding': decimal_rnd} + try: + result = pytime_converter(value, time_rnd) + expected = expected_func(value) + except Exception as exc: + self.fail("Error on timestamp conversion: %s" % debug_info) + self.assertEqual(result, + expected, + debug_info) + + # test overflow + ns = self.OVERFLOW_SECONDS * SEC_TO_NS + ns_timestamps = (-ns, ns) + overflow_values = convert_values(ns_timestamps) + for time_rnd, _ in ROUNDING_MODES : + for value in overflow_values: + debug_info = {'value': value, 'rounding': time_rnd} + with self.assertRaises(OverflowError, msg=debug_info): + pytime_converter(value, time_rnd) + + def check_int_rounding(self, pytime_converter, expected_func, + unit_to_sec=1, value_filter=None): + self._check_rounding(pytime_converter, expected_func, + False, unit_to_sec, value_filter) + + def check_float_rounding(self, pytime_converter, expected_func, + unit_to_sec=1, value_filter=None): + self._check_rounding(pytime_converter, expected_func, + True, unit_to_sec, value_filter) + + def decimal_round(self, x): + d = decimal.Decimal(x) + d = d.quantize(1) + return int(d) + + +class TestCPyTime(CPyTimeTestCase, unittest.TestCase): + """ + Test the C _PyTime_t API. + """ + # _PyTime_t is a 64-bit signed integer + OVERFLOW_SECONDS = math.ceil((2**63 + 1) / SEC_TO_NS) + def test_FromSeconds(self): from _testcapi import PyTime_FromSeconds - for seconds in (0, 3, -456, _testcapi.INT_MAX, _testcapi.INT_MIN): - with self.subTest(seconds=seconds): - self.assertEqual(PyTime_FromSeconds(seconds), - seconds * SEC_TO_NS) + + # PyTime_FromSeconds() expects a C int, reject values out of range + def c_int_filter(secs): + return (_testcapi.INT_MIN <= secs <= _testcapi.INT_MAX) + + self.check_int_rounding(lambda secs, rnd: PyTime_FromSeconds(secs), + lambda secs: secs * SEC_TO_NS, + value_filter=c_int_filter) def test_FromSecondsObject(self): from _testcapi import PyTime_FromSecondsObject - # Conversion giving the same result for all rounding methods - for rnd in ALL_ROUNDING_METHODS: - for obj, ts in ( - # integers - (0, 0), - (1, SEC_TO_NS), - (-3, -3 * SEC_TO_NS), - - # float: subseconds - (0.0, 0), - (1e-9, 1), - (1e-6, 10 ** 3), - (1e-3, 10 ** 6), - - # float: seconds - (2.0, 2 * SEC_TO_NS), - (123.0, 123 * SEC_TO_NS), - (-7.0, -7 * SEC_TO_NS), - - # nanosecond are kept for value <= 2^23 seconds - (2**22 - 1e-9, 4194303999999999), - (2**22, 4194304000000000), - (2**22 + 1e-9, 4194304000000001), - (2**23 - 1e-9, 8388607999999999), - (2**23, 8388608000000000), - - # start losing precision for value > 2^23 seconds - (2**23 + 1e-9, 8388608000000002), - - # nanoseconds are lost for value > 2^23 seconds - (2**24 - 1e-9, 16777215999999998), - (2**24, 16777216000000000), - (2**24 + 1e-9, 16777216000000000), - (2**25 - 1e-9, 33554432000000000), - (2**25 , 33554432000000000), - (2**25 + 1e-9, 33554432000000000), - - # close to 2^63 nanoseconds (_PyTime_t limit) - (9223372036, 9223372036 * SEC_TO_NS), - (9223372036.0, 9223372036 * SEC_TO_NS), - (-9223372036, -9223372036 * SEC_TO_NS), - (-9223372036.0, -9223372036 * SEC_TO_NS), - ): - with self.subTest(obj=obj, round=rnd, timestamp=ts): - self.assertEqual(PyTime_FromSecondsObject(obj, rnd), ts) - - with self.subTest(round=rnd): - with self.assertRaises(OverflowError): - PyTime_FromSecondsObject(9223372037, rnd) - PyTime_FromSecondsObject(9223372037.0, rnd) - PyTime_FromSecondsObject(-9223372037, rnd) - PyTime_FromSecondsObject(-9223372037.0, rnd) - - # Conversion giving different results depending on the rounding method - FLOOR = _PyTime.ROUND_FLOOR - CEILING = _PyTime.ROUND_CEILING - for obj, ts, rnd in ( - # close to zero - ( 1e-10, 0, FLOOR), - ( 1e-10, 1, CEILING), - (-1e-10, -1, FLOOR), - (-1e-10, 0, CEILING), - - # test rounding of the last nanosecond - ( 1.1234567899, 1123456789, FLOOR), - ( 1.1234567899, 1123456790, CEILING), - (-1.1234567899, -1123456790, FLOOR), - (-1.1234567899, -1123456789, CEILING), - - # close to 1 second - ( 0.9999999999, 999999999, FLOOR), - ( 0.9999999999, 1000000000, CEILING), - (-0.9999999999, -1000000000, FLOOR), - (-0.9999999999, -999999999, CEILING), - ): - with self.subTest(obj=obj, round=rnd, timestamp=ts): - self.assertEqual(PyTime_FromSecondsObject(obj, rnd), ts) + self.check_int_rounding( + PyTime_FromSecondsObject, + lambda secs: secs * SEC_TO_NS) + + self.check_float_rounding( + PyTime_FromSecondsObject, + lambda ns: self.decimal_round(ns * SEC_TO_NS)) def test_AsSecondsDouble(self): from _testcapi import PyTime_AsSecondsDouble - for nanoseconds, seconds in ( - # near 1 nanosecond - ( 0, 0.0), - ( 1, 1e-9), - (-1, -1e-9), - - # near 1 second - (SEC_TO_NS + 1, 1.0 + 1e-9), - (SEC_TO_NS, 1.0), - (SEC_TO_NS - 1, 1.0 - 1e-9), - - # a few seconds - (123 * SEC_TO_NS, 123.0), - (-567 * SEC_TO_NS, -567.0), - - # nanosecond are kept for value <= 2^23 seconds - (4194303999999999, 2**22 - 1e-9), - (4194304000000000, 2**22), - (4194304000000001, 2**22 + 1e-9), - - # start losing precision for value > 2^23 seconds - (8388608000000002, 2**23 + 1e-9), - - # nanoseconds are lost for value > 2^23 seconds - (16777215999999998, 2**24 - 1e-9), - (16777215999999999, 2**24 - 1e-9), - (16777216000000000, 2**24 ), - (16777216000000001, 2**24 ), - (16777216000000002, 2**24 + 2e-9), - - (33554432000000000, 2**25 ), - (33554432000000002, 2**25 ), - (33554432000000004, 2**25 + 4e-9), - - # close to 2^63 nanoseconds (_PyTime_t limit) - (9223372036 * SEC_TO_NS, 9223372036.0), - (-9223372036 * SEC_TO_NS, -9223372036.0), - ): - with self.subTest(nanoseconds=nanoseconds, seconds=seconds): - self.assertEqual(PyTime_AsSecondsDouble(nanoseconds), - seconds) - - def test_timeval(self): + def float_converter(ns): + if abs(ns) % SEC_TO_NS == 0: + return float(ns // SEC_TO_NS) + else: + return float(ns) / SEC_TO_NS + + self.check_int_rounding(lambda ns, rnd: PyTime_AsSecondsDouble(ns), + float_converter, + NS_TO_SEC) + + def create_decimal_converter(self, denominator): + denom = decimal.Decimal(denominator) + + def converter(value): + d = decimal.Decimal(value) / denom + return self.decimal_round(d) + + return converter + + def test_AsTimeval(self): from _testcapi import PyTime_AsTimeval - for rnd in ALL_ROUNDING_METHODS: - for ns, tv in ( - # microseconds - (0, (0, 0)), - (1000, (0, 1)), - (-1000, (-1, 999999)), - - # seconds - (2 * SEC_TO_NS, (2, 0)), - (-3 * SEC_TO_NS, (-3, 0)), - ): - with self.subTest(nanoseconds=ns, timeval=tv, round=rnd): - self.assertEqual(PyTime_AsTimeval(ns, rnd), tv) - - FLOOR = _PyTime.ROUND_FLOOR - CEILING = _PyTime.ROUND_CEILING - for ns, tv, rnd in ( - # nanoseconds - (1, (0, 0), FLOOR), - (1, (0, 1), CEILING), - (-1, (-1, 999999), FLOOR), - (-1, (0, 0), CEILING), - - # seconds + nanoseconds - (1234567001, (1, 234567), FLOOR), - (1234567001, (1, 234568), CEILING), - (-1234567001, (-2, 765432), FLOOR), - (-1234567001, (-2, 765433), CEILING), - ): - with self.subTest(nanoseconds=ns, timeval=tv, round=rnd): - self.assertEqual(PyTime_AsTimeval(ns, rnd), tv) + + us_converter = self.create_decimal_converter(US_TO_NS) + + def timeval_converter(ns): + us = us_converter(ns) + return divmod(us, SEC_TO_US) + + if sys.platform == 'win32': + from _testcapi import LONG_MIN, LONG_MAX + + # On Windows, timeval.tv_sec type is a C long + def seconds_filter(secs): + return LONG_MIN <= secs <= LONG_MAX + else: + seconds_filter = self.time_t_filter + + self.check_int_rounding(PyTime_AsTimeval, + timeval_converter, + NS_TO_SEC, + value_filter=seconds_filter) @unittest.skipUnless(hasattr(_testcapi, 'PyTime_AsTimespec'), 'need _testcapi.PyTime_AsTimespec') - def test_timespec(self): + def test_AsTimespec(self): from _testcapi import PyTime_AsTimespec - for ns, ts in ( - # nanoseconds - (0, (0, 0)), - (1, (0, 1)), - (-1, (-1, 999999999)), - - # seconds - (2 * SEC_TO_NS, (2, 0)), - (-3 * SEC_TO_NS, (-3, 0)), - - # seconds + nanoseconds - (1234567890, (1, 234567890)), - (-1234567890, (-2, 765432110)), - ): - with self.subTest(nanoseconds=ns, timespec=ts): - self.assertEqual(PyTime_AsTimespec(ns), ts) - - def test_milliseconds(self): + + def timespec_converter(ns): + return divmod(ns, SEC_TO_NS) + + self.check_int_rounding(lambda ns, rnd: PyTime_AsTimespec(ns), + timespec_converter, + NS_TO_SEC, + value_filter=self.time_t_filter) + + def test_AsMilliseconds(self): from _testcapi import PyTime_AsMilliseconds - for rnd in ALL_ROUNDING_METHODS: - for ns, tv in ( - # milliseconds - (1 * MS_TO_NS, 1), - (-2 * MS_TO_NS, -2), - - # seconds - (2 * SEC_TO_NS, 2000), - (-3 * SEC_TO_NS, -3000), - ): - with self.subTest(nanoseconds=ns, timeval=tv, round=rnd): - self.assertEqual(PyTime_AsMilliseconds(ns, rnd), tv) - - FLOOR = _PyTime.ROUND_FLOOR - CEILING = _PyTime.ROUND_CEILING - for ns, ms, rnd in ( - # nanoseconds - (1, 0, FLOOR), - (1, 1, CEILING), - (-1, -1, FLOOR), - (-1, 0, CEILING), - - # seconds + nanoseconds - (1234 * MS_TO_NS + 1, 1234, FLOOR), - (1234 * MS_TO_NS + 1, 1235, CEILING), - (-1234 * MS_TO_NS - 1, -1235, FLOOR), - (-1234 * MS_TO_NS - 1, -1234, CEILING), - ): - with self.subTest(nanoseconds=ns, milliseconds=ms, round=rnd): - self.assertEqual(PyTime_AsMilliseconds(ns, rnd), ms) - - def test_microseconds(self): + + self.check_int_rounding(PyTime_AsMilliseconds, + self.create_decimal_converter(MS_TO_NS), + NS_TO_SEC) + + def test_AsMicroseconds(self): from _testcapi import PyTime_AsMicroseconds - for rnd in ALL_ROUNDING_METHODS: - for ns, tv in ( - # microseconds - (1 * US_TO_NS, 1), - (-2 * US_TO_NS, -2), - - # milliseconds - (1 * MS_TO_NS, 1000), - (-2 * MS_TO_NS, -2000), - - # seconds - (2 * SEC_TO_NS, 2000000), - (-3 * SEC_TO_NS, -3000000), - ): - with self.subTest(nanoseconds=ns, timeval=tv, round=rnd): - self.assertEqual(PyTime_AsMicroseconds(ns, rnd), tv) - - FLOOR = _PyTime.ROUND_FLOOR - CEILING = _PyTime.ROUND_CEILING - for ns, ms, rnd in ( - # nanoseconds - (1, 0, FLOOR), - (1, 1, CEILING), - (-1, -1, FLOOR), - (-1, 0, CEILING), - - # seconds + nanoseconds - (1234 * US_TO_NS + 1, 1234, FLOOR), - (1234 * US_TO_NS + 1, 1235, CEILING), - (-1234 * US_TO_NS - 1, -1235, FLOOR), - (-1234 * US_TO_NS - 1, -1234, CEILING), - ): - with self.subTest(nanoseconds=ns, milliseconds=ms, round=rnd): - self.assertEqual(PyTime_AsMicroseconds(ns, rnd), ms) + + self.check_int_rounding(PyTime_AsMicroseconds, + self.create_decimal_converter(US_TO_NS), + NS_TO_SEC) + + +class TestOldPyTime(CPyTimeTestCase, unittest.TestCase): + """ + Test the old C _PyTime_t API: _PyTime_ObjectToXXX() functions. + """ + + # time_t is a 32-bit or 64-bit signed integer + OVERFLOW_SECONDS = 2 ** 64 + + def test_object_to_time_t(self): + from _testcapi import pytime_object_to_time_t + + self.check_int_rounding(pytime_object_to_time_t, + lambda secs: secs, + value_filter=self.time_t_filter) + + self.check_float_rounding(pytime_object_to_time_t, + self.decimal_round, + value_filter=self.time_t_filter) + + def create_converter(self, sec_to_unit): + def converter(secs): + floatpart, intpart = math.modf(secs) + intpart = int(intpart) + floatpart *= sec_to_unit + floatpart = self.decimal_round(floatpart) + if floatpart < 0: + floatpart += sec_to_unit + intpart -= 1 + elif floatpart >= sec_to_unit: + floatpart -= sec_to_unit + intpart += 1 + return (intpart, floatpart) + return converter + + def test_object_to_timeval(self): + from _testcapi import pytime_object_to_timeval + + self.check_int_rounding(pytime_object_to_timeval, + lambda secs: (secs, 0), + value_filter=self.time_t_filter) + + self.check_float_rounding(pytime_object_to_timeval, + self.create_converter(SEC_TO_US), + value_filter=self.time_t_filter) + + def test_object_to_timespec(self): + from _testcapi import pytime_object_to_timespec + + self.check_int_rounding(pytime_object_to_timespec, + lambda secs: (secs, 0), + value_filter=self.time_t_filter) + + self.check_float_rounding(pytime_object_to_timespec, + self.create_converter(SEC_TO_NS), + value_filter=self.time_t_filter) if __name__ == "__main__": diff --git a/Lib/test/test_timeit.py b/Lib/test/test_timeit.py index 2db3c1b..1a95e29 100644 --- a/Lib/test/test_timeit.py +++ b/Lib/test/test_timeit.py @@ -354,6 +354,28 @@ class TestTimeit(unittest.TestCase): s = self.run_main(switches=['-n1', '1/0']) self.assert_exc_string(error_stringio.getvalue(), 'ZeroDivisionError') + def autorange(self, callback=None): + timer = FakeTimer(seconds_per_increment=0.001) + t = timeit.Timer(stmt=self.fake_stmt, setup=self.fake_setup, timer=timer) + return t.autorange(callback) + + def test_autorange(self): + num_loops, time_taken = self.autorange() + self.assertEqual(num_loops, 1000) + self.assertEqual(time_taken, 1.0) + + def test_autorange_with_callback(self): + def callback(a, b): + print("{} {:.3f}".format(a, b)) + with captured_stdout() as s: + num_loops, time_taken = self.autorange(callback) + self.assertEqual(num_loops, 1000) + self.assertEqual(time_taken, 1.0) + expected = ('10 0.010\n' + '100 0.100\n' + '1000 1.000\n') + self.assertEqual(s.getvalue(), expected) + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_tokenize.py b/Lib/test/test_tokenize.py index 3b17ca6..90438e7 100644 --- a/Lib/test/test_tokenize.py +++ b/Lib/test/test_tokenize.py @@ -24,8 +24,7 @@ class TokenizeTest(TestCase): if type == ENDMARKER: break type = tok_name[type] - result.append(" %(type)-10.10s %(token)-13.13r %(start)s %(end)s" % - locals()) + result.append(f" {type:10} {token!r:13} {start} {end}") self.assertEqual(result, [" ENCODING 'utf-8' (0, 0) (0, 0)"] + expected.rstrip().splitlines()) @@ -132,18 +131,18 @@ def k(x): self.check_tokenize("x = 0xfffffffffff", """\ NAME 'x' (1, 0) (1, 1) OP '=' (1, 2) (1, 3) - NUMBER '0xffffffffff (1, 4) (1, 17) + NUMBER '0xfffffffffff' (1, 4) (1, 17) """) self.check_tokenize("x = 123141242151251616110", """\ NAME 'x' (1, 0) (1, 1) OP '=' (1, 2) (1, 3) - NUMBER '123141242151 (1, 4) (1, 25) + NUMBER '123141242151251616110' (1, 4) (1, 25) """) self.check_tokenize("x = -15921590215012591", """\ NAME 'x' (1, 0) (1, 1) OP '=' (1, 2) (1, 3) OP '-' (1, 4) (1, 5) - NUMBER '159215902150 (1, 5) (1, 22) + NUMBER '15921590215012591' (1, 5) (1, 22) """) def test_float(self): @@ -307,6 +306,50 @@ def k(x): OP '+' (1, 28) (1, 29) STRING 'RB"abc"' (1, 30) (1, 37) """) + # Check 0, 1, and 2 character string prefixes. + self.check_tokenize(r'"a\ +de\ +fg"', """\ + STRING '"a\\\\\\nde\\\\\\nfg"\' (1, 0) (3, 3) + """) + self.check_tokenize(r'u"a\ +de"', """\ + STRING 'u"a\\\\\\nde"\' (1, 0) (2, 3) + """) + self.check_tokenize(r'rb"a\ +d"', """\ + STRING 'rb"a\\\\\\nd"\' (1, 0) (2, 2) + """) + self.check_tokenize(r'"""a\ +b"""', """\ + STRING '\"\""a\\\\\\nb\"\""' (1, 0) (2, 4) + """) + self.check_tokenize(r'u"""a\ +b"""', """\ + STRING 'u\"\""a\\\\\\nb\"\""' (1, 0) (2, 4) + """) + self.check_tokenize(r'rb"""a\ +b\ +c"""', """\ + STRING 'rb"\""a\\\\\\nb\\\\\\nc"\""' (1, 0) (3, 4) + """) + self.check_tokenize('f"abc"', """\ + STRING 'f"abc"' (1, 0) (1, 6) + """) + self.check_tokenize('fR"a{b}c"', """\ + STRING 'fR"a{b}c"' (1, 0) (1, 9) + """) + self.check_tokenize('f"""abc"""', """\ + STRING 'f\"\"\"abc\"\"\"' (1, 0) (1, 10) + """) + self.check_tokenize(r'f"abc\ +def"', """\ + STRING 'f"abc\\\\\\ndef"' (1, 0) (2, 4) + """) + self.check_tokenize(r'Rf"abc\ +def"', """\ + STRING 'Rf"abc\\\\\\ndef"' (1, 0) (2, 4) + """) def test_function(self): self.check_tokenize("def d22(a, b, c=2, d=2, *k): pass", """\ @@ -505,7 +548,7 @@ def k(x): # Methods self.check_tokenize("@staticmethod\ndef foo(x,y): pass", """\ OP '@' (1, 0) (1, 1) - NAME 'staticmethod (1, 1) (1, 13) + NAME 'staticmethod' (1, 1) (1, 13) NEWLINE '\\n' (1, 13) (1, 14) NAME 'def' (2, 0) (2, 3) NAME 'foo' (2, 4) (2, 7) diff --git a/Lib/test/test_tools/__init__.py b/Lib/test/test_tools/__init__.py index 04c8726..4d0fca3 100644 --- a/Lib/test/test_tools/__init__.py +++ b/Lib/test/test_tools/__init__.py @@ -3,7 +3,6 @@ import os import unittest import importlib from test import support -from fnmatch import fnmatch basepath = os.path.dirname( # <src/install dir> os.path.dirname( # Lib diff --git a/Lib/test/test_tools/test_gprof2html.py b/Lib/test/test_tools/test_gprof2html.py index 0c294ec..9489ed9 100644 --- a/Lib/test/test_tools/test_gprof2html.py +++ b/Lib/test/test_tools/test_gprof2html.py @@ -2,12 +2,11 @@ import os import sys -import importlib import unittest from unittest import mock import tempfile -from test.test_tools import scriptsdir, skip_if_missing, import_tool +from test.test_tools import skip_if_missing, import_tool skip_if_missing() diff --git a/Lib/test/test_tools/test_md5sum.py b/Lib/test/test_tools/test_md5sum.py index 1305295..fb565b7 100644 --- a/Lib/test/test_tools/test_md5sum.py +++ b/Lib/test/test_tools/test_md5sum.py @@ -1,12 +1,11 @@ """Tests for the md5sum script in the Tools directory.""" import os -import sys import unittest from test import support from test.support.script_helper import assert_python_ok, assert_python_failure -from test.test_tools import scriptsdir, import_tool, skip_if_missing +from test.test_tools import scriptsdir, skip_if_missing skip_if_missing() diff --git a/Lib/test/test_tools/test_pdeps.py b/Lib/test/test_tools/test_pdeps.py index 091fa6a..27cbfe2 100644 --- a/Lib/test/test_tools/test_pdeps.py +++ b/Lib/test/test_tools/test_pdeps.py @@ -1,12 +1,10 @@ """Tests for the pdeps script in the Tools directory.""" import os -import sys import unittest import tempfile -from test import support -from test.test_tools import scriptsdir, skip_if_missing, import_tool +from test.test_tools import skip_if_missing, import_tool skip_if_missing() diff --git a/Lib/test/test_tools/test_unparse.py b/Lib/test/test_tools/test_unparse.py index 734bbc2..517f261 100644 --- a/Lib/test/test_tools/test_unparse.py +++ b/Lib/test/test_tools/test_unparse.py @@ -134,6 +134,11 @@ class ASTTestCase(unittest.TestCase): class UnparseTestCase(ASTTestCase): # Tests for specific bugs found in earlier versions of unparse + def test_fstrings(self): + # See issue 25180 + self.check_roundtrip(r"""f'{f"{0}"*3}'""") + self.check_roundtrip(r"""f'{f"{y}"*3}'""") + def test_del_statement(self): self.check_roundtrip("del x, y, z") @@ -279,6 +284,15 @@ class DirectoryTestCase(ASTTestCase): for filename in names: if test.support.verbose: print('Testing %s' % filename) + + # it's very much a hack that I'm skipping these files, but + # I can't figure out why they fail. I'll fix it when I + # address issue #27948. + if os.path.basename(filename) in ('test_fstring.py', 'test_traceback.py'): + if test.support.verbose: + print(f'Skipping {filename}: see issue 27921') + continue + source = read_pyfile(filename) self.check_roundtrip(source) diff --git a/Lib/test/test_trace.py b/Lib/test/test_trace.py index 03dff84..1d894aa 100644 --- a/Lib/test/test_trace.py +++ b/Lib/test/test_trace.py @@ -1,11 +1,11 @@ import os -import io import sys from test.support import TESTFN, rmtree, unlink, captured_stdout +from test.support.script_helper import assert_python_ok, assert_python_failure import unittest import trace -from trace import CoverageResults, Trace +from trace import Trace from test.tracedmodules import testmod @@ -365,51 +365,27 @@ class Test_Ignore(unittest.TestCase): # Matched before. self.assertTrue(ignore.names(jn('bar', 'baz.py'), 'baz')) - -class TestDeprecatedMethods(unittest.TestCase): - - def test_deprecated_usage(self): - sio = io.StringIO() - with self.assertWarns(DeprecationWarning): - trace.usage(sio) - self.assertIn('Usage:', sio.getvalue()) - - def test_deprecated_Ignore(self): - with self.assertWarns(DeprecationWarning): - trace.Ignore() - - def test_deprecated_modname(self): - with self.assertWarns(DeprecationWarning): - self.assertEqual("spam", trace.modname("spam")) - - def test_deprecated_fullmodname(self): - with self.assertWarns(DeprecationWarning): - self.assertEqual("spam", trace.fullmodname("spam")) - - def test_deprecated_find_lines_from_code(self): - with self.assertWarns(DeprecationWarning): - def foo(): - pass - trace.find_lines_from_code(foo.__code__, ["eggs"]) - - def test_deprecated_find_lines(self): - with self.assertWarns(DeprecationWarning): - def foo(): - pass - trace.find_lines(foo.__code__, ["eggs"]) - - def test_deprecated_find_strings(self): - with open(TESTFN, 'w') as fd: - self.addCleanup(unlink, TESTFN) - with self.assertWarns(DeprecationWarning): - trace.find_strings(fd.name) - - def test_deprecated_find_executable_linenos(self): +class TestCommandLine(unittest.TestCase): + + def test_failures(self): + _errors = ( + (b'filename is missing: required with the main options', '-l', '-T'), + (b'cannot specify both --listfuncs and (--trace or --count)', '-lc'), + (b'argument -R/--no-report: not allowed with argument -r/--report', '-rR'), + (b'must specify one of --trace, --count, --report, --listfuncs, or --trackcalls', '-g'), + (b'-r/--report requires -f/--file', '-r'), + (b'--summary can only be used with --count or --report', '-sT'), + (b'unrecognized arguments: -y', '-y')) + for message, *args in _errors: + *_, stderr = assert_python_failure('-m', 'trace', *args) + self.assertIn(message, stderr) + + def test_listfuncs_flag_success(self): with open(TESTFN, 'w') as fd: self.addCleanup(unlink, TESTFN) - with self.assertWarns(DeprecationWarning): - trace.find_executable_linenos(fd.name) - + fd.write("a = 1\n") + status, stdout, stderr = assert_python_ok('-m', 'trace', '-l', TESTFN) + self.assertIn(b'functions called:', stdout) if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 549d8d1..fc7e6cc 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -129,12 +129,12 @@ class SyntaxTracebackCases(unittest.TestCase): def do_test(firstlines, message, charset, lineno): # Raise the message in a subprocess, and catch the output try: - output = open(TESTFN, "w", encoding=charset) - output.write("""{0}if 1: - import traceback; - raise RuntimeError('{1}') - """.format(firstlines, message)) - output.close() + with open(TESTFN, "w", encoding=charset) as output: + output.write("""{0}if 1: + import traceback; + raise RuntimeError('{1}') + """.format(firstlines, message)) + process = subprocess.Popen([sys.executable, TESTFN], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout, stderr = process.communicate() @@ -175,8 +175,8 @@ class SyntaxTracebackCases(unittest.TestCase): text, charset, 5) do_test(" \t\f\n# coding: {0}\n".format(charset), text, charset, 5) - # Issue #18960: coding spec should has no effect - do_test("0\n# coding: GBK\n", "h\xe9 ho", 'utf-8', 5) + # Issue #18960: coding spec should have no effect + do_test("x=0\n# coding: GBK\n", "h\xe9 ho", 'utf-8', 5) @support.requires_type_collecting def test_print_traceback_at_exit(self): @@ -303,6 +303,137 @@ class TracebackFormatTests(unittest.TestCase): ' traceback.print_stack()', ]) + # issue 26823 - Shrink recursive tracebacks + def _check_recursive_traceback_display(self, render_exc): + # Always show full diffs when this test fails + # Note that rearranging things may require adjusting + # the relative line numbers in the expected tracebacks + self.maxDiff = None + + # Check hitting the recursion limit + def f(): + f() + + with captured_output("stderr") as stderr_f: + try: + f() + except RecursionError as exc: + render_exc() + else: + self.fail("no recursion occurred") + + lineno_f = f.__code__.co_firstlineno + result_f = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display''\n' + ' f()\n' + f' File "{__file__}", line {lineno_f+1}, in f''\n' + ' f()\n' + f' File "{__file__}", line {lineno_f+1}, in f''\n' + ' f()\n' + f' File "{__file__}", line {lineno_f+1}, in f''\n' + ' f()\n' + # XXX: The following line changes depending on whether the tests + # are run through the interactive interpreter or with -m + # It also varies depending on the platform (stack size) + # Fortunately, we don't care about exactness here, so we use regex + r' \[Previous line repeated (\d+) more times\]' '\n' + 'RecursionError: maximum recursion depth exceeded\n' + ) + + expected = result_f.splitlines() + actual = stderr_f.getvalue().splitlines() + + # Check the output text matches expectations + # 2nd last line contains the repetition count + self.assertEqual(actual[:-2], expected[:-2]) + self.assertRegex(actual[-2], expected[-2]) + self.assertEqual(actual[-1], expected[-1]) + + # Check the recursion count is roughly as expected + rec_limit = sys.getrecursionlimit() + self.assertIn(int(re.search(r"\d+", actual[-2]).group()), range(rec_limit-50, rec_limit)) + + # Check a known (limited) number of recursive invocations + def g(count=10): + if count: + return g(count-1) + raise ValueError + + with captured_output("stderr") as stderr_g: + try: + g() + except ValueError as exc: + render_exc() + else: + self.fail("no value error was raised") + + lineno_g = g.__code__.co_firstlineno + result_g = ( + f' File "{__file__}", line {lineno_g+2}, in g''\n' + ' return g(count-1)\n' + f' File "{__file__}", line {lineno_g+2}, in g''\n' + ' return g(count-1)\n' + f' File "{__file__}", line {lineno_g+2}, in g''\n' + ' return g(count-1)\n' + ' [Previous line repeated 6 more times]\n' + f' File "{__file__}", line {lineno_g+3}, in g''\n' + ' raise ValueError\n' + 'ValueError\n' + ) + tb_line = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {lineno_g+7}, in _check_recursive_traceback_display''\n' + ' g()\n' + ) + expected = (tb_line + result_g).splitlines() + actual = stderr_g.getvalue().splitlines() + self.assertEqual(actual, expected) + + # Check 2 different repetitive sections + def h(count=10): + if count: + return h(count-1) + g() + + with captured_output("stderr") as stderr_h: + try: + h() + except ValueError as exc: + render_exc() + else: + self.fail("no value error was raised") + + lineno_h = h.__code__.co_firstlineno + result_h = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {lineno_h+7}, in _check_recursive_traceback_display''\n' + ' h()\n' + f' File "{__file__}", line {lineno_h+2}, in h''\n' + ' return h(count-1)\n' + f' File "{__file__}", line {lineno_h+2}, in h''\n' + ' return h(count-1)\n' + f' File "{__file__}", line {lineno_h+2}, in h''\n' + ' return h(count-1)\n' + ' [Previous line repeated 6 more times]\n' + f' File "{__file__}", line {lineno_h+3}, in h''\n' + ' g()\n' + ) + expected = (result_h + result_g).splitlines() + actual = stderr_h.getvalue().splitlines() + self.assertEqual(actual, expected) + + def test_recursive_traceback_python(self): + self._check_recursive_traceback_display(traceback.print_exc) + + @cpython_only + def test_recursive_traceback_cpython_internal(self): + from _testcapi import exception_print + def render_exc(): + exc_type, exc_value, exc_tb = sys.exc_info() + exception_print(exc_value) + self._check_recursive_traceback_display(render_exc) + def test_format_stack(self): def fmt(): return traceback.format_stack() diff --git a/Lib/test/test_tracemalloc.py b/Lib/test/test_tracemalloc.py index da89a9a..790ab7e 100644 --- a/Lib/test/test_tracemalloc.py +++ b/Lib/test/test_tracemalloc.py @@ -11,9 +11,15 @@ try: import threading except ImportError: threading = None +try: + import _testcapi +except ImportError: + _testcapi = None + EMPTY_STRING_SIZE = sys.getsizeof(b'') + def get_frames(nframe, lineno_delta): frames = [] frame = sys._getframe(1) @@ -37,28 +43,31 @@ def allocate_bytes(size): def create_snapshots(): traceback_limit = 2 + # _tracemalloc._get_traces() returns a list of (domain, size, + # traceback_frames) tuples. traceback_frames is a tuple of (filename, + # line_number) tuples. raw_traces = [ - (10, (('a.py', 2), ('b.py', 4))), - (10, (('a.py', 2), ('b.py', 4))), - (10, (('a.py', 2), ('b.py', 4))), + (0, 10, (('a.py', 2), ('b.py', 4))), + (0, 10, (('a.py', 2), ('b.py', 4))), + (0, 10, (('a.py', 2), ('b.py', 4))), - (2, (('a.py', 5), ('b.py', 4))), + (1, 2, (('a.py', 5), ('b.py', 4))), - (66, (('b.py', 1),)), + (2, 66, (('b.py', 1),)), - (7, (('<unknown>', 0),)), + (3, 7, (('<unknown>', 0),)), ] snapshot = tracemalloc.Snapshot(raw_traces, traceback_limit) raw_traces2 = [ - (10, (('a.py', 2), ('b.py', 4))), - (10, (('a.py', 2), ('b.py', 4))), - (10, (('a.py', 2), ('b.py', 4))), + (0, 10, (('a.py', 2), ('b.py', 4))), + (0, 10, (('a.py', 2), ('b.py', 4))), + (0, 10, (('a.py', 2), ('b.py', 4))), - (2, (('a.py', 5), ('b.py', 4))), - (5000, (('a.py', 5), ('b.py', 4))), + (2, 2, (('a.py', 5), ('b.py', 4))), + (2, 5000, (('a.py', 5), ('b.py', 4))), - (400, (('c.py', 578),)), + (4, 400, (('c.py', 578),)), ] snapshot2 = tracemalloc.Snapshot(raw_traces2, traceback_limit) @@ -126,7 +135,7 @@ class TestTracemallocEnabled(unittest.TestCase): def find_trace(self, traces, traceback): for trace in traces: - if trace[1] == traceback._frames: + if trace[2] == traceback._frames: return trace self.fail("trace not found") @@ -140,7 +149,7 @@ class TestTracemallocEnabled(unittest.TestCase): trace = self.find_trace(traces, obj_traceback) self.assertIsInstance(trace, tuple) - size, traceback = trace + domain, size, traceback = trace self.assertEqual(size, obj_size) self.assertEqual(traceback, obj_traceback._frames) @@ -167,9 +176,8 @@ class TestTracemallocEnabled(unittest.TestCase): trace1 = self.find_trace(traces, obj1_traceback) trace2 = self.find_trace(traces, obj2_traceback) - size1, traceback1 = trace1 - size2, traceback2 = trace2 - self.assertEqual(traceback2, traceback1) + domain1, size1, traceback1 = trace1 + domain2, size2, traceback2 = trace2 self.assertIs(traceback2, traceback1) def test_get_traced_memory(self): @@ -292,7 +300,7 @@ class TestSnapshot(unittest.TestCase): maxDiff = 4000 def test_create_snapshot(self): - raw_traces = [(5, (('a.py', 2),))] + raw_traces = [(0, 5, (('a.py', 2),))] with contextlib.ExitStack() as stack: stack.enter_context(patch.object(tracemalloc, 'is_tracing', @@ -322,11 +330,11 @@ class TestSnapshot(unittest.TestCase): # exclude b.py snapshot3 = snapshot.filter_traces((filter1,)) self.assertEqual(snapshot3.traces._traces, [ - (10, (('a.py', 2), ('b.py', 4))), - (10, (('a.py', 2), ('b.py', 4))), - (10, (('a.py', 2), ('b.py', 4))), - (2, (('a.py', 5), ('b.py', 4))), - (7, (('<unknown>', 0),)), + (0, 10, (('a.py', 2), ('b.py', 4))), + (0, 10, (('a.py', 2), ('b.py', 4))), + (0, 10, (('a.py', 2), ('b.py', 4))), + (1, 2, (('a.py', 5), ('b.py', 4))), + (3, 7, (('<unknown>', 0),)), ]) # filter_traces() must not touch the original snapshot @@ -335,10 +343,10 @@ class TestSnapshot(unittest.TestCase): # only include two lines of a.py snapshot4 = snapshot3.filter_traces((filter2, filter3)) self.assertEqual(snapshot4.traces._traces, [ - (10, (('a.py', 2), ('b.py', 4))), - (10, (('a.py', 2), ('b.py', 4))), - (10, (('a.py', 2), ('b.py', 4))), - (2, (('a.py', 5), ('b.py', 4))), + (0, 10, (('a.py', 2), ('b.py', 4))), + (0, 10, (('a.py', 2), ('b.py', 4))), + (0, 10, (('a.py', 2), ('b.py', 4))), + (1, 2, (('a.py', 5), ('b.py', 4))), ]) # No filter: just duplicate the snapshot @@ -349,6 +357,54 @@ class TestSnapshot(unittest.TestCase): self.assertRaises(TypeError, snapshot.filter_traces, filter1) + def test_filter_traces_domain(self): + snapshot, snapshot2 = create_snapshots() + filter1 = tracemalloc.Filter(False, "a.py", domain=1) + filter2 = tracemalloc.Filter(True, "a.py", domain=1) + + original_traces = list(snapshot.traces._traces) + + # exclude a.py of domain 1 + snapshot3 = snapshot.filter_traces((filter1,)) + self.assertEqual(snapshot3.traces._traces, [ + (0, 10, (('a.py', 2), ('b.py', 4))), + (0, 10, (('a.py', 2), ('b.py', 4))), + (0, 10, (('a.py', 2), ('b.py', 4))), + (2, 66, (('b.py', 1),)), + (3, 7, (('<unknown>', 0),)), + ]) + + # include domain 1 + snapshot3 = snapshot.filter_traces((filter1,)) + self.assertEqual(snapshot3.traces._traces, [ + (0, 10, (('a.py', 2), ('b.py', 4))), + (0, 10, (('a.py', 2), ('b.py', 4))), + (0, 10, (('a.py', 2), ('b.py', 4))), + (2, 66, (('b.py', 1),)), + (3, 7, (('<unknown>', 0),)), + ]) + + def test_filter_traces_domain_filter(self): + snapshot, snapshot2 = create_snapshots() + filter1 = tracemalloc.DomainFilter(False, domain=3) + filter2 = tracemalloc.DomainFilter(True, domain=3) + + # exclude domain 2 + snapshot3 = snapshot.filter_traces((filter1,)) + self.assertEqual(snapshot3.traces._traces, [ + (0, 10, (('a.py', 2), ('b.py', 4))), + (0, 10, (('a.py', 2), ('b.py', 4))), + (0, 10, (('a.py', 2), ('b.py', 4))), + (1, 2, (('a.py', 5), ('b.py', 4))), + (2, 66, (('b.py', 1),)), + ]) + + # include domain 2 + snapshot3 = snapshot.filter_traces((filter2,)) + self.assertEqual(snapshot3.traces._traces, [ + (3, 7, (('<unknown>', 0),)), + ]) + def test_snapshot_group_by_line(self): snapshot, snapshot2 = create_snapshots() tb_0 = traceback_lineno('<unknown>', 0) @@ -816,12 +872,121 @@ class TestCommandLine(unittest.TestCase): assert_python_ok('-X', 'tracemalloc', '-c', code) +@unittest.skipIf(_testcapi is None, 'need _testcapi') +class TestCAPI(unittest.TestCase): + maxDiff = 80 * 20 + + def setUp(self): + if tracemalloc.is_tracing(): + self.skipTest("tracemalloc must be stopped before the test") + + self.domain = 5 + self.size = 123 + self.obj = allocate_bytes(self.size)[0] + + # for the type "object", id(obj) is the address of its memory block. + # This type is not tracked by the garbage collector + self.ptr = id(self.obj) + + def tearDown(self): + tracemalloc.stop() + + def get_traceback(self): + frames = _testcapi.tracemalloc_get_traceback(self.domain, self.ptr) + if frames is not None: + return tracemalloc.Traceback(frames) + else: + return None + + def track(self, release_gil=False, nframe=1): + frames = get_frames(nframe, 2) + _testcapi.tracemalloc_track(self.domain, self.ptr, self.size, + release_gil) + return frames + + def untrack(self): + _testcapi.tracemalloc_untrack(self.domain, self.ptr) + + def get_traced_memory(self): + # Get the traced size in the domain + snapshot = tracemalloc.take_snapshot() + domain_filter = tracemalloc.DomainFilter(True, self.domain) + snapshot = snapshot.filter_traces([domain_filter]) + return sum(trace.size for trace in snapshot.traces) + + def check_track(self, release_gil): + nframe = 5 + tracemalloc.start(nframe) + + size = tracemalloc.get_traced_memory()[0] + + frames = self.track(release_gil, nframe) + self.assertEqual(self.get_traceback(), + tracemalloc.Traceback(frames)) + + self.assertEqual(self.get_traced_memory(), self.size) + + def test_track(self): + self.check_track(False) + + def test_track_without_gil(self): + # check that calling _PyTraceMalloc_Track() without holding the GIL + # works too + self.check_track(True) + + def test_track_already_tracked(self): + nframe = 5 + tracemalloc.start(nframe) + + # track a first time + self.track() + + # calling _PyTraceMalloc_Track() must remove the old trace and add + # a new trace with the new traceback + frames = self.track(nframe=nframe) + self.assertEqual(self.get_traceback(), + tracemalloc.Traceback(frames)) + + def test_untrack(self): + tracemalloc.start() + + self.track() + self.assertIsNotNone(self.get_traceback()) + self.assertEqual(self.get_traced_memory(), self.size) + + # untrack must remove the trace + self.untrack() + self.assertIsNone(self.get_traceback()) + self.assertEqual(self.get_traced_memory(), 0) + + # calling _PyTraceMalloc_Untrack() multiple times must not crash + self.untrack() + self.untrack() + + def test_stop_track(self): + tracemalloc.start() + tracemalloc.stop() + + with self.assertRaises(RuntimeError): + self.track() + self.assertIsNone(self.get_traceback()) + + def test_stop_untrack(self): + tracemalloc.start() + self.track() + + tracemalloc.stop() + with self.assertRaises(RuntimeError): + self.untrack() + + def test_main(): support.run_unittest( TestTracemallocEnabled, TestSnapshot, TestFilters, TestCommandLine, + TestCAPI, ) if __name__ == "__main__": diff --git a/Lib/test/test_ttk_guionly.py b/Lib/test/test_ttk_guionly.py index 490e723..462665d 100644 --- a/Lib/test/test_ttk_guionly.py +++ b/Lib/test/test_ttk_guionly.py @@ -1,4 +1,3 @@ -import os import unittest from test import support diff --git a/Lib/test/test_ttk_textonly.py b/Lib/test/test_ttk_textonly.py index 566fc9d..7540a431 100644 --- a/Lib/test/test_ttk_textonly.py +++ b/Lib/test/test_ttk_textonly.py @@ -1,4 +1,3 @@ -import os from test import support # Skip this test if _tkinter does not exist. diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index 5e74115..e5e110f 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -825,6 +825,28 @@ class ClassCreationTests(unittest.TestCase): self.assertEqual(C.y, 1) self.assertEqual(C.z, 2) + def test_new_class_deforder(self): + C = types.new_class("C") + self.assertEqual(C.__definition_order__, tuple()) + + Meta = self.Meta + def func(ns): + ns["x"] = 0 + D = types.new_class("D", (), {"metaclass": Meta, "z": 2}, func) + self.assertEqual(D.__definition_order__, ('y', 'z', 'x')) + + def func(ns): + ns["__definition_order__"] = None + ns["x"] = 0 + D = types.new_class("D", (), {"metaclass": Meta, "z": 2}, func) + self.assertEqual(D.__definition_order__, None) + + def func(ns): + ns["__definition_order__"] = ('a', 'b', 'c') + ns["x"] = 0 + D = types.new_class("D", (), {"metaclass": Meta, "z": 2}, func) + self.assertEqual(D.__definition_order__, ('a', 'b', 'c')) + # Many of the following tests are derived from test_descr.py def test_prepare_class(self): # Basic test of metaclass derivation @@ -1000,6 +1022,24 @@ class ClassCreationTests(unittest.TestCase): with self.assertRaises(TypeError): X = types.new_class("X", (int(), C)) + def test_one_argument_type(self): + expected_message = 'type.__new__() takes exactly 3 arguments (1 given)' + + # Only type itself can use the one-argument form (#27157) + self.assertIs(type(5), int) + + class M(type): + pass + with self.assertRaises(TypeError) as cm: + M(5) + self.assertEqual(str(cm.exception), expected_message) + + class N(type, metaclass=M): + pass + with self.assertRaises(TypeError) as cm: + N(5) + self.assertEqual(str(cm.exception), expected_message) + class SimpleNamespaceTests(unittest.TestCase): diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py index a38e7b1..78f9668 100644 --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -986,6 +986,19 @@ class UnicodeTest(string_tests.CommonTest, def __format__(self, format_spec): return int.__format__(self * 2, format_spec) + class M: + def __init__(self, x): + self.x = x + def __repr__(self): + return 'M(' + self.x + ')' + __str__ = None + + class N: + def __init__(self, x): + self.x = x + def __repr__(self): + return 'N(' + self.x + ')' + __format__ = None self.assertEqual(''.format(), '') self.assertEqual('abc'.format(), 'abc') @@ -1200,6 +1213,16 @@ class UnicodeTest(string_tests.CommonTest, self.assertEqual("0x{:0{:d}X}".format(0x0,16), "0x0000000000000000") + # Blocking fallback + m = M('data') + self.assertEqual("{!r}".format(m), 'M(data)') + self.assertRaises(TypeError, "{!s}".format, m) + self.assertRaises(TypeError, "{}".format, m) + n = N('data') + self.assertEqual("{!r}".format(n), 'N(data)') + self.assertEqual("{!s}".format(n), 'N(data)') + self.assertRaises(TypeError, "{}".format, n) + def test_format_map(self): self.assertEqual(''.format_map({}), '') self.assertEqual('a'.format_map({}), 'a') diff --git a/Lib/test/test_unpack.py b/Lib/test/test_unpack.py index d1ccb38..3fcb18f 100644 --- a/Lib/test/test_unpack.py +++ b/Lib/test/test_unpack.py @@ -117,6 +117,27 @@ error) ... test.test_unpack.BozoError +Allow unpacking empty iterables + + >>> () = [] + >>> [] = () + >>> [] = [] + >>> () = () + +Unpacking non-iterables should raise TypeError + + >>> () = 42 + Traceback (most recent call last): + ... + TypeError: 'int' object is not iterable + +Unpacking to an empty iterable should raise ValueError + + >>> () = [42] + Traceback (most recent call last): + ... + ValueError: too many values to unpack (expected 0) + """ __test__ = {'doctests' : doctests} diff --git a/Lib/test/test_unpack_ex.py b/Lib/test/test_unpack_ex.py index 74346b4..6be8f55 100644 --- a/Lib/test/test_unpack_ex.py +++ b/Lib/test/test_unpack_ex.py @@ -357,7 +357,6 @@ Some size constraints (all fail.) __test__ = {'doctests' : doctests} def test_main(verbose=False): - import sys from test import support from test import test_unpack_ex support.run_doctest(test_unpack_ex, verbose) diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py index c26c52a..247598a 100644 --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -1,4 +1,4 @@ -"""Regresssion tests for what was in Python 2's "urllib" module""" +"""Regression tests for what was in Python 2's "urllib" module""" import urllib.parse import urllib.request diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py index eda7ccc..34329f8 100644 --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -7,6 +7,8 @@ import io import socket import array import sys +import tempfile +import subprocess import urllib.request # The proxy bypass method imported below has logic specific to the OSX @@ -335,7 +337,8 @@ class MockHTTPClass: else: self._tunnel_headers.clear() - def request(self, method, url, body=None, headers=None): + def request(self, method, url, body=None, headers=None, *, + encode_chunked=False): self.method = method self.selector = url if headers is not None: @@ -343,6 +346,7 @@ class MockHTTPClass: self.req_headers.sort() if body: self.data = body + self.encode_chunked = encode_chunked if self.raise_on_endheaders: raise OSError() @@ -908,41 +912,110 @@ class HandlerTests(unittest.TestCase): self.assertEqual(req.unredirected_hdrs["Host"], "baz") self.assertEqual(req.unredirected_hdrs["Spam"], "foo") - # Check iterable body support - def iterable_body(): - yield b"one" - yield b"two" - yield b"three" + def test_http_body_file(self): + # A regular file - chunked encoding is used unless Content Length is + # already set. - for headers in {}, {"Content-Length": 11}: - req = Request("http://example.com/", iterable_body(), headers) - if not headers: - # Having an iterable body without a Content-Length should - # raise an exception - self.assertRaises(ValueError, h.do_request_, req) - else: - newreq = h.do_request_(req) + h = urllib.request.AbstractHTTPHandler() + o = h.parent = MockOpener() - # A file object. - # Test only Content-Length attribute of request. + file_obj = tempfile.NamedTemporaryFile(mode='w+b', delete=False) + file_path = file_obj.name + file_obj.close() + self.addCleanup(os.unlink, file_path) + + with open(file_path, "rb") as f: + req = Request("http://example.com/", f, {}) + newreq = h.do_request_(req) + te = newreq.get_header('Transfer-encoding') + self.assertEqual(te, "chunked") + self.assertFalse(newreq.has_header('Content-length')) + with open(file_path, "rb") as f: + req = Request("http://example.com/", f, {"Content-Length": 30}) + newreq = h.do_request_(req) + self.assertEqual(int(newreq.get_header('Content-length')), 30) + self.assertFalse(newreq.has_header("Transfer-encoding")) + + def test_http_body_fileobj(self): + # A file object - chunked encoding is used + # unless Content Length is already set. + # (Note that there are some subtle differences to a regular + # file, that is why we are testing both cases.) + + h = urllib.request.AbstractHTTPHandler() + o = h.parent = MockOpener() file_obj = io.BytesIO() - file_obj.write(b"Something\nSomething\nSomething\n") + req = Request("http://example.com/", file_obj, {}) + newreq = h.do_request_(req) + self.assertEqual(newreq.get_header('Transfer-encoding'), 'chunked') + self.assertFalse(newreq.has_header('Content-length')) + + headers = {"Content-Length": 30} + req = Request("http://example.com/", file_obj, headers) + newreq = h.do_request_(req) + self.assertEqual(int(newreq.get_header('Content-length')), 30) + self.assertFalse(newreq.has_header("Transfer-encoding")) + + file_obj.close() + + def test_http_body_pipe(self): + # A file reading from a pipe. + # A pipe cannot be seek'ed. There is no way to determine the + # content length up front. Thus, do_request_() should fall + # back to Transfer-encoding chunked. + + h = urllib.request.AbstractHTTPHandler() + o = h.parent = MockOpener() + + cmd = [sys.executable, "-c", r"pass"] for headers in {}, {"Content-Length": 30}: - req = Request("http://example.com/", file_obj, headers) + with subprocess.Popen(cmd, stdout=subprocess.PIPE) as proc: + req = Request("http://example.com/", proc.stdout, headers) + newreq = h.do_request_(req) + if not headers: + self.assertEqual(newreq.get_header('Content-length'), None) + self.assertEqual(newreq.get_header('Transfer-encoding'), + 'chunked') + else: + self.assertEqual(int(newreq.get_header('Content-length')), + 30) + + def test_http_body_iterable(self): + # Generic iterable. There is no way to determine the content + # length up front. Fall back to Transfer-encoding chunked. + + h = urllib.request.AbstractHTTPHandler() + o = h.parent = MockOpener() + + def iterable_body(): + yield b"one" + + for headers in {}, {"Content-Length": 11}: + req = Request("http://example.com/", iterable_body(), headers) + newreq = h.do_request_(req) if not headers: - # Having an iterable body without a Content-Length should - # raise an exception - self.assertRaises(ValueError, h.do_request_, req) + self.assertEqual(newreq.get_header('Content-length'), None) + self.assertEqual(newreq.get_header('Transfer-encoding'), + 'chunked') else: - newreq = h.do_request_(req) - self.assertEqual(int(newreq.get_header('Content-length')), 30) + self.assertEqual(int(newreq.get_header('Content-length')), 11) - file_obj.close() + def test_http_body_empty_seq(self): + # Zero-length iterable body should be treated like any other iterable + h = urllib.request.AbstractHTTPHandler() + h.parent = MockOpener() + req = h.do_request_(Request("http://example.com/", ())) + self.assertEqual(req.get_header("Transfer-encoding"), "chunked") + self.assertFalse(req.has_header("Content-length")) + def test_http_body_array(self): # array.array Iterable - Content Length is calculated + h = urllib.request.AbstractHTTPHandler() + o = h.parent = MockOpener() + iterable_array = array.array("I",[1,2,3,4]) for headers in {}, {"Content-Length": 16}: diff --git a/Lib/test/test_urllib2_localnet.py b/Lib/test/test_urllib2_localnet.py index c8b37ee..e9564fd 100644 --- a/Lib/test/test_urllib2_localnet.py +++ b/Lib/test/test_urllib2_localnet.py @@ -289,12 +289,12 @@ class BasicAuthTests(unittest.TestCase): def http_server_with_basic_auth_handler(*args, **kwargs): return BasicAuthHandler(*args, **kwargs) self.server = LoopbackHttpServerThread(http_server_with_basic_auth_handler) + self.addCleanup(self.server.stop) self.server_url = 'http://127.0.0.1:%s' % self.server.port self.server.start() self.server.ready.wait() def tearDown(self): - self.server.stop() super(BasicAuthTests, self).tearDown() def test_basic_auth_success(self): @@ -438,17 +438,13 @@ class TestUrlopen(unittest.TestCase): def setUp(self): super(TestUrlopen, self).setUp() + # Ignore proxies for localhost tests. - self.old_environ = os.environ.copy() + def restore_environ(old_environ): + os.environ.clear() + os.environ.update(old_environ) + self.addCleanup(restore_environ, os.environ.copy()) os.environ['NO_PROXY'] = '*' - self.server = None - - def tearDown(self): - if self.server is not None: - self.server.stop() - os.environ.clear() - os.environ.update(self.old_environ) - super(TestUrlopen, self).tearDown() def urlopen(self, url, data=None, **kwargs): l = [] @@ -469,6 +465,7 @@ class TestUrlopen(unittest.TestCase): handler = GetRequestHandler(responses) self.server = LoopbackHttpServerThread(handler) + self.addCleanup(self.server.stop) self.server.start() self.server.ready.wait() port = self.server.port @@ -592,7 +589,8 @@ class TestUrlopen(unittest.TestCase): handler = self.start_server() req = urllib.request.Request("http://localhost:%s/" % handler.port, headers={"Range": "bytes=20-39"}) - urllib.request.urlopen(req) + with urllib.request.urlopen(req): + pass self.assertEqual(handler.headers_received["Range"], "bytes=20-39") def test_basic(self): @@ -608,22 +606,21 @@ class TestUrlopen(unittest.TestCase): def test_info(self): handler = self.start_server() - try: - open_url = urllib.request.urlopen( - "http://localhost:%s" % handler.port) + open_url = urllib.request.urlopen( + "http://localhost:%s" % handler.port) + with open_url: info_obj = open_url.info() - self.assertIsInstance(info_obj, email.message.Message, - "object returned by 'info' is not an " - "instance of email.message.Message") - self.assertEqual(info_obj.get_content_subtype(), "plain") - finally: - self.server.stop() + self.assertIsInstance(info_obj, email.message.Message, + "object returned by 'info' is not an " + "instance of email.message.Message") + self.assertEqual(info_obj.get_content_subtype(), "plain") def test_geturl(self): # Make sure same URL as opened is returned by geturl. handler = self.start_server() open_url = urllib.request.urlopen("http://localhost:%s" % handler.port) - url = open_url.geturl() + with open_url: + url = open_url.geturl() self.assertEqual(url, "http://localhost:%s" % handler.port) def test_iteration(self): diff --git a/Lib/test/test_urllibnet.py b/Lib/test/test_urllibnet.py index b811930..949716c 100644 --- a/Lib/test/test_urllibnet.py +++ b/Lib/test/test_urllibnet.py @@ -4,7 +4,6 @@ from test import support import contextlib import socket import urllib.request -import sys import os import email.message import time diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py index 829997f..9165d73 100644 --- a/Lib/test/test_urlparse.py +++ b/Lib/test/test_urlparse.py @@ -605,29 +605,27 @@ class UrlParseTestCase(unittest.TestCase): self.assertEqual(p.port, 80) self.assertEqual(p.geturl(), url) - # Verify an illegal port is returned as None + # Verify an illegal port raises ValueError url = b"HTTP://WWW.PYTHON.ORG:65536/doc/#frag" p = urllib.parse.urlsplit(url) - self.assertEqual(p.port, None) + with self.assertRaisesRegex(ValueError, "out of range"): + p.port def test_attributes_bad_port(self): - """Check handling of non-integer ports.""" - p = urllib.parse.urlsplit("http://www.example.net:foo") - self.assertEqual(p.netloc, "www.example.net:foo") - self.assertRaises(ValueError, lambda: p.port) - - p = urllib.parse.urlparse("http://www.example.net:foo") - self.assertEqual(p.netloc, "www.example.net:foo") - self.assertRaises(ValueError, lambda: p.port) - - # Once again, repeat ourselves to test bytes - p = urllib.parse.urlsplit(b"http://www.example.net:foo") - self.assertEqual(p.netloc, b"www.example.net:foo") - self.assertRaises(ValueError, lambda: p.port) - - p = urllib.parse.urlparse(b"http://www.example.net:foo") - self.assertEqual(p.netloc, b"www.example.net:foo") - self.assertRaises(ValueError, lambda: p.port) + """Check handling of invalid ports.""" + for bytes in (False, True): + for parse in (urllib.parse.urlsplit, urllib.parse.urlparse): + for port in ("foo", "1.5", "-1", "0x10"): + with self.subTest(bytes=bytes, parse=parse, port=port): + netloc = "www.example.net:" + port + url = "http://" + netloc + if bytes: + netloc = netloc.encode("ascii") + url = url.encode("ascii") + p = parse(url) + self.assertEqual(p.netloc, netloc) + with self.assertRaises(ValueError): + p.port def test_attributes_without_netloc(self): # This example is straight from RFC 3261. It looks like it diff --git a/Lib/test/test_userdict.py b/Lib/test/test_userdict.py index 8357f8b..662c7f6 100644 --- a/Lib/test/test_userdict.py +++ b/Lib/test/test_userdict.py @@ -1,6 +1,6 @@ # Check every path through every method of UserDict -from test import support, mapping_tests +from test import mapping_tests import unittest import collections @@ -30,7 +30,7 @@ class UserDictTest(mapping_tests.TestHashMappingProtocol): self.assertEqual(collections.UserDict(one=1, two=2), d2) # item sequence constructor self.assertEqual(collections.UserDict([('one',1), ('two',2)]), d2) - with self.assertWarnsRegex(PendingDeprecationWarning, "'dict'"): + with self.assertWarnsRegex(DeprecationWarning, "'dict'"): self.assertEqual(collections.UserDict(dict=[('one',1), ('two',2)]), d2) # both together self.assertEqual(collections.UserDict([('one',1), ('two',2)], two=3, three=5), d3) @@ -149,7 +149,7 @@ class UserDictTest(mapping_tests.TestHashMappingProtocol): [('dict', 42)]) self.assertEqual(list(collections.UserDict({}, dict=None).items()), [('dict', None)]) - with self.assertWarnsRegex(PendingDeprecationWarning, "'dict'"): + with self.assertWarnsRegex(DeprecationWarning, "'dict'"): self.assertEqual(list(collections.UserDict(dict={'a': 42}).items()), [('a', 42)]) self.assertRaises(TypeError, collections.UserDict, 42) diff --git a/Lib/test/test_userlist.py b/Lib/test/test_userlist.py index f92e4d3..8de6c14 100644 --- a/Lib/test/test_userlist.py +++ b/Lib/test/test_userlist.py @@ -1,7 +1,7 @@ # Check every path through every method of UserList from collections import UserList -from test import support, list_tests +from test import list_tests import unittest class UserListTest(list_tests.CommonTest): diff --git a/Lib/test/test_userstring.py b/Lib/test/test_userstring.py index 9bc8edd..7152822 100644 --- a/Lib/test/test_userstring.py +++ b/Lib/test/test_userstring.py @@ -1,9 +1,8 @@ # UserString is a wrapper around the native builtin string type. # UserString instances should behave similar to builtin string objects. -import string import unittest -from test import support, string_tests +from test import string_tests from collections import UserString diff --git a/Lib/test/test_uu.py b/Lib/test/test_uu.py index 25fffbf..ad2f2c5 100644 --- a/Lib/test/test_uu.py +++ b/Lib/test/test_uu.py @@ -8,7 +8,6 @@ from test import support import sys, os import uu -from io import BytesIO import io plaintext = b"The smooth-scaled python crept over the sleeping dog\n" diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index d2c986e..f4ad7c7 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -15,7 +15,6 @@ import sys import tempfile from test.support import (captured_stdout, captured_stderr, can_symlink, EnvironmentVarGuard, rmtree) -import textwrap import unittest import venv @@ -31,18 +30,17 @@ try: except ImportError: threading = None +try: + import ctypes +except ImportError: + ctypes = None + skipInVenv = unittest.skipIf(sys.prefix != sys.base_prefix, 'Test not appropriate in a venv') -# os.path.exists('nul') is False: http://bugs.python.org/issue20541 -if os.devnull.lower() == 'nul': - failsOnWindows = unittest.expectedFailure -else: - def failsOnWindows(f): - return f - class BaseTest(unittest.TestCase): """Base class for venv tests.""" + maxDiff = 80 * 50 def setUp(self): self.env_dir = os.path.realpath(tempfile.mkdtemp()) @@ -52,7 +50,7 @@ class BaseTest(unittest.TestCase): self.include = 'Include' else: self.bindir = 'bin' - self.lib = ('lib', 'python%s' % sys.version[:3]) + self.lib = ('lib', 'python%d.%d' % sys.version_info[:2]) self.include = 'include' if sys.platform == 'darwin' and '__PYVENV_LAUNCHER__' in os.environ: executable = os.environ['__PYVENV_LAUNCHER__'] @@ -116,6 +114,17 @@ class BasicTest(BaseTest): print(' %r' % os.listdir(bd)) self.assertTrue(os.path.exists(fn), 'File %r should exist.' % fn) + def test_prompt(self): + env_name = os.path.split(self.env_dir)[1] + + builder = venv.EnvBuilder() + context = builder.ensure_directories(self.env_dir) + self.assertEqual(context.prompt, '(%s) ' % env_name) + + builder = venv.EnvBuilder(prompt='My prompt') + context = builder.ensure_directories(self.env_dir) + self.assertEqual(context.prompt, '(My prompt) ') + @skipInVenv def test_prefixes(self): """ @@ -313,20 +322,27 @@ class EnsurePipTest(BaseTest): self.run_with_capture(venv.create, self.env_dir, with_pip=False) self.assert_pip_not_installed() - @failsOnWindows - def test_devnull_exists_and_is_empty(self): + def test_devnull(self): # Fix for issue #20053 uses os.devnull to force a config file to # appear empty. However http://bugs.python.org/issue20541 means # that doesn't currently work properly on Windows. Once that is # fixed, the "win_location" part of test_with_pip should be restored - self.assertTrue(os.path.exists(os.devnull)) with open(os.devnull, "rb") as f: self.assertEqual(f.read(), b"") + # Issue #20541: os.path.exists('nul') is False on Windows + if os.devnull.lower() == 'nul': + self.assertFalse(os.path.exists(os.devnull)) + else: + self.assertTrue(os.path.exists(os.devnull)) + + # Requesting pip fails without SSL (http://bugs.python.org/issue19744) @unittest.skipIf(ssl is None, ensurepip._MISSING_SSL_MESSAGE) @unittest.skipUnless(threading, 'some dependencies of pip import threading' ' module unconditionally') + # Issue #26610: pip/pep425tags.py requires ctypes + @unittest.skipUnless(ctypes, 'pip requires ctypes') def test_with_pip(self): rmtree(self.env_dir) with EnvironmentVarGuard() as envvars: diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py index 84a6fb5..c11ee40 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -2,7 +2,9 @@ from contextlib import contextmanager import linecache import os from io import StringIO +import re import sys +import textwrap import unittest from test import support from test.support.script_helper import assert_python_ok, assert_python_failure @@ -720,6 +722,17 @@ class _WarningsTests(BaseTest, unittest.TestCase): result = stream.getvalue() self.assertIn(text, result) + def test_showwarnmsg_missing(self): + # Test that _showwarnmsg() missing is okay. + text = 'del _showwarnmsg test' + with original_warnings.catch_warnings(module=self.module): + self.module.filterwarnings("always", category=UserWarning) + del self.module._showwarnmsg + with support.captured_output('stderr') as stream: + self.module.warn(text) + result = stream.getvalue() + self.assertIn(text, result) + def test_showwarning_not_callable(self): with original_warnings.catch_warnings(module=self.module): self.module.filterwarnings("always", category=UserWarning) @@ -821,12 +834,44 @@ class WarningsDisplayTests(BaseTest): file_object, expected_file_line) self.assertEqual(expect, file_object.getvalue()) + class CWarningsDisplayTests(WarningsDisplayTests, unittest.TestCase): module = c_warnings class PyWarningsDisplayTests(WarningsDisplayTests, unittest.TestCase): module = py_warnings + def test_tracemalloc(self): + self.addCleanup(support.unlink, support.TESTFN) + + with open(support.TESTFN, 'w') as fp: + fp.write(textwrap.dedent(""" + def func(): + f = open(__file__) + # Emit ResourceWarning + f = None + + func() + """)) + + res = assert_python_ok('-Wd', '-X', 'tracemalloc=2', support.TESTFN) + + stderr = res.err.decode('ascii', 'replace') + # normalize newlines + stderr = '\n'.join(stderr.splitlines()) + stderr = re.sub('<.*>', '<...>', stderr) + expected = textwrap.dedent(''' + {fname}:5: ResourceWarning: unclosed file <...> + f = None + Object allocated at (most recent call first): + File "{fname}", lineno 3 + f = open(__file__) + File "{fname}", lineno 7 + func() + ''') + expected = expected.format(fname=support.TESTFN).strip() + self.assertEqual(stderr, expected) + class CatchWarningTests(BaseTest): diff --git a/Lib/test/test_wave.py b/Lib/test/test_wave.py index 3eff773..8666f72 100644 --- a/Lib/test/test_wave.py +++ b/Lib/test/test_wave.py @@ -1,6 +1,6 @@ -from test.support import TESTFN import unittest from test import audiotests +from test import support from audioop import byteswap import sys import wave @@ -103,5 +103,11 @@ class WavePCM32Test(WaveTest, unittest.TestCase): frames = byteswap(frames, 4) +class MiscTestCase(unittest.TestCase): + def test__all__(self): + blacklist = {'WAVE_FORMAT_PCM'} + support.check__all__(self, wave, blacklist=blacklist) + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_weakset.py b/Lib/test/test_weakset.py index 9ce672b..691b95e 100644 --- a/Lib/test/test_weakset.py +++ b/Lib/test/test_weakset.py @@ -1,13 +1,6 @@ import unittest -from weakref import proxy, ref, WeakSet -import operator -import copy +from weakref import WeakSet import string -import os -from random import randrange, shuffle -import sys -import warnings -import collections from collections import UserString as ustr import gc import contextlib diff --git a/Lib/test/test_winreg.py b/Lib/test/test_winreg.py index 2c4ac08..d642b13 100644 --- a/Lib/test/test_winreg.py +++ b/Lib/test/test_winreg.py @@ -37,6 +37,7 @@ test_reflect_key_name = "SOFTWARE\\Classes\\" + test_key_base test_data = [ ("Int Value", 45, REG_DWORD), + ("Qword Value", 0x1122334455667788, REG_QWORD), ("String Val", "A string value", REG_SZ), ("StringExpand", "The path is %path%", REG_EXPAND_SZ), ("Multi-string", ["Lots", "of", "string", "values"], REG_MULTI_SZ), @@ -168,7 +169,7 @@ class BaseWinregTests(unittest.TestCase): DeleteKey(key, subkeystr) try: - # Shouldnt be able to delete it twice! + # Shouldn't be able to delete it twice! DeleteKey(key, subkeystr) self.fail("Deleting the key twice succeeded") except OSError: diff --git a/Lib/test/test_winsound.py b/Lib/test/test_winsound.py index 4a8ab7d..1cfef77 100644 --- a/Lib/test/test_winsound.py +++ b/Lib/test/test_winsound.py @@ -87,6 +87,22 @@ class PlaySoundTest(unittest.TestCase): winsound.PlaySound, "none", winsound.SND_ASYNC | winsound.SND_MEMORY ) + self.assertRaises(TypeError, winsound.PlaySound, b"bad", 0) + self.assertRaises(TypeError, winsound.PlaySound, "bad", + winsound.SND_MEMORY) + self.assertRaises(TypeError, winsound.PlaySound, 1, 0) + + def test_snd_memory(self): + with open(support.findfile('pluck-pcm8.wav', + subdir='audiodata'), 'rb') as f: + audio_data = f.read() + safe_PlaySound(audio_data, winsound.SND_MEMORY) + audio_data = bytearray(audio_data) + safe_PlaySound(audio_data, winsound.SND_MEMORY) + + def test_snd_filename(self): + fn = support.findfile('pluck-pcm8.wav', subdir='audiodata') + safe_PlaySound(fn, winsound.SND_FILENAME | winsound.SND_NODEFAULT) def test_aliases(self): aliases = [ diff --git a/Lib/test/test_with.py b/Lib/test/test_with.py index e8d789b..e247ff6 100644 --- a/Lib/test/test_with.py +++ b/Lib/test/test_with.py @@ -140,11 +140,6 @@ class FailureTestCase(unittest.TestCase): 'with mock as (None):\n' ' pass') - def testAssignmentToEmptyTupleError(self): - self.assertRaisesSyntaxError( - 'with mock as ():\n' - ' pass') - def testAssignmentToTupleOnlyContainingNoneError(self): self.assertRaisesSyntaxError('with mock as None,:\n pass') self.assertRaisesSyntaxError( @@ -454,7 +449,7 @@ class ExceptionalTestCase(ContextmanagerAssertionMixin, unittest.TestCase): with cm(): raise StopIteration("from with") - with self.assertWarnsRegex(PendingDeprecationWarning, "StopIteration"): + with self.assertWarnsRegex(DeprecationWarning, "StopIteration"): self.assertRaises(StopIteration, shouldThrow) def testRaisedStopIteration2(self): @@ -482,7 +477,7 @@ class ExceptionalTestCase(ContextmanagerAssertionMixin, unittest.TestCase): with cm(): raise next(iter([])) - with self.assertWarnsRegex(PendingDeprecationWarning, "StopIteration"): + with self.assertWarnsRegex(DeprecationWarning, "StopIteration"): self.assertRaises(StopIteration, shouldThrow) def testRaisedGeneratorExit1(self): diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index bc1dd14..6e54628 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -91,8 +91,6 @@ ENTITY_XML = """\ class ModuleTest(unittest.TestCase): - # TODO: this should be removed once we get rid of the global module vars - def test_sanity(self): # Import sanity. @@ -100,6 +98,10 @@ class ModuleTest(unittest.TestCase): from xml.etree import ElementInclude from xml.etree import ElementPath + def test_all(self): + names = ("xml.etree.ElementTree", "_elementtree") + support.check__all__(self, ET, names, blacklist=("HTML_EMPTY",)) + def serialize(elem, to_string=True, encoding='unicode', **options): if encoding != 'unicode': @@ -182,10 +184,12 @@ class ElementTreeTest(unittest.TestCase): def check_element(element): self.assertTrue(ET.iselement(element), msg="not an element") - self.assertTrue(hasattr(element, "tag"), msg="no tag member") - self.assertTrue(hasattr(element, "attrib"), msg="no attrib member") - self.assertTrue(hasattr(element, "text"), msg="no text member") - self.assertTrue(hasattr(element, "tail"), msg="no tail member") + direlem = dir(element) + for attr in 'tag', 'attrib', 'text', 'tail': + self.assertTrue(hasattr(element, attr), + msg='no %s member' % attr) + self.assertIn(attr, direlem, + msg='no %s visible by dir' % attr) check_string(element.tag) check_mapping(element.attrib) diff --git a/Lib/test/test_xml_etree_c.py b/Lib/test/test_xml_etree_c.py index 96b446e..87f3f27 100644 --- a/Lib/test/test_xml_etree_c.py +++ b/Lib/test/test_xml_etree_c.py @@ -1,5 +1,5 @@ # xml.etree test for cElementTree -import sys, struct +import struct from test import support from test.support import import_fresh_module import types @@ -108,7 +108,7 @@ class SizeofTest(unittest.TestCase): struct.calcsize('8P')) def test_main(): - from test import test_xml_etree, test_xml_etree_c + from test import test_xml_etree # Run the tests specific to the C implementation support.run_unittest( diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py index 02d9f5c..0773a86 100644 --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -9,7 +9,6 @@ import xmlrpc.server import http.client import http, http.server import socket -import os import re import io import contextlib @@ -1147,7 +1146,6 @@ def captured_stdout(encoding='utf-8'): """A variation on support.captured_stdout() which gives a text stream having a `buffer` attribute. """ - import io orig_stdout = sys.stdout sys.stdout = io.TextIOWrapper(io.BytesIO(), encoding=encoding) try: diff --git a/Lib/test/test_xmlrpc_net.py b/Lib/test/test_xmlrpc_net.py index b60b82f..ae0a28e 100644 --- a/Lib/test/test_xmlrpc_net.py +++ b/Lib/test/test_xmlrpc_net.py @@ -1,7 +1,4 @@ import collections.abc -import errno -import socket -import sys import unittest from test import support diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index d278e06..ef3c3d8 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -1,8 +1,8 @@ import contextlib import io import os -import sys import importlib.util +import posixpath import time import struct import zipfile @@ -38,10 +38,6 @@ def get_files(test): yield f test.assertFalse(f.closed) -def openU(zipfp, fn): - with check_warnings(('', DeprecationWarning)): - return zipfp.open(fn, 'rU') - class AbstractTestsWithSourceFile: @classmethod def setUpClass(cls): @@ -61,6 +57,9 @@ class AbstractTestsWithSourceFile: zipfp.write(TESTFN, "another.name") zipfp.write(TESTFN, TESTFN) zipfp.writestr("strfile", self.data) + with zipfp.open('written-open-w', mode='w') as f: + for line in self.line_gen: + f.write(line) def zip_test(self, f, compression): self.make_test_archive(f, compression) @@ -76,7 +75,7 @@ class AbstractTestsWithSourceFile: zipfp.printdir(file=fp) directory = fp.getvalue() lines = directory.splitlines() - self.assertEqual(len(lines), 4) # Number of files + header + self.assertEqual(len(lines), 5) # Number of files + header self.assertIn('File Name', lines[0]) self.assertIn('Modified', lines[0]) @@ -90,23 +89,25 @@ class AbstractTestsWithSourceFile: # Check the namelist names = zipfp.namelist() - self.assertEqual(len(names), 3) + self.assertEqual(len(names), 4) self.assertIn(TESTFN, names) self.assertIn("another.name", names) self.assertIn("strfile", names) + self.assertIn("written-open-w", names) # Check infolist infos = zipfp.infolist() names = [i.filename for i in infos] - self.assertEqual(len(names), 3) + self.assertEqual(len(names), 4) self.assertIn(TESTFN, names) self.assertIn("another.name", names) self.assertIn("strfile", names) + self.assertIn("written-open-w", names) for i in infos: self.assertEqual(i.file_size, len(self.data)) # check getinfo - for nm in (TESTFN, "another.name", "strfile"): + for nm in (TESTFN, "another.name", "strfile", "written-open-w"): info = zipfp.getinfo(nm) self.assertEqual(info.filename, nm) self.assertEqual(info.file_size, len(self.data)) @@ -372,14 +373,18 @@ class StoredTestsWithSourceFile(AbstractTestsWithSourceFile, test_low_compression = None def zip_test_writestr_permissions(self, f, compression): - # Make sure that writestr creates files with mode 0600, - # when it is passed a name rather than a ZipInfo instance. + # Make sure that writestr and open(... mode='w') create files with + # mode 0600, when they are passed a name rather than a ZipInfo + # instance. self.make_test_archive(f, compression) with zipfile.ZipFile(f, "r") as zipfp: zinfo = zipfp.getinfo('strfile') self.assertEqual(zinfo.external_attr, 0o600 << 16) + zinfo2 = zipfp.getinfo('written-open-w') + self.assertEqual(zinfo2.external_attr, 0o600 << 16) + def test_writestr_permissions(self): for f in get_files(self): self.zip_test_writestr_permissions(f, zipfile.ZIP_STORED) @@ -451,6 +456,10 @@ class StoredTestsWithSourceFile(AbstractTestsWithSourceFile, with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: self.assertRaises(RuntimeError, zipfp.write, TESTFN) + with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: + with self.assertRaises(RuntimeError): + zipfp.open(TESTFN, mode='w') + def test_add_file_before_1980(self): # Set atime and mtime to 1970-01-01 os.utime(TESTFN, (0, 0)) @@ -1022,32 +1031,6 @@ class OtherTests(unittest.TestCase): data += zipfp.read(info) self.assertIn(data, {b"foobar", b"barfoo"}) - def test_universal_deprecation(self): - f = io.BytesIO() - with zipfile.ZipFile(f, "w") as zipfp: - zipfp.writestr('spam.txt', b'ababagalamaga') - - with zipfile.ZipFile(f, "r") as zipfp: - for mode in 'U', 'rU': - with self.assertWarns(DeprecationWarning): - zipopen = zipfp.open('spam.txt', mode) - zipopen.close() - - def test_universal_readaheads(self): - f = io.BytesIO() - - data = b'a\r\n' * 16 * 1024 - with zipfile.ZipFile(f, 'w', zipfile.ZIP_STORED) as zipfp: - zipfp.writestr(TESTFN, data) - - data2 = b'' - with zipfile.ZipFile(f, 'r') as zipfp, \ - openU(zipfp, TESTFN) as zipopen: - for line in zipopen: - data2 += line - - self.assertEqual(data, data2.replace(b'\n', b'\r\n')) - def test_writestr_extended_local_header_issue1202(self): with zipfile.ZipFile(TESTFN2, 'w') as orig_zip: for data in 'abcdefghijklmnop': @@ -1255,9 +1238,12 @@ class OtherTests(unittest.TestCase): zipf.writestr("foo.txt", "O, for a Muse of Fire!") with zipfile.ZipFile(TESTFN, mode="r") as zipf: - # read the data to make sure the file is there + # read the data to make sure the file is there zipf.read("foo.txt") self.assertRaises(RuntimeError, zipf.open, "foo.txt", "q") + # universal newlines support is removed + self.assertRaises(RuntimeError, zipf.open, "foo.txt", "U") + self.assertRaises(RuntimeError, zipf.open, "foo.txt", "rU") def test_read0(self): """Check that calling read(0) on a ZipExtFile object returns an empty @@ -1428,6 +1414,35 @@ class OtherTests(unittest.TestCase): # testzip returns the name of the first corrupt file, or None self.assertIsNone(zipf.testzip()) + def test_open_conflicting_handles(self): + # It's only possible to open one writable file handle at a time + msg1 = b"It's fun to charter an accountant!" + msg2 = b"And sail the wide accountant sea" + msg3 = b"To find, explore the funds offshore" + with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipf: + with zipf.open('foo', mode='w') as w2: + w2.write(msg1) + with zipf.open('bar', mode='w') as w1: + with self.assertRaises(RuntimeError): + zipf.open('handle', mode='w') + with self.assertRaises(RuntimeError): + zipf.open('foo', mode='r') + with self.assertRaises(RuntimeError): + zipf.writestr('str', 'abcde') + with self.assertRaises(RuntimeError): + zipf.write(__file__, 'file') + with self.assertRaises(RuntimeError): + zipf.close() + w1.write(msg2) + with zipf.open('baz', mode='w') as w2: + w2.write(msg3) + + with zipfile.ZipFile(TESTFN2, 'r') as zipf: + self.assertEqual(zipf.read('foo'), msg1) + self.assertEqual(zipf.read('bar'), msg2) + self.assertEqual(zipf.read('baz'), msg3) + self.assertEqual(zipf.namelist(), ['foo', 'bar', 'baz']) + def tearDown(self): unlink(TESTFN) unlink(TESTFN2) @@ -1761,6 +1776,22 @@ class UnseekableTests(unittest.TestCase): with zipf.open('twos') as zopen: self.assertEqual(zopen.read(), b'222') + def test_open_write(self): + for wrapper in (lambda f: f), Tellable, Unseekable: + with self.subTest(wrapper=wrapper): + f = io.BytesIO() + f.write(b'abc') + bf = io.BufferedWriter(f) + with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipf: + with zipf.open('ones', 'w') as zopen: + zopen.write(b'111') + with zipf.open('twos', 'w') as zopen: + zopen.write(b'222') + self.assertEqual(f.getvalue()[:5], b'abcPK') + with zipfile.ZipFile(f) as zipf: + self.assertEqual(zipf.read('ones'), b'111') + self.assertEqual(zipf.read('twos'), b'222') + @requires_zlib class TestsWithMultipleOpens(unittest.TestCase): @@ -1871,6 +1902,19 @@ class TestsWithMultipleOpens(unittest.TestCase): with open(os.devnull) as f: self.assertLess(f.fileno(), 100) + def test_write_while_reading(self): + with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_DEFLATED) as zipf: + zipf.writestr('ones', self.data1) + with zipfile.ZipFile(TESTFN2, 'a', zipfile.ZIP_DEFLATED) as zipf: + with zipf.open('ones', 'r') as r1: + data1 = r1.read(500) + with zipf.open('twos', 'w') as w1: + w1.write(self.data2) + data1 += r1.read() + self.assertEqual(data1, self.data1) + with zipfile.ZipFile(TESTFN2) as zipf: + self.assertEqual(zipf.read('twos'), self.data2) + def tearDown(self): unlink(TESTFN2) @@ -1940,137 +1984,19 @@ class TestWithDirectory(unittest.TestCase): unlink(TESTFN) -class AbstractUniversalNewlineTests: - @classmethod - def setUpClass(cls): - cls.line_gen = [bytes("Test of zipfile line %d." % i, "ascii") - for i in range(FIXEDTEST_SIZE)] - cls.seps = (b'\r', b'\r\n', b'\n') - cls.arcdata = {} - for n, s in enumerate(cls.seps): - cls.arcdata[s] = s.join(cls.line_gen) + s - - def setUp(self): - self.arcfiles = {} - for n, s in enumerate(self.seps): - self.arcfiles[s] = '%s-%d' % (TESTFN, n) - with open(self.arcfiles[s], "wb") as f: - f.write(self.arcdata[s]) - - def make_test_archive(self, f, compression): - # Create the ZIP archive - with zipfile.ZipFile(f, "w", compression) as zipfp: - for fn in self.arcfiles.values(): - zipfp.write(fn, fn) - - def read_test(self, f, compression): - self.make_test_archive(f, compression) - - # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp: - for sep, fn in self.arcfiles.items(): - with openU(zipfp, fn) as fp: - zipdata = fp.read() - self.assertEqual(self.arcdata[sep], zipdata) - - def test_read(self): - for f in get_files(self): - self.read_test(f, self.compression) - - def readline_read_test(self, f, compression): - self.make_test_archive(f, compression) - - # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp: - for sep, fn in self.arcfiles.items(): - with openU(zipfp, fn) as zipopen: - data = b'' - while True: - read = zipopen.readline() - if not read: - break - data += read - - read = zipopen.read(5) - if not read: - break - data += read - - self.assertEqual(data, self.arcdata[b'\n']) - - def test_readline_read(self): - for f in get_files(self): - self.readline_read_test(f, self.compression) - - def readline_test(self, f, compression): - self.make_test_archive(f, compression) - - # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp: - for sep, fn in self.arcfiles.items(): - with openU(zipfp, fn) as zipopen: - for line in self.line_gen: - linedata = zipopen.readline() - self.assertEqual(linedata, line + b'\n') - - def test_readline(self): - for f in get_files(self): - self.readline_test(f, self.compression) - - def readlines_test(self, f, compression): - self.make_test_archive(f, compression) - - # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp: - for sep, fn in self.arcfiles.items(): - with openU(zipfp, fn) as fp: - ziplines = fp.readlines() - for line, zipline in zip(self.line_gen, ziplines): - self.assertEqual(zipline, line + b'\n') - - def test_readlines(self): - for f in get_files(self): - self.readlines_test(f, self.compression) - - def iterlines_test(self, f, compression): - self.make_test_archive(f, compression) +class ZipInfoTests(unittest.TestCase): + def test_from_file(self): + zi = zipfile.ZipInfo.from_file(__file__) + self.assertEqual(posixpath.basename(zi.filename), 'test_zipfile.py') + self.assertFalse(zi.is_dir()) - # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp: - for sep, fn in self.arcfiles.items(): - with openU(zipfp, fn) as fp: - for line, zipline in zip(self.line_gen, fp): - self.assertEqual(zipline, line + b'\n') - - def test_iterlines(self): - for f in get_files(self): - self.iterlines_test(f, self.compression) - - def tearDown(self): - for sep, fn in self.arcfiles.items(): - unlink(fn) - unlink(TESTFN) - unlink(TESTFN2) - - -class StoredUniversalNewlineTests(AbstractUniversalNewlineTests, - unittest.TestCase): - compression = zipfile.ZIP_STORED - -@requires_zlib -class DeflateUniversalNewlineTests(AbstractUniversalNewlineTests, - unittest.TestCase): - compression = zipfile.ZIP_DEFLATED - -@requires_bz2 -class Bzip2UniversalNewlineTests(AbstractUniversalNewlineTests, - unittest.TestCase): - compression = zipfile.ZIP_BZIP2 - -@requires_lzma -class LzmaUniversalNewlineTests(AbstractUniversalNewlineTests, - unittest.TestCase): - compression = zipfile.ZIP_LZMA + def test_from_dir(self): + dirpath = os.path.dirname(os.path.abspath(__file__)) + zi = zipfile.ZipInfo.from_file(dirpath, 'stdlib_tests') + self.assertEqual(zi.filename, 'stdlib_tests/') + self.assertTrue(zi.is_dir()) + self.assertEqual(zi.compress_type, zipfile.ZIP_STORED) + self.assertEqual(zi.file_size, 0) if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_zipimport.py b/Lib/test/test_zipimport.py index 8e5e12d..a2482d4 100644 --- a/Lib/test/test_zipimport.py +++ b/Lib/test/test_zipimport.py @@ -398,7 +398,8 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase): packdir2 = packdir + TESTPACK2 + os.sep files = {packdir + "__init__" + pyc_ext: (NOW, test_pyc), packdir2 + "__init__" + pyc_ext: (NOW, test_pyc), - packdir2 + TESTMOD + pyc_ext: (NOW, test_pyc)} + packdir2 + TESTMOD + pyc_ext: (NOW, test_pyc), + "spam" + pyc_ext: (NOW, test_pyc)} z = ZipFile(TEMP_ZIP, "w") try: @@ -412,6 +413,14 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase): zi = zipimport.zipimporter(TEMP_ZIP) self.assertEqual(zi.archive, TEMP_ZIP) self.assertEqual(zi.is_package(TESTPACK), True) + + find_mod = zi.find_module('spam') + self.assertIsNotNone(find_mod) + self.assertIsInstance(find_mod, zipimport.zipimporter) + self.assertFalse(find_mod.is_package('spam')) + load_mod = find_mod.load_module('spam') + self.assertEqual(find_mod.get_filename('spam'), load_mod.__file__) + mod = zi.load_module(TESTPACK) self.assertEqual(zi.get_filename(TESTPACK), mod.__file__) @@ -471,6 +480,16 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase): self.assertEqual( zi.is_package(TESTPACK2 + os.sep + TESTMOD), False) + pkg_path = TEMP_ZIP + os.sep + packdir + TESTPACK2 + zi2 = zipimport.zipimporter(pkg_path) + find_mod_dotted = zi2.find_module(TESTMOD) + self.assertIsNotNone(find_mod_dotted) + self.assertIsInstance(find_mod_dotted, zipimport.zipimporter) + self.assertFalse(zi2.is_package(TESTMOD)) + load_mod = find_mod_dotted.load_module(TESTMOD) + self.assertEqual( + find_mod_dotted.get_filename(TESTMOD), load_mod.__file__) + mod_path = TESTPACK2 + os.sep + TESTMOD mod_name = module_path_to_dotted_name(mod_path) mod = importlib.import_module(mod_name) @@ -610,8 +629,10 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase): zipimport.zipimporter(filename) zipimport.zipimporter(os.fsencode(filename)) - zipimport.zipimporter(bytearray(os.fsencode(filename))) - zipimport.zipimporter(memoryview(os.fsencode(filename))) + with self.assertWarns(DeprecationWarning): + zipimport.zipimporter(bytearray(os.fsencode(filename))) + with self.assertWarns(DeprecationWarning): + zipimport.zipimporter(memoryview(os.fsencode(filename))) @support.requires_zlib diff --git a/Lib/test/test_zipimport_support.py b/Lib/test/test_zipimport_support.py index 5913622..84d526c 100644 --- a/Lib/test/test_zipimport_support.py +++ b/Lib/test/test_zipimport_support.py @@ -12,7 +12,6 @@ import zipimport import doctest import inspect import linecache -import pdb import unittest from test.support.script_helper import (spawn_python, kill_python, assert_python_ok, make_script, make_zip_script) diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py index 6fea893..20174d8 100644 --- a/Lib/test/test_zlib.py +++ b/Lib/test/test_zlib.py @@ -164,6 +164,16 @@ class CompressTestCase(BaseCompressTestCase, unittest.TestCase): x = zlib.compress(HAMLET_SCENE) self.assertEqual(zlib.decompress(x), HAMLET_SCENE) + def test_keywords(self): + x = zlib.compress(HAMLET_SCENE, level=3) + self.assertEqual(zlib.decompress(x), HAMLET_SCENE) + with self.assertRaises(TypeError): + zlib.compress(data=HAMLET_SCENE, level=3) + self.assertEqual(zlib.decompress(x, + wbits=zlib.MAX_WBITS, + bufsize=zlib.DEF_BUF_SIZE), + HAMLET_SCENE) + def test_speech128(self): # compress more data data = HAMLET_SCENE * 128 @@ -234,6 +244,27 @@ class CompressObjectTestCase(BaseCompressTestCase, unittest.TestCase): self.assertIsInstance(dco.unconsumed_tail, bytes) self.assertIsInstance(dco.unused_data, bytes) + def test_keywords(self): + level = 2 + method = zlib.DEFLATED + wbits = -12 + memLevel = 9 + strategy = zlib.Z_FILTERED + co = zlib.compressobj(level=level, + method=method, + wbits=wbits, + memLevel=memLevel, + strategy=strategy, + zdict=b"") + do = zlib.decompressobj(wbits=wbits, zdict=b"") + with self.assertRaises(TypeError): + co.compress(data=HAMLET_SCENE) + with self.assertRaises(TypeError): + do.decompress(data=zlib.compress(HAMLET_SCENE)) + x = co.compress(HAMLET_SCENE) + co.flush() + y = do.decompress(x, max_length=len(HAMLET_SCENE)) + do.flush() + self.assertEqual(HAMLET_SCENE, y) + def test_compressoptions(self): # specify lots of options to compressobj() level = 2 @@ -249,10 +280,6 @@ class CompressObjectTestCase(BaseCompressTestCase, unittest.TestCase): y2 = dco.flush() self.assertEqual(HAMLET_SCENE, y1 + y2) - # keyword arguments should also be supported - zlib.compressobj(level=level, method=method, wbits=wbits, - memLevel=memLevel, strategy=strategy, zdict=b"") - def test_compressincremental(self): # compress object in steps, decompress object as one-shot data = HAMLET_SCENE * 128 |