Author: Yannick Jadoul <[email protected]> Branch: py3.7-time-minor-bpos Changeset: r98403:29f5a19c932a Date: 2019-12-29 15:44 +0100 http://bitbucket.org/pypy/pypy/changeset/29f5a19c932a/
Log: merge py3.7 into py3.7-time-minor-bpos diff too long, truncating to 2000 out of 25236 lines diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -57,3 +57,7 @@ 4a68d8d3d2fc1faec2e83bcb4d28559099092574 release-pypy2.7-v7.2.0rc2 4a68d8d3d2fc1faec2e83bcb4d28559099092574 release-pypy2.7-v7.2.0 5da45ced70e515f94686be0df47c59abd1348ebc release-pypy3.6-v7.2.0 +e6471221abc16f4584a07fbfeece7ebcaeb7fc38 release-pypy2.7-v7.3.0rc1 +533398cfd64e5146a07c4824e90a1b629c8b6523 release-pypy3.6-v7.3.0rc1 +285307a0f5a77ffa46781b5c54c52eb1c385081d release-pypy2.7-v7.3.0rc2 +008914050baeedb6d3ca30fe26ef43b78bb63841 release-pypy3.6-v7.3.0rc2 diff --git a/LICENSE b/LICENSE --- a/LICENSE +++ b/LICENSE @@ -99,16 +99,16 @@ Spenser Bauman Michal Bendowski Jan de Mooij + Stefano Rivera Tyler Wade + Stefan Beyer Vincent Legoll Michael Foord Stephan Diehl - Stefano Rivera Jean-Paul Calderone Stefan Schwarzer Tomek Meka Valentino Volonghi - Stefan Beyer Patrick Maupin Devin Jeanpierre Bob Ippolito @@ -137,9 +137,10 @@ Jean-Philippe St. Pierre Guido van Rossum Pavel Vinogradov + Stian Andreassen + Julian Berman William Leslie Paweł Piotr Przeradowski - Stian Andreassen marky1991 Ilya Osadchiy Tobias Oberstein @@ -150,7 +151,7 @@ tav Georg Brandl Joannah Nanjekye - Julian Berman + Yannick Jadoul Bert Freudenberg Wanja Saatkamp Mike Blume @@ -275,6 +276,7 @@ Lutz Paelike Ian Foote Philipp Rustemeuer + Bernd Schoeller Logan Chien Catalin Gabriel Manciu Jacob Oscarson @@ -302,7 +304,6 @@ Laurens Van Houtven Bobby Impollonia Roberto De Ioris - Yannick Jadoul Jeong YunWon Christopher Armstrong Aaron Tubbs @@ -357,6 +358,7 @@ Daniil Yarancev Min RK OlivierBlanvillain + [email protected] [email protected] Jonas Pfannschmidt Zearin @@ -398,6 +400,7 @@ Jesdi Konrad Delong Dinu Gherman + Sam Edwards pizi Tomáš Pružina James Robert diff --git a/extra_tests/cffi_tests/cffi0/test_verify.py b/extra_tests/cffi_tests/cffi0/test_verify.py --- a/extra_tests/cffi_tests/cffi0/test_verify.py +++ b/extra_tests/cffi_tests/cffi0/test_verify.py @@ -4,6 +4,7 @@ import sys, os, math, weakref from cffi import FFI, VerificationError, VerificationMissing, model, FFIError from extra_tests.cffi_tests.support import * +from extra_tests.cffi_tests.support import extra_compile_args lib_m = ['m'] @@ -14,17 +15,6 @@ lib_m = ['msvcrt'] pass # no obvious -Werror equivalent on MSVC else: - if (sys.platform == 'darwin' and - [int(x) for x in os.uname()[2].split('.')] >= [11, 0, 0]): - # assume a standard clang or gcc - extra_compile_args = ['-Werror', '-Wall', '-Wextra', '-Wconversion'] - # special things for clang - extra_compile_args.append('-Qunused-arguments') - else: - # assume a standard gcc - extra_compile_args = ['-Werror', '-Wall', '-Wextra', '-Wconversion', - '-Wno-unused-parameter'] - class FFI(FFI): def verify(self, *args, **kwds): return super(FFI, self).verify( diff --git a/extra_tests/cffi_tests/cffi1/test_recompiler.py b/extra_tests/cffi_tests/cffi1/test_recompiler.py --- a/extra_tests/cffi_tests/cffi1/test_recompiler.py +++ b/extra_tests/cffi_tests/cffi1/test_recompiler.py @@ -35,8 +35,9 @@ source = 'extern "C" {\n%s\n}' % (source,) elif sys.platform != 'win32': # add '-Werror' to the existing 'extra_compile_args' flags + from extra_tests.cffi_tests.support import extra_compile_args kwds['extra_compile_args'] = (kwds.get('extra_compile_args', []) + - ['-Werror']) + extra_compile_args) return _verify(ffi, module_name, source, *args, **kwds) def test_set_source_no_slashes(): @@ -2039,7 +2040,7 @@ ffi.cdef("float _Complex f1(float a, float b);"); lib = verify(ffi, "test_function_returns_float_complex", """ #include <complex.h> - static float _Complex f1(float a, float b) { return a + I*2.0*b; } + static float _Complex f1(float a, float b) { return a + I*2.0f*b; } """, no_cpp=True) # <complex.h> fails on some systems with C++ result = lib.f1(1.25, 5.1) assert type(result) == complex diff --git a/extra_tests/cffi_tests/cffi1/test_verify1.py b/extra_tests/cffi_tests/cffi1/test_verify1.py --- a/extra_tests/cffi_tests/cffi1/test_verify1.py +++ b/extra_tests/cffi_tests/cffi1/test_verify1.py @@ -5,7 +5,7 @@ from cffi import CDefError from cffi import recompiler from extra_tests.cffi_tests.support import * -from extra_tests.cffi_tests.support import _verify +from extra_tests.cffi_tests.support import _verify, extra_compile_args import _cffi_backend lib_m = ['m'] @@ -14,18 +14,6 @@ import distutils.ccompiler if distutils.ccompiler.get_default_compiler() == 'msvc': lib_m = ['msvcrt'] - extra_compile_args = [] # no obvious -Werror equivalent on MSVC -else: - if (sys.platform == 'darwin' and - [int(x) for x in os.uname()[2].split('.')] >= [11, 0, 0]): - # assume a standard clang or gcc - extra_compile_args = ['-Werror', '-Wall', '-Wextra', '-Wconversion'] - # special things for clang - extra_compile_args.append('-Qunused-arguments') - else: - # assume a standard gcc - extra_compile_args = ['-Werror', '-Wall', '-Wextra', '-Wconversion', - '-Wno-unused-parameter'] class FFI(FFI): error = _cffi_backend.FFI.error diff --git a/extra_tests/cffi_tests/support.py b/extra_tests/cffi_tests/support.py --- a/extra_tests/cffi_tests/support.py +++ b/extra_tests/cffi_tests/support.py @@ -1,5 +1,5 @@ # Generated by pypy/tool/import_cffi.py -import sys +import sys, os if sys.version_info < (3,): __all__ = ['u'] @@ -87,3 +87,24 @@ if not name.startswith('_') and not hasattr(module.ffi, name): setattr(ffi, name, NotImplemented) return module.lib + + +# For testing, we call gcc with "-Werror". This is fragile because newer +# versions of gcc are always better at producing warnings, particularly for +# auto-generated code. We need here to adapt and silence them as needed. + +if sys.platform == 'win32': + extra_compile_args = [] # no obvious -Werror equivalent on MSVC +else: + if (sys.platform == 'darwin' and + [int(x) for x in os.uname()[2].split('.')] >= [11, 0, 0]): + # assume a standard clang or gcc + extra_compile_args = ['-Werror', '-Wall', '-Wextra', '-Wconversion', + '-Wno-unreachable-code'] + # special things for clang + extra_compile_args.append('-Qunused-arguments') + else: + # assume a standard gcc + extra_compile_args = ['-Werror', '-Wall', '-Wextra', '-Wconversion', + '-Wno-unused-parameter', + '-Wno-unreachable-code'] diff --git a/extra_tests/test_datetime.py b/extra_tests/test_datetime.py --- a/extra_tests/test_datetime.py +++ b/extra_tests/test_datetime.py @@ -350,3 +350,31 @@ d2 = d.replace(hour=7) assert type(d2) is MyDatetime assert d2 == datetime.datetime(2016, 4, 5, 7, 2, 3) + +def test_normalize_pair(): + normalize = datetime._normalize_pair + + assert normalize(1, 59, 60) == (1, 59) + assert normalize(1, 60, 60) == (2, 0) + assert normalize(1, 95, 60) == (2, 35) + +def test_normalize_date(): + normalize = datetime._normalize_date + + # Huge year is caught correctly + with pytest.raises(OverflowError): + normalize(1000 * 1000, 1, 1) + # Normal dates should be unchanged + assert normalize(3000, 1, 1) == (3000, 1, 1) + # Month overflows year boundary + assert normalize(2001, 24, 1) == (2002, 12, 1) + # Day overflows month boundary + assert normalize(2001, 14, 31) == (2002, 3, 3) + # Leap years? :S + assert normalize(2001, 1, 61) == (2001, 3, 2) + assert normalize(2000, 1, 61) == (2000, 3, 1) + +def test_normalize_datetime(): + normalize = datetime._normalize_datetime + abnormal = (2002, 13, 35, 30, 95, 75, 1000001) + assert normalize(*abnormal) == (2003, 2, 5, 7, 36, 16, 1) diff --git a/extra_tests/test_immutables_map.py b/extra_tests/test_immutables_map.py new file mode 100644 --- /dev/null +++ b/extra_tests/test_immutables_map.py @@ -0,0 +1,1298 @@ +import collections.abc +import gc +import pickle +import random +import sys +import weakref +import pytest + +from _immutables_map import Map + + +class HashKey: + _crasher = None + + def __init__(self, hash, name, *, error_on_eq_to=None): + assert hash != -1 + self.name = name + self.hash = hash + self.error_on_eq_to = error_on_eq_to + + def __repr__(self): + if self._crasher is not None and self._crasher.error_on_repr: + raise ReprError + return '<Key name:{} hash:{}>'.format(self.name, self.hash) + + def __hash__(self): + if self._crasher is not None and self._crasher.error_on_hash: + raise HashingError + + return self.hash + + def __eq__(self, other): + if not isinstance(other, HashKey): + return NotImplemented + + if self._crasher is not None and self._crasher.error_on_eq: + raise EqError + + if self.error_on_eq_to is not None and self.error_on_eq_to is other: + raise ValueError('cannot compare {!r} to {!r}'.format(self, other)) + if other.error_on_eq_to is not None and other.error_on_eq_to is self: + raise ValueError('cannot compare {!r} to {!r}'.format(other, self)) + + return (self.name, self.hash) == (other.name, other.hash) + + +class KeyStr(str): + + def __hash__(self): + if HashKey._crasher is not None and HashKey._crasher.error_on_hash: + raise HashingError + return super().__hash__() + + def __eq__(self, other): + if HashKey._crasher is not None and HashKey._crasher.error_on_eq: + raise EqError + return super().__eq__(other) + + def __repr__(self, other): + if HashKey._crasher is not None and HashKey._crasher.error_on_repr: + raise ReprError + return super().__eq__(other) + + +class HashKeyCrasher: + + def __init__(self, *, error_on_hash=False, error_on_eq=False, + error_on_repr=False): + self.error_on_hash = error_on_hash + self.error_on_eq = error_on_eq + self.error_on_repr = error_on_repr + + def __enter__(self): + if HashKey._crasher is not None: + raise RuntimeError('cannot nest crashers') + HashKey._crasher = self + + def __exit__(self, *exc): + HashKey._crasher = None + + +class HashingError(Exception): + pass + + +class EqError(Exception): + pass + + +class ReprError(Exception): + pass + + +class BaseMapTest: + + def test_hashkey_helper_1(self): + k1 = HashKey(10, 'aaa') + k2 = HashKey(10, 'bbb') + + assert k1 != k2 + assert hash(k1) == hash(k2) + + d = dict() + d[k1] = 'a' + d[k2] = 'b' + + assert d[k1] == 'a' + assert d[k2] == 'b' + + def test_map_basics_1(self): + h = self.Map() + h = None # NoQA + + def test_map_basics_2(self): + h = self.Map() + assert len(h) == 0 + + h2 = h.set('a', 'b') + assert h is not h2 + assert len(h) == 0 + assert len(h2) == 1 + + assert h.get('a') is None + assert h.get('a', 42) == 42 + + assert h2.get('a') == 'b' + + h3 = h2.set('b', 10) + assert h2 is not h3 + assert len(h) == 0 + assert len(h2) == 1 + assert len(h3) == 2 + assert h3.get('a') == 'b' + assert h3.get('b') == 10 + + assert h.get('b') is None + assert h2.get('b') is None + + assert h.get('a') is None + assert h2.get('a') == 'b' + + h = h2 = h3 = None + + def test_map_basics_3(self): + h = self.Map() + o = object() + h1 = h.set('1', o) + h2 = h1.set('1', o) + assert h1 is h2 + + def test_map_basics_4(self): + h = self.Map() + h1 = h.set('key', []) + h2 = h1.set('key', []) + assert h1 is not h2 + assert len(h1) == 1 + assert len(h2) == 1 + assert h1.get('key') is not h2.get('key') + + def test_map_collision_1(self): + k1 = HashKey(10, 'aaa') + k2 = HashKey(10, 'bbb') + k3 = HashKey(10, 'ccc') + + h = self.Map() + h2 = h.set(k1, 'a') + h3 = h2.set(k2, 'b') + + assert h.get(k1) == None + assert h.get(k2) == None + + assert h2.get(k1) == 'a' + assert h2.get(k2) == None + + assert h3.get(k1) == 'a' + assert h3.get(k2) == 'b' + + h4 = h3.set(k2, 'cc') + h5 = h4.set(k3, 'aa') + + assert h3.get(k1) == 'a' + assert h3.get(k2) == 'b' + assert h4.get(k1) == 'a' + assert h4.get(k2) == 'cc' + assert h4.get(k3) == None + assert h5.get(k1) == 'a' + assert h5.get(k2) == 'cc' + assert h5.get(k2) == 'cc' + assert h5.get(k3) == 'aa' + + assert len(h) == 0 + assert len(h2) == 1 + assert len(h3) == 2 + assert len(h4) == 2 + assert len(h5) == 3 + + def test_map_collision_2(self): + A = HashKey(100, 'A') + B = HashKey(101, 'B') + C = HashKey(0b011000011100000100, 'C') + D = HashKey(0b011000011100000100, 'D') + E = HashKey(0b1011000011100000100, 'E') + + h = self.Map() + h = h.set(A, 'a') + h = h.set(B, 'b') + h = h.set(C, 'c') + h = h.set(D, 'd') + + # BitmapNode(size=6 bitmap=0b100110000): + # NULL: + # BitmapNode(size=4 bitmap=0b1000000000000000000001000): + # <Key name:A hash:100>: 'a' + # NULL: + # CollisionNode(size=4 id=0x108572410): + # <Key name:C hash:100100>: 'c' + # <Key name:D hash:100100>: 'd' + # <Key name:B hash:101>: 'b' + + h = h.set(E, 'e') + + # BitmapNode(size=4 count=2.0 bitmap=0b110000 id=10b8ea5c0): + # None: + # BitmapNode(size=4 count=2.0 + # bitmap=0b1000000000000000000001000 id=10b8ea518): + # <Key name:A hash:100>: 'a' + # None: + # BitmapNode(size=2 count=1.0 bitmap=0b10 + # id=10b8ea4a8): + # None: + # BitmapNode(size=4 count=2.0 + # bitmap=0b100000001000 + # id=10b8ea4e0): + # None: + # CollisionNode(size=4 id=10b8ea470): + # <Key name:C hash:100100>: 'c' + # <Key name:D hash:100100>: 'd' + # <Key name:E hash:362244>: 'e' + # <Key name:B hash:101>: 'b' + + def test_map_stress(self): + COLLECTION_SIZE = 7000 + TEST_ITERS_EVERY = 647 + CRASH_HASH_EVERY = 97 + CRASH_EQ_EVERY = 11 + RUN_XTIMES = 3 + + for _ in range(RUN_XTIMES): + h = self.Map() + d = dict() + + for i in range(COLLECTION_SIZE): + key = KeyStr(i) + + if not (i % CRASH_HASH_EVERY): + with HashKeyCrasher(error_on_hash=True): + with pytest.raises(HashingError): + h.set(key, i) + + h = h.set(key, i) + + if not (i % CRASH_EQ_EVERY): + with HashKeyCrasher(error_on_eq=True): + with pytest.raises(EqError): + h.get(KeyStr(i)) # really trigger __eq__ + + d[key] = i + assert len(d) == len(h) + + if not (i % TEST_ITERS_EVERY): + assert set(h.items()) == set(d.items()) + assert len(h.items()) == len(d.items()) + + assert len(h) == COLLECTION_SIZE + + for key in range(COLLECTION_SIZE): + assert h.get(KeyStr(key), 'not found') == key + + keys_to_delete = list(range(COLLECTION_SIZE)) + random.shuffle(keys_to_delete) + for iter_i, i in enumerate(keys_to_delete): + key = KeyStr(i) + + if not (iter_i % CRASH_HASH_EVERY): + with HashKeyCrasher(error_on_hash=True): + with pytest.raises(HashingError): + h.delete(key) + + if not (iter_i % CRASH_EQ_EVERY): + with HashKeyCrasher(error_on_eq=True): + with pytest.raises(EqError): + h.delete(KeyStr(i)) + + h = h.delete(key) + assert h.get(key, 'not found') == 'not found' + del d[key] + assert len(d) == len(h) + + if iter_i == COLLECTION_SIZE // 2: + hm = h + dm = d.copy() + + if not (iter_i % TEST_ITERS_EVERY): + assert set(h.keys()) == set(d.keys()) + assert len(h.keys()) == len(d.keys()) + + assert len(d) == 0 + assert len(h) == 0 + + # ============ + + for key in dm: + assert hm.get(str(key)) == dm[key] + assert len(dm) == len(hm) + + for i, key in enumerate(keys_to_delete): + if str(key) in dm: + hm = hm.delete(str(key)) + dm.pop(str(key)) + assert hm.get(str(key), 'not found') == 'not found' + assert len(d) == len(h) + + if not (i % TEST_ITERS_EVERY): + assert set(h.values()) == set(d.values()) + assert len(h.values()) == len(d.values()) + + assert len(d) == 0 + assert len(h) == 0 + assert list(h.items()) == [] + + def test_map_delete_1(self): + A = HashKey(100, 'A') + B = HashKey(101, 'B') + C = HashKey(102, 'C') + D = HashKey(103, 'D') + E = HashKey(104, 'E') + Z = HashKey(-100, 'Z') + + Er = HashKey(103, 'Er', error_on_eq_to=D) + + h = self.Map() + h = h.set(A, 'a') + h = h.set(A, 'a') + h = h.set(B, 'b') + h = h.set(C, 'c') + h = h.set(D, 'd') + h = h.set(E, 'e') + + orig_len = len(h) + + # BitmapNode(size=10 bitmap=0b111110000 id=0x10eadc618): + # <Key name:A hash:100>: 'a' + # <Key name:B hash:101>: 'b' + # <Key name:C hash:102>: 'c' + # <Key name:D hash:103>: 'd' + # <Key name:E hash:104>: 'e' + + h = h.delete(C) + assert len(h) == orig_len - 1 + + with pytest.raises(ValueError, match='cannot compare'): + h.delete(Er) + + h = h.delete(D) + assert len(h) == orig_len - 2 + + with pytest.raises(KeyError) as ex: + h.delete(Z) + assert ex.value.args[0] is Z + + h = h.delete(A) + assert len(h) == orig_len - 3 + + assert h.get(A, 42) == 42 + assert h.get(B) == 'b' + assert h.get(E) == 'e' + + def test_map_delete_2(self): + A = HashKey(100, 'A') + B = HashKey(201001, 'B') + C = HashKey(101001, 'C') + BLike = HashKey(201001, 'B-like') + D = HashKey(103, 'D') + E = HashKey(104, 'E') + Z = HashKey(-100, 'Z') + + Er = HashKey(201001, 'Er', error_on_eq_to=B) + + h = self.Map() + h = h.set(A, 'a') + h = h.set(B, 'b') + h = h.set(C, 'c') + h = h.set(D, 'd') + h = h.set(E, 'e') + + h = h.set(B, 'b') # trigger branch in BitmapNode.assoc + + with pytest.raises(KeyError): + h.delete(BLike) # trigger branch in BitmapNode.without + + orig_len = len(h) + + # BitmapNode(size=8 bitmap=0b1110010000): + # <Key name:A hash:100>: 'a' + # <Key name:D hash:103>: 'd' + # <Key name:E hash:104>: 'e' + # NULL: + # BitmapNode(size=4 bitmap=0b100000000001000000000): + # <Key name:B hash:201001>: 'b' + # <Key name:C hash:101001>: 'c' + + with pytest.raises(ValueError, match='cannot compare'): + h.delete(Er) + + with pytest.raises(KeyError) as ex: + h.delete(Z) + assert ex.value.args[0] is Z + assert len(h) == orig_len + + h = h.delete(C) + assert len(h) == orig_len - 1 + + h = h.delete(B) + assert len(h) == orig_len - 2 + + h = h.delete(A) + assert len(h) == orig_len - 3 + + assert h.get(D) == 'd' + assert h.get(E) == 'e' + + with pytest.raises(KeyError): + h = h.delete(A) + with pytest.raises(KeyError): + h = h.delete(B) + h = h.delete(D) + h = h.delete(E) + assert len(h) == 0 + + def test_map_delete_3(self): + A = HashKey(0b00000000001100100, 'A') + B = HashKey(0b00000000001100101, 'B') + + C = HashKey(0b11000011100000100, 'C') + D = HashKey(0b11000011100000100, 'D') + X = HashKey(0b01000011100000100, 'Z') + Y = HashKey(0b11000011100000100, 'Y') + + E = HashKey(0b00000000001101000, 'E') + + h = self.Map() + h = h.set(A, 'a') + h = h.set(B, 'b') + h = h.set(C, 'c') + h = h.set(D, 'd') + h = h.set(E, 'e') + + assert len(h) == 5 + h = h.set(C, 'c') # trigger branch in CollisionNode.assoc + assert len(h) == 5 + + orig_len = len(h) + + with pytest.raises(KeyError): + h.delete(X) + with pytest.raises(KeyError): + h.delete(Y) + + # BitmapNode(size=6 bitmap=0b100110000): + # NULL: + # BitmapNode(size=4 bitmap=0b1000000000000000000001000): + # <Key name:A hash:100>: 'a' + # NULL: + # CollisionNode(size=4 id=0x108572410): + # <Key name:C hash:100100>: 'c' + # <Key name:D hash:100100>: 'd' + # <Key name:B hash:101>: 'b' + # <Key name:E hash:104>: 'e' + + h = h.delete(A) + assert len(h) == orig_len - 1 + + h = h.delete(E) + assert len(h) == orig_len - 2 + + assert h.get(C) == 'c' + assert h.get(B) == 'b' + + h2 = h.delete(C) + assert len(h2) == orig_len - 3 + + h2 = h.delete(D) + assert len(h2) == orig_len - 3 + + assert len(h) == orig_len - 2 + + def test_map_delete_4(self): + A = HashKey(100, 'A') + B = HashKey(101, 'B') + C = HashKey(100100, 'C') + D = HashKey(100100, 'D') + E = HashKey(100100, 'E') + + h = self.Map() + h = h.set(A, 'a') + h = h.set(B, 'b') + h = h.set(C, 'c') + h = h.set(D, 'd') + h = h.set(E, 'e') + + orig_len = len(h) + + # BitmapNode(size=4 bitmap=0b110000): + # NULL: + # BitmapNode(size=4 bitmap=0b1000000000000000000001000): + # <Key name:A hash:100>: 'a' + # NULL: + # CollisionNode(size=6 id=0x10515ef30): + # <Key name:C hash:100100>: 'c' + # <Key name:D hash:100100>: 'd' + # <Key name:E hash:100100>: 'e' + # <Key name:B hash:101>: 'b' + + h = h.delete(D) + assert len(h) == orig_len - 1 + + h = h.delete(E) + assert len(h) == orig_len - 2 + + h = h.delete(C) + assert len(h) == orig_len - 3 + + h = h.delete(A) + assert len(h) == orig_len - 4 + + h = h.delete(B) + assert len(h) == 0 + + def test_map_delete_5(self): + h = self.Map() + + keys = [] + for i in range(17): + key = HashKey(i, str(i)) + keys.append(key) + h = h.set(key, 'val-{}'.format(i)) + + collision_key16 = HashKey(16, '18') + h = h.set(collision_key16, 'collision') + + # ArrayNode(id=0x10f8b9318): + # 0:: + # BitmapNode(size=2 count=1 bitmap=0b1): + # <Key name:0 hash:0>: 'val-0' + # + # ... 14 more BitmapNodes ... + # + # 15:: + # BitmapNode(size=2 count=1 bitmap=0b1): + # <Key name:15 hash:15>: 'val-15' + # + # 16:: + # BitmapNode(size=2 count=1 bitmap=0b1): + # NULL: + # CollisionNode(size=4 id=0x10f2f5af8): + # <Key name:16 hash:16>: 'val-16' + # <Key name:18 hash:16>: 'collision' + + assert len(h) == 18 + + h = h.delete(keys[2]) + assert len(h) == 17 + + h = h.delete(collision_key16) + assert len(h) == 16 + h = h.delete(keys[16]) + assert len(h) == 15 + + h = h.delete(keys[1]) + assert len(h) == 14 + with pytest.raises(KeyError) as ex: + h.delete(keys[1]) + assert ex.value.args[0] is keys[1] + assert len(h) == 14 + + for key in keys: + if key in h: + h = h.delete(key) + assert len(h) == 0 + + def test_map_delete_6(self): + h = self.Map() + h = h.set(1, 1) + h = h.delete(1) + assert len(h) == 0 + assert h == self.Map() + + def test_map_items_1(self): + A = HashKey(100, 'A') + B = HashKey(201001, 'B') + C = HashKey(101001, 'C') + D = HashKey(103, 'D') + E = HashKey(104, 'E') + F = HashKey(110, 'F') + + h = self.Map() + h = h.set(A, 'a') + h = h.set(B, 'b') + h = h.set(C, 'c') + h = h.set(D, 'd') + h = h.set(E, 'e') + h = h.set(F, 'f') + + it = h.items() + assert set(list(it)) == \ + {(A, 'a'), (B, 'b'), (C, 'c'), (D, 'd'), (E, 'e'), (F, 'f')} + + def test_map_items_2(self): + A = HashKey(100, 'A') + B = HashKey(101, 'B') + C = HashKey(100100, 'C') + D = HashKey(100100, 'D') + E = HashKey(100100, 'E') + F = HashKey(110, 'F') + + h = self.Map() + h = h.set(A, 'a') + h = h.set(B, 'b') + h = h.set(C, 'c') + h = h.set(D, 'd') + h = h.set(E, 'e') + h = h.set(F, 'f') + + it = h.items() + assert set(list(it)) == \ + {(A, 'a'), (B, 'b'), (C, 'c'), (D, 'd'), (E, 'e'), (F, 'f')} + + def test_map_items_3(self): + h = self.Map() + assert len(h.items()) == 0 + assert list(h.items()) == [] + + def test_map_items_4(self): + h = self.Map(a=1, b=2, c=3) + k = h.items() + assert set(k) == {('a', 1), ('b', 2), ('c', 3)} + assert set(k) == {('a', 1), ('b', 2), ('c', 3)} + + def test_map_keys_1(self): + A = HashKey(100, 'A') + B = HashKey(101, 'B') + C = HashKey(100100, 'C') + D = HashKey(100100, 'D') + E = HashKey(100100, 'E') + F = HashKey(110, 'F') + + h = self.Map() + h = h.set(A, 'a') + h = h.set(B, 'b') + h = h.set(C, 'c') + h = h.set(D, 'd') + h = h.set(E, 'e') + h = h.set(F, 'f') + + assert set(list(h.keys())) == {A, B, C, D, E, F} + assert set(list(h)) == {A, B, C, D, E, F} + + def test_map_keys_2(self): + h = self.Map(a=1, b=2, c=3) + k = h.keys() + assert set(k) == {'a', 'b', 'c'} + assert set(k) == {'a', 'b', 'c'} + + def test_map_values_1(self): + A = HashKey(100, 'A') + B = HashKey(101, 'B') + C = HashKey(100100, 'C') + D = HashKey(100100, 'D') + E = HashKey(100100, 'E') + F = HashKey(110, 'F') + + h = self.Map() + h = h.set(A, 'a') + h = h.set(B, 'b') + h = h.set(C, 'c') + h = h.set(D, 'd') + h = h.set(E, 'e') + h = h.set(F, 'f') + + assert set(list(h.values())) == {'a', 'b', 'c', 'd', 'e', 'f'} + + def test_map_values_2(self): + h = self.Map(a=1, b=2, c=3) + k = h.values() + assert set(k) == {1, 2, 3} + assert set(k) == {1, 2, 3} + + def test_map_eq_1(self): + A = HashKey(100, 'A') + B = HashKey(101, 'B') + C = HashKey(100100, 'C') + D = HashKey(100100, 'D') + E = HashKey(120, 'E') + + h1 = self.Map() + h1 = h1.set(A, 'a') + h1 = h1.set(B, 'b') + h1 = h1.set(C, 'c') + h1 = h1.set(D, 'd') + + h2 = self.Map() + h2 = h2.set(A, 'a') + + assert not (h1 == h2) + assert h1 != h2 + + h2 = h2.set(B, 'b') + assert not (h1 == h2) + assert h1 != h2 + + h2 = h2.set(C, 'c') + assert not (h1 == h2) + assert h1 != h2 + + h2 = h2.set(D, 'd2') + assert not (h1 == h2) + assert h1 != h2 + + h2 = h2.set(D, 'd') + assert h1 == h2 + assert not (h1 != h2) + + h2 = h2.set(E, 'e') + assert not (h1 == h2) + assert h1 != h2 + + h2 = h2.delete(D) + assert not (h1 == h2) + assert h1 != h2 + + h2 = h2.set(E, 'd') + assert not (h1 == h2) + assert h1 != h2 + + def test_map_eq_2(self): + A = HashKey(100, 'A') + Er = HashKey(100, 'Er', error_on_eq_to=A) + + h1 = self.Map() + h1 = h1.set(A, 'a') + + h2 = self.Map() + h2 = h2.set(Er, 'a') + + with pytest.raises(ValueError, match='cannot compare'): + h1 == h2 + + with pytest.raises(ValueError, match='cannot compare'): + h1 != h2 + + def test_map_eq_3(self): + assert self.Map() != 1 + + def test_map_gc_1(self): + A = HashKey(100, 'A') + + h = self.Map() + h = h.set(0, 0) # empty Map node is memoized in _map.c + ref = weakref.ref(h) + + a = [] + a.append(a) + a.append(h) + b = [] + a.append(b) + b.append(a) + h = h.set(A, b) + + del h, a, b + + gc.collect() + gc.collect() + gc.collect() + + assert ref() is None + + def test_map_gc_2(self): + A = HashKey(100, 'A') + + h = self.Map() + h = h.set(A, 'a') + h = h.set(A, h) + + ref = weakref.ref(h) + hi = iter(h.items()) + next(hi) + + del h, hi + + gc.collect() + gc.collect() + gc.collect() + + assert ref() is None + + def test_map_in_1(self): + A = HashKey(100, 'A') + AA = HashKey(100, 'A') + + B = HashKey(101, 'B') + + h = self.Map() + h = h.set(A, 1) + + assert A in h + assert not (B in h) + + with pytest.raises(EqError): + with HashKeyCrasher(error_on_eq=True): + AA in h + + with pytest.raises(HashingError): + with HashKeyCrasher(error_on_hash=True): + AA in h + + def test_map_getitem_1(self): + A = HashKey(100, 'A') + AA = HashKey(100, 'A') + + B = HashKey(101, 'B') + + h = self.Map() + h = h.set(A, 1) + + assert h[A] == 1 + assert h[AA] == 1 + + with pytest.raises(KeyError): + h[B] + + with pytest.raises(EqError): + with HashKeyCrasher(error_on_eq=True): + h[AA] + + with pytest.raises(HashingError): + with HashKeyCrasher(error_on_hash=True): + h[AA] + + def test_repr_1(self): + h = self.Map() + assert repr(h).startswith('<immutables.Map({}) at 0x') + + h = h.set(1, 2).set(2, 3).set(3, 4) + assert repr(h).startswith( + '<immutables.Map({1: 2, 2: 3, 3: 4}) at 0x') + + def test_repr_2(self): + h = self.Map() + A = HashKey(100, 'A') + + with pytest.raises(ReprError): + with HashKeyCrasher(error_on_repr=True): + repr(h.set(1, 2).set(A, 3).set(3, 4)) + + with pytest.raises(ReprError): + with HashKeyCrasher(error_on_repr=True): + repr(h.set(1, 2).set(2, A).set(3, 4)) + + def test_repr_3(self): + class Key: + def __init__(self): + self.val = None + + def __hash__(self): + return 123 + + def __repr__(self): + return repr(self.val) + + h = self.Map() + k = Key() + h = h.set(k, 1) + k.val = h + + assert repr(h).startswith( + '<immutables.Map({{...}: 1}) at 0x') + + def test_hash_1(self): + h = self.Map() + assert hash(h) != -1 + assert hash(h) == hash(h) + + h = h.set(1, 2).set('a', 'b') + assert hash(h) != -1 + assert hash(h) == hash(h) + + assert hash(h.set(1, 2).set('a', 'b')) == \ + hash(h.set('a', 'b').set(1, 2)) + + def test_hash_2(self): + h = self.Map() + A = HashKey(100, 'A') + + m = h.set(1, 2).set(A, 3).set(3, 4) + with pytest.raises(HashingError): + with HashKeyCrasher(error_on_hash=True): + hash(m) + + m = h.set(1, 2).set(2, A).set(3, 4) + with pytest.raises(HashingError): + with HashKeyCrasher(error_on_hash=True): + hash(m) + + def test_abc_1(self): + assert issubclass(self.Map, collections.abc.Mapping) + + def test_map_mut_1(self): + h = self.Map() + h = h.set('a', 1) + + hm1 = h.mutate() + hm2 = h.mutate() + + assert not isinstance(hm1, self.Map) + + assert hm1 is not hm2 + assert hm1['a'] == 1 + assert hm2['a'] == 1 + + hm1.set('b', 2) + hm1.set('c', 3) + + hm2.set('x', 100) + hm2.set('a', 1000) + + assert hm1['a'] == 1 + assert hm1.get('x', -1) == -1 + + assert hm2['a'] == 1000 + assert 'x' in hm2 + + h1 = hm1.finish() + h2 = hm2.finish() + + assert isinstance(h1, self.Map) + + assert dict(h.items()) == {'a': 1} + assert dict(h1.items()) == {'a': 1, 'b': 2, 'c': 3} + assert dict(h2.items()) == {'a': 1000, 'x': 100} + + def test_map_mut_2(self): + h = self.Map() + h = h.set('a', 1) + + hm1 = h.mutate() + hm1.set('a', 2) + hm1.set('a', 3) + hm1.set('a', 4) + h2 = hm1.finish() + + assert dict(h.items()) == {'a': 1} + assert dict(h2.items()) == {'a': 4} + + def test_map_mut_3(self): + h = self.Map() + h = h.set('a', 1) + hm1 = h.mutate() + + assert repr(hm1).startswith( + "<immutables.MapMutation({'a': 1})") + + with pytest.raises(TypeError, match='unhashable type'): + hash(hm1) + + def test_map_mut_4(self): + h = self.Map() + h = h.set('a', 1) + h = h.set('b', 2) + + hm1 = h.mutate() + hm2 = h.mutate() + + assert hm1 == hm2 + + hm1.set('a', 10) + assert hm1 != hm2 + + hm2.set('a', 10) + assert hm1 == hm2 + + assert hm2.pop('a') == 10 + assert hm1 != hm2 + + def test_map_mut_5(self): + h = self.Map({'a': 1, 'b': 2}, z=100) + assert isinstance(h, self.Map) + assert dict(h.items()) == {'a': 1, 'b': 2, 'z': 100} + + h2 = h.update(z=200, y=-1) + assert dict(h.items()) == {'a': 1, 'b': 2, 'z': 100} + assert dict(h2.items()) == {'a': 1, 'b': 2, 'z': 200, 'y': -1} + + h3 = h2.update([(1, 2), (3, 4)]) + assert dict(h.items()) == {'a': 1, 'b': 2, 'z': 100} + assert dict(h2.items()) == {'a': 1, 'b': 2, 'z': 200, 'y': -1} + assert dict(h3.items()) == \ + {'a': 1, 'b': 2, 'z': 200, 'y': -1, 1: 2, 3: 4} + + h4 = h3.update() + assert h4 is h3 + + h5 = h4.update(self.Map({'zzz': 'yyz'})) + + assert dict(h5.items()) == \ + {'a': 1, 'b': 2, 'z': 200, 'y': -1, 1: 2, 3: 4, + 'zzz': 'yyz'} + + def test_map_mut_6(self): + h = self.Map({'a': 1, 'b': 2}, z=100) + assert dict(h.items()) == {'a': 1, 'b': 2, 'z': 100} + + with pytest.raises(TypeError, match='not iterable'): + h.update(1) + + with pytest.raises(ValueError, match='map update sequence element'): + h.update([(1, 2), (3, 4, 5)]) + + with pytest.raises(TypeError, match='cannot convert map update'): + h.update([(1, 2), 1]) + + assert dict(h.items()) == {'a': 1, 'b': 2, 'z': 100} + + def test_map_mut_7(self): + key = HashKey(123, 'aaa') + + h = self.Map({'a': 1, 'b': 2}, z=100) + assert dict(h.items()) == {'a': 1, 'b': 2, 'z': 100} + + upd = {key: 1} + with HashKeyCrasher(error_on_hash=True): + with pytest.raises(HashingError): + h.update(upd) + + upd = self.Map({key: 'zzz'}) + with HashKeyCrasher(error_on_hash=True): + with pytest.raises(HashingError): + h.update(upd) + + upd = [(1, 2), (key, 'zzz')] + with HashKeyCrasher(error_on_hash=True): + with pytest.raises(HashingError): + h.update(upd) + + assert dict(h.items()) == {'a': 1, 'b': 2, 'z': 100} + + def test_map_mut_8(self): + key1 = HashKey(123, 'aaa') + key2 = HashKey(123, 'bbb') + + h = self.Map({key1: 123}) + assert dict(h.items()) == {key1: 123} + + upd = {key2: 1} + with HashKeyCrasher(error_on_eq=True): + with pytest.raises(EqError): + h.update(upd) + + upd = self.Map({key2: 'zzz'}) + with HashKeyCrasher(error_on_eq=True): + with pytest.raises(EqError): + h.update(upd) + + upd = [(1, 2), (key2, 'zzz')] + with HashKeyCrasher(error_on_eq=True): + with pytest.raises(EqError): + h.update(upd) + + assert dict(h.items()) == {key1: 123} + + def test_map_mut_9(self): + key1 = HashKey(123, 'aaa') + + src = {key1: 123} + with HashKeyCrasher(error_on_hash=True): + with pytest.raises(HashingError): + self.Map(src) + + src = [(1, 2), (key1, 123)] + with HashKeyCrasher(error_on_hash=True): + with pytest.raises(HashingError): + self.Map(src) + + def test_map_mut_10(self): + key1 = HashKey(123, 'aaa') + + m = self.Map({key1: 123}) + + mm = m.mutate() + with HashKeyCrasher(error_on_hash=True): + with pytest.raises(HashingError): + del mm[key1] + + mm = m.mutate() + with HashKeyCrasher(error_on_hash=True): + with pytest.raises(HashingError): + mm.pop(key1, None) + + mm = m.mutate() + with HashKeyCrasher(error_on_hash=True): + with pytest.raises(HashingError): + mm.set(key1, 123) + + def test_map_mut_11(self): + m = self.Map({'a': 1, 'b': 2}) + + mm = m.mutate() + assert mm.pop('a', 1) == 1 + assert mm.finish() == self.Map({'b': 2}) + + mm = m.mutate() + assert mm.pop('b', 1) == 2 + assert mm.finish() == self.Map({'a': 1}) + + mm = m.mutate() + assert mm.pop('b', 1) == 2 + del mm['a'] + assert mm.finish() == self.Map() + + def test_map_mut_12(self): + m = self.Map({'a': 1, 'b': 2}) + + mm = m.mutate() + mm.finish() + + with pytest.raises(ValueError, match='has been finished'): + mm.pop('a') + + with pytest.raises(ValueError, match='has been finished'): + del mm['a'] + + with pytest.raises(ValueError, match='has been finished'): + mm.set('a', 'b') + + with pytest.raises(ValueError, match='has been finished'): + mm['a'] = 'b' + + with pytest.raises(ValueError, match='has been finished'): + mm.update(a='b') + + def test_map_mut_13(self): + key1 = HashKey(123, 'aaa') + key2 = HashKey(123, 'aaa') + + m = self.Map({key1: 123}) + + mm = m.mutate() + with HashKeyCrasher(error_on_eq=True): + with pytest.raises(EqError): + del mm[key2] + + mm = m.mutate() + with HashKeyCrasher(error_on_eq=True): + with pytest.raises(EqError): + mm.pop(key2, None) + + mm = m.mutate() + with HashKeyCrasher(error_on_eq=True): + with pytest.raises(EqError): + mm.set(key2, 123) + + def test_map_mut_14(self): + m = self.Map(a=1, b=2) + + with m.mutate() as mm: + mm['z'] = 100 + del mm['a'] + + assert mm.finish() == self.Map(z=100, b=2) + + def test_map_mut_15(self): + m = self.Map(a=1, b=2) + + with pytest.raises(ZeroDivisionError): + with m.mutate() as mm: + mm['z'] = 100 + del mm['a'] + 1 / 0 + + assert mm.finish() == self.Map(z=100, b=2) + assert m == self.Map(a=1, b=2) + + def test_map_mut_16(self): + m = self.Map(a=1, b=2) + hash(m) + + m2 = self.Map(m) + m3 = self.Map(m, c=3) + + assert m == m2 + assert len(m) == len(m2) + assert hash(m) == hash(m2) + + assert m is not m2 + assert m3 == self.Map(a=1, b=2, c=3) + + def test_map_mut_17(self): + m = self.Map(a=1) + with m.mutate() as mm: + with pytest.raises(TypeError, match='cannot create Maps from MapMutations'): + self.Map(mm) + + def test_map_mut_18(self): + m = self.Map(a=1, b=2) + with m.mutate() as mm: + mm.update(self.Map(x=1), z=2) + mm.update(c=3) + mm.update({'n': 100, 'a': 20}) + m2 = mm.finish() + + expected = self.Map( + {'b': 2, 'c': 3, 'n': 100, 'z': 2, 'x': 1, 'a': 20}) + + assert len(m2) == 6 + assert m2 == expected + assert m == self.Map(a=1, b=2) + + def test_map_mut_19(self): + m = self.Map(a=1, b=2) + m2 = m.update({'a': 20}) + assert len(m2) == 2 + + def test_map_mut_stress(self): + COLLECTION_SIZE = 7000 + TEST_ITERS_EVERY = 647 + RUN_XTIMES = 3 + + for _ in range(RUN_XTIMES): + h = self.Map() + d = dict() + + for i in range(COLLECTION_SIZE // TEST_ITERS_EVERY): + + hm = h.mutate() + for j in range(TEST_ITERS_EVERY): + key = random.randint(1, 100000) + key = HashKey(key % 271, str(key)) + + hm.set(key, key) + d[key] = key + + assert len(hm) == len(d) + + h2 = hm.finish() + assert dict(h2.items()) == d + h = h2 + + assert dict(h.items()) == d + assert len(h) == len(d) + + it = iter(tuple(d.keys())) + for i in range(COLLECTION_SIZE // TEST_ITERS_EVERY): + + hm = h.mutate() + for j in range(TEST_ITERS_EVERY): + try: + key = next(it) + except StopIteration: + break + + del d[key] + del hm[key] + + assert len(hm) == len(d) + + h2 = hm.finish() + assert dict(h2.items()) == d + h = h2 + + assert dict(h.items()) == d + assert len(h) == len(d) + + def test_map_pickle(self): + h = self.Map(a=1, b=2) + for proto in range(pickle.HIGHEST_PROTOCOL): + p = pickle.dumps(h, proto) + uh = pickle.loads(p) + + assert isinstance(uh, self.Map) + assert h == uh + + with pytest.raises(TypeError, match="can('t|not) pickle"): + pickle.dumps(h.mutate()) + + def test_map_is_subscriptable(self): + assert self.Map[int, str] is self.Map + +class TestPyMap(BaseMapTest): + Map = Map diff --git a/lib-python/3/datetime.py b/lib-python/3/datetime.py --- a/lib-python/3/datetime.py +++ b/lib-python/3/datetime.py @@ -1544,9 +1544,13 @@ self.__setstate(year, month) self._hashcode = -1 return self - year, month, day = _check_date_fields(year, month, day) - hour, minute, second, microsecond, fold = _check_time_fields( - hour, minute, second, microsecond, fold) + elif isinstance(year, tuple) and len(year) == 7: + # Internal operation - numbers guaranteed to be valid + year, month, day, hour, minute, second, microsecond = year + else: + year, month, day = _check_date_fields(year, month, day) + hour, minute, second, microsecond, fold = _check_time_fields( + hour, minute, second, microsecond, fold) _check_tzinfo_arg(tzinfo) self = dateinterop.__new__(cls) self._year = int(year) @@ -2035,20 +2039,18 @@ "Add a datetime and a timedelta." if not isinstance(other, timedelta): return NotImplemented - delta = timedelta(self.toordinal(), - hours=self._hour, - minutes=self._minute, - seconds=self._second, - microseconds=self._microsecond) - delta += other - hour, rem = divmod(delta.seconds, 3600) - minute, second = divmod(rem, 60) - if 0 < delta.days <= _MAXORDINAL: - return datetime.combine(date.fromordinal(delta.days), - time(hour, minute, second, - delta.microseconds, - tzinfo=self._tzinfo)) - raise OverflowError("result out of range") + + result = _normalize_datetime( + self._year, + self._month, + self._day + other.days, + self._hour, + self._minute, + self._second + other.seconds, + self._microsecond + other.microseconds, + ) + + return datetime(result, tzinfo=self._tzinfo) __radd__ = __add__ @@ -2145,6 +2147,65 @@ datetime.resolution = timedelta(microseconds=1) +def _normalize_pair(hi, lo, factor): + if not 0 <= lo <= factor-1: + inc, lo = divmod(lo, factor) + hi += inc + return hi, lo + + +def _normalize_datetime(y, m, d, hh, mm, ss, us): + # Normalize all the inputs, and store the normalized values. + ss, us = _normalize_pair(ss, us, 1000000) + mm, ss = _normalize_pair(mm, ss, 60) + hh, mm = _normalize_pair(hh, mm, 60) + d, hh = _normalize_pair(d, hh, 24) + y, m, d = _normalize_date(y, m, d) + return y, m, d, hh, mm, ss, us + + +def _normalize_date(year, month, day): + # That was easy. Now it gets muddy: the proper range for day + # can't be determined without knowing the correct month and year, + # but if day is, e.g., plus or minus a million, the current month + # and year values make no sense (and may also be out of bounds + # themselves). + # Saying 12 months == 1 year should be non-controversial. + if not 1 <= month <= 12: + year, month = _normalize_pair(year, month-1, 12) + month += 1 + assert 1 <= month <= 12 + + # Now only day can be out of bounds (year may also be out of bounds + # for a datetime object, but we don't care about that here). + # If day is out of bounds, what to do is arguable, but at least the + # method here is principled and explainable. + dim = _days_in_month(year, month) + if not 1 <= day <= dim: + # Move day-1 days from the first of the month. First try to + # get off cheap if we're only one day out of range (adjustments + # for timezone alone can't be worse than that). + if day == 0: # move back a day + month -= 1 + if month > 0: + day = _days_in_month(year, month) + else: + year, month, day = year-1, 12, 31 + elif day == dim + 1: # move forward a day + month += 1 + day = 1 + if month > 12: + month = 1 + year += 1 + else: + ordinal = _ymd2ord(year, month, 1) + (day - 1) + year, month, day = _ord2ymd(ordinal) + + if not MINYEAR <= year <= MAXYEAR: + raise OverflowError("date value out of range") + return year, month, day + + def _isoweek1monday(year): # Helper to calculate the day number of the Monday starting week 1 # XXX This could be done more efficiently diff --git a/lib-python/3/importlib/_bootstrap.py b/lib-python/3/importlib/_bootstrap.py --- a/lib-python/3/importlib/_bootstrap.py +++ b/lib-python/3/importlib/_bootstrap.py @@ -67,6 +67,7 @@ # Deadlock avoidance for concurrent circular imports. me = _thread.get_ident() tid = self.owner + count = 0 while True: lock = _blocking_on.get(tid) if lock is None: @@ -74,6 +75,14 @@ tid = lock.owner if tid == me: return True + # workaround for https://bugs.python.org/issue38091: + # instead of looping here forever, eventually return False. + # Unsure if this will cause real deadlocks to go undetected, + # but at least it doesn't cause *this* logic here to + # deadlock when there is otherwise no deadlock! + count += 1 + if count >= 100: + return False def acquire(self): """ diff --git a/lib-python/3/sysconfig.py b/lib-python/3/sysconfig.py --- a/lib-python/3/sysconfig.py +++ b/lib-python/3/sysconfig.py @@ -451,6 +451,10 @@ vars['EXE'] = '.exe' vars['VERSION'] = _PY_VERSION_SHORT_NO_DOT vars['BINDIR'] = os.path.dirname(_safe_realpath(sys.executable)) + # pypy: give us control over the ABI tag in a wheel name + import _imp + so_ext = _imp.extension_suffixes()[0] + vars['SOABI']= '-'.join(so_ext.split('.')[1].split('-')[:2]) # # public APIs diff --git a/lib-python/3/test/test_asyncio/test_events.py b/lib-python/3/test/test_asyncio/test_events.py --- a/lib-python/3/test/test_asyncio/test_events.py +++ b/lib-python/3/test/test_asyncio/test_events.py @@ -943,9 +943,14 @@ server = self.loop.run_until_complete(f) self.assertEqual(len(server.sockets), 1) sock = server.sockets[0] - self.assertFalse( - sock.getsockopt( - socket.SOL_SOCKET, socket.SO_REUSEPORT)) + try: + self.assertFalse( + sock.getsockopt( + socket.SOL_SOCKET, socket.SO_REUSEPORT)) + except OSError: + # SO_REUSEPORT is not actually supported, bail! + server.close() + return server.close() test_utils.run_briefly(self.loop) diff --git a/lib-python/3/test/test_context.py b/lib-python/3/test/test_context.py --- a/lib-python/3/test/test_context.py +++ b/lib-python/3/test/test_context.py @@ -24,7 +24,7 @@ class ContextTest(unittest.TestCase): def test_context_var_new_1(self): - with self.assertRaisesRegex(TypeError, 'takes exactly 1'): + with self.assertRaises(TypeError): contextvars.ContextVar() with self.assertRaisesRegex(TypeError, 'must be a str'): @@ -76,11 +76,11 @@ pass def test_context_new_1(self): - with self.assertRaisesRegex(TypeError, 'any arguments'): + with self.assertRaises(TypeError): contextvars.Context(1) - with self.assertRaisesRegex(TypeError, 'any arguments'): + with self.assertRaises(TypeError): contextvars.Context(1, a=1) - with self.assertRaisesRegex(TypeError, 'any arguments'): + with self.assertRaises(TypeError): contextvars.Context(a=1) contextvars.Context(**{}) diff --git a/lib-python/3/test/test_dis.py b/lib-python/3/test/test_dis.py --- a/lib-python/3/test/test_dis.py +++ b/lib-python/3/test/test_dis.py @@ -272,7 +272,7 @@ 20 RETURN_VALUE """ -# XXX: change for PyPy? +# changed for PyPy dis_traceback = """\ %3d 0 SETUP_EXCEPT 12 (to 14) @@ -830,9 +830,9 @@ # End fodder for opinfo generation tests expected_outer_line = 1 _line_offset = outer.__code__.co_firstlineno - 1 -code_object_f = outer.__code__.co_consts[3] +code_object_f = outer.__code__.co_consts[2] expected_f_line = code_object_f.co_firstlineno - _line_offset -code_object_inner = code_object_f.co_consts[3] +code_object_inner = code_object_f.co_consts[2] expected_inner_line = code_object_inner.co_firstlineno - _line_offset expected_jumpy_line = 1 @@ -857,22 +857,22 @@ Instruction = dis.Instruction expected_opinfo_outer = [ - 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_CONST', opcode=100, arg=1, 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='LOAD_CONST', opcode=100, arg=2, 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=3, 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='LOAD_CONST', opcode=100, arg=4, argval='', argrepr="''", offset=22, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=5, 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='LOAD_CONST', opcode=100, arg=6, 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='', 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), @@ -880,14 +880,14 @@ ] expected_opinfo_f = [ - 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_CONST', opcode=100, arg=1, 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='LOAD_CONST', opcode=100, arg=2, 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=3, 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), diff --git a/lib-python/3/test/test_extcall.py b/lib-python/3/test/test_extcall.py --- a/lib-python/3/test/test_extcall.py +++ b/lib-python/3/test/test_extcall.py @@ -57,7 +57,7 @@ Traceback (most recent call last): ... TypeError: ...got multiple values for keyword argument 'a' - >>> f(1, 2, a=3, **{'a': 4}, **{'a': 5}) + >>> f(1, 2, a=3, **{'a': 4}, **{'a': 5}) #doctest: +ELLIPSIS Traceback (most recent call last): ... TypeError: ...got multiple values for keyword argument 'a' @@ -254,20 +254,21 @@ ... TypeError: h() argument after * must be an iterable, not function - >>> h(*[1], *h) + >>> h(*[1], *h) #doctest: +ELLIPSIS Traceback (most recent call last): ... - TypeError: h() argument after * must be an iterable, not function + TypeError: ... >>> dir(*h) Traceback (most recent call last): ... TypeError: dir() argument after * must be an iterable, not function - >>> None(*h) + >>> None(**h) #doctest: +ELLIPSIS Traceback (most recent call last): ... - TypeError: ...argument after * must be an iterable, not function + TypeError: ... object argument after ** must be a mapping, \ +not function >>> h(**h) Traceback (most recent call last): @@ -289,35 +290,20 @@ ... TypeError: h() argument after ** must be a mapping, not list - >>> h(**{'a': 1}, **h) + >>> h(**{'a': 1}, **h) #doctest: +ELLIPSIS Traceback (most recent call last): ... - TypeError: h() argument after ** must be a mapping, not function + TypeError: ...argument after ** must be a mapping, not function - >>> h(**{'a': 1}, **[]) + >>> h(**{'a': 1}, **[]) #doctest: +ELLIPSIS Traceback (most recent call last): ... - TypeError: h() argument after ** must be a mapping, not list + TypeError: ...argument after ** must be a mapping, not list >>> dir(**h) Traceback (most recent call last): ... - TypeError: ...argument after * must be an iterable, not function - - >>> None(*h) #doctest: +ELLIPSIS - Traceback (most recent call last): - ... - TypeError: ...argument after * must be an iterable, not function - - >>> h(**h) #doctest: +ELLIPSIS - Traceback (most recent call last): - ... - TypeError: ...argument after ** must be a mapping, not function - - >>> dir(**h) #doctest: +ELLIPSIS - Traceback (most recent call last): - ... - TypeError: ...argument after ** must be a mapping, not function + TypeError: dir() argument after ** must be a mapping, not function >>> None(**h) #doctest: +ELLIPSIS Traceback (most recent call last): diff --git a/lib-python/3/test/test_flufl.py b/lib-python/3/test/test_flufl.py --- a/lib-python/3/test/test_flufl.py +++ b/lib-python/3/test/test_flufl.py @@ -15,7 +15,7 @@ self.assertEqual(cm.exception.text, '2 != 3\n') self.assertEqual(cm.exception.filename, '<FLUFL test>') self.assertEqual(cm.exception.lineno, 2) - self.assertEqual(cm.exception.offset, 4) + self.assertEqual(cm.exception.offset, 2) # changed in PyPy def test_guido_as_bdfl(self): code = '2 {0} 3' @@ -26,7 +26,7 @@ self.assertEqual(cm.exception.text, '2 <> 3\n') self.assertEqual(cm.exception.filename, '<FLUFL test>') self.assertEqual(cm.exception.lineno, 1) - self.assertEqual(cm.exception.offset, 4) + self.assertEqual(cm.exception.offset, 2) # changed in PyPy if __name__ == '__main__': diff --git a/lib-python/3/test/test_import/__init__.py b/lib-python/3/test/test_import/__init__.py --- a/lib-python/3/test/test_import/__init__.py +++ b/lib-python/3/test/test_import/__init__.py @@ -414,16 +414,22 @@ os.does_not_exist def test_concurrency(self): + def delay_has_deadlock(frame, event, arg): + if event == 'call' and frame.f_code.co_name == 'has_deadlock': + time.sleep(0.05) + sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'data')) try: exc = None def run(): + sys.settrace(delay_has_deadlock) event.wait() try: import package except BaseException as e: nonlocal exc exc = e + sys.settrace(None) for i in range(10): event = threading.Event() diff --git a/lib_pypy/_cffi_ssl/_stdssl/__init__.py b/lib_pypy/_cffi_ssl/_stdssl/__init__.py --- a/lib_pypy/_cffi_ssl/_stdssl/__init__.py +++ b/lib_pypy/_cffi_ssl/_stdssl/__init__.py @@ -1,4 +1,5 @@ import sys +import os import time import _thread import weakref @@ -10,7 +11,7 @@ import os msg = "\n\nThe _ssl cffi module either doesn't exist or is incompatible with your machine's shared libraries.\n" + \ "If you have a compiler installed, you can try to rebuild it by running:\n" + \ - "cd %s\n" % os.path.abspath(os.path.dirname(os.path.dirname(__file__))) + \ + "cd %s\n" % os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) + \ "%s _ssl_build.py\n" % sys.executable raise ImportError(str(e) + msg) @@ -83,6 +84,11 @@ OP_NO_SSLv2 = lib.SSL_OP_NO_SSLv2 OP_NO_SSLv3 = lib.SSL_OP_NO_SSLv3 OP_NO_TLSv1_3 = lib.SSL_OP_NO_TLSv1_3 +if OPENSSL_VERSION_INFO > (1, 1, 0, 0, 0): + # OP_ENABLE_MIDDLEBOX_COMPAT = lib.SSL_OP_ENABLE_MIDDLEBOX_COMPAT + # XXX should be conditionally compiled into lib + OP_ENABLE_MIDDLEBOX_COMPAT = 0x00100000 + SSL_CLIENT = 0 @@ -289,6 +295,20 @@ mode |= lib.SSL_MODE_AUTO_RETRY lib.SSL_set_mode(ssl, mode) + if HAS_TLSv1_3: + if sslctx._post_handshake_auth: + if socket_type == SSL_SERVER: + # bpo-37428: OpenSSL does not ignore SSL_VERIFY_POST_HANDSHAKE. + # Set SSL_VERIFY_POST_HANDSHAKE flag only for server sockets and + # only in combination with SSL_VERIFY_PEER flag. + mode = lib.SSL_CTX_get_verify_mode(lib.SSL_get_SSL_CTX(self.ssl)) + if (mode & lib.SSL_VERIFY_PEER): + verify_cb = lib.SSL_get_verify_callback(self.ssl) + mode |= lib.SSL_VERIFY_POST_HANDSHAKE + lib.SSL_set_verify(ssl, mode, verify_cb) + else: + lib.SSL_set_post_handshake_auth(ssl, 1) + if HAS_SNI and self.server_hostname: name = _str_to_ffi_buffer(self.server_hostname) lib.SSL_set_tlsext_host_name(ssl, name) @@ -711,6 +731,15 @@ else: return None _______________________________________________ pypy-commit mailing list [email protected] https://mail.python.org/mailman/listinfo/pypy-commit
