From jython-checkins at python.org Tue May 1 00:38:08 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Tue, 01 May 2012 00:38:08 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_from=3A?= Message-ID: http://hg.python.org/jython/rev/adaff7b065b4 changeset: 6632:adaff7b065b4 user: Frank Wierzbicki date: Mon Apr 30 11:14:37 2012 -0700 summary: from: http://hg.python.org/cpython/Lib/test/test_long.py at 22db03646d9b files: Lib/test/test_long.py | 914 ++++++++++++++++++++++++++++++ 1 files changed, 914 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py new file mode 100644 --- /dev/null +++ b/Lib/test/test_long.py @@ -0,0 +1,914 @@ +import unittest +from test import test_support +import sys + +import random +import math + +# Used for lazy formatting of failure messages +class Frm(object): + def __init__(self, format, *args): + self.format = format + self.args = args + + def __str__(self): + return self.format % self.args + +# SHIFT should match the value in longintrepr.h for best testing. +SHIFT = sys.long_info.bits_per_digit +BASE = 2 ** SHIFT +MASK = BASE - 1 +KARATSUBA_CUTOFF = 70 # from longobject.c + +# Max number of base BASE digits to use in test cases. Doubling +# this will more than double the runtime. +MAXDIGITS = 15 + +# build some special values +special = map(long, [0, 1, 2, BASE, BASE >> 1]) +special.append(0x5555555555555555L) +special.append(0xaaaaaaaaaaaaaaaaL) +# some solid strings of one bits +p2 = 4L # 0 and 1 already added +for i in range(2*SHIFT): + special.append(p2 - 1) + p2 = p2 << 1 +del p2 +# add complements & negations +special = special + map(lambda x: ~x, special) + \ + map(lambda x: -x, special) + +L = [ + ('0', 0), + ('1', 1), + ('9', 9), + ('10', 10), + ('99', 99), + ('100', 100), + ('314', 314), + (' 314', 314), + ('314 ', 314), + (' \t\t 314 \t\t ', 314), + (repr(sys.maxint), sys.maxint), + (' 1x', ValueError), + (' 1 ', 1), + (' 1\02 ', ValueError), + ('', ValueError), + (' ', ValueError), + (' \t\t ', ValueError) +] +if test_support.have_unicode: + L += [ + (unicode('0'), 0), + (unicode('1'), 1), + (unicode('9'), 9), + (unicode('10'), 10), + (unicode('99'), 99), + (unicode('100'), 100), + (unicode('314'), 314), + (unicode(' 314'), 314), + (unicode('\u0663\u0661\u0664 ','raw-unicode-escape'), 314), + (unicode(' \t\t 314 \t\t '), 314), + (unicode(' 1x'), ValueError), + (unicode(' 1 '), 1), + (unicode(' 1\02 '), ValueError), + (unicode(''), ValueError), + (unicode(' '), ValueError), + (unicode(' \t\t '), ValueError), + (unichr(0x200), ValueError), +] + + +class LongTest(unittest.TestCase): + + # Get quasi-random long consisting of ndigits digits (in base BASE). + # quasi == the most-significant digit will not be 0, and the number + # is constructed to contain long strings of 0 and 1 bits. These are + # more likely than random bits to provoke digit-boundary errors. + # The sign of the number is also random. + + def getran(self, ndigits): + self.assertTrue(ndigits > 0) + nbits_hi = ndigits * SHIFT + nbits_lo = nbits_hi - SHIFT + 1 + answer = 0L + nbits = 0 + r = int(random.random() * (SHIFT * 2)) | 1 # force 1 bits to start + while nbits < nbits_lo: + bits = (r >> 1) + 1 + bits = min(bits, nbits_hi - nbits) + self.assertTrue(1 <= bits <= SHIFT) + nbits = nbits + bits + answer = answer << bits + if r & 1: + answer = answer | ((1 << bits) - 1) + r = int(random.random() * (SHIFT * 2)) + self.assertTrue(nbits_lo <= nbits <= nbits_hi) + if random.random() < 0.5: + answer = -answer + return answer + + # Get random long consisting of ndigits random digits (relative to base + # BASE). The sign bit is also random. + + def getran2(ndigits): + answer = 0L + for i in xrange(ndigits): + answer = (answer << SHIFT) | random.randint(0, MASK) + if random.random() < 0.5: + answer = -answer + return answer + + def check_division(self, x, y): + eq = self.assertEqual + q, r = divmod(x, y) + q2, r2 = x//y, x%y + pab, pba = x*y, y*x + eq(pab, pba, Frm("multiplication does not commute for %r and %r", x, y)) + eq(q, q2, Frm("divmod returns different quotient than / for %r and %r", x, y)) + eq(r, r2, Frm("divmod returns different mod than %% for %r and %r", x, y)) + eq(x, q*y + r, Frm("x != q*y + r after divmod on x=%r, y=%r", x, y)) + if y > 0: + self.assertTrue(0 <= r < y, Frm("bad mod from divmod on %r and %r", x, y)) + else: + self.assertTrue(y < r <= 0, Frm("bad mod from divmod on %r and %r", x, y)) + + def test_division(self): + digits = range(1, MAXDIGITS+1) + range(KARATSUBA_CUTOFF, + KARATSUBA_CUTOFF + 14) + digits.append(KARATSUBA_CUTOFF * 3) + for lenx in digits: + x = self.getran(lenx) + for leny in digits: + y = self.getran(leny) or 1L + self.check_division(x, y) + + # specific numbers chosen to exercise corner cases of the + # current long division implementation + + # 30-bit cases involving a quotient digit estimate of BASE+1 + self.check_division(1231948412290879395966702881L, + 1147341367131428698L) + self.check_division(815427756481275430342312021515587883L, + 707270836069027745L) + self.check_division(627976073697012820849443363563599041L, + 643588798496057020L) + self.check_division(1115141373653752303710932756325578065L, + 1038556335171453937726882627L) + # 30-bit cases that require the post-subtraction correction step + self.check_division(922498905405436751940989320930368494L, + 949985870686786135626943396L) + self.check_division(768235853328091167204009652174031844L, + 1091555541180371554426545266L) + + # 15-bit cases involving a quotient digit estimate of BASE+1 + self.check_division(20172188947443L, 615611397L) + self.check_division(1020908530270155025L, 950795710L) + self.check_division(128589565723112408L, 736393718L) + self.check_division(609919780285761575L, 18613274546784L) + # 15-bit cases that require the post-subtraction correction step + self.check_division(710031681576388032L, 26769404391308L) + self.check_division(1933622614268221L, 30212853348836L) + + + + def test_karatsuba(self): + digits = range(1, 5) + range(KARATSUBA_CUTOFF, KARATSUBA_CUTOFF + 10) + digits.extend([KARATSUBA_CUTOFF * 10, KARATSUBA_CUTOFF * 100]) + + bits = [digit * SHIFT for digit in digits] + + # Test products of long strings of 1 bits -- (2**x-1)*(2**y-1) == + # 2**(x+y) - 2**x - 2**y + 1, so the proper result is easy to check. + for abits in bits: + a = (1L << abits) - 1 + for bbits in bits: + if bbits < abits: + continue + b = (1L << bbits) - 1 + x = a * b + y = ((1L << (abits + bbits)) - + (1L << abits) - + (1L << bbits) + + 1) + self.assertEqual(x, y, + Frm("bad result for a*b: a=%r, b=%r, x=%r, y=%r", a, b, x, y)) + + def check_bitop_identities_1(self, x): + eq = self.assertEqual + eq(x & 0, 0, Frm("x & 0 != 0 for x=%r", x)) + eq(x | 0, x, Frm("x | 0 != x for x=%r", x)) + eq(x ^ 0, x, Frm("x ^ 0 != x for x=%r", x)) + eq(x & -1, x, Frm("x & -1 != x for x=%r", x)) + eq(x | -1, -1, Frm("x | -1 != -1 for x=%r", x)) + eq(x ^ -1, ~x, Frm("x ^ -1 != ~x for x=%r", x)) + eq(x, ~~x, Frm("x != ~~x for x=%r", x)) + eq(x & x, x, Frm("x & x != x for x=%r", x)) + eq(x | x, x, Frm("x | x != x for x=%r", x)) + eq(x ^ x, 0, Frm("x ^ x != 0 for x=%r", x)) + eq(x & ~x, 0, Frm("x & ~x != 0 for x=%r", x)) + eq(x | ~x, -1, Frm("x | ~x != -1 for x=%r", x)) + eq(x ^ ~x, -1, Frm("x ^ ~x != -1 for x=%r", x)) + eq(-x, 1 + ~x, Frm("not -x == 1 + ~x for x=%r", x)) + eq(-x, ~(x-1), Frm("not -x == ~(x-1) forx =%r", x)) + for n in xrange(2*SHIFT): + p2 = 2L ** n + eq(x << n >> n, x, + Frm("x << n >> n != x for x=%r, n=%r", (x, n))) + eq(x // p2, x >> n, + Frm("x // p2 != x >> n for x=%r n=%r p2=%r", (x, n, p2))) + eq(x * p2, x << n, + Frm("x * p2 != x << n for x=%r n=%r p2=%r", (x, n, p2))) + eq(x & -p2, x >> n << n, + Frm("not x & -p2 == x >> n << n for x=%r n=%r p2=%r", (x, n, p2))) + eq(x & -p2, x & ~(p2 - 1), + Frm("not x & -p2 == x & ~(p2 - 1) for x=%r n=%r p2=%r", (x, n, p2))) + + def check_bitop_identities_2(self, x, y): + eq = self.assertEqual + eq(x & y, y & x, Frm("x & y != y & x for x=%r, y=%r", (x, y))) + eq(x | y, y | x, Frm("x | y != y | x for x=%r, y=%r", (x, y))) + eq(x ^ y, y ^ x, Frm("x ^ y != y ^ x for x=%r, y=%r", (x, y))) + eq(x ^ y ^ x, y, Frm("x ^ y ^ x != y for x=%r, y=%r", (x, y))) + eq(x & y, ~(~x | ~y), Frm("x & y != ~(~x | ~y) for x=%r, y=%r", (x, y))) + eq(x | y, ~(~x & ~y), Frm("x | y != ~(~x & ~y) for x=%r, y=%r", (x, y))) + eq(x ^ y, (x | y) & ~(x & y), + Frm("x ^ y != (x | y) & ~(x & y) for x=%r, y=%r", (x, y))) + eq(x ^ y, (x & ~y) | (~x & y), + Frm("x ^ y == (x & ~y) | (~x & y) for x=%r, y=%r", (x, y))) + eq(x ^ y, (x | y) & (~x | ~y), + Frm("x ^ y == (x | y) & (~x | ~y) for x=%r, y=%r", (x, y))) + + def check_bitop_identities_3(self, x, y, z): + eq = self.assertEqual + eq((x & y) & z, x & (y & z), + Frm("(x & y) & z != x & (y & z) for x=%r, y=%r, z=%r", (x, y, z))) + eq((x | y) | z, x | (y | z), + Frm("(x | y) | z != x | (y | z) for x=%r, y=%r, z=%r", (x, y, z))) + eq((x ^ y) ^ z, x ^ (y ^ z), + Frm("(x ^ y) ^ z != x ^ (y ^ z) for x=%r, y=%r, z=%r", (x, y, z))) + eq(x & (y | z), (x & y) | (x & z), + Frm("x & (y | z) != (x & y) | (x & z) for x=%r, y=%r, z=%r", (x, y, z))) + eq(x | (y & z), (x | y) & (x | z), + Frm("x | (y & z) != (x | y) & (x | z) for x=%r, y=%r, z=%r", (x, y, z))) + + def test_bitop_identities(self): + for x in special: + self.check_bitop_identities_1(x) + digits = xrange(1, MAXDIGITS+1) + for lenx in digits: + x = self.getran(lenx) + self.check_bitop_identities_1(x) + for leny in digits: + y = self.getran(leny) + self.check_bitop_identities_2(x, y) + self.check_bitop_identities_3(x, y, self.getran((lenx + leny)//2)) + + def slow_format(self, x, base): + if (x, base) == (0, 8): + # this is an oddball! + return "0L" + digits = [] + sign = 0 + if x < 0: + sign, x = 1, -x + while x: + x, r = divmod(x, base) + digits.append(int(r)) + digits.reverse() + digits = digits or [0] + return '-'[:sign] + \ + {8: '0', 10: '', 16: '0x'}[base] + \ + "".join(map(lambda i: "0123456789abcdef"[i], digits)) + "L" + + def check_format_1(self, x): + for base, mapper in (8, oct), (10, repr), (16, hex): + got = mapper(x) + expected = self.slow_format(x, base) + msg = Frm("%s returned %r but expected %r for %r", + mapper.__name__, got, expected, x) + self.assertEqual(got, expected, msg) + self.assertEqual(long(got, 0), x, Frm('long("%s", 0) != %r', got, x)) + # str() has to be checked a little differently since there's no + # trailing "L" + got = str(x) + expected = self.slow_format(x, 10)[:-1] + msg = Frm("%s returned %r but expected %r for %r", + mapper.__name__, got, expected, x) + self.assertEqual(got, expected, msg) + + def test_format(self): + for x in special: + self.check_format_1(x) + for i in xrange(10): + for lenx in xrange(1, MAXDIGITS+1): + x = self.getran(lenx) + self.check_format_1(x) + + def test_long(self): + self.assertEqual(long(314), 314L) + self.assertEqual(long(3.14), 3L) + self.assertEqual(long(314L), 314L) + # Check that long() of basic types actually returns a long + self.assertEqual(type(long(314)), long) + self.assertEqual(type(long(3.14)), long) + self.assertEqual(type(long(314L)), long) + # Check that conversion from float truncates towards zero + self.assertEqual(long(-3.14), -3L) + self.assertEqual(long(3.9), 3L) + self.assertEqual(long(-3.9), -3L) + self.assertEqual(long(3.5), 3L) + self.assertEqual(long(-3.5), -3L) + self.assertEqual(long("-3"), -3L) + self.assertEqual(long("0b10", 2), 2L) + self.assertEqual(long("0o10", 8), 8L) + self.assertEqual(long("0x10", 16), 16L) + if test_support.have_unicode: + self.assertEqual(long(unicode("-3")), -3L) + # Different base: + self.assertEqual(long("10",16), 16L) + if test_support.have_unicode: + self.assertEqual(long(unicode("10"),16), 16L) + # Check conversions from string (same test set as for int(), and then some) + LL = [ + ('1' + '0'*20, 10L**20), + ('1' + '0'*100, 10L**100) + ] + L2 = L[:] + if test_support.have_unicode: + L2 += [ + (unicode('1') + unicode('0')*20, 10L**20), + (unicode('1') + unicode('0')*100, 10L**100), + ] + for s, v in L2 + LL: + for sign in "", "+", "-": + for prefix in "", " ", "\t", " \t\t ": + ss = prefix + sign + s + vv = v + if sign == "-" and v is not ValueError: + vv = -v + try: + self.assertEqual(long(ss), long(vv)) + except v: + pass + + self.assertRaises(ValueError, long, '123\0') + self.assertRaises(ValueError, long, '53', 40) + self.assertRaises(TypeError, long, 1, 12) + + # tests with base 0 + self.assertEqual(long(' 0123 ', 0), 83) + self.assertEqual(long(' 0123 ', 0), 83) + self.assertEqual(long('000', 0), 0) + self.assertEqual(long('0o123', 0), 83) + self.assertEqual(long('0x123', 0), 291) + self.assertEqual(long('0b100', 0), 4) + self.assertEqual(long(' 0O123 ', 0), 83) + self.assertEqual(long(' 0X123 ', 0), 291) + self.assertEqual(long(' 0B100 ', 0), 4) + self.assertEqual(long('0', 0), 0) + self.assertEqual(long('+0', 0), 0) + self.assertEqual(long('-0', 0), 0) + self.assertEqual(long('00', 0), 0) + self.assertRaises(ValueError, long, '08', 0) + self.assertRaises(ValueError, long, '-012395', 0) + + # SF patch #1638879: embedded NULs were not detected with + # explicit base + self.assertRaises(ValueError, long, '123\0', 10) + self.assertRaises(ValueError, long, '123\x00 245', 20) + + self.assertEqual(long('100000000000000000000000000000000', 2), + 4294967296) + self.assertEqual(long('102002022201221111211', 3), 4294967296) + self.assertEqual(long('10000000000000000', 4), 4294967296) + self.assertEqual(long('32244002423141', 5), 4294967296) + self.assertEqual(long('1550104015504', 6), 4294967296) + self.assertEqual(long('211301422354', 7), 4294967296) + self.assertEqual(long('40000000000', 8), 4294967296) + self.assertEqual(long('12068657454', 9), 4294967296) + self.assertEqual(long('4294967296', 10), 4294967296) + self.assertEqual(long('1904440554', 11), 4294967296) + self.assertEqual(long('9ba461594', 12), 4294967296) + self.assertEqual(long('535a79889', 13), 4294967296) + self.assertEqual(long('2ca5b7464', 14), 4294967296) + self.assertEqual(long('1a20dcd81', 15), 4294967296) + self.assertEqual(long('100000000', 16), 4294967296) + self.assertEqual(long('a7ffda91', 17), 4294967296) + self.assertEqual(long('704he7g4', 18), 4294967296) + self.assertEqual(long('4f5aff66', 19), 4294967296) + self.assertEqual(long('3723ai4g', 20), 4294967296) + self.assertEqual(long('281d55i4', 21), 4294967296) + self.assertEqual(long('1fj8b184', 22), 4294967296) + self.assertEqual(long('1606k7ic', 23), 4294967296) + self.assertEqual(long('mb994ag', 24), 4294967296) + self.assertEqual(long('hek2mgl', 25), 4294967296) + self.assertEqual(long('dnchbnm', 26), 4294967296) + self.assertEqual(long('b28jpdm', 27), 4294967296) + self.assertEqual(long('8pfgih4', 28), 4294967296) + self.assertEqual(long('76beigg', 29), 4294967296) + self.assertEqual(long('5qmcpqg', 30), 4294967296) + self.assertEqual(long('4q0jto4', 31), 4294967296) + self.assertEqual(long('4000000', 32), 4294967296) + self.assertEqual(long('3aokq94', 33), 4294967296) + self.assertEqual(long('2qhxjli', 34), 4294967296) + self.assertEqual(long('2br45qb', 35), 4294967296) + self.assertEqual(long('1z141z4', 36), 4294967296) + + self.assertEqual(long('100000000000000000000000000000001', 2), + 4294967297) + self.assertEqual(long('102002022201221111212', 3), 4294967297) + self.assertEqual(long('10000000000000001', 4), 4294967297) + self.assertEqual(long('32244002423142', 5), 4294967297) + self.assertEqual(long('1550104015505', 6), 4294967297) + self.assertEqual(long('211301422355', 7), 4294967297) + self.assertEqual(long('40000000001', 8), 4294967297) + self.assertEqual(long('12068657455', 9), 4294967297) + self.assertEqual(long('4294967297', 10), 4294967297) + self.assertEqual(long('1904440555', 11), 4294967297) + self.assertEqual(long('9ba461595', 12), 4294967297) + self.assertEqual(long('535a7988a', 13), 4294967297) + self.assertEqual(long('2ca5b7465', 14), 4294967297) + self.assertEqual(long('1a20dcd82', 15), 4294967297) + self.assertEqual(long('100000001', 16), 4294967297) + self.assertEqual(long('a7ffda92', 17), 4294967297) + self.assertEqual(long('704he7g5', 18), 4294967297) + self.assertEqual(long('4f5aff67', 19), 4294967297) + self.assertEqual(long('3723ai4h', 20), 4294967297) + self.assertEqual(long('281d55i5', 21), 4294967297) + self.assertEqual(long('1fj8b185', 22), 4294967297) + self.assertEqual(long('1606k7id', 23), 4294967297) + self.assertEqual(long('mb994ah', 24), 4294967297) + self.assertEqual(long('hek2mgm', 25), 4294967297) + self.assertEqual(long('dnchbnn', 26), 4294967297) + self.assertEqual(long('b28jpdn', 27), 4294967297) + self.assertEqual(long('8pfgih5', 28), 4294967297) + self.assertEqual(long('76beigh', 29), 4294967297) + self.assertEqual(long('5qmcpqh', 30), 4294967297) + self.assertEqual(long('4q0jto5', 31), 4294967297) + self.assertEqual(long('4000001', 32), 4294967297) + self.assertEqual(long('3aokq95', 33), 4294967297) + self.assertEqual(long('2qhxjlj', 34), 4294967297) + self.assertEqual(long('2br45qc', 35), 4294967297) + self.assertEqual(long('1z141z5', 36), 4294967297) + + + def test_conversion(self): + # Test __long__() + class ClassicMissingMethods: + pass + self.assertRaises(AttributeError, long, ClassicMissingMethods()) + + class MissingMethods(object): + pass + self.assertRaises(TypeError, long, MissingMethods()) + + class Foo0: + def __long__(self): + return 42L + + class Foo1(object): + def __long__(self): + return 42L + + class Foo2(long): + def __long__(self): + return 42L + + class Foo3(long): + def __long__(self): + return self + + class Foo4(long): + def __long__(self): + return 42 + + class Foo5(long): + def __long__(self): + return 42. + + self.assertEqual(long(Foo0()), 42L) + self.assertEqual(long(Foo1()), 42L) + self.assertEqual(long(Foo2()), 42L) + self.assertEqual(long(Foo3()), 0) + self.assertEqual(long(Foo4()), 42) + self.assertRaises(TypeError, long, Foo5()) + + class Classic: + pass + for base in (object, Classic): + class LongOverridesTrunc(base): + def __long__(self): + return 42 + def __trunc__(self): + return -12 + self.assertEqual(long(LongOverridesTrunc()), 42) + + class JustTrunc(base): + def __trunc__(self): + return 42 + self.assertEqual(long(JustTrunc()), 42) + + for trunc_result_base in (object, Classic): + class Integral(trunc_result_base): + def __int__(self): + return 42 + + class TruncReturnsNonLong(base): + def __trunc__(self): + return Integral() + self.assertEqual(long(TruncReturnsNonLong()), 42) + + class NonIntegral(trunc_result_base): + def __trunc__(self): + # Check that we avoid infinite recursion. + return NonIntegral() + + class TruncReturnsNonIntegral(base): + def __trunc__(self): + return NonIntegral() + try: + long(TruncReturnsNonIntegral()) + except TypeError as e: + self.assertEqual(str(e), + "__trunc__ returned non-Integral" + " (type NonIntegral)") + else: + self.fail("Failed to raise TypeError with %s" % + ((base, trunc_result_base),)) + + def test_misc(self): + + # check the extremes in int<->long conversion + hugepos = sys.maxint + hugeneg = -hugepos - 1 + hugepos_aslong = long(hugepos) + hugeneg_aslong = long(hugeneg) + self.assertEqual(hugepos, hugepos_aslong, "long(sys.maxint) != sys.maxint") + self.assertEqual(hugeneg, hugeneg_aslong, + "long(-sys.maxint-1) != -sys.maxint-1") + + # long -> int should not fail for hugepos_aslong or hugeneg_aslong + x = int(hugepos_aslong) + try: + self.assertEqual(x, hugepos, + "converting sys.maxint to long and back to int fails") + except OverflowError: + self.fail("int(long(sys.maxint)) overflowed!") + if not isinstance(x, int): + self.fail("int(long(sys.maxint)) should have returned int") + x = int(hugeneg_aslong) + try: + self.assertEqual(x, hugeneg, + "converting -sys.maxint-1 to long and back to int fails") + except OverflowError: + self.fail("int(long(-sys.maxint-1)) overflowed!") + if not isinstance(x, int): + self.fail("int(long(-sys.maxint-1)) should have returned int") + # but long -> int should overflow for hugepos+1 and hugeneg-1 + x = hugepos_aslong + 1 + try: + y = int(x) + except OverflowError: + self.fail("int(long(sys.maxint) + 1) mustn't overflow") + self.assertIsInstance(y, long, + "int(long(sys.maxint) + 1) should have returned long") + + x = hugeneg_aslong - 1 + try: + y = int(x) + except OverflowError: + self.fail("int(long(-sys.maxint-1) - 1) mustn't overflow") + self.assertIsInstance(y, long, + "int(long(-sys.maxint-1) - 1) should have returned long") + + class long2(long): + pass + x = long2(1L<<100) + y = int(x) + self.assertTrue(type(y) is long, + "overflowing int conversion must return long not long subtype") + + # long -> Py_ssize_t conversion + class X(object): + def __getslice__(self, i, j): + return i, j + + with test_support.check_py3k_warnings(): + self.assertEqual(X()[-5L:7L], (-5, 7)) + # use the clamping effect to test the smallest and largest longs + # that fit a Py_ssize_t + slicemin, slicemax = X()[-2L**100:2L**100] + self.assertEqual(X()[slicemin:slicemax], (slicemin, slicemax)) + + def test_issue9869(self): + # Issue 9869: Interpreter crash when initializing an instance + # of a long subclass from an object whose __long__ method returns + # a plain int. + class BadLong(object): + def __long__(self): + return 1000000 + + class MyLong(long): + pass + + x = MyLong(BadLong()) + self.assertIsInstance(x, long) + self.assertEqual(x, 1000000) + + +# ----------------------------------- tests of auto int->long conversion + + def test_auto_overflow(self): + special = [0, 1, 2, 3, sys.maxint-1, sys.maxint, sys.maxint+1] + sqrt = int(math.sqrt(sys.maxint)) + special.extend([sqrt-1, sqrt, sqrt+1]) + special.extend([-i for i in special]) + + def checkit(*args): + # Heavy use of nested scopes here! + self.assertEqual(got, expected, + Frm("for %r expected %r got %r", args, expected, got)) + + for x in special: + longx = long(x) + + expected = -longx + got = -x + checkit('-', x) + + for y in special: + longy = long(y) + + expected = longx + longy + got = x + y + checkit(x, '+', y) + + expected = longx - longy + got = x - y + checkit(x, '-', y) + + expected = longx * longy + got = x * y + checkit(x, '*', y) + + if y: + with test_support.check_py3k_warnings(): + expected = longx / longy + got = x / y + checkit(x, '/', y) + + expected = longx // longy + got = x // y + checkit(x, '//', y) + + expected = divmod(longx, longy) + got = divmod(longx, longy) + checkit(x, 'divmod', y) + + if abs(y) < 5 and not (x == 0 and y < 0): + expected = longx ** longy + got = x ** y + checkit(x, '**', y) + + for z in special: + if z != 0 : + if y >= 0: + expected = pow(longx, longy, long(z)) + got = pow(x, y, z) + checkit('pow', x, y, '%', z) + else: + self.assertRaises(TypeError, pow,longx, longy, long(z)) + + @unittest.skipUnless(float.__getformat__("double").startswith("IEEE"), + "test requires IEEE 754 doubles") + def test_float_conversion(self): + import sys + DBL_MAX = sys.float_info.max + DBL_MAX_EXP = sys.float_info.max_exp + DBL_MANT_DIG = sys.float_info.mant_dig + + exact_values = [0L, 1L, 2L, + long(2**53-3), + long(2**53-2), + long(2**53-1), + long(2**53), + long(2**53+2), + long(2**54-4), + long(2**54-2), + long(2**54), + long(2**54+4)] + for x in exact_values: + self.assertEqual(long(float(x)), x) + self.assertEqual(long(float(-x)), -x) + + # test round-half-even + for x, y in [(1, 0), (2, 2), (3, 4), (4, 4), (5, 4), (6, 6), (7, 8)]: + for p in xrange(15): + self.assertEqual(long(float(2L**p*(2**53+x))), 2L**p*(2**53+y)) + + for x, y in [(0, 0), (1, 0), (2, 0), (3, 4), (4, 4), (5, 4), (6, 8), + (7, 8), (8, 8), (9, 8), (10, 8), (11, 12), (12, 12), + (13, 12), (14, 16), (15, 16)]: + for p in xrange(15): + self.assertEqual(long(float(2L**p*(2**54+x))), 2L**p*(2**54+y)) + + # behaviour near extremes of floating-point range + long_dbl_max = long(DBL_MAX) + top_power = 2**DBL_MAX_EXP + halfway = (long_dbl_max + top_power)//2 + self.assertEqual(float(long_dbl_max), DBL_MAX) + self.assertEqual(float(long_dbl_max+1), DBL_MAX) + self.assertEqual(float(halfway-1), DBL_MAX) + self.assertRaises(OverflowError, float, halfway) + self.assertEqual(float(1-halfway), -DBL_MAX) + self.assertRaises(OverflowError, float, -halfway) + self.assertRaises(OverflowError, float, top_power-1) + self.assertRaises(OverflowError, float, top_power) + self.assertRaises(OverflowError, float, top_power+1) + self.assertRaises(OverflowError, float, 2*top_power-1) + self.assertRaises(OverflowError, float, 2*top_power) + self.assertRaises(OverflowError, float, top_power*top_power) + + for p in xrange(100): + x = long(2**p * (2**53 + 1) + 1) + y = long(2**p * (2**53+ 2)) + self.assertEqual(long(float(x)), y) + + x = long(2**p * (2**53 + 1)) + y = long(2**p * 2**53) + self.assertEqual(long(float(x)), y) + + def test_float_overflow(self): + for x in -2.0, -1.0, 0.0, 1.0, 2.0: + self.assertEqual(float(long(x)), x) + + shuge = '12345' * 120 + huge = 1L << 30000 + mhuge = -huge + namespace = {'huge': huge, 'mhuge': mhuge, 'shuge': shuge, 'math': math} + for test in ["float(huge)", "float(mhuge)", + "complex(huge)", "complex(mhuge)", + "complex(huge, 1)", "complex(mhuge, 1)", + "complex(1, huge)", "complex(1, mhuge)", + "1. + huge", "huge + 1.", "1. + mhuge", "mhuge + 1.", + "1. - huge", "huge - 1.", "1. - mhuge", "mhuge - 1.", + "1. * huge", "huge * 1.", "1. * mhuge", "mhuge * 1.", + "1. // huge", "huge // 1.", "1. // mhuge", "mhuge // 1.", + "1. / huge", "huge / 1.", "1. / mhuge", "mhuge / 1.", + "1. ** huge", "huge ** 1.", "1. ** mhuge", "mhuge ** 1.", + "math.sin(huge)", "math.sin(mhuge)", + "math.sqrt(huge)", "math.sqrt(mhuge)", # should do better + "math.floor(huge)", "math.floor(mhuge)"]: + + self.assertRaises(OverflowError, eval, test, namespace) + + # XXX Perhaps float(shuge) can raise OverflowError on some box? + # The comparison should not. + self.assertNotEqual(float(shuge), int(shuge), + "float(shuge) should not equal int(shuge)") + + def test_logs(self): + LOG10E = math.log10(math.e) + + for exp in range(10) + [100, 1000, 10000]: + value = 10 ** exp + log10 = math.log10(value) + self.assertAlmostEqual(log10, exp) + + # log10(value) == exp, so log(value) == log10(value)/log10(e) == + # exp/LOG10E + expected = exp / LOG10E + log = math.log(value) + self.assertAlmostEqual(log, expected) + + for bad in -(1L << 10000), -2L, 0L: + self.assertRaises(ValueError, math.log, bad) + self.assertRaises(ValueError, math.log10, bad) + + def test_mixed_compares(self): + eq = self.assertEqual + + # We're mostly concerned with that mixing floats and longs does the + # right stuff, even when longs are too large to fit in a float. + # The safest way to check the results is to use an entirely different + # method, which we do here via a skeletal rational class (which + # represents all Python ints, longs and floats exactly). + class Rat: + def __init__(self, value): + if isinstance(value, (int, long)): + self.n = value + self.d = 1 + elif isinstance(value, float): + # Convert to exact rational equivalent. + f, e = math.frexp(abs(value)) + assert f == 0 or 0.5 <= f < 1.0 + # |value| = f * 2**e exactly + + # Suck up CHUNK bits at a time; 28 is enough so that we suck + # up all bits in 2 iterations for all known binary double- + # precision formats, and small enough to fit in an int. + CHUNK = 28 + top = 0 + # invariant: |value| = (top + f) * 2**e exactly + while f: + f = math.ldexp(f, CHUNK) + digit = int(f) + assert digit >> CHUNK == 0 + top = (top << CHUNK) | digit + f -= digit + assert 0.0 <= f < 1.0 + e -= CHUNK + + # Now |value| = top * 2**e exactly. + if e >= 0: + n = top << e + d = 1 + else: + n = top + d = 1 << -e + if value < 0: + n = -n + self.n = n + self.d = d + assert float(n) / float(d) == value + else: + raise TypeError("can't deal with %r" % value) + + def __cmp__(self, other): + if not isinstance(other, Rat): + other = Rat(other) + return cmp(self.n * other.d, self.d * other.n) + + cases = [0, 0.001, 0.99, 1.0, 1.5, 1e20, 1e200] + # 2**48 is an important boundary in the internals. 2**53 is an + # important boundary for IEEE double precision. + for t in 2.0**48, 2.0**50, 2.0**53: + cases.extend([t - 1.0, t - 0.3, t, t + 0.3, t + 1.0, + long(t-1), long(t), long(t+1)]) + cases.extend([0, 1, 2, sys.maxint, float(sys.maxint)]) + # 1L<<20000 should exceed all double formats. long(1e200) is to + # check that we get equality with 1e200 above. + t = long(1e200) + cases.extend([0L, 1L, 2L, 1L << 20000, t-1, t, t+1]) + cases.extend([-x for x in cases]) + for x in cases: + Rx = Rat(x) + for y in cases: + Ry = Rat(y) + Rcmp = cmp(Rx, Ry) + xycmp = cmp(x, y) + eq(Rcmp, xycmp, Frm("%r %r %d %d", x, y, Rcmp, xycmp)) + eq(x == y, Rcmp == 0, Frm("%r == %r %d", x, y, Rcmp)) + eq(x != y, Rcmp != 0, Frm("%r != %r %d", x, y, Rcmp)) + eq(x < y, Rcmp < 0, Frm("%r < %r %d", x, y, Rcmp)) + eq(x <= y, Rcmp <= 0, Frm("%r <= %r %d", x, y, Rcmp)) + eq(x > y, Rcmp > 0, Frm("%r > %r %d", x, y, Rcmp)) + eq(x >= y, Rcmp >= 0, Frm("%r >= %r %d", x, y, Rcmp)) + + def test_nan_inf(self): + self.assertRaises(OverflowError, long, float('inf')) + self.assertRaises(OverflowError, long, float('-inf')) + self.assertRaises(ValueError, long, float('nan')) + + def test_bit_length(self): + tiny = 1e-10 + for x in xrange(-65000, 65000): + x = long(x) + k = x.bit_length() + # Check equivalence with Python version + self.assertEqual(k, len(bin(x).lstrip('-0b'))) + # Behaviour as specified in the docs + if x != 0: + self.assertTrue(2**(k-1) <= abs(x) < 2**k) + else: + self.assertEqual(k, 0) + # Alternative definition: x.bit_length() == 1 + floor(log_2(x)) + if x != 0: + # When x is an exact power of 2, numeric errors can + # cause floor(log(x)/log(2)) to be one too small; for + # small x this can be fixed by adding a small quantity + # to the quotient before taking the floor. + self.assertEqual(k, 1 + math.floor( + math.log(abs(x))/math.log(2) + tiny)) + + self.assertEqual((0L).bit_length(), 0) + self.assertEqual((1L).bit_length(), 1) + self.assertEqual((-1L).bit_length(), 1) + self.assertEqual((2L).bit_length(), 2) + self.assertEqual((-2L).bit_length(), 2) + for i in [2, 3, 15, 16, 17, 31, 32, 33, 63, 64, 234]: + a = 2L**i + self.assertEqual((a-1).bit_length(), i) + self.assertEqual((1-a).bit_length(), i) + self.assertEqual((a).bit_length(), i+1) + self.assertEqual((-a).bit_length(), i+1) + self.assertEqual((a+1).bit_length(), i+1) + self.assertEqual((-a-1).bit_length(), i+1) + + +def test_main(): + test_support.run_unittest(LongTest) + +if __name__ == "__main__": + test_main() -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Tue May 1 00:38:08 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Tue, 01 May 2012 00:38:08 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_2=2E7_updates_for_long_type?= =?utf8?q?=2E?= Message-ID: http://hg.python.org/jython/rev/23342ccbeb12 changeset: 6633:23342ccbeb12 user: Frank Wierzbicki date: Mon Apr 30 15:38:00 2012 -0700 summary: 2.7 updates for long type. files: Lib/test/test_long.py | 32 +++++++----- src/org/python/core/PyLong.java | 52 ++++++++++++++++---- 2 files changed, 61 insertions(+), 23 deletions(-) diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py --- a/Lib/test/test_long.py +++ b/Lib/test/test_long.py @@ -457,7 +457,10 @@ # Test __long__() class ClassicMissingMethods: pass - self.assertRaises(AttributeError, long, ClassicMissingMethods()) + if test_support.is_jython: + self.assertRaises(TypeError, int, ClassicMissingMethods()) + else: + self.assertRaises(AttributeError, int, ClassicMissingMethods()) class MissingMethods(object): pass @@ -530,9 +533,10 @@ try: long(TruncReturnsNonIntegral()) except TypeError as e: - self.assertEqual(str(e), - "__trunc__ returned non-Integral" - " (type NonIntegral)") + if not test_support.is_jython: + self.assertEqual(str(e), + "__trunc__ returned non-Integral" + " (type NonIntegral)") else: self.fail("Failed to raise TypeError with %s" % ((base, trunc_result_base),)) @@ -719,15 +723,17 @@ halfway = (long_dbl_max + top_power)//2 self.assertEqual(float(long_dbl_max), DBL_MAX) self.assertEqual(float(long_dbl_max+1), DBL_MAX) - self.assertEqual(float(halfway-1), DBL_MAX) - self.assertRaises(OverflowError, float, halfway) - self.assertEqual(float(1-halfway), -DBL_MAX) - self.assertRaises(OverflowError, float, -halfway) - self.assertRaises(OverflowError, float, top_power-1) - self.assertRaises(OverflowError, float, top_power) - self.assertRaises(OverflowError, float, top_power+1) - self.assertRaises(OverflowError, float, 2*top_power-1) - self.assertRaises(OverflowError, float, 2*top_power) + #XXX: Most or all of these fail on Jython ATM - needs investigation. + if not test_support.is_jython: + self.assertEqual(float(halfway-1), DBL_MAX) + self.assertRaises(OverflowError, float, halfway) + self.assertEqual(float(1-halfway), -DBL_MAX) + self.assertRaises(OverflowError, float, -halfway) + self.assertRaises(OverflowError, float, top_power-1) + self.assertRaises(OverflowError, float, top_power) + self.assertRaises(OverflowError, float, top_power+1) + self.assertRaises(OverflowError, float, 2*top_power-1) + self.assertRaises(OverflowError, float, 2*top_power) self.assertRaises(OverflowError, float, top_power*top_power) for p in xrange(100): diff --git a/src/org/python/core/PyLong.java b/src/org/python/core/PyLong.java --- a/src/org/python/core/PyLong.java +++ b/src/org/python/core/PyLong.java @@ -82,15 +82,7 @@ return new PyLong(0); } if (base == -909) { - try { - return x.__long__(); - } catch (PyException pye) { - if (!pye.match(Py.AttributeError)) { - throw pye; - } - throw Py.TypeError(String.format("long() argument must be a string or a number, " - + "not '%.200s'", x.getType().fastGetName())); - } + return asPyLong(x); } if (!(x instanceof PyString)) { throw Py.TypeError("long: can't convert non-string with explicit base"); @@ -99,6 +91,46 @@ } /** + * @return convert to a long. + * @throws TypeError and AttributeError. + */ + private static PyObject asPyLong(PyObject x) { + try { + return x.__long__(); + } catch (PyException pye) { + if (!pye.match(Py.AttributeError)) { + throw pye; + } + try { + PyObject integral = x.invoke("__trunc__"); + return convertIntegralToLong(integral); + } catch (PyException pye2) { + if (!pye2.match(Py.AttributeError)) { + throw pye2; + } + throw Py.TypeError( + String.format("long() argument must be a string or a number, not '%.200s'", x.getType().fastGetName())); + } + } + } + + /** + * @return convert to an int. + * @throws TypeError and AttributeError. + */ + private static PyObject convertIntegralToLong(PyObject integral) { + if (!(integral instanceof PyInteger) && !(integral instanceof PyLong)) { + PyObject i = integral.invoke("__int__"); + if (!(i instanceof PyInteger) && !(i instanceof PyLong)) { + throw Py.TypeError(String.format("__trunc__ returned non-Integral (type %.200s)", + integral.getType().fastGetName())); + } + return i; + } + return integral; + } + + /** * Wimpy, slow approach to new calls for subtypes of long. * * First creates a regular long from whatever arguments we got, then allocates a @@ -125,7 +157,7 @@ throw Py.OverflowError("cannot convert float infinity to long"); } if (Double.isNaN(value)) { - return BigInteger.valueOf(0); + throw Py.ValueError("cannot convert float NaN to integer"); } return new BigDecimal(value).toBigInteger(); } -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Tue May 1 05:00:36 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Tue, 01 May 2012 05:00:36 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_from=3A?= Message-ID: http://hg.python.org/jython/rev/f544cce955a4 changeset: 6634:f544cce955a4 user: Frank Wierzbicki date: Mon Apr 30 19:31:44 2012 -0700 summary: from: http://hg.python.org/cpython/Lib/test/test_pdb.py at 22db03646d9b files: Lib/test/test_pdb.py | 315 +++++++++++++++++++++++++++++++ 1 files changed, 315 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py new file mode 100644 --- /dev/null +++ b/Lib/test/test_pdb.py @@ -0,0 +1,315 @@ +# A test suite for pdb; at the moment, this only validates skipping of +# specified test modules (RFE #5142). + +import imp +import sys +import os +import unittest +import subprocess + +from test import test_support +# This little helper class is essential for testing pdb under doctest. +from test_doctest import _FakeInput + + +class PdbTestInput(object): + """Context manager that makes testing Pdb in doctests easier.""" + + def __init__(self, input): + self.input = input + + def __enter__(self): + self.real_stdin = sys.stdin + sys.stdin = _FakeInput(self.input) + + def __exit__(self, *exc): + sys.stdin = self.real_stdin + + +def write(x): + print x + +def test_pdb_displayhook(): + """This tests the custom displayhook for pdb. + + >>> def test_function(foo, bar): + ... import pdb; pdb.Pdb().set_trace() + ... pass + + >>> with PdbTestInput([ + ... 'foo', + ... 'bar', + ... 'for i in range(5): write(i)', + ... 'continue', + ... ]): + ... test_function(1, None) + > (3)test_function() + -> pass + (Pdb) foo + 1 + (Pdb) bar + (Pdb) for i in range(5): write(i) + 0 + 1 + 2 + 3 + 4 + (Pdb) continue + """ + +def test_pdb_breakpoint_commands(): + """Test basic commands related to breakpoints. + + >>> def test_function(): + ... import pdb; pdb.Pdb().set_trace() + ... print(1) + ... print(2) + ... print(3) + ... print(4) + + First, need to clear bdb state that might be left over from previous tests. + Otherwise, the new breakpoints might get assigned different numbers. + + >>> from bdb import Breakpoint + >>> Breakpoint.next = 1 + >>> Breakpoint.bplist = {} + >>> Breakpoint.bpbynumber = [None] + + Now test the breakpoint commands. NORMALIZE_WHITESPACE is needed because + the breakpoint list outputs a tab for the "stop only" and "ignore next" + lines, which we don't want to put in here. + + >>> with PdbTestInput([ # doctest: +NORMALIZE_WHITESPACE + ... 'break 3', + ... 'disable 1', + ... 'ignore 1 10', + ... 'condition 1 1 < 2', + ... 'break 4', + ... 'break 4', + ... 'break', + ... 'clear 3', + ... 'break', + ... 'condition 1', + ... 'enable 1', + ... 'clear 1', + ... 'commands 2', + ... 'print 42', + ... 'end', + ... 'continue', # will stop at breakpoint 2 (line 4) + ... 'clear', # clear all! + ... 'y', + ... 'tbreak 5', + ... 'continue', # will stop at temporary breakpoint + ... 'break', # make sure breakpoint is gone + ... 'continue', + ... ]): + ... test_function() + > (3)test_function() + -> print(1) + (Pdb) break 3 + Breakpoint 1 at :3 + (Pdb) disable 1 + (Pdb) ignore 1 10 + Will ignore next 10 crossings of breakpoint 1. + (Pdb) condition 1 1 < 2 + (Pdb) break 4 + Breakpoint 2 at :4 + (Pdb) break 4 + Breakpoint 3 at :4 + (Pdb) break + Num Type Disp Enb Where + 1 breakpoint keep no at :3 + stop only if 1 < 2 + ignore next 10 hits + 2 breakpoint keep yes at :4 + 3 breakpoint keep yes at :4 + (Pdb) clear 3 + Deleted breakpoint 3 + (Pdb) break + Num Type Disp Enb Where + 1 breakpoint keep no at :3 + stop only if 1 < 2 + ignore next 10 hits + 2 breakpoint keep yes at :4 + (Pdb) condition 1 + Breakpoint 1 is now unconditional. + (Pdb) enable 1 + (Pdb) clear 1 + Deleted breakpoint 1 + (Pdb) commands 2 + (com) print 42 + (com) end + (Pdb) continue + 1 + 42 + > (4)test_function() + -> print(2) + (Pdb) clear + Clear all breaks? y + (Pdb) tbreak 5 + Breakpoint 4 at :5 + (Pdb) continue + 2 + Deleted breakpoint 4 + > (5)test_function() + -> print(3) + (Pdb) break + (Pdb) continue + 3 + 4 + """ + + +def test_pdb_skip_modules(): + """This illustrates the simple case of module skipping. + + >>> def skip_module(): + ... import string + ... import pdb; pdb.Pdb(skip=['string*']).set_trace() + ... string.lower('FOO') + + >>> with PdbTestInput([ + ... 'step', + ... 'continue', + ... ]): + ... skip_module() + > (4)skip_module() + -> string.lower('FOO') + (Pdb) step + --Return-- + > (4)skip_module()->None + -> string.lower('FOO') + (Pdb) continue + """ + + +# Module for testing skipping of module that makes a callback +mod = imp.new_module('module_to_skip') +exec 'def foo_pony(callback): x = 1; callback(); return None' in mod.__dict__ + + +def test_pdb_skip_modules_with_callback(): + """This illustrates skipping of modules that call into other code. + + >>> def skip_module(): + ... def callback(): + ... return None + ... import pdb; pdb.Pdb(skip=['module_to_skip*']).set_trace() + ... mod.foo_pony(callback) + + >>> with PdbTestInput([ + ... 'step', + ... 'step', + ... 'step', + ... 'step', + ... 'step', + ... 'continue', + ... ]): + ... skip_module() + ... pass # provides something to "step" to + > (5)skip_module() + -> mod.foo_pony(callback) + (Pdb) step + --Call-- + > (2)callback() + -> def callback(): + (Pdb) step + > (3)callback() + -> return None + (Pdb) step + --Return-- + > (3)callback()->None + -> return None + (Pdb) step + --Return-- + > (5)skip_module()->None + -> mod.foo_pony(callback) + (Pdb) step + > (10)() + -> pass # provides something to "step" to + (Pdb) continue + """ + + +def test_pdb_continue_in_bottomframe(): + """Test that "continue" and "next" work properly in bottom frame (issue #5294). + + >>> def test_function(): + ... import pdb, sys; inst = pdb.Pdb() + ... inst.set_trace() + ... inst.botframe = sys._getframe() # hackery to get the right botframe + ... print(1) + ... print(2) + ... print(3) + ... print(4) + + First, need to clear bdb state that might be left over from previous tests. + Otherwise, the new breakpoints might get assigned different numbers. + + >>> from bdb import Breakpoint + >>> Breakpoint.next = 1 + >>> Breakpoint.bplist = {} + >>> Breakpoint.bpbynumber = [None] + + >>> with PdbTestInput([ + ... 'next', + ... 'break 7', + ... 'continue', + ... 'next', + ... 'continue', + ... 'continue', + ... ]): + ... test_function() + > (4)test_function() + -> inst.botframe = sys._getframe() # hackery to get the right botframe + (Pdb) next + > (5)test_function() + -> print(1) + (Pdb) break 7 + Breakpoint 1 at :7 + (Pdb) continue + 1 + 2 + > (7)test_function() + -> print(3) + (Pdb) next + 3 + > (8)test_function() + -> print(4) + (Pdb) continue + 4 + """ + +class ModuleInitTester(unittest.TestCase): + + def test_filename_correct(self): + """ + In issue 7750, it was found that if the filename has a sequence that + resolves to an escape character in a Python string (such as \t), it + will be treated as the escaped character. + """ + # the test_fn must contain something like \t + # on Windows, this will create 'test_mod.py' in the current directory. + # on Unix, this will create '.\test_mod.py' in the current directory. + test_fn = '.\\test_mod.py' + code = 'print("testing pdb")' + with open(test_fn, 'w') as f: + f.write(code) + self.addCleanup(os.remove, test_fn) + cmd = [sys.executable, '-m', 'pdb', test_fn,] + proc = subprocess.Popen(cmd, + stdout=subprocess.PIPE, + stdin=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + stdout, stderr = proc.communicate('quit\n') + self.assertIn(code, stdout, "pdb munged the filename") + + +def test_main(): + from test import test_pdb + test_support.run_doctest(test_pdb, verbosity=True) + test_support.run_unittest(ModuleInitTester) + +if __name__ == '__main__': + test_main() -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Tue May 1 05:00:36 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Tue, 01 May 2012 05:00:36 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Skip_test=2E?= Message-ID: http://hg.python.org/jython/rev/cbc95e174268 changeset: 6635:cbc95e174268 user: Frank Wierzbicki date: Mon Apr 30 19:32:29 2012 -0700 summary: Skip test. files: Lib/test/test_pdb.py | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -282,6 +282,7 @@ class ModuleInitTester(unittest.TestCase): + @unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") def test_filename_correct(self): """ In issue 7750, it was found that if the filename has a sequence that -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Tue May 1 05:00:36 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Tue, 01 May 2012 05:00:36 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_long=28nan=29_is_now_raises_?= =?utf8?q?an_exception=2E?= Message-ID: http://hg.python.org/jython/rev/d53002b5f3d0 changeset: 6636:d53002b5f3d0 user: Frank Wierzbicki date: Mon Apr 30 20:00:28 2012 -0700 summary: long(nan) is now raises an exception. files: Lib/test/test_float_jy.py | 3 --- 1 files changed, 0 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_float_jy.py b/Lib/test/test_float_jy.py --- a/Lib/test/test_float_jy.py +++ b/Lib/test/test_float_jy.py @@ -72,9 +72,6 @@ # support Java syntax self.assert_(type(float('NaN')), float) - # CPython 2.4/2.5 allow this - self.assertEqual(long(nan), 0) - self.assertNotEqual(nan, float('nan')) self.assertNotEqual(nan, nan) self.assertEqual(cmp(nan, float('nan')), 1) -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Wed May 2 00:03:06 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Wed, 02 May 2012 00:03:06 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Add_more_skips=2E?= Message-ID: http://hg.python.org/jython/rev/23d0189442da changeset: 6637:23d0189442da user: Frank Wierzbicki date: Tue May 01 15:02:58 2012 -0700 summary: Add more skips. files: Lib/test/test_tarfile.py | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -1099,6 +1099,7 @@ def test_iso8859_1_filename(self): self._test_unicode_filename("iso8859-1") + @unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") def test_utf7_filename(self): self._test_unicode_filename("utf7") @@ -1176,6 +1177,7 @@ tar.addfile(t) tar.close() + @unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") def test_error_handlers(self): # Test if the unicode error handlers work correctly for characters # that cannot be expressed in a given encoding. -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Thu May 3 21:03:57 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Thu, 03 May 2012 21:03:57 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Fix_for_asinh=2E?= Message-ID: http://hg.python.org/jython/rev/d988f9431117 changeset: 6638:d988f9431117 user: Miki Tebeka date: Thu May 03 09:26:17 2012 -0700 summary: Fix for asinh. files: src/org/python/modules/math.java | 24 +++++++++++++++++++- 1 files changed, 23 insertions(+), 1 deletions(-) diff --git a/src/org/python/modules/math.java b/src/org/python/modules/math.java --- a/src/org/python/modules/math.java +++ b/src/org/python/modules/math.java @@ -86,7 +86,29 @@ if (isnan(v) || isinf(v)) { return v; } - return log(v + sqrt(v * v + 1)); + + final double ln2 = 6.93147180559945286227e-01; + final double large = 1 << 28; + final double small = 1.0 / (1 << 28); + boolean sign = false; + + if (v < 0) { + v = -v; + sign = true; + } + + double temp; + if (v > large) { + temp = log(v) + ln2; + } else if (v > 2) { + temp = log(2*v + 1/(sqrt(v*v+1)+v)); + } else if (v < small) { + temp = v; + } else { + temp = log1p(v + v*v/(1+sqrt(1+v*v))); + } + + return sign ? -temp : temp; } public static double atan(double v) { -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Thu May 3 21:03:57 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Thu, 03 May 2012 21:03:57 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_from=3A?= Message-ID: http://hg.python.org/jython/rev/35e21170d57f changeset: 6639:35e21170d57f user: Frank Wierzbicki date: Thu May 03 10:00:22 2012 -0700 summary: from: http://hg.python.org/cpython/Lib/doctest.py at 22db03646d9b files: Lib/doctest.py | 72 +++++++++++++++++++++++++++++-------- 1 files changed, 56 insertions(+), 16 deletions(-) diff --git a/Lib/doctest.py b/Lib/doctest.py --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -451,6 +451,25 @@ self.options = options self.exc_msg = exc_msg + def __eq__(self, other): + if type(self) is not type(other): + return NotImplemented + + return self.source == other.source and \ + self.want == other.want and \ + self.lineno == other.lineno and \ + self.indent == other.indent and \ + self.options == other.options and \ + self.exc_msg == other.exc_msg + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash((self.source, self.want, self.lineno, self.indent, + self.exc_msg)) + + class DocTest: """ A collection of doctest examples that should be run in a single @@ -499,6 +518,22 @@ return ('' % (self.name, self.filename, self.lineno, examples)) + def __eq__(self, other): + if type(self) is not type(other): + return NotImplemented + + return self.examples == other.examples and \ + self.docstring == other.docstring and \ + self.globs == other.globs and \ + self.name == other.name and \ + self.filename == other.filename and \ + self.lineno == other.lineno + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash((self.docstring, self.name, self.filename, self.lineno)) # This lets us sort tests by name: def __cmp__(self, other): @@ -882,12 +917,6 @@ elif inspect.isfunction(object): return module.__dict__ is object.func_globals elif inspect.isclass(object): - # XXX: Jython transition 2.5 - # Java classes appear as Python classes to inspect, but they - # have no __module__ http://jython.org/bugs/1758279 - # org.python.modules uses Java classes to masq - if not hasattr(object, '__module__'): - return False return module.__name__ == object.__module__ elif hasattr(object, '__module__'): return module.__name__ == object.__module__ @@ -993,8 +1022,6 @@ filename = getattr(module, '__file__', module.__name__) if filename[-4:] in (".pyc", ".pyo"): filename = filename[:-1] - elif filename.endswith('$py.class'): - filename = '%s.py' % filename[:-9] return self._parser.get_doctest(docstring, globs, name, filename, lineno) @@ -1225,7 +1252,7 @@ # Process each example. for examplenum, example in enumerate(test.examples): - # If REPORT_ONLY_FIRST_FAILURE is set, then supress + # If REPORT_ONLY_FIRST_FAILURE is set, then suppress # reporting after the first failure. quiet = (self.optionflags & REPORT_ONLY_FIRST_FAILURE and failures > 0) @@ -1711,8 +1738,7 @@ If a failure or error occurs, the globals are left intact: - >>> if '__builtins__' in test.globs: - ... del test.globs['__builtins__'] + >>> del test.globs['__builtins__'] >>> test.globs {'x': 1} @@ -1726,8 +1752,7 @@ ... UnexpectedException: - >>> if '__builtins__' in test.globs: - ... del test.globs['__builtins__'] + >>> del test.globs['__builtins__'] >>> test.globs {'x': 2} @@ -2196,7 +2221,7 @@ caller can catch the errors and initiate post-mortem debugging. The DocTestCase provides a debug method that raises - UnexpectedException errors if there is an unexepcted + UnexpectedException errors if there is an unexpected exception: >>> test = DocTestParser().get_doctest('>>> raise KeyError\n42', @@ -2262,6 +2287,23 @@ def id(self): return self._dt_test.name + def __eq__(self, other): + if type(self) is not type(other): + return NotImplemented + + return self._dt_test == other._dt_test and \ + self._dt_optionflags == other._dt_optionflags and \ + self._dt_setUp == other._dt_setUp and \ + self._dt_tearDown == other._dt_tearDown and \ + self._dt_checker == other._dt_checker + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash((self._dt_optionflags, self._dt_setUp, self._dt_tearDown, + self._dt_checker)) + def __repr__(self): name = self._dt_test.name.split('.') return "%s (%s)" % (name[-1], '.'.join(name[:-1])) @@ -2347,8 +2389,6 @@ filename = module.__file__ if filename[-4:] in (".pyc", ".pyo"): filename = filename[:-1] - elif filename.endswith('$py.class'): - filename = '%s.py' % filename[:-9] test.filename = filename suite.addTest(DocTestCase(test, **options)) -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Thu May 3 21:03:58 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Thu, 03 May 2012 21:03:58 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Re-apply_Jython_changes_to_d?= =?utf8?q?octest=2Epy=2C_remove_modified_test=5Fdoctest=2E?= Message-ID: http://hg.python.org/jython/rev/2c6a18a16d45 changeset: 6640:2c6a18a16d45 user: Frank Wierzbicki date: Thu May 03 10:50:45 2012 -0700 summary: Re-apply Jython changes to doctest.py, remove modified test_doctest. files: Lib/doctest.py | 10 +- Lib/test/test_doctest.py | 2313 -------------------------- 2 files changed, 8 insertions(+), 2315 deletions(-) diff --git a/Lib/doctest.py b/Lib/doctest.py --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -1022,6 +1022,8 @@ filename = getattr(module, '__file__', module.__name__) if filename[-4:] in (".pyc", ".pyo"): filename = filename[:-1] + elif filename.endswith('$py.class'): + filename = '%s.py' % filename[:-9] return self._parser.get_doctest(docstring, globs, name, filename, lineno) @@ -1738,7 +1740,8 @@ If a failure or error occurs, the globals are left intact: - >>> del test.globs['__builtins__'] + >>> if '__builtins__' in test.globs: + ... del test.globs['__builtins__'] >>> test.globs {'x': 1} @@ -1752,7 +1755,8 @@ ... UnexpectedException: - >>> del test.globs['__builtins__'] + >>> if '__builtins__' in test.globs: + ... del test.globs['__builtins__'] >>> test.globs {'x': 2} @@ -2389,6 +2393,8 @@ filename = module.__file__ if filename[-4:] in (".pyc", ".pyo"): filename = filename[:-1] + elif filename.endswith('$py.class'): + filename = '%s.py' % filename[:-9] test.filename = filename suite.addTest(DocTestCase(test, **options)) diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py deleted file mode 100644 --- a/Lib/test/test_doctest.py +++ /dev/null @@ -1,2313 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Test script for doctest. - -FIXME: Large parts gutted to run on Jython -- #1863. -""" - -import sys -from test import test_support -import doctest - -# NOTE: There are some additional tests relating to interaction with -# zipimport in the test_zipimport_support test module. - -###################################################################### -## Sample Objects (used by test cases) -###################################################################### - -def sample_func(v): - """ - Blah blah - - >>> print sample_func(22) - 44 - - Yee ha! - """ - return v+v - -class SampleClass: - """ - >>> print 1 - 1 - - >>> # comments get ignored. so are empty PS1 and PS2 prompts: - >>> - ... - - Multiline example: - >>> sc = SampleClass(3) - >>> for i in range(10): - ... sc = sc.double() - ... print sc.get(), - 6 12 24 48 96 192 384 768 1536 3072 - """ - def __init__(self, val): - """ - >>> print SampleClass(12).get() - 12 - """ - self.val = val - - def double(self): - """ - >>> print SampleClass(12).double().get() - 24 - """ - return SampleClass(self.val + self.val) - - def get(self): - """ - >>> print SampleClass(-5).get() - -5 - """ - return self.val - - def a_staticmethod(v): - """ - >>> print SampleClass.a_staticmethod(10) - 11 - """ - return v+1 - a_staticmethod = staticmethod(a_staticmethod) - - def a_classmethod(cls, v): - """ - >>> print SampleClass.a_classmethod(10) - 12 - >>> print SampleClass(0).a_classmethod(10) - 12 - """ - return v+2 - a_classmethod = classmethod(a_classmethod) - - a_property = property(get, doc=""" - >>> print SampleClass(22).a_property - 22 - """) - - class NestedClass: - """ - >>> x = SampleClass.NestedClass(5) - >>> y = x.square() - >>> print y.get() - 25 - """ - def __init__(self, val=0): - """ - >>> print SampleClass.NestedClass().get() - 0 - """ - self.val = val - def square(self): - return SampleClass.NestedClass(self.val*self.val) - def get(self): - return self.val - -class SampleNewStyleClass(object): - r""" - >>> print '1\n2\n3' - 1 - 2 - 3 - """ - def __init__(self, val): - """ - >>> print SampleNewStyleClass(12).get() - 12 - """ - self.val = val - - def double(self): - """ - >>> print SampleNewStyleClass(12).double().get() - 24 - """ - return SampleNewStyleClass(self.val + self.val) - - def get(self): - """ - >>> print SampleNewStyleClass(-5).get() - -5 - """ - return self.val - -###################################################################### -## Fake stdin (for testing interactive debugging) -###################################################################### - -class _FakeInput: - """ - A fake input stream for pdb's interactive debugger. Whenever a - line is read, print it (to simulate the user typing it), and then - return it. The set of lines to return is specified in the - constructor; they should not have trailing newlines. - """ - def __init__(self, lines): - self.lines = lines - - def readline(self): - line = self.lines.pop(0) - print line - return line+'\n' - -###################################################################### -## Test Cases -###################################################################### - -def test_Example(): r""" -Unit tests for the `Example` class. - -Example is a simple container class that holds: - - `source`: A source string. - - `want`: An expected output string. - - `exc_msg`: An expected exception message string (or None if no - exception is expected). - - `lineno`: A line number (within the docstring). - - `indent`: The example's indentation in the input string. - - `options`: An option dictionary, mapping option flags to True or - False. - -These attributes are set by the constructor. `source` and `want` are -required; the other attributes all have default values: - - >>> example = doctest.Example('print 1', '1\n') - >>> (example.source, example.want, example.exc_msg, - ... example.lineno, example.indent, example.options) - ('print 1\n', '1\n', None, 0, 0, {}) - -The first three attributes (`source`, `want`, and `exc_msg`) may be -specified positionally; the remaining arguments should be specified as -keyword arguments: - - >>> exc_msg = 'IndexError: pop from an empty list' - >>> example = doctest.Example('[].pop()', '', exc_msg, - ... lineno=5, indent=4, - ... options={doctest.ELLIPSIS: True}) - >>> (example.source, example.want, example.exc_msg, - ... example.lineno, example.indent, example.options) - ('[].pop()\n', '', 'IndexError: pop from an empty list\n', 5, 4, {8: True}) - -The constructor normalizes the `source` string to end in a newline: - - Source spans a single line: no terminating newline. - >>> e = doctest.Example('print 1', '1\n') - >>> e.source, e.want - ('print 1\n', '1\n') - - >>> e = doctest.Example('print 1\n', '1\n') - >>> e.source, e.want - ('print 1\n', '1\n') - - Source spans multiple lines: require terminating newline. - >>> e = doctest.Example('print 1;\nprint 2\n', '1\n2\n') - >>> e.source, e.want - ('print 1;\nprint 2\n', '1\n2\n') - - >>> e = doctest.Example('print 1;\nprint 2', '1\n2\n') - >>> e.source, e.want - ('print 1;\nprint 2\n', '1\n2\n') - - Empty source string (which should never appear in real examples) - >>> e = doctest.Example('', '') - >>> e.source, e.want - ('\n', '') - -The constructor normalizes the `want` string to end in a newline, -unless it's the empty string: - - >>> e = doctest.Example('print 1', '1\n') - >>> e.source, e.want - ('print 1\n', '1\n') - - >>> e = doctest.Example('print 1', '1') - >>> e.source, e.want - ('print 1\n', '1\n') - - >>> e = doctest.Example('print', '') - >>> e.source, e.want - ('print\n', '') - -The constructor normalizes the `exc_msg` string to end in a newline, -unless it's `None`: - - Message spans one line - >>> exc_msg = 'IndexError: pop from an empty list' - >>> e = doctest.Example('[].pop()', '', exc_msg) - >>> e.exc_msg - 'IndexError: pop from an empty list\n' - - >>> exc_msg = 'IndexError: pop from an empty list\n' - >>> e = doctest.Example('[].pop()', '', exc_msg) - >>> e.exc_msg - 'IndexError: pop from an empty list\n' - - Message spans multiple lines - >>> exc_msg = 'ValueError: 1\n 2' - >>> e = doctest.Example('raise ValueError("1\n 2")', '', exc_msg) - >>> e.exc_msg - 'ValueError: 1\n 2\n' - - >>> exc_msg = 'ValueError: 1\n 2\n' - >>> e = doctest.Example('raise ValueError("1\n 2")', '', exc_msg) - >>> e.exc_msg - 'ValueError: 1\n 2\n' - - Empty (but non-None) exception message (which should never appear - in real examples) - >>> exc_msg = '' - >>> e = doctest.Example('raise X()', '', exc_msg) - >>> e.exc_msg - '\n' - -""" - -def test_DocTest(): r""" -Unit tests for the `DocTest` class. - -DocTest is a collection of examples, extracted from a docstring, along -with information about where the docstring comes from (a name, -filename, and line number). The docstring is parsed by the `DocTest` -constructor: - - >>> docstring = ''' - ... >>> print 12 - ... 12 - ... - ... Non-example text. - ... - ... >>> print 'another\example' - ... another - ... example - ... ''' - >>> globs = {} # globals to run the test in. - >>> parser = doctest.DocTestParser() - >>> test = parser.get_doctest(docstring, globs, 'some_test', - ... 'some_file', 20) - >>> print test - - >>> len(test.examples) - 2 - >>> e1, e2 = test.examples - >>> (e1.source, e1.want, e1.lineno) - ('print 12\n', '12\n', 1) - >>> (e2.source, e2.want, e2.lineno) - ("print 'another\\example'\n", 'another\nexample\n', 6) - -Source information (name, filename, and line number) is available as -attributes on the doctest object: - - >>> (test.name, test.filename, test.lineno) - ('some_test', 'some_file', 20) - -The line number of an example within its containing file is found by -adding the line number of the example and the line number of its -containing test: - - >>> test.lineno + e1.lineno - 21 - >>> test.lineno + e2.lineno - 26 - -If the docstring contains inconsistant leading whitespace in the -expected output of an example, then `DocTest` will raise a ValueError: - - >>> docstring = r''' - ... >>> print 'bad\nindentation' - ... bad - ... indentation - ... ''' - >>> parser.get_doctest(docstring, globs, 'some_test', 'filename', 0) - Traceback (most recent call last): - ValueError: line 4 of the docstring for some_test has inconsistent leading whitespace: 'indentation' - -If the docstring contains inconsistent leading whitespace on -continuation lines, then `DocTest` will raise a ValueError: - - >>> docstring = r''' - ... >>> print ('bad indentation', - ... ... 2) - ... ('bad', 'indentation') - ... ''' - >>> parser.get_doctest(docstring, globs, 'some_test', 'filename', 0) - Traceback (most recent call last): - ValueError: line 2 of the docstring for some_test has inconsistent leading whitespace: '... 2)' - -If there's no blank space after a PS1 prompt ('>>>'), then `DocTest` -will raise a ValueError: - - >>> docstring = '>>>print 1\n1' - >>> parser.get_doctest(docstring, globs, 'some_test', 'filename', 0) - Traceback (most recent call last): - ValueError: line 1 of the docstring for some_test lacks blank after >>>: '>>>print 1' - -If there's no blank space after a PS2 prompt ('...'), then `DocTest` -will raise a ValueError: - - >>> docstring = '>>> if 1:\n...print 1\n1' - >>> parser.get_doctest(docstring, globs, 'some_test', 'filename', 0) - Traceback (most recent call last): - ValueError: line 2 of the docstring for some_test lacks blank after ...: '...print 1' - -Compare `DocTest`: - - >>> docstring = ''' - ... >>> print 12 - ... 12 - ... ''' - >>> test = parser.get_doctest(docstring, globs, 'some_test', - ... 'some_test', 20) - >>> same_test = parser.get_doctest(docstring, globs, 'some_test', - ... 'some_test', 20) - >>> docstring = ''' - ... >>> print 42 - ... 42 - ... ''' - >>> other_test = parser.get_doctest(docstring, globs, 'other_test', - ... 'other_file', 10) - >>> test == other_test - False - >>> test != other_test - True - -Compare `DocTestCase`: - - >>> DocTestCase = doctest.DocTestCase - >>> test_case = DocTestCase(test) - >>> same_test_case = DocTestCase(same_test) - >>> other_test_case = DocTestCase(other_test) - >>> test_case == same_test_case - True - >>> test_case != same_test_case - False - >>> hash(test_case) == hash(same_test_case) - True - >>> test == other_test_case - False - >>> test != other_test_case - True - -""" - -def test_DocTestFinder(): r""" -Unit tests for the `DocTestFinder` class. - -DocTestFinder is used to extract DocTests from an object's docstring -and the docstrings of its contained objects. It can be used with -modules, functions, classes, methods, staticmethods, classmethods, and -properties. - -Finding Tests in Functions -~~~~~~~~~~~~~~~~~~~~~~~~~~ -For a function whose docstring contains examples, DocTestFinder.find() -will return a single test (for that function's docstring): - - >>> finder = doctest.DocTestFinder() - -We'll simulate a __file__ attr that ends in pyc: - - >>> import test.test_doctest - >>> old = test.test_doctest.__file__ - >>> test.test_doctest.__file__ = 'test_doctest.pyc' - - >>> tests = finder.find(sample_func) - - >>> print tests # doctest: +ELLIPSIS - [] - -The exact name depends on how test_doctest was invoked, so allow for -leading path components. - - >>> tests[0].filename # doctest: +ELLIPSIS - '...test_doctest.py' - - >>> test.test_doctest.__file__ = old - - - >>> e = tests[0].examples[0] - >>> (e.source, e.want, e.lineno) - ('print sample_func(22)\n', '44\n', 3) - -By default, tests are created for objects with no docstring: - - >>> def no_docstring(v): - ... pass - >>> finder.find(no_docstring) - [] - -However, the optional argument `exclude_empty` to the DocTestFinder -constructor can be used to exclude tests for objects with empty -docstrings: - - >>> def no_docstring(v): - ... pass - >>> excl_empty_finder = doctest.DocTestFinder(exclude_empty=True) - >>> excl_empty_finder.find(no_docstring) - [] - -If the function has a docstring with no examples, then a test with no -examples is returned. (This lets `DocTestRunner` collect statistics -about which functions have no tests -- but is that useful? And should -an empty test also be created when there's no docstring?) - - >>> def no_examples(v): - ... ''' no doctest examples ''' - >>> finder.find(no_examples) # doctest: +ELLIPSIS - [] - -Finding Tests in Classes -~~~~~~~~~~~~~~~~~~~~~~~~ -For a class, DocTestFinder will create a test for the class's -docstring, and will recursively explore its contents, including -methods, classmethods, staticmethods, properties, and nested classes. - - >>> finder = doctest.DocTestFinder() - >>> tests = finder.find(SampleClass) - >>> for t in tests: - ... print '%2s %s' % (len(t.examples), t.name) - 3 SampleClass - 3 SampleClass.NestedClass - 1 SampleClass.NestedClass.__init__ - 1 SampleClass.__init__ - 2 SampleClass.a_classmethod - 1 SampleClass.a_property - 1 SampleClass.a_staticmethod - 1 SampleClass.double - 1 SampleClass.get - -New-style classes are also supported: - - >>> tests = finder.find(SampleNewStyleClass) - >>> for t in tests: - ... print '%2s %s' % (len(t.examples), t.name) - 1 SampleNewStyleClass - 1 SampleNewStyleClass.__init__ - 1 SampleNewStyleClass.double - 1 SampleNewStyleClass.get - -Finding Tests in Modules -~~~~~~~~~~~~~~~~~~~~~~~~ -For a module, DocTestFinder will create a test for the class's -docstring, and will recursively explore its contents, including -functions, classes, and the `__test__` dictionary, if it exists: - - >>> # A module - >>> import types - >>> m = types.ModuleType('some_module') - >>> def triple(val): - ... ''' - ... >>> print triple(11) - ... 33 - ... ''' - ... return val*3 - >>> m.__dict__.update({ - ... 'sample_func': sample_func, - ... 'SampleClass': SampleClass, - ... '__doc__': ''' - ... Module docstring. - ... >>> print 'module' - ... module - ... ''', - ... '__test__': { - ... 'd': '>>> print 6\n6\n>>> print 7\n7\n', - ... 'c': triple}}) - - >>> finder = doctest.DocTestFinder() - >>> # Use module=test.test_doctest, to prevent doctest from - >>> # ignoring the objects since they weren't defined in m. - >>> import test.test_doctest - >>> tests = finder.find(m, module=test.test_doctest) - >>> for t in tests: - ... print '%2s %s' % (len(t.examples), t.name) - 1 some_module - 3 some_module.SampleClass - 3 some_module.SampleClass.NestedClass - 1 some_module.SampleClass.NestedClass.__init__ - 1 some_module.SampleClass.__init__ - 2 some_module.SampleClass.a_classmethod - 1 some_module.SampleClass.a_property - 1 some_module.SampleClass.a_staticmethod - 1 some_module.SampleClass.double - 1 some_module.SampleClass.get - 1 some_module.__test__.c - 2 some_module.__test__.d - 1 some_module.sample_func - -Duplicate Removal -~~~~~~~~~~~~~~~~~ -If a single object is listed twice (under different names), then tests -will only be generated for it once: - - >>> from test import doctest_aliases - >>> assert doctest_aliases.TwoNames.f - >>> assert doctest_aliases.TwoNames.g - >>> tests = excl_empty_finder.find(doctest_aliases) - >>> print len(tests) - 2 - >>> print tests[0].name - test.doctest_aliases.TwoNames - - TwoNames.f and TwoNames.g are bound to the same object. - We can't guess which will be found in doctest's traversal of - TwoNames.__dict__ first, so we have to allow for either. - - >>> tests[1].name.split('.')[-1] in ['f', 'g'] - True - -Empty Tests -~~~~~~~~~~~ -By default, an object with no doctests doesn't create any tests: - - >>> tests = doctest.DocTestFinder().find(SampleClass) - >>> for t in tests: - ... print '%2s %s' % (len(t.examples), t.name) - 3 SampleClass - 3 SampleClass.NestedClass - 1 SampleClass.NestedClass.__init__ - 1 SampleClass.__init__ - 2 SampleClass.a_classmethod - 1 SampleClass.a_property - 1 SampleClass.a_staticmethod - 1 SampleClass.double - 1 SampleClass.get - -By default, that excluded objects with no doctests. exclude_empty=False -tells it to include (empty) tests for objects with no doctests. This feature -is really to support backward compatibility in what doctest.master.summarize() -displays. - - >>> tests = doctest.DocTestFinder(exclude_empty=False).find(SampleClass) - >>> for t in tests: - ... print '%2s %s' % (len(t.examples), t.name) - 3 SampleClass - 3 SampleClass.NestedClass - 1 SampleClass.NestedClass.__init__ - 0 SampleClass.NestedClass.get - 0 SampleClass.NestedClass.square - 1 SampleClass.__init__ - 2 SampleClass.a_classmethod - 1 SampleClass.a_property - 1 SampleClass.a_staticmethod - 1 SampleClass.double - 1 SampleClass.get - -Turning off Recursion -~~~~~~~~~~~~~~~~~~~~~ -DocTestFinder can be told not to look for tests in contained objects -using the `recurse` flag: - - >>> tests = doctest.DocTestFinder(recurse=False).find(SampleClass) - >>> for t in tests: - ... print '%2s %s' % (len(t.examples), t.name) - 3 SampleClass - -Line numbers -~~~~~~~~~~~~ -DocTestFinder finds the line number of each example: - - >>> def f(x): - ... ''' - ... >>> x = 12 - ... - ... some text - ... - ... >>> # examples are not created for comments & bare prompts. - ... >>> - ... ... - ... - ... >>> for x in range(10): - ... ... print x, - ... 0 1 2 3 4 5 6 7 8 9 - ... >>> x//2 - ... 6 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> [e.lineno for e in test.examples] - [1, 9, 12] -""" - -def test_DocTestParser(): r""" -Unit tests for the `DocTestParser` class. - -DocTestParser is used to parse docstrings containing doctest examples. - -The `parse` method divides a docstring into examples and intervening -text: - - >>> s = ''' - ... >>> x, y = 2, 3 # no output expected - ... >>> if 1: - ... ... print x - ... ... print y - ... 2 - ... 3 - ... - ... Some text. - ... >>> x+y - ... 5 - ... ''' - >>> parser = doctest.DocTestParser() - >>> for piece in parser.parse(s): - ... if isinstance(piece, doctest.Example): - ... print 'Example:', (piece.source, piece.want, piece.lineno) - ... else: - ... print ' Text:', `piece` - Text: '\n' - Example: ('x, y = 2, 3 # no output expected\n', '', 1) - Text: '' - Example: ('if 1:\n print x\n print y\n', '2\n3\n', 2) - Text: '\nSome text.\n' - Example: ('x+y\n', '5\n', 9) - Text: '' - -The `get_examples` method returns just the examples: - - >>> for piece in parser.get_examples(s): - ... print (piece.source, piece.want, piece.lineno) - ('x, y = 2, 3 # no output expected\n', '', 1) - ('if 1:\n print x\n print y\n', '2\n3\n', 2) - ('x+y\n', '5\n', 9) - -The `get_doctest` method creates a Test from the examples, along with the -given arguments: - - >>> test = parser.get_doctest(s, {}, 'name', 'filename', lineno=5) - >>> (test.name, test.filename, test.lineno) - ('name', 'filename', 5) - >>> for piece in test.examples: - ... print (piece.source, piece.want, piece.lineno) - ('x, y = 2, 3 # no output expected\n', '', 1) - ('if 1:\n print x\n print y\n', '2\n3\n', 2) - ('x+y\n', '5\n', 9) -""" - -class test_DocTestRunner: - def basics(): r""" -Unit tests for the `DocTestRunner` class. - -DocTestRunner is used to run DocTest test cases, and to accumulate -statistics. Here's a simple DocTest case we can use: - - >>> def f(x): - ... ''' - ... >>> x = 12 - ... >>> print x - ... 12 - ... >>> x//2 - ... 6 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - -The main DocTestRunner interface is the `run` method, which runs a -given DocTest case in a given namespace (globs). It returns a tuple -`(f,t)`, where `f` is the number of failed tests and `t` is the number -of tried tests. - - >>> doctest.DocTestRunner(verbose=False).run(test) - TestResults(failed=0, attempted=3) - -If any example produces incorrect output, then the test runner reports -the failure and proceeds to the next example: - - >>> def f(x): - ... ''' - ... >>> x = 12 - ... >>> print x - ... 14 - ... >>> x//2 - ... 6 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=True).run(test) - ... # doctest: +ELLIPSIS - Trying: - x = 12 - Expecting nothing - ok - Trying: - print x - Expecting: - 14 - ********************************************************************** - File ..., line 4, in f - Failed example: - print x - Expected: - 14 - Got: - 12 - Trying: - x//2 - Expecting: - 6 - ok - TestResults(failed=1, attempted=3) -""" - def verbose_flag(): r""" -The `verbose` flag makes the test runner generate more detailed -output: - - >>> def f(x): - ... ''' - ... >>> x = 12 - ... >>> print x - ... 12 - ... >>> x//2 - ... 6 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - - >>> doctest.DocTestRunner(verbose=True).run(test) - Trying: - x = 12 - Expecting nothing - ok - Trying: - print x - Expecting: - 12 - ok - Trying: - x//2 - Expecting: - 6 - ok - TestResults(failed=0, attempted=3) - -If the `verbose` flag is unspecified, then the output will be verbose -iff `-v` appears in sys.argv: - - >>> # Save the real sys.argv list. - >>> old_argv = sys.argv - - >>> # If -v does not appear in sys.argv, then output isn't verbose. - >>> sys.argv = ['test'] - >>> doctest.DocTestRunner().run(test) - TestResults(failed=0, attempted=3) - - >>> # If -v does appear in sys.argv, then output is verbose. - >>> sys.argv = ['test', '-v'] - >>> doctest.DocTestRunner().run(test) - Trying: - x = 12 - Expecting nothing - ok - Trying: - print x - Expecting: - 12 - ok - Trying: - x//2 - Expecting: - 6 - ok - TestResults(failed=0, attempted=3) - - >>> # Restore sys.argv - >>> sys.argv = old_argv - -In the remaining examples, the test runner's verbosity will be -explicitly set, to ensure that the test behavior is consistent. - """ - def exceptions(): r""" -Tests of `DocTestRunner`'s exception handling. - -An expected exception is specified with a traceback message. The -lines between the first line and the type/value may be omitted or -replaced with any other string: - - >>> def f(x): - ... ''' - ... >>> x = 12 - ... >>> print x//0 - ... Traceback (most recent call last): - ... ZeroDivisionError: integer division or modulo by zero - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - TestResults(failed=0, attempted=2) - -An example may not generate output before it raises an exception; if -it does, then the traceback message will not be recognized as -signaling an expected exception, so the example will be reported as an -unexpected exception: - - >>> def f(x): - ... ''' - ... >>> x = 12 - ... >>> print 'pre-exception output', x//0 - ... pre-exception output - ... Traceback (most recent call last): - ... ZeroDivisionError: integer division or modulo by zero - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 4, in f - Failed example: - print 'pre-exception output', x//0 - Exception raised: - ... - ZeroDivisionError: integer division or modulo by zero - TestResults(failed=1, attempted=2) - -Exception messages may contain newlines: - - >>> def f(x): - ... r''' - ... >>> raise ValueError, 'multi\nline\nmessage' - ... Traceback (most recent call last): - ... ValueError: multi - ... line - ... message - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - TestResults(failed=0, attempted=1) - -If an exception is expected, but an exception with the wrong type or -message is raised, then it is reported as a failure: - - >>> def f(x): - ... r''' - ... >>> raise ValueError, 'message' - ... Traceback (most recent call last): - ... ValueError: wrong message - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 3, in f - Failed example: - raise ValueError, 'message' - Expected: - Traceback (most recent call last): - ValueError: wrong message - Got: - Traceback (most recent call last): - ... - ValueError: message - TestResults(failed=1, attempted=1) - -However, IGNORE_EXCEPTION_DETAIL can be used to allow a mismatch in the -detail: - - >>> def f(x): - ... r''' - ... >>> raise ValueError, 'message' #doctest: +IGNORE_EXCEPTION_DETAIL - ... Traceback (most recent call last): - ... ValueError: wrong message - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - TestResults(failed=0, attempted=1) - -IGNORE_EXCEPTION_DETAIL also ignores difference in exception formatting -between Python versions. For example, in Python 3.x, the module path of -the exception is in the output, but this will fail under Python 2: - - >>> def f(x): - ... r''' - ... >>> from httplib import HTTPException - ... >>> raise HTTPException('message') - ... Traceback (most recent call last): - ... httplib.HTTPException: message - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 4, in f - Failed example: - raise HTTPException('message') - Expected: - Traceback (most recent call last): - httplib.HTTPException: message - Got: - Traceback (most recent call last): - ... - HTTPException: message - TestResults(failed=1, attempted=2) - -But in Python 2 the module path is not included, an therefore a test must look -like the following test to succeed in Python 2. But that test will fail under -Python 3. - - >>> def f(x): - ... r''' - ... >>> from httplib import HTTPException - ... >>> raise HTTPException('message') - ... Traceback (most recent call last): - ... HTTPException: message - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - TestResults(failed=0, attempted=2) - -However, with IGNORE_EXCEPTION_DETAIL, the module name of the exception -(if any) will be ignored: - - >>> def f(x): - ... r''' - ... >>> from httplib import HTTPException - ... >>> raise HTTPException('message') #doctest: +IGNORE_EXCEPTION_DETAIL - ... Traceback (most recent call last): - ... HTTPException: message - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - TestResults(failed=0, attempted=2) - -The module path will be completely ignored, so two different module paths will -still pass if IGNORE_EXCEPTION_DETAIL is given. This is intentional, so it can -be used when exceptions have changed module. - - >>> def f(x): - ... r''' - ... >>> from httplib import HTTPException - ... >>> raise HTTPException('message') #doctest: +IGNORE_EXCEPTION_DETAIL - ... Traceback (most recent call last): - ... foo.bar.HTTPException: message - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - TestResults(failed=0, attempted=2) - -But IGNORE_EXCEPTION_DETAIL does not allow a mismatch in the exception type: - - >>> def f(x): - ... r''' - ... >>> raise ValueError, 'message' #doctest: +IGNORE_EXCEPTION_DETAIL - ... Traceback (most recent call last): - ... TypeError: wrong type - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 3, in f - Failed example: - raise ValueError, 'message' #doctest: +IGNORE_EXCEPTION_DETAIL - Expected: - Traceback (most recent call last): - TypeError: wrong type - Got: - Traceback (most recent call last): - ... - ValueError: message - TestResults(failed=1, attempted=1) - -If an exception is raised but not expected, then it is reported as an -unexpected exception: - - >>> def f(x): - ... r''' - ... >>> 1//0 - ... 0 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 3, in f - Failed example: - 1//0 - Exception raised: - Traceback (most recent call last): - ... - ZeroDivisionError: integer division or modulo by zero - TestResults(failed=1, attempted=1) -""" - def displayhook(): r""" -Test that changing sys.displayhook doesn't matter for doctest. - - >>> import sys - >>> orig_displayhook = sys.displayhook - >>> def my_displayhook(x): - ... print('hi!') - >>> sys.displayhook = my_displayhook - >>> def f(): - ... ''' - ... >>> 3 - ... 3 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> r = doctest.DocTestRunner(verbose=False).run(test) - >>> post_displayhook = sys.displayhook - - We need to restore sys.displayhook now, so that we'll be able to test - results. - - >>> sys.displayhook = orig_displayhook - - Ok, now we can check that everything is ok. - - >>> r - TestResults(failed=0, attempted=1) - >>> post_displayhook is my_displayhook - True -""" - def optionflags(): r""" -Tests of `DocTestRunner`'s option flag handling. - -Several option flags can be used to customize the behavior of the test -runner. These are defined as module constants in doctest, and passed -to the DocTestRunner constructor (multiple constants should be ORed -together). - -The DONT_ACCEPT_TRUE_FOR_1 flag disables matches between True/False -and 1/0: - - >>> def f(x): - ... '>>> True\n1\n' - - >>> # Without the flag: - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - TestResults(failed=0, attempted=1) - - >>> # With the flag: - >>> test = doctest.DocTestFinder().find(f)[0] - >>> flags = doctest.DONT_ACCEPT_TRUE_FOR_1 - >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - True - Expected: - 1 - Got: - True - TestResults(failed=1, attempted=1) - -The DONT_ACCEPT_BLANKLINE flag disables the match between blank lines -and the '' marker: - - >>> def f(x): - ... '>>> print "a\\n\\nb"\na\n\nb\n' - - >>> # Without the flag: - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - TestResults(failed=0, attempted=1) - - >>> # With the flag: - >>> test = doctest.DocTestFinder().find(f)[0] - >>> flags = doctest.DONT_ACCEPT_BLANKLINE - >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - print "a\n\nb" - Expected: - a - - b - Got: - a - - b - TestResults(failed=1, attempted=1) - -The NORMALIZE_WHITESPACE flag causes all sequences of whitespace to be -treated as equal: - - >>> def f(x): - ... '>>> print 1, 2, 3\n 1 2\n 3' - - >>> # Without the flag: - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - print 1, 2, 3 - Expected: - 1 2 - 3 - Got: - 1 2 3 - TestResults(failed=1, attempted=1) - - >>> # With the flag: - >>> test = doctest.DocTestFinder().find(f)[0] - >>> flags = doctest.NORMALIZE_WHITESPACE - >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) - TestResults(failed=0, attempted=1) - - An example from the docs: - >>> print range(20) #doctest: +NORMALIZE_WHITESPACE - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] - -The ELLIPSIS flag causes ellipsis marker ("...") in the expected -output to match any substring in the actual output: - - >>> def f(x): - ... '>>> print range(15)\n[0, 1, 2, ..., 14]\n' - - >>> # Without the flag: - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - print range(15) - Expected: - [0, 1, 2, ..., 14] - Got: - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] - TestResults(failed=1, attempted=1) - - >>> # With the flag: - >>> test = doctest.DocTestFinder().find(f)[0] - >>> flags = doctest.ELLIPSIS - >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) - TestResults(failed=0, attempted=1) - - ... also matches nothing: - - >>> for i in range(100): - ... print i**2, #doctest: +ELLIPSIS - 0 1...4...9 16 ... 36 49 64 ... 9801 - - ... can be surprising; e.g., this test passes: - - >>> for i in range(21): #doctest: +ELLIPSIS - ... print i, - 0 1 2 ...1...2...0 - - Examples from the docs: - - >>> print range(20) # doctest:+ELLIPSIS - [0, 1, ..., 18, 19] - - >>> print range(20) # doctest: +ELLIPSIS - ... # doctest: +NORMALIZE_WHITESPACE - [0, 1, ..., 18, 19] - -The SKIP flag causes an example to be skipped entirely. I.e., the -example is not run. It can be useful in contexts where doctest -examples serve as both documentation and test cases, and an example -should be included for documentation purposes, but should not be -checked (e.g., because its output is random, or depends on resources -which would be unavailable.) The SKIP flag can also be used for -'commenting out' broken examples. - - >>> import unavailable_resource # doctest: +SKIP - >>> unavailable_resource.do_something() # doctest: +SKIP - >>> unavailable_resource.blow_up() # doctest: +SKIP - Traceback (most recent call last): - ... - UncheckedBlowUpError: Nobody checks me. - - >>> import random - >>> print random.random() # doctest: +SKIP - 0.721216923889 - -The REPORT_UDIFF flag causes failures that involve multi-line expected -and actual outputs to be displayed using a unified diff: - - >>> def f(x): - ... r''' - ... >>> print '\n'.join('abcdefg') - ... a - ... B - ... c - ... d - ... f - ... g - ... h - ... ''' - -The REPORT_NDIFF flag causes failures to use the difflib.Differ algorithm -used by the popular ndiff.py utility. This does intraline difference -marking, as well as interline differences. - - >>> def f(x): - ... r''' - ... >>> print "a b c d e f g h i j k l m" - ... a b c d e f g h i j k 1 m - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> flags = doctest.REPORT_NDIFF - >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 3, in f - Failed example: - print "a b c d e f g h i j k l m" - Differences (ndiff with -expected +actual): - - a b c d e f g h i j k 1 m - ? ^ - + a b c d e f g h i j k l m - ? + ++ ^ - TestResults(failed=1, attempted=1) - -The REPORT_ONLY_FIRST_FAILURE suppresses result output after the first -failing example: - - >>> def f(x): - ... r''' - ... >>> print 1 # first success - ... 1 - ... >>> print 2 # first failure - ... 200 - ... >>> print 3 # second failure - ... 300 - ... >>> print 4 # second success - ... 4 - ... >>> print 5 # third failure - ... 500 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> flags = doctest.REPORT_ONLY_FIRST_FAILURE - >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 5, in f - Failed example: - print 2 # first failure - Expected: - 200 - Got: - 2 - TestResults(failed=3, attempted=5) - -However, output from `report_start` is not suppressed: - - >>> doctest.DocTestRunner(verbose=True, optionflags=flags).run(test) - ... # doctest: +ELLIPSIS - Trying: - print 1 # first success - Expecting: - 1 - ok - Trying: - print 2 # first failure - Expecting: - 200 - ********************************************************************** - File ..., line 5, in f - Failed example: - print 2 # first failure - Expected: - 200 - Got: - 2 - TestResults(failed=3, attempted=5) - -For the purposes of REPORT_ONLY_FIRST_FAILURE, unexpected exceptions -count as failures: - - >>> def f(x): - ... r''' - ... >>> print 1 # first success - ... 1 - ... >>> raise ValueError(2) # first failure - ... 200 - ... >>> print 3 # second failure - ... 300 - ... >>> print 4 # second success - ... 4 - ... >>> print 5 # third failure - ... 500 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> flags = doctest.REPORT_ONLY_FIRST_FAILURE - >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 5, in f - Failed example: - raise ValueError(2) # first failure - Exception raised: - ... - ValueError: 2 - TestResults(failed=3, attempted=5) - -New option flags can also be registered, via register_optionflag(). Here -we reach into doctest's internals a bit. - - >>> unlikely = "UNLIKELY_OPTION_NAME" - >>> unlikely in doctest.OPTIONFLAGS_BY_NAME - False - >>> new_flag_value = doctest.register_optionflag(unlikely) - >>> unlikely in doctest.OPTIONFLAGS_BY_NAME - True - -Before 2.4.4/2.5, registering a name more than once erroneously created -more than one flag value. Here we verify that's fixed: - - >>> redundant_flag_value = doctest.register_optionflag(unlikely) - >>> redundant_flag_value == new_flag_value - True - -Clean up. - >>> del doctest.OPTIONFLAGS_BY_NAME[unlikely] - - """ - - def option_directives(): r""" -Tests of `DocTestRunner`'s option directive mechanism. - -Option directives can be used to turn option flags on or off for a -single example. To turn an option on for an example, follow that -example with a comment of the form ``# doctest: +OPTION``: - - >>> def f(x): r''' - ... >>> print range(10) # should fail: no ellipsis - ... [0, 1, ..., 9] - ... - ... >>> print range(10) # doctest: +ELLIPSIS - ... [0, 1, ..., 9] - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - print range(10) # should fail: no ellipsis - Expected: - [0, 1, ..., 9] - Got: - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - TestResults(failed=1, attempted=2) - -To turn an option off for an example, follow that example with a -comment of the form ``# doctest: -OPTION``: - - >>> def f(x): r''' - ... >>> print range(10) - ... [0, 1, ..., 9] - ... - ... >>> # should fail: no ellipsis - ... >>> print range(10) # doctest: -ELLIPSIS - ... [0, 1, ..., 9] - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False, - ... optionflags=doctest.ELLIPSIS).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 6, in f - Failed example: - print range(10) # doctest: -ELLIPSIS - Expected: - [0, 1, ..., 9] - Got: - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - TestResults(failed=1, attempted=2) - -Option directives affect only the example that they appear with; they -do not change the options for surrounding examples: - - >>> def f(x): r''' - ... >>> print range(10) # Should fail: no ellipsis - ... [0, 1, ..., 9] - ... - ... >>> print range(10) # doctest: +ELLIPSIS - ... [0, 1, ..., 9] - ... - ... >>> print range(10) # Should fail: no ellipsis - ... [0, 1, ..., 9] - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - print range(10) # Should fail: no ellipsis - Expected: - [0, 1, ..., 9] - Got: - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - ********************************************************************** - File ..., line 8, in f - Failed example: - print range(10) # Should fail: no ellipsis - Expected: - [0, 1, ..., 9] - Got: - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - TestResults(failed=2, attempted=3) - -Multiple options may be modified by a single option directive. They -may be separated by whitespace, commas, or both: - - >>> def f(x): r''' - ... >>> print range(10) # Should fail - ... [0, 1, ..., 9] - ... >>> print range(10) # Should succeed - ... ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE - ... [0, 1, ..., 9] - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - print range(10) # Should fail - Expected: - [0, 1, ..., 9] - Got: - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - TestResults(failed=1, attempted=2) - - >>> def f(x): r''' - ... >>> print range(10) # Should fail - ... [0, 1, ..., 9] - ... >>> print range(10) # Should succeed - ... ... # doctest: +ELLIPSIS,+NORMALIZE_WHITESPACE - ... [0, 1, ..., 9] - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - print range(10) # Should fail - Expected: - [0, 1, ..., 9] - Got: - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - TestResults(failed=1, attempted=2) - - >>> def f(x): r''' - ... >>> print range(10) # Should fail - ... [0, 1, ..., 9] - ... >>> print range(10) # Should succeed - ... ... # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE - ... [0, 1, ..., 9] - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - print range(10) # Should fail - Expected: - [0, 1, ..., 9] - Got: - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - TestResults(failed=1, attempted=2) - -The option directive may be put on the line following the source, as -long as a continuation prompt is used: - - >>> def f(x): r''' - ... >>> print range(10) - ... ... # doctest: +ELLIPSIS - ... [0, 1, ..., 9] - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - TestResults(failed=0, attempted=1) - -For examples with multi-line source, the option directive may appear -at the end of any line: - - >>> def f(x): r''' - ... >>> for x in range(10): # doctest: +ELLIPSIS - ... ... print x, - ... 0 1 2 ... 9 - ... - ... >>> for x in range(10): - ... ... print x, # doctest: +ELLIPSIS - ... 0 1 2 ... 9 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - TestResults(failed=0, attempted=2) - -If more than one line of an example with multi-line source has an -option directive, then they are combined: - - >>> def f(x): r''' - ... Should fail (option directive not on the last line): - ... >>> for x in range(10): # doctest: +ELLIPSIS - ... ... print x, # doctest: +NORMALIZE_WHITESPACE - ... 0 1 2...9 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - TestResults(failed=0, attempted=1) - -It is an error to have a comment of the form ``# doctest:`` that is -*not* followed by words of the form ``+OPTION`` or ``-OPTION``, where -``OPTION`` is an option that has been registered with -`register_option`: - - >>> # Error: Option not registered - >>> s = '>>> print 12 #doctest: +BADOPTION' - >>> test = doctest.DocTestParser().get_doctest(s, {}, 's', 's.py', 0) - Traceback (most recent call last): - ValueError: line 1 of the doctest for s has an invalid option: '+BADOPTION' - - >>> # Error: No + or - prefix - >>> s = '>>> print 12 #doctest: ELLIPSIS' - >>> test = doctest.DocTestParser().get_doctest(s, {}, 's', 's.py', 0) - Traceback (most recent call last): - ValueError: line 1 of the doctest for s has an invalid option: 'ELLIPSIS' - -It is an error to use an option directive on a line that contains no -source: - - >>> s = '>>> # doctest: +ELLIPSIS' - >>> test = doctest.DocTestParser().get_doctest(s, {}, 's', 's.py', 0) - Traceback (most recent call last): - ValueError: line 0 of the doctest for s has an option directive on a line with no example: '# doctest: +ELLIPSIS' - - """ - - def test_unicode_output(self): r""" - -Check that unicode output works: - - >>> u'\xe9' - u'\xe9' - -If we return unicode, SpoofOut's buf variable becomes automagically -converted to unicode. This means all subsequent output becomes converted -to unicode, and if the output contains non-ascii characters that failed. -It used to be that this state change carried on between tests, meaning -tests would fail if unicode has been output previously in the testrun. -This test tests that this is no longer so: - - >>> print u'abc' - abc - -And then return a string with non-ascii characters: - - >>> print u'\xe9'.encode('utf-8') - ? - - """ - - -def test_testsource(): r""" -Unit tests for `testsource()`. - -The testsource() function takes a module and a name, finds the (first) -test with that name in that module, and converts it to a script. The -example code is converted to regular Python code. The surrounding -words and expected output are converted to comments: - - >>> import test.test_doctest - >>> name = 'test.test_doctest.sample_func' - >>> print doctest.testsource(test.test_doctest, name) - # Blah blah - # - print sample_func(22) - # Expected: - ## 44 - # - # Yee ha! - - - >>> name = 'test.test_doctest.SampleNewStyleClass' - >>> print doctest.testsource(test.test_doctest, name) - print '1\n2\n3' - # Expected: - ## 1 - ## 2 - ## 3 - - - >>> name = 'test.test_doctest.SampleClass.a_classmethod' - >>> print doctest.testsource(test.test_doctest, name) - print SampleClass.a_classmethod(10) - # Expected: - ## 12 - print SampleClass(0).a_classmethod(10) - # Expected: - ## 12 - -""" - -def test_DocTestSuite(): - """DocTestSuite creates a unittest test suite from a doctest. - - We create a Suite by providing a module. A module can be provided - by passing a module object: - - >>> import unittest - >>> import test.sample_doctest - >>> suite = doctest.DocTestSuite(test.sample_doctest) - >>> suite.run(unittest.TestResult()) - - - We can also supply the module by name: - - >>> suite = doctest.DocTestSuite('test.sample_doctest') - >>> suite.run(unittest.TestResult()) - - - We can use the current module: - - >>> suite = test.sample_doctest.test_suite() - >>> suite.run(unittest.TestResult()) - - - We can supply global variables. If we pass globs, they will be - used instead of the module globals. Here we'll pass an empty - globals, triggering an extra error: - - >>> suite = doctest.DocTestSuite('test.sample_doctest', globs={}) - >>> suite.run(unittest.TestResult()) - - - Alternatively, we can provide extra globals. Here we'll make an - error go away by providing an extra global variable: - - >>> suite = doctest.DocTestSuite('test.sample_doctest', - ... extraglobs={'y': 1}) - >>> suite.run(unittest.TestResult()) - - - You can pass option flags. Here we'll cause an extra error - by disabling the blank-line feature: - - >>> suite = doctest.DocTestSuite('test.sample_doctest', - ... optionflags=doctest.DONT_ACCEPT_BLANKLINE) - >>> suite.run(unittest.TestResult()) - - - You can supply setUp and tearDown functions: - - >>> def setUp(t): - ... import test.test_doctest - ... test.test_doctest.sillySetup = True - - >>> def tearDown(t): - ... import test.test_doctest - ... del test.test_doctest.sillySetup - - Here, we installed a silly variable that the test expects: - - >>> suite = doctest.DocTestSuite('test.sample_doctest', - ... setUp=setUp, tearDown=tearDown) - >>> suite.run(unittest.TestResult()) - - - But the tearDown restores sanity: - - >>> import test.test_doctest - >>> test.test_doctest.sillySetup - Traceback (most recent call last): - ... - AttributeError: 'module' object has no attribute 'sillySetup' - - The setUp and tearDown funtions are passed test objects. Here - we'll use the setUp function to supply the missing variable y: - - >>> def setUp(test): - ... test.globs['y'] = 1 - - >>> suite = doctest.DocTestSuite('test.sample_doctest', setUp=setUp) - >>> suite.run(unittest.TestResult()) - - - Here, we didn't need to use a tearDown function because we - modified the test globals, which are a copy of the - sample_doctest module dictionary. The test globals are - automatically cleared for us after a test. - """ - -def test_DocFileSuite(): - """We can test tests found in text files using a DocFileSuite. - - We create a suite by providing the names of one or more text - files that include examples: - - >>> import unittest - >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... 'test_doctest2.txt', - ... 'test_doctest4.txt') - >>> suite.run(unittest.TestResult()) - - - The test files are looked for in the directory containing the - calling module. A package keyword argument can be provided to - specify a different relative location. - - >>> import unittest - >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... 'test_doctest2.txt', - ... 'test_doctest4.txt', - ... package='test') - >>> suite.run(unittest.TestResult()) - - - Support for using a package's __loader__.get_data() is also - provided. - - >>> import unittest, pkgutil, test - >>> added_loader = False - >>> if not hasattr(test, '__loader__'): - ... test.__loader__ = pkgutil.get_loader(test) - ... added_loader = True - >>> try: - ... suite = doctest.DocFileSuite('test_doctest.txt', - ... 'test_doctest2.txt', - ... 'test_doctest4.txt', - ... package='test') - ... suite.run(unittest.TestResult()) - ... finally: - ... if added_loader: - ... del test.__loader__ - - - '/' should be used as a path separator. It will be converted - to a native separator at run time: - - >>> suite = doctest.DocFileSuite('../test/test_doctest.txt') - >>> suite.run(unittest.TestResult()) - - - If DocFileSuite is used from an interactive session, then files - are resolved relative to the directory of sys.argv[0]: - - >>> import types, os.path, test.test_doctest - >>> save_argv = sys.argv - >>> sys.argv = [test.test_doctest.__file__] - >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... package=types.ModuleType('__main__')) - >>> sys.argv = save_argv - - By setting `module_relative=False`, os-specific paths may be - used (including absolute paths and paths relative to the - working directory): - - >>> # Get the absolute path of the test package. - >>> test_doctest_path = os.path.abspath(test.test_doctest.__file__) - >>> test_pkg_path = os.path.split(test_doctest_path)[0] - - >>> # Use it to find the absolute path of test_doctest.txt. - >>> test_file = os.path.join(test_pkg_path, 'test_doctest.txt') - - >>> suite = doctest.DocFileSuite(test_file, module_relative=False) - >>> suite.run(unittest.TestResult()) - - - It is an error to specify `package` when `module_relative=False`: - - >>> suite = doctest.DocFileSuite(test_file, module_relative=False, - ... package='test') - Traceback (most recent call last): - ValueError: Package may only be specified for module-relative paths. - - You can specify initial global variables: - - >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... 'test_doctest2.txt', - ... 'test_doctest4.txt', - ... globs={'favorite_color': 'blue'}) - >>> suite.run(unittest.TestResult()) - - - In this case, we supplied a missing favorite color. You can - provide doctest options: - - >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... 'test_doctest2.txt', - ... 'test_doctest4.txt', - ... optionflags=doctest.DONT_ACCEPT_BLANKLINE, - ... globs={'favorite_color': 'blue'}) - >>> suite.run(unittest.TestResult()) - - - And, you can provide setUp and tearDown functions: - - >>> def setUp(t): - ... import test.test_doctest - ... test.test_doctest.sillySetup = True - - >>> def tearDown(t): - ... import test.test_doctest - ... del test.test_doctest.sillySetup - - Here, we installed a silly variable that the test expects: - - >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... 'test_doctest2.txt', - ... 'test_doctest4.txt', - ... setUp=setUp, tearDown=tearDown) - >>> suite.run(unittest.TestResult()) - - - But the tearDown restores sanity: - - >>> import test.test_doctest - >>> test.test_doctest.sillySetup - Traceback (most recent call last): - ... - AttributeError: 'module' object has no attribute 'sillySetup' - - The setUp and tearDown funtions are passed test objects. - Here, we'll use a setUp function to set the favorite color in - test_doctest.txt: - - >>> def setUp(test): - ... test.globs['favorite_color'] = 'blue' - - >>> suite = doctest.DocFileSuite('test_doctest.txt', setUp=setUp) - >>> suite.run(unittest.TestResult()) - - - Here, we didn't need to use a tearDown function because we - modified the test globals. The test globals are - automatically cleared for us after a test. - - Tests in a file run using `DocFileSuite` can also access the - `__file__` global, which is set to the name of the file - containing the tests: - - >>> suite = doctest.DocFileSuite('test_doctest3.txt') - >>> suite.run(unittest.TestResult()) - - - If the tests contain non-ASCII characters, we have to specify which - encoding the file is encoded with. We do so by using the `encoding` - parameter: - - >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... 'test_doctest2.txt', - ... 'test_doctest4.txt', - ... encoding='utf-8') - >>> suite.run(unittest.TestResult()) - - - """ - -def test_trailing_space_in_test(): - """ - Trailing spaces in expected output are significant: - - >>> x, y = 'foo', '' - >>> print x, y - foo \n - """ - - -def test_unittest_reportflags(): - """Default unittest reporting flags can be set to control reporting - - Here, we'll set the REPORT_ONLY_FIRST_FAILURE option so we see - only the first failure of each test. First, we'll look at the - output without the flag. The file test_doctest.txt file has two - tests. They both fail if blank lines are disabled: - - >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... optionflags=doctest.DONT_ACCEPT_BLANKLINE) - >>> import unittest - >>> result = suite.run(unittest.TestResult()) - >>> print result.failures[0][1] # doctest: +ELLIPSIS - Traceback ... - Failed example: - favorite_color - ... - Failed example: - if 1: - ... - - Note that we see both failures displayed. - - >>> old = doctest.set_unittest_reportflags( - ... doctest.REPORT_ONLY_FIRST_FAILURE) - - Now, when we run the test: - - >>> result = suite.run(unittest.TestResult()) - >>> print result.failures[0][1] # doctest: +ELLIPSIS - Traceback ... - Failed example: - favorite_color - Exception raised: - ... - NameError: name 'favorite_color' is not defined - - - - We get only the first failure. - - If we give any reporting options when we set up the tests, - however: - - >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... optionflags=doctest.DONT_ACCEPT_BLANKLINE | doctest.REPORT_NDIFF) - - Then the default eporting options are ignored: - - >>> result = suite.run(unittest.TestResult()) - >>> print result.failures[0][1] # doctest: +ELLIPSIS - Traceback ... - Failed example: - favorite_color - ... - Failed example: - if 1: - print 'a' - print - print 'b' - Differences (ndiff with -expected +actual): - a - - - + - b - - - - - Test runners can restore the formatting flags after they run: - - >>> ignored = doctest.set_unittest_reportflags(old) - - """ - -def test_testfile(): r""" -Tests for the `testfile()` function. This function runs all the -doctest examples in a given file. In its simple invokation, it is -called with the name of a file, which is taken to be relative to the -calling module. The return value is (#failures, #tests). - -We don't want `-v` in sys.argv for these tests. - - >>> save_argv = sys.argv - >>> if '-v' in sys.argv: - ... sys.argv = [arg for arg in save_argv if arg != '-v'] - - - >>> doctest.testfile('test_doctest.txt') # doctest: +ELLIPSIS - ********************************************************************** - File "...", line 6, in test_doctest.txt - Failed example: - favorite_color - Exception raised: - ... - NameError: name 'favorite_color' is not defined - ********************************************************************** - 1 items had failures: - 1 of 2 in test_doctest.txt - ***Test Failed*** 1 failures. - TestResults(failed=1, attempted=2) - >>> doctest.master = None # Reset master. - -(Note: we'll be clearing doctest.master after each call to -`doctest.testfile`, to suppress warnings about multiple tests with the -same name.) - -Globals may be specified with the `globs` and `extraglobs` parameters: - - >>> globs = {'favorite_color': 'blue'} - >>> doctest.testfile('test_doctest.txt', globs=globs) - TestResults(failed=0, attempted=2) - >>> doctest.master = None # Reset master. - - >>> extraglobs = {'favorite_color': 'red'} - >>> doctest.testfile('test_doctest.txt', globs=globs, - ... extraglobs=extraglobs) # doctest: +ELLIPSIS - ********************************************************************** - File "...", line 6, in test_doctest.txt - Failed example: - favorite_color - Expected: - 'blue' - Got: - 'red' - ********************************************************************** - 1 items had failures: - 1 of 2 in test_doctest.txt - ***Test Failed*** 1 failures. - TestResults(failed=1, attempted=2) - >>> doctest.master = None # Reset master. - -The file may be made relative to a given module or package, using the -optional `module_relative` parameter: - - >>> doctest.testfile('test_doctest.txt', globs=globs, - ... module_relative='test') - TestResults(failed=0, attempted=2) - >>> doctest.master = None # Reset master. - -Verbosity can be increased with the optional `verbose` parameter: - - >>> doctest.testfile('test_doctest.txt', globs=globs, verbose=True) - Trying: - favorite_color - Expecting: - 'blue' - ok - Trying: - if 1: - print 'a' - print - print 'b' - Expecting: - a - - b - ok - 1 items passed all tests: - 2 tests in test_doctest.txt - 2 tests in 1 items. - 2 passed and 0 failed. - Test passed. - TestResults(failed=0, attempted=2) - >>> doctest.master = None # Reset master. - -The name of the test may be specified with the optional `name` -parameter: - - >>> doctest.testfile('test_doctest.txt', name='newname') - ... # doctest: +ELLIPSIS - ********************************************************************** - File "...", line 6, in newname - ... - TestResults(failed=1, attempted=2) - >>> doctest.master = None # Reset master. - -The summary report may be suppressed with the optional `report` -parameter: - - >>> doctest.testfile('test_doctest.txt', report=False) - ... # doctest: +ELLIPSIS - ********************************************************************** - File "...", line 6, in test_doctest.txt - Failed example: - favorite_color - Exception raised: - ... - NameError: name 'favorite_color' is not defined - TestResults(failed=1, attempted=2) - >>> doctest.master = None # Reset master. - -The optional keyword argument `raise_on_error` can be used to raise an -exception on the first error (which may be useful for postmortem -debugging): - - >>> doctest.testfile('test_doctest.txt', raise_on_error=True) - ... # doctest: +ELLIPSIS - Traceback (most recent call last): - UnexpectedException: ... - >>> doctest.master = None # Reset master. - -If the tests contain non-ASCII characters, the tests might fail, since -it's unknown which encoding is used. The encoding can be specified -using the optional keyword argument `encoding`: - - >>> doctest.testfile('test_doctest4.txt') # doctest: +ELLIPSIS - ********************************************************************** - File "...", line 7, in test_doctest4.txt - Failed example: - u'...' - Expected: - u'f\xf6\xf6' - Got: - u'f\xc3\xb6\xc3\xb6' - ********************************************************************** - ... - ********************************************************************** - 1 items had failures: - 2 of 4 in test_doctest4.txt - ***Test Failed*** 2 failures. - TestResults(failed=2, attempted=4) - >>> doctest.master = None # Reset master. - - >>> doctest.testfile('test_doctest4.txt', encoding='utf-8') - TestResults(failed=0, attempted=4) - >>> doctest.master = None # Reset master. - -Switch the module encoding to 'utf-8' to test the verbose output without -bothering with the current sys.stdout encoding. - - >>> doctest._encoding, saved_encoding = 'utf-8', doctest._encoding - >>> doctest.testfile('test_doctest4.txt', encoding='utf-8', verbose=True) - Trying: - u'f??' - Expecting: - u'f\xf6\xf6' - ok - Trying: - u'b?r' - Expecting: - u'b\u0105r' - ok - Trying: - 'f??' - Expecting: - 'f\xc3\xb6\xc3\xb6' - ok - Trying: - 'b?r' - Expecting: - 'b\xc4\x85r' - ok - 1 items passed all tests: - 4 tests in test_doctest4.txt - 4 tests in 1 items. - 4 passed and 0 failed. - Test passed. - TestResults(failed=0, attempted=4) - >>> doctest._encoding = saved_encoding - >>> doctest.master = None # Reset master. - >>> sys.argv = save_argv -""" - -# old_test1, ... used to live in doctest.py, but cluttered it. Note -# that these use the deprecated doctest.Tester, so should go away (or -# be rewritten) someday. - -def old_test1(): r""" ->>> from doctest import Tester ->>> t = Tester(globs={'x': 42}, verbose=0) ->>> t.runstring(r''' -... >>> x = x * 2 -... >>> print x -... 42 -... ''', 'XYZ') -********************************************************************** -Line 3, in XYZ -Failed example: - print x -Expected: - 42 -Got: - 84 -TestResults(failed=1, attempted=2) ->>> t.runstring(">>> x = x * 2\n>>> print x\n84\n", 'example2') -TestResults(failed=0, attempted=2) ->>> t.summarize() -********************************************************************** -1 items had failures: - 1 of 2 in XYZ -***Test Failed*** 1 failures. -TestResults(failed=1, attempted=4) ->>> t.summarize(verbose=1) -1 items passed all tests: - 2 tests in example2 -********************************************************************** -1 items had failures: - 1 of 2 in XYZ -4 tests in 2 items. -3 passed and 1 failed. -***Test Failed*** 1 failures. -TestResults(failed=1, attempted=4) -""" - -def old_test2(): r""" - >>> from doctest import Tester - >>> t = Tester(globs={}, verbose=1) - >>> test = r''' - ... # just an example - ... >>> x = 1 + 2 - ... >>> x - ... 3 - ... ''' - >>> t.runstring(test, "Example") - Running string Example - Trying: - x = 1 + 2 - Expecting nothing - ok - Trying: - x - Expecting: - 3 - ok - 0 of 2 examples failed in string Example - TestResults(failed=0, attempted=2) -""" - -def old_test3(): r""" - >>> from doctest import Tester - >>> t = Tester(globs={}, verbose=0) - >>> def _f(): - ... '''Trivial docstring example. - ... >>> assert 2 == 2 - ... ''' - ... return 32 - ... - >>> t.rundoc(_f) # expect 0 failures in 1 example - TestResults(failed=0, attempted=1) -""" - -def old_test4(): """ - >>> import types - >>> m1 = types.ModuleType('_m1') - >>> m2 = types.ModuleType('_m2') - >>> test_data = \""" - ... def _f(): - ... '''>>> assert 1 == 1 - ... ''' - ... def g(): - ... '''>>> assert 2 != 1 - ... ''' - ... class H: - ... '''>>> assert 2 > 1 - ... ''' - ... def bar(self): - ... '''>>> assert 1 < 2 - ... ''' - ... \""" - >>> exec test_data in m1.__dict__ - >>> exec test_data in m2.__dict__ - >>> m1.__dict__.update({"f2": m2._f, "g2": m2.g, "h2": m2.H}) - - Tests that objects outside m1 are excluded: - - >>> from doctest import Tester - >>> t = Tester(globs={}, verbose=0) - >>> t.rundict(m1.__dict__, "rundict_test", m1) # f2 and g2 and h2 skipped - TestResults(failed=0, attempted=4) - - Once more, not excluding stuff outside m1: - - >>> t = Tester(globs={}, verbose=0) - >>> t.rundict(m1.__dict__, "rundict_test_pvt") # None are skipped. - TestResults(failed=0, attempted=8) - - The exclusion of objects from outside the designated module is - meant to be invoked automagically by testmod. - - >>> doctest.testmod(m1, verbose=False) - TestResults(failed=0, attempted=4) -""" - -###################################################################### -## Main -###################################################################### - -def test_main(): - # Check the doctest cases in doctest itself: - test_support.run_doctest(doctest, verbosity=True) - - from test import test_doctest - - # Ignore all warnings about the use of class Tester in this module. - deprecations = [("class Tester is deprecated", DeprecationWarning)] - if sys.py3kwarning: - deprecations += [("backquote not supported", SyntaxWarning), - ("execfile.. not supported", DeprecationWarning)] - with test_support.check_warnings(*deprecations): - # Check the doctest cases defined here: - test_support.run_doctest(test_doctest, verbosity=True) - -if __name__ == '__main__': - if '-c' in sys.argv: - test_coverage('/tmp/doctest.cover') - else: - test_main() -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Thu May 3 21:03:58 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Thu, 03 May 2012 21:03:58 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Fix_ZeroDivisionError_messag?= =?utf8?q?e_to_match_CPython=2E?= Message-ID: http://hg.python.org/jython/rev/55792e2c6019 changeset: 6641:55792e2c6019 user: Frank Wierzbicki date: Thu May 03 11:01:58 2012 -0700 summary: Fix ZeroDivisionError message to match CPython. files: src/org/python/core/PyInteger.java | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/src/org/python/core/PyInteger.java b/src/org/python/core/PyInteger.java --- a/src/org/python/core/PyInteger.java +++ b/src/org/python/core/PyInteger.java @@ -583,7 +583,7 @@ if (value != 0) { return left.__float__().__pow__(right, modulo); } else { - throw Py.ZeroDivisionError("cannot raise 0 to a negative power"); + throw Py.ZeroDivisionError("0.0 cannot be raised to a negative power"); } } -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Thu May 3 21:03:58 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Thu, 03 May 2012 21:03:58 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Add_expm1=2E?= Message-ID: http://hg.python.org/jython/rev/12e83e76be70 changeset: 6642:12e83e76be70 user: Miki Tebeka date: Thu May 03 11:04:36 2012 -0700 summary: Add expm1. files: src/org/python/modules/math.java | 13 +++++++++++++ 1 files changed, 13 insertions(+), 0 deletions(-) diff --git a/src/org/python/modules/math.java b/src/org/python/modules/math.java --- a/src/org/python/modules/math.java +++ b/src/org/python/modules/math.java @@ -40,6 +40,19 @@ return math_erf.erfc(v); } + public static double expm1(double v) { + if (Double.POSITIVE_INFINITY == v) { + return v; + } + + double result = Math.expm1(v); + if (Double.isInfinite(result)) { + throw Py.OverflowError(Double.toString(v)); + } + + return result; + } + public static double acos(double v) { if (isinf(v)) { throwMathDomainValueError(); -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Thu May 3 21:03:58 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Thu, 03 May 2012 21:03:58 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Don=27t_swallow_SystemExit_o?= =?utf8?q?r_KeyboardInterrupt_from_hasattr=2E?= Message-ID: http://hg.python.org/jython/rev/003b5ba52cfa changeset: 6643:003b5ba52cfa parent: 6637:23d0189442da user: Frank Wierzbicki date: Thu May 03 11:59:51 2012 -0700 summary: Don't swallow SystemExit or KeyboardInterrupt from hasattr. files: Lib/test/test_builtin.py | 6 ++---- src/org/python/core/__builtin__.java | 5 ++++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -602,13 +602,11 @@ class A: def __getattr__(self, what): raise KeyboardInterrupt - if not is_jython: #FIXME #1861: not working in Jython - self.assertRaises(KeyboardInterrupt, hasattr, A(), "b") + self.assertRaises(KeyboardInterrupt, hasattr, A(), "b") class B: def __getattr__(self, what): raise SystemExit - if not is_jython: #FIXME #1861: not working in Jython - self.assertRaises(SystemExit, hasattr, B(), "b") + self.assertRaises(SystemExit, hasattr, B(), "b") def test_hash(self): hash(None) diff --git a/src/org/python/core/__builtin__.java b/src/org/python/core/__builtin__.java --- a/src/org/python/core/__builtin__.java +++ b/src/org/python/core/__builtin__.java @@ -647,7 +647,10 @@ try { return obj.__findattr__(name) != null; } catch (PyException pye) { - // swallow + if (pye.match(Py.KeyboardInterrupt) || pye.match(Py.SystemExit)) { + throw pye; + } + //Otherwise swallow exception. } return false; } -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Thu May 3 21:03:58 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Thu, 03 May 2012 21:03:58 +0200 Subject: [Jython-checkins] =?utf8?q?jython_=28merge_default_-=3E_default?= =?utf8?b?KTogTWVyZ2Uu?= Message-ID: http://hg.python.org/jython/rev/1b99514a96e6 changeset: 6644:1b99514a96e6 parent: 6643:003b5ba52cfa parent: 6642:12e83e76be70 user: Frank Wierzbicki date: Thu May 03 12:01:00 2012 -0700 summary: Merge. files: Lib/doctest.py | 62 +- Lib/test/test_doctest.py | 2313 ---------------- src/org/python/core/PyInteger.java | 2 +- src/org/python/modules/math.java | 37 +- 4 files changed, 91 insertions(+), 2323 deletions(-) diff --git a/Lib/doctest.py b/Lib/doctest.py --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -451,6 +451,25 @@ self.options = options self.exc_msg = exc_msg + def __eq__(self, other): + if type(self) is not type(other): + return NotImplemented + + return self.source == other.source and \ + self.want == other.want and \ + self.lineno == other.lineno and \ + self.indent == other.indent and \ + self.options == other.options and \ + self.exc_msg == other.exc_msg + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash((self.source, self.want, self.lineno, self.indent, + self.exc_msg)) + + class DocTest: """ A collection of doctest examples that should be run in a single @@ -499,6 +518,22 @@ return ('' % (self.name, self.filename, self.lineno, examples)) + def __eq__(self, other): + if type(self) is not type(other): + return NotImplemented + + return self.examples == other.examples and \ + self.docstring == other.docstring and \ + self.globs == other.globs and \ + self.name == other.name and \ + self.filename == other.filename and \ + self.lineno == other.lineno + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash((self.docstring, self.name, self.filename, self.lineno)) # This lets us sort tests by name: def __cmp__(self, other): @@ -882,12 +917,6 @@ elif inspect.isfunction(object): return module.__dict__ is object.func_globals elif inspect.isclass(object): - # XXX: Jython transition 2.5 - # Java classes appear as Python classes to inspect, but they - # have no __module__ http://jython.org/bugs/1758279 - # org.python.modules uses Java classes to masq - if not hasattr(object, '__module__'): - return False return module.__name__ == object.__module__ elif hasattr(object, '__module__'): return module.__name__ == object.__module__ @@ -1225,7 +1254,7 @@ # Process each example. for examplenum, example in enumerate(test.examples): - # If REPORT_ONLY_FIRST_FAILURE is set, then supress + # If REPORT_ONLY_FIRST_FAILURE is set, then suppress # reporting after the first failure. quiet = (self.optionflags & REPORT_ONLY_FIRST_FAILURE and failures > 0) @@ -2196,7 +2225,7 @@ caller can catch the errors and initiate post-mortem debugging. The DocTestCase provides a debug method that raises - UnexpectedException errors if there is an unexepcted + UnexpectedException errors if there is an unexpected exception: >>> test = DocTestParser().get_doctest('>>> raise KeyError\n42', @@ -2262,6 +2291,23 @@ def id(self): return self._dt_test.name + def __eq__(self, other): + if type(self) is not type(other): + return NotImplemented + + return self._dt_test == other._dt_test and \ + self._dt_optionflags == other._dt_optionflags and \ + self._dt_setUp == other._dt_setUp and \ + self._dt_tearDown == other._dt_tearDown and \ + self._dt_checker == other._dt_checker + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash((self._dt_optionflags, self._dt_setUp, self._dt_tearDown, + self._dt_checker)) + def __repr__(self): name = self._dt_test.name.split('.') return "%s (%s)" % (name[-1], '.'.join(name[:-1])) diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py deleted file mode 100644 --- a/Lib/test/test_doctest.py +++ /dev/null @@ -1,2313 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Test script for doctest. - -FIXME: Large parts gutted to run on Jython -- #1863. -""" - -import sys -from test import test_support -import doctest - -# NOTE: There are some additional tests relating to interaction with -# zipimport in the test_zipimport_support test module. - -###################################################################### -## Sample Objects (used by test cases) -###################################################################### - -def sample_func(v): - """ - Blah blah - - >>> print sample_func(22) - 44 - - Yee ha! - """ - return v+v - -class SampleClass: - """ - >>> print 1 - 1 - - >>> # comments get ignored. so are empty PS1 and PS2 prompts: - >>> - ... - - Multiline example: - >>> sc = SampleClass(3) - >>> for i in range(10): - ... sc = sc.double() - ... print sc.get(), - 6 12 24 48 96 192 384 768 1536 3072 - """ - def __init__(self, val): - """ - >>> print SampleClass(12).get() - 12 - """ - self.val = val - - def double(self): - """ - >>> print SampleClass(12).double().get() - 24 - """ - return SampleClass(self.val + self.val) - - def get(self): - """ - >>> print SampleClass(-5).get() - -5 - """ - return self.val - - def a_staticmethod(v): - """ - >>> print SampleClass.a_staticmethod(10) - 11 - """ - return v+1 - a_staticmethod = staticmethod(a_staticmethod) - - def a_classmethod(cls, v): - """ - >>> print SampleClass.a_classmethod(10) - 12 - >>> print SampleClass(0).a_classmethod(10) - 12 - """ - return v+2 - a_classmethod = classmethod(a_classmethod) - - a_property = property(get, doc=""" - >>> print SampleClass(22).a_property - 22 - """) - - class NestedClass: - """ - >>> x = SampleClass.NestedClass(5) - >>> y = x.square() - >>> print y.get() - 25 - """ - def __init__(self, val=0): - """ - >>> print SampleClass.NestedClass().get() - 0 - """ - self.val = val - def square(self): - return SampleClass.NestedClass(self.val*self.val) - def get(self): - return self.val - -class SampleNewStyleClass(object): - r""" - >>> print '1\n2\n3' - 1 - 2 - 3 - """ - def __init__(self, val): - """ - >>> print SampleNewStyleClass(12).get() - 12 - """ - self.val = val - - def double(self): - """ - >>> print SampleNewStyleClass(12).double().get() - 24 - """ - return SampleNewStyleClass(self.val + self.val) - - def get(self): - """ - >>> print SampleNewStyleClass(-5).get() - -5 - """ - return self.val - -###################################################################### -## Fake stdin (for testing interactive debugging) -###################################################################### - -class _FakeInput: - """ - A fake input stream for pdb's interactive debugger. Whenever a - line is read, print it (to simulate the user typing it), and then - return it. The set of lines to return is specified in the - constructor; they should not have trailing newlines. - """ - def __init__(self, lines): - self.lines = lines - - def readline(self): - line = self.lines.pop(0) - print line - return line+'\n' - -###################################################################### -## Test Cases -###################################################################### - -def test_Example(): r""" -Unit tests for the `Example` class. - -Example is a simple container class that holds: - - `source`: A source string. - - `want`: An expected output string. - - `exc_msg`: An expected exception message string (or None if no - exception is expected). - - `lineno`: A line number (within the docstring). - - `indent`: The example's indentation in the input string. - - `options`: An option dictionary, mapping option flags to True or - False. - -These attributes are set by the constructor. `source` and `want` are -required; the other attributes all have default values: - - >>> example = doctest.Example('print 1', '1\n') - >>> (example.source, example.want, example.exc_msg, - ... example.lineno, example.indent, example.options) - ('print 1\n', '1\n', None, 0, 0, {}) - -The first three attributes (`source`, `want`, and `exc_msg`) may be -specified positionally; the remaining arguments should be specified as -keyword arguments: - - >>> exc_msg = 'IndexError: pop from an empty list' - >>> example = doctest.Example('[].pop()', '', exc_msg, - ... lineno=5, indent=4, - ... options={doctest.ELLIPSIS: True}) - >>> (example.source, example.want, example.exc_msg, - ... example.lineno, example.indent, example.options) - ('[].pop()\n', '', 'IndexError: pop from an empty list\n', 5, 4, {8: True}) - -The constructor normalizes the `source` string to end in a newline: - - Source spans a single line: no terminating newline. - >>> e = doctest.Example('print 1', '1\n') - >>> e.source, e.want - ('print 1\n', '1\n') - - >>> e = doctest.Example('print 1\n', '1\n') - >>> e.source, e.want - ('print 1\n', '1\n') - - Source spans multiple lines: require terminating newline. - >>> e = doctest.Example('print 1;\nprint 2\n', '1\n2\n') - >>> e.source, e.want - ('print 1;\nprint 2\n', '1\n2\n') - - >>> e = doctest.Example('print 1;\nprint 2', '1\n2\n') - >>> e.source, e.want - ('print 1;\nprint 2\n', '1\n2\n') - - Empty source string (which should never appear in real examples) - >>> e = doctest.Example('', '') - >>> e.source, e.want - ('\n', '') - -The constructor normalizes the `want` string to end in a newline, -unless it's the empty string: - - >>> e = doctest.Example('print 1', '1\n') - >>> e.source, e.want - ('print 1\n', '1\n') - - >>> e = doctest.Example('print 1', '1') - >>> e.source, e.want - ('print 1\n', '1\n') - - >>> e = doctest.Example('print', '') - >>> e.source, e.want - ('print\n', '') - -The constructor normalizes the `exc_msg` string to end in a newline, -unless it's `None`: - - Message spans one line - >>> exc_msg = 'IndexError: pop from an empty list' - >>> e = doctest.Example('[].pop()', '', exc_msg) - >>> e.exc_msg - 'IndexError: pop from an empty list\n' - - >>> exc_msg = 'IndexError: pop from an empty list\n' - >>> e = doctest.Example('[].pop()', '', exc_msg) - >>> e.exc_msg - 'IndexError: pop from an empty list\n' - - Message spans multiple lines - >>> exc_msg = 'ValueError: 1\n 2' - >>> e = doctest.Example('raise ValueError("1\n 2")', '', exc_msg) - >>> e.exc_msg - 'ValueError: 1\n 2\n' - - >>> exc_msg = 'ValueError: 1\n 2\n' - >>> e = doctest.Example('raise ValueError("1\n 2")', '', exc_msg) - >>> e.exc_msg - 'ValueError: 1\n 2\n' - - Empty (but non-None) exception message (which should never appear - in real examples) - >>> exc_msg = '' - >>> e = doctest.Example('raise X()', '', exc_msg) - >>> e.exc_msg - '\n' - -""" - -def test_DocTest(): r""" -Unit tests for the `DocTest` class. - -DocTest is a collection of examples, extracted from a docstring, along -with information about where the docstring comes from (a name, -filename, and line number). The docstring is parsed by the `DocTest` -constructor: - - >>> docstring = ''' - ... >>> print 12 - ... 12 - ... - ... Non-example text. - ... - ... >>> print 'another\example' - ... another - ... example - ... ''' - >>> globs = {} # globals to run the test in. - >>> parser = doctest.DocTestParser() - >>> test = parser.get_doctest(docstring, globs, 'some_test', - ... 'some_file', 20) - >>> print test - - >>> len(test.examples) - 2 - >>> e1, e2 = test.examples - >>> (e1.source, e1.want, e1.lineno) - ('print 12\n', '12\n', 1) - >>> (e2.source, e2.want, e2.lineno) - ("print 'another\\example'\n", 'another\nexample\n', 6) - -Source information (name, filename, and line number) is available as -attributes on the doctest object: - - >>> (test.name, test.filename, test.lineno) - ('some_test', 'some_file', 20) - -The line number of an example within its containing file is found by -adding the line number of the example and the line number of its -containing test: - - >>> test.lineno + e1.lineno - 21 - >>> test.lineno + e2.lineno - 26 - -If the docstring contains inconsistant leading whitespace in the -expected output of an example, then `DocTest` will raise a ValueError: - - >>> docstring = r''' - ... >>> print 'bad\nindentation' - ... bad - ... indentation - ... ''' - >>> parser.get_doctest(docstring, globs, 'some_test', 'filename', 0) - Traceback (most recent call last): - ValueError: line 4 of the docstring for some_test has inconsistent leading whitespace: 'indentation' - -If the docstring contains inconsistent leading whitespace on -continuation lines, then `DocTest` will raise a ValueError: - - >>> docstring = r''' - ... >>> print ('bad indentation', - ... ... 2) - ... ('bad', 'indentation') - ... ''' - >>> parser.get_doctest(docstring, globs, 'some_test', 'filename', 0) - Traceback (most recent call last): - ValueError: line 2 of the docstring for some_test has inconsistent leading whitespace: '... 2)' - -If there's no blank space after a PS1 prompt ('>>>'), then `DocTest` -will raise a ValueError: - - >>> docstring = '>>>print 1\n1' - >>> parser.get_doctest(docstring, globs, 'some_test', 'filename', 0) - Traceback (most recent call last): - ValueError: line 1 of the docstring for some_test lacks blank after >>>: '>>>print 1' - -If there's no blank space after a PS2 prompt ('...'), then `DocTest` -will raise a ValueError: - - >>> docstring = '>>> if 1:\n...print 1\n1' - >>> parser.get_doctest(docstring, globs, 'some_test', 'filename', 0) - Traceback (most recent call last): - ValueError: line 2 of the docstring for some_test lacks blank after ...: '...print 1' - -Compare `DocTest`: - - >>> docstring = ''' - ... >>> print 12 - ... 12 - ... ''' - >>> test = parser.get_doctest(docstring, globs, 'some_test', - ... 'some_test', 20) - >>> same_test = parser.get_doctest(docstring, globs, 'some_test', - ... 'some_test', 20) - >>> docstring = ''' - ... >>> print 42 - ... 42 - ... ''' - >>> other_test = parser.get_doctest(docstring, globs, 'other_test', - ... 'other_file', 10) - >>> test == other_test - False - >>> test != other_test - True - -Compare `DocTestCase`: - - >>> DocTestCase = doctest.DocTestCase - >>> test_case = DocTestCase(test) - >>> same_test_case = DocTestCase(same_test) - >>> other_test_case = DocTestCase(other_test) - >>> test_case == same_test_case - True - >>> test_case != same_test_case - False - >>> hash(test_case) == hash(same_test_case) - True - >>> test == other_test_case - False - >>> test != other_test_case - True - -""" - -def test_DocTestFinder(): r""" -Unit tests for the `DocTestFinder` class. - -DocTestFinder is used to extract DocTests from an object's docstring -and the docstrings of its contained objects. It can be used with -modules, functions, classes, methods, staticmethods, classmethods, and -properties. - -Finding Tests in Functions -~~~~~~~~~~~~~~~~~~~~~~~~~~ -For a function whose docstring contains examples, DocTestFinder.find() -will return a single test (for that function's docstring): - - >>> finder = doctest.DocTestFinder() - -We'll simulate a __file__ attr that ends in pyc: - - >>> import test.test_doctest - >>> old = test.test_doctest.__file__ - >>> test.test_doctest.__file__ = 'test_doctest.pyc' - - >>> tests = finder.find(sample_func) - - >>> print tests # doctest: +ELLIPSIS - [] - -The exact name depends on how test_doctest was invoked, so allow for -leading path components. - - >>> tests[0].filename # doctest: +ELLIPSIS - '...test_doctest.py' - - >>> test.test_doctest.__file__ = old - - - >>> e = tests[0].examples[0] - >>> (e.source, e.want, e.lineno) - ('print sample_func(22)\n', '44\n', 3) - -By default, tests are created for objects with no docstring: - - >>> def no_docstring(v): - ... pass - >>> finder.find(no_docstring) - [] - -However, the optional argument `exclude_empty` to the DocTestFinder -constructor can be used to exclude tests for objects with empty -docstrings: - - >>> def no_docstring(v): - ... pass - >>> excl_empty_finder = doctest.DocTestFinder(exclude_empty=True) - >>> excl_empty_finder.find(no_docstring) - [] - -If the function has a docstring with no examples, then a test with no -examples is returned. (This lets `DocTestRunner` collect statistics -about which functions have no tests -- but is that useful? And should -an empty test also be created when there's no docstring?) - - >>> def no_examples(v): - ... ''' no doctest examples ''' - >>> finder.find(no_examples) # doctest: +ELLIPSIS - [] - -Finding Tests in Classes -~~~~~~~~~~~~~~~~~~~~~~~~ -For a class, DocTestFinder will create a test for the class's -docstring, and will recursively explore its contents, including -methods, classmethods, staticmethods, properties, and nested classes. - - >>> finder = doctest.DocTestFinder() - >>> tests = finder.find(SampleClass) - >>> for t in tests: - ... print '%2s %s' % (len(t.examples), t.name) - 3 SampleClass - 3 SampleClass.NestedClass - 1 SampleClass.NestedClass.__init__ - 1 SampleClass.__init__ - 2 SampleClass.a_classmethod - 1 SampleClass.a_property - 1 SampleClass.a_staticmethod - 1 SampleClass.double - 1 SampleClass.get - -New-style classes are also supported: - - >>> tests = finder.find(SampleNewStyleClass) - >>> for t in tests: - ... print '%2s %s' % (len(t.examples), t.name) - 1 SampleNewStyleClass - 1 SampleNewStyleClass.__init__ - 1 SampleNewStyleClass.double - 1 SampleNewStyleClass.get - -Finding Tests in Modules -~~~~~~~~~~~~~~~~~~~~~~~~ -For a module, DocTestFinder will create a test for the class's -docstring, and will recursively explore its contents, including -functions, classes, and the `__test__` dictionary, if it exists: - - >>> # A module - >>> import types - >>> m = types.ModuleType('some_module') - >>> def triple(val): - ... ''' - ... >>> print triple(11) - ... 33 - ... ''' - ... return val*3 - >>> m.__dict__.update({ - ... 'sample_func': sample_func, - ... 'SampleClass': SampleClass, - ... '__doc__': ''' - ... Module docstring. - ... >>> print 'module' - ... module - ... ''', - ... '__test__': { - ... 'd': '>>> print 6\n6\n>>> print 7\n7\n', - ... 'c': triple}}) - - >>> finder = doctest.DocTestFinder() - >>> # Use module=test.test_doctest, to prevent doctest from - >>> # ignoring the objects since they weren't defined in m. - >>> import test.test_doctest - >>> tests = finder.find(m, module=test.test_doctest) - >>> for t in tests: - ... print '%2s %s' % (len(t.examples), t.name) - 1 some_module - 3 some_module.SampleClass - 3 some_module.SampleClass.NestedClass - 1 some_module.SampleClass.NestedClass.__init__ - 1 some_module.SampleClass.__init__ - 2 some_module.SampleClass.a_classmethod - 1 some_module.SampleClass.a_property - 1 some_module.SampleClass.a_staticmethod - 1 some_module.SampleClass.double - 1 some_module.SampleClass.get - 1 some_module.__test__.c - 2 some_module.__test__.d - 1 some_module.sample_func - -Duplicate Removal -~~~~~~~~~~~~~~~~~ -If a single object is listed twice (under different names), then tests -will only be generated for it once: - - >>> from test import doctest_aliases - >>> assert doctest_aliases.TwoNames.f - >>> assert doctest_aliases.TwoNames.g - >>> tests = excl_empty_finder.find(doctest_aliases) - >>> print len(tests) - 2 - >>> print tests[0].name - test.doctest_aliases.TwoNames - - TwoNames.f and TwoNames.g are bound to the same object. - We can't guess which will be found in doctest's traversal of - TwoNames.__dict__ first, so we have to allow for either. - - >>> tests[1].name.split('.')[-1] in ['f', 'g'] - True - -Empty Tests -~~~~~~~~~~~ -By default, an object with no doctests doesn't create any tests: - - >>> tests = doctest.DocTestFinder().find(SampleClass) - >>> for t in tests: - ... print '%2s %s' % (len(t.examples), t.name) - 3 SampleClass - 3 SampleClass.NestedClass - 1 SampleClass.NestedClass.__init__ - 1 SampleClass.__init__ - 2 SampleClass.a_classmethod - 1 SampleClass.a_property - 1 SampleClass.a_staticmethod - 1 SampleClass.double - 1 SampleClass.get - -By default, that excluded objects with no doctests. exclude_empty=False -tells it to include (empty) tests for objects with no doctests. This feature -is really to support backward compatibility in what doctest.master.summarize() -displays. - - >>> tests = doctest.DocTestFinder(exclude_empty=False).find(SampleClass) - >>> for t in tests: - ... print '%2s %s' % (len(t.examples), t.name) - 3 SampleClass - 3 SampleClass.NestedClass - 1 SampleClass.NestedClass.__init__ - 0 SampleClass.NestedClass.get - 0 SampleClass.NestedClass.square - 1 SampleClass.__init__ - 2 SampleClass.a_classmethod - 1 SampleClass.a_property - 1 SampleClass.a_staticmethod - 1 SampleClass.double - 1 SampleClass.get - -Turning off Recursion -~~~~~~~~~~~~~~~~~~~~~ -DocTestFinder can be told not to look for tests in contained objects -using the `recurse` flag: - - >>> tests = doctest.DocTestFinder(recurse=False).find(SampleClass) - >>> for t in tests: - ... print '%2s %s' % (len(t.examples), t.name) - 3 SampleClass - -Line numbers -~~~~~~~~~~~~ -DocTestFinder finds the line number of each example: - - >>> def f(x): - ... ''' - ... >>> x = 12 - ... - ... some text - ... - ... >>> # examples are not created for comments & bare prompts. - ... >>> - ... ... - ... - ... >>> for x in range(10): - ... ... print x, - ... 0 1 2 3 4 5 6 7 8 9 - ... >>> x//2 - ... 6 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> [e.lineno for e in test.examples] - [1, 9, 12] -""" - -def test_DocTestParser(): r""" -Unit tests for the `DocTestParser` class. - -DocTestParser is used to parse docstrings containing doctest examples. - -The `parse` method divides a docstring into examples and intervening -text: - - >>> s = ''' - ... >>> x, y = 2, 3 # no output expected - ... >>> if 1: - ... ... print x - ... ... print y - ... 2 - ... 3 - ... - ... Some text. - ... >>> x+y - ... 5 - ... ''' - >>> parser = doctest.DocTestParser() - >>> for piece in parser.parse(s): - ... if isinstance(piece, doctest.Example): - ... print 'Example:', (piece.source, piece.want, piece.lineno) - ... else: - ... print ' Text:', `piece` - Text: '\n' - Example: ('x, y = 2, 3 # no output expected\n', '', 1) - Text: '' - Example: ('if 1:\n print x\n print y\n', '2\n3\n', 2) - Text: '\nSome text.\n' - Example: ('x+y\n', '5\n', 9) - Text: '' - -The `get_examples` method returns just the examples: - - >>> for piece in parser.get_examples(s): - ... print (piece.source, piece.want, piece.lineno) - ('x, y = 2, 3 # no output expected\n', '', 1) - ('if 1:\n print x\n print y\n', '2\n3\n', 2) - ('x+y\n', '5\n', 9) - -The `get_doctest` method creates a Test from the examples, along with the -given arguments: - - >>> test = parser.get_doctest(s, {}, 'name', 'filename', lineno=5) - >>> (test.name, test.filename, test.lineno) - ('name', 'filename', 5) - >>> for piece in test.examples: - ... print (piece.source, piece.want, piece.lineno) - ('x, y = 2, 3 # no output expected\n', '', 1) - ('if 1:\n print x\n print y\n', '2\n3\n', 2) - ('x+y\n', '5\n', 9) -""" - -class test_DocTestRunner: - def basics(): r""" -Unit tests for the `DocTestRunner` class. - -DocTestRunner is used to run DocTest test cases, and to accumulate -statistics. Here's a simple DocTest case we can use: - - >>> def f(x): - ... ''' - ... >>> x = 12 - ... >>> print x - ... 12 - ... >>> x//2 - ... 6 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - -The main DocTestRunner interface is the `run` method, which runs a -given DocTest case in a given namespace (globs). It returns a tuple -`(f,t)`, where `f` is the number of failed tests and `t` is the number -of tried tests. - - >>> doctest.DocTestRunner(verbose=False).run(test) - TestResults(failed=0, attempted=3) - -If any example produces incorrect output, then the test runner reports -the failure and proceeds to the next example: - - >>> def f(x): - ... ''' - ... >>> x = 12 - ... >>> print x - ... 14 - ... >>> x//2 - ... 6 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=True).run(test) - ... # doctest: +ELLIPSIS - Trying: - x = 12 - Expecting nothing - ok - Trying: - print x - Expecting: - 14 - ********************************************************************** - File ..., line 4, in f - Failed example: - print x - Expected: - 14 - Got: - 12 - Trying: - x//2 - Expecting: - 6 - ok - TestResults(failed=1, attempted=3) -""" - def verbose_flag(): r""" -The `verbose` flag makes the test runner generate more detailed -output: - - >>> def f(x): - ... ''' - ... >>> x = 12 - ... >>> print x - ... 12 - ... >>> x//2 - ... 6 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - - >>> doctest.DocTestRunner(verbose=True).run(test) - Trying: - x = 12 - Expecting nothing - ok - Trying: - print x - Expecting: - 12 - ok - Trying: - x//2 - Expecting: - 6 - ok - TestResults(failed=0, attempted=3) - -If the `verbose` flag is unspecified, then the output will be verbose -iff `-v` appears in sys.argv: - - >>> # Save the real sys.argv list. - >>> old_argv = sys.argv - - >>> # If -v does not appear in sys.argv, then output isn't verbose. - >>> sys.argv = ['test'] - >>> doctest.DocTestRunner().run(test) - TestResults(failed=0, attempted=3) - - >>> # If -v does appear in sys.argv, then output is verbose. - >>> sys.argv = ['test', '-v'] - >>> doctest.DocTestRunner().run(test) - Trying: - x = 12 - Expecting nothing - ok - Trying: - print x - Expecting: - 12 - ok - Trying: - x//2 - Expecting: - 6 - ok - TestResults(failed=0, attempted=3) - - >>> # Restore sys.argv - >>> sys.argv = old_argv - -In the remaining examples, the test runner's verbosity will be -explicitly set, to ensure that the test behavior is consistent. - """ - def exceptions(): r""" -Tests of `DocTestRunner`'s exception handling. - -An expected exception is specified with a traceback message. The -lines between the first line and the type/value may be omitted or -replaced with any other string: - - >>> def f(x): - ... ''' - ... >>> x = 12 - ... >>> print x//0 - ... Traceback (most recent call last): - ... ZeroDivisionError: integer division or modulo by zero - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - TestResults(failed=0, attempted=2) - -An example may not generate output before it raises an exception; if -it does, then the traceback message will not be recognized as -signaling an expected exception, so the example will be reported as an -unexpected exception: - - >>> def f(x): - ... ''' - ... >>> x = 12 - ... >>> print 'pre-exception output', x//0 - ... pre-exception output - ... Traceback (most recent call last): - ... ZeroDivisionError: integer division or modulo by zero - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 4, in f - Failed example: - print 'pre-exception output', x//0 - Exception raised: - ... - ZeroDivisionError: integer division or modulo by zero - TestResults(failed=1, attempted=2) - -Exception messages may contain newlines: - - >>> def f(x): - ... r''' - ... >>> raise ValueError, 'multi\nline\nmessage' - ... Traceback (most recent call last): - ... ValueError: multi - ... line - ... message - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - TestResults(failed=0, attempted=1) - -If an exception is expected, but an exception with the wrong type or -message is raised, then it is reported as a failure: - - >>> def f(x): - ... r''' - ... >>> raise ValueError, 'message' - ... Traceback (most recent call last): - ... ValueError: wrong message - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 3, in f - Failed example: - raise ValueError, 'message' - Expected: - Traceback (most recent call last): - ValueError: wrong message - Got: - Traceback (most recent call last): - ... - ValueError: message - TestResults(failed=1, attempted=1) - -However, IGNORE_EXCEPTION_DETAIL can be used to allow a mismatch in the -detail: - - >>> def f(x): - ... r''' - ... >>> raise ValueError, 'message' #doctest: +IGNORE_EXCEPTION_DETAIL - ... Traceback (most recent call last): - ... ValueError: wrong message - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - TestResults(failed=0, attempted=1) - -IGNORE_EXCEPTION_DETAIL also ignores difference in exception formatting -between Python versions. For example, in Python 3.x, the module path of -the exception is in the output, but this will fail under Python 2: - - >>> def f(x): - ... r''' - ... >>> from httplib import HTTPException - ... >>> raise HTTPException('message') - ... Traceback (most recent call last): - ... httplib.HTTPException: message - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 4, in f - Failed example: - raise HTTPException('message') - Expected: - Traceback (most recent call last): - httplib.HTTPException: message - Got: - Traceback (most recent call last): - ... - HTTPException: message - TestResults(failed=1, attempted=2) - -But in Python 2 the module path is not included, an therefore a test must look -like the following test to succeed in Python 2. But that test will fail under -Python 3. - - >>> def f(x): - ... r''' - ... >>> from httplib import HTTPException - ... >>> raise HTTPException('message') - ... Traceback (most recent call last): - ... HTTPException: message - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - TestResults(failed=0, attempted=2) - -However, with IGNORE_EXCEPTION_DETAIL, the module name of the exception -(if any) will be ignored: - - >>> def f(x): - ... r''' - ... >>> from httplib import HTTPException - ... >>> raise HTTPException('message') #doctest: +IGNORE_EXCEPTION_DETAIL - ... Traceback (most recent call last): - ... HTTPException: message - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - TestResults(failed=0, attempted=2) - -The module path will be completely ignored, so two different module paths will -still pass if IGNORE_EXCEPTION_DETAIL is given. This is intentional, so it can -be used when exceptions have changed module. - - >>> def f(x): - ... r''' - ... >>> from httplib import HTTPException - ... >>> raise HTTPException('message') #doctest: +IGNORE_EXCEPTION_DETAIL - ... Traceback (most recent call last): - ... foo.bar.HTTPException: message - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - TestResults(failed=0, attempted=2) - -But IGNORE_EXCEPTION_DETAIL does not allow a mismatch in the exception type: - - >>> def f(x): - ... r''' - ... >>> raise ValueError, 'message' #doctest: +IGNORE_EXCEPTION_DETAIL - ... Traceback (most recent call last): - ... TypeError: wrong type - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 3, in f - Failed example: - raise ValueError, 'message' #doctest: +IGNORE_EXCEPTION_DETAIL - Expected: - Traceback (most recent call last): - TypeError: wrong type - Got: - Traceback (most recent call last): - ... - ValueError: message - TestResults(failed=1, attempted=1) - -If an exception is raised but not expected, then it is reported as an -unexpected exception: - - >>> def f(x): - ... r''' - ... >>> 1//0 - ... 0 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 3, in f - Failed example: - 1//0 - Exception raised: - Traceback (most recent call last): - ... - ZeroDivisionError: integer division or modulo by zero - TestResults(failed=1, attempted=1) -""" - def displayhook(): r""" -Test that changing sys.displayhook doesn't matter for doctest. - - >>> import sys - >>> orig_displayhook = sys.displayhook - >>> def my_displayhook(x): - ... print('hi!') - >>> sys.displayhook = my_displayhook - >>> def f(): - ... ''' - ... >>> 3 - ... 3 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> r = doctest.DocTestRunner(verbose=False).run(test) - >>> post_displayhook = sys.displayhook - - We need to restore sys.displayhook now, so that we'll be able to test - results. - - >>> sys.displayhook = orig_displayhook - - Ok, now we can check that everything is ok. - - >>> r - TestResults(failed=0, attempted=1) - >>> post_displayhook is my_displayhook - True -""" - def optionflags(): r""" -Tests of `DocTestRunner`'s option flag handling. - -Several option flags can be used to customize the behavior of the test -runner. These are defined as module constants in doctest, and passed -to the DocTestRunner constructor (multiple constants should be ORed -together). - -The DONT_ACCEPT_TRUE_FOR_1 flag disables matches between True/False -and 1/0: - - >>> def f(x): - ... '>>> True\n1\n' - - >>> # Without the flag: - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - TestResults(failed=0, attempted=1) - - >>> # With the flag: - >>> test = doctest.DocTestFinder().find(f)[0] - >>> flags = doctest.DONT_ACCEPT_TRUE_FOR_1 - >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - True - Expected: - 1 - Got: - True - TestResults(failed=1, attempted=1) - -The DONT_ACCEPT_BLANKLINE flag disables the match between blank lines -and the '' marker: - - >>> def f(x): - ... '>>> print "a\\n\\nb"\na\n\nb\n' - - >>> # Without the flag: - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - TestResults(failed=0, attempted=1) - - >>> # With the flag: - >>> test = doctest.DocTestFinder().find(f)[0] - >>> flags = doctest.DONT_ACCEPT_BLANKLINE - >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - print "a\n\nb" - Expected: - a - - b - Got: - a - - b - TestResults(failed=1, attempted=1) - -The NORMALIZE_WHITESPACE flag causes all sequences of whitespace to be -treated as equal: - - >>> def f(x): - ... '>>> print 1, 2, 3\n 1 2\n 3' - - >>> # Without the flag: - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - print 1, 2, 3 - Expected: - 1 2 - 3 - Got: - 1 2 3 - TestResults(failed=1, attempted=1) - - >>> # With the flag: - >>> test = doctest.DocTestFinder().find(f)[0] - >>> flags = doctest.NORMALIZE_WHITESPACE - >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) - TestResults(failed=0, attempted=1) - - An example from the docs: - >>> print range(20) #doctest: +NORMALIZE_WHITESPACE - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] - -The ELLIPSIS flag causes ellipsis marker ("...") in the expected -output to match any substring in the actual output: - - >>> def f(x): - ... '>>> print range(15)\n[0, 1, 2, ..., 14]\n' - - >>> # Without the flag: - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - print range(15) - Expected: - [0, 1, 2, ..., 14] - Got: - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] - TestResults(failed=1, attempted=1) - - >>> # With the flag: - >>> test = doctest.DocTestFinder().find(f)[0] - >>> flags = doctest.ELLIPSIS - >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) - TestResults(failed=0, attempted=1) - - ... also matches nothing: - - >>> for i in range(100): - ... print i**2, #doctest: +ELLIPSIS - 0 1...4...9 16 ... 36 49 64 ... 9801 - - ... can be surprising; e.g., this test passes: - - >>> for i in range(21): #doctest: +ELLIPSIS - ... print i, - 0 1 2 ...1...2...0 - - Examples from the docs: - - >>> print range(20) # doctest:+ELLIPSIS - [0, 1, ..., 18, 19] - - >>> print range(20) # doctest: +ELLIPSIS - ... # doctest: +NORMALIZE_WHITESPACE - [0, 1, ..., 18, 19] - -The SKIP flag causes an example to be skipped entirely. I.e., the -example is not run. It can be useful in contexts where doctest -examples serve as both documentation and test cases, and an example -should be included for documentation purposes, but should not be -checked (e.g., because its output is random, or depends on resources -which would be unavailable.) The SKIP flag can also be used for -'commenting out' broken examples. - - >>> import unavailable_resource # doctest: +SKIP - >>> unavailable_resource.do_something() # doctest: +SKIP - >>> unavailable_resource.blow_up() # doctest: +SKIP - Traceback (most recent call last): - ... - UncheckedBlowUpError: Nobody checks me. - - >>> import random - >>> print random.random() # doctest: +SKIP - 0.721216923889 - -The REPORT_UDIFF flag causes failures that involve multi-line expected -and actual outputs to be displayed using a unified diff: - - >>> def f(x): - ... r''' - ... >>> print '\n'.join('abcdefg') - ... a - ... B - ... c - ... d - ... f - ... g - ... h - ... ''' - -The REPORT_NDIFF flag causes failures to use the difflib.Differ algorithm -used by the popular ndiff.py utility. This does intraline difference -marking, as well as interline differences. - - >>> def f(x): - ... r''' - ... >>> print "a b c d e f g h i j k l m" - ... a b c d e f g h i j k 1 m - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> flags = doctest.REPORT_NDIFF - >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 3, in f - Failed example: - print "a b c d e f g h i j k l m" - Differences (ndiff with -expected +actual): - - a b c d e f g h i j k 1 m - ? ^ - + a b c d e f g h i j k l m - ? + ++ ^ - TestResults(failed=1, attempted=1) - -The REPORT_ONLY_FIRST_FAILURE suppresses result output after the first -failing example: - - >>> def f(x): - ... r''' - ... >>> print 1 # first success - ... 1 - ... >>> print 2 # first failure - ... 200 - ... >>> print 3 # second failure - ... 300 - ... >>> print 4 # second success - ... 4 - ... >>> print 5 # third failure - ... 500 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> flags = doctest.REPORT_ONLY_FIRST_FAILURE - >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 5, in f - Failed example: - print 2 # first failure - Expected: - 200 - Got: - 2 - TestResults(failed=3, attempted=5) - -However, output from `report_start` is not suppressed: - - >>> doctest.DocTestRunner(verbose=True, optionflags=flags).run(test) - ... # doctest: +ELLIPSIS - Trying: - print 1 # first success - Expecting: - 1 - ok - Trying: - print 2 # first failure - Expecting: - 200 - ********************************************************************** - File ..., line 5, in f - Failed example: - print 2 # first failure - Expected: - 200 - Got: - 2 - TestResults(failed=3, attempted=5) - -For the purposes of REPORT_ONLY_FIRST_FAILURE, unexpected exceptions -count as failures: - - >>> def f(x): - ... r''' - ... >>> print 1 # first success - ... 1 - ... >>> raise ValueError(2) # first failure - ... 200 - ... >>> print 3 # second failure - ... 300 - ... >>> print 4 # second success - ... 4 - ... >>> print 5 # third failure - ... 500 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> flags = doctest.REPORT_ONLY_FIRST_FAILURE - >>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 5, in f - Failed example: - raise ValueError(2) # first failure - Exception raised: - ... - ValueError: 2 - TestResults(failed=3, attempted=5) - -New option flags can also be registered, via register_optionflag(). Here -we reach into doctest's internals a bit. - - >>> unlikely = "UNLIKELY_OPTION_NAME" - >>> unlikely in doctest.OPTIONFLAGS_BY_NAME - False - >>> new_flag_value = doctest.register_optionflag(unlikely) - >>> unlikely in doctest.OPTIONFLAGS_BY_NAME - True - -Before 2.4.4/2.5, registering a name more than once erroneously created -more than one flag value. Here we verify that's fixed: - - >>> redundant_flag_value = doctest.register_optionflag(unlikely) - >>> redundant_flag_value == new_flag_value - True - -Clean up. - >>> del doctest.OPTIONFLAGS_BY_NAME[unlikely] - - """ - - def option_directives(): r""" -Tests of `DocTestRunner`'s option directive mechanism. - -Option directives can be used to turn option flags on or off for a -single example. To turn an option on for an example, follow that -example with a comment of the form ``# doctest: +OPTION``: - - >>> def f(x): r''' - ... >>> print range(10) # should fail: no ellipsis - ... [0, 1, ..., 9] - ... - ... >>> print range(10) # doctest: +ELLIPSIS - ... [0, 1, ..., 9] - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - print range(10) # should fail: no ellipsis - Expected: - [0, 1, ..., 9] - Got: - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - TestResults(failed=1, attempted=2) - -To turn an option off for an example, follow that example with a -comment of the form ``# doctest: -OPTION``: - - >>> def f(x): r''' - ... >>> print range(10) - ... [0, 1, ..., 9] - ... - ... >>> # should fail: no ellipsis - ... >>> print range(10) # doctest: -ELLIPSIS - ... [0, 1, ..., 9] - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False, - ... optionflags=doctest.ELLIPSIS).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 6, in f - Failed example: - print range(10) # doctest: -ELLIPSIS - Expected: - [0, 1, ..., 9] - Got: - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - TestResults(failed=1, attempted=2) - -Option directives affect only the example that they appear with; they -do not change the options for surrounding examples: - - >>> def f(x): r''' - ... >>> print range(10) # Should fail: no ellipsis - ... [0, 1, ..., 9] - ... - ... >>> print range(10) # doctest: +ELLIPSIS - ... [0, 1, ..., 9] - ... - ... >>> print range(10) # Should fail: no ellipsis - ... [0, 1, ..., 9] - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - print range(10) # Should fail: no ellipsis - Expected: - [0, 1, ..., 9] - Got: - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - ********************************************************************** - File ..., line 8, in f - Failed example: - print range(10) # Should fail: no ellipsis - Expected: - [0, 1, ..., 9] - Got: - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - TestResults(failed=2, attempted=3) - -Multiple options may be modified by a single option directive. They -may be separated by whitespace, commas, or both: - - >>> def f(x): r''' - ... >>> print range(10) # Should fail - ... [0, 1, ..., 9] - ... >>> print range(10) # Should succeed - ... ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE - ... [0, 1, ..., 9] - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - print range(10) # Should fail - Expected: - [0, 1, ..., 9] - Got: - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - TestResults(failed=1, attempted=2) - - >>> def f(x): r''' - ... >>> print range(10) # Should fail - ... [0, 1, ..., 9] - ... >>> print range(10) # Should succeed - ... ... # doctest: +ELLIPSIS,+NORMALIZE_WHITESPACE - ... [0, 1, ..., 9] - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - print range(10) # Should fail - Expected: - [0, 1, ..., 9] - Got: - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - TestResults(failed=1, attempted=2) - - >>> def f(x): r''' - ... >>> print range(10) # Should fail - ... [0, 1, ..., 9] - ... >>> print range(10) # Should succeed - ... ... # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE - ... [0, 1, ..., 9] - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - ... # doctest: +ELLIPSIS - ********************************************************************** - File ..., line 2, in f - Failed example: - print range(10) # Should fail - Expected: - [0, 1, ..., 9] - Got: - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - TestResults(failed=1, attempted=2) - -The option directive may be put on the line following the source, as -long as a continuation prompt is used: - - >>> def f(x): r''' - ... >>> print range(10) - ... ... # doctest: +ELLIPSIS - ... [0, 1, ..., 9] - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - TestResults(failed=0, attempted=1) - -For examples with multi-line source, the option directive may appear -at the end of any line: - - >>> def f(x): r''' - ... >>> for x in range(10): # doctest: +ELLIPSIS - ... ... print x, - ... 0 1 2 ... 9 - ... - ... >>> for x in range(10): - ... ... print x, # doctest: +ELLIPSIS - ... 0 1 2 ... 9 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - TestResults(failed=0, attempted=2) - -If more than one line of an example with multi-line source has an -option directive, then they are combined: - - >>> def f(x): r''' - ... Should fail (option directive not on the last line): - ... >>> for x in range(10): # doctest: +ELLIPSIS - ... ... print x, # doctest: +NORMALIZE_WHITESPACE - ... 0 1 2...9 - ... ''' - >>> test = doctest.DocTestFinder().find(f)[0] - >>> doctest.DocTestRunner(verbose=False).run(test) - TestResults(failed=0, attempted=1) - -It is an error to have a comment of the form ``# doctest:`` that is -*not* followed by words of the form ``+OPTION`` or ``-OPTION``, where -``OPTION`` is an option that has been registered with -`register_option`: - - >>> # Error: Option not registered - >>> s = '>>> print 12 #doctest: +BADOPTION' - >>> test = doctest.DocTestParser().get_doctest(s, {}, 's', 's.py', 0) - Traceback (most recent call last): - ValueError: line 1 of the doctest for s has an invalid option: '+BADOPTION' - - >>> # Error: No + or - prefix - >>> s = '>>> print 12 #doctest: ELLIPSIS' - >>> test = doctest.DocTestParser().get_doctest(s, {}, 's', 's.py', 0) - Traceback (most recent call last): - ValueError: line 1 of the doctest for s has an invalid option: 'ELLIPSIS' - -It is an error to use an option directive on a line that contains no -source: - - >>> s = '>>> # doctest: +ELLIPSIS' - >>> test = doctest.DocTestParser().get_doctest(s, {}, 's', 's.py', 0) - Traceback (most recent call last): - ValueError: line 0 of the doctest for s has an option directive on a line with no example: '# doctest: +ELLIPSIS' - - """ - - def test_unicode_output(self): r""" - -Check that unicode output works: - - >>> u'\xe9' - u'\xe9' - -If we return unicode, SpoofOut's buf variable becomes automagically -converted to unicode. This means all subsequent output becomes converted -to unicode, and if the output contains non-ascii characters that failed. -It used to be that this state change carried on between tests, meaning -tests would fail if unicode has been output previously in the testrun. -This test tests that this is no longer so: - - >>> print u'abc' - abc - -And then return a string with non-ascii characters: - - >>> print u'\xe9'.encode('utf-8') - ? - - """ - - -def test_testsource(): r""" -Unit tests for `testsource()`. - -The testsource() function takes a module and a name, finds the (first) -test with that name in that module, and converts it to a script. The -example code is converted to regular Python code. The surrounding -words and expected output are converted to comments: - - >>> import test.test_doctest - >>> name = 'test.test_doctest.sample_func' - >>> print doctest.testsource(test.test_doctest, name) - # Blah blah - # - print sample_func(22) - # Expected: - ## 44 - # - # Yee ha! - - - >>> name = 'test.test_doctest.SampleNewStyleClass' - >>> print doctest.testsource(test.test_doctest, name) - print '1\n2\n3' - # Expected: - ## 1 - ## 2 - ## 3 - - - >>> name = 'test.test_doctest.SampleClass.a_classmethod' - >>> print doctest.testsource(test.test_doctest, name) - print SampleClass.a_classmethod(10) - # Expected: - ## 12 - print SampleClass(0).a_classmethod(10) - # Expected: - ## 12 - -""" - -def test_DocTestSuite(): - """DocTestSuite creates a unittest test suite from a doctest. - - We create a Suite by providing a module. A module can be provided - by passing a module object: - - >>> import unittest - >>> import test.sample_doctest - >>> suite = doctest.DocTestSuite(test.sample_doctest) - >>> suite.run(unittest.TestResult()) - - - We can also supply the module by name: - - >>> suite = doctest.DocTestSuite('test.sample_doctest') - >>> suite.run(unittest.TestResult()) - - - We can use the current module: - - >>> suite = test.sample_doctest.test_suite() - >>> suite.run(unittest.TestResult()) - - - We can supply global variables. If we pass globs, they will be - used instead of the module globals. Here we'll pass an empty - globals, triggering an extra error: - - >>> suite = doctest.DocTestSuite('test.sample_doctest', globs={}) - >>> suite.run(unittest.TestResult()) - - - Alternatively, we can provide extra globals. Here we'll make an - error go away by providing an extra global variable: - - >>> suite = doctest.DocTestSuite('test.sample_doctest', - ... extraglobs={'y': 1}) - >>> suite.run(unittest.TestResult()) - - - You can pass option flags. Here we'll cause an extra error - by disabling the blank-line feature: - - >>> suite = doctest.DocTestSuite('test.sample_doctest', - ... optionflags=doctest.DONT_ACCEPT_BLANKLINE) - >>> suite.run(unittest.TestResult()) - - - You can supply setUp and tearDown functions: - - >>> def setUp(t): - ... import test.test_doctest - ... test.test_doctest.sillySetup = True - - >>> def tearDown(t): - ... import test.test_doctest - ... del test.test_doctest.sillySetup - - Here, we installed a silly variable that the test expects: - - >>> suite = doctest.DocTestSuite('test.sample_doctest', - ... setUp=setUp, tearDown=tearDown) - >>> suite.run(unittest.TestResult()) - - - But the tearDown restores sanity: - - >>> import test.test_doctest - >>> test.test_doctest.sillySetup - Traceback (most recent call last): - ... - AttributeError: 'module' object has no attribute 'sillySetup' - - The setUp and tearDown funtions are passed test objects. Here - we'll use the setUp function to supply the missing variable y: - - >>> def setUp(test): - ... test.globs['y'] = 1 - - >>> suite = doctest.DocTestSuite('test.sample_doctest', setUp=setUp) - >>> suite.run(unittest.TestResult()) - - - Here, we didn't need to use a tearDown function because we - modified the test globals, which are a copy of the - sample_doctest module dictionary. The test globals are - automatically cleared for us after a test. - """ - -def test_DocFileSuite(): - """We can test tests found in text files using a DocFileSuite. - - We create a suite by providing the names of one or more text - files that include examples: - - >>> import unittest - >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... 'test_doctest2.txt', - ... 'test_doctest4.txt') - >>> suite.run(unittest.TestResult()) - - - The test files are looked for in the directory containing the - calling module. A package keyword argument can be provided to - specify a different relative location. - - >>> import unittest - >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... 'test_doctest2.txt', - ... 'test_doctest4.txt', - ... package='test') - >>> suite.run(unittest.TestResult()) - - - Support for using a package's __loader__.get_data() is also - provided. - - >>> import unittest, pkgutil, test - >>> added_loader = False - >>> if not hasattr(test, '__loader__'): - ... test.__loader__ = pkgutil.get_loader(test) - ... added_loader = True - >>> try: - ... suite = doctest.DocFileSuite('test_doctest.txt', - ... 'test_doctest2.txt', - ... 'test_doctest4.txt', - ... package='test') - ... suite.run(unittest.TestResult()) - ... finally: - ... if added_loader: - ... del test.__loader__ - - - '/' should be used as a path separator. It will be converted - to a native separator at run time: - - >>> suite = doctest.DocFileSuite('../test/test_doctest.txt') - >>> suite.run(unittest.TestResult()) - - - If DocFileSuite is used from an interactive session, then files - are resolved relative to the directory of sys.argv[0]: - - >>> import types, os.path, test.test_doctest - >>> save_argv = sys.argv - >>> sys.argv = [test.test_doctest.__file__] - >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... package=types.ModuleType('__main__')) - >>> sys.argv = save_argv - - By setting `module_relative=False`, os-specific paths may be - used (including absolute paths and paths relative to the - working directory): - - >>> # Get the absolute path of the test package. - >>> test_doctest_path = os.path.abspath(test.test_doctest.__file__) - >>> test_pkg_path = os.path.split(test_doctest_path)[0] - - >>> # Use it to find the absolute path of test_doctest.txt. - >>> test_file = os.path.join(test_pkg_path, 'test_doctest.txt') - - >>> suite = doctest.DocFileSuite(test_file, module_relative=False) - >>> suite.run(unittest.TestResult()) - - - It is an error to specify `package` when `module_relative=False`: - - >>> suite = doctest.DocFileSuite(test_file, module_relative=False, - ... package='test') - Traceback (most recent call last): - ValueError: Package may only be specified for module-relative paths. - - You can specify initial global variables: - - >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... 'test_doctest2.txt', - ... 'test_doctest4.txt', - ... globs={'favorite_color': 'blue'}) - >>> suite.run(unittest.TestResult()) - - - In this case, we supplied a missing favorite color. You can - provide doctest options: - - >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... 'test_doctest2.txt', - ... 'test_doctest4.txt', - ... optionflags=doctest.DONT_ACCEPT_BLANKLINE, - ... globs={'favorite_color': 'blue'}) - >>> suite.run(unittest.TestResult()) - - - And, you can provide setUp and tearDown functions: - - >>> def setUp(t): - ... import test.test_doctest - ... test.test_doctest.sillySetup = True - - >>> def tearDown(t): - ... import test.test_doctest - ... del test.test_doctest.sillySetup - - Here, we installed a silly variable that the test expects: - - >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... 'test_doctest2.txt', - ... 'test_doctest4.txt', - ... setUp=setUp, tearDown=tearDown) - >>> suite.run(unittest.TestResult()) - - - But the tearDown restores sanity: - - >>> import test.test_doctest - >>> test.test_doctest.sillySetup - Traceback (most recent call last): - ... - AttributeError: 'module' object has no attribute 'sillySetup' - - The setUp and tearDown funtions are passed test objects. - Here, we'll use a setUp function to set the favorite color in - test_doctest.txt: - - >>> def setUp(test): - ... test.globs['favorite_color'] = 'blue' - - >>> suite = doctest.DocFileSuite('test_doctest.txt', setUp=setUp) - >>> suite.run(unittest.TestResult()) - - - Here, we didn't need to use a tearDown function because we - modified the test globals. The test globals are - automatically cleared for us after a test. - - Tests in a file run using `DocFileSuite` can also access the - `__file__` global, which is set to the name of the file - containing the tests: - - >>> suite = doctest.DocFileSuite('test_doctest3.txt') - >>> suite.run(unittest.TestResult()) - - - If the tests contain non-ASCII characters, we have to specify which - encoding the file is encoded with. We do so by using the `encoding` - parameter: - - >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... 'test_doctest2.txt', - ... 'test_doctest4.txt', - ... encoding='utf-8') - >>> suite.run(unittest.TestResult()) - - - """ - -def test_trailing_space_in_test(): - """ - Trailing spaces in expected output are significant: - - >>> x, y = 'foo', '' - >>> print x, y - foo \n - """ - - -def test_unittest_reportflags(): - """Default unittest reporting flags can be set to control reporting - - Here, we'll set the REPORT_ONLY_FIRST_FAILURE option so we see - only the first failure of each test. First, we'll look at the - output without the flag. The file test_doctest.txt file has two - tests. They both fail if blank lines are disabled: - - >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... optionflags=doctest.DONT_ACCEPT_BLANKLINE) - >>> import unittest - >>> result = suite.run(unittest.TestResult()) - >>> print result.failures[0][1] # doctest: +ELLIPSIS - Traceback ... - Failed example: - favorite_color - ... - Failed example: - if 1: - ... - - Note that we see both failures displayed. - - >>> old = doctest.set_unittest_reportflags( - ... doctest.REPORT_ONLY_FIRST_FAILURE) - - Now, when we run the test: - - >>> result = suite.run(unittest.TestResult()) - >>> print result.failures[0][1] # doctest: +ELLIPSIS - Traceback ... - Failed example: - favorite_color - Exception raised: - ... - NameError: name 'favorite_color' is not defined - - - - We get only the first failure. - - If we give any reporting options when we set up the tests, - however: - - >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... optionflags=doctest.DONT_ACCEPT_BLANKLINE | doctest.REPORT_NDIFF) - - Then the default eporting options are ignored: - - >>> result = suite.run(unittest.TestResult()) - >>> print result.failures[0][1] # doctest: +ELLIPSIS - Traceback ... - Failed example: - favorite_color - ... - Failed example: - if 1: - print 'a' - print - print 'b' - Differences (ndiff with -expected +actual): - a - - - + - b - - - - - Test runners can restore the formatting flags after they run: - - >>> ignored = doctest.set_unittest_reportflags(old) - - """ - -def test_testfile(): r""" -Tests for the `testfile()` function. This function runs all the -doctest examples in a given file. In its simple invokation, it is -called with the name of a file, which is taken to be relative to the -calling module. The return value is (#failures, #tests). - -We don't want `-v` in sys.argv for these tests. - - >>> save_argv = sys.argv - >>> if '-v' in sys.argv: - ... sys.argv = [arg for arg in save_argv if arg != '-v'] - - - >>> doctest.testfile('test_doctest.txt') # doctest: +ELLIPSIS - ********************************************************************** - File "...", line 6, in test_doctest.txt - Failed example: - favorite_color - Exception raised: - ... - NameError: name 'favorite_color' is not defined - ********************************************************************** - 1 items had failures: - 1 of 2 in test_doctest.txt - ***Test Failed*** 1 failures. - TestResults(failed=1, attempted=2) - >>> doctest.master = None # Reset master. - -(Note: we'll be clearing doctest.master after each call to -`doctest.testfile`, to suppress warnings about multiple tests with the -same name.) - -Globals may be specified with the `globs` and `extraglobs` parameters: - - >>> globs = {'favorite_color': 'blue'} - >>> doctest.testfile('test_doctest.txt', globs=globs) - TestResults(failed=0, attempted=2) - >>> doctest.master = None # Reset master. - - >>> extraglobs = {'favorite_color': 'red'} - >>> doctest.testfile('test_doctest.txt', globs=globs, - ... extraglobs=extraglobs) # doctest: +ELLIPSIS - ********************************************************************** - File "...", line 6, in test_doctest.txt - Failed example: - favorite_color - Expected: - 'blue' - Got: - 'red' - ********************************************************************** - 1 items had failures: - 1 of 2 in test_doctest.txt - ***Test Failed*** 1 failures. - TestResults(failed=1, attempted=2) - >>> doctest.master = None # Reset master. - -The file may be made relative to a given module or package, using the -optional `module_relative` parameter: - - >>> doctest.testfile('test_doctest.txt', globs=globs, - ... module_relative='test') - TestResults(failed=0, attempted=2) - >>> doctest.master = None # Reset master. - -Verbosity can be increased with the optional `verbose` parameter: - - >>> doctest.testfile('test_doctest.txt', globs=globs, verbose=True) - Trying: - favorite_color - Expecting: - 'blue' - ok - Trying: - if 1: - print 'a' - print - print 'b' - Expecting: - a - - b - ok - 1 items passed all tests: - 2 tests in test_doctest.txt - 2 tests in 1 items. - 2 passed and 0 failed. - Test passed. - TestResults(failed=0, attempted=2) - >>> doctest.master = None # Reset master. - -The name of the test may be specified with the optional `name` -parameter: - - >>> doctest.testfile('test_doctest.txt', name='newname') - ... # doctest: +ELLIPSIS - ********************************************************************** - File "...", line 6, in newname - ... - TestResults(failed=1, attempted=2) - >>> doctest.master = None # Reset master. - -The summary report may be suppressed with the optional `report` -parameter: - - >>> doctest.testfile('test_doctest.txt', report=False) - ... # doctest: +ELLIPSIS - ********************************************************************** - File "...", line 6, in test_doctest.txt - Failed example: - favorite_color - Exception raised: - ... - NameError: name 'favorite_color' is not defined - TestResults(failed=1, attempted=2) - >>> doctest.master = None # Reset master. - -The optional keyword argument `raise_on_error` can be used to raise an -exception on the first error (which may be useful for postmortem -debugging): - - >>> doctest.testfile('test_doctest.txt', raise_on_error=True) - ... # doctest: +ELLIPSIS - Traceback (most recent call last): - UnexpectedException: ... - >>> doctest.master = None # Reset master. - -If the tests contain non-ASCII characters, the tests might fail, since -it's unknown which encoding is used. The encoding can be specified -using the optional keyword argument `encoding`: - - >>> doctest.testfile('test_doctest4.txt') # doctest: +ELLIPSIS - ********************************************************************** - File "...", line 7, in test_doctest4.txt - Failed example: - u'...' - Expected: - u'f\xf6\xf6' - Got: - u'f\xc3\xb6\xc3\xb6' - ********************************************************************** - ... - ********************************************************************** - 1 items had failures: - 2 of 4 in test_doctest4.txt - ***Test Failed*** 2 failures. - TestResults(failed=2, attempted=4) - >>> doctest.master = None # Reset master. - - >>> doctest.testfile('test_doctest4.txt', encoding='utf-8') - TestResults(failed=0, attempted=4) - >>> doctest.master = None # Reset master. - -Switch the module encoding to 'utf-8' to test the verbose output without -bothering with the current sys.stdout encoding. - - >>> doctest._encoding, saved_encoding = 'utf-8', doctest._encoding - >>> doctest.testfile('test_doctest4.txt', encoding='utf-8', verbose=True) - Trying: - u'f??' - Expecting: - u'f\xf6\xf6' - ok - Trying: - u'b?r' - Expecting: - u'b\u0105r' - ok - Trying: - 'f??' - Expecting: - 'f\xc3\xb6\xc3\xb6' - ok - Trying: - 'b?r' - Expecting: - 'b\xc4\x85r' - ok - 1 items passed all tests: - 4 tests in test_doctest4.txt - 4 tests in 1 items. - 4 passed and 0 failed. - Test passed. - TestResults(failed=0, attempted=4) - >>> doctest._encoding = saved_encoding - >>> doctest.master = None # Reset master. - >>> sys.argv = save_argv -""" - -# old_test1, ... used to live in doctest.py, but cluttered it. Note -# that these use the deprecated doctest.Tester, so should go away (or -# be rewritten) someday. - -def old_test1(): r""" ->>> from doctest import Tester ->>> t = Tester(globs={'x': 42}, verbose=0) ->>> t.runstring(r''' -... >>> x = x * 2 -... >>> print x -... 42 -... ''', 'XYZ') -********************************************************************** -Line 3, in XYZ -Failed example: - print x -Expected: - 42 -Got: - 84 -TestResults(failed=1, attempted=2) ->>> t.runstring(">>> x = x * 2\n>>> print x\n84\n", 'example2') -TestResults(failed=0, attempted=2) ->>> t.summarize() -********************************************************************** -1 items had failures: - 1 of 2 in XYZ -***Test Failed*** 1 failures. -TestResults(failed=1, attempted=4) ->>> t.summarize(verbose=1) -1 items passed all tests: - 2 tests in example2 -********************************************************************** -1 items had failures: - 1 of 2 in XYZ -4 tests in 2 items. -3 passed and 1 failed. -***Test Failed*** 1 failures. -TestResults(failed=1, attempted=4) -""" - -def old_test2(): r""" - >>> from doctest import Tester - >>> t = Tester(globs={}, verbose=1) - >>> test = r''' - ... # just an example - ... >>> x = 1 + 2 - ... >>> x - ... 3 - ... ''' - >>> t.runstring(test, "Example") - Running string Example - Trying: - x = 1 + 2 - Expecting nothing - ok - Trying: - x - Expecting: - 3 - ok - 0 of 2 examples failed in string Example - TestResults(failed=0, attempted=2) -""" - -def old_test3(): r""" - >>> from doctest import Tester - >>> t = Tester(globs={}, verbose=0) - >>> def _f(): - ... '''Trivial docstring example. - ... >>> assert 2 == 2 - ... ''' - ... return 32 - ... - >>> t.rundoc(_f) # expect 0 failures in 1 example - TestResults(failed=0, attempted=1) -""" - -def old_test4(): """ - >>> import types - >>> m1 = types.ModuleType('_m1') - >>> m2 = types.ModuleType('_m2') - >>> test_data = \""" - ... def _f(): - ... '''>>> assert 1 == 1 - ... ''' - ... def g(): - ... '''>>> assert 2 != 1 - ... ''' - ... class H: - ... '''>>> assert 2 > 1 - ... ''' - ... def bar(self): - ... '''>>> assert 1 < 2 - ... ''' - ... \""" - >>> exec test_data in m1.__dict__ - >>> exec test_data in m2.__dict__ - >>> m1.__dict__.update({"f2": m2._f, "g2": m2.g, "h2": m2.H}) - - Tests that objects outside m1 are excluded: - - >>> from doctest import Tester - >>> t = Tester(globs={}, verbose=0) - >>> t.rundict(m1.__dict__, "rundict_test", m1) # f2 and g2 and h2 skipped - TestResults(failed=0, attempted=4) - - Once more, not excluding stuff outside m1: - - >>> t = Tester(globs={}, verbose=0) - >>> t.rundict(m1.__dict__, "rundict_test_pvt") # None are skipped. - TestResults(failed=0, attempted=8) - - The exclusion of objects from outside the designated module is - meant to be invoked automagically by testmod. - - >>> doctest.testmod(m1, verbose=False) - TestResults(failed=0, attempted=4) -""" - -###################################################################### -## Main -###################################################################### - -def test_main(): - # Check the doctest cases in doctest itself: - test_support.run_doctest(doctest, verbosity=True) - - from test import test_doctest - - # Ignore all warnings about the use of class Tester in this module. - deprecations = [("class Tester is deprecated", DeprecationWarning)] - if sys.py3kwarning: - deprecations += [("backquote not supported", SyntaxWarning), - ("execfile.. not supported", DeprecationWarning)] - with test_support.check_warnings(*deprecations): - # Check the doctest cases defined here: - test_support.run_doctest(test_doctest, verbosity=True) - -if __name__ == '__main__': - if '-c' in sys.argv: - test_coverage('/tmp/doctest.cover') - else: - test_main() diff --git a/src/org/python/core/PyInteger.java b/src/org/python/core/PyInteger.java --- a/src/org/python/core/PyInteger.java +++ b/src/org/python/core/PyInteger.java @@ -583,7 +583,7 @@ if (value != 0) { return left.__float__().__pow__(right, modulo); } else { - throw Py.ZeroDivisionError("cannot raise 0 to a negative power"); + throw Py.ZeroDivisionError("0.0 cannot be raised to a negative power"); } } diff --git a/src/org/python/modules/math.java b/src/org/python/modules/math.java --- a/src/org/python/modules/math.java +++ b/src/org/python/modules/math.java @@ -40,6 +40,19 @@ return math_erf.erfc(v); } + public static double expm1(double v) { + if (Double.POSITIVE_INFINITY == v) { + return v; + } + + double result = Math.expm1(v); + if (Double.isInfinite(result)) { + throw Py.OverflowError(Double.toString(v)); + } + + return result; + } + public static double acos(double v) { if (isinf(v)) { throwMathDomainValueError(); @@ -86,7 +99,29 @@ if (isnan(v) || isinf(v)) { return v; } - return log(v + sqrt(v * v + 1)); + + final double ln2 = 6.93147180559945286227e-01; + final double large = 1 << 28; + final double small = 1.0 / (1 << 28); + boolean sign = false; + + if (v < 0) { + v = -v; + sign = true; + } + + double temp; + if (v > large) { + temp = log(v) + ln2; + } else if (v > 2) { + temp = log(2*v + 1/(sqrt(v*v+1)+v)); + } else if (v < small) { + temp = v; + } else { + temp = log1p(v + v*v/(1+sqrt(1+v*v))); + } + + return sign ? -temp : temp; } public static double atan(double v) { -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Thu May 3 23:24:55 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Thu, 03 May 2012 23:24:55 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Fix_for_test=5Fexceptions=23?= =?utf8?q?test=5Fnew=5Freturns=5Finvalid=5Finstance=2E?= Message-ID: http://hg.python.org/jython/rev/8821780eb20c changeset: 6645:8821780eb20c user: Frank Wierzbicki date: Thu May 03 13:22:27 2012 -0700 summary: Fix for test_exceptions#test_new_returns_invalid_instance. files: src/org/python/core/PyException.java | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-) diff --git a/src/org/python/core/PyException.java b/src/org/python/core/PyException.java --- a/src/org/python/core/PyException.java +++ b/src/org/python/core/PyException.java @@ -197,6 +197,11 @@ if (isExceptionClass(type)) { PyException pye = new PyException(type, value, (PyTraceback)traceback); pye.normalize(); + if (!isExceptionInstance(pye.value)) { + throw Py.TypeError(String.format( + "calling %s() should have returned an instance of BaseException, not '%s'", + pye.type, pye.value)); + } return pye; } else if (isExceptionInstance(type)) { // Raising an instance. The value should be a dummy. -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Thu May 3 23:24:55 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Thu, 03 May 2012 23:24:55 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_from=3A?= Message-ID: http://hg.python.org/jython/rev/a73742233a08 changeset: 6646:a73742233a08 user: Frank Wierzbicki date: Thu May 03 14:21:01 2012 -0700 summary: from: http://hg.python.org/cpython/Lib/test/test_exceptions.py at 22db03646d9b files: lib-python/2.7/test/test_exceptions.py | 0 1 files changed, 0 insertions(+), 0 deletions(-) diff --git a/lib-python/2.7/test/test_exceptions.py b/Lib/test/test_exceptions.py copy from lib-python/2.7/test/test_exceptions.py copy to Lib/test/test_exceptions.py -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Thu May 3 23:24:55 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Thu, 03 May 2012 23:24:55 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Add_some_skips_to_test=5Fexc?= =?utf8?q?eptions=2E?= Message-ID: http://hg.python.org/jython/rev/17caa65fabac changeset: 6647:17caa65fabac user: Frank Wierzbicki date: Thu May 03 14:24:47 2012 -0700 summary: Add some skips to test_exceptions. files: Lib/test/test_exceptions.py | 7 ++++++- 1 files changed, 6 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -6,7 +6,7 @@ import pickle, cPickle from test.test_support import (TESTFN, unlink, run_unittest, captured_output, - check_warnings, cpython_only) + check_warnings, cpython_only, is_jython) from test.test_pep352 import ignore_deprecation_warnings # XXX This is not really enough, each *operation* should be tested! @@ -473,6 +473,7 @@ with self.assertRaises(TypeError): raise MyException + @unittest.skipIf(is_jython, "FIXME: not working in Jython") def test_assert_with_tuple_arg(self): try: assert False, (3,) @@ -522,6 +523,8 @@ # empty string self.check_same_msg(Exception(), '') + + @unittest.skipIf(is_jython, "FIXME: not working in Jython") def test_0_args_with_overridden___str__(self): """Check same msg for exceptions with 0 args and overridden __str__""" # str() and unicode() on an exception with overridden __str__ that @@ -547,6 +550,7 @@ self.assertRaises(UnicodeEncodeError, str, e) self.assertEqual(unicode(e), u'f\xf6\xf6') + @unittest.skipIf(is_jython, "FIXME: not working in Jython") def test_1_arg_with_overridden___str__(self): """Check same msg for exceptions with overridden __str__ and 1 arg""" # when __str__ is overridden and __unicode__ is not implemented @@ -571,6 +575,7 @@ for args in argslist: self.check_same_msg(Exception(*args), repr(args)) + @unittest.skipIf(is_jython, "FIXME: not working in Jython") def test_many_args_with_overridden___str__(self): """Check same msg for exceptions with overridden __str__ and many args""" # if __str__ returns an ascii string / ascii unicode string -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sat May 5 17:14:34 2012 From: jython-checkins at python.org (alan.kennedy) Date: Sat, 05 May 2012 17:14:34 +0200 Subject: [Jython-checkins] =?utf8?q?jython_=282=2E5=29=3A_Implementing_OS_?= =?utf8?q?recognition_for_OS/400=3A_Partially_addresses_=231842?= Message-ID: http://hg.python.org/jython/rev/c62b1a8fa99b changeset: 6648:c62b1a8fa99b branch: 2.5 parent: 6613:61758cb384eb user: Alan Kennedy date: Sat May 05 16:12:46 2012 +0100 summary: Implementing OS recognition for OS/400: Partially addresses #1842 files: Lib/os.py | 14 ++++++++++++++ src/org/python/modules/posix/OS.java | 2 ++ 2 files changed, 16 insertions(+), 0 deletions(-) diff --git a/Lib/os.py b/Lib/os.py --- a/Lib/os.py +++ b/Lib/os.py @@ -114,6 +114,20 @@ __all__.extend(_get_exports_list(riscos)) del riscos +elif 'ibmi' in _names: + _name = 'ibmi' + linesep = '\n' + from ibmi import * + try: + from ibmi import _exit + except ImportError: + pass + import posixpath as path + + import ibmi + __all__.extend(_get_exports_list(ibmi)) + del ibmi + else: raise ImportError, 'no os specific module found' diff --git a/src/org/python/modules/posix/OS.java b/src/org/python/modules/posix/OS.java --- a/src/org/python/modules/posix/OS.java +++ b/src/org/python/modules/posix/OS.java @@ -9,6 +9,8 @@ */ enum OS { NT("Windows", new String[] {"cmd.exe", "/c"}, new String[] {"command.com", "/c"}), + // http://bugs.jython.org/issue1842 + IBMi("OS/400", new String[] {"/QOpenSys/usr/bin/sh", "-c"}), POSIX(new String[] {"/bin/sh", "-c"}); /** An array of potential shell commands this platform may use. */ -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sat May 12 01:54:46 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Sat, 12 May 2012 01:54:46 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Fixes_=231880_Sha_224_librar?= =?utf8?q?y_not_present_in_Jython=2C_thanks_Guarav_Raje!?= Message-ID: http://hg.python.org/jython/rev/47c55317a1c9 changeset: 6649:47c55317a1c9 parent: 6647:17caa65fabac user: Gaurav Raje date: Fri May 11 16:28:23 2012 -0700 summary: Fixes #1880 Sha 224 library not present in Jython, thanks Guarav Raje! files: Lib/_sha256.py | 265 ------ Lib/test/test_hashlib.py | 10 +- NEWS | 1 + src/org/python/modules/SHA224Digest.java | 483 +++++++++++ src/org/python/modules/_hashlib.java | 7 +- 5 files changed, 492 insertions(+), 274 deletions(-) diff --git a/Lib/_sha256.py b/Lib/_sha256.py deleted file mode 100644 --- a/Lib/_sha256.py +++ /dev/null @@ -1,265 +0,0 @@ -import struct - -SHA_BLOCKSIZE = 64 -SHA_DIGESTSIZE = 32 - - -def new_shaobject(): - return { - 'digest': [0]*8, - 'count_lo': 0, - 'count_hi': 0, - 'data': [0]* SHA_BLOCKSIZE, - 'local': 0, - 'digestsize': 0 - } - -ROR = lambda x, y: (((x & 0xffffffff) >> (y & 31)) | (x << (32 - (y & 31)))) & 0xffffffff -Ch = lambda x, y, z: (z ^ (x & (y ^ z))) -Maj = lambda x, y, z: (((x | y) & z) | (x & y)) -S = lambda x, n: ROR(x, n) -R = lambda x, n: (x & 0xffffffff) >> n -Sigma0 = lambda x: (S(x, 2) ^ S(x, 13) ^ S(x, 22)) -Sigma1 = lambda x: (S(x, 6) ^ S(x, 11) ^ S(x, 25)) -Gamma0 = lambda x: (S(x, 7) ^ S(x, 18) ^ R(x, 3)) -Gamma1 = lambda x: (S(x, 17) ^ S(x, 19) ^ R(x, 10)) - -def sha_transform(sha_info): - W = [] - - d = sha_info['data'] - for i in xrange(0,16): - W.append( (d[4*i]<<24) + (d[4*i+1]<<16) + (d[4*i+2]<<8) + d[4*i+3]) - - for i in xrange(16,64): - W.append( (Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]) & 0xffffffff ) - - ss = sha_info['digest'][:] - - def RND(a,b,c,d,e,f,g,h,i,ki): - t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i]; - t1 = Sigma0(a) + Maj(a, b, c); - d += t0; - h = t0 + t1; - return d & 0xffffffff, h & 0xffffffff - - ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],0,0x428a2f98); - ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],1,0x71374491); - ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],2,0xb5c0fbcf); - ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],3,0xe9b5dba5); - ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],4,0x3956c25b); - ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],5,0x59f111f1); - ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],6,0x923f82a4); - ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],7,0xab1c5ed5); - ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],8,0xd807aa98); - ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],9,0x12835b01); - ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],10,0x243185be); - ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],11,0x550c7dc3); - ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],12,0x72be5d74); - ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],13,0x80deb1fe); - ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],14,0x9bdc06a7); - ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],15,0xc19bf174); - ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],16,0xe49b69c1); - ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],17,0xefbe4786); - ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],18,0x0fc19dc6); - ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],19,0x240ca1cc); - ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],20,0x2de92c6f); - ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],21,0x4a7484aa); - ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],22,0x5cb0a9dc); - ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],23,0x76f988da); - ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],24,0x983e5152); - ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],25,0xa831c66d); - ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],26,0xb00327c8); - ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],27,0xbf597fc7); - ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],28,0xc6e00bf3); - ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],29,0xd5a79147); - ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],30,0x06ca6351); - ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],31,0x14292967); - ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],32,0x27b70a85); - ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],33,0x2e1b2138); - ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],34,0x4d2c6dfc); - ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],35,0x53380d13); - ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],36,0x650a7354); - ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],37,0x766a0abb); - ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],38,0x81c2c92e); - ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],39,0x92722c85); - ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],40,0xa2bfe8a1); - ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],41,0xa81a664b); - ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],42,0xc24b8b70); - ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],43,0xc76c51a3); - ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],44,0xd192e819); - ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],45,0xd6990624); - ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],46,0xf40e3585); - ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],47,0x106aa070); - ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],48,0x19a4c116); - ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],49,0x1e376c08); - ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],50,0x2748774c); - ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],51,0x34b0bcb5); - ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],52,0x391c0cb3); - ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],53,0x4ed8aa4a); - ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],54,0x5b9cca4f); - ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],55,0x682e6ff3); - ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],56,0x748f82ee); - ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],57,0x78a5636f); - ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],58,0x84c87814); - ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],59,0x8cc70208); - ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],60,0x90befffa); - ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],61,0xa4506ceb); - ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],62,0xbef9a3f7); - ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],63,0xc67178f2); - - dig = [] - for i, x in enumerate(sha_info['digest']): - dig.append( (x + ss[i]) & 0xffffffff ) - sha_info['digest'] = dig - -def sha_init(): - sha_info = new_shaobject() - sha_info['digest'] = [0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19] - sha_info['count_lo'] = 0 - sha_info['count_hi'] = 0 - sha_info['local'] = 0 - sha_info['digestsize'] = 32 - return sha_info - -def sha224_init(): - sha_info = new_shaobject() - sha_info['digest'] = [0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4] - sha_info['count_lo'] = 0 - sha_info['count_hi'] = 0 - sha_info['local'] = 0 - sha_info['digestsize'] = 28 - return sha_info - -def getbuf(s): - if isinstance(s, str): - return s - elif isinstance(s, unicode): - return str(s) - else: - return buffer(s) - -def sha_update(sha_info, buffer): - count = len(buffer) - buffer_idx = 0 - clo = (sha_info['count_lo'] + (count << 3)) & 0xffffffff - if clo < sha_info['count_lo']: - sha_info['count_hi'] += 1 - sha_info['count_lo'] = clo - - sha_info['count_hi'] += (count >> 29) - - if sha_info['local']: - i = SHA_BLOCKSIZE - sha_info['local'] - if i > count: - i = count - - # copy buffer - for x in enumerate(buffer[buffer_idx:buffer_idx+i]): - sha_info['data'][sha_info['local']+x[0]] = struct.unpack('B', x[1])[0] - - count -= i - buffer_idx += i - - sha_info['local'] += i - if sha_info['local'] == SHA_BLOCKSIZE: - sha_transform(sha_info) - sha_info['local'] = 0 - else: - return - - while count >= SHA_BLOCKSIZE: - # copy buffer - sha_info['data'] = [struct.unpack('B',c)[0] for c in buffer[buffer_idx:buffer_idx + SHA_BLOCKSIZE]] - count -= SHA_BLOCKSIZE - buffer_idx += SHA_BLOCKSIZE - sha_transform(sha_info) - - - # copy buffer - pos = sha_info['local'] - sha_info['data'][pos:pos+count] = [struct.unpack('B',c)[0] for c in buffer[buffer_idx:buffer_idx + count]] - sha_info['local'] = count - -def sha_final(sha_info): - lo_bit_count = sha_info['count_lo'] - hi_bit_count = sha_info['count_hi'] - count = (lo_bit_count >> 3) & 0x3f - sha_info['data'][count] = 0x80; - count += 1 - if count > SHA_BLOCKSIZE - 8: - # zero the bytes in data after the count - sha_info['data'] = sha_info['data'][:count] + ([0] * (SHA_BLOCKSIZE - count)) - sha_transform(sha_info) - # zero bytes in data - sha_info['data'] = [0] * SHA_BLOCKSIZE - else: - sha_info['data'] = sha_info['data'][:count] + ([0] * (SHA_BLOCKSIZE - count)) - - sha_info['data'][56] = (hi_bit_count >> 24) & 0xff - sha_info['data'][57] = (hi_bit_count >> 16) & 0xff - sha_info['data'][58] = (hi_bit_count >> 8) & 0xff - sha_info['data'][59] = (hi_bit_count >> 0) & 0xff - sha_info['data'][60] = (lo_bit_count >> 24) & 0xff - sha_info['data'][61] = (lo_bit_count >> 16) & 0xff - sha_info['data'][62] = (lo_bit_count >> 8) & 0xff - sha_info['data'][63] = (lo_bit_count >> 0) & 0xff - - sha_transform(sha_info) - - dig = [] - for i in sha_info['digest']: - dig.extend([ ((i>>24) & 0xff), ((i>>16) & 0xff), ((i>>8) & 0xff), (i & 0xff) ]) - return ''.join([chr(i) for i in dig]) - -class sha256(object): - digest_size = digestsize = SHA_DIGESTSIZE - block_size = SHA_BLOCKSIZE - - def __init__(self, s=None): - self._sha = sha_init() - if s: - sha_update(self._sha, getbuf(s)) - - def update(self, s): - sha_update(self._sha, getbuf(s)) - - def digest(self): - return sha_final(self._sha.copy())[:self._sha['digestsize']] - - def hexdigest(self): - return ''.join(['%.2x' % ord(i) for i in self.digest()]) - - def copy(self): - new = sha256.__new__(sha256) - new._sha = self._sha.copy() - return new - -class sha224(sha256): - digest_size = digestsize = 28 - - def __init__(self, s=None): - self._sha = sha224_init() - if s: - sha_update(self._sha, getbuf(s)) - - def copy(self): - new = sha224.__new__(sha224) - new._sha = self._sha.copy() - return new - -def test(): - a_str = "just a test string" - - assert 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' == sha256().hexdigest() - assert 'd7b553c6f09ac85d142415f857c5310f3bbbe7cdd787cce4b985acedd585266f' == sha256(a_str).hexdigest() - assert '8113ebf33c97daa9998762aacafe750c7cefc2b2f173c90c59663a57fe626f21' == sha256(a_str*7).hexdigest() - - s = sha256(a_str) - s.update(a_str) - assert '03d9963e05a094593190b6fc794cb1a3e1ac7d7883f0b5855268afeccc70d461' == s.hexdigest() - -if __name__ == "__main__": - test() - - diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py --- a/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py @@ -299,6 +299,8 @@ "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973eb"+ "de0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b") + @unittest.skipIf(test_support.is_jython, + "FIXME: Jython's threading appears to fail, investigate") @unittest.skipUnless(threading, 'Threading required for this test.') @test_support.reap_threads def test_threaded_hashing(self): @@ -338,14 +340,8 @@ def test_main(): if test_support.is_jython: - # Java has no builtin support for sha224 - hashes = [hash for hash in HashLibTestCase.supported_hash_names - if hash.lower() != 'sha224'] + hashes = [hash for hash in HashLibTestCase.supported_hash_names] HashLibTestCase.supported_hash_names = tuple(hashes) - del HashLibTestCase.test_case_sha224_0 - del HashLibTestCase.test_case_sha224_1 - del HashLibTestCase.test_case_sha224_2 - del HashLibTestCase.test_case_sha224_3 test_support.run_unittest(HashLibTestCase) diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -2,6 +2,7 @@ Jython 2.7a1 Bugs Fixed + - [ 1880 ] Sha 224 library not present in Jython Jython 2.5.3b2 Bugs Fixed diff --git a/src/org/python/modules/SHA224Digest.java b/src/org/python/modules/SHA224Digest.java new file mode 100644 --- /dev/null +++ b/src/org/python/modules/SHA224Digest.java @@ -0,0 +1,483 @@ +package org.python.modules; + +/** + * Copyright 2011 Gaurav Raje + * Licensed to PSF under a Contributor Agreement. + */ +import java.security.MessageDigest; + +/** + * SHA-224 as described in RFC 3874. This introduces the SHA224 Digest which has + * been ommitted from the JDK {@link java.security}. + * + * + * This implementation has been borrowed from the Bouncy Castle implementation + * of SHA2 algorithms. Since they are MIT Licensed, they are compatible with + * this project. Their mandatory copyright notice follows. + * + * Copyright (c) 2000 - 2011 The Legion Of The Bouncy Castle + * (http://www.bouncycastle.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +public class SHA224Digest + extends MessageDigest +{ + + private static final int BYTE_LENGTH = 64; + private byte[] xBuf; + private int xBufOff; + + private long byteCount; + + @Override + public void update( + byte in) + { + xBuf[xBufOff++] = in; + + if (xBufOff == xBuf.length) + { + processWord(xBuf, 0); + xBufOff = 0; + } + + byteCount++; + } + + @Override + public void update( + byte[] in, + int inOff, + int len) + { + // + // fill the current word + // + while ((xBufOff != 0) && (len > 0)) + { + update(in[inOff]); + + inOff++; + len--; + } + + // + // process whole words. + // + while (len > xBuf.length) + { + processWord(in, inOff); + + inOff += xBuf.length; + len -= xBuf.length; + byteCount += xBuf.length; + } + + // + // load in the remainder. + // + while (len > 0) + { + update(in[inOff]); + + inOff++; + len--; + } + } + + public void finish() + { + long bitLength = (byteCount << 3); + + // + // add the pad bytes. + // + update((byte) 128); + + while (xBufOff != 0) + { + update((byte) 0); + } + + processLength(bitLength); + + processBlock(); + } + + public int getByteLength() + { + return BYTE_LENGTH; + } + + private static final int DIGEST_LENGTH = 28; + + private int H1, H2, H3, H4, H5, H6, H7, H8; + + private int[] X = new int[64]; + private int xOff; + + /** + * Standard constructor + */ + public SHA224Digest() + { + super("SHA-224"); + xBuf = new byte[4]; + xBufOff = 0; + reset(); + } + + /** + * Copy constructor. This will copy the state of the provided message + * digest. + */ + public SHA224Digest(SHA224Digest t) + { + super("SHA-224"); + xBuf = new byte[t.xBuf.length]; + System.arraycopy(t.xBuf, 0, xBuf, 0, t.xBuf.length); + + xBufOff = t.xBufOff; + byteCount = t.byteCount; + + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + H5 = t.H5; + H6 = t.H6; + H7 = t.H7; + H8 = t.H8; + + System.arraycopy(t.X, 0, X, 0, t.X.length); + xOff = t.xOff; + } + + public String getAlgorithmName() + { + return "SHA-224"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + protected void processWord( + byte[] in, + int inOff) + { + // Note: Inlined for performance + // X[xOff] = Pack.bigEndianToInt(in, inOff); + int n = in[inOff] << 24; + n |= (in[++inOff] & 0xff) << 16; + n |= (in[++inOff] & 0xff) << 8; + n |= (in[++inOff] & 0xff); + X[xOff] = n; + + if (++xOff == 16) + { + processBlock(); + } + } + + protected void processLength( + long bitLength) + { + if (xOff > 14) + { + processBlock(); + } + + X[14] = (int) (bitLength >>> 32); + X[15] = (int) (bitLength & 0xffffffff); + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + intToBigEndian(H1, out, outOff); + intToBigEndian(H2, out, outOff + 4); + intToBigEndian(H3, out, outOff + 8); + intToBigEndian(H4, out, outOff + 12); + intToBigEndian(H5, out, outOff + 16); + intToBigEndian(H6, out, outOff + 20); + intToBigEndian(H7, out, outOff + 24); + + reset(); + + return DIGEST_LENGTH; + } + + @Override + public void reset() + { + byteCount = 0; + + xBufOff = 0; + for (int i = 0; i < xBuf.length; i++) + { + xBuf[i] = 0; + } + + /* + * SHA-224 initial hash value + */ + + H1 = 0xc1059ed8; + H2 = 0x367cd507; + H3 = 0x3070dd17; + H4 = 0xf70e5939; + H5 = 0xffc00b31; + H6 = 0x68581511; + H7 = 0x64f98fa7; + H8 = 0xbefa4fa4; + + xOff = 0; + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + protected void processBlock() + { + // + // expand 16 word block into 64 word blocks. + // + for (int t = 16; t <= 63; t++) + { + X[t] = Theta1(X[t - 2]) + X[t - 7] + Theta0(X[t - 15]) + X[t - 16]; + } + + // + // set up working variables. + // + int a = H1; + int b = H2; + int c = H3; + int d = H4; + int e = H5; + int f = H6; + int g = H7; + int h = H8; + + int t = 0; + for (int i = 0; i < 8; i++) + { + // t = 8 * i + h += Sum1(e) + Ch(e, f, g) + K[t] + X[t]; + d += h; + h += Sum0(a) + Maj(a, b, c); + ++t; + + // t = 8 * i + 1 + g += Sum1(d) + Ch(d, e, f) + K[t] + X[t]; + c += g; + g += Sum0(h) + Maj(h, a, b); + ++t; + + // t = 8 * i + 2 + f += Sum1(c) + Ch(c, d, e) + K[t] + X[t]; + b += f; + f += Sum0(g) + Maj(g, h, a); + ++t; + + // t = 8 * i + 3 + e += Sum1(b) + Ch(b, c, d) + K[t] + X[t]; + a += e; + e += Sum0(f) + Maj(f, g, h); + ++t; + + // t = 8 * i + 4 + d += Sum1(a) + Ch(a, b, c) + K[t] + X[t]; + h += d; + d += Sum0(e) + Maj(e, f, g); + ++t; + + // t = 8 * i + 5 + c += Sum1(h) + Ch(h, a, b) + K[t] + X[t]; + g += c; + c += Sum0(d) + Maj(d, e, f); + ++t; + + // t = 8 * i + 6 + b += Sum1(g) + Ch(g, h, a) + K[t] + X[t]; + f += b; + b += Sum0(c) + Maj(c, d, e); + ++t; + + // t = 8 * i + 7 + a += Sum1(f) + Ch(f, g, h) + K[t] + X[t]; + e += a; + a += Sum0(b) + Maj(b, c, d); + ++t; + } + + H1 += a; + H2 += b; + H3 += c; + H4 += d; + H5 += e; + H6 += f; + H7 += g; + H8 += h; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + for (int i = 0; i < 16; i++) + { + X[i] = 0; + } + } + + /* SHA-224 functions */ + private int Ch( + int x, + int y, + int z) + { + return ((x & y) ^ ((~x) & z)); + } + + private int Maj( + int x, + int y, + int z) + { + return ((x & y) ^ (x & z) ^ (y & z)); + } + + private int Sum0( + int x) + { + return ((x >>> 2) | (x << 30)) ^ ((x >>> 13) | (x << 19)) + ^ ((x >>> 22) | (x << 10)); + } + + private int Sum1( + int x) + { + return ((x >>> 6) | (x << 26)) ^ ((x >>> 11) | (x << 21)) + ^ ((x >>> 25) | (x << 7)); + } + + private int Theta0( + int x) + { + return ((x >>> 7) | (x << 25)) ^ ((x >>> 18) | (x << 14)) ^ (x >>> 3); + } + + private int Theta1( + int x) + { + return ((x >>> 17) | (x << 15)) ^ ((x >>> 19) | (x << 13)) + ^ (x >>> 10); + } + + /* + * SHA-224 Constants (represent the first 32 bits of the fractional parts of + * the cube roots of the first sixty-four prime numbers) + */ + static final int K[] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, + 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, + 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, + 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, + 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, + 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, + 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, + 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, + 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, + 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + }; + + @Override + protected void engineUpdate(byte input) { + update(input); + } + + @Override + protected void engineUpdate(byte[] input, int offset, int len) { + update(input, offset, len); + + } + + @Override + protected byte[] engineDigest() { + byte[] digestBytes = new byte[getDigestSize()]; + doFinal(digestBytes, 0); + return digestBytes; + } + + @Override + protected void engineReset() { + reset(); + } + + @Override + public Object clone() + throws CloneNotSupportedException + { + SHA224Digest d = new SHA224Digest(this); + return d; + } + + public static int bigEndianToInt(byte[] bs, int off) + { + int n = bs[off] << 24; + n |= (bs[++off] & 0xff) << 16; + n |= (bs[++off] & 0xff) << 8; + n |= (bs[++off] & 0xff); + return n; + } + + public static void intToBigEndian(int n, byte[] bs, int off) + { + bs[off] = (byte) (n >>> 24); + bs[++off] = (byte) (n >>> 16); + bs[++off] = (byte) (n >>> 8); + bs[++off] = (byte) (n); + } + + public static long bigEndianToLong(byte[] bs, int off) + { + int hi = bigEndianToInt(bs, off); + int lo = bigEndianToInt(bs, off + 4); + return ((hi & 0xffffffffL) << 32) | (lo & 0xffffffffL); + } + + public static void longToBigEndian(long n, byte[] bs, int off) + { + intToBigEndian((int) (n >>> 32), bs, off); + intToBigEndian((int) (n & 0xffffffffL), bs, off + 4); + } +} diff --git a/src/org/python/modules/_hashlib.java b/src/org/python/modules/_hashlib.java --- a/src/org/python/modules/_hashlib.java +++ b/src/org/python/modules/_hashlib.java @@ -145,6 +145,10 @@ private static final MessageDigest getDigest(String name) { try { + // since sha 224 is not present in java.security + if (name.equals("sha-224")) { + return new SHA224Digest(); + } return MessageDigest.getInstance(name); } catch (NoSuchAlgorithmException nsae) { throw Py.ValueError("unsupported hash type"); @@ -186,8 +190,7 @@ string = obj.toString(); } else if (obj instanceof PyArray) { string = ((PyArray)obj).tostring(); - } - else { + } else { throw Py.TypeError("update() argument 1 must be string or read-only buffer, not " + obj.getType().fastGetName()); } -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sat May 12 02:45:19 2012 From: jython-checkins at python.org (philip.jenvey) Date: Sat, 12 May 2012 02:45:19 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_make_hashlib_threadsafe?= Message-ID: http://hg.python.org/jython/rev/88ee96ae3c05 changeset: 6650:88ee96ae3c05 user: Philip Jenvey date: Fri May 11 17:45:14 2012 -0700 summary: make hashlib threadsafe files: Lib/test/test_hashlib.py | 349 --------------- src/org/python/modules/_hashlib.java | 11 +- 2 files changed, 8 insertions(+), 352 deletions(-) diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py deleted file mode 100644 --- a/Lib/test/test_hashlib.py +++ /dev/null @@ -1,349 +0,0 @@ -# Test hashlib module -# -# $Id: test_hashlib.py 80564 2010-04-27 22:59:35Z victor.stinner $ -# -# Copyright (C) 2005-2010 Gregory P. Smith (greg at krypto.org) -# Licensed to PSF under a Contributor Agreement. -# - -import array -import hashlib -import itertools -import sys -try: - import threading -except ImportError: - threading = None -import unittest -import warnings -from test import test_support -from test.test_support import _4G, precisionbigmemtest - -# Were we compiled --with-pydebug or with #define Py_DEBUG? -COMPILED_WITH_PYDEBUG = hasattr(sys, 'gettotalrefcount') - - -def hexstr(s): - import string - h = string.hexdigits - r = '' - for c in s: - i = ord(c) - r = r + h[(i >> 4) & 0xF] + h[i & 0xF] - return r - - -class HashLibTestCase(unittest.TestCase): - supported_hash_names = ( 'md5', 'MD5', 'sha1', 'SHA1', - 'sha224', 'SHA224', 'sha256', 'SHA256', - 'sha384', 'SHA384', 'sha512', 'SHA512' ) - - _warn_on_extension_import = COMPILED_WITH_PYDEBUG - - def _conditional_import_module(self, module_name): - """Import a module and return a reference to it or None on failure.""" - try: - exec('import '+module_name) - except ImportError, error: - if self._warn_on_extension_import: - warnings.warn('Did a C extension fail to compile? %s' % error) - return locals().get(module_name) - - def __init__(self, *args, **kwargs): - algorithms = set() - for algorithm in self.supported_hash_names: - algorithms.add(algorithm.lower()) - self.constructors_to_test = {} - for algorithm in algorithms: - self.constructors_to_test[algorithm] = set() - - # For each algorithm, test the direct constructor and the use - # 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): - if data is None: - return hashlib.new(_alg) - return hashlib.new(_alg, data) - constructors.add(_test_algorithm_via_hashlib_new) - - _hashlib = self._conditional_import_module('_hashlib') - if _hashlib: - # These two algorithms should always be present when this module - # is compiled. If not, something was compiled wrong. - assert hasattr(_hashlib, 'openssl_md5') - assert hasattr(_hashlib, 'openssl_sha1') - for algorithm, constructors in self.constructors_to_test.items(): - constructor = getattr(_hashlib, 'openssl_'+algorithm, None) - if constructor: - constructors.add(constructor) - - _md5 = self._conditional_import_module('_md5') - if _md5: - self.constructors_to_test['md5'].add(_md5.new) - _sha = self._conditional_import_module('_sha') - if _sha: - self.constructors_to_test['sha1'].add(_sha.new) - _sha256 = self._conditional_import_module('_sha256') - if _sha256: - self.constructors_to_test['sha224'].add(_sha256.sha224) - self.constructors_to_test['sha256'].add(_sha256.sha256) - _sha512 = self._conditional_import_module('_sha512') - if _sha512: - self.constructors_to_test['sha384'].add(_sha512.sha384) - self.constructors_to_test['sha512'].add(_sha512.sha512) - - super(HashLibTestCase, self).__init__(*args, **kwargs) - - def test_hash_array(self): - a = array.array("b", range(10)) - constructors = self.constructors_to_test.itervalues() - for cons in itertools.chain.from_iterable(constructors): - c = cons(a) - c.hexdigest() - - def test_algorithms_attribute(self): - self.assertEqual(hashlib.algorithms, - tuple([_algo for _algo in self.supported_hash_names if - _algo.islower()])) - - def test_unknown_hash(self): - try: - hashlib.new('spam spam spam spam spam') - except ValueError: - pass - else: - self.assertTrue(0 == "hashlib didn't reject bogus hash name") - - def test_hexdigest(self): - for name in self.supported_hash_names: - h = hashlib.new(name) - self.assertTrue(hexstr(h.digest()) == h.hexdigest()) - - def test_large_update(self): - aas = 'a' * 128 - bees = 'b' * 127 - cees = 'c' * 126 - abcs = aas + bees + cees - - for name in self.supported_hash_names: - m1 = hashlib.new(name) - m1.update(aas) - m1.update(bees) - m1.update(cees) - - m2 = hashlib.new(name) - m2.update(abcs) - self.assertEqual(m1.digest(), m2.digest(), name+' update problem.') - - m3 = hashlib.new(name, abcs) - self.assertEqual(m1.digest(), m3.digest(), name+' new problem.') - - def check(self, name, data, digest): - 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: - computed = hash_object_constructor(data).hexdigest() - self.assertEqual( - computed, digest, - "Hash algorithm %s constructed using %s returned hexdigest" - " %r for %d byte input data that should have hashed to %r." - % (name, hash_object_constructor, - computed, len(data), digest)) - - def check_unicode(self, algorithm_name): - # Unicode objects are not allowed as input. - expected = hashlib.new(algorithm_name, str(u'spam')).hexdigest() - self.check(algorithm_name, u'spam', expected) - - def test_unicode(self): - # In python 2.x unicode is auto-encoded to the system default encoding - # when passed to hashlib functions. - self.check_unicode('md5') - self.check_unicode('sha1') - self.check_unicode('sha224') - self.check_unicode('sha256') - self.check_unicode('sha384') - self.check_unicode('sha512') - - def test_case_md5_0(self): - self.check('md5', '', 'd41d8cd98f00b204e9800998ecf8427e') - - def test_case_md5_1(self): - self.check('md5', 'abc', '900150983cd24fb0d6963f7d28e17f72') - - def test_case_md5_2(self): - self.check('md5', 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', - 'd174ab98d277d9f5a5611c2c9f419d9f') - - @precisionbigmemtest(size=_4G + 5, memuse=1) - def test_case_md5_huge(self, size): - if size == _4G + 5: - try: - self.check('md5', 'A'*size, 'c9af2dff37468ce5dfee8f2cfc0a9c6d') - except OverflowError: - pass # 32-bit arch - - @precisionbigmemtest(size=_4G - 1, memuse=1) - def test_case_md5_uintmax(self, size): - if size == _4G - 1: - try: - self.check('md5', 'A'*size, '28138d306ff1b8281f1a9067e1a1a2b3') - except OverflowError: - pass # 32-bit arch - - # use the three examples from Federal Information Processing Standards - # Publication 180-1, Secure Hash Standard, 1995 April 17 - # http://www.itl.nist.gov/div897/pubs/fip180-1.htm - - def test_case_sha1_0(self): - self.check('sha1', "", - "da39a3ee5e6b4b0d3255bfef95601890afd80709") - - def test_case_sha1_1(self): - self.check('sha1', "abc", - "a9993e364706816aba3e25717850c26c9cd0d89d") - - def test_case_sha1_2(self): - self.check('sha1', "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - "84983e441c3bd26ebaae4aa1f95129e5e54670f1") - - def test_case_sha1_3(self): - self.check('sha1', "a" * 1000000, - "34aa973cd4c4daa4f61eeb2bdbad27316534016f") - - - # use the examples from Federal Information Processing Standards - # Publication 180-2, Secure Hash Standard, 2002 August 1 - # http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf - - def test_case_sha224_0(self): - self.check('sha224', "", - "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f") - - def test_case_sha224_1(self): - self.check('sha224', "abc", - "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7") - - def test_case_sha224_2(self): - self.check('sha224', - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - "75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525") - - def test_case_sha224_3(self): - self.check('sha224', "a" * 1000000, - "20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67") - - - def test_case_sha256_0(self): - self.check('sha256', "", - "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") - - def test_case_sha256_1(self): - self.check('sha256', "abc", - "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad") - - def test_case_sha256_2(self): - self.check('sha256', - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1") - - def test_case_sha256_3(self): - self.check('sha256', "a" * 1000000, - "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0") - - - def test_case_sha384_0(self): - self.check('sha384', "", - "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da"+ - "274edebfe76f65fbd51ad2f14898b95b") - - def test_case_sha384_1(self): - self.check('sha384', "abc", - "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed"+ - "8086072ba1e7cc2358baeca134c825a7") - - def test_case_sha384_2(self): - self.check('sha384', - "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"+ - "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", - "09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712"+ - "fcc7c71a557e2db966c3e9fa91746039") - - def test_case_sha384_3(self): - self.check('sha384', "a" * 1000000, - "9d0e1809716474cb086e834e310a4a1ced149e9c00f248527972cec5704c2a5b"+ - "07b8b3dc38ecc4ebae97ddd87f3d8985") - - - def test_case_sha512_0(self): - self.check('sha512', "", - "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce"+ - "47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e") - - def test_case_sha512_1(self): - self.check('sha512', "abc", - "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a"+ - "2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f") - - def test_case_sha512_2(self): - self.check('sha512', - "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"+ - "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", - "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018"+ - "501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909") - - def test_case_sha512_3(self): - self.check('sha512', "a" * 1000000, - "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973eb"+ - "de0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b") - - @unittest.skipIf(test_support.is_jython, - "FIXME: Jython's threading appears to fail, investigate") - @unittest.skipUnless(threading, 'Threading required for this test.') - @test_support.reap_threads - def test_threaded_hashing(self): - # Updating the same hash object from several threads at once - # using data chunk sizes containing the same byte sequences. - # - # If the internal locks are working to prevent multiple - # updates on the same object from running at once, the resulting - # hash will be the same as doing it single threaded upfront. - hasher = hashlib.sha1() - num_threads = 5 - smallest_data = 'swineflu' - data = smallest_data*200000 - expected_hash = hashlib.sha1(data*num_threads).hexdigest() - - def hash_in_chunks(chunk_size, event): - index = 0 - while index < len(data): - hasher.update(data[index:index+chunk_size]) - index += chunk_size - event.set() - - events = [] - for threadnum in xrange(num_threads): - chunk_size = len(data) // (10**threadnum) - assert chunk_size > 0 - assert chunk_size % len(smallest_data) == 0 - event = threading.Event() - events.append(event) - threading.Thread(target=hash_in_chunks, - args=(chunk_size, event)).start() - - for event in events: - event.wait() - - self.assertEqual(expected_hash, hasher.hexdigest()) - -def test_main(): - if test_support.is_jython: - hashes = [hash for hash in HashLibTestCase.supported_hash_names] - HashLibTestCase.supported_hash_names = tuple(hashes) - - test_support.run_unittest(HashLibTestCase) - -if __name__ == "__main__": - test_main() diff --git a/src/org/python/modules/_hashlib.java b/src/org/python/modules/_hashlib.java --- a/src/org/python/modules/_hashlib.java +++ b/src/org/python/modules/_hashlib.java @@ -162,7 +162,9 @@ */ private MessageDigest cloneDigest() { try { - return (MessageDigest)digest.clone(); + synchronized (this) { + return (MessageDigest)digest.clone(); + } } catch (CloneNotSupportedException cnse) { throw Py.RuntimeError(String.format("_hashlib.HASH (%s) internal error", name)); } @@ -194,7 +196,10 @@ throw Py.TypeError("update() argument 1 must be string or read-only buffer, not " + obj.getType().fastGetName()); } - digest.update(StringUtil.toBytes(string)); + byte[] input = StringUtil.toBytes(string); + synchronized (this) { + digest.update(input); + } } public PyObject digest() { @@ -236,7 +241,7 @@ } @ExposedGet(name = "digestsize") - public int getDigestSize() { + public synchronized int getDigestSize() { return digest.getDigestLength(); } -- Repository URL: http://hg.python.org/jython From pjenvey at underboss.org Sat May 12 02:46:12 2012 From: pjenvey at underboss.org (Philip Jenvey) Date: Fri, 11 May 2012 17:46:12 -0700 Subject: [Jython-checkins] jython: Fixes #1880 Sha 224 library not present in Jython, thanks Guarav Raje! In-Reply-To: References: Message-ID: <4F187BBD-B45A-4A90-82B6-9D828D333EB7@underboss.org> Cool. Though it'd be nice to have this on 2.5 On May 11, 2012, at 4:54 PM, frank.wierzbicki wrote: > http://hg.python.org/jython/rev/47c55317a1c9 > changeset: 6649:47c55317a1c9 > parent: 6647:17caa65fabac > user: Gaurav Raje > date: Fri May 11 16:28:23 2012 -0700 > summary: > Fixes #1880 Sha 224 library not present in Jython, thanks Guarav Raje! > > files: > Lib/_sha256.py | 265 ------ > Lib/test/test_hashlib.py | 10 +- > NEWS | 1 + > src/org/python/modules/SHA224Digest.java | 483 +++++++++++ > src/org/python/modules/_hashlib.java | 7 +- > 5 files changed, 492 insertions(+), 274 deletions(-) > > > diff --git a/Lib/_sha256.py b/Lib/_sha256.py > deleted file mode 100644 > --- a/Lib/_sha256.py > +++ /dev/null > @@ -1,265 +0,0 @@ > -import struct > - > -SHA_BLOCKSIZE = 64 > -SHA_DIGESTSIZE = 32 > - > - > -def new_shaobject(): > - return { > - 'digest': [0]*8, > - 'count_lo': 0, > - 'count_hi': 0, > - 'data': [0]* SHA_BLOCKSIZE, > - 'local': 0, > - 'digestsize': 0 > - } > - > -ROR = lambda x, y: (((x & 0xffffffff) >> (y & 31)) | (x << (32 - (y & 31)))) & 0xffffffff > -Ch = lambda x, y, z: (z ^ (x & (y ^ z))) > -Maj = lambda x, y, z: (((x | y) & z) | (x & y)) > -S = lambda x, n: ROR(x, n) > -R = lambda x, n: (x & 0xffffffff) >> n > -Sigma0 = lambda x: (S(x, 2) ^ S(x, 13) ^ S(x, 22)) > -Sigma1 = lambda x: (S(x, 6) ^ S(x, 11) ^ S(x, 25)) > -Gamma0 = lambda x: (S(x, 7) ^ S(x, 18) ^ R(x, 3)) > -Gamma1 = lambda x: (S(x, 17) ^ S(x, 19) ^ R(x, 10)) > - > -def sha_transform(sha_info): > - W = [] > - > - d = sha_info['data'] > - for i in xrange(0,16): > - W.append( (d[4*i]<<24) + (d[4*i+1]<<16) + (d[4*i+2]<<8) + d[4*i+3]) > - > - for i in xrange(16,64): > - W.append( (Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]) & 0xffffffff ) > - > - ss = sha_info['digest'][:] > - > - def RND(a,b,c,d,e,f,g,h,i,ki): > - t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i]; > - t1 = Sigma0(a) + Maj(a, b, c); > - d += t0; > - h = t0 + t1; > - return d & 0xffffffff, h & 0xffffffff > - > - ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],0,0x428a2f98); > - ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],1,0x71374491); > - ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],2,0xb5c0fbcf); > - ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],3,0xe9b5dba5); > - ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],4,0x3956c25b); > - ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],5,0x59f111f1); > - ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],6,0x923f82a4); > - ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],7,0xab1c5ed5); > - ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],8,0xd807aa98); > - ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],9,0x12835b01); > - ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],10,0x243185be); > - ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],11,0x550c7dc3); > - ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],12,0x72be5d74); > - ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],13,0x80deb1fe); > - ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],14,0x9bdc06a7); > - ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],15,0xc19bf174); > - ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],16,0xe49b69c1); > - ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],17,0xefbe4786); > - ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],18,0x0fc19dc6); > - ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],19,0x240ca1cc); > - ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],20,0x2de92c6f); > - ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],21,0x4a7484aa); > - ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],22,0x5cb0a9dc); > - ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],23,0x76f988da); > - ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],24,0x983e5152); > - ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],25,0xa831c66d); > - ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],26,0xb00327c8); > - ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],27,0xbf597fc7); > - ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],28,0xc6e00bf3); > - ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],29,0xd5a79147); > - ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],30,0x06ca6351); > - ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],31,0x14292967); > - ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],32,0x27b70a85); > - ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],33,0x2e1b2138); > - ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],34,0x4d2c6dfc); > - ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],35,0x53380d13); > - ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],36,0x650a7354); > - ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],37,0x766a0abb); > - ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],38,0x81c2c92e); > - ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],39,0x92722c85); > - ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],40,0xa2bfe8a1); > - ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],41,0xa81a664b); > - ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],42,0xc24b8b70); > - ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],43,0xc76c51a3); > - ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],44,0xd192e819); > - ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],45,0xd6990624); > - ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],46,0xf40e3585); > - ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],47,0x106aa070); > - ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],48,0x19a4c116); > - ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],49,0x1e376c08); > - ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],50,0x2748774c); > - ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],51,0x34b0bcb5); > - ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],52,0x391c0cb3); > - ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],53,0x4ed8aa4a); > - ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],54,0x5b9cca4f); > - ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],55,0x682e6ff3); > - ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],56,0x748f82ee); > - ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],57,0x78a5636f); > - ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],58,0x84c87814); > - ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],59,0x8cc70208); > - ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],60,0x90befffa); > - ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],61,0xa4506ceb); > - ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],62,0xbef9a3f7); > - ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],63,0xc67178f2); > - > - dig = [] > - for i, x in enumerate(sha_info['digest']): > - dig.append( (x + ss[i]) & 0xffffffff ) > - sha_info['digest'] = dig > - > -def sha_init(): > - sha_info = new_shaobject() > - sha_info['digest'] = [0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19] > - sha_info['count_lo'] = 0 > - sha_info['count_hi'] = 0 > - sha_info['local'] = 0 > - sha_info['digestsize'] = 32 > - return sha_info > - > -def sha224_init(): > - sha_info = new_shaobject() > - sha_info['digest'] = [0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4] > - sha_info['count_lo'] = 0 > - sha_info['count_hi'] = 0 > - sha_info['local'] = 0 > - sha_info['digestsize'] = 28 > - return sha_info > - > -def getbuf(s): > - if isinstance(s, str): > - return s > - elif isinstance(s, unicode): > - return str(s) > - else: > - return buffer(s) > - > -def sha_update(sha_info, buffer): > - count = len(buffer) > - buffer_idx = 0 > - clo = (sha_info['count_lo'] + (count << 3)) & 0xffffffff > - if clo < sha_info['count_lo']: > - sha_info['count_hi'] += 1 > - sha_info['count_lo'] = clo > - > - sha_info['count_hi'] += (count >> 29) > - > - if sha_info['local']: > - i = SHA_BLOCKSIZE - sha_info['local'] > - if i > count: > - i = count > - > - # copy buffer > - for x in enumerate(buffer[buffer_idx:buffer_idx+i]): > - sha_info['data'][sha_info['local']+x[0]] = struct.unpack('B', x[1])[0] > - > - count -= i > - buffer_idx += i > - > - sha_info['local'] += i > - if sha_info['local'] == SHA_BLOCKSIZE: > - sha_transform(sha_info) > - sha_info['local'] = 0 > - else: > - return > - > - while count >= SHA_BLOCKSIZE: > - # copy buffer > - sha_info['data'] = [struct.unpack('B',c)[0] for c in buffer[buffer_idx:buffer_idx + SHA_BLOCKSIZE]] > - count -= SHA_BLOCKSIZE > - buffer_idx += SHA_BLOCKSIZE > - sha_transform(sha_info) > - > - > - # copy buffer > - pos = sha_info['local'] > - sha_info['data'][pos:pos+count] = [struct.unpack('B',c)[0] for c in buffer[buffer_idx:buffer_idx + count]] > - sha_info['local'] = count > - > -def sha_final(sha_info): > - lo_bit_count = sha_info['count_lo'] > - hi_bit_count = sha_info['count_hi'] > - count = (lo_bit_count >> 3) & 0x3f > - sha_info['data'][count] = 0x80; > - count += 1 > - if count > SHA_BLOCKSIZE - 8: > - # zero the bytes in data after the count > - sha_info['data'] = sha_info['data'][:count] + ([0] * (SHA_BLOCKSIZE - count)) > - sha_transform(sha_info) > - # zero bytes in data > - sha_info['data'] = [0] * SHA_BLOCKSIZE > - else: > - sha_info['data'] = sha_info['data'][:count] + ([0] * (SHA_BLOCKSIZE - count)) > - > - sha_info['data'][56] = (hi_bit_count >> 24) & 0xff > - sha_info['data'][57] = (hi_bit_count >> 16) & 0xff > - sha_info['data'][58] = (hi_bit_count >> 8) & 0xff > - sha_info['data'][59] = (hi_bit_count >> 0) & 0xff > - sha_info['data'][60] = (lo_bit_count >> 24) & 0xff > - sha_info['data'][61] = (lo_bit_count >> 16) & 0xff > - sha_info['data'][62] = (lo_bit_count >> 8) & 0xff > - sha_info['data'][63] = (lo_bit_count >> 0) & 0xff > - > - sha_transform(sha_info) > - > - dig = [] > - for i in sha_info['digest']: > - dig.extend([ ((i>>24) & 0xff), ((i>>16) & 0xff), ((i>>8) & 0xff), (i & 0xff) ]) > - return ''.join([chr(i) for i in dig]) > - > -class sha256(object): > - digest_size = digestsize = SHA_DIGESTSIZE > - block_size = SHA_BLOCKSIZE > - > - def __init__(self, s=None): > - self._sha = sha_init() > - if s: > - sha_update(self._sha, getbuf(s)) > - > - def update(self, s): > - sha_update(self._sha, getbuf(s)) > - > - def digest(self): > - return sha_final(self._sha.copy())[:self._sha['digestsize']] > - > - def hexdigest(self): > - return ''.join(['%.2x' % ord(i) for i in self.digest()]) > - > - def copy(self): > - new = sha256.__new__(sha256) > - new._sha = self._sha.copy() > - return new > - > -class sha224(sha256): > - digest_size = digestsize = 28 > - > - def __init__(self, s=None): > - self._sha = sha224_init() > - if s: > - sha_update(self._sha, getbuf(s)) > - > - def copy(self): > - new = sha224.__new__(sha224) > - new._sha = self._sha.copy() > - return new > - > -def test(): > - a_str = "just a test string" > - > - assert 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' == sha256().hexdigest() > - assert 'd7b553c6f09ac85d142415f857c5310f3bbbe7cdd787cce4b985acedd585266f' == sha256(a_str).hexdigest() > - assert '8113ebf33c97daa9998762aacafe750c7cefc2b2f173c90c59663a57fe626f21' == sha256(a_str*7).hexdigest() > - > - s = sha256(a_str) > - s.update(a_str) > - assert '03d9963e05a094593190b6fc794cb1a3e1ac7d7883f0b5855268afeccc70d461' == s.hexdigest() > - > -if __name__ == "__main__": > - test() > - > - > diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py > --- a/Lib/test/test_hashlib.py > +++ b/Lib/test/test_hashlib.py > @@ -299,6 +299,8 @@ > "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973eb"+ > "de0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b") > > + @unittest.skipIf(test_support.is_jython, > + "FIXME: Jython's threading appears to fail, investigate") > @unittest.skipUnless(threading, 'Threading required for this test.') > @test_support.reap_threads > def test_threaded_hashing(self): > @@ -338,14 +340,8 @@ > > def test_main(): > if test_support.is_jython: > - # Java has no builtin support for sha224 > - hashes = [hash for hash in HashLibTestCase.supported_hash_names > - if hash.lower() != 'sha224'] > + hashes = [hash for hash in HashLibTestCase.supported_hash_names] > HashLibTestCase.supported_hash_names = tuple(hashes) > - del HashLibTestCase.test_case_sha224_0 > - del HashLibTestCase.test_case_sha224_1 > - del HashLibTestCase.test_case_sha224_2 > - del HashLibTestCase.test_case_sha224_3 > > test_support.run_unittest(HashLibTestCase) > > diff --git a/NEWS b/NEWS > --- a/NEWS > +++ b/NEWS > @@ -2,6 +2,7 @@ > > Jython 2.7a1 > Bugs Fixed > + - [ 1880 ] Sha 224 library not present in Jython > > Jython 2.5.3b2 > Bugs Fixed > diff --git a/src/org/python/modules/SHA224Digest.java b/src/org/python/modules/SHA224Digest.java > new file mode 100644 > --- /dev/null > +++ b/src/org/python/modules/SHA224Digest.java > @@ -0,0 +1,483 @@ > +package org.python.modules; > + > +/** > + * Copyright 2011 Gaurav Raje > + * Licensed to PSF under a Contributor Agreement. > + */ > +import java.security.MessageDigest; > + > +/** > + * SHA-224 as described in RFC 3874. This introduces the SHA224 Digest which has > + * been ommitted from the JDK {@link java.security}. > + * > + * > + * This implementation has been borrowed from the Bouncy Castle implementation > + * of SHA2 algorithms. Since they are MIT Licensed, they are compatible with > + * this project. Their mandatory copyright notice follows. > + * > + * Copyright (c) 2000 - 2011 The Legion Of The Bouncy Castle > + * (http://www.bouncycastle.org) > + * > + * Permission is hereby granted, free of charge, to any person obtaining a copy > + * of this software and associated documentation files (the "Software"), to deal > + * in the Software without restriction, including without limitation the rights > + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell > + * copies of the Software, and to permit persons to whom the Software is > + * furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice shall be included in > + * all copies or substantial portions of the Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE > + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE > + * SOFTWARE. > + */ > +public class SHA224Digest > + extends MessageDigest > +{ > + > + private static final int BYTE_LENGTH = 64; > + private byte[] xBuf; > + private int xBufOff; > + > + private long byteCount; > + > + @Override > + public void update( > + byte in) > + { > + xBuf[xBufOff++] = in; > + > + if (xBufOff == xBuf.length) > + { > + processWord(xBuf, 0); > + xBufOff = 0; > + } > + > + byteCount++; > + } > + > + @Override > + public void update( > + byte[] in, > + int inOff, > + int len) > + { > + // > + // fill the current word > + // > + while ((xBufOff != 0) && (len > 0)) > + { > + update(in[inOff]); > + > + inOff++; > + len--; > + } > + > + // > + // process whole words. > + // > + while (len > xBuf.length) > + { > + processWord(in, inOff); > + > + inOff += xBuf.length; > + len -= xBuf.length; > + byteCount += xBuf.length; > + } > + > + // > + // load in the remainder. > + // > + while (len > 0) > + { > + update(in[inOff]); > + > + inOff++; > + len--; > + } > + } > + > + public void finish() > + { > + long bitLength = (byteCount << 3); > + > + // > + // add the pad bytes. > + // > + update((byte) 128); > + > + while (xBufOff != 0) > + { > + update((byte) 0); > + } > + > + processLength(bitLength); > + > + processBlock(); > + } > + > + public int getByteLength() > + { > + return BYTE_LENGTH; > + } > + > + private static final int DIGEST_LENGTH = 28; > + > + private int H1, H2, H3, H4, H5, H6, H7, H8; > + > + private int[] X = new int[64]; > + private int xOff; > + > + /** > + * Standard constructor > + */ > + public SHA224Digest() > + { > + super("SHA-224"); > + xBuf = new byte[4]; > + xBufOff = 0; > + reset(); > + } > + > + /** > + * Copy constructor. This will copy the state of the provided message > + * digest. > + */ > + public SHA224Digest(SHA224Digest t) > + { > + super("SHA-224"); > + xBuf = new byte[t.xBuf.length]; > + System.arraycopy(t.xBuf, 0, xBuf, 0, t.xBuf.length); > + > + xBufOff = t.xBufOff; > + byteCount = t.byteCount; > + > + H1 = t.H1; > + H2 = t.H2; > + H3 = t.H3; > + H4 = t.H4; > + H5 = t.H5; > + H6 = t.H6; > + H7 = t.H7; > + H8 = t.H8; > + > + System.arraycopy(t.X, 0, X, 0, t.X.length); > + xOff = t.xOff; > + } > + > + public String getAlgorithmName() > + { > + return "SHA-224"; > + } > + > + public int getDigestSize() > + { > + return DIGEST_LENGTH; > + } > + > + protected void processWord( > + byte[] in, > + int inOff) > + { > + // Note: Inlined for performance > + // X[xOff] = Pack.bigEndianToInt(in, inOff); > + int n = in[inOff] << 24; > + n |= (in[++inOff] & 0xff) << 16; > + n |= (in[++inOff] & 0xff) << 8; > + n |= (in[++inOff] & 0xff); > + X[xOff] = n; > + > + if (++xOff == 16) > + { > + processBlock(); > + } > + } > + > + protected void processLength( > + long bitLength) > + { > + if (xOff > 14) > + { > + processBlock(); > + } > + > + X[14] = (int) (bitLength >>> 32); > + X[15] = (int) (bitLength & 0xffffffff); > + } > + > + public int doFinal( > + byte[] out, > + int outOff) > + { > + finish(); > + > + intToBigEndian(H1, out, outOff); > + intToBigEndian(H2, out, outOff + 4); > + intToBigEndian(H3, out, outOff + 8); > + intToBigEndian(H4, out, outOff + 12); > + intToBigEndian(H5, out, outOff + 16); > + intToBigEndian(H6, out, outOff + 20); > + intToBigEndian(H7, out, outOff + 24); > + > + reset(); > + > + return DIGEST_LENGTH; > + } > + > + @Override > + public void reset() > + { > + byteCount = 0; > + > + xBufOff = 0; > + for (int i = 0; i < xBuf.length; i++) > + { > + xBuf[i] = 0; > + } > + > + /* > + * SHA-224 initial hash value > + */ > + > + H1 = 0xc1059ed8; > + H2 = 0x367cd507; > + H3 = 0x3070dd17; > + H4 = 0xf70e5939; > + H5 = 0xffc00b31; > + H6 = 0x68581511; > + H7 = 0x64f98fa7; > + H8 = 0xbefa4fa4; > + > + xOff = 0; > + for (int i = 0; i != X.length; i++) > + { > + X[i] = 0; > + } > + } > + > + protected void processBlock() > + { > + // > + // expand 16 word block into 64 word blocks. > + // > + for (int t = 16; t <= 63; t++) > + { > + X[t] = Theta1(X[t - 2]) + X[t - 7] + Theta0(X[t - 15]) + X[t - 16]; > + } > + > + // > + // set up working variables. > + // > + int a = H1; > + int b = H2; > + int c = H3; > + int d = H4; > + int e = H5; > + int f = H6; > + int g = H7; > + int h = H8; > + > + int t = 0; > + for (int i = 0; i < 8; i++) > + { > + // t = 8 * i > + h += Sum1(e) + Ch(e, f, g) + K[t] + X[t]; > + d += h; > + h += Sum0(a) + Maj(a, b, c); > + ++t; > + > + // t = 8 * i + 1 > + g += Sum1(d) + Ch(d, e, f) + K[t] + X[t]; > + c += g; > + g += Sum0(h) + Maj(h, a, b); > + ++t; > + > + // t = 8 * i + 2 > + f += Sum1(c) + Ch(c, d, e) + K[t] + X[t]; > + b += f; > + f += Sum0(g) + Maj(g, h, a); > + ++t; > + > + // t = 8 * i + 3 > + e += Sum1(b) + Ch(b, c, d) + K[t] + X[t]; > + a += e; > + e += Sum0(f) + Maj(f, g, h); > + ++t; > + > + // t = 8 * i + 4 > + d += Sum1(a) + Ch(a, b, c) + K[t] + X[t]; > + h += d; > + d += Sum0(e) + Maj(e, f, g); > + ++t; > + > + // t = 8 * i + 5 > + c += Sum1(h) + Ch(h, a, b) + K[t] + X[t]; > + g += c; > + c += Sum0(d) + Maj(d, e, f); > + ++t; > + > + // t = 8 * i + 6 > + b += Sum1(g) + Ch(g, h, a) + K[t] + X[t]; > + f += b; > + b += Sum0(c) + Maj(c, d, e); > + ++t; > + > + // t = 8 * i + 7 > + a += Sum1(f) + Ch(f, g, h) + K[t] + X[t]; > + e += a; > + a += Sum0(b) + Maj(b, c, d); > + ++t; > + } > + > + H1 += a; > + H2 += b; > + H3 += c; > + H4 += d; > + H5 += e; > + H6 += f; > + H7 += g; > + H8 += h; > + > + // > + // reset the offset and clean out the word buffer. > + // > + xOff = 0; > + for (int i = 0; i < 16; i++) > + { > + X[i] = 0; > + } > + } > + > + /* SHA-224 functions */ > + private int Ch( > + int x, > + int y, > + int z) > + { > + return ((x & y) ^ ((~x) & z)); > + } > + > + private int Maj( > + int x, > + int y, > + int z) > + { > + return ((x & y) ^ (x & z) ^ (y & z)); > + } > + > + private int Sum0( > + int x) > + { > + return ((x >>> 2) | (x << 30)) ^ ((x >>> 13) | (x << 19)) > + ^ ((x >>> 22) | (x << 10)); > + } > + > + private int Sum1( > + int x) > + { > + return ((x >>> 6) | (x << 26)) ^ ((x >>> 11) | (x << 21)) > + ^ ((x >>> 25) | (x << 7)); > + } > + > + private int Theta0( > + int x) > + { > + return ((x >>> 7) | (x << 25)) ^ ((x >>> 18) | (x << 14)) ^ (x >>> 3); > + } > + > + private int Theta1( > + int x) > + { > + return ((x >>> 17) | (x << 15)) ^ ((x >>> 19) | (x << 13)) > + ^ (x >>> 10); > + } > + > + /* > + * SHA-224 Constants (represent the first 32 bits of the fractional parts of > + * the cube roots of the first sixty-four prime numbers) > + */ > + static final int K[] = { > + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, > + 0x59f111f1, 0x923f82a4, 0xab1c5ed5, > + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, > + 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, > + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, > + 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, > + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, > + 0xd5a79147, 0x06ca6351, 0x14292967, > + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, > + 0x766a0abb, 0x81c2c92e, 0x92722c85, > + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, > + 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, > + 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, > + 0x682e6ff3, > + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, > + 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 > + }; > + > + @Override > + protected void engineUpdate(byte input) { > + update(input); > + } > + > + @Override > + protected void engineUpdate(byte[] input, int offset, int len) { > + update(input, offset, len); > + > + } > + > + @Override > + protected byte[] engineDigest() { > + byte[] digestBytes = new byte[getDigestSize()]; > + doFinal(digestBytes, 0); > + return digestBytes; > + } > + > + @Override > + protected void engineReset() { > + reset(); > + } > + > + @Override > + public Object clone() > + throws CloneNotSupportedException > + { > + SHA224Digest d = new SHA224Digest(this); > + return d; > + } > + > + public static int bigEndianToInt(byte[] bs, int off) > + { > + int n = bs[off] << 24; > + n |= (bs[++off] & 0xff) << 16; > + n |= (bs[++off] & 0xff) << 8; > + n |= (bs[++off] & 0xff); > + return n; > + } > + > + public static void intToBigEndian(int n, byte[] bs, int off) > + { > + bs[off] = (byte) (n >>> 24); > + bs[++off] = (byte) (n >>> 16); > + bs[++off] = (byte) (n >>> 8); > + bs[++off] = (byte) (n); > + } > + > + public static long bigEndianToLong(byte[] bs, int off) > + { > + int hi = bigEndianToInt(bs, off); > + int lo = bigEndianToInt(bs, off + 4); > + return ((hi & 0xffffffffL) << 32) | (lo & 0xffffffffL); > + } > + > + public static void longToBigEndian(long n, byte[] bs, int off) > + { > + intToBigEndian((int) (n >>> 32), bs, off); > + intToBigEndian((int) (n & 0xffffffffL), bs, off + 4); > + } > +} > diff --git a/src/org/python/modules/_hashlib.java b/src/org/python/modules/_hashlib.java > --- a/src/org/python/modules/_hashlib.java > +++ b/src/org/python/modules/_hashlib.java > @@ -145,6 +145,10 @@ > > private static final MessageDigest getDigest(String name) { > try { > + // since sha 224 is not present in java.security > + if (name.equals("sha-224")) { > + return new SHA224Digest(); > + } > return MessageDigest.getInstance(name); > } catch (NoSuchAlgorithmException nsae) { > throw Py.ValueError("unsupported hash type"); > @@ -186,8 +190,7 @@ > string = obj.toString(); > } else if (obj instanceof PyArray) { > string = ((PyArray)obj).tostring(); > - } > - else { > + } else { > throw Py.TypeError("update() argument 1 must be string or read-only buffer, not " > + obj.getType().fastGetName()); > } > > -- > Repository URL: http://hg.python.org/jython > _______________________________________________ > Jython-checkins mailing list > Jython-checkins at python.org > http://mail.python.org/mailman/listinfo/jython-checkins -- Philip Jenvey From fwierzbicki at gmail.com Sat May 12 03:19:56 2012 From: fwierzbicki at gmail.com (fwierzbicki at gmail.com) Date: Fri, 11 May 2012 18:19:56 -0700 Subject: [Jython-checkins] jython: Fixes #1880 Sha 224 library not present in Jython, thanks Guarav Raje! In-Reply-To: <4F187BBD-B45A-4A90-82B6-9D828D333EB7@underboss.org> References: <4F187BBD-B45A-4A90-82B6-9D828D333EB7@underboss.org> Message-ID: On Fri, May 11, 2012 at 5:46 PM, Philip Jenvey wrote: > Cool. Though it'd be nice to have this on 2.5 Oh right, how silly of me -- I'll back port it. -Frank From jython-checkins at python.org Tue May 15 04:06:15 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Tue, 15 May 2012 04:06:15 +0200 Subject: [Jython-checkins] =?utf8?q?jython_=282=2E5=29=3A_=231871_Relative?= =?utf8?q?_import_in_module_gets_imported_to_top_level_=28regression=29?= Message-ID: http://hg.python.org/jython/rev/207e3db21d5b changeset: 6651:207e3db21d5b branch: 2.5 parent: 6648:c62b1a8fa99b user: Frank Wierzbicki date: Mon May 14 18:44:09 2012 -0700 summary: #1871 Relative import in module gets imported to top level (regression) files: Lib/test/test_pkg_jy.py | 104 +++++++++++++++++++++++ NEWS | 1 + src/org/python/core/imp.java | 5 + 3 files changed, 110 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_pkg_jy.py b/Lib/test/test_pkg_jy.py new file mode 100644 --- /dev/null +++ b/Lib/test/test_pkg_jy.py @@ -0,0 +1,104 @@ +# Test packages (dotted-name import) + +# XXX: This test is borrowed from CPython 2.7 as it tickles +# http://bugs.jython.org/issue1871 so it should be removed in Jython 2.7 +import sys +import os +import tempfile +import textwrap +import unittest +from test import test_support + + +# Helpers to create and destroy hierarchies. + +def cleanout(root): + names = os.listdir(root) + for name in names: + fullname = os.path.join(root, name) + if os.path.isdir(fullname) and not os.path.islink(fullname): + cleanout(fullname) + else: + os.remove(fullname) + os.rmdir(root) + +def fixdir(lst): + if "__builtins__" in lst: + lst.remove("__builtins__") + return lst + + +class Test(unittest.TestCase): + + def setUp(self): + self.root = None + self.pkgname = None + self.syspath = list(sys.path) + + def tearDown(self): + sys.path[:] = self.syspath + if self.root: # Only clean if the test was actually run + cleanout(self.root) + + # delete all modules concerning the tested hierarchy + if self.pkgname: + modules = [name for name in sys.modules + if self.pkgname in name.split('.')] + for name in modules: + del sys.modules[name] + + def run_code(self, code): + exec(textwrap.dedent(code), globals(), {"self": self}) + + def mkhier(self, descr): + root = tempfile.mkdtemp() + sys.path.insert(0, root) + if not os.path.isdir(root): + os.mkdir(root) + for name, contents in descr: + comps = name.split() + fullname = root + for c in comps: + fullname = os.path.join(fullname, c) + if contents is None: + os.mkdir(fullname) + else: + f = open(fullname, "w") + f.write(contents) + if contents and contents[-1] != '\n': + f.write('\n') + f.close() + self.root = root + # package name is the name of the first item + self.pkgname = descr[0][0] + + def test_5(self): + hier = [ + ("t5", None), + ("t5 __init__"+os.extsep+"py", "import t5.foo"), + ("t5 string"+os.extsep+"py", "spam = 1"), + ("t5 foo"+os.extsep+"py", + "from . import string; assert string.spam == 1"), + ] + self.mkhier(hier) + + import t5 + s = """ + from t5 import * + self.assertEqual(dir(), ['foo', 'self', 'string', 't5']) + """ + self.run_code(s) + + import t5 + self.assertEqual(fixdir(dir(t5)), + ['__doc__', '__file__', '__name__', + '__path__', 'foo', 'string', 't5']) + self.assertEqual(fixdir(dir(t5.foo)), + ['__doc__', '__file__', '__name__', + 'string']) + self.assertEqual(fixdir(dir(t5.string)), + ['__doc__', '__file__', '__name__', + 'spam']) + +if __name__ == "__main__": + unittest.main() diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -5,6 +5,7 @@ Jython 2.5.3b2 Bugs Fixed + - [ 1871 ] Relative import in module gets imported to top level (regression) - [ 1854 ] set().pop() race condition - [ 1730 ] functools.partial incorrectly makes __doc__ property readonly - [ 1537 ] expat: org.python.apache.xerces.parsers.SAXParser diff --git a/src/org/python/core/imp.java b/src/org/python/core/imp.java --- a/src/org/python/core/imp.java +++ b/src/org/python/core/imp.java @@ -862,6 +862,11 @@ return; } + //This can happen with imports like "from . import foo" + if (name.length() == 0) { + name = mod.__findattr__("__name__").toString(); + } + StringBuilder modNameBuffer = new StringBuilder(name); for (PyObject item : fromlist.asIterable()) { if (!Py.isInstance(item, PyString.TYPE)) { -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Tue May 15 04:06:16 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Tue, 15 May 2012 04:06:16 +0200 Subject: [Jython-checkins] =?utf8?q?jython_=28merge_2=2E5_-=3E_default=29?= =?utf8?q?=3A_Merge_from_2=2E5=2E?= Message-ID: http://hg.python.org/jython/rev/9eed5af3291a changeset: 6652:9eed5af3291a parent: 6650:88ee96ae3c05 parent: 6651:207e3db21d5b user: Frank Wierzbicki date: Mon May 14 18:45:34 2012 -0700 summary: Merge from 2.5. files: Lib/os.py | 14 ++ Lib/test/test_pkg_jy.py | 104 +++++++++++++++ NEWS | 1 + src/org/python/core/imp.java | 5 + src/org/python/modules/posix/OS.java | 2 + 5 files changed, 126 insertions(+), 0 deletions(-) diff --git a/Lib/os.py b/Lib/os.py --- a/Lib/os.py +++ b/Lib/os.py @@ -114,6 +114,20 @@ __all__.extend(_get_exports_list(riscos)) del riscos +elif 'ibmi' in _names: + _name = 'ibmi' + linesep = '\n' + from ibmi import * + try: + from ibmi import _exit + except ImportError: + pass + import posixpath as path + + import ibmi + __all__.extend(_get_exports_list(ibmi)) + del ibmi + else: raise ImportError, 'no os specific module found' diff --git a/Lib/test/test_pkg_jy.py b/Lib/test/test_pkg_jy.py new file mode 100644 --- /dev/null +++ b/Lib/test/test_pkg_jy.py @@ -0,0 +1,104 @@ +# Test packages (dotted-name import) + +# XXX: This test is borrowed from CPython 2.7 as it tickles +# http://bugs.jython.org/issue1871 so it should be removed in Jython 2.7 +import sys +import os +import tempfile +import textwrap +import unittest +from test import test_support + + +# Helpers to create and destroy hierarchies. + +def cleanout(root): + names = os.listdir(root) + for name in names: + fullname = os.path.join(root, name) + if os.path.isdir(fullname) and not os.path.islink(fullname): + cleanout(fullname) + else: + os.remove(fullname) + os.rmdir(root) + +def fixdir(lst): + if "__builtins__" in lst: + lst.remove("__builtins__") + return lst + + +class Test(unittest.TestCase): + + def setUp(self): + self.root = None + self.pkgname = None + self.syspath = list(sys.path) + + def tearDown(self): + sys.path[:] = self.syspath + if self.root: # Only clean if the test was actually run + cleanout(self.root) + + # delete all modules concerning the tested hierarchy + if self.pkgname: + modules = [name for name in sys.modules + if self.pkgname in name.split('.')] + for name in modules: + del sys.modules[name] + + def run_code(self, code): + exec(textwrap.dedent(code), globals(), {"self": self}) + + def mkhier(self, descr): + root = tempfile.mkdtemp() + sys.path.insert(0, root) + if not os.path.isdir(root): + os.mkdir(root) + for name, contents in descr: + comps = name.split() + fullname = root + for c in comps: + fullname = os.path.join(fullname, c) + if contents is None: + os.mkdir(fullname) + else: + f = open(fullname, "w") + f.write(contents) + if contents and contents[-1] != '\n': + f.write('\n') + f.close() + self.root = root + # package name is the name of the first item + self.pkgname = descr[0][0] + + def test_5(self): + hier = [ + ("t5", None), + ("t5 __init__"+os.extsep+"py", "import t5.foo"), + ("t5 string"+os.extsep+"py", "spam = 1"), + ("t5 foo"+os.extsep+"py", + "from . import string; assert string.spam == 1"), + ] + self.mkhier(hier) + + import t5 + s = """ + from t5 import * + self.assertEqual(dir(), ['foo', 'self', 'string', 't5']) + """ + self.run_code(s) + + import t5 + self.assertEqual(fixdir(dir(t5)), + ['__doc__', '__file__', '__name__', + '__path__', 'foo', 'string', 't5']) + self.assertEqual(fixdir(dir(t5.foo)), + ['__doc__', '__file__', '__name__', + 'string']) + self.assertEqual(fixdir(dir(t5.string)), + ['__doc__', '__file__', '__name__', + 'spam']) + +if __name__ == "__main__": + unittest.main() diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -6,6 +6,7 @@ Jython 2.5.3b2 Bugs Fixed + - [ 1871 ] Relative import in module gets imported to top level (regression) - [ 1854 ] set().pop() race condition - [ 1730 ] functools.partial incorrectly makes __doc__ property readonly - [ 1537 ] expat: org.python.apache.xerces.parsers.SAXParser diff --git a/src/org/python/core/imp.java b/src/org/python/core/imp.java --- a/src/org/python/core/imp.java +++ b/src/org/python/core/imp.java @@ -864,6 +864,11 @@ return; } + //This can happen with imports like "from . import foo" + if (name.length() == 0) { + name = mod.__findattr__("__name__").toString(); + } + StringBuilder modNameBuffer = new StringBuilder(name); for (PyObject item : fromlist.asIterable()) { if (!Py.isInstance(item, PyString.TYPE)) { diff --git a/src/org/python/modules/posix/OS.java b/src/org/python/modules/posix/OS.java --- a/src/org/python/modules/posix/OS.java +++ b/src/org/python/modules/posix/OS.java @@ -9,6 +9,8 @@ */ enum OS { NT("Windows", new String[] {"cmd.exe", "/c"}, new String[] {"command.com", "/c"}), + // http://bugs.jython.org/issue1842 + IBMi("OS/400", new String[] {"/QOpenSys/usr/bin/sh", "-c"}), POSIX(new String[] {"/bin/sh", "-c"}); /** An array of potential shell commands this platform may use. */ -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Tue May 15 04:11:32 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Tue, 15 May 2012 04:11:32 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_This_test_is_already_in_test?= =?utf8?q?=5Fpkg_=28it_was_backported_to_2=2E5_to_test_a_bug=29=2E?= Message-ID: http://hg.python.org/jython/rev/81637b430a3f changeset: 6653:81637b430a3f user: Frank Wierzbicki date: Mon May 14 19:11:25 2012 -0700 summary: This test is already in test_pkg (it was backported to 2.5 to test a bug). files: Lib/test/test_pkg_jy.py | 104 ---------------------------- 1 files changed, 0 insertions(+), 104 deletions(-) diff --git a/Lib/test/test_pkg_jy.py b/Lib/test/test_pkg_jy.py deleted file mode 100644 --- a/Lib/test/test_pkg_jy.py +++ /dev/null @@ -1,104 +0,0 @@ -# Test packages (dotted-name import) - -# XXX: This test is borrowed from CPython 2.7 as it tickles -# http://bugs.jython.org/issue1871 so it should be removed in Jython 2.7 -import sys -import os -import tempfile -import textwrap -import unittest -from test import test_support - - -# Helpers to create and destroy hierarchies. - -def cleanout(root): - names = os.listdir(root) - for name in names: - fullname = os.path.join(root, name) - if os.path.isdir(fullname) and not os.path.islink(fullname): - cleanout(fullname) - else: - os.remove(fullname) - os.rmdir(root) - -def fixdir(lst): - if "__builtins__" in lst: - lst.remove("__builtins__") - return lst - - -class Test(unittest.TestCase): - - def setUp(self): - self.root = None - self.pkgname = None - self.syspath = list(sys.path) - - def tearDown(self): - sys.path[:] = self.syspath - if self.root: # Only clean if the test was actually run - cleanout(self.root) - - # delete all modules concerning the tested hierarchy - if self.pkgname: - modules = [name for name in sys.modules - if self.pkgname in name.split('.')] - for name in modules: - del sys.modules[name] - - def run_code(self, code): - exec(textwrap.dedent(code), globals(), {"self": self}) - - def mkhier(self, descr): - root = tempfile.mkdtemp() - sys.path.insert(0, root) - if not os.path.isdir(root): - os.mkdir(root) - for name, contents in descr: - comps = name.split() - fullname = root - for c in comps: - fullname = os.path.join(fullname, c) - if contents is None: - os.mkdir(fullname) - else: - f = open(fullname, "w") - f.write(contents) - if contents and contents[-1] != '\n': - f.write('\n') - f.close() - self.root = root - # package name is the name of the first item - self.pkgname = descr[0][0] - - def test_5(self): - hier = [ - ("t5", None), - ("t5 __init__"+os.extsep+"py", "import t5.foo"), - ("t5 string"+os.extsep+"py", "spam = 1"), - ("t5 foo"+os.extsep+"py", - "from . import string; assert string.spam == 1"), - ] - self.mkhier(hier) - - import t5 - s = """ - from t5 import * - self.assertEqual(dir(), ['foo', 'self', 'string', 't5']) - """ - self.run_code(s) - - import t5 - self.assertEqual(fixdir(dir(t5)), - ['__doc__', '__file__', '__name__', - '__path__', 'foo', 'string', 't5']) - self.assertEqual(fixdir(dir(t5.foo)), - ['__doc__', '__file__', '__name__', - 'string']) - self.assertEqual(fixdir(dir(t5.string)), - ['__doc__', '__file__', '__name__', - 'spam']) - -if __name__ == "__main__": - unittest.main() -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Tue May 15 05:01:37 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Tue, 15 May 2012 05:01:37 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Add_support_for_the_dictiona?= =?utf8?q?ry_views?= Message-ID: http://hg.python.org/jython/rev/079316207094 changeset: 6654:079316207094 user: Darjus Loktevic date: Wed May 09 23:57:33 2012 -0700 summary: Add support for the dictionary views http://docs.python.org/dev/whatsnew/2.7.html#pep-3106-dictionary-views files: CoreExposed.includes | 3 + src/org/python/core/BaseDictionaryView.java | 165 +++++ src/org/python/core/PyDictionary.java | 287 +++++++++- 3 files changed, 454 insertions(+), 1 deletions(-) diff --git a/CoreExposed.includes b/CoreExposed.includes --- a/CoreExposed.includes +++ b/CoreExposed.includes @@ -13,6 +13,9 @@ org/python/core/PyComplex.class org/python/core/PyDataDescr.class org/python/core/PyDictionary.class +org/python/core/PyDictionary$PyDictionaryViewValues.class +org/python/core/PyDictionary$PyDictionaryViewKeys.class +org/python/core/PyDictionary$PyDictionaryViewItems.class org/python/core/PyDictProxy.class org/python/core/PyEnumerate.class org/python/core/PyEllipsis.class diff --git a/src/org/python/core/BaseDictionaryView.java b/src/org/python/core/BaseDictionaryView.java new file mode 100644 --- /dev/null +++ b/src/org/python/core/BaseDictionaryView.java @@ -0,0 +1,165 @@ +package org.python.core; + +/* + * See these for details on the dictionary views + * http://docs.python.org/dev/whatsnew/2.7.html#pep-3106-dictionary-views + * http://hg.python.org/cpython/rev/d9805a96351c + */ + +import java.util.Iterator; +import org.python.core.PyObject; + +public abstract class BaseDictionaryView extends PyObject { + protected final PyDictionary dvDict; + + public BaseDictionaryView(PyDictionary dvDict) { + this.dvDict = dvDict; + } + + final boolean allContainedIn(PyObject self, PyObject other) { + for (PyObject ob_value: self.asIterable()) { + if (!other.__contains__(ob_value)) { + return false; + } + } + return true; + } + + static final boolean isSetDictViewInstance(PyObject otherObj) { + if (otherObj instanceof BaseSet || otherObj instanceof BaseDictionaryView) { + return true; + } + return false; + } + + public int __len__() { + return dict_view___len__(); + } + + final int dict_view___len__() { + return dvDict.getMap().size(); + } + + public PyObject __eq__(PyObject otherObj) { + return dict_view___eq__(otherObj); + } + + final PyObject dict_view___eq__(PyObject otherObj) { + if (!isSetDictViewInstance(otherObj)) { + return Py.False; + } + + if (this.__len__() != otherObj.__len__()) { + return Py.False; + } + + if (!allContainedIn(this, otherObj)) { + return Py.False; + } + + return Py.True; + } + + public PyObject __ne__(PyObject otherObj) { + return dict_view___ne__(otherObj); + } + + final PyObject dict_view___ne__(PyObject otherObj) { + if (dict_view___eq__(otherObj) == Py.True) { + return Py.False; + } + return Py.True; + } + + public PyObject __lt__(PyObject otherObj) { + return dict_view___lt__(otherObj); + } + + final PyObject dict_view___lt__(PyObject otherObj) { + if (!isSetDictViewInstance(otherObj)) { + return Py.False; + } + + if (this.__len__() < otherObj.__len__()) { + if (allContainedIn(this, otherObj)) { + return Py.False; + } + } + return Py.True; + } + + public PyObject __le__(PyObject otherObj) { + return dict_view___le__(otherObj); + } + + final PyObject dict_view___le__(PyObject otherObj) { + if (!isSetDictViewInstance(otherObj)) { + return Py.False; + } + + if (this.__len__() <= otherObj.__len__()) { + if (allContainedIn(this, otherObj)) { + return Py.False; + } + } + return Py.True; + } + + public PyObject __gt__(PyObject otherObj) { + return dict_view___gt__(otherObj); + } + + final PyObject dict_view___gt__(PyObject otherObj) { + if (!isSetDictViewInstance(otherObj)) { + return Py.False; + } + + if (this.__len__() > otherObj.__len__()) { + if (allContainedIn(otherObj, this)) { + return Py.False; + } + } + return Py.True; + } + + public PyObject __ge__(PyObject otherObj) { + return dict_view___ge__(otherObj); + } + + final PyObject dict_view___ge__(PyObject otherObj) { + if (!isSetDictViewInstance(otherObj)) { + return Py.False; + } + + if (this.__len__() >= otherObj.__len__()) { + if (allContainedIn(otherObj, this)) { + return Py.False; + } + } + return Py.True; + } + + public String toString() { + return dict_view_toString(); + } + + final String dict_view_toString() { + String name = getType().fastGetName(); + + ThreadState ts = Py.getThreadState(); + if (!ts.enterRepr(this)) { + return name + "([])"; + } + + StringBuilder buf = new StringBuilder(name).append("(["); + for (Iterator i = this.asIterable().iterator(); i.hasNext();) { + buf.append((i.next()).__repr__().toString()); + if (i.hasNext()) { + buf.append(", "); + } + } + buf.append("])"); + ts.exitRepr(this); + return buf.toString(); + } +} diff --git a/src/org/python/core/PyDictionary.java b/src/org/python/core/PyDictionary.java --- a/src/org/python/core/PyDictionary.java +++ b/src/org/python/core/PyDictionary.java @@ -21,6 +21,7 @@ import org.python.expose.ExposedType; import org.python.expose.MethodType; import org.python.util.Generic; +import org.python.core.BaseDictionaryView; /** @@ -693,7 +694,31 @@ public PyObject itervalues() { return dict_itervalues(); } - + + /** + * Returns a dict_keys on the dictionary's keys + */ + @ExposedMethod(doc = BuiltinDocs.dict_viewkeys_doc) + public PyObject viewkeys() { + return new PyDictionaryViewKeys(this); + } + + /** + * Returns a dict_items on the dictionary's items + */ + @ExposedMethod(doc = BuiltinDocs.dict_viewitems_doc) + public PyObject viewitems() { + return new PyDictionaryViewItems(this); + } + + /** + * Returns a dict_values on the dictionary's values + */ + @ExposedMethod(doc = BuiltinDocs.dict_viewvalues_doc) + public PyObject viewvalues() { + return new PyDictionaryViewValues(this); + } + @ExposedMethod(doc = BuiltinDocs.dict_itervalues_doc) final PyObject dict_itervalues() { return new ValuesIter(getMap().values()); @@ -781,6 +806,266 @@ } } + @ExposedType(name = "dict_values", base = PyObject.class, doc = "") + class PyDictionaryViewValues extends BaseDictionaryView { + public final PyType TYPE = PyType.fromClass(PyDictionaryViewValues.class); + + public PyDictionaryViewValues(PyDictionary dvDict) { + super(dvDict); + } + + @Override + public PyObject __iter__() { + return dict_values___iter__(); + } + + @ExposedMethod(doc = BuiltinDocs.set___iter___doc) + final PyObject dict_values___iter__() { + return new ValuesIter(dvDict.getMap().values()); + } + + @ExposedMethod(doc = BuiltinDocs.set___len___doc) + final int dict_values___len__() { + return dict_view___len__(); + } + + @ExposedMethod(names = {"__repr__", "__str__"}, doc = BuiltinDocs.set___str___doc) + final String dict_values_toString() { + return dict_view_toString(); + } + } + + @ExposedType(name = "dict_keys", base = PyObject.class) + class PyDictionaryViewKeys extends BaseDictionaryView { + public final PyType TYPE = PyType.fromClass(PyDictionaryViewKeys.class); + + public PyDictionaryViewKeys(PyDictionary dvDict) { + super(dvDict); + } + + @Override + public PyObject __iter__() { + return dict_keys___iter__(); + } + + @ExposedMethod(doc = BuiltinDocs.set___iter___doc) + final PyObject dict_keys___iter__() { + return new ValuesIter(dvDict.getMap().keySet()); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.set___ne___doc) + final PyObject dict_keys___ne__(PyObject otherObj) { + return dict_view___ne__(otherObj); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.set___eq___doc) + final PyObject dict_keys___eq__(PyObject otherObj) { + return dict_view___eq__(otherObj); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.set___lt___doc) + final PyObject dict_keys___lt__(PyObject otherObj) { + return dict_view___lt__(otherObj); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.set___gt___doc) + final PyObject dict_keys___gt__(PyObject otherObj) { + return dict_view___gt__(otherObj); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.set___ge___doc) + final PyObject dict_keys___ge__(PyObject otherObj) { + return dict_view___ge__(otherObj); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.set___le___doc) + final PyObject dict_keys___le__(PyObject otherObj) { + return dict_view___le__(otherObj); + } + + @Override + public PyObject __or__(PyObject otherObj) { + return dict_keys___or__(otherObj); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.set___or___doc) + final PyObject dict_keys___or__(PyObject otherObj) { + PySet result = new PySet(dvDict); + result.set_update(new PyObject[]{otherObj}, new String[] {}); + return result; + } + + @Override + public PyObject __xor__(PyObject otherObj) { + return dict_keys___xor__(otherObj); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.set___xor___doc) + final PyObject dict_keys___xor__(PyObject otherObj) { + PySet result = new PySet(dvDict); + result.set_symmetric_difference_update(otherObj); + return result; + } + + @Override + public PyObject __sub__(PyObject otherObj) { + return dict_keys___sub__(otherObj); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.set___sub___doc) + final PyObject dict_keys___sub__(PyObject otherObj) { + PySet result = new PySet(dvDict); + result.set_difference_update(new PyObject[]{otherObj}, new String[] {}); + return result; + } + + @Override + public PyObject __and__(PyObject otherObj) { + return dict_keys___and__(otherObj); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.set___and___doc) + final PyObject dict_keys___and__(PyObject otherObj) { + PySet result = new PySet(dvDict); + result.set_intersection_update(new PyObject[]{otherObj}, new String[] {}); + return result; + } + + @Override + public boolean __contains__(PyObject otherObj) { + return dict_keys___contains__(otherObj); + } + + @ExposedMethod(doc = BuiltinDocs.set___contains___doc) + final boolean dict_keys___contains__(PyObject item) { + return dvDict.__contains__(item); + } + + @ExposedMethod(names = "__repr__", doc = BuiltinDocs.set___repr___doc) + final String dict_keys_toString() { + return dict_view_toString(); + } + } + + @ExposedType(name = "dict_items") + class PyDictionaryViewItems extends BaseDictionaryView { + public final PyType TYPE = PyType.fromClass(PyDictionaryViewItems.class); + + public PyDictionaryViewItems(PyDictionary dvDict) { + super(dvDict); + } + + @Override + public PyObject __iter__() { + return dict_items___iter__(); + } + + @ExposedMethod(doc = BuiltinDocs.set___iter___doc) + final PyObject dict_items___iter__() { + return new ItemsIter(dvDict.getMap().entrySet()); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.set___ne___doc) + final PyObject dict_items___ne__(PyObject otherObj) { + return dict_view___ne__(otherObj); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.set___eq___doc) + final PyObject dict_items___eq__(PyObject otherObj) { + return dict_view___eq__(otherObj); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.set___lt___doc) + final PyObject dict_items___lt__(PyObject otherObj) { + return dict_view___lt__(otherObj); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.set___gt___doc) + final PyObject dict_items___gt__(PyObject otherObj) { + return dict_view___gt__(otherObj); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.set___ge___doc) + final PyObject dict_items___ge__(PyObject otherObj) { + return dict_view___ge__(otherObj); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.set___le___doc) + final PyObject dict_items___le__(PyObject otherObj) { + return dict_view___le__(otherObj); + } + + @Override + public PyObject __or__(PyObject otherObj) { + return dict_items___or__(otherObj); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.set___or___doc) + final PyObject dict_items___or__(PyObject otherObj) { + PySet result = new PySet(dvDict.iteritems()); + result.set_update(new PyObject[]{otherObj}, new String[] {}); + return result; + } + + @Override + public PyObject __xor__(PyObject otherObj) { + return dict_items___xor__(otherObj); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.set___xor___doc) + final PyObject dict_items___xor__(PyObject otherObj) { + PySet result = new PySet(dvDict.iteritems()); + result.set_symmetric_difference_update(otherObj); + return result; + } + + @Override + public PyObject __sub__(PyObject otherObj) { + return dict_items___sub__(otherObj); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.set___sub___doc) + final PyObject dict_items___sub__(PyObject otherObj) { + PySet result = new PySet(dvDict.iteritems()); + result.set_difference_update(new PyObject[]{otherObj}, new String[] {}); + return result; + } + + @Override + public PyObject __and__(PyObject otherObj) { + return dict_items___and__(otherObj); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.set___and___doc) + final PyObject dict_items___and__(PyObject otherObj) { + PySet result = new PySet(dvDict.iteritems()); + result.set_intersection_update(new PyObject[]{otherObj}, new String[] {}); + return result; + } + + @Override + public boolean __contains__(PyObject otherObj) { + return dict_items___contains__(otherObj); + } + + @ExposedMethod(doc = BuiltinDocs.set___contains___doc) + final boolean dict_items___contains__(PyObject item) { + if (item instanceof PyTuple) { + PyTuple tupleItem = (PyTuple)item; + if (tupleItem.size() == 2) { + SimpleEntry entry = new SimpleEntry(tupleItem.get(0), tupleItem.get(1)); + return dvDict.entrySet().contains(entry); + } + } + return false; + } + + @ExposedMethod(names = "__repr__", doc = BuiltinDocs.set___repr___doc) + final String dict_keys_toString() { + return dict_view_toString(); + } + } + /* * The following methods implement the java.util.Map interface which allows PyDictionary to be * passed to java methods that take java.util.Map as a parameter. Basically, the Map methods are -- Repository URL: http://hg.python.org/jython From jython-dev at xhaus.com Tue May 15 12:57:29 2012 From: jython-dev at xhaus.com (Alan Kennedy) Date: Tue, 15 May 2012 11:57:29 +0100 Subject: [Jython-checkins] jython (merge 2.5 -> default): Merge from 2.5. In-Reply-To: References: Message-ID: Hi Frank, [frank.wierzbicki] > diff --git a/Lib/os.py b/Lib/os.py > --- a/Lib/os.py > +++ b/Lib/os.py > @@ -114,6 +114,20 @@ > ? ? __all__.extend(_get_exports_list(riscos)) > ? ? del riscos > > +elif 'ibmi' in _names: > + ? ?_name = 'ibmi' > + ? ?linesep = '\n' > + ? ?from ibmi import * > + ? ?try: > + ? ? ? ?from ibmi import _exit > + ? ?except ImportError: > + ? ? ? ?pass > + ? ?import posixpath as path > + > + ? ?import ibmi > + ? ?__all__.extend(_get_exports_list(ibmi)) > + ? ?del ibmi > + > ?else: > ? ? raise ImportError, 'no os specific module found' I was holding off on merging this up from 2.5: I'd prefer to get feedback from the IBM people before propagating it. I don't have any capability to run the code to verify its right-or-wrong'ness. Alan. From jython-checkins at python.org Wed May 16 05:41:29 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Wed, 16 May 2012 05:41:29 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_PEP_366=2E?= Message-ID: http://hg.python.org/jython/rev/9462896d4a0b changeset: 6655:9462896d4a0b user: Frank Wierzbicki date: Tue May 15 20:40:38 2012 -0700 summary: PEP 366. files: src/org/python/core/imp.java | 53 +++++++++++++++-------- 1 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/org/python/core/imp.java b/src/org/python/core/imp.java --- a/src/org/python/core/imp.java +++ b/src/org/python/core/imp.java @@ -658,30 +658,47 @@ * @return the parent name for a module */ private static String get_parent(PyObject dict, int level) { + String modname; if (dict == null || level == 0) { // try an absolute import return null; } - PyObject tmp = dict.__finditem__("__name__"); - if (tmp == null) { - return null; - } - String modname = tmp.toString(); - // locate the current package - tmp = dict.__finditem__("__path__"); - if (! (tmp instanceof PyList)) { - // __name__ is not a package name, try one level upwards. - int dot = modname.lastIndexOf('.'); - if (dot == -1) { - if (level <= -1) { - // there is no package, perform an absolute search - return null; - } - throw Py.ValueError("Attempted relative import in non-package"); + PyObject tmp = dict.__finditem__("__package__"); + if (tmp != null && tmp != Py.None) { + if (!Py.isInstance(tmp, PyString.TYPE)) { + throw Py.ValueError("__package__ set to non-string"); } - // modname should be the package name. - modname = modname.substring(0, dot); + modname = ((PyString)tmp).getString(); + } else { + //__package__ not set, so figure it out and set it. + + tmp = dict.__finditem__("__name__"); + if (tmp == null) { + return null; + } + modname = tmp.toString(); + + // locate the current package + tmp = dict.__finditem__("__path__"); + if (tmp instanceof PyList) { + //__path__ is set, so modname is already the package name. + dict.__setitem__("__package__", new PyString(modname)); + } else { + // __name__ is not a package name, try one level upwards. + int dot = modname.lastIndexOf('.'); + if (dot == -1) { + if (level <= -1) { + // there is no package, perform an absolute search + dict.__setitem__("__package__", Py.None); + return null; + } + throw Py.ValueError("Attempted relative import in non-package"); + } + // modname should be the package name. + modname = modname.substring(0, dot); + dict.__setitem__("__package__", new PyString(modname)); + } } // walk upwards if required (level >= 2) -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Wed May 16 19:27:58 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Wed, 16 May 2012 19:27:58 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Updates_for_alpha_release=2E?= Message-ID: http://hg.python.org/jython/rev/cf741da06d5c changeset: 6656:cf741da06d5c user: Frank Wierzbicki date: Wed May 16 10:27:47 2012 -0700 summary: Updates for alpha release. files: README.txt | 11 +++++++---- build.xml | 10 ++++++---- src/org/python/core/imp.java | 2 +- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/README.txt b/README.txt --- a/README.txt +++ b/README.txt @@ -1,9 +1,12 @@ -Welcome to Jython 2.5.3b1 -======================= +Welcome to Jython 2.7.0a1 +========================= -This is the first beta release of the 2.5.3 version of Jython +This is the first alpha release of the 2.7.0 version of Jython. Thanks to +Adconion Media Group (http://www.adconion.com/) for sponsoring this release. +Thanks to all who contributed to Jython. + Please see the NEWS file for detailed release notes. -The release was compiled on Ubuntu with JDK 6 and requires JDK 5 to run. +The release was compiled on Ubuntu with JDK 6 and requires JDK 6 to run. Please try this out and report any bugs at http://bugs.jython.org. diff --git a/build.xml b/build.xml --- a/build.xml +++ b/build.xml @@ -123,13 +123,15 @@ - - + + - + + @@ -467,7 +469,7 @@ + value='2.7a${svn.revision}' /> ======================= -------------------------- diff --git a/src/org/python/core/imp.java b/src/org/python/core/imp.java --- a/src/org/python/core/imp.java +++ b/src/org/python/core/imp.java @@ -26,7 +26,7 @@ private static final String UNKNOWN_SOURCEFILE = ""; - private static final int APIVersion = 32; + private static final int APIVersion = 33; public static final int NO_MTIME = -1; -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Wed May 16 22:31:26 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Wed, 16 May 2012 22:31:26 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Fix_jarjar_jars=2E?= Message-ID: http://hg.python.org/jython/rev/4f5e3c12edc0 changeset: 6657:4f5e3c12edc0 user: Frank Wierzbicki date: Wed May 16 13:31:12 2012 -0700 summary: Fix jarjar jars. files: build.xml | 22 ++++++++++++---------- 1 files changed, 12 insertions(+), 10 deletions(-) diff --git a/build.xml b/build.xml --- a/build.xml +++ b/build.xml @@ -184,7 +184,6 @@ - @@ -219,10 +218,11 @@ - + + @@ -399,11 +399,14 @@ + + - + @@ -655,13 +658,12 @@ - - - - - - - + + + + + + -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Thu May 17 06:34:10 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Thu, 17 May 2012 06:34:10 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Added_tag_v2=2E7=2E0a1_for_c?= =?utf8?q?hangeset_4f5e3c12edc0?= Message-ID: http://hg.python.org/jython/rev/9fd8c5122bb1 changeset: 6658:9fd8c5122bb1 user: Frank Wierzbicki date: Wed May 16 21:33:41 2012 -0700 summary: Added tag v2.7.0a1 for changeset 4f5e3c12edc0 files: .hgtags | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -58,3 +58,4 @@ c72d5c5c968abac480c0841057797abf2d7f0018 v2.5.2 91332231a44804192e47ca25be334bab8ac8ea7c v2.5.2 c5567b7757e7ff086bdb10006ddbd513a727a803 v2.5.3b1 +4f5e3c12edc040058d724d4469a53e8649e64420 v2.7.0a1 -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Fri May 18 23:25:49 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Fri, 18 May 2012 23:25:49 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Use_invoke_instead_of_=5F=5F?= =?utf8?b?Z2V0YXR0cl9fKCkuX19jYWxsX18oKS4=?= Message-ID: http://hg.python.org/jython/rev/a60a3c1f87a6 changeset: 6659:a60a3c1f87a6 user: Frank Wierzbicki date: Fri May 18 14:25:36 2012 -0700 summary: Use invoke instead of __getattr__().__call__(). files: src/org/python/modules/time/Time.java | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/org/python/modules/time/Time.java b/src/org/python/modules/time/Time.java --- a/src/org/python/modules/time/Time.java +++ b/src/org/python/modules/time/Time.java @@ -688,9 +688,9 @@ */ private static PyTuple pystrptime(String data_string, String format) { return (PyTuple) __builtin__.__import__("_strptime") - .__getattr__("_strptime") - .__call__(Py.newUnicode(data_string), - Py.newUnicode(format)); + .invoke("_strptime", + Py.newUnicode(data_string), + Py.newUnicode(format)); } public static PyTuple strptime(String data_string, String format) { -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Fri May 18 23:37:36 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Fri, 18 May 2012 23:37:36 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Pure_python_version_of_fsum_?= =?utf8?q?for_math_module=2E?= Message-ID: http://hg.python.org/jython/rev/ee12ca8ebf01 changeset: 6660:ee12ca8ebf01 user: Miki Tebeka date: Fri May 18 14:37:25 2012 -0700 summary: Pure python version of fsum for math module. files: Lib/_fsum.py | 36 ++++++++++++++++++++ src/org/python/modules/math.java | 11 +++-- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/Lib/_fsum.py b/Lib/_fsum.py new file mode 100644 --- /dev/null +++ b/Lib/_fsum.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python + +from sys import float_info +import math + + +mant_dig = float_info.mant_dig +etiny = float_info.min_exp - mant_dig + +def fsum(iterable): + """Full precision summation. Compute sum(iterable) without any + intermediate accumulation of error. Based on the 'lsum' function + at http://code.activestate.com/recipes/393090/ + + """ + tmant, texp = 0, 0 + for x in iterable: + mant, exp = math.frexp(x) + mant, exp = int(math.ldexp(mant, mant_dig)), exp - mant_dig + if texp > exp: + tmant <<= texp-exp + texp = exp + else: + mant <<= exp-texp + tmant += mant + + # Round tmant * 2**texp to a float. The original recipe + # used float(str(tmant)) * 2.0**texp for this, but that's + # a little unsafe because str -> float conversion can't be + # relied upon to do correct rounding on all platforms. + tail = max(len(bin(abs(tmant)))-2 - mant_dig, etiny - texp) + if tail > 0: + h = 1 << (tail-1) + tmant = tmant // (2*h) + bool(tmant & h and tmant & 3*h-1) + texp += tail + return math.ldexp(tmant, texp) diff --git a/src/org/python/modules/math.java b/src/org/python/modules/math.java --- a/src/org/python/modules/math.java +++ b/src/org/python/modules/math.java @@ -9,7 +9,9 @@ import org.python.core.PyInteger; import org.python.core.PyLong; import org.python.core.PyObject; +import org.python.core.PySystemState; import org.python.core.PyTuple; +import org.python.core.__builtin__; import org.python.modules.math_erf; public class math implements ClassDictInit { @@ -507,12 +509,13 @@ return log(ONE + v); } - public static double fsum(double... values) { - // TODO need an iterable - // TODO need sys.float_info - return 0; + public static double fsum(final PyObject iterable) { + PyFloat result = (PyFloat)__builtin__.__import__("_fsum") + .invoke("fsum", iterable); + return result.asDouble(); } + private static double calculateLongLog(PyLong v) { int exp[] = new int[1]; double x = v.scaledDoubleValue(exp); -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Sat May 19 05:18:23 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Sat, 19 May 2012 05:18:23 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_gamma/lgamma_-_finishing_the?= =?utf8?q?_majority_of_2=2E7_math!_Thanks_Miki_Tebeka!?= Message-ID: http://hg.python.org/jython/rev/2dd71d4bda11 changeset: 6661:2dd71d4bda11 user: Miki Tebeka date: Fri May 18 20:18:15 2012 -0700 summary: gamma/lgamma - finishing the majority of 2.7 math! Thanks Miki Tebeka! files: src/org/python/modules/math.java | 9 + src/org/python/modules/math_gamma.java | 338 +++++++++++++ 2 files changed, 347 insertions(+), 0 deletions(-) diff --git a/src/org/python/modules/math.java b/src/org/python/modules/math.java --- a/src/org/python/modules/math.java +++ b/src/org/python/modules/math.java @@ -13,6 +13,7 @@ import org.python.core.PyTuple; import org.python.core.__builtin__; import org.python.modules.math_erf; +import org.python.modules.math_gamma; public class math implements ClassDictInit { public static PyFloat pi = new PyFloat(Math.PI); @@ -34,6 +35,14 @@ public static void classDictInit(@SuppressWarnings("unused") PyObject dict) { } + public static double gamma(double v) { + return math_gamma.gamma(v); + } + + public static double lgamma(double v) { + return math_gamma.lgamma(v); + } + public static double erf(double v) { return math_erf.erf(v); } diff --git a/src/org/python/modules/math_gamma.java b/src/org/python/modules/math_gamma.java new file mode 100644 --- /dev/null +++ b/src/org/python/modules/math_gamma.java @@ -0,0 +1,338 @@ +package org.python.modules; +/* This is a translation for the code in Python's mathmodule.c */ + +import org.python.core.Py; +import java.lang.Math; + + +public class math_gamma { +/* + sin(pi*x), giving accurate results for all finite x (especially x + integral or close to an integer). This is here for use in the + reflection formula for the gamma function. It conforms to IEEE + 754-2008 for finite arguments, but not for infinities or nans. +*/ + +static final double pi = 3.141592653589793238462643383279502884197; +static final double sqrtpi = 1.772453850905516027298167483341145182798; + +static double +sinpi(double x) +{ + double y, r; + int n; + + y = Math.abs(x) % 2.0; + n = (int)Math.round(2.0*y); + + assert((0 <= n) && (n <= 4)); + + switch (n) { + case 0: + r = Math.sin(pi*y); + break; + case 1: + r = Math.cos(pi*(y-0.5)); + break; + case 2: + /* N.B. -sin(pi*(y-1.0)) is *not* equivalent: it would give + -0.0 instead of 0.0 when y == 1.0. */ + r = Math.sin(pi*(1.0-y)); + break; + case 3: + r = -Math.cos(pi*(y-1.5)); + break; + case 4: + r = Math.sin(pi*(y-2.0)); + break; + default: + assert(false); /* should never get here */ + r = 3; // Make javac happy + } + + return Math.copySign(1.0, x)*r; +} + +/* Implementation of the real gamma function. In extensive but non-exhaustive + random tests, this function proved accurate to within <= 10 ulps across the + entire float domain. Note that accuracy may depend on the quality of the + system math functions, the pow function in particular. Special cases + follow C99 annex F. The parameters and method are tailored to platforms + whose double format is the IEEE 754 binary64 format. + + Method: for x > 0.0 we use the Lanczos approximation with parameters N=13 + and g=6.024680040776729583740234375; these parameters are amongst those + used by the Boost library. Following Boost (again), we re-express the + Lanczos sum as a rational function, and compute it that way. The + coefficients below were computed independently using MPFR, and have been + double-checked against the coefficients in the Boost source code. + + For x < 0.0 we use the reflection formula. + + There's one minor tweak that deserves explanation: Lanczos' formula for + Gamma(x) involves computing pow(x+g-0.5, x-0.5) / exp(x+g-0.5). For many x + values, x+g-0.5 can be represented exactly. However, in cases where it + can't be represented exactly the small error in x+g-0.5 can be magnified + significantly by the pow and exp calls, especially for large x. A cheap + correction is to multiply by (1 + e*g/(x+g-0.5)), where e is the error + involved in the computation of x+g-0.5 (that is, e = computed value of + x+g-0.5 - exact value of x+g-0.5). Here's the proof: + + Correction factor + ----------------- + Write x+g-0.5 = y-e, where y is exactly representable as an IEEE 754 + double, and e is tiny. Then: + + pow(x+g-0.5,x-0.5)/exp(x+g-0.5) = pow(y-e, x-0.5)/exp(y-e) + = pow(y, x-0.5)/exp(y) * C, + + where the correction_factor C is given by + + C = pow(1-e/y, x-0.5) * exp(e) + + Since e is tiny, pow(1-e/y, x-0.5) ~ 1-(x-0.5)*e/y, and exp(x) ~ 1+e, so: + + C ~ (1-(x-0.5)*e/y) * (1+e) ~ 1 + e*(y-(x-0.5))/y + + But y-(x-0.5) = g+e, and g+e ~ g. So we get C ~ 1 + e*g/y, and + + pow(x+g-0.5,x-0.5)/exp(x+g-0.5) ~ pow(y, x-0.5)/exp(y) * (1 + e*g/y), + + Note that for accuracy, when computing r*C it's better to do + + r + e*g/y*r; + + than + + r * (1 + e*g/y); + + since the addition in the latter throws away most of the bits of + information in e*g/y. +*/ + +static final int LANCZOS_N = 13; +static final double lanczos_g = 6.024680040776729583740234375; +static final double lanczos_g_minus_half = 5.524680040776729583740234375; +static final double[] lanczos_num_coeffs = new double[]{ + 23531376880.410759688572007674451636754734846804940, + 42919803642.649098768957899047001988850926355848959, + 35711959237.355668049440185451547166705960488635843, + 17921034426.037209699919755754458931112671403265390, + 6039542586.3520280050642916443072979210699388420708, + 1439720407.3117216736632230727949123939715485786772, + 248874557.86205415651146038641322942321632125127801, + 31426415.585400194380614231628318205362874684987640, + 2876370.6289353724412254090516208496135991145378768, + 186056.26539522349504029498971604569928220784236328, + 8071.6720023658162106380029022722506138218516325024, + 210.82427775157934587250973392071336271166969580291, + 2.5066282746310002701649081771338373386264310793408 +}; + +/* denominator is x*(x+1)*...*(x+LANCZOS_N-2) */ +static final double[] lanczos_den_coeffs = new double[]{ + 0.0, 39916800.0, 120543840.0, 150917976.0, 105258076.0, 45995730.0, + 13339535.0, 2637558.0, 357423.0, 32670.0, 1925.0, 66.0, 1.0}; + +/* gamma values for small positive integers, 1 though NGAMMA_INTEGRAL */ +static final int NGAMMA_INTEGRAL = 23; +static final double[] gamma_integral = new double[]{ + 1.0, 1.0, 2.0, 6.0, 24.0, 120.0, 720.0, 5040.0, 40320.0, 362880.0, + 3628800.0, 39916800.0, 479001600.0, 6227020800.0, 87178291200.0, + 1307674368000.0, 20922789888000.0, 355687428096000.0, + 6402373705728000.0, 121645100408832000.0, 2432902008176640000.0, + 51090942171709440000.0, 1124000727777607680000.0, +}; + +/* Lanczos' sum L_g(x), for positive x */ + +static double +lanczos_sum(double x) +{ + double num = 0.0, den = 0.0; + int i; + + assert(x > 0.0); + + /* evaluate the rational function lanczos_sum(x). For large + x, the obvious algorithm risks overflow, so we instead + rescale the denominator and numerator of the rational + function by x**(1-LANCZOS_N) and treat this as a + rational function in 1/x. This also reduces the error for + larger x values. The choice of cutoff point (5.0 below) is + somewhat arbitrary; in tests, smaller cutoff values than + this resulted in lower accuracy. */ + if (x < 5.0) { + for (i = LANCZOS_N; --i >= 0; ) { + num = num * x + lanczos_num_coeffs[i]; + den = den * x + lanczos_den_coeffs[i]; + } + } + else { + for (i = 0; i < LANCZOS_N; i++) { + num = num / x + lanczos_num_coeffs[i]; + den = den / x + lanczos_den_coeffs[i]; + } + } + return num/den; +} + +public static double +gamma(double x) +{ + double absx, r, y, z, sqrtpow; + + if (Double.isNaN(x)) { + return x; + } + + /* special cases */ + if (Double.isInfinite(x)) { + if (x > 0.0) { + return x; + } + + throw Py.ValueError("math domain error"); + } + + if (x == 0.0) { + throw Py.ValueError("math domain error"); + } + + /* integer arguments */ + if (x == Math.floor(x)) { + if (x < 0.0) { + throw Py.ValueError("math domain error"); + } + if (x <= NGAMMA_INTEGRAL) { + return gamma_integral[(int)x - 1]; + } + } + absx = Math.abs(x); + + /* tiny arguments: tgamma(x) ~ 1/x for x near 0 */ + if (absx < 1e-20) { + r = 1.0/x; + if (Double.isInfinite(r)) { + throw Py.OverflowError("math range error"); + } + return r; + } + + /* large arguments: assuming IEEE 754 doubles, tgamma(x) overflows for + x > 200, and underflows to +-0.0 for x < -200, not a negative + integer. */ + if (absx > 200.0) { + if (x < 0.0) { + return 0.0/sinpi(x); + } + else { + throw Py.OverflowError("math range error"); + } + } + + y = absx + lanczos_g_minus_half; + /* compute error in sum */ + if (absx > lanczos_g_minus_half) { + /* note: the correction can be foiled by an optimizing + compiler that (incorrectly) thinks that an expression like + a + b - a - b can be optimized to 0.0. This shouldn't + happen in a standards-conforming compiler. */ + double q = y - absx; + z = q - lanczos_g_minus_half; + } + else { + double q = y - lanczos_g_minus_half; + z = q - absx; + } + z = z * lanczos_g / y; + if (x < 0.0) { + r = -pi / sinpi(absx) / absx * Math.exp(y) / lanczos_sum(absx); + r -= z * r; + if (absx < 140.0) { + r /= Math.pow(y, absx - 0.5); + } + else { + sqrtpow = Math.pow(y, absx / 2.0 - 0.25); + r /= sqrtpow; + r /= sqrtpow; + } + } + else { + r = lanczos_sum(absx) / Math.exp(y); + r += z * r; + if (absx < 140.0) { + r *= Math.pow(y, absx - 0.5); + } + else { + sqrtpow = Math.pow(y, absx / 2.0 - 0.25); + r *= sqrtpow; + r *= sqrtpow; + } + } + + if (Double.isInfinite(r)) { + throw Py.OverflowError("math range error"); + } + + return r; +} + +/* + lgamma: natural log of the absolute value of the Gamma function. + For large arguments, Lanczos' formula works extremely well here. +*/ + +public static double +lgamma(double x) +{ + double r, absx; + + /* special cases */ + if (Double.isNaN(x)) { + return x; + } + if (Double.isInfinite(x)) { + /* lgamma(+-inf) = +inf */ + return Double.POSITIVE_INFINITY; + } + + /* integer arguments */ + if (x == Math.floor(x) && x <= 2.0) { + if (x <= 0.0) { + throw Py.ValueError("math domain error"); + } + else { + return 0.0; /* lgamma(1) = lgamma(2) = 0.0 */ + } + } + + absx = Math.abs(x); + /* tiny arguments: lgamma(x) ~ -log(fabs(x)) for small x */ + if (absx < 1e-20) { + return -Math.log(absx); + } + + /* Lanczos' formula */ + if (x > 0.0) { + /* we could save a fraction of a ulp in accuracy by having a + second set of numerator coefficients for lanczos_sum that + absorbed the exp(-lanczos_g) term, and throwing out the + lanczos_g subtraction below; it's probably not worth it. */ + r = Math.log(lanczos_sum(x)) - lanczos_g + + (x-0.5)*(Math.log(x+lanczos_g-0.5)-1); + } + else { + r = Math.log(pi) - Math.log(Math.abs(sinpi(absx))) - Math.log(absx) - + (Math.log(lanczos_sum(absx)) - lanczos_g + + (absx-0.5)*(Math.log(absx+lanczos_g-0.5)-1)); + } + + if (Double.isInfinite(r)) { + throw Py.OverflowError("math range error"); + } + + return r; +} + +} -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Tue May 22 17:52:30 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Tue, 22 May 2012 17:52:30 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_=231892_site-packages_is_not?= =?utf8?q?_in_sys=2Epath?= Message-ID: http://hg.python.org/jython/rev/5896873707bc changeset: 6662:5896873707bc user: Frank Wierzbicki date: Tue May 22 08:52:20 2012 -0700 summary: #1892 site-packages is not in sys.path Thanks for the bug report Stephane! files: Lib/site.py | 2 +- Lib/test/test_site.py | 2 +- NEWS | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Lib/site.py b/Lib/site.py --- a/Lib/site.py +++ b/Lib/site.py @@ -65,7 +65,7 @@ import platform as _platform -_is_jython = _platform.python_implementation == "Jython" +_is_jython = _platform.python_implementation() == "Jython" if _is_jython: _ModuleType = type(os) diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -227,7 +227,7 @@ site.PREFIXES = ['xoxo'] dirs = site.getsitepackages() - if sys.platform in ('os2emx', 'riscos'): + if sys.platform in ('os2emx', 'riscos') or is_jython: self.assertEqual(len(dirs), 1) wanted = os.path.join('xoxo', 'Lib', 'site-packages') self.assertEqual(dirs[0], wanted) diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -1,5 +1,8 @@ Jython NEWS +Jython 2.7a2 + - [ 1892 ] site-packages is not in sys.path + Jython 2.7a1 Bugs Fixed - [ 1880 ] Sha 224 library not present in Jython -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Tue May 22 19:04:19 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Tue, 22 May 2012 19:04:19 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Avoid_importing_platform_and?= =?utf8?q?_use_to_sys=2Eplatform=2Estartswith=28=22java=22=29?= Message-ID: http://hg.python.org/jython/rev/a2cf580d1f2d changeset: 6663:a2cf580d1f2d user: Frank Wierzbicki date: Tue May 22 10:01:30 2012 -0700 summary: Avoid importing platform and use to sys.platform.startswith("java") files: Lib/SimpleHTTPServer.py | 3 +-- Lib/site.py | 4 +--- Lib/tempfile.py | 7 +------ 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/Lib/SimpleHTTPServer.py b/Lib/SimpleHTTPServer.py --- a/Lib/SimpleHTTPServer.py +++ b/Lib/SimpleHTTPServer.py @@ -11,7 +11,6 @@ __all__ = ["SimpleHTTPRequestHandler"] import os -import platform import posixpath import BaseHTTPServer import urllib @@ -133,7 +132,7 @@ length = f.tell() f.seek(0) self.send_response(200) - if not platform.python_implementation() == "Jython": + if not sys.platform.startswith("java"): encoding = sys.getfilesystemencoding() self.send_header("Content-type", "text/html; charset=%s" % encoding) self.send_header("Content-Length", str(length)) diff --git a/Lib/site.py b/Lib/site.py --- a/Lib/site.py +++ b/Lib/site.py @@ -63,9 +63,7 @@ import __builtin__ import traceback -import platform as _platform - -_is_jython = _platform.python_implementation() == "Jython" +_is_jython = sys.platform.startswith("java") if _is_jython: _ModuleType = type(os) diff --git a/Lib/tempfile.py b/Lib/tempfile.py --- a/Lib/tempfile.py +++ b/Lib/tempfile.py @@ -33,11 +33,6 @@ import errno as _errno from random import Random as _Random -import platform as _platform - -_is_jython = _platform.python_implementation() == "Jython" - - try: from cStringIO import StringIO as _StringIO except ImportError: @@ -127,7 +122,7 @@ @property def rng(self): - if _is_jython: + if _os.sys.platform.startswith("java"): #A JVM run cannot determine or change its pid so dummy this. cur_pid = 1 else: -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Wed May 23 19:21:27 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Wed, 23 May 2012 19:21:27 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_bytearray=3A_increment_inclu?= =?utf8?q?ding_comparison_operations=2C_=5F=5Fadd=5F=5F=2C_append=2C_find?= Message-ID: http://hg.python.org/jython/rev/f09dfd68d9e4 changeset: 6664:f09dfd68d9e4 user: Jeff Allen date: Fri May 18 09:30:33 2012 +0100 summary: bytearray: increment including comparison operations, __add__, append, find This increment includes _eq__, __ne__ and so on (which makes Python unit test work), and some of the basic Python API. In the absence of a memory buffer equivalent, BaseBytes provides a wrapper that permits bytearray operations to accept str arguments. test_bytes.py currently produces 4 fail and 70 error results. There are also improved JUnit tests to cover slice deletion. files: src/org/python/core/BaseBytes.java | 1200 ++++- src/org/python/core/PyByteArray.java | 1069 ++- src/org/python/core/PyByteArrayDerived.java | 2232 +++++----- tests/java/org/python/core/BaseBytesTest.java | 61 +- tests/java/org/python/core/PyByteArrayTest.java | 921 +++- 5 files changed, 3616 insertions(+), 1867 deletions(-) diff --git a/src/org/python/core/BaseBytes.java b/src/org/python/core/BaseBytes.java --- a/src/org/python/core/BaseBytes.java +++ b/src/org/python/core/BaseBytes.java @@ -9,28 +9,31 @@ /** * Base class for Jython bytearray (and bytes in due course) that provides most of the Java API, - * including Java List behaviour. Attempts to modify the contents through this API will throw - * a TypeError if the actual type of the object is not mutable. + * including Java List behaviour. Attempts to modify the contents through this API will throw a + * TypeError if the actual type of the object is not mutable. *

- * It is possible for a Java client to treat this class as a List<PyInteger>, - * obtaining equivalent functionality to the Python interface in a Java paradigm. - * The reason - * {@link } - *

Subclasses must define (from {@link PySequence}):

    + * It is possible for a Java client to treat this class as a List<PyInteger>, obtaining + * equivalent functionality to the Python interface in a Java paradigm. The reason {@link } + *

    + * Subclasses must define (from {@link PySequence}): + *

      *
    • {@link #getslice(int, int, int)}
    • *
    • {@link #repeat(int)}
    • - *
    each returning an appropriate concrete type. Mutable subclasses should override:
      + *
    + * each returning an appropriate concrete type. Mutable subclasses should override: + *
      *
    • {@link #pyset(int, PyObject)}
    • *
    • {@link #setslice(int, int, int, PyObject)}
    • *
    • {@link #del(int)}
    • *
    • {@link #delRange(int, int)}
    • - *
    + *
* since the default implementations will otherwise throw an exception. */ public abstract class BaseBytes extends PySequence implements MemoryViewProtocol, List { - + /** * Simple constructor of empty zero-length array of defined type. + * * @param type explicit Jython type */ public BaseBytes(PyType type) { @@ -40,17 +43,18 @@ /** * Simple constructor of zero-filled array of defined size and type. + * * @param size required * @param type explicit Jython type */ public BaseBytes(PyType type, int size) { super(type); - newStorage( size ); + newStorage(size); } /** * Construct byte array of defined type by copying values from int[]. - * + * * @param type explicit Jython type * @param value source of values (and size) */ @@ -58,14 +62,15 @@ super(type); int n = value.length; newStorage(n); - for (int i = offset, j = 0; j < n; i++, j++) // Note offset may be non-zero + for (int i = offset, j = 0; j < n; i++, j++) { storage[i] = byteCheck(value[j]); + } } /** * Construct byte array of defined type by copying character values from a String. These values * have to be in the Python byte range 0 to 255. - * + * * @param type explicit Jython type * @param value source of characters * @throws PyException if any value[i] > 255 @@ -74,17 +79,17 @@ super(type); int n = value.length(); newStorage(n); - int i = offset + size; - while (n > 0) - storage[--i] = byteCheck(value.charAt(--n)); - } - + for (int i = offset, j = 0; j < n; j++) { + storage[i++] = byteCheck(value.charAt(j)); + } + } + /** * Helper for constructors and methods that manipulate the storage in mutable subclasses. It * also permits shared storage between objects, which in general is unsafe if the storage is * subject to modification independent of the object now being created. Immutable types may * share storage (safely). - * + * * @param storage byte array allocated by client * @param size number of bytes actually used * @param offset index of first byte used @@ -102,9 +107,9 @@ } /** - * Helper for constructors and methods that manipulate the storage in mutable subclassesin the + * Helper for constructors and methods that manipulate the storage in mutable subclasses in the * case where the storage should consist of the first part of the given array. - * + * * @param storage byte array allocated by client * @param size number of bytes actually used * @throws IllegalArgumentException if the range [0:size] is not within the array bounds of @@ -123,7 +128,7 @@ /** * Helper for constructors and methods that manipulate the storage in mutable subclasses in the * case where the storage should consist of exactly the whole of the given array. - * + * * @param storage byte array allocated by client */ protected void setStorage(byte[] storage) { @@ -132,23 +137,25 @@ this.offset = 0; } - /* * ======================================================================================== * Support for memoryview * ======================================================================================== - * + * * This is present in order to facilitate development of PyMemoryView which a full * implementation of bytearray would depend on, while at the same time a full implementation of * memoryview depends on bytearray. */ /** * Get hold of a memoryview on the current byte array. + * * @see MemoryViewProtocol#getMemoryView() */ @Override public MemoryView getMemoryView() { - if (mv == null) mv = new MemoryViewImpl(); + if (mv == null) { + mv = new MemoryViewImpl(); + } return mv; } @@ -198,23 +205,22 @@ } } - - + /* * ======================================================================================== * Support for construction and initialisation * ======================================================================================== - * + * * Methods here help subclasses set the initial state. They are designed with bytearray in mind, * but note that from Python 3, bytes() has the same set of calls and behaviours, although in - * Peterson's "sort of backport" to Python 2.x, bytes is effectively an alias for str and - * it shows. + * Peterson's "sort of backport" to Python 2.x, bytes is effectively an alias for str and it + * shows. */ /** * Helper for __new__ and __init__ and the Java API constructor from * PyObject in subclasses. - * + * * @see org.python.core.ByteArray#bytearray___init__(PyObject[], String[]) * @see org.python.core.ByteArray#ByteArray(PyObject) * @param arg primary argument from which value is taken @@ -268,7 +274,7 @@ /** * Helper for __new__ and __init__ and the Java API constructor from a * text string with the specified encoding in subclasses. - * + * * @see #bytearray___init__(PyObject[], String[]) * @see PyByteArray#PyByteArray(PyString, String, String) * @param arg primary argument from which value is taken @@ -284,7 +290,7 @@ /** * Helper for __new__ and __init__ and the Java API constructor from a * text string with the specified encoding in subclasses. - * + * * @see #bytearray___init__(PyObject[], String[]) * @see PyByteArray#PyByteArray(PyString, String, String) * @param arg primary argument from which value is taken @@ -298,14 +304,12 @@ setBytes(0, encoded); } - /** - * Helper for {@linkplain #setslice(int, int, int, PyObject)}, - * for __new__ and __init__ and the Java API constructor from a - * text string with the specified encoding in subclasses. This method thinly wraps a call to - * the codecs module and deals with checking - * for PyUnicode (where the encoding argument is mandatory). - * + * Helper for {@linkplain #setslice(int, int, int, PyObject)}, for __new__ and + * __init__ and the Java API constructor from a text string with the specified + * encoding in subclasses. This method thinly wraps a call to the codecs module and deals with + * checking for PyUnicode (where the encoding argument is mandatory). + * * @see #ByteArray(PyString, String, String) * @param arg primary argument from which value is taken * @param encoding name of optional encoding @@ -319,25 +323,24 @@ if (arg instanceof PyUnicode) { if (encoding != null) { - encoded = codecs.encode((PyUnicode)arg, encoding, errors); + encoded = codecs.encode(arg, encoding, errors); } else { throw Py.TypeError("unicode argument without an encoding"); } } else { if (encoding != null) { - encoded = codecs.encode((PyString)arg, encoding, errors); + encoded = codecs.encode(arg, encoding, errors); } else { - encoded = ((PyString)arg).getString(); + encoded = arg.getString(); } } return encoded; } - /** * Fill a defined section of a byte array by copying character values from a String. These * values have to be in the Python byte range 0 to 255. - * + * * @param start index in this byte array at which the first character code lands * @param value source of characters * @throws PyException(ValueError) if any value[i] > 255 @@ -345,14 +348,15 @@ protected void setBytes(int start, String value) throws PyException { int n = value.length(); int io = offset + start; - for (int j = 0; j < n; j++) + for (int j = 0; j < n; j++) { storage[io++] = byteCheck(value.charAt(j)); + } } /** * Fill a strided slice of a byte array by copying character values from a String. These values * have to be in the Python byte range 0 to 255. - * + * * @param start index in this byte array at which the first character code lands * @param value source of characters * @throws PyException(ValueError) if any value[i] > 255 @@ -365,12 +369,11 @@ io += step; } } - /** * Helper for __new__ and __init__ and the Java API constructor from * int in subclasses. Construct zero-filled bytearray of specified size. - * + * * @param n size of zero-filled array */ protected void init(int n) { @@ -383,7 +386,7 @@ /** * Helper for __new__ and __init__ and the Java API constructor from * objects supporting the Jython implementation of PEP 3118 (memoryview) in subclasses. - * + * * @param value a memoryview object consistent with the slice assignment * @throws PyException(NotImplementedError) until memoryview is properly supported * @throws PyException(TypeError) if the memoryview is not byte-oriented @@ -405,7 +408,7 @@ /** * Helper for __new__ and __init__ and the Java API constructor from * bytearray or bytes in subclasses. - * + * * @param source bytearray (or bytes) to copy */ protected void init(BaseBytes source) { @@ -416,7 +419,7 @@ /** * Helper for __new__ and __init__ and the Java API constructor from * an arbitrary iterable Python type in subclasses. This will include generators and lists. - * + * * @param iter iterable source of values to enter in the array */ protected void init(Iterable iter) { @@ -426,33 +429,31 @@ */ FragmentList fragList = new FragmentList(); fragList.loadFrom(iter); - + // Now, aggregate all those fragments. // - if (fragList.totalCount>0) { - - if (fragList.size()==1) { + if (fragList.totalCount > 0) { + + if (fragList.size() == 1) { // Note that the first fragment is small: negligible waste if stolen directly. Fragment frag = fragList.getFirst(); setStorage(frag.storage, frag.count); - + } else { // Stitch the fragments together in new storage of sufficient size newStorage(fragList.totalCount); fragList.emptyInto(storage, offset); } - - } else + + } else { // Nothing in the iterator setStorage(emptyStorage); + } } - - - /** - * Intended as a fragment of temporary storage for use we do not know how many bytes of allocate, and we are - * reading in some kind of iterable stream. + * Intended as a fragment of temporary storage for use we do not know how many bytes of + * allocate, and we are reading in some kind of iterable stream. */ protected static class Fragment { @@ -472,7 +473,7 @@ return count == storage.length; } } - + /** * A container of temporary storage when we do not know how many bytes to allocate, and we are * reading in some kind of iterable stream. @@ -486,7 +487,7 @@ /** * Load bytes into the container from the given iterable - * + * * @param iter iterable source of values to enter into the container * @throws PyException(TypeError) if any value not acceptable type * @throws PyException(ValueError) if any value<0 or value>255 or string length!=1 @@ -522,8 +523,9 @@ } /** - * Move the contents of this container to the given byte array at the specified index. - * This method leaves this container empty. + * Move the contents of this container to the given byte array at the specified index. This + * method leaves this container empty. + * * @param target destination array * @param p position to write first byte */ @@ -543,7 +545,7 @@ * we run out. (You must have checked beforehand that the destination is big enough.) This * method leaves this container empty. If the step size is one, it would be much quicker to * call {@link BaseBytes#emptyInto(byte[], int)} - * + * * @param target destination array * @param start position to write first byte * @param step amount to advance index with each byte @@ -561,39 +563,39 @@ } } - - - - /* ======================================================================================== + + /* + * ======================================================================================== * Sharable storage * ======================================================================================== - * - * The storage is provided by a byte array that may be somewhat larger than the number of - * bytes actually stored, and these bytes may begin at an offset within the storage. - * Immutable subclasses of BaseBytes may exploit this to share storage when - * constructed from a slice of another immutable subclass. Mutable subclasses may exploit it - * to provide efficient insertions near the start of the array. + * + * The storage is provided by a byte array that may be somewhat larger than the number of bytes + * actually stored, and these bytes may begin at an offset within the storage. Immutable + * subclasses of BaseBytes may exploit this to share storage when constructed from a slice of + * another immutable subclass. Mutable subclasses may exploit it to provide efficient insertions + * near the start of the array. */ - + /** Empty storage constant */ protected static final byte[] emptyStorage = new byte[0]; - + /** Storage array. */ - protected byte[] storage; - + protected byte[] storage = emptyStorage; + /** Number of bytes actually used in storage array. */ - protected int size; + protected int size = 0; /** Index of first byte used in storage array. */ - protected int offset; + protected int offset = 0; /** * Check that an index is within the range of the array, that is 0<=index<size. + * * @param index to check * @throws PyException(IndexError) if the index is outside the array bounds */ protected final void indexCheck(int index) throws PyException { - if (index<0 || index>=size) { + if (index < 0 || index >= size) { throw Py.IndexError(getType().fastGetName() + " index out of range"); } } @@ -602,11 +604,11 @@ * Allocate fresh, zero-filled storage for the requested number of bytes and make that the size. * If the size needed is zero, the "storage" allocated is the shared emptyStorage array. The * allocated size may be bigger than needed, and the method chooses a value for offset. - * + * * @param needed becomes the new value of this.size */ protected void newStorage(int needed) { - // The implementation for immutable arrays allocates exactly, and with offset zero. + // The implementation for immutable arrays allocates only as many bytes as needed. if (needed > 0) { setStorage(new byte[needed]); // guaranteed zero (by JLS 2ed para 4.5.5) } else { @@ -614,96 +616,955 @@ } } - /** - * Check that an integer is suitable for storage in a (Python) byte array, - * and convert it to the Java byte value that can be stored there. - * (Java bytes run -128..127 whereas Python bytes run 0..255.) + * Check that an integer is suitable for storage in a (Python) byte array, and convert it to the + * Java byte value that can be stored there. (Java bytes run -128..127 whereas Python bytes run + * 0..255.) + * * @param value to convert. * @throws PyException(ValueError) if value<0 or value>255 */ protected static final byte byteCheck(int value) throws PyException { - if (value<0 || value>=255) { + if (value < 0 || value > 255) { throw Py.ValueError("byte must be in range(0, 256)"); } - return (byte) value; + return (byte)value; } - + /** - * Check that the value of an PyInteger is suitable for storage in a (Python) byte array, - * and convert it to the Java byte value that can be stored there. - * (Java bytes run -128..127 whereas Python bytes run 0..255.) + * Check that the value of an PyInteger is suitable for storage in a (Python) byte array, and + * convert it to the Java byte value that can be stored there. (Java bytes run -128..127 whereas + * Python bytes run 0..255.) + * * @param value to convert. * @throws PyException(ValueError) if value<0 or value>255 */ protected static final byte byteCheck(PyInteger value) throws PyException { return byteCheck(value.asInt()); } - + /** - * Check that the type and value of a PyObject is suitable for storage in a (Python) byte - * array, and convert it to the Java byte value that can be stored there. - * (Java bytes run -128..127 whereas Python bytes run 0..255.) - * Acceptable types are:
    + * Check that the type and value of a PyObject is suitable for storage in a (Python) byte array, + * and convert it to the Java byte value that can be stored there. (Java bytes run -128..127 + * whereas Python bytes run 0..255.) Acceptable types are: + *
      *
    • PyInteger in range 0 to 255 inclusive
    • *
    • PyLong in range 0 to 255 inclusive
    • + *
    • Any type having an __index__() method, in range 0 to 255 inclusive
    • *
    • PyString of length 1
    • *
    + * * @param value to convert. * @throws PyException(TypeError) if not acceptable type * @throws PyException(ValueError) if value<0 or value>255 or string length!=1 */ protected static final byte byteCheck(PyObject value) throws PyException { - if (value instanceof PyInteger || value instanceof PyLong) { + if (value.isIndex()) { // This will possibly produce Py.OverflowError("long int too large to convert") - return byteCheck(value.asInt()); - } else if (value instanceof PyString) { + return byteCheck(value.asIndex()); + } else if (value.getType() == PyString.TYPE) { + // Exactly PyString (not PyUnicode) String strValue = ((PyString)value).getString(); if (strValue.length() != 1) { throw Py.ValueError("string must be of size 1"); } return byteCheck(strValue.charAt(0)); - } else + } else { throw Py.TypeError("an integer or string of size 1 is required"); + } } - - /* ======================================================================================== - * API for org.python.core.PySequence + + /* + * ======================================================================================== + * Wrapper class to make other objects into byte arrays + * ======================================================================================== + * + * In much of the bytearray and bytes API, the "other sequence" argument will accept any type + * that supports the buffer protocol, that is, the object can supply a memoryview through which + * the value is treated as a byte array. We have not implemented memoryview objects yet, and it + * is not clear what the Java API should be. As a temporary expedient, we define here a + * byte-oriented view on the key built-in types. + */ + + interface View { + + /** + * Return the indexed byte as a byte + * + * @param index + * @return byte at index + */ + public byte byteAt(int index); + + /** + * Return the indexed byte as an unsigned integer + * + * @param index + * @return value of the byte at index + */ + public int intAt(int index); + + /** + * Number of bytes in the view: valid indexes are from 0 to + * size()-1. + * + * @return the size + */ + public int size(); + + /** + * Return a new view that is a simple slice of this one defined by [start:end]. + * Py.None or null are acceptable for start and end, and have + * Python slice semantics. Negative values for start or end are treated as "from the end", + * in the usual manner of Python slices. + * + * @param start first element to include + * @param end first element after slice, not to include + * @return byte-oriented view + */ + public View slice(PyObject start, PyObject end); + } + + /** + * Some common apparatus for views including the implementation of slice semantics. + */ + static abstract class ViewBase implements View { + + // Implement Python slice semantics + public View slice(PyObject ostart, PyObject oend) { + PySlice s = new PySlice(ostart, oend, null); + int[] index = s.indicesEx(size()); // [ start, end, 1, end-start ] + return sliceImpl(index[0], index[(index[3] > 0) ? 1 : 0]); + } + + /** + * Implementation-specific part of returning a slice of the current view. This is called by + * the default implementations of {@link #slice(int, int)} and + * {@link #slice(PyObject, PyObject)} once the start and end + * arguments have been reduced to simple integer indexes. It is guaranteed that + * start>=0 and size()>=end>=start when the method is called. + * Implementors are encouraged to do something more efficient than piling on anothe wrapper. + * + * @param start first element to include + * @param end first element after slice, not to include + * @return byte-oriented view + */ + protected abstract View sliceImpl(int start, int end); + + } + + /** + * Return a wrapper providing a byte-oriented view for whatever object is passed, or return + * null if we don't know how. + * + * @param b object to wrap + * @return byte-oriented view or null + */ + protected static View getView(PyObject b) { + if (b == null) { + return null; + } else if (b instanceof BaseBytes) { + return new ViewOfBytes((BaseBytes)b); + } else if (b.getType() == PyString.TYPE) { + return new ViewOfString(b.asString()); + } + return null; + } + + /** + * Return a wrapper providing a byte-oriented view for a slice of whatever object is passed, or + * return null if we don't know how. + * + * @param b object to wrap + * @param start index of first byte to include + * @param end index of first byte after slice + * @return byte-oriented view or null + */ + protected static View getView(PyObject b, PyObject start, PyObject end) { + View whole = getView(b); + if (whole != null) { + return whole.slice(start, end); + } else { + return null; + } + } + + /** + * Return a wrapper providing a byte-oriented view of a slice of this object. + * + * @param ostart index of first byte to include + * @param oend index of first byte after slice + * @return byte-oriented view or null + */ + protected ViewOfBytes slice(PyObject ostart, PyObject oend) { + PySlice s = new PySlice(ostart, oend, null); + int[] index = s.indicesEx(size); // [ start, end, 1, end-start ] + return new ViewOfBytes(storage, offset + index[0], index[3]); + } + + /** + * Return a wrapper providing a byte-oriented view for whatever object is passed, or raise an + * exception if we don't know how. + * + * @param b object to wrap + * @return byte-oriented view + */ + protected static View getViewOrError(PyObject b) { + View res = getView(b); + if (res == null) { + // String fmt = "type %s doesn't support the buffer API"; // CPython + String fmt = "cannot access type %s as bytes"; + throw Py.NotImplementedError(String.format(fmt, b.getType().fastGetName())); + } + return res; + } + + /** + * Return a wrapper providing a byte-oriented view for a slice of whatever object is passed, or + * raise an exception if we don't know how. + * + * @param b object to wrap + * @param start index of first byte to include + * @param end index of first byte after slice + * @return byte-oriented view or null + */ + protected static View getViewOrError(PyObject b, PyObject start, PyObject end) { + View whole = getViewOrError(b); + return whole.slice(start, end); + } + + /** + * Wrapper providing a byte-oriented view for String (or PyString). + */ + protected static class ViewOfString extends ViewBase { + + private String str; + + /** + * Create a byte-oriented view of a String. + * + * @param str + */ + public ViewOfString(String str) { + this.str = str; + } + + public byte byteAt(int index) { + return byteCheck(str.charAt(index)); + } + + public int intAt(int index) { + return str.charAt(index); + } + + public int size() { + return str.length(); + } + + public View sliceImpl(int start, int end) { + return new ViewOfString(str.substring(start, end)); + } + + } + + /** + * Wrapper providing a byte-oriented view for byte arrays descended from BaseBytes. Not that + * this view is not safe against concurrent modification by this or another thread: if the byte + * array type is mutable, and the contents change, the contents of the view are likely to be + * invalid. + */ + protected static class ViewOfBytes extends ViewBase { + + private byte[] storage; + private int offset; + private int size; + + /** + * Create a byte-oriented view of a byte array descended from BaseBytes. + * + * @param obj + */ + public ViewOfBytes(BaseBytes obj) { + this.storage = obj.storage; + this.offset = obj.offset; + this.size = obj.size; + } + + /** + * Create a byte-oriented view of a byte array explicitly. If the size<0, a zero-length + * slice results. + * + * @param storage storage array + * @param offset + * @param size + */ + ViewOfBytes(byte[] storage, int offset, int size) { + if (size > 0) { + this.storage = storage; + this.offset = offset; + this.size = size; + } else { + this.storage = emptyStorage; + this.offset = 0; + this.size = 0; + } + } + + public byte byteAt(int index) { + return storage[offset + index]; + } + + public int intAt(int index) { + return 0xff & storage[offset + index]; + } + + public int size() { + return size; + } + + public View sliceImpl(int start, int end) { + return new ViewOfBytes(storage, offset + start, end - start); + } + } + + /* + * ======================================================================================== API + * for org.python.core.PySequence * ======================================================================================== */ protected PyInteger pyget(int index) { return new PyInteger(intAt(index)); } - - /* We're not implementing these here, but we can give a stronger guarantee about the return - * type and save some casting and type anxiety. + + /* + * We're not implementing these here, but we can give a stronger guarantee about the return type + * and save some casting and type anxiety. */ protected abstract BaseBytes getslice(int start, int stop, int step); + protected abstract BaseBytes repeat(int count); - + /* * And this extension point should be overridden in mutable subclasses */ - + /** * Insert the element (interpreted as a Python byte value) at the given index. The default * implementation produces a Python TypeError, for the benefit of immutable types. Mutable types * must override it. - * + * * @param index to insert at * @param element to insert (by value) * @throws PyException(IndexError) if the index is outside the array bounds * @throws PyException(ValueError) if element<0 or element>255 * @throws PyException(TypeError) if the subclass is immutable */ - public void pyadd(int index, PyInteger element) { + public void pyinsert(int index, PyObject element) { // This won't succeed: it just produces the right error. // storageReplace(index, 0, 1); pyset(index, element); } - - /* ======================================================================================== - * API for Java access as byte[] + + /* + * ======================================================================================== + * Support for Python API common to mutable and immutable subclasses + * ======================================================================================== + */ + + @Override + public int __len__() { + return size; + } + + /** + * Comparison function between two byte arrays returning 1, 0, or -1 as a>b, a==b, or a<b + * respectively. The comparison is by value, using Python unsigned byte conventions, and + * left-to-right (low to high index). Zero bytes are significant, even at the end of the array: + * [1,2,3]<[1,2,3,0], for example and [] is less than every other + * value, even [0]. + * + * @param a left-hand array in the comparison + * @param b right-hand array in the comparison + * @return 1, 0 or -1 as a>b, a==b, or a<b respectively + */ + private static int compare(BaseBytes a, BaseBytes b) { + + // Compare elements one by one in these ranges: + int ap = a.offset; + int aEnd = ap + a.size; + int bp = b.offset; + int bEnd = bp + b.size; + + while (ap < aEnd) { + if (bp >= bEnd) { + // a is longer than b + return 1; + } else { + // Compare the corresponding bytes (as unsigned ints) + int aVal = 0xff & a.storage[ap++]; + int bVal = 0xff & b.storage[bp++]; + int diff = aVal - bVal; + if (diff != 0) { + return (diff < 0) ? -1 : 1; + } + } + } + + // All the bytes matched and we reached the end of a + if (bp < bEnd) { + // But we didn't reach the end of b + return -1; + } else { + // And the end of b at the same time, so they're equal + return 0; + } + + } + +// /** +// * Comparison function between a byte array and a String returning 1, 0 or -1 as a>b, a==b, or +// * a<b respectively. The comparison is by value, using Python unsigned byte conventions for +// * the byte array and character code for elements of the string, and left-to-right (low to high +// * index). Zero bytes are significant, even at the end of the array: +// * [65,66,67]<"ABC\u0000", for example and [] is less than every +// * non-empty String, while []=="". +// * +// * @param a left-hand array in the comparison +// * @param b right-hand String in the comparison +// * @return 1, 0 or -1 as a>b, a==b, or a<b respectively +// */ +// private static int compare(BaseBytes a, String b) { +// +// // Compare elements one by one in these ranges: +// int ap = a.offset; +// int aEnd = ap + a.size; +// int bp = 0; +// int bEnd = b.length(); +// +// while (ap < aEnd) { +// if (bp >= bEnd) { +// // a is longer than b +// return 1; +// } else { +// // Compare the corresponding bytes and character codes +// int aVal = 0xff & a.storage[ap++]; +// int bVal = b.charAt(bp++); +// int diff = aVal - bVal; +// if (diff != 0) { +// return (diff < 0) ? -1 : 1; +// } +// } +// } +// +// // All the bytes matched and we reached the end of a +// if (bp < bEnd) { +// // But we didn't reach the end of b +// return -1; +// } else { +// // And the end of b at the same time, so they're equal +// return 0; +// } +// +// } + + /** + * Comparison function between a byte array and a byte-oriented View of some other object, such + * as a String, returning 1, 0 or -1 as a>b, a==b, or a<b respectively. The comparison is by + * value, using Python unsigned byte conventions, left-to-right (low to high index). Zero bytes + * are significant, even at the end of the array: [65,66,67]<"ABC\u0000", for + * example and [] is less than every non-empty b, while []=="". + * + * @param a left-hand array in the comparison + * @param b right-hand wrapped object in the comparison + * @return 1, 0 or -1 as a>b, a==b, or a<b respectively + */ + private static int compare(BaseBytes a, View b) { + + // Compare elements one by one in these ranges: + int ap = a.offset; + int aEnd = ap + a.size; + int bp = 0; + int bEnd = b.size(); + + while (ap < aEnd) { + if (bp >= bEnd) { + // a is longer than b + return 1; + } else { + // Compare the corresponding bytes + int aVal = 0xff & a.storage[ap++]; + int bVal = b.intAt(bp++); + int diff = aVal - bVal; + if (diff != 0) { + return (diff < 0) ? -1 : 1; + } + } + } + + // All the bytes matched and we reached the end of a + if (bp < bEnd) { + // But we didn't reach the end of b + return -1; + } else { + // And the end of b at the same time, so they're equal + return 0; + } + + } + + /** + * Comparison function between byte array types and any other object. The set of 6 + * "rich comparison" operators are based on this. + * + * @param b + * @return 1, 0 or -1 as this>b, this==b, or this<b respectively, or -2 if the comparison is + * not implemented + */ + private synchronized int basebytes_cmp(PyObject b) { + + // This is not exposed as bytearray and bytes have no __cmp__. + + if (this == b) { + // Same object: quick result + return 0; + + } else { + + // Try to get a byte-oriented view + View bv = getView(b); + + if (bv == null) { + // Signifies a type mis-match. See PyObject _cmp_unsafe() and related code. + return -2; + + } else { + // Object supported by our interim memory view + return compare(this, bv); + + } + } + } + +// /** +// * Comparison function between byte array types and any other object. The set of 6 +// * "rich comparison" operators are based on this. +// * +// * @param b +// * @return 1, 0 or -1 as this>b, this==b, or this<b respectively, or -2 if the comparison is +// * not implemented +// */ +// private synchronized int basebytes_cmp(PyObject b) { +// +// // This is based on PyFloat.float___cmp__() as it seems to have the same need to support +// // multiple types for other, but it is not exposed as bytearray and bytes have no __cmp__. +// +// if (this == b) { +// // Same object: quick result +// return 0; +// +// } else if (b instanceof BaseBytes) { +// return compare(this, (BaseBytes)b); +// +// } else if (b.getType() == PyString.TYPE) { +// /* +// * Necessary to permit comparison of bytearray and bytes, which in in Python 2.7 is an +// * alias of str. Remove in 3.0 +// */ +// return compare(this, ((PyString)b).asString()); +// +// } else if (b instanceof MemoryViewProtocol) { +// // XXX: Revisit when we have an implementation of memoryview +// // MemoryView mv = ((MemoryViewProtocol) other).getMemoryView(); +// return -2; +// +// } else { +// // Signifies a type mis-match. See PyObject _cmp_unsafe() and related code. +// return -2; +// } +// } + + /** + * Fail-fast comparison function between byte array types and any other object, for when the + * test is only for equality. The "rich comparison" operators __eq__ and + * __ne__ are based on this. + * + * @param b + * @return 0 if this==b, or +1 or -1 if this!=b, or -2 if the comparison is not implemented + */ + private synchronized int basebytes_cmpeq(PyObject b) { + + if (this == b) { + // Same object: quick result + return 0; + + } else { + + // Try to get a byte-oriented view + View bv = getView(b); + + if (bv == null) { + // Signifies a type mis-match. See PyObject _cmp_unsafe() and related code. + return -2; + + } else if (bv.size() != size) { + // Different size: can't be equal, and we don't care which is bigger + return 1; + + } else { + // Object supported by our interim memory view + return compare(this, bv); + + } + } + } + +// /** +// * Fail-fast comparison function between byte array types and any other object, for when the +// * test is only for equality. The "rich comparison" operators __eq__ and +// * __ne__ are based on this. +// * +// * @param b +// * @return 0 if this==b, or +1 or -1 if this!=b, or -2 if the comparison is not implemented +// */ +// private synchronized int basebytes_cmpeq(PyObject b) { +// +// if (b instanceof BaseBytes) { +// if (((BaseBytes)b).size != size) { +// // Different size: can't be equal, and we don't care which is bigger +// return 1; +// } +// } else if (b.getType() == PyString.TYPE) { +// /* +// * Necessary to permit comparison of bytearray and bytes, which in in Python 2.7 is an +// * alias of str. Remove in 3.0 +// */ +// if (((PyString)b).__len__() != size) { +// // Different size: can't be equal, and we don't care which is bigger +// return 1; +// } +// } +// return basebytes_cmp(b); +// } + + /** + * Implementation of __eq__ (equality) operator, capable of comparison with another byte array + * or bytes. Comparison with an invalid type returns null. + * + * @param other + * @return + */ + final PyObject basebytes___eq__(PyObject other) { + int cmp = basebytes_cmpeq(other); + if (cmp == 0) { + return Py.True; + } else if (cmp > -2) { + return Py.False; + } else { + return null; + } + } + + /** + * Implementation of __ne__ (not equals) operator, capable of comparison with another byte array + * or bytes. Comparison with an invalid type returns null. + * + * @param other + * @return + */ + final PyObject basebytes___ne__(PyObject other) { + int cmp = basebytes_cmpeq(other); + if (cmp == 0) { + return Py.False; + } else if (cmp > -2) { + return Py.True; + } else { + return null; + } + } + + /** + * Implementation of __lt__ (less than) operator, capable of comparison with another byte array + * or bytes. Comparison with an invalid type returns null. + * + * @param other + * @return + */ + final PyObject basebytes___lt__(PyObject other) { + int cmp = basebytes_cmp(other); + if (cmp >= 0) { + return Py.False; + } else if (cmp > -2) { + return Py.True; + } else { + return null; + } + } + + /** + * Implementation of __le__ (less than or equal to) operator, capable of comparison with another + * byte array or bytes. Comparison with an invalid type returns null. + * + * @param other + * @return + */ + final PyObject basebytes___le__(PyObject other) { + int cmp = basebytes_cmp(other); + if (cmp > 0) { + return Py.False; + } else if (cmp > -2) { + return Py.True; + } else { + return null; + } + } + + /** + * Implementation of __ge__ (greater than or equal to) operator, capable of comparison with + * another byte array or bytes. Comparison with an invalid type returns null. + * + * @param other + * @return + */ + final PyObject basebytes___ge__(PyObject other) { + int cmp = basebytes_cmp(other); + if (cmp >= 0) { + return Py.True; + } else if (cmp > -2) { + return Py.False; + } else { + return null; + } + } + + /** + * Implementation of __gt__ (greater than) operator, capable of comparison with another byte + * array or bytes. Comparison with an invalid type returns null. + * + * @param other + * @return + */ + final PyObject basebytes___gt__(PyObject other) { + int cmp = basebytes_cmp(other); + if (cmp > 0) { + return Py.True; + } else if (cmp > -2) { + return Py.False; + } else { + return null; + } + } + + /** + * Equivalent of the 'string_escape' decode to a String that is all-printable, showing non + * printable charater as lowercase hex escapes, except '\t', '\n', and '\r'. This supports + * __repr__(). + * + * @return the byte array as a String, still encoded + */ + protected synchronized String asEscapedString() { + StringBuilder buf = new StringBuilder(size + (size >> 8) + 10); + int jmax = offset + size; + for (int j = offset; j < jmax; j++) { + int c = 0xff & storage[j]; + if (c >= 0x7f) { // Unprintable high 128 and DEL + appendHexEscape(buf, c); + } else if (c >= ' ') { // Printable + if (c == '\\') { // Special case + buf.append("\\\\"); + } else { + buf.append((char)c); + } + } else if (c == '\t') { // Spacial cases in the low 32 + buf.append("\\t"); + } else if (c == '\n') { + buf.append("\\n"); + } else if (c == '\r') { + buf.append("\\r"); + } else { + appendHexEscape(buf, c); + } + } + return buf.toString(); + } + + private static final void appendHexEscape(StringBuilder buf, int c) { + buf.append("\\x") + .append(Character.forDigit((c & 0xf0) >> 4, 16)) + .append(Character.forDigit(c & 0xf, 16)); + } + + /** + * Search for the target in this byte array, returning true if found and false if not. The + * target must be compatible with the Python byte range. + * + * @param target byte value to search for + * @return true iff found + */ + protected final synchronized boolean basebytes___contains__(PyObject target) { + byte t = byteCheck(target); + int jmax = offset + size; + for (int j = offset; j < jmax; j++) { + if (storage[j] == t) { + return true; + } + } + return false; + } + + /** + * Copy the bytes of a byte array to the characters of a String with no change in ordinal value. + * This could also be described as 'latin-1' decoding of the byte array to a String. + * + * @return the byte array as a String, still encoded + */ + private synchronized String asEncodedString() { + StringBuilder buf = new StringBuilder(size); + int jmax = offset + size; + for (int j = offset; j < jmax; j++) { + buf.append((char)(0xff & storage[j])); + } + return buf.toString(); + } + + /** + * Decode the byte array to a Unicode string according to the default encoding. The returned + * PyObject should be a PyUnicode, since the default codec is well-behaved. + * + * @return object containing the decoded characters + */ + public PyObject decode() { + return decode(null, null); + } + + /** + * Decode the byte array to a Unicode string according to the specified encoding and default + * error policy. The returned PyObject will usually be a PyUnicode, but in practice + * it is whatever the decode method of the codec decides. + * + * @param encoding the name of the codec (uses default codec if null) + * @return object containing the decoded characters + */ + public PyObject decode(String encoding) { + return decode(encoding, null); + } + + /** + * Decode the byte array to a Unicode string according to the specified encoding and error + * policy. The returned PyObject will usually be a PyUnicode, but in practice it is + * whatever the decode method of the codec decides. + * + * @param encoding the name of the codec (uses default codec if null) + * @param errors the name of the error policy (uses 'strict' if null) + * @return object containing the decoded characters + */ + public PyObject decode(String encoding, String errors) { + /* + * Provide a Python str input to the decode method of a codec, which in v2.7 + * expects a PyString. (In Python 3k the codecs decode from the bytes type, so + * we can pass this directly.) + */ + PyString this_ = new PyString(this.asEncodedString()); + return codecs.decode(this_, encoding, errors); + } + + /** + * Ready-to-expose implementation of decode( [ encoding [, errors ]] ) + * + * @param args Python argument list + * @param keywords Assocaited keywords + * @return + */ + protected final PyObject basebytes_decode(PyObject[] args, String[] keywords) { + ArgParser ap = new ArgParser("decode", args, keywords, "encoding", "errors"); + String encoding = ap.getString(0, null); + String errors = ap.getString(1, null); + return decode(encoding, errors); + } + + /** + * Ready-to-expose implementation of Python find( sub [, start [, end ]] ). Return + * the lowest index in the byte array where byte sequence sub is found, such that + * sub is contained in the slice [start:end]. Arguments + * start and end (which may be null or + * Py.None ) are interpreted as in slice notation. Return -1 if sub is + * not found. + * + * @param sub bytes to find + * @param start of slice to search + * @param end of slice to search + * @return index of start of ocurrence of sub within this byte array + */ + final int basebytes_find(PyObject sub, PyObject start, PyObject end) { + ViewOfBytes v = this.slice(start, end); + byte[] buf = v.storage; + View vsub = getViewOrError(sub); + int n = vsub.size(); + // No point testing beyond this location, as insufficient space: + int pmax = v.offset + v.size - n; + // Use start positions from v.offset to pmax in the buffer buf + for (int p = v.offset; p < pmax; p++) { + int j; + for (j = 0; j < n; j++) { + if (buf[p + j] != vsub.byteAt(j)) { + break; + } + } + // If we tested all n bytes, that's a match. Note that the returned index is + // relative to the (operative) start of this, not relative to the start of the slice. + if (j == n) { + return p - this.offset; + } + } + // We tested all the start positions without success + return -1; + } + + /** + * Convenience method to create a TypeError PyException with the message + * "can't concat {type} to {toType}" + * + * @param type + * @param toType + * @return PyException (TypeError) as detailed + */ + public static PyException ConcatenationTypeError(PyType type, PyType toType) { + String fmt = "can't concat %s to %s"; + return Py.TypeError(String.format(fmt, type.fastGetName(), toType.fastGetName())); + } + + /** + * Support for pickling byte arrays: reduce a byte array to the actual type, arguments for + * (re-)construction of the object, and the dictionary of any user-defined sub-class. + * + * @return PyTuple that is first stage in pickling byte array + */ + public PyObject __reduce__() { + return basebytes___reduce__(); + } + + final PyTuple basebytes___reduce__() { + PyUnicode encoded = new PyUnicode(this.asEncodedString()); + PyObject args = new PyTuple(encoded, getPickleEncoding()); + PyObject dict = __findattr__("__dict__"); + return new PyTuple(getType(), args, (dict != null) ? dict : Py.None); + } + + private static PyString PICKLE_ENCODING; + + /** + * Name the encoding effectively used in __reduce__() suport for pickling: this choice is + * hard-coded in CPython as "latin-1". + */ + private static final PyString getPickleEncoding() { + if (PICKLE_ENCODING == null) { + PICKLE_ENCODING = new PyString("latin-1"); + } + return PICKLE_ENCODING; + } + + /* + * ======================================================================================== API + * for Java access as byte[] * ======================================================================================== * * Just the immutable case for now @@ -711,29 +1572,33 @@ /** * No range check access to byte[index]. + * * @param index * @return the byte at the given index */ private final synchronized byte byteAt(int index) { - return storage[index+offset]; + return storage[index + offset]; } /** * Return the Python byte (in range 0 to 255 inclusive) at the given index. + * * @param index of value in byte array * @return the integer value at the index * @throws PyException(IndexError) if the index is outside the array bounds */ public synchronized int intAt(int index) throws PyException { indexCheck(index); - return 0xff & ((int)byteAt(index)); + return 0xff & byteAt(index); } /** * Helper to implement {@link #repeat(int)}. Use something like: - * + * *
    -     * 
    +     *
    +     *
    +     *
          * @Override
          * protected PyByteArray repeat(int count) {
          *     PyByteArray ret = new PyByteArray();
    @@ -741,7 +1606,7 @@
          *     return ret;
          * }
          * 
    - * + * * @param count the number of times to repeat this. * @return this byte array repeated count times. */ @@ -753,9 +1618,9 @@ return dst; } - - /* ======================================================================================== - * API for java.util.List + /* + * ======================================================================================== API + * for java.util.List * ======================================================================================== */ @@ -773,12 +1638,15 @@ } @Override - public int size() { return size; } + public int size() { + return size; + } // For mutable subclass use - + /** * Replaces the element at the specified position in this list with the specified element. + * * @see java.util.AbstractList#set(int, java.lang.Object) * @throws PyException(TypeError) if actual class is immutable * @throws PyException(IndexError) if the index is outside the array bounds @@ -794,8 +1662,9 @@ } /** - * Inserts the specified element at the specified position in this list. - * Shifts the element currently at that position and any subsequent elements to the right. + * Inserts the specified element at the specified position in this list. Shifts the element + * currently at that position and any subsequent elements to the right. + * * @see java.util.AbstractList#add(int, java.lang.Object) * @throws PyException(IndexError) if the index is outside the array bounds * @throws PyException(ValueError) if element<0 or element>255 @@ -805,13 +1674,14 @@ public void add(int index, PyInteger element) throws PyException { // Not using __setitem__ as it applies Python index semantics to e.g. b[-1]. indexCheck(index); - pyadd(index, element); // TypeError if immutable + pyinsert(index, element); // TypeError if immutable } /** * Removes the element at the specified position in this list. Shifts any subsequent - * elements to the left (subtracts one from their indices). - * Returns the element that was removed from the list. + * elements to the left (subtracts one from their indices). Returns the element that was + * removed from the list. + * * @see java.util.AbstractList#remove(int) * @throws PyException(IndexError) if the index is outside the array bounds */ @@ -825,17 +1695,22 @@ } }; - /** - * Number of bytes in bytearray (or bytes) object. + /** + * Number of bytes in bytearray (or bytes) object. + * * @see java.util.List#size() * @return Number of bytes in byte array. * */ - public int size() { return size;} + public int size() { + return size; + } /* * @see java.util.List#isEmpty() */ - public boolean isEmpty() { return size==0; } + public boolean isEmpty() { + return size == 0; + } /** * Returns true if this list contains the specified value. More formally, returns true if and @@ -847,6 +1722,7 @@ /* * @return + * * @see java.util.List#iterator() */ public Iterator iterator() { @@ -855,6 +1731,7 @@ /* * @return + * * @see java.util.List#toArray() */ public Object[] toArray() { @@ -863,7 +1740,9 @@ /* * @param a + * * @return + * * @see java.util.List#toArray(T[]) */ public T[] toArray(T[] a) { @@ -872,7 +1751,9 @@ /* * @param o + * * @return + * * @see java.util.List#add(java.lang.Object) */ public boolean add(PyInteger o) { @@ -881,7 +1762,9 @@ /* * @param o + * * @return + * * @see java.util.List#remove(java.lang.Object) */ public boolean remove(Object o) { @@ -890,7 +1773,9 @@ /* * @param c + * * @return + * * @see java.util.List#containsAll(java.util.Collection) */ public boolean containsAll(Collection c) { @@ -899,7 +1784,9 @@ /* * @param c + * * @return + * * @see java.util.List#addAll(java.util.Collection) */ public boolean addAll(Collection c) { @@ -908,8 +1795,11 @@ /* * @param index + * * @param c + * * @return + * * @see java.util.List#addAll(int, java.util.Collection) */ public boolean addAll(int index, Collection c) { @@ -918,7 +1808,9 @@ /* * @param c + * * @return + * * @see java.util.List#removeAll(java.util.Collection) */ public boolean removeAll(Collection c) { @@ -927,7 +1819,9 @@ /* * @param c + * * @return + * * @see java.util.List#retainAll(java.util.Collection) */ public boolean retainAll(Collection c) { @@ -935,7 +1829,7 @@ } /* - * + * * @see java.util.List#clear() */ public void clear() { @@ -944,7 +1838,9 @@ /* * @param o + * * @return + * * @see java.util.List#equals(java.lang.Object) */ public boolean equals(Object o) { @@ -953,6 +1849,7 @@ /* * @return + * * @see java.util.List#hashCode() */ public int hashCode() { @@ -961,7 +1858,9 @@ /* * @param index + * * @return + * * @see java.util.List#get(int) */ public PyInteger get(int index) { @@ -970,8 +1869,11 @@ /* * @param index + * * @param element + * * @return + * * @see java.util.List#set(int, java.lang.Object) */ public PyInteger set(int index, PyInteger element) { @@ -980,7 +1882,9 @@ /* * @param index + * * @param element + * * @see java.util.List#add(int, java.lang.Object) */ public void add(int index, PyInteger element) { @@ -989,7 +1893,9 @@ /* * @param index + * * @return + * * @see java.util.List#remove(int) */ public PyInteger remove(int index) { @@ -998,7 +1904,9 @@ /* * @param o + * * @return + * * @see java.util.List#indexOf(java.lang.Object) */ public int indexOf(Object o) { @@ -1007,7 +1915,9 @@ /* * @param o + * * @return + * * @see java.util.List#lastIndexOf(java.lang.Object) */ public int lastIndexOf(Object o) { @@ -1016,6 +1926,7 @@ /* * @return + * * @see java.util.List#listIterator() */ public ListIterator listIterator() { @@ -1024,7 +1935,9 @@ /* * @param index + * * @return + * * @see java.util.List#listIterator(int) */ public ListIterator listIterator(int index) { @@ -1033,12 +1946,15 @@ /* * @param fromIndex + * * @param toIndex + * * @return + * * @see java.util.List#subList(int, int) */ public List subList(int fromIndex, int toIndex) { return listDelegate.subList(fromIndex, toIndex); } - + } diff --git a/src/org/python/core/PyByteArray.java b/src/org/python/core/PyByteArray.java --- a/src/org/python/core/PyByteArray.java +++ b/src/org/python/core/PyByteArray.java @@ -5,6 +5,7 @@ import org.python.expose.ExposedMethod; import org.python.expose.ExposedNew; import org.python.expose.ExposedType; +import org.python.expose.MethodType; /** * Partial implementation of Python bytearray. At the present stage of development, the class @@ -18,21 +19,22 @@ * implementation does not support the memoryview interface either for access or a a * source for its constructors although the signatures are present. The rich set of string-like * operations due a bytearray is not implemented. - * + * */ @ExposedType(name = "bytearray", base = PyObject.class, doc = BuiltinDocs.bytearray_doc) public class PyByteArray extends BaseBytes { public static final PyType TYPE = PyType.fromClass(PyByteArray.class); - + /** * Create a zero-length Python bytearray of explicitly-specified sub-type + * * @param type explicit Jython type */ public PyByteArray(PyType type) { super(type); } - + /** * Create a zero-length Python bytearray. */ @@ -42,6 +44,7 @@ /** * Create zero-filled Python bytearray of specified size. + * * @param size of bytearray */ public PyByteArray(int size) { @@ -51,7 +54,7 @@ /** * Construct bytearray by copying values from int[]. - * + * * @param value source of the bytes (and size) */ public PyByteArray(int[] value) { @@ -59,39 +62,40 @@ } /** - * Create a new array filled exactly by a copy of the contents of the - * source. + * Create a new array filled exactly by a copy of the contents of the source. + * * @param value source of the bytes (and size) */ public PyByteArray(BaseBytes value) { super(TYPE); init(value); } - + /** - * Create a new array filled exactly by a copy of the contents of the - * source. + * Create a new array filled exactly by a copy of the contents of the source. + * * @param value source of the bytes (and size) */ public PyByteArray(MemoryViewProtocol value) { super(TYPE); init(value.getMemoryView()); } - + /** * Create a new array filled from an iterable of PyObject. The iterable must yield objects * convertible to Python bytes (non-negative integers less than 256 or strings of length 1). + * * @param value source of the bytes (and size) */ public PyByteArray(Iterable value) { super(TYPE); init(value); } - + /** * Create a new array by encoding a PyString argument to bytes. If the PyString is actually a * PyUnicode, the encoding must be explicitly specified. - * + * * @param arg primary argument from which value is taken * @param encoding name of optional encoding (must be a string type) * @param errors name of optional errors policy (must be a string type) @@ -104,7 +108,7 @@ /** * Create a new array by encoding a PyString argument to bytes. If the PyString is actually a * PyUnicode, the encoding must be explicitly specified. - * + * * @param arg primary argument from which value is taken * @param encoding name of optional encoding (may be null to select the default for this * installation) @@ -116,6 +120,17 @@ } /** + * Create a new array by encoding a PyString argument to bytes. If the PyString is actually a + * PyUnicode, an exception is thrown saying that the encoding must be explicitly specified. + * + * @param arg primary argument from which value is taken + */ + public PyByteArray(PyString arg) { + super(TYPE); + init(arg, (String)null, (String)null); + } + + /** * Create a new bytearray object from an arbitrary Python object according to the same rules as * apply in Python to the bytearray() constructor: *
      @@ -133,16 +148,17 @@ * bytearray(string, encoding[, errors]), use the constructor * {@link #PyByteArray(PyString, String, String)}. If the PyString is actually a PyUnicode, an * encoding must be specified, and using this constructor will throw an exception about that. - * + * * @param arg primary argument from which value is taken (may be null) * @throws PyException in the same circumstances as bytearray(arg), TypeError for non-iterable, - * non-integer argument type, and ValueError if iterables do not yield byte [0..255] values. + * non-integer argument type, and ValueError if iterables do not yield byte [0..255] + * values. */ public PyByteArray(PyObject arg) throws PyException { super(TYPE); init(arg); } - + /* ======================================================================================== * API for org.python.core.PySequence * ======================================================================================== @@ -160,12 +176,12 @@ protected synchronized PyByteArray getslice(int start, int stop, int step) { if (step == 1) { // Efficiently copy contiguous slice - int n = stop-start; - if (n<=0) { + int n = stop - start; + if (n <= 0) { return new PyByteArray(); } else { PyByteArray ret = new PyByteArray(n); - System.arraycopy(storage, offset+start, ret.storage, ret.offset, n); + System.arraycopy(storage, offset + start, ret.storage, ret.offset, n); return ret; } } else { @@ -173,16 +189,17 @@ PyByteArray ret = new PyByteArray(n); n += ret.offset; byte[] dst = ret.storage; - for (int io = start + offset, jo = ret.offset; jo < n; io += step, jo++) + for (int io = start + offset, jo = ret.offset; jo < n; io += step, jo++) { dst[jo] = storage[io]; + } return ret; } } - /** - * Returns a PyByteArray that repeats this sequence the given number of times, as - * in the implementation of __mul__ for strings. + * Returns a PyByteArray that repeats this sequence the given number of times, as in the + * implementation of __mul__ for strings. + * * @param count the number of times to repeat this. * @return this byte array repeated count times. */ @@ -191,14 +208,13 @@ PyByteArray ret = new PyByteArray(); ret.setStorage(repeatImpl(count)); return ret; - } + } /** - * Sets the indexed element of the bytearray to the given value. - * This is an extension point called by PySequence in its implementation of - * {@link #__setitem__} - * It is guaranteed by PySequence that the index is within the bounds of the array. - * Any other clients calling pyset(int) must make the same guarantee. + * Sets the indexed element of the bytearray to the given value. This is an extension point + * called by PySequence in its implementation of {@link #__setitem__} It is guaranteed by + * PySequence that the index is within the bounds of the array. Any other clients calling + * pyset(int) must make the same guarantee. * * @param index index of the element to set. * @param value the value to set this element to. @@ -206,23 +222,26 @@ * @throws PyException(ValueError) if value<0 or value>255 */ public synchronized void pyset(int index, PyObject value) throws PyException { - storage[index+offset] = byteCheck(value); + storage[index + offset] = byteCheck(value); } /** * Insert the element (interpreted as a Python byte value) at the given index. - * + * Python int, long and string types of length 1 are allowed. + * * @param index to insert at * @param element to insert (by value) * @throws PyException(IndexError) if the index is outside the array bounds * @throws PyException(ValueError) if element<0 or element>255 + * @throws PyException(TypeError) if the subclass is immutable */ - public synchronized void pyadd(int index, PyInteger element) { + @Override + public synchronized void pyinsert(int index, PyObject element) { // Open a space at the right location. storageReplace(index, 0, 1); - storage[index] = byteCheck(element); + storage[offset+index] = byteCheck(element); } - + /** * Sets the given range of elements according to Python slice assignment semantics. If the step * size is one, it is a simple slice and the operation is equivalent to deleting that slice, @@ -236,15 +255,15 @@ * PyObjects, but acceptable ones are PyInteger, PyLong or PyString of length 1. If * any one of them proves unsuitable for assignment to a Python bytarray element, an exception * is thrown and this bytearray is unchanged. - * + * *
            * a = bytearray(b'abcdefghijklmnopqrst')
            * a[2:12:2] = iter( [65, 66, 67, long(68), "E"] )
            * 
      - * + * * Results in a=bytearray(b'abAdBfChDjElmnopqrst'). *

      - * + * * @param start the position of the first element. * @param stop one more than the position of the last element. * @param step the step size. @@ -308,12 +327,10 @@ } } - - /** * Sets the given range of elements according to Python slice assignment semantics from a * zero-filled bytearray of the given length. - * + * * @see #setslice(int, int, int, PyObject) * @param start the position of the first element. * @param stop one more than the position of the last element. @@ -330,17 +347,19 @@ } else { // This is an extended slice which means we are replacing elements int n = sliceLength(start, stop, step); - if (n != len) throw SliceSizeError("bytes", len, n); - for (int io = start + offset; n > 0; io += step, --n) + if (n != len) { + throw SliceSizeError("bytes", len, n); + } + for (int io = start + offset; n > 0; io += step, --n) { storage[io] = 0; + } } } - /** * Sets the given range of elements according to Python slice assignment semantics from a * PyString. - * + * * @see #setslice(int, int, int, PyObject) * @param start the position of the first element. * @param stop one more than the position of the last element. @@ -368,7 +387,7 @@ /** * Sets the given range of elements according to Python slice assignment semantics from an * object supporting the Jython implementation of PEP 3118. - * + * * @see #setslice(int, int, int, PyObject) * @param start the position of the first element. * @param stop one more than the position of the last element. @@ -378,34 +397,37 @@ */ private void setslice(int start, int stop, int step, MemoryView value) throws PyException { // XXX Support memoryview once means of access to bytes is defined - Py.NotImplementedError("memoryview not yet supported in bytearray"); - String format = value.get_format(); - boolean isBytes = format == null || "B".equals(format); - if (value.get_ndim() != 1 || !isBytes) { - Py.TypeError("memoryview value must be byte-oriented"); - } else { - // Dimensions are given as a PyTple (although only one) - int len = value.get_shape().pyget(0).asInt(); - if (step == 1) { - // Delete this[start:stop] and open a space of the right size - storageReplace(start, stop - start, len); - // System.arraycopy(value.storage, value.offset, storage, start - // + offset, len); - } else { - // This is an extended slice which means we are replacing elements - int n = sliceLength(start, stop, step); - if (n != len) throw SliceSizeError("bytes", len, n); - // int no = n + value.offset; - // for (int io = start + offset, jo = value.offset; jo < no; io += step, jo++) { - // storage[io] = value.storage[jo]; // Assign this[i] = value[j] - // } - } - } + throw Py.NotImplementedError("memoryview not yet supported in bytearray"); +// String format = value.get_format(); +// boolean isBytes = format == null || "B".equals(format); +// if (value.get_ndim() != 1 || !isBytes) { +// throw Py.TypeError("memoryview value must be byte-oriented"); +// } else { +// // Dimensions are given as a PyTuple (although only one) +// int len = value.get_shape().pyget(0).asInt(); +// if (step == 1) { +// // Delete this[start:stop] and open a space of the right size +// storageReplace(start, stop - start, len); +// // System.arraycopy(value.storage, value.offset, storage, start +// // + offset, len); +// } else { +// // This is an extended slice which means we are replacing elements +// int n = sliceLength(start, stop, step); +// if (n != len) { +// throw SliceSizeError("bytes", len, n); +// } +// // int no = n + value.offset; +// // for (int io = start + offset, jo = value.offset; jo < no; io += step, jo++) { +// // storage[io] = value.storage[jo]; // Assign this[i] = value[j] +// // } +// } +// } } /** - * Sets the given range of elements according to Python slice assignment semantics - * from a bytearray (or bytes). + * Sets the given range of elements according to Python slice assignment semantics from a + * bytearray (or bytes). + * * @see #setslice(int, int, int, PyObject) * @param start the position of the first element. * @param stop one more than the position of the last element. @@ -414,33 +436,37 @@ * @throws PyException(SliceSizeError) if the value size is inconsistent with an extended slice */ private void setslice(int start, int stop, int step, BaseBytes value) throws PyException { - if (value == this) { - value = new PyByteArray(value); // Must work with a copy + + if (value == this) { // Must work with a copy + value = new PyByteArray(value); } + int len = value.size; + if (step == 1) { - //Delete this[start:stop] and open a space of the right size + // Delete this[start:stop] and open a space of the right size storageReplace(start, stop - start, len); - System.arraycopy(value.storage, value.offset, storage, start - + offset, len); + System.arraycopy(value.storage, value.offset, storage, start + offset, len); + } else { // This is an extended slice which means we are replacing elements int n = sliceLength(start, stop, step); if (n != len) { throw SliceSizeError("bytes", len, n); } + int no = n + value.offset; for (int io = start + offset, jo = value.offset; jo < no; io += step, jo++) { storage[io] = value.storage[jo]; // Assign this[i] = value[j] } + } } - /** * Sets the given range of elements according to Python slice assignment semantics from a * bytearray (or bytes). - * + * * @see #setslice(int, int, int, PyObject) * @param start the position of the first element. * @param stop one more than the position of the last element. @@ -452,7 +478,8 @@ private void setslice(int start, int stop, int step, Iterable iter) { /* * As we don't know how many elements the iterable represents, we can't adjust the array - * until after we run the iterator. We use this elastic byte structure to hold the bytes until then. + * until after we run the iterator. We use this elastic byte structure to hold the bytes + * until then. */ FragmentList fragList = new BaseBytes.FragmentList(); fragList.loadFrom(iter); @@ -464,29 +491,30 @@ // Stitch the fragments together in the space we made fragList.emptyInto(storage, start + offset); } + } else { // This is an extended slice which means we are replacing elements int n = sliceLength(start, stop, step); - if (n != fragList.totalCount) throw SliceSizeError("bytes", fragList.totalCount, n); + if (n != fragList.totalCount) { + throw SliceSizeError("bytes", fragList.totalCount, n); + } fragList.emptyInto(storage, start + offset, step); } } +// Idiom: +// if (step == 1) { +// // Do something efficient with block start...stop-1 +// } else { +// int n = sliceLength(start, stop, step); +// for (int i = start, j = 0; j < n; i += step, j++) { +// // Perform jth operation with element i +// } +// } -// Idiom: -// if (step == 1) { -// // Do something efficient with block start...stop-1 -// } else { -// int n = sliceLength(start, stop, step); -// for (int i = start, j = 0; j < n; i += step, j++) { -// // Perform jth operation with element i -// } -// } - - - /* * Deletes an element from the sequence (and closes up the gap). + * * @param index index of the element to delete. */ protected synchronized void del(int index) { @@ -496,17 +524,18 @@ /* * Deletes contiguous sub-sequence (and closes up the gap). + * * @param start the position of the first element. + * * @param stop one more than the position of the last element. */ protected synchronized void delRange(int start, int stop) { - // XXX Use the specialised storageDelete() - storageReplace(start, stop-start, 0); + storageDelete(start, stop - start); } /** * Deletes a simple or extended slice and closes up the gap(s). - * + * * @param start the position of the first element. * @param stop one more than the position of the last element. * @param step from one element to the next @@ -532,22 +561,20 @@ } /** - * Convenience method to create a ValueError PyException with the message + * Convenience method to build (but not throw) a ValueError PyException with the message * "attempt to assign {type} of size {valueSize} to extended slice of size {sliceSize}" - * + * * @param valueType * @param valueSize size of sequence being assigned to slice * @param sliceSize size of slice expected to receive - * @throws PyException (ValueError) as detailed + * @return PyException (ValueError) as detailed */ - public static PyException SliceSizeError(String valueType, int valueSize, int sliceSize) - throws PyException { + public static PyException SliceSizeError(String valueType, int valueSize, int sliceSize) { String fmt = "attempt to assign %s of size %d to extended slice of size %d"; return Py.ValueError(String.format(fmt, valueType, valueSize, sliceSize)); - // XXX consider moving to SequenceIndexDelegate.java or somewhere else generic + // XXX consider moving to SequenceIndexDelegate.java or somewhere else generic, even Py } - /** * Initialise a mutable bytearray object from various arguments. This single initialisation must * support: @@ -566,17 +593,18 @@ * Construct as copy of any object implementing the buffer API.

    Although effectively * a constructor, it is possible to call __init__ on a 'used' object so the method does not * assume any particular prior state. - * + * * @param args argument array according to Jython conventions - * @param kwds Keywords according to Jython conventions + * @param kwds Keywords according to Jython conventions * @throws PyException in the same circumstances as bytearray(arg), TypeError for non-iterable, - * non-integer argument type, and ValueError if iterables do not yield byte [0..255] values. + * non-integer argument type, and ValueError if iterables do not yield byte [0..255] + * values. */ @ExposedNew @ExposedMethod(doc = BuiltinDocs.bytearray___init___doc) final synchronized void bytearray___init__(PyObject[] args, String[] kwds) { - - ArgParser ap = new ArgParser("bytearray", args, kwds, "source", "encoding", "errors"); + + ArgParser ap = new ArgParser("bytearray", args, kwds, "source", "encoding", "errors"); PyObject arg = ap.getPyObject(0, null); // If not null, encoding and errors must be PyString (or PyUnicode) PyObject encoding = ap.getPyObjectByType(1, PyBaseString.TYPE, null); @@ -598,58 +626,278 @@ throw Py.TypeError("encoding or errors without sequence argument"); } init((PyString)arg, encoding, errors); - + } else { // Now construct from arbitrary object (or null) init(arg); } } - - + + + /* ======================================================================================== + * Python API rich comparison operations + * ======================================================================================== + */ + @Override - public int __len__() { - return list___len__(); + public PyObject __eq__(PyObject other) { + return basebytes___eq__(other); } - @ExposedMethod(doc = BuiltinDocs.list___len___doc) - final int list___len__() { - return size; + @Override + public PyObject __ne__(PyObject other) { + return basebytes___ne__(other); } - + @Override + public PyObject __lt__(PyObject other) { + return basebytes___lt__(other); + } + + @Override + public PyObject __le__(PyObject other) { + return basebytes___le__(other); + } + + @Override + public PyObject __ge__(PyObject other) { + return basebytes___ge__(other); + } + + @Override + public PyObject __gt__(PyObject other) { + return basebytes___gt__(other); + } + + + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___eq___doc) + final synchronized PyObject bytearray___eq__(PyObject other) { + return basebytes___eq__(other); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___ne___doc) + final synchronized PyObject bytearray___ne__(PyObject other) { + return basebytes___ne__(other); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___lt___doc) + final synchronized PyObject bytearray___lt__(PyObject other) { + return basebytes___lt__(other); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___le___doc) + final synchronized PyObject bytearray___le__(PyObject other) { + return basebytes___le__(other); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___ge___doc) + final synchronized PyObject bytearray___ge__(PyObject other) { + return basebytes___ge__(other); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___gt___doc) + final synchronized PyObject bytearray___gt__(PyObject other) { + return basebytes___gt__(other); + } + +/* ======================================================================================== + * Python API for bytearray + * ======================================================================================== + */ + + @Override + public PyObject __add__(PyObject o) { + return bytearray___add__(o); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___add___doc) + final synchronized PyObject bytearray___add__(PyObject o) { + PyByteArray sum = null; + + if (o instanceof BaseBytes) { + BaseBytes ob = (BaseBytes)o; + // Quick route: allocate the right size bytearray and copy the two parts in. + sum = new PyByteArray(size + ob.size); + System.arraycopy(storage, offset, sum.storage, sum.offset, size); + System.arraycopy(ob.storage, ob.offset, sum.storage, sum.offset + size, ob.size); + + } else if (o.getType() == PyString.TYPE) { + // Support bytes type, which in in Python 2.7 is an alias of str. Remove in 3.0 + PyString os = (PyString)o; + // Allocate the right size bytearray and copy the two parts in. + sum = new PyByteArray(size + os.__len__()); + System.arraycopy(storage, offset, sum.storage, sum.offset, size); + sum.setslice(size, sum.size, 1, os); + + } else { + // Unsuitable type + // XXX note reversed order relative to __iadd__ may be wrong, matches Python 2.7 + throw ConcatenationTypeError(TYPE, o.getType()); + } + + return sum; + } + + + /** + * Returns the number of bytes actually allocated. + */ + public int __alloc__() { + return bytearray___alloc__(); + } + + @ExposedMethod(doc = BuiltinDocs.bytearray___alloc___doc) + final int bytearray___alloc__() { + return storage.length; + } + + /** + * Append a single element to the end of the array, equivalent to: + * s[len(s):len(s)] = o. The argument must be a PyInteger, PyLong or string of length 1. + * + * @param o the item to append to the list. + */ + public void append(PyObject o) { + bytearray_append(o); + } + + @ExposedMethod(doc = BuiltinDocs.bytearray_append_doc) + final synchronized void bytearray_append(PyObject o) { + // Insert at the end, checked for type and range + pyinsert(size, o); + } + + @ExposedMethod(doc = BuiltinDocs.bytearray___contains___doc) + final boolean bytearray___contains__(PyObject o) { + return basebytes___contains__(o); + } + + @ExposedMethod(doc = BuiltinDocs.bytearray_decode_doc) + final PyObject bytearray_decode(PyObject[] args, String[] keywords) { + return basebytes_decode(args, keywords); + } + + /** + * Implementation of Python find(sub). Return the lowest index in the byte array + * where byte sequence sub is found. Return -1 if sub is not found. + * + * @param sub bytes to find + * @return index of start of ocurrence of sub within this byte array + */ + public int find(PyObject sub) { + return basebytes_find(sub, null, null); + } + + /** + * Implementation of Python find( sub [, start ] ). Return the lowest index in the + * byte array where byte sequence sub is found, such that sub is + * contained in the slice [start:]. Return -1 if sub is not found. + * + * @param sub bytes to find + * @param start of slice to search + * @return index of start of ocurrence of sub within this byte array + */ + public int find(PyObject sub, PyObject start) { + return basebytes_find(sub, start, null); + } + + /** + * Implementation of Python find( sub [, start [, end ]] ). Return the lowest index + * in the byte array where byte sequence sub is found, such that sub + * is contained in the slice [start:end]. Arguments start and + * end (which may be null or Py.None ) are interpreted as + * in slice notation. Return -1 if sub is not found. + * + * @param sub bytes to find + * @param start of slice to search + * @param end of slice to search + * @return index of start of ocurrence of sub within this byte array + */ + public int find(PyObject sub, PyObject start, PyObject end) { + return basebytes_find(sub, start, end); + } + + @ExposedMethod(defaults = {"null", "null"}, doc = BuiltinDocs.bytearray_find_doc) + final int bytearray_find(PyObject sub, PyObject start, PyObject end) { + return basebytes_find(sub, start, end); + } + + + + /** + * Append the elements in the argument sequence to the end of the array, equivalent to: + * s[len(s):len(s)] = o. The argument must be a subclass of BaseBytes or an + * iterable type returning elements compatible with byte assignment. + * + * @param o the sequence of items to append to the list. + */ + public void extend(PyObject o) { + bytearray_extend(o); + } + + @ExposedMethod(doc = BuiltinDocs.bytearray_extend_doc) + final synchronized void bytearray_extend(PyObject o) { + // Use the general method, assigning to the crack at the end of the array. + // Note this deals with all legitimate PyObject types and the case o==this. + setslice(size, size, 1, o); + } + + @Override + public PyObject __iadd__(PyObject o) { + return bytearray___iadd__(o); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___iadd___doc) + final synchronized PyObject bytearray___iadd__(PyObject o) { + PyType oType = o.getType(); + if (oType == TYPE) { + // Use the general method, specifying the crack at the end of the array. + // Note this deals with the case o==this. + setslice(size, size, 1, (BaseBytes)o); + } else if (oType == PyString.TYPE) { + // Will fail if somehow not 8-bit clean + setslice(size, size, 1, (PyString)o); + } else { + // Unsuitable type + throw ConcatenationTypeError(oType, TYPE); + } + return this; + } + + /** + * Insert the argument element into the byte array at the specified index. + * Same as s[index:index] = [o] if index >= 0. + * + * @param index the position where the element will be inserted. + * @param value the element to insert. + */ + public void insert(PyObject index, PyObject value) { + bytearray_insert(index, value); + } + + @ExposedMethod(doc = BuiltinDocs.bytearray_insert_doc) + final synchronized void bytearray_insert(PyObject index, PyObject value) { + // XXX: do something with delegator instead? + pyinsert(boundToSequence(index.asIndex()), value); + } + + + @ExposedMethod(doc = BuiltinDocs.bytearray___len___doc) + final int bytearray___len__() { + return __len__(); + } + + @ExposedMethod(doc = BuiltinDocs.bytearray___reduce___doc) + final PyObject bytearray___reduce__() { + return basebytes___reduce__(); + } + + + // Based on PyList and not yet properly implemented. // -// @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___ne___doc) -// final synchronized PyObject bytearray___ne__(PyObject o) { -// return seq___ne__(o); -// } -// -// @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___eq___doc) -// final synchronized PyObject bytearray___eq__(PyObject o) { -// return seq___eq__(o); -// } -// -// @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___lt___doc) -// final synchronized PyObject bytearray___lt__(PyObject o) { -// return seq___lt__(o); -// } -// -// @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___le___doc) -// final synchronized PyObject bytearray___le__(PyObject o) { -// return seq___le__(o); -// } -// -// @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___gt___doc) -// final synchronized PyObject bytearray___gt__(PyObject o) { -// return seq___gt__(o); -// } -// -// @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___ge___doc) -// final synchronized PyObject bytearray___ge__(PyObject o) { -// return seq___ge__(o); -// } -// // @Override // public PyObject __imul__(PyObject o) { // return bytearray___imul__(o); @@ -714,37 +962,6 @@ // return repeat(o.asIndex(Py.OverflowError)); // } // -// @Override -// public PyObject __add__(PyObject o) { -// return bytearray___add__(o); -// } -// -// @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___add___doc) -// final synchronized PyObject bytearray___add__(PyObject o) { -// PyByteArray sum = null; -// if (o instanceof PySequenceList && !(o instanceof PyTuple)) { -// if (o instanceof PyByteArray) { -// List oList = ((PyByteArray) o).storage; -// List newList = new ArrayList(storage.size() + oList.size()); -// newList.addAll(storage); -// newList.addAll(oList); -// sum = fromList(newList); -// } -// } else if (!(o instanceof PySequenceList)) { -// // also support adding java lists (but not PyTuple!) -// Object oList = o.__tojava__(List.class); -// if (oList != Py.NoConversion && oList != null) { -// List otherList = (List) oList; -// sum = new PyByteArray(); -// sum.bytearray_extend(this); -// for (Iterator i = otherList.iterator(); i.hasNext();) { -// sum.add(i.next()); -// } -// } -// } -// return sum; -// } -// // @ExposedMethod(doc = BuiltinDocs.bytearray___contains___doc) // final synchronized boolean bytearray___contains__(PyObject o) { // return object___contains__(o); @@ -787,275 +1004,238 @@ // return super.unsupportedopMessage(op, o2); // } // -// public String toString() { -// return bytearray_toString(); -// } -// - @ExposedMethod(names = "__repr__", doc = BuiltinDocs.bytearray___repr___doc) - final synchronized String bytearray_toString() { - // XXX revisit: understand the thread state logic and use encode() -// ThreadState ts = Py.getThreadState(); -// if (!ts.enterRepr(this)) { -// return "[...]"; -// } - StringBuilder buf = new StringBuilder("bytearray(b'"); - final int last = size()-1; - for (int i=0; i<=last; i++) { - int element = intAt(i); - if (Character.isISOControl(element)) - buf.append(String.format("\\x%02x", element)); - else - buf.append((char)element); - } - buf.append("')"); -// ts.exitRepr(this); - return buf.toString(); + @Override + public String toString() { + return bytearray_toString(); } - - - - + @ExposedMethod(names = {"__repr__", "__str__"}, doc = BuiltinDocs.bytearray___repr___doc) + final synchronized String bytearray_toString() { + return "bytearray(b'" + asEscapedString() + "')"; + } + /* * ======================================================================================== * Manipulation of storage capacity * ======================================================================================== - * + * * Here we add to the inherited variables defining byte storage, the methods necessary to resize * it. - */ - + */ + /** * Choose a size appropriate to store the given number of bytes, with some room for growth. - * @param size - * @return n >= needed + * We'll be more generous than CPython for small array sizes to avoid needless reallocation. + * + * @param size of storage actually needed + * @return n >= size a recommended storage array size */ private static final int roundUp(int size) { - // XXX Consider equivalent case statement - int alloc = size + (size >> 3) + (size < 9 ? 3 : 6); // As CPython - // XXX What's a good allocation unit size here? - final int ALLOC = 8; - return (alloc+(ALLOC-1)) & ~(ALLOC-1); // round up to multiple of ALLOC - } - - /** - * Used mainly to prevent repeated attempts to shrink an array that is already minimal. - */ - private static final int minAlloc = roundUp(1); - - /** - * Decide whether a new storage array should be allocated (but don't do it). This method returns - * true if the needed storage is bigger than the allocated array length. - * - * @param needed number of bytes needed - * @return true if needed number of bytes justifies a new array - */ - private final boolean shouldGrow(int needed) { - return needed > storage.length; + /* + * The CPython formula is: size + (size >> 3) + (size < 9 ? 3 : 6). But when the array + * grows, CPython can use a realloc(), which will often be able to extend the allocation + * into memory already secretly allocated by the initial malloc(). Extension in Java means + * that we have to allocate a new array of bytes and copy to it. + */ + final int ALLOC = 16; // Must be power of two! + final int SIZE2 = 10; // Smallest size leading to a return value of 2*ALLOC + if (size >= SIZE2) { // Result > ALLOC, so work it out + // Same recommendation as Python, but rounded up to multiple of ALLOC + return (size + (size >> 3) + (6 + ALLOC - 1)) & ~(ALLOC - 1); + } else if (size > 0) { // Easy: save arithmetic + return ALLOC; + } else { // Very easy + return 0; + } } /** - * Decide whether a smaller storage array should be allocated (but don't do it). This method - * returns true if the needed storage size is much smaller than the allocated array length. - * + * Recommend a length for (but don't allocate) a new storage array, taking into account the + * current length and the number of bytes now needed. The returned value will always be at least + * as big as the argument (the length will always be sufficient). If the return value is equal + * to the present length, it is recommending no reallocation; otherwise, the return is either + * comfortably bigger than what is currently needed, or significantly smaller than currently + * allocated. This method embeds our policy for growing or shrinking the allocated array. + * * @param needed number of bytes needed - * @return true if needed number of bytes justifies a new array + * @return recommended amount to allocate */ - private final boolean shouldShrink(int needed) { - return needed == 0 || (needed * 2 + minAlloc) < storage.length; - } - - /** - * Decide whether a new storage array should be allocated (but don't do it). This method returns - * true if the needed storage is bigger, or much smaller, than the allocated array length. - * - * @param needed number of bytes needed - * @return true if needed number of bytes justifies a new array - */ - private final boolean shouldResize(int needed) { - return shouldGrow(needed) || shouldShrink(needed); + private final int recLength(int needed) { + final int L = storage.length; + if (needed > L || needed * 2 < L) { + // Amount needed is a lot less than current length, or + // more space we have, so recommend the new ideal size. + return roundUp(needed); + } else { + // Amount needed is less than current length, but it doesn't justify reallocation + return L; + } } /** * Allocate fresh storage for at least the requested number of bytes. Spare bytes are alloceted - * evenly at each end of the new storage by choice of a new value for offset. - * If the size needed is zero, the "storage" allocated is the shared emptyStorage array. + * evenly at each end of the new storage by choice of a new value for offset. If the size needed + * is zero, the "storage" allocated is the shared emptyStorage array. + * * @param needed becomes the new value of this.size */ protected void newStorage(int needed) { if (needed > 0) { - byte[] s = new byte[roundUp(needed)]; // guaranteed zero (by JLS 2ed para 4.5.5) - setStorage(s, needed, (s.length - needed) / 2); - } else + final int L = recLength(needed); + byte[] s = new byte[L]; // guaranteed zero (by JLS 2ed para 4.5.5) + setStorage(s, needed, (L - needed) / 2); + } else { setStorage(emptyStorage); + } } /** - * Ensure there is storage for at least the requested number of bytes, optionally clearing - * elements to zero. After the call, the needed number of bytes will be available, - * and if requested in the second parameter, they are guaranteed to be zero. - * @param needed number of bytes - * @param clear if true, storage bytes guaranteed zero - */ - private void newStorage(int needed, boolean clear) { - if (shouldResize(needed)) { - newStorage(needed); // guaranteed zero - } else { - setStorage(storage, needed, (storage.length - needed) / 2); - if (clear) { - Arrays.fill(storage, (byte)0); // guarantee zero - } - } - } - - - - /** - * Delete d elements at index a and prepare to insert - * e elements there by moving aside the surrounding elements. - * The method manipulates the storage array contents, size and - * offset. It will allocate a new array storage if necessary, - * or if desirable for efficiency. If the initial storage looks like this: + * Delete d elements at index a and prepare to insert e + * elements there by moving aside the surrounding elements. The method manipulates the + * storage array contents, size and offset. It will + * allocate a new array storage if necessary, or if desirable for efficiency. If + * the initial storage looks like this: + * *
          *       |-                  s                -|
          * |--f--|--------a--------|---d---|-----b-----|----------------|
          * 
    + * * then after the call the (possibly new) storage looks like this: + * *
          *            |-                   s'                -|
          * |----f'----|--------a--------|----e----|-----b-----|--------------|
          * 
    + * * where the contents of regions of length a and b=size-(a+d) have - * been preserved, although probably moved, and the gap between them has been adjusted to - * the requested size. + * been preserved, although probably moved, and the gap between them has been adjusted to the + * requested size. *

    * The effect on this PyByteArray is that: + * *

          * this.offset = f'
          * this.size = s' = a + e + b
          * 
    - * The method does not implement the Python repertoire of slice indices but avoids indexing - * outside the bytearray by silently adjusting a to be within it. - * Negative d or e is treated as 0 and if d is too large, it is truncated to the array end. - * @param a index of hole in byte array + * + * The method does not implement the Python repertoire of slice indices, or bound-check the + * sizes given, since code leading up to the call has done that. + * + * @param a index of hole in byte array * @param d number to discard (will discard x[a,a+d-1]) * @param e size of hole to open (will be x[a, a+e-1]) */ private void storageReplace(int a, int d, int e) { - int s = this.size; + final int c = e - d; // Change in overall size + if (c == 0) + { + return; // Everything stays where it is. + } - // Some of these should perhaps be errors but let's silently correct insane requests - if (a<0) a=0; else if (a>s) a = s; - if (d<0) d=0; else if (d>s-a) d = s-a; - if (e<0) e=0; + // Compute some handy points of reference + final int L = storage.length; + final int f = offset; + final int b = size - (a + d); // Count of B section + final int s2 = a + e + b; // Size of result s' + final int L2 = recLength(s2); // Length of storage for result - if (e != d) { - // Otherwise, everything stays where it is. - // Handy derived values: - int b = s - (a + d); // which is >= 0 - int s2 = a + e + b; // which is >= 0 - int f = this.offset; // Location of x[0] - int g = f + (a + d); // Location of x[-b] - - if (shouldShrink(s2)) { - if (s2 > 0) { - // We have far more storage than we need: shrink and copy both parts - newStorage(f, a, g, b, e); - } else { - // Need no storage as a+e+b = 0 - setStorage(emptyStorage); - } + if (L2 == L) { + // The result will definitely fit into the existing array - } else if (a < b) { + if (a <= b) { + // It would be less copying if we moved A=x[:a] not B=x[-b:]. // If B is to stay where it is, it means A will land here: - int f2 = f - (e - d); + final int f2 = f - c; if (f2 >= 0) { // ... which luckily is still inside the array if (a > 0) { System.arraycopy(storage, f, storage, f2, a); } - this.offset = f2; + offset = f2; size = s2; + } else { // ... which unfortunately is before the start of the array. - // We have to move both A and B and it might be time for a new array. - if (s2<=storage.length) { - // Repack it all in the existing array - newStorageAvoided(f, a, g, b, e); - } else { - newStorage(f, a, g, b, e); - } + // We have to move both A and B within the existing array + newStorageAvoided(a, d, b, e); } - } else /* a >= b */{ + } else /* a > b */{ + // It would be less copying if we moved B=x[-b:] not A=x[:a] // If A is to stay where it is, it means B will land here: - int g2 = g + (e - d); - if (g2 + b <= storage.length) { + final int g2 = f + a + e; + if (g2 + b <= L) { // ... which luckily leaves all of B inside the array if (b > 0) { - System.arraycopy(storage, g, storage, g2, b); + System.arraycopy(storage, g2 - c, storage, g2, b); } // this.offset is unchanged size = s2; + } else { // ... which unfortunately runs beyond the end of the array. - // We have to move both A and B and it might be time for a new array. - if (s2<=storage.length) { - // Repack it all in the existing array - newStorageAvoided(f, a, g, b, e); - } else { - newStorage(f, a, g, b, e); - } + // We have to move both A and B within the existing array + newStorageAvoided(a, d, b, e); } } + + } else if (L2 > 0) { + + // New storage (bigger or much smaller) is called for. Copy A and B to it. + newStorage(L2, a, d, b, e); + + } else { + + // We need no storage at all + setStorage(emptyStorage); + } + } - } - - /** * Use the existing storage but move two blocks within it to leave a gap of the required size. - * This is the strategy usually used when the array is still big enough to hold the required - * new value, but we can't leave either block fixed. - * If the initial storage looks like this: - * + * This is the strategy usually used when the array is still big enough to hold the required new + * value, but we can't leave either block fixed. If the initial storage looks like this: + * *
          * |-----f-----|--------a--------|---d---|----------b----------|----------|
          * 
    - * + * * then after the call the storage looks like this: - * + * *
          *        |-                             s'                          -|
          * |--f'--|--------a--------|---------e---------|----------b----------|---|
          * 
    - * + * * where the regions of length a and b=size-(a+d) have been preserved * and the gap between them adjusted to specification. The new offset f' is chosen heuristically * by the method to optimise the efficiency of repeated adjustment near either end of the array, * e.g. repeated prepend or append operations. The effect on this PyByteArray is that: - * + * *
          * this.offset = f'
          * this.size = s' = a+e+b
          * 
    - * - * Arguments are not checked for validity at all. - * a, e and b are non-negative and not all zero. - * - * @param f location (with offset) of A + * + * Arguments are not checked for validity at all. At call time, a, d, b, and e are + * non-negative and not all zero. + * * @param a length of A - * @param g = f+a+d location (with offset) of B + * @param d gap between A and B in current storage layout * @param b length of B - * @param e gap between A and B in new storage. + * @param e gap between A and B in new storage layout. */ - private void newStorageAvoided(int f, int a, int g, int b, int e) { + private void newStorageAvoided(int a, int d, int b, int e) { - // Shorthands - int s2 = a + e + b; + // Compute some handy points of reference + final int f = offset; + final int g = f + a + d; // Location of B section x[-b] + final int s2 = a + e + b; // Size of result s' // Choose the new offset f' to make prepend or append operations quicker. // E.g. if insertion was near the end (b small) put most of the new space at the end. @@ -1068,164 +1248,201 @@ long spare = storage.length - s2; f2 = (int)((spare * b) / (a + b)); } - // We have a new size and offset (but the same storage) - size = s2; - offset = f2; - + // This puts B at - int g2 = f2 + a + e; + final int g2 = f2 + a + e; // We can make do with the existing array. Do an in place copy. if (f2 + a > g) { // New A overlaps existing B so we must copy B first - if (b > 0) System.arraycopy(storage, g, storage, g2, b); - if (a > 0) System.arraycopy(storage, f, storage, f2, a); + if (b > 0) { + System.arraycopy(storage, g, storage, g2, b); + } + if (a > 0) { + System.arraycopy(storage, f, storage, f2, a); + } } else { // Safe to copy A first - if (a > 0) System.arraycopy(storage, f, storage, f2, a); - if (b > 0) System.arraycopy(storage, g, storage, g2, b); + if (a > 0) { + System.arraycopy(storage, f, storage, f2, a); + } + if (b > 0) { + System.arraycopy(storage, g, storage, g2, b); + } } + // We have a new size and offset (but the same storage) + size = s2; + offset = f2; + } - /** * Allocate new storage and copy two blocks from the current storage to it. If the initial * storage looks like this: - * + * *
          * |--f--|--------a--------|---d---|-----b-----|----------------|
          * 
    - * + * * then after the call the (definitely new) storage looks like this: - * + * *
          *            |-                   s'                -|
          * |----f'----|--------a--------|----e----|-----b-----|--------------|
          * 
    - * + * * where the regions of length a and b=size-(a+d) have been preserved * and the gap between them adjusted to specification. The new offset f' is chosen heuristically * by the method to optimise the efficiency of repeated adjustment near either end of the array, * e.g. repeated prepend or append operations. The effect on this PyByteArray is that: - * + * *
          * this.offset = f'
          * this.size = s' = a+e+b
          * 
    - * - * Arguments are not checked for validity at all. - * a, e and b are non-negative and not all zero. - * - * @param f location (with offset) of A + * + * Arguments are not checked for validity at all. At call time, a, e and b are + * non-negative and not all zero. + * + * @param L2 length of storage array to allocate (decided by caller) * @param a length of A - * @param g = f+a+d location (with offset) of B + * @param d gap between A and B in current storage layout * @param b length of B - * @param e gap between A and B in new storage. + * @param e gap between A and B in new storage layout. */ - private void newStorage(int f, int a, int g, int b, int e) { - // Enough room for the data and the gap - int s2 = a + e + b; - // Preserve a reference to the current data in the storage being discarded - byte[] source = this.storage; - // New storage with a bit of elbow-room - byte[] newStorage = new byte[roundUp(s2)]; - // Choose the new offset f' to make prepend or append operations quicker. + private void newStorage(int L2, int a, int d, int b, int e) { + + // Compute some handy points of reference + final int f = offset; + final int g = f + a + d; // Location of B section x[-b] + final int s2 = a + e + b; // Size of result s' + + // New storage as specified by caller + byte[] newStorage = new byte[L2]; + + // Choose the new offset f' to make repeated prepend or append operations quicker. // E.g. if insertion was near the end (b small) put most of the new space at the end. int f2; if (a == b) { // Mainly to trap the case a=b=0 - f2 = (newStorage.length - s2) / 2; + f2 = (L2 - s2) / 2; } else { // a and b are not both zero (since not equal) - long spare = newStorage.length - s2; + long spare = L2 - s2; f2 = (int)((spare * b) / (a + b)); } + + // Copy across the data from existing to new storage. + if (a > 0) { + System.arraycopy(storage, f, newStorage, f2, a); + } + if (b > 0) { + System.arraycopy(storage, g, newStorage, f2 + a + e, b); + } setStorage(newStorage, s2, f2); - - // Copy across the data - if (a > 0) System.arraycopy(source, f, storage, offset, a); - if (b > 0) System.arraycopy(source, g, storage, offset + (a + e), b); } - /** * Delete d elements at index a by moving together the surrounding * elements. The method manipulates the storage array, size and * offset, and will allocate a new storage array if necessary, or if the deletion * is big enough. If the initial storage looks like this: - * + * *
          * |-                           L                              -|
          *       |-                  s                -|
          * |--f--|--------a--------|---d---|-----b-----|----------------|
          * 
    - * + * * then after the call the (possibly new) storage looks like this: - * + * *
          * |-                 L'                     -|
          *      |-                  s'               -|
          * |-f'-|--------a--------|-----b-----|-------|
          * 
    - * + * * where the regions of length a and b=size-(a+d) have been preserved * and the gap between them eliminated. The effect on this PyByteArray is that: - * + * *
          * this.offset = f'
          * this.size = s' = a+b
          * 
    + * * The method does not implement the Python repertoire of slice indices but avoids indexing - * outside the bytearray by silently adjusting a to be within it. - * Negative d is treated as 0 and if d is too large, it is truncated to the array end. - * + * outside the bytearray by silently adjusting a to be within it. Negative d is treated as 0 and + * if d is too large, it is truncated to the array end. + * * @param a index of hole in byte array * @param d number to discard (will discard x[a,a+d-1]) * @param e size of hole to open (will be x[a, a+e-1]) */ private void storageDelete(int a, int d) { // storageReplace specialised for delete (e=0) - int s = this.size; - // Some of these should perhaps be errors but let's silently correct insane requests - if (a < 0) a = 0; else if (a > s) a = s; - if (d < 0) d = 0; else if (d > s - a) d = s - a; + if (d == 0) + { + return; // Everything stays where it is. + } - // Handy derived values - int b = s - (a + d); // which is >= 0 - int s2 = s - d; // which is >= 0 - int f = this.offset; // Location of x[0] - int g = f + (a + d); // Location of x[-b] + // Compute some handy points of reference + final int L = storage.length; + final int f = offset; + final int b = size - (a + d); // Count of B section + final int s2 = a + b; // Size of result s' + final int L2 = recLength(s2); // Length of storage for result - if (shouldShrink(s2)) { - // We have far more storage than we need: shrink and copy both parts - // Preserve a reference to the current data in the storage being discarded - byte[] source = this.storage; - // New storage with a bit of elbow-room - newStorage(s2); - // Copy across the data - if (a > 0) System.arraycopy(source, f, storage, offset, a); - if (b > 0) System.arraycopy(source, g, storage, offset + a, b); + if (L2 == L) { - } else { - if (a < b) { + // We are re-using the existing array + if (a <= b) { // It would be less copying if we moved A=x[:a] not B=x[-b:]. // If B is to stay where it is, it means A will land here: - int f2 = f + d; + final int f2 = f + d; if (a > 0) { System.arraycopy(storage, f, storage, f2, a); } - this.offset = f2; + offset = f2; + size = s2; - } else /* a >= b */{ + } else /* a > b */{ // It would be less copying if we moved B=x[-b:] not A=x[:a] // If A is to stay where it is, it means B will land here: - int g2 = f + a; + final int g2 = f + a; if (b > 0) { - System.arraycopy(storage, g, storage, g2, b); + System.arraycopy(storage, g2 + d, storage, g2, b); } + // this.offset is unchanged + size = s2; } + + } else if (L2 > 0) { + + // New storage (much smaller) is called for. Copy A and B to it. + final int g = f + a + d; // Location of B section x[-b] + + // Choose the new offset f' to distribute space evenly. + int f2 = (L2 - s2) / 2; + + // New storage as specified + byte[] newStorage = new byte[L2]; + + // Copy across the data from existing to new storage. + if (a > 0) { + System.arraycopy(storage, f, newStorage, f2, a); + } + if (b > 0) { + System.arraycopy(storage, g, newStorage, f2 + a, b); + } + setStorage(newStorage, s2, f2); + + } else { + + // Everything must go + setStorage(emptyStorage); + } } @@ -1234,46 +1451,48 @@ * a by moving together the surrounding elements. The method manipulates the * storage array, size and offset, and will allocate a * new storage array if the deletion is big enough. If the initial storage looks like this: - * + * *
          * |-                               L                                -|
          *       |-                    s                    -|
          * |--f--|-----a-----|---------e---------|-----b-----|----------------|
          * 
    - * + * * then after the call the (possibly new) storage looks like this: - * + * *
    -     * |-                  L'                  -|
    +     * |-                   L'                         -|
          *      |-                s'               -|
          * |-f'-|-----a-----|---(e-d)---|-----b-----|-------|
          * 
    - * + * * where the regions of length a and b=size-(a+e) have been preserved * and the e intervening elements reduced to e-d elements, by removing * exactly the elements with indices (relative to the start of valid data) a+k*c * for k=0...d-1. The effect on this PyByteArray is that: - * + * *
          * this.offset = f'
          * this.size = s' = a+b
          * 
    - * + * * The method does not implement the Python repertoire of slice indices but avoids indexing * outside the bytearray by silently adjusting a to be within it. Negative d is treated as 0 and * if d is too large, it is truncated to the array end. - * + * * @param a index of hole in byte array * @param c (>0) step size between the locations of elements to delete * @param d number to discard (will discard x[a+k*c] for k=0...d-1) */ private void storageDeleteEx(int a, int c, int d) { - + // XXX Base this on storageReplace with the same a>> for method in dir(bytearray): ... print method @@ -1355,4 +1574,4 @@ upper zfill >>> - */ + */ \ No newline at end of file diff --git a/src/org/python/core/PyByteArrayDerived.java b/src/org/python/core/PyByteArrayDerived.java --- a/src/org/python/core/PyByteArrayDerived.java +++ b/src/org/python/core/PyByteArrayDerived.java @@ -1,1116 +1,1116 @@ -/* Generated file, do not modify. See jython/src/templates/gderived.py. */ -package org.python.core; - -import java.io.Serializable; - -public class PyByteArrayDerived extends PyByteArray implements Slotted { - - public PyObject getSlot(int index) { - return slots[index]; - } - - public void setSlot(int index,PyObject value) { - slots[index]=value; - } - - private PyObject[]slots; - - private PyObject dict; - - public PyObject fastGetDict() { - return dict; - } - - public PyObject getDict() { - return dict; - } - - public void setDict(PyObject newDict) { - if (newDict instanceof PyStringMap||newDict instanceof PyDictionary) { - dict=newDict; - } else { - throw Py.TypeError("__dict__ must be set to a Dictionary "+newDict.getClass().getName()); - } - } - - public void delDict() { - // deleting an object's instance dict makes it grow a new one - dict=new PyStringMap(); - } - - public PyByteArrayDerived(PyType subtype) { - super(subtype); - slots=new PyObject[subtype.getNumSlots()]; - dict=subtype.instDict(); - } - - public PyString __str__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__str__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(); - if (res instanceof PyString) - return(PyString)res; - throw Py.TypeError("__str__"+" returned non-"+"string"+" (type "+res.getType().fastGetName()+")"); - } - return super.__str__(); - } - - public PyString __repr__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__repr__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(); - if (res instanceof PyString) - return(PyString)res; - throw Py.TypeError("__repr__"+" returned non-"+"string"+" (type "+res.getType().fastGetName()+")"); - } - return super.__repr__(); - } - - public PyString __hex__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__hex__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(); - if (res instanceof PyString) - return(PyString)res; - throw Py.TypeError("__hex__"+" returned non-"+"string"+" (type "+res.getType().fastGetName()+")"); - } - return super.__hex__(); - } - - public PyString __oct__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__oct__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(); - if (res instanceof PyString) - return(PyString)res; - throw Py.TypeError("__oct__"+" returned non-"+"string"+" (type "+res.getType().fastGetName()+")"); - } - return super.__oct__(); - } - - public PyFloat __float__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__float__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(); - if (res instanceof PyFloat) - return(PyFloat)res; - throw Py.TypeError("__float__"+" returned non-"+"float"+" (type "+res.getType().fastGetName()+")"); - } - return super.__float__(); - } - - public PyComplex __complex__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__complex__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(); - if (res instanceof PyComplex) - return(PyComplex)res; - throw Py.TypeError("__complex__"+" returned non-"+"complex"+" (type "+res.getType().fastGetName()+")"); - } - return super.__complex__(); - } - - public PyObject __pos__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__pos__"); - if (impl!=null) - return impl.__get__(this,self_type).__call__(); - return super.__pos__(); - } - - public PyObject __neg__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__neg__"); - if (impl!=null) - return impl.__get__(this,self_type).__call__(); - return super.__neg__(); - } - - public PyObject __abs__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__abs__"); - if (impl!=null) - return impl.__get__(this,self_type).__call__(); - return super.__abs__(); - } - - public PyObject __invert__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__invert__"); - if (impl!=null) - return impl.__get__(this,self_type).__call__(); - return super.__invert__(); - } - - public PyObject __reduce__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__reduce__"); - if (impl!=null) - return impl.__get__(this,self_type).__call__(); - return super.__reduce__(); - } - - public PyObject __add__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__add__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__add__(other); - } - - public PyObject __radd__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__radd__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__radd__(other); - } - - public PyObject __sub__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__sub__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__sub__(other); - } - - public PyObject __rsub__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__rsub__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__rsub__(other); - } - - public PyObject __mul__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__mul__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__mul__(other); - } - - public PyObject __rmul__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__rmul__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__rmul__(other); - } - - public PyObject __div__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__div__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__div__(other); - } - - public PyObject __rdiv__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__rdiv__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__rdiv__(other); - } - - public PyObject __floordiv__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__floordiv__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__floordiv__(other); - } - - public PyObject __rfloordiv__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__rfloordiv__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__rfloordiv__(other); - } - - public PyObject __truediv__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__truediv__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__truediv__(other); - } - - public PyObject __rtruediv__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__rtruediv__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__rtruediv__(other); - } - - public PyObject __mod__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__mod__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__mod__(other); - } - - public PyObject __rmod__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__rmod__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__rmod__(other); - } - - public PyObject __divmod__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__divmod__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__divmod__(other); - } - - public PyObject __rdivmod__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__rdivmod__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__rdivmod__(other); - } - - public PyObject __rpow__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__rpow__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__rpow__(other); - } - - public PyObject __lshift__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__lshift__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__lshift__(other); - } - - public PyObject __rlshift__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__rlshift__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__rlshift__(other); - } - - public PyObject __rshift__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__rshift__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__rshift__(other); - } - - public PyObject __rrshift__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__rrshift__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__rrshift__(other); - } - - public PyObject __and__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__and__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__and__(other); - } - - public PyObject __rand__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__rand__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__rand__(other); - } - - public PyObject __or__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__or__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__or__(other); - } - - public PyObject __ror__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__ror__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__ror__(other); - } - - public PyObject __xor__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__xor__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__xor__(other); - } - - public PyObject __rxor__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__rxor__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__rxor__(other); - } - - public PyObject __lt__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__lt__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__lt__(other); - } - - public PyObject __le__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__le__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__le__(other); - } - - public PyObject __gt__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__gt__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__gt__(other); - } - - public PyObject __ge__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__ge__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__ge__(other); - } - - public PyObject __eq__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__eq__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__eq__(other); - } - - public PyObject __ne__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__ne__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__ne__(other); - } - - public PyObject __iadd__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__iadd__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__iadd__(other); - } - - public PyObject __isub__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__isub__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__isub__(other); - } - - public PyObject __imul__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__imul__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__imul__(other); - } - - public PyObject __idiv__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__idiv__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__idiv__(other); - } - - public PyObject __ifloordiv__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__ifloordiv__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__ifloordiv__(other); - } - - public PyObject __itruediv__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__itruediv__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__itruediv__(other); - } - - public PyObject __imod__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__imod__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__imod__(other); - } - - public PyObject __ipow__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__ipow__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__ipow__(other); - } - - public PyObject __ilshift__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__ilshift__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__ilshift__(other); - } - - public PyObject __irshift__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__irshift__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__irshift__(other); - } - - public PyObject __iand__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__iand__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__iand__(other); - } - - public PyObject __ior__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__ior__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__ior__(other); - } - - public PyObject __ixor__(PyObject other) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__ixor__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__ixor__(other); - } - - public PyObject __int__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__int__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(); - if (res instanceof PyInteger||res instanceof PyLong) - return res; - throw Py.TypeError("__int__"+" should return an integer"); - } - return super.__int__(); - } - - public PyObject __long__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__long__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(); - if (res instanceof PyLong||res instanceof PyInteger) - return res; - throw Py.TypeError("__long__"+" returned non-"+"long"+" (type "+res.getType().fastGetName()+")"); - } - return super.__long__(); - } - - public int hashCode() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__hash__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(); - if (res instanceof PyInteger) { - return((PyInteger)res).getValue(); - } else - if (res instanceof PyLong) { - return((PyLong)res).getValue().intValue(); - } - throw Py.TypeError("__hash__ should return a int"); - } - if (self_type.lookup("__eq__")!=null||self_type.lookup("__cmp__")!=null) { - throw Py.TypeError(String.format("unhashable type: '%.200s'",getType().fastGetName())); - } - return super.hashCode(); - } - - public PyUnicode __unicode__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__unicode__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(); - if (res instanceof PyUnicode) - return(PyUnicode)res; - if (res instanceof PyString) - return new PyUnicode((PyString)res); - throw Py.TypeError("__unicode__"+" should return a "+"unicode"); - } - return super.__unicode__(); - } - - public int __cmp__(PyObject other) { - PyType self_type=getType(); - PyObject[]where_type=new PyObject[1]; - PyObject impl=self_type.lookup_where("__cmp__",where_type); - // Full Compatibility with CPython __cmp__: - // If the derived type don't override __cmp__, the - // *internal* super().__cmp__ should be called, not the - // exposed one. The difference is that the exposed __cmp__ - // throws a TypeError if the argument is an instance of the same type. - if (impl==null||where_type[0]==TYPE||Py.isSubClass(TYPE,where_type[0])) { - return super.__cmp__(other); - } - PyObject res=impl.__get__(this,self_type).__call__(other); - if (res==Py.NotImplemented) { - return-2; - } - int c=res.asInt(); - return c<0?-1:c>0?1:0; - } - - public boolean __nonzero__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__nonzero__"); - if (impl==null) { - impl=self_type.lookup("__len__"); - if (impl==null) - return super.__nonzero__(); - } - PyObject o=impl.__get__(this,self_type).__call__(); - Class c=o.getClass(); - if (c!=PyInteger.class&&c!=PyBoolean.class) { - throw Py.TypeError(String.format("__nonzero__ should return bool or int, returned %s",self_type.getName())); - } - return o.__nonzero__(); - } - - public boolean __contains__(PyObject o) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__contains__"); - if (impl==null) - return super.__contains__(o); - return impl.__get__(this,self_type).__call__(o).__nonzero__(); - } - - public int __len__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__len__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(); - if (res instanceof PyInteger) - return((PyInteger)res).getValue(); - throw Py.TypeError("__len__ should return a int"); - } - return super.__len__(); - } - - public PyObject __iter__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__iter__"); - if (impl!=null) - return impl.__get__(this,self_type).__call__(); - impl=self_type.lookup("__getitem__"); - if (impl==null) - return super.__iter__(); - return new PySequenceIter(this); - } - - public PyObject __iternext__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("next"); - if (impl!=null) { - try { - return impl.__get__(this,self_type).__call__(); - } catch (PyException exc) { - if (exc.match(Py.StopIteration)) - return null; - throw exc; - } - } - return super.__iternext__(); // ??? - } - - public PyObject __finditem__(PyObject key) { // ??? - PyType self_type=getType(); - PyObject impl=self_type.lookup("__getitem__"); - if (impl!=null) - try { - return impl.__get__(this,self_type).__call__(key); - } catch (PyException exc) { - if (exc.match(Py.LookupError)) - return null; - throw exc; - } - return super.__finditem__(key); - } - - public PyObject __finditem__(int key) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__getitem__"); - if (impl!=null) - try { - return impl.__get__(this,self_type).__call__(new PyInteger(key)); - } catch (PyException exc) { - if (exc.match(Py.LookupError)) - return null; - throw exc; - } - return super.__finditem__(key); - } - - public PyObject __getitem__(PyObject key) { - // Same as __finditem__, without swallowing LookupErrors. This allows - // __getitem__ implementations written in Python to raise custom - // exceptions (such as subclasses of KeyError). - // - // We are forced to duplicate the code, instead of defining __finditem__ - // in terms of __getitem__. That's because PyObject defines __getitem__ - // in terms of __finditem__. Therefore, we would end with an infinite - // loop when self_type.lookup("__getitem__") returns null: - // - // __getitem__ -> super.__getitem__ -> __finditem__ -> __getitem__ - // - // By duplicating the (short) lookup and call code, we are safe, because - // the call chains will be: - // - // __finditem__ -> super.__finditem__ - // - // __getitem__ -> super.__getitem__ -> __finditem__ -> super.__finditem__ - - PyType self_type=getType(); - PyObject impl=self_type.lookup("__getitem__"); - if (impl!=null) - return impl.__get__(this,self_type).__call__(key); - return super.__getitem__(key); - } - - public void __setitem__(PyObject key,PyObject value) { // ??? - PyType self_type=getType(); - PyObject impl=self_type.lookup("__setitem__"); - if (impl!=null) { - impl.__get__(this,self_type).__call__(key,value); - return; - } - super.__setitem__(key,value); - } - - public PyObject __getslice__(PyObject start,PyObject stop,PyObject step) { // ??? - if (step!=null) { - return __getitem__(new PySlice(start,stop,step)); - } - PyType self_type=getType(); - PyObject impl=self_type.lookup("__getslice__"); - if (impl!=null) { - PyObject[]indices=PySlice.indices2(this,start,stop); - return impl.__get__(this,self_type).__call__(indices[0],indices[1]); - } - return super.__getslice__(start,stop,step); - } - - public void __setslice__(PyObject start,PyObject stop,PyObject step,PyObject value) { - if (step!=null) { - __setitem__(new PySlice(start,stop,step),value); - return; - } - PyType self_type=getType(); - PyObject impl=self_type.lookup("__setslice__"); - if (impl!=null) { - PyObject[]indices=PySlice.indices2(this,start,stop); - impl.__get__(this,self_type).__call__(indices[0],indices[1],value); - return; - } - super.__setslice__(start,stop,step,value); - } - - public void __delslice__(PyObject start,PyObject stop,PyObject step) { - if (step!=null) { - __delitem__(new PySlice(start,stop,step)); - return; - } - PyType self_type=getType(); - PyObject impl=self_type.lookup("__delslice__"); - if (impl!=null) { - PyObject[]indices=PySlice.indices2(this,start,stop); - impl.__get__(this,self_type).__call__(indices[0],indices[1]); - return; - } - super.__delslice__(start,stop,step); - } - - public void __delitem__(PyObject key) { // ??? - PyType self_type=getType(); - PyObject impl=self_type.lookup("__delitem__"); - if (impl!=null) { - impl.__get__(this,self_type).__call__(key); - return; - } - super.__delitem__(key); - } - - public PyObject __call__(PyObject args[],String keywords[]) { - ThreadState ts=Py.getThreadState(); - if (ts.recursion_depth++>ts.systemState.getrecursionlimit()) - throw Py.RuntimeError("maximum __call__ recursion depth exceeded"); - try { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__call__"); - if (impl!=null) - return impl.__get__(this,self_type).__call__(args,keywords); - return super.__call__(args,keywords); - } finally { - --ts.recursion_depth; - } - } - - public PyObject __findattr_ex__(String name) { - return Deriveds.__findattr_ex__(this,name); - } - - public void __setattr__(String name,PyObject value) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__setattr__"); - if (impl!=null) { - impl.__get__(this,self_type).__call__(PyString.fromInterned(name),value); - return; - } - super.__setattr__(name,value); - } - - public void __delattr__(String name) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__delattr__"); - if (impl!=null) { - impl.__get__(this,self_type).__call__(PyString.fromInterned(name)); - return; - } - super.__delattr__(name); - } - - public PyObject __get__(PyObject obj,PyObject type) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__get__"); - if (impl!=null) { - if (obj==null) - obj=Py.None; - if (type==null) - type=Py.None; - return impl.__get__(this,self_type).__call__(obj,type); - } - return super.__get__(obj,type); - } - - public void __set__(PyObject obj,PyObject value) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__set__"); - if (impl!=null) { - impl.__get__(this,self_type).__call__(obj,value); - return; - } - super.__set__(obj,value); - } - - public void __delete__(PyObject obj) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__delete__"); - if (impl!=null) { - impl.__get__(this,self_type).__call__(obj); - return; - } - super.__delete__(obj); - } - - public PyObject __pow__(PyObject other,PyObject modulo) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__pow__"); - if (impl!=null) { - PyObject res; - if (modulo==null) { - res=impl.__get__(this,self_type).__call__(other); - } else { - res=impl.__get__(this,self_type).__call__(other,modulo); - } - if (res==Py.NotImplemented) - return null; - return res; - } - return super.__pow__(other,modulo); - } - - public void dispatch__init__(PyObject[]args,String[]keywords) { - Deriveds.dispatch__init__(this,args,keywords); - } - - public PyObject __index__() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__index__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(); - if (res instanceof PyInteger||res instanceof PyLong) { - return res; - } - throw Py.TypeError(String.format("__index__ returned non-(int,long) (type %s)",res.getType().fastGetName())); - } - return super.__index__(); - } - - public Object __tojava__(Class c) { - // If we are not being asked by the "default" conversion to java, then - // we can provide this as the result, as long as it is a instance of the - // specified class. Without this, derived.__tojava__(PyObject.class) - // would broke. (And that's not pure speculation: PyReflectedFunction's - // ReflectedArgs asks for things like that). - if ((c!=Object.class)&&(c!=Serializable.class)&&(c.isInstance(this))) { - return this; - } - // Otherwise, we call the derived __tojava__, if it exists: - PyType self_type=getType(); - PyObject impl=self_type.lookup("__tojava__"); - if (impl!=null) - return impl.__get__(this,self_type).__call__(Py.java2py(c)).__tojava__(Object.class); - return super.__tojava__(c); - } - - public Object __coerce_ex__(PyObject o) { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__coerce__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(o); - if (res==Py.NotImplemented) - return Py.None; - if (!(res instanceof PyTuple)) - throw Py.TypeError("__coerce__ didn't return a 2-tuple"); - return((PyTuple)res).getArray(); - } - return super.__coerce_ex__(o); - } - - public String toString() { - PyType self_type=getType(); - PyObject impl=self_type.lookup("__repr__"); - if (impl!=null) { - PyObject res=impl.__get__(this,self_type).__call__(); - if (!(res instanceof PyString)) - throw Py.TypeError("__repr__ returned non-string (type "+res.getType().fastGetName()+")"); - return((PyString)res).toString(); - } - return super.toString(); - } - -} +/* Generated file, do not modify. See jython/src/templates/gderived.py. */ +package org.python.core; + +import java.io.Serializable; + +public class PyByteArrayDerived extends PyByteArray implements Slotted { + + public PyObject getSlot(int index) { + return slots[index]; + } + + public void setSlot(int index,PyObject value) { + slots[index]=value; + } + + private PyObject[]slots; + + private PyObject dict; + + public PyObject fastGetDict() { + return dict; + } + + public PyObject getDict() { + return dict; + } + + public void setDict(PyObject newDict) { + if (newDict instanceof PyStringMap||newDict instanceof PyDictionary) { + dict=newDict; + } else { + throw Py.TypeError("__dict__ must be set to a Dictionary "+newDict.getClass().getName()); + } + } + + public void delDict() { + // deleting an object's instance dict makes it grow a new one + dict=new PyStringMap(); + } + + public PyByteArrayDerived(PyType subtype) { + super(subtype); + slots=new PyObject[subtype.getNumSlots()]; + dict=subtype.instDict(); + } + + public PyString __str__() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__str__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); + if (res instanceof PyString) + return(PyString)res; + throw Py.TypeError("__str__"+" returned non-"+"string"+" (type "+res.getType().fastGetName()+")"); + } + return super.__str__(); + } + + public PyString __repr__() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__repr__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); + if (res instanceof PyString) + return(PyString)res; + throw Py.TypeError("__repr__"+" returned non-"+"string"+" (type "+res.getType().fastGetName()+")"); + } + return super.__repr__(); + } + + public PyString __hex__() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__hex__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); + if (res instanceof PyString) + return(PyString)res; + throw Py.TypeError("__hex__"+" returned non-"+"string"+" (type "+res.getType().fastGetName()+")"); + } + return super.__hex__(); + } + + public PyString __oct__() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__oct__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); + if (res instanceof PyString) + return(PyString)res; + throw Py.TypeError("__oct__"+" returned non-"+"string"+" (type "+res.getType().fastGetName()+")"); + } + return super.__oct__(); + } + + public PyFloat __float__() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__float__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); + if (res instanceof PyFloat) + return(PyFloat)res; + throw Py.TypeError("__float__"+" returned non-"+"float"+" (type "+res.getType().fastGetName()+")"); + } + return super.__float__(); + } + + public PyComplex __complex__() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__complex__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); + if (res instanceof PyComplex) + return(PyComplex)res; + throw Py.TypeError("__complex__"+" returned non-"+"complex"+" (type "+res.getType().fastGetName()+")"); + } + return super.__complex__(); + } + + public PyObject __pos__() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__pos__"); + if (impl!=null) + return impl.__get__(this,self_type).__call__(); + return super.__pos__(); + } + + public PyObject __neg__() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__neg__"); + if (impl!=null) + return impl.__get__(this,self_type).__call__(); + return super.__neg__(); + } + + public PyObject __abs__() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__abs__"); + if (impl!=null) + return impl.__get__(this,self_type).__call__(); + return super.__abs__(); + } + + public PyObject __invert__() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__invert__"); + if (impl!=null) + return impl.__get__(this,self_type).__call__(); + return super.__invert__(); + } + + public PyObject __reduce__() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__reduce__"); + if (impl!=null) + return impl.__get__(this,self_type).__call__(); + return super.__reduce__(); + } + + public PyObject __add__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__add__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__add__(other); + } + + public PyObject __radd__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__radd__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__radd__(other); + } + + public PyObject __sub__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__sub__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__sub__(other); + } + + public PyObject __rsub__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__rsub__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__rsub__(other); + } + + public PyObject __mul__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__mul__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__mul__(other); + } + + public PyObject __rmul__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__rmul__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__rmul__(other); + } + + public PyObject __div__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__div__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__div__(other); + } + + public PyObject __rdiv__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__rdiv__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__rdiv__(other); + } + + public PyObject __floordiv__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__floordiv__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__floordiv__(other); + } + + public PyObject __rfloordiv__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__rfloordiv__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__rfloordiv__(other); + } + + public PyObject __truediv__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__truediv__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__truediv__(other); + } + + public PyObject __rtruediv__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__rtruediv__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__rtruediv__(other); + } + + public PyObject __mod__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__mod__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__mod__(other); + } + + public PyObject __rmod__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__rmod__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__rmod__(other); + } + + public PyObject __divmod__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__divmod__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__divmod__(other); + } + + public PyObject __rdivmod__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__rdivmod__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__rdivmod__(other); + } + + public PyObject __rpow__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__rpow__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__rpow__(other); + } + + public PyObject __lshift__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__lshift__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__lshift__(other); + } + + public PyObject __rlshift__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__rlshift__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__rlshift__(other); + } + + public PyObject __rshift__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__rshift__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__rshift__(other); + } + + public PyObject __rrshift__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__rrshift__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__rrshift__(other); + } + + public PyObject __and__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__and__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__and__(other); + } + + public PyObject __rand__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__rand__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__rand__(other); + } + + public PyObject __or__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__or__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__or__(other); + } + + public PyObject __ror__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__ror__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__ror__(other); + } + + public PyObject __xor__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__xor__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__xor__(other); + } + + public PyObject __rxor__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__rxor__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__rxor__(other); + } + + public PyObject __lt__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__lt__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__lt__(other); + } + + public PyObject __le__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__le__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__le__(other); + } + + public PyObject __gt__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__gt__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__gt__(other); + } + + public PyObject __ge__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__ge__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__ge__(other); + } + + public PyObject __eq__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__eq__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__eq__(other); + } + + public PyObject __ne__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__ne__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__ne__(other); + } + + public PyObject __iadd__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__iadd__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__iadd__(other); + } + + public PyObject __isub__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__isub__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__isub__(other); + } + + public PyObject __imul__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__imul__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__imul__(other); + } + + public PyObject __idiv__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__idiv__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__idiv__(other); + } + + public PyObject __ifloordiv__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__ifloordiv__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__ifloordiv__(other); + } + + public PyObject __itruediv__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__itruediv__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__itruediv__(other); + } + + public PyObject __imod__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__imod__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__imod__(other); + } + + public PyObject __ipow__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__ipow__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__ipow__(other); + } + + public PyObject __ilshift__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__ilshift__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__ilshift__(other); + } + + public PyObject __irshift__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__irshift__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__irshift__(other); + } + + public PyObject __iand__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__iand__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__iand__(other); + } + + public PyObject __ior__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__ior__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__ior__(other); + } + + public PyObject __ixor__(PyObject other) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__ixor__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__ixor__(other); + } + + public PyObject __int__() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__int__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); + if (res instanceof PyInteger||res instanceof PyLong) + return res; + throw Py.TypeError("__int__"+" should return an integer"); + } + return super.__int__(); + } + + public PyObject __long__() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__long__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); + if (res instanceof PyLong||res instanceof PyInteger) + return res; + throw Py.TypeError("__long__"+" returned non-"+"long"+" (type "+res.getType().fastGetName()+")"); + } + return super.__long__(); + } + + public int hashCode() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__hash__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); + if (res instanceof PyInteger) { + return((PyInteger)res).getValue(); + } else + if (res instanceof PyLong) { + return((PyLong)res).getValue().intValue(); + } + throw Py.TypeError("__hash__ should return a int"); + } + if (self_type.lookup("__eq__")!=null||self_type.lookup("__cmp__")!=null) { + throw Py.TypeError(String.format("unhashable type: '%.200s'",getType().fastGetName())); + } + return super.hashCode(); + } + + public PyUnicode __unicode__() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__unicode__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); + if (res instanceof PyUnicode) + return(PyUnicode)res; + if (res instanceof PyString) + return new PyUnicode((PyString)res); + throw Py.TypeError("__unicode__"+" should return a "+"unicode"); + } + return super.__unicode__(); + } + + public int __cmp__(PyObject other) { + PyType self_type=getType(); + PyObject[]where_type=new PyObject[1]; + PyObject impl=self_type.lookup_where("__cmp__",where_type); + // Full Compatibility with CPython __cmp__: + // If the derived type don't override __cmp__, the + // *internal* super().__cmp__ should be called, not the + // exposed one. The difference is that the exposed __cmp__ + // throws a TypeError if the argument is an instance of the same type. + if (impl==null||where_type[0]==TYPE||Py.isSubClass(TYPE,where_type[0])) { + return super.__cmp__(other); + } + PyObject res=impl.__get__(this,self_type).__call__(other); + if (res==Py.NotImplemented) { + return-2; + } + int c=res.asInt(); + return c<0?-1:c>0?1:0; + } + + public boolean __nonzero__() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__nonzero__"); + if (impl==null) { + impl=self_type.lookup("__len__"); + if (impl==null) + return super.__nonzero__(); + } + PyObject o=impl.__get__(this,self_type).__call__(); + Class c=o.getClass(); + if (c!=PyInteger.class&&c!=PyBoolean.class) { + throw Py.TypeError(String.format("__nonzero__ should return bool or int, returned %s",self_type.getName())); + } + return o.__nonzero__(); + } + + public boolean __contains__(PyObject o) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__contains__"); + if (impl==null) + return super.__contains__(o); + return impl.__get__(this,self_type).__call__(o).__nonzero__(); + } + + public int __len__() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__len__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); + if (res instanceof PyInteger) + return((PyInteger)res).getValue(); + throw Py.TypeError("__len__ should return a int"); + } + return super.__len__(); + } + + public PyObject __iter__() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__iter__"); + if (impl!=null) + return impl.__get__(this,self_type).__call__(); + impl=self_type.lookup("__getitem__"); + if (impl==null) + return super.__iter__(); + return new PySequenceIter(this); + } + + public PyObject __iternext__() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("next"); + if (impl!=null) { + try { + return impl.__get__(this,self_type).__call__(); + } catch (PyException exc) { + if (exc.match(Py.StopIteration)) + return null; + throw exc; + } + } + return super.__iternext__(); // ??? + } + + public PyObject __finditem__(PyObject key) { // ??? + PyType self_type=getType(); + PyObject impl=self_type.lookup("__getitem__"); + if (impl!=null) + try { + return impl.__get__(this,self_type).__call__(key); + } catch (PyException exc) { + if (exc.match(Py.LookupError)) + return null; + throw exc; + } + return super.__finditem__(key); + } + + public PyObject __finditem__(int key) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__getitem__"); + if (impl!=null) + try { + return impl.__get__(this,self_type).__call__(new PyInteger(key)); + } catch (PyException exc) { + if (exc.match(Py.LookupError)) + return null; + throw exc; + } + return super.__finditem__(key); + } + + public PyObject __getitem__(PyObject key) { + // Same as __finditem__, without swallowing LookupErrors. This allows + // __getitem__ implementations written in Python to raise custom + // exceptions (such as subclasses of KeyError). + // + // We are forced to duplicate the code, instead of defining __finditem__ + // in terms of __getitem__. That's because PyObject defines __getitem__ + // in terms of __finditem__. Therefore, we would end with an infinite + // loop when self_type.lookup("__getitem__") returns null: + // + // __getitem__ -> super.__getitem__ -> __finditem__ -> __getitem__ + // + // By duplicating the (short) lookup and call code, we are safe, because + // the call chains will be: + // + // __finditem__ -> super.__finditem__ + // + // __getitem__ -> super.__getitem__ -> __finditem__ -> super.__finditem__ + + PyType self_type=getType(); + PyObject impl=self_type.lookup("__getitem__"); + if (impl!=null) + return impl.__get__(this,self_type).__call__(key); + return super.__getitem__(key); + } + + public void __setitem__(PyObject key,PyObject value) { // ??? + PyType self_type=getType(); + PyObject impl=self_type.lookup("__setitem__"); + if (impl!=null) { + impl.__get__(this,self_type).__call__(key,value); + return; + } + super.__setitem__(key,value); + } + + public PyObject __getslice__(PyObject start,PyObject stop,PyObject step) { // ??? + if (step!=null) { + return __getitem__(new PySlice(start,stop,step)); + } + PyType self_type=getType(); + PyObject impl=self_type.lookup("__getslice__"); + if (impl!=null) { + PyObject[]indices=PySlice.indices2(this,start,stop); + return impl.__get__(this,self_type).__call__(indices[0],indices[1]); + } + return super.__getslice__(start,stop,step); + } + + public void __setslice__(PyObject start,PyObject stop,PyObject step,PyObject value) { + if (step!=null) { + __setitem__(new PySlice(start,stop,step),value); + return; + } + PyType self_type=getType(); + PyObject impl=self_type.lookup("__setslice__"); + if (impl!=null) { + PyObject[]indices=PySlice.indices2(this,start,stop); + impl.__get__(this,self_type).__call__(indices[0],indices[1],value); + return; + } + super.__setslice__(start,stop,step,value); + } + + public void __delslice__(PyObject start,PyObject stop,PyObject step) { + if (step!=null) { + __delitem__(new PySlice(start,stop,step)); + return; + } + PyType self_type=getType(); + PyObject impl=self_type.lookup("__delslice__"); + if (impl!=null) { + PyObject[]indices=PySlice.indices2(this,start,stop); + impl.__get__(this,self_type).__call__(indices[0],indices[1]); + return; + } + super.__delslice__(start,stop,step); + } + + public void __delitem__(PyObject key) { // ??? + PyType self_type=getType(); + PyObject impl=self_type.lookup("__delitem__"); + if (impl!=null) { + impl.__get__(this,self_type).__call__(key); + return; + } + super.__delitem__(key); + } + + public PyObject __call__(PyObject args[],String keywords[]) { + ThreadState ts=Py.getThreadState(); + if (ts.recursion_depth++>ts.systemState.getrecursionlimit()) + throw Py.RuntimeError("maximum __call__ recursion depth exceeded"); + try { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__call__"); + if (impl!=null) + return impl.__get__(this,self_type).__call__(args,keywords); + return super.__call__(args,keywords); + } finally { + --ts.recursion_depth; + } + } + + public PyObject __findattr_ex__(String name) { + return Deriveds.__findattr_ex__(this,name); + } + + public void __setattr__(String name,PyObject value) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__setattr__"); + if (impl!=null) { + impl.__get__(this,self_type).__call__(PyString.fromInterned(name),value); + return; + } + super.__setattr__(name,value); + } + + public void __delattr__(String name) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__delattr__"); + if (impl!=null) { + impl.__get__(this,self_type).__call__(PyString.fromInterned(name)); + return; + } + super.__delattr__(name); + } + + public PyObject __get__(PyObject obj,PyObject type) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__get__"); + if (impl!=null) { + if (obj==null) + obj=Py.None; + if (type==null) + type=Py.None; + return impl.__get__(this,self_type).__call__(obj,type); + } + return super.__get__(obj,type); + } + + public void __set__(PyObject obj,PyObject value) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__set__"); + if (impl!=null) { + impl.__get__(this,self_type).__call__(obj,value); + return; + } + super.__set__(obj,value); + } + + public void __delete__(PyObject obj) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__delete__"); + if (impl!=null) { + impl.__get__(this,self_type).__call__(obj); + return; + } + super.__delete__(obj); + } + + public PyObject __pow__(PyObject other,PyObject modulo) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__pow__"); + if (impl!=null) { + PyObject res; + if (modulo==null) { + res=impl.__get__(this,self_type).__call__(other); + } else { + res=impl.__get__(this,self_type).__call__(other,modulo); + } + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__pow__(other,modulo); + } + + public void dispatch__init__(PyObject[]args,String[]keywords) { + Deriveds.dispatch__init__(this,args,keywords); + } + + public PyObject __index__() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__index__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); + if (res instanceof PyInteger||res instanceof PyLong) { + return res; + } + throw Py.TypeError(String.format("__index__ returned non-(int,long) (type %s)",res.getType().fastGetName())); + } + return super.__index__(); + } + + public Object __tojava__(Class c) { + // If we are not being asked by the "default" conversion to java, then + // we can provide this as the result, as long as it is a instance of the + // specified class. Without this, derived.__tojava__(PyObject.class) + // would broke. (And that's not pure speculation: PyReflectedFunction's + // ReflectedArgs asks for things like that). + if ((c!=Object.class)&&(c!=Serializable.class)&&(c.isInstance(this))) { + return this; + } + // Otherwise, we call the derived __tojava__, if it exists: + PyType self_type=getType(); + PyObject impl=self_type.lookup("__tojava__"); + if (impl!=null) + return impl.__get__(this,self_type).__call__(Py.java2py(c)).__tojava__(Object.class); + return super.__tojava__(c); + } + + public Object __coerce_ex__(PyObject o) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__coerce__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(o); + if (res==Py.NotImplemented) + return Py.None; + if (!(res instanceof PyTuple)) + throw Py.TypeError("__coerce__ didn't return a 2-tuple"); + return((PyTuple)res).getArray(); + } + return super.__coerce_ex__(o); + } + + public String toString() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__repr__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); + if (!(res instanceof PyString)) + throw Py.TypeError("__repr__ returned non-string (type "+res.getType().fastGetName()+")"); + return((PyString)res).toString(); + } + return super.toString(); + } + +} diff --git a/tests/java/org/python/core/BaseBytesTest.java b/tests/java/org/python/core/BaseBytesTest.java --- a/tests/java/org/python/core/BaseBytesTest.java +++ b/tests/java/org/python/core/BaseBytesTest.java @@ -42,8 +42,10 @@ super(name); } - static PythonInterpreter interp = null; + /** Sometimes we need the interpreter to be initialised **/ + PythonInterpreter interp; + /** State for random data fills */ Random random; public static char toChar(int b) { @@ -102,17 +104,20 @@ /** * Compare expected and result array sections at specified locations and length. * - * @param expected reference values + * @param expected reference values (may be null iff len==0) * @param first first value to compare in expected values * @param result bytearray from method under test * @param start first value to compare in result values * @param len number of values to compare */ static void checkInts(int[] expected, int first, BaseBytes result, int start, int len) { - int end = first + len; - if (end > expected.length) end = expected.length; - for (int i = first, j = start; i < end; i++, j++) - assertEquals("element value", expected[i], result.intAt(j)); + if (len > 0) { + int end = first + len; + if (end > expected.length) end = expected.length; + for (int i = first, j = start; i < end; i++, j++) { + assertEquals("element value", expected[i], result.intAt(j)); + } + } } /** @@ -125,8 +130,9 @@ // Size must be the same assertEquals("size", expected.length, result.size()); // And each element - for (int i = 0; i < expected.length; i++) + for (int i = 0; i < expected.length; i++) { assertEquals("element value", expected[i], result.intAt(i)); + } } /** @@ -168,7 +174,7 @@ List list = new ArrayList(source.length); int choose = 0; for (int b : source) { - switch(choose++){ + switch (choose++) { case 0: PyInteger i = new PyInteger(b); list.add(i); @@ -210,6 +216,7 @@ BaseBytes a = getInstance(aRef); System.out.println(toString(a)); assertEquals(aRef.length, a.size()); + // init(int) at various sizes for (int n : new int[] {0, 1, 2, 7, 8, 9, MEDIUM, LARGE, HUGE}) { a = getInstance(n); @@ -231,8 +238,9 @@ assertEquals(a.size(), b.size()); // assertEquals(a.storage, b.storage); // Supposed to share? // Check we got the same bytes - for (int i = 0; i < a.size(); i++) + for (int i = 0; i < a.size(); i++) { assertEquals(a.intAt(i), b.intAt(i)); + } } /** @@ -424,9 +432,11 @@ for (int count = 0; count <= maxCount; count++) { // Reference answer int[] bRef = new int[count * L]; - for (int i = 0; i < count; i++) - for (int j = 0; j < L; j++) + for (int i = 0; i < count; i++) { + for (int j = 0; j < L; j++) { bRef[i * L + j] = aRef[j]; + } + } // Test BaseBytes b = a.repeat(count); // System.out.println(toString(b)); @@ -704,13 +714,16 @@ if (step == 1) { // This is a contiguous slice [start:stop] so we can share storage r = new MyBytes(); - if (stop > start) r.setStorage(storage, stop - start, start + offset); + if (stop > start) { + r.setStorage(storage, stop - start, start + offset); + } } else { // This is an extended slice [start:stop:step] so we have to copy elements from it r = new MyBytes(sliceLength(start, stop, step)); int iomax = r.size + r.offset; - for (int io = r.offset, jo = start; io < iomax; jo += step, io++) + for (int io = r.offset, jo = start; io < iomax; jo += step, io++) { r.storage[io] = storage[jo]; // Assign r[i] = this[j] + } } return r; } @@ -747,13 +760,16 @@ super(TYPE); int n = value.length; store = new byte[n]; - for (int i = 0; i < n; i++) + for (int i = 0; i < n; i++) { store[i] = (byte)value[i]; + } } @Override public MemoryView getMemoryView() { - if (mv == null) mv = new MemoryViewImpl(); + if (mv == null) { + mv = new MemoryViewImpl(); + } return mv; } @@ -823,26 +839,31 @@ private StringBuilder image = new StringBuilder(100); private void repeat(char c, int n) { - for (int i = 0; i < n; i++) + for (int i = 0; i < n; i++) { image.append(i == 0 ? '|' : ' ').append(c); + } } // Show in image s[pos:pos+n] (as 2*n characters) private void append(byte[] s, int pos, int n) { - if (pos < 0 || pos + n > s.length) return; + if (pos < 0 || pos + n > s.length) + return; for (int i = 0; i < n; i++) { int c = 0xff & ((int)s[pos + i]); - if (c == 0) + if (c == 0) { c = '.'; - else if (Character.isISOControl(c)) c = '#'; + } else if (Character.isISOControl(c)) { + c = '#'; + } image.append(i == 0 ? '|' : ' ').append(toChar(c)); } } // Show an extent of n bytes (as 2*n charactrs) public void padTo(int n) { - while (n > image.length()) + while (n > image.length()) { image.append(' '); + } } /** diff --git a/tests/java/org/python/core/PyByteArrayTest.java b/tests/java/org/python/core/PyByteArrayTest.java --- a/tests/java/org/python/core/PyByteArrayTest.java +++ b/tests/java/org/python/core/PyByteArrayTest.java @@ -21,12 +21,11 @@ super(name); } - /** * Generate character codes for in a pattern matching an intended deletion or slice to be * replaced. If c="adb", something like b'aaaaaaddddbbbb' where the 'd' characters should be * deleted or replaced in the slice operation. - * + * * @param na number of c.charAt(0) characters * @param nd number of c.charAt(1) characters * @param nb number of c.charAt(2) characters @@ -36,12 +35,15 @@ public static int[] patternInts(int na, int nd, int nb, String c) { int[] r = new int[na + nd + nb]; int p = 0; - for (int i = 0; i < na; i++) + for (int i = 0; i < na; i++) { r[p++] = c.charAt(0); - for (int i = 0; i < nd; i++) + } + for (int i = 0; i < nd; i++) { r[p++] = c.charAt(1); - for (int i = 0; i < nb; i++) + } + for (int i = 0; i < nb; i++) { r[p++] = c.charAt(2); + } return r; } @@ -49,7 +51,7 @@ * Generate character codes for 'a', 'D', 'b' in a pattern matching an intended deletion or * slice to be replaced. Something like b'aaaaaaddddbbbb' where the 'E' characters should be * deleted or replaced in the slice operation. - * + * * @param na number of a characters * @param nd number of D characters * @param nb number of b characters @@ -63,7 +65,7 @@ * Generate character codes for 'a', 'E', 'b' in a pattern matching an intended result of slice * replacement. Something like b'aaaaaaEEEbbbb' where the 'E' characters are the replacement in * the slice operation. - * + * * @param na number of a characters * @param ne number of E characters * @param nb number of b characters @@ -74,11 +76,11 @@ } /** - * Generate a tuple of int arrays at random in the range 0..255 for testing slice operations. - * In effect, the method generates 4 arrays of random data A, B, D, E and returns an array of three arrays formed thus: - * { A + D + B, A + E + B, E } where + means concatenation. This can be used to test slice - * assignment and deletion. - * + * Generate a tuple of int arrays at random in the range 0..255 for testing slice operations. In + * effect, the method generates 4 arrays of random data A, B, D, E and returns an array of three + * arrays formed thus: { A + D + B, A + E + B, E } where + means concatenation. This can be used + * to test slice assignment and deletion. + * * @param random the random generator * @param na the number of elements in A * @param nd the number of elements in D (the deleted material) @@ -90,34 +92,33 @@ int[] adb = new int[na + nd + nb]; int[] aeb = new int[na + ne + nb]; int[] e = new int[ne]; - int[][] ret = { adb, aeb, e }; - int p=0, q = 0; + int[][] ret = {adb, aeb, e}; + int p = 0, q = 0; // The A values go into adb and aeb for (int i = 0; i < na; i++) { int a = random.nextInt(256); - adb[p++] =a; - aeb[q++] =a; + adb[p++] = a; + aeb[q++] = a; } // The D values go into adb only for (int i = 0; i < nd; i++) { int d = random.nextInt(256); - adb[p++] =d; + adb[p++] = d; } // The E values go into e and aeb for (int i = 0; i < ne; i++) { int x = random.nextInt(256); - e[p++] =x; - aeb[q++] =x; + e[p++] = x; + aeb[q++] = x; } // The B values go into adb and aeb for (int i = 0; i < nb; i++) { int b = random.nextInt(256); - adb[p++] =b; - aeb[q++] =b; + adb[p++] = b; + aeb[q++] = b; } return ret; } - /** * Check result of slice operations, synthesised from the elements passed. This method accepts @@ -127,7 +128,7 @@ * B=X[-nb:N], D=X[na:nb] and E=Y[:ne], and posed the problem setslice( A + D + B, E ), where + * means concatenation in this expression, to which the answer should be A + E + B. This method * checks that the result is exactly that. - * + * * @param na the number of elements in A * @param nd the number of elements in D (the deleted material) * @param nb the number of elements in B @@ -146,13 +147,13 @@ // Check that B is preserved checkInts(x, na + nd, result, na + ne, nb); } - + /** * Check result of extended slice operations, synthesised from the elements passed. This method * accepts the 'dimensions' of a slice problem and tests whether a resulting byte array contains * the correct result. The result array has been filled from (the whole of) array x[], then * slice assignment took place from y[k] to element u[start + k*step]. - * + * * @param start * @param step * @param n number of steps @@ -163,57 +164,142 @@ public static void checkSlice(int start, int step, int n, int[] x, int[] y, BaseBytes u) { // Check the size is right assertEquals("size", x.length, u.size()); + if (step > 0) { + // Check before start of slice int px = 0, py = 0; - for (; px < start; px++) + for (; px < start; px++) { assertEquals("before slice", x[px], u.intAt(px)); + } + // Check slice-affected region at n assignments and n-1 gaps of length step-1. - if (n>0) assertEquals("first affected", y[py++], u.intAt(px++)); + if (n > 0) { + assertEquals("first affected", y[py++], u.intAt(px++)); + } + for (int i = 1; i < n; i++) { for (int j = 1; j < step; j++, px++) { assertEquals("in gap", x[px], u.intAt(px)); } assertEquals("next affected", y[py++], u.intAt(px++)); } + // Check after slice-affected region - for (; px < x.length; px++) + for (; px < x.length; px++) { assertEquals("after slice", x[px], u.intAt(px)); - + } + } else { // Negative step but easier to think about as a positive number step = -step; // Check after start of slice int px = x.length - 1, py = 0; - for (; px > start; --px) + for (; px > start; --px) { assertEquals("after slice", x[px], u.intAt(px)); + } + // Check slice-affected region at n assignments and n-1 gaps of length step-1. - if (n>0) assertEquals("first affected", y[py++], u.intAt(px--)); + if (n > 0) { + assertEquals("first affected", y[py++], u.intAt(px--)); + } + for (int i = 1; i < n; i++) { for (int j = 1; j < step; j++, px--) { assertEquals("in gap", x[px], u.intAt(px)); } assertEquals("next affected", y[py++], u.intAt(px--)); } + // Check before slice-affected region - for (; px >= 0; px--) + for (; px >= 0; px--) { assertEquals("before slice", x[px], u.intAt(px)); - + } } } - /* (non-Javadoc) + /** + * Check result of extended slice deletion operations, synthesised from the elements passed. + * This method accepts the 'dimensions' of a slice deletion problem and tests whether a + * resulting byte array contains the correct result. The result array has been filled from (the + * whole of) array x[], then slice deletion took place at original element u[start + k*step]. + * + * @param start + * @param step + * @param n number of steps (deletions) + * @param x source of the original data + * @param u the result to be tested against properly selected elements of x + */ + public static void checkDelSlice(int start, int step, int n, int[] x, BaseBytes u) { + // Check the size is right + assertEquals("size", x.length - n, u.size()); + + if (step > 0) { + + // Check before start of slice + int px = 0, pu = 0; + for (; px < start; px++) { + assertEquals("before slice", x[px], u.intAt(pu++)); + } + + // Check slice-affected region at n deletions and n-1 gaps of length step-1. + // px now points to the first element that should be missing from u + px++; + for (int i = 1; i < n; i++) { + for (int j = 1; j < step; j++, px++) { + assertEquals("in gap", x[px], u.intAt(pu++)); + } + // px now points to the i.th element that should be missing from u + px++; + } + + // Check after slice-affected region + for (; px < x.length; px++) { + assertEquals("after slice", x[px], u.intAt(pu++)); + } + + } else { + + // Negative step but easier to think about as a positive number + step = -step; + + // Check after start of slice + int px = x.length - 1, pu = u.size - 1; + for (; px > start; --px) { + assertEquals("after slice", x[px], u.intAt(pu--)); + } + + // Check slice-affected region at n assignments and n-1 gaps of length step-1. + // px now points to the first element that should be missing from u + px--; + for (int i = 1; i < n; i++) { + for (int j = 1; j < step; j++, px--) { + assertEquals("in gap", x[px], u.intAt(pu--)); + } + // px now points to the i.th element that should be missing from u + px--; + } + + // Check before slice-affected region + for (; px >= 0; px--) { + assertEquals("before slice", x[px], u.intAt(pu--)); + } + } + } + + /* + * (non-Javadoc) + * * @see org.python.core.BaseBytesTest#setUp() */ protected void setUp() throws Exception { super.setUp(); } - /** * Test method for {@link PyObject#__getslice__(PyObject, PyObject)}. - * + * * @see BaseBytes#getslice(int, int) */ public void test__getslice__2() { @@ -233,29 +319,33 @@ for (int start : posStart) { PyObject pyStart, pyStop, pyStart_L, pyStop_L; pyStart = new PyInteger(start); - if (start > 0) + if (start > 0) { // The other way of saying [start:stop] is [start-L:stop] pyStart_L = new PyInteger(start - L); - else + } else { // The other way of saying [0:stop] is [:stop] pyStart_L = Py.None; + } for (int stop = start; stop <= L; stop++) { // Make a reference answer by picking elements of aRef in slice pattern bList.clear(); for (int i = start; i < stop; i++) { - if (verbose >= 5) System.out.printf(" (%d,%d) i=%d\n", start, stop, i); + if (verbose >= 5) { + System.out.printf(" (%d,%d) i=%d\n", start, stop, i); + } bList.add(new PyInteger(aRef[i])); } // PyObject versions of stop - if (stop < L) + if (stop < L) { // The other way of saying [start:stop:+s] is [start:stop-L:+s] pyStop_L = new PyInteger(stop - L); - else + } else { // The other way of saying [start:L:+s] is [start::+s] pyStop_L = Py.None; + } pyStop = new PyInteger(stop); // Generate test result and check it @@ -273,7 +363,7 @@ /** * Common code to check {@link PyByteArray#__getslice__(PyObject, PyObject)} against a reference * answer. - * + * * @param a object under test * @param pyStart * @param pyStop @@ -285,21 +375,25 @@ PyObject pyStop, List bList, int verbose) { - if (verbose >= 4) System.out.printf(" __getslice__(%s,%s)\n", pyStart, pyStop); + if (verbose >= 4) { + System.out.printf(" __getslice__(%s,%s)\n", pyStart, pyStop); + } PyObject b = a.__getslice__(pyStart, pyStop); - if (verbose >= 3) System.out.println(toString((BaseBytes)b)); + if (verbose >= 3) { + System.out.println(toString((BaseBytes)b)); + } checkInts(bList, b); } /** * Test method for {@link PyObject#__getslice__(PyObject, PyObject, PyObject)}. - * + * * @see BaseBytes#getslice(int, int, int) */ public void test__getslice__3() { int verbose = 0; // __getslice__() deals with start, stop values also relative to the end. - String ver = "Quand je br?le et que tu t'enflammes ;"; + String ver = "Quand je br?le et que tu t'enflammes ;"; final int L = ver.length(); int[] aRef = toInts(ver); BaseBytes a = getInstance(aRef); @@ -318,30 +412,33 @@ for (int start : posStart) { PyObject pyStart, pyStop, pyStart_L, pyStop_L; pyStart = new PyInteger(start); - if (start > 0) + if (start > 0) { // The other way of saying [start:stop:+s] is [start-L:stop:+s] pyStart_L = new PyInteger(start - L); - else + } else { // The other way of saying [0:stop:+s] is [:stop:+s] pyStart_L = Py.None; + } for (int stop = start; stop <= L; stop++) { // Make a reference answer by picking elements of aRef in slice pattern bList.clear(); for (int i = start; i < stop; i += step) { - if (verbose >= 5) System.out.printf(" (%d,%d,%d) i=%d\n", start, stop, - step, i); + if (verbose >= 5) { + System.out.printf(" (%d,%d,%d) i=%d\n", start, stop, step, i); + } bList.add(new PyInteger(aRef[i])); } // PyObject versions of stop - if (stop < L) + if (stop < L) { // The other way of saying [start:stop:+s] is [start:stop-L:+s] pyStop_L = new PyInteger(stop - L); - else + } else { // The other way of saying [start:L:+s] is [start::+s] pyStop_L = Py.None; + } pyStop = new PyInteger(stop); // Generate test result and check it @@ -363,37 +460,41 @@ for (int step = -1; step > -4; step--) { PyInteger pyStep = new PyInteger(step); + for (int start : negStart) { PyObject pyStart, pyStop, pyStart_L, pyStop_L; pyStart = new PyInteger(start); - if (start < L - 1) + + if (start < L - 1) { // The other way of saying [start:stop:-s] is [start-L:stop:-s] pyStart_L = new PyInteger(start - L); - else + } else { // The other way of saying [L-1:stop:-s] is [:stop:-s] pyStart_L = Py.None; + } for (int stop = start; stop >= -1; stop--) { // Make a reference answer by picking elements of aRef in slice pattern bList.clear(); for (int i = start; i > stop; i += step) { - if (verbose >= 5) System.out.printf(" (%d,%d,%d) i=%d\n", start, stop, - step, i); + if (verbose >= 5) { + System.out.printf(" (%d,%d,%d) i=%d\n", start, stop, step, i); + } bList.add(new PyInteger(aRef[i])); } // PyObject versions of stop - if (stop >= 0) + if (stop >= 0) { // The other way of saying [start:stop:-s] is [start:stop-L:-s] pyStop_L = new PyInteger(stop - L); - else { + } else { // intended final value is 0, but [start:-1:-s] doesn't mean that - stop = -(L+1); // This does. + stop = -(L + 1); // This does. pyStop_L = Py.None; // And so does [start::-s] } pyStop = new PyInteger(stop); - + // Generate test result and check it doTest__getslice__3(a, pyStart, pyStop, pyStep, bList, verbose + 2); // Repeat same result specifying start relative to end @@ -410,7 +511,7 @@ /** * Common code to check {@link PyByteArray#__getslice__(PyObject, PyObject, PyObject)} against a * reference answer. - * + * * @param a answer from method under test * @param pyStart * @param pyStop @@ -424,13 +525,16 @@ PyObject pyStep, List bList, int verbose) { - if (verbose >= 4) System.out.printf(" __getslice__(%s,%s,%s)\n", pyStart, pyStop, pyStep); + if (verbose >= 4) { + System.out.printf(" __getslice__(%s,%s,%s)\n", pyStart, pyStop, pyStep); + } PyObject b = a.__getslice__(pyStart, pyStop, pyStep); - if (verbose >= 3) System.out.println(toString((BaseBytes)b)); + if (verbose >= 3) { + System.out.println(toString((BaseBytes)b)); + } checkInts(bList, b); } - - + /** * Test method for {@link PyByteArray#__setitem__(int,PyObject)}, and through it of * {@link PyByteArray#pyset(int,PyObject)}. @@ -464,7 +568,9 @@ fail("Exception not thrown for __setitem__(" + 0 + ", " + b + ")"); } catch (PyException pye) { assertEquals(Py.ValueError, pye.type); - if (verbose >= 2) System.out.printf(" Exception: %s\n", pye); + if (verbose >= 2) { + System.out.printf(" Exception: %s\n", pye); + } } } @@ -476,7 +582,9 @@ fail("Exception not thrown for __setitem__(" + i + ", x)"); } catch (PyException pye) { assertEquals(Py.IndexError, pye.type); - if (verbose >= 2) System.out.printf(" Exception: %s\n", pye); + if (verbose >= 2) { + System.out.printf(" Exception: %s\n", pye); + } } } @@ -501,8 +609,8 @@ Arrays.fill(eInts, 'E'); PyByteArray e = new PyByteArray(eInts); - for (int nd : ndList) - for (int na : naList) + for (int nd : ndList) { + for (int na : naList) { for (int nb : nbList) { int[] aRef = adbInts(na, nd, nb); int[] bRef = aebInts(na, ne, nb); @@ -512,9 +620,10 @@ byte[] oldStorage = b.storage; if (verbose >= 2) { - System.out.printf("setslice(%d,%d,%d,e[len=%d])\n", - na, na + nd, 1, ne); - if (verbose >= 3) System.out.println(toString(b)); + System.out.printf("setslice(%d,%d,%d,e[len=%d])\n", na, na + nd, 1, ne); + if (verbose >= 3) { + System.out.println(toString(b)); + } } b.setslice(na, na + nd, 1, e); @@ -522,11 +631,15 @@ if (verbose >= 2) { boolean avAlloc = (b.storage != oldStorage) && (bRef.length <= oldStorage.length); - if (b.storage.length * 2 < oldStorage.length) avAlloc = false; + if (b.storage.length * 2 < oldStorage.length) { + avAlloc = false; + } System.out.println(toString(b) + (avAlloc ? " avoidable new" : "")); } checkInts(bRef, b); } + } + } } // Insertions at a range of positions and all sizes with random data @@ -543,9 +656,9 @@ int[] nbList2 = {0, 1, BMAX}; - for (int na = 0; na <= AMAX; na++) + for (int na = 0; na <= AMAX; na++) { for (int nb : nbList2) { - for (int nd = 0; nd < DMAX; nd++) + for (int nd = 0; nd < DMAX; nd++) { for (int ne = 0; ne < EMAX; ne++) { PyByteArray u = x.getslice(0, na + nd + nb, 1); PyByteArray e = y.getslice(0, ne, 1); @@ -558,10 +671,14 @@ } } u.setslice(na, na + nd, 1, e); - if (verbose >= 1) System.out.println("u'= " + toString(u)); + if (verbose >= 1) { + System.out.println("u'= " + toString(u)); + } checkSlice(na, nd, nb, ne, xInts, yInts, u); } + } } + } } /** @@ -579,7 +696,7 @@ interp = new PythonInterpreter(); // Source of assigned values. - int[] eRef = randomInts(random, 2*SMALL, 'V', 'Z'); + int[] eRef = randomInts(random, 2 * SMALL, 'V', 'Z'); BaseBytes eFull = new BaseBytesTest.MyBytes(eRef); final int[] posStart = new int[] {0, 4, 10, 18, L - 9}; @@ -588,23 +705,28 @@ for (int start : posStart) { PyObject pyStart, pyStop, pyStart_L, pyStop_L; pyStart = new PyInteger(start); - if (start > 0) + if (start > 0) { // The other way of saying [start:stop] is [start-L:stop] pyStart_L = new PyInteger(start - L); - else + } else { // The other way of saying [0:stop] is [:stop] pyStart_L = Py.None; + } for (int stop : posStop) { - if (stop < start) continue; // Skip backwards cases + if (stop < start) + { + continue; // Skip backwards cases + } // PyObject versions of stop - if (stop < L) + if (stop < L) { // The other way of saying [start:stop] is [start:stop-L] pyStop_L = new PyInteger(stop - L); - else + } else { // The other way of saying [start:L] is [start:] pyStop_L = Py.None; + } pyStop = new PyInteger(stop); for (int n = 0; n <= eRef.length; n++) { @@ -628,7 +750,7 @@ /** * Common code to check {@link PyByteArray#__setslice__(PyObject, PyObject, PyObject)} against a * reference answer. - * + * * @param uRef to initialise the object to test * @param pyStart * @param pyStop @@ -643,7 +765,9 @@ BaseBytes eFull, int n, int[] eRef, - int start, int stop, int verbose) { + int start, + int stop, + int verbose) { PyByteArray u = getInstance(uRef); BaseBytes e = eFull.getslice(0, n, 1); if (verbose >= 4) { @@ -653,12 +777,14 @@ } // Now do the test u.__setslice__(pyStart, pyStop, e); - if (verbose >= 3) System.out.println("u'= " + toString(u)); - int nd = stop-start; - int nb = uRef.length-stop; + if (verbose >= 3) { + System.out.println("u'= " + toString(u)); + } + int nd = stop - start; + int nb = uRef.length - stop; checkSlice(start, nd, nb, n, uRef, eRef, u); } - + /** * Test method for {@link org.python.core.PyByteArray#setslice(int,int,int,PyObject)}, when the * slice to replace is extended (3-argument slice and step!=0). Note that PySequence checks and @@ -701,7 +827,9 @@ } } u.setslice(start, stop, step, e); - if (verbose >= 1) System.out.println("u'= " + toString(u)); + if (verbose >= 1) { + System.out.println("u'= " + toString(u)); + } checkSlice(start, step, n, uRef, eRef, u); } } @@ -734,7 +862,9 @@ } } u.setslice(start, stop, step, e); - if (verbose >= 1) System.out.println("u'= " + toString(u)); + if (verbose >= 1) { + System.out.println("u'= " + toString(u)); + } checkSlice(start, step, n, uRef, eRef, u); } } @@ -742,8 +872,7 @@ } } - - + /** * Test method for {@link org.python.core.PyByteArray#setslice(int,int,int,PyObject)}, when the * slice to replace is extended (3-argument slice and step!=0). Note that PySequence checks and @@ -784,15 +913,17 @@ PyByteArray u = getInstance(uRef); BaseBytes e = eFull.getslice(0, n, 1); if (verbose >= 2) { - System.out.printf("setslice(%d,%d,%d, e[len=%d])\n", - start, stop, step, n); + System.out.printf("setslice(%d,%d,%d, e[len=%d])\n", start, stop, step, + n); if (verbose >= 3) { System.out.println("u = " + toString(u)); System.out.println("e = " + toString(e)); } } u.__setslice__(pyStart, pyStop, pyStep, e); - if (verbose >= 1) System.out.println("u'= " + toString(u)); + if (verbose >= 1) { + System.out.println("u'= " + toString(u)); + } checkSlice(start, step, n, uRef, eRef, u); } } @@ -820,21 +951,22 @@ PyByteArray u = getInstance(uRef); BaseBytes e = eFull.getslice(0, n, 1); if (verbose >= 2) { - System.out.printf("setslice(%d,%d,%d, e[len=%d])\n", - start, stop, step, n); + System.out.printf("setslice(%d,%d,%d, e[len=%d])\n", start, stop, step, + n); if (verbose >= 3) { System.out.println("u = " + toString(u)); System.out.println("e = " + toString(e)); } } u.__setslice__(pyStart, pyStop, pyStep, e); - if (verbose >= 1) System.out.println("u'= " + toString(u)); + if (verbose >= 1) { + System.out.println("u'= " + toString(u)); + } checkSlice(start, step, n, uRef, eRef, u); } } } } - } /** @@ -842,40 +974,48 @@ * slice to replace is simple and contiguous (2-argument slice). */ public void testSetsliceTime() { - int verbose = 0; - timeSetslice(100, SMALL, 2*SMALL, verbose); - timeSetslice(100, MEDIUM, MEDIUM, verbose); - timeSetslice(10, LARGE, LARGE/5, verbose); - timeSetslice(10, HUGE, HUGE/5, verbose); + int verbose = 1; + timeSetslice(100, 200, SMALL, 2*SMALL, verbose); + timeSetslice(100, 200, MEDIUM, MEDIUM, verbose); + timeSetslice(1000, 20, LARGE, LARGE/5, verbose); +// timeSetslice(1000, 4, HUGE, HUGE/5, verbose); } /** * Tabulate the elapsed time for calls to setslice, for a given array size and maximum slice - * length to insert arrays of a range of sizes. - * - * @param repeats number of repeats over which to average + * length to insert arrays of a range of sizes. The aim is to demonstrate benefit from the + * centring of the occupied storage in the storage array as a whole and catch any drop-off in + * implementation that while functionally correct (gets the right value) is massively + * inefficient. + * + * @param trials number of trials over which to take minimum "uninterrupted" time + * @param repeats number of repeat calls in each trial, over which to average * @param N of bytearray subjected to the change * @param M Size of change (inserted, removed or replaced slice) - * @param verbose Control level of textual output 0=none CSV-style, 1=just the timings, etc.. + * @param verbose Control level of textual output 1=just the timings, 2=enumerate calls, etc.. */ - protected void timeSetslice(int repeats, int M, int N, int verbose) { + private void timeSetslice(int trials, int repeats, int N, int M, int verbose) { // Trials we intend to do: insertion at a variety of points. int[] startList = new int[11]; // 11 means 0%, 10%, 20%, ... 100% of N - for (int i = 0; i < startList.length; i++) + for (int i = 0; i < startList.length; i++) { startList[i] = N * i / (startList.length - 1); + } // Insertion slice sizes. int[] changeList = new int[11]; // 0%, ... 100% of M - for (int i = 0; i < changeList.length; i++) + for (int i = 0; i < changeList.length; i++) { changeList[i] = M * i / (changeList.length - 1); + } // We are going to tabulate this for each startList and changeList entry. long[][] elapsed = new long[startList.length][changeList.length]; // Initialise the timing record - for (int row = 0; row < startList.length; row++) - for (int col = 0; col < changeList.length; col++) + for (int row = 0; row < startList.length; row++) { + for (int col = 0; col < changeList.length; col++) { elapsed[row][col] = Long.MAX_VALUE; + } + } // Create test material as bytearrays int[] xRef = randomInts(random, N, 'u', 'z'); @@ -886,21 +1026,20 @@ // We will time repeated calls: need a fresh bytearray each time PyByteArray[] u = new PyByteArray[repeats]; - // Now take the shortest of 10 trials in each row and column - - // Work through the combinations necessary - for (int trial = 0; trial < 10; trial++) { - for (int row = 0; row < startList.length; row++) { - int na = startList[row]; - int nd = 0; - // nd = changeList[3]; // XXX experiment - // if (na+nd>N) nd = N-na; // XXX experiment - for (int col = 0; col < changeList.length; col++) { - int ne = changeList[col]; - int start = na; - int stop = na + nd; - // Data to replace the slice with - PyByteArray e = y.getslice(0, ne, 1); + // Now take the shortest of some number of trials in each row and column + for (int trial = 0; trial < trials; trial++) { + // Work through the combinations necessary + for (int irow = 0; irow < startList.length; irow++) { + int row = (irow + 5 * trial) % startList.length; // Shuffle order + int na = startList[row]; + int nd = 0; + for (int icol = 0; icol < changeList.length; icol++) { + int col = (icol + trial) % changeList.length; // Shuffle order + int ne = changeList[col]; + int start = na; + int stop = na + nd; + // Data to replace the slice with + PyByteArray e = y.getslice(0, ne, 1); if (trial == 0) { // First trial: do once untimed in order ensure classes loaded. @@ -912,34 +1051,39 @@ long t = doTimeSetslice(u, start, stop, e, x, -1); // Retain the shortest time so far - if (t < elapsed[row][col]) elapsed[row][col] = t; + if (t < elapsed[row][col]) { + elapsed[row][col] = t; + } } } } // Tabulate the time for each array size and change size - System.out.print(" N , na "); - for (int col = 0; col < changeList.length; col++) - System.out.printf(", ne=%7d", changeList[col]); - System.out.println(", elements inserted: time in microseconds."); + if (verbose >= 1) { + System.out.print(" N , na "); + for (int col = 0; col < changeList.length; col++) { + System.out.printf(", ne=%7d", changeList[col]); + } + System.out.println(", elements inserted: time in microseconds."); - for (int row = 0; row < startList.length; row++) { - System.out.printf("%8d, %8d", N, startList[row]); - for (int col = 0; col < changeList.length; col++) { - double usPerCall = (1e-3 * elapsed[row][col]) / repeats; - System.out.printf(", %10.3f", usPerCall); + for (int row = 0; row < startList.length; row++) { + System.out.printf("%8d, %8d", N, startList[row]); + for (int col = 0; col < changeList.length; col++) { + double usPerCall = (1e-3 * elapsed[row][col]) / repeats; + System.out.printf(", %10.3f", usPerCall); + // System.out.printf(", %10d", elapsed[row][col]); + } + System.out.println(); } - System.out.println(); } - } /** * Time trial of {@link PyByteArray#setslice(int,int,int)}. Every element of the array of test * objects will be initialised to the same value then the specified slice replacement will take * place, with the block of repetitions timed. - * + * * @param u array of test objects * @param start * @param stop @@ -949,26 +1093,25 @@ * @return elapsed time in nanoseconds for setslice operation on array of objects */ private long doTimeSetslice(PyByteArray[] u, - int start, - int stop, - BaseBytes e, - BaseBytes x, - int verbose) { + int start, + int stop, + BaseBytes e, + BaseBytes x, + int verbose) { - // The call is either to do a time trial (block of test objects) or one test + // The call is either to do a time trial (block of test objects) or one test of correctness int repeats = 1; - if (verbose < 0) + if (verbose < 0) { // We're doing a timed trial on an array of identical objects. repeats = u.length; - else - // We're testing the correctness of the code with one object. - repeats = 1; + } // Set up clean bytearray objects - for (int i = 0; i < repeats; i++) + for (int i = 0; i < repeats; i++) { u[i] = new PyByteArray(x); + } - // First trial: do once untimed in order ensure classes loaded. + // Display effects (if verbose) using first element only. PyByteArray v = u[0]; byte[] oldStorage = v.storage; @@ -981,23 +1124,29 @@ // Start the clock long beginTime = System.nanoTime(); // Do the work lots of times - for (int i = 0; i < repeats; i++) + for (int i = 0; i < repeats; i++) { u[i].setslice(start, stop, 1, e); + } // Stop the clock long t = System.nanoTime() - beginTime; // Diagnostic printout if (verbose >= 2) { - byte[] newStorage = v.storage; - boolean avAlloc = (newStorage != oldStorage); - if (newStorage.length * 2 < oldStorage.length) avAlloc = false; + // Was there a reallocation? + boolean avAlloc = (v.storage != oldStorage); + // Justified if ... + if (v.size * 2 <= oldStorage.length) { + avAlloc = false; + } + if (v.size > oldStorage.length) { + avAlloc = false; + } System.out.println("u'= " + toString(v) + (avAlloc ? " new" : "")); } return t; } - - + // /** // * Test method for {@link org.python.core.PyByteArray#del(int)}. // */ @@ -1012,9 +1161,453 @@ // fail("Not yet implemented"); // } // - - - + + /** + * Test method for {@link org.python.core.PyByteArray#delslice(int,int,int)}, when the + * slice to delete is simple (a contiguous 2-argument slice). + */ + public void testDelslice2() { + int verbose = 0; + + // Tests where we transform aaaaaDDDDbbbbb into aaaaabbbbb. + // Lists of the lengths to try, for each of the aaaa, DDDD, bbbb sections + int[] naList = {2, 5, 0}; // Interesting cases: slice is at start, or not at start + int[] ndList = {5, 20, 0}; // Slice to delete is small, large or zero + int[] nbList = {4, 7, 0}; // Interesting cases: slice is at end, or not at end + + for (int nd : ndList) { + for (int na : naList) { + for (int nb : nbList) { + int[] aRef = adbInts(na, nd, nb); + int[] bRef = aebInts(na, 0, nb); + + PyByteArray b = getInstance(aRef); + + byte[] oldStorage = b.storage; + + if (verbose >= 2) { + System.out.printf("delslice(%d,%d,%d)\n", + na, na + nd, 1); + if (verbose >= 3) { + System.out.println(toString(b)); + } + } + + b.delslice(na, na + nd, 1); + + if (verbose >= 2) { + // Was there a reallocation? + boolean avAlloc = (b.storage != oldStorage); + // Justified if ... + if (bRef.length * 2 <= oldStorage.length) { + avAlloc = false; + } + System.out.println(toString(b) + (avAlloc ? " avoidable new" : "")); + } + checkInts(bRef, b); + } + } + } + + // Deletions at a range of positions and all sizes with random data + + final int AMAX = SMALL; + final int BMAX = SMALL; + final int DMAX = MEDIUM; + + int[] xInts = randomInts(random, AMAX + DMAX + BMAX, 'u', 'z'); + PyByteArray x = getInstance(xInts); + // Use the checker for assignments, pretending to have assigned a zero length array. + // int[] yInts = new int[0]; + + int[] nbList2 = {0, 1, BMAX}; + + for (int na = 0; na <= AMAX; na++) { + for (int nb : nbList2) { + for (int nd = 0; nd < DMAX; nd++){ + PyByteArray u = x.getslice(0, na + nd + nb, 1); + if (verbose >= 2) { + System.out.printf("delslice(start=%d, stop=%d, step=%d)\n", + na, na + nd, 1); + if (verbose >= 3) { + System.out.println("u = " + toString(u)); + } + } + u.delslice(na, na + nd, 1); + if (verbose >= 1) { + System.out.println("u'= " + toString(u)); + } + checkSlice(na, nd, nb, 0, xInts, null, u); + } + } + } + } + + /** + * Test method for {@link PyByteArray#__delslice__(PyObject, PyObject, PyObject)}, when the + * slice to delete is simple (a contiguous 2-argument slice). + */ + public void test__delslice__2() { + int verbose = 0; + // __delslice__() deals with start, stop values also relative to the end. + + String ver = "Et tes p?leurs, alors que lasse,"; + final int L = ver.length(); + int[] uRef = toInts(ver); + + // Need interpreter (for Py.None and exceptions) + interp = new PythonInterpreter(); + + final int[] posStart = new int[] {0, 3, 7, 16, L - 1}; + final int[] posStop = new int[] {0, 3, 7, 16, L - 6, L}; +// final int[] posStart = new int[] {16}; +// final int[] posStop = new int[] {L}; + + for (int start : posStart) { + PyObject pyStart, pyStop, pyStart_L, pyStop_L; + pyStart = new PyInteger(start); + if (start > 0) { + // The other way of saying [start:stop] is [start-L:stop] + pyStart_L = new PyInteger(start - L); + } else { + // The other way of saying [0:stop] is [:stop] + pyStart_L = Py.None; + } + + for (int stop : posStop) { + if (stop < start) + { + continue; // Skip backwards cases + } + + // PyObject versions of stop + if (stop < L) { + // The other way of saying [start:stop] is [start:stop-L] + pyStop_L = new PyInteger(stop - L); + } else { + // The other way of saying [start:L] is [start:] + pyStop_L = Py.None; + } + pyStop = new PyInteger(stop); + + // Generate test result and check it + doTest__delslice__2(uRef, pyStart, pyStop, start, stop, verbose + 2); + // Repeat same result specifying start relative to end + doTest__delslice__2(uRef, pyStart_L, pyStop, start, stop, verbose); + // Repeat same result specifying stop relative to end + doTest__delslice__2(uRef, pyStart, pyStop_L, start, stop, verbose); + // Repeat same result specifying start and stop relative to end + doTest__delslice__2(uRef, pyStart_L, pyStop_L, start, stop, verbose); + } + } + } + + /** + * Common code to check {@link PyByteArray#__delslice__(PyObject, PyObject, PyObject)} against a + * reference answer. + * + * @param uRef to initialise the object to test + * @param pyStart + * @param pyStop + * @param verbose 0..4 control output + */ + private void doTest__delslice__2(int[] uRef, + PyObject pyStart, + PyObject pyStop, + int start, int stop, int verbose) { + PyByteArray u = getInstance(uRef); + if (verbose >= 4) { + System.out.printf(" __delslice__(%s,%s,1)\n", pyStart, pyStop); + System.out.println("u = " + toString(u)); + } + // Now do the test + u.__delslice__(pyStart, pyStop); + if (verbose >= 3) { + System.out.println("u'= " + toString(u)); + } + int nd = stop-start; + int nb = uRef.length-stop; + checkSlice(start, nd, nb, 0, uRef, null, u); + } + + + /** + * Test method for {@link org.python.core.PyByteArray#delslice(int,int,int)}, when the + * slice to delete is extended (3-argument slice and step!=0). Note that PySequence checks and + * converts arguments first, so we need only test with valid combinations of indices. + */ + public void test__delslice__3() { + int verbose = 0; + + // Need interpreter + interp = new PythonInterpreter(); + + // Source of assigned values. +// int[] eRef = randomInts(random, MEDIUM, 'A', 'H'); +// BaseBytes eFull = new BaseBytesTest.MyBytes(eRef); + + // Forwards cases + + int[] uRef = randomInts(random, MEDIUM, 'm', 's'); + + // Positive step sizes we will try + int[] posStep = {2, 3, 5, 8, 25, 100}; + + for (int start = 0; start < uRef.length; start++) { + PyInteger pyStart = new PyInteger(start); + // Bytes from start to end of array + int len = uRef.length - start; + for (int step : posStep) { + PyInteger pyStep = new PyInteger(step); + // Allowable number of deletions to end of array at given step size + int nmax = (len + step - 1) / step; + for (int n = 1; n <= nmax; n++) { + // Location of last i + int last = start + step * (n - 1) + 1; + // But any stop value in this range results in n deletions + for (int stop = last + 1; stop < last + step; stop++) { + PyInteger pyStop = new PyInteger(stop); + // Now do the test + PyByteArray u = getInstance(uRef); + if (verbose >= 2) { + System.out.printf("delslice(%d,%d,%d) (%d deletions)\n", + start, stop, step, n); + if (verbose >= 3) { + System.out.println("u = " + toString(u)); + } + } + u.__delslice__(pyStart, pyStop, pyStep); + if (verbose >= 1) { + System.out.println("u'= " + toString(u)); + } + checkDelSlice(start, step, n, uRef, u); + } + } + } + } + + // Negative step sizes we will try + int[] negStep = {-1, -2, -5, -8, -25, -100}; + + for (int start = uRef.length - 1; start >= 0; start--) { + PyInteger pyStart = new PyInteger(start); + // Bytes from slice start to start of array + int len = start + 1; + for (int step : negStep) { + PyInteger pyStep = new PyInteger(step); + // Allowable number of assignments to end of array at given step size + int nmax = (len + (-step) - 1) / (-step); + for (int n = 1; n <= nmax; n++) { + // Location of last i + int last = start + step * (n - 1) - 1; + // But any stop value in this range results in n deletions + for (int stop = last; stop > last - (-step) && stop >= 0; stop--) { + PyInteger pyStop = new PyInteger(stop); + // Now do the test + PyByteArray u = getInstance(uRef); + if (verbose >= 2) { + System.out.printf("delslice(%d,%d,%d) (%d deletions)\n", + start, stop, step, n); + if (verbose >= 3) { + System.out.println("u = " + toString(u)); + } + } + u.__delslice__(pyStart, pyStop, pyStep); + if (verbose >= 1) { + System.out.println("u'= " + toString(u)); + } + checkDelSlice(start, step, n, uRef, u); + } + } + } + } + + } + + /** + * Performance for {@link org.python.core.PyByteArray#delslice(int,int,int,PyObject)}, when the + * slice to replace is extended (3-argument slice). + */ + public void testDelsliceTime3() { + int verbose = 1; + timeDelslice3(100, 200, SMALL, verbose); + timeDelslice3(100, 200, MEDIUM, verbose); + timeDelslice3(10, 4, LARGE, verbose); + // timeDelslice3(10, 1, HUGE, verbose); + } + + /** + * Tabulate the elapsed time for calls to __delslice__, for a given array size and maximum + * step-size between deleted items length to delete. The aim is to demonstrate benefit from the + * centring of the occupied storage in the storage array as a whole and catch any drop-off in + * implementation that while functionally correct (gets the right value) is massively + * inefficient. + * + * @param trials number of trials over which to take minimum "uninterrupted" time + * @param repeats number of repeat calls in each trial, over which to average + * @param N of bytearray subjected to the change + * @param verbose Control level of textual output 1=just the timings, 2=eumerate calls, etc.. + */ + private void timeDelslice3(int trials, int repeats, int N, int verbose) { + + // Trials we intend to do: deletion from a variety of points. + int[] startList = new int[10]; // 10 means 0%, 10%, 20%, ... 90% of N + for (int i = 0; i < startList.length; i++) { + startList[i] = N * i / startList.length; + } + + // Deletion step sizes. Positive and negative of these will be used + int[] posStep = {1, 2, 3, 5, 10}; + int[] stepList = new int[2 * posStep.length]; + for (int i = 0; i < posStep.length; i++) { + stepList[posStep.length - i - 1] = posStep[i]; + stepList[posStep.length + i] = -posStep[i]; + } + + // We are going to tabulate this for each startList and changeList entry. + long[][] elapsed = new long[startList.length][stepList.length]; + // Initialise the timing record + for (int row = 0; row < startList.length; row++) { + for (int col = 0; col < stepList.length; col++) { + elapsed[row][col] = Long.MAX_VALUE; + } + } + + // Create test material as bytearrays + int[] xRef = randomInts(random, N, 'u', 'z'); + PyByteArray x = getInstance(xRef); + + // We will time repeated calls: need a fresh bytearray each time + PyByteArray[] u = new PyByteArray[repeats]; + + // Now take the shortest of some number of trials in each row and column + for (int trial = 0; trial < trials; trial++) { + + // Work through the combinations necessary + for (int irow = 0; irow < startList.length; irow++) { + int row = (irow + 5 * trial) % startList.length; // Shuffle order + int start = startList[row]; + for (int icol = 0; icol < stepList.length; icol++) { + int col = (icol + trial) % stepList.length; // Shuffle order + int step = stepList[col]; + int n; + + if (step > 0) { + // Deletions available from start location to end of array + n = (xRef.length - start + step-1) / step; + } else { + // Deletions available from start location to beginning of array + n = (start + (-step) ) / (-step); + } + + // Make objects of the arguments + PyInteger pyStart = new PyInteger(start); + PyObject pyStop = Py.None; + PyInteger pyStep = new PyInteger(step); + + if (trial == 0) { + // First trial: do once untimed in order ensure classes loaded. + doTimeDelslice3(u, pyStart, pyStop, pyStep, x, verbose); + checkDelSlice(start, step, n, xRef, u[0]); + } + + // Now do the trial properly + long t = doTimeDelslice3(u, pyStart, pyStop, pyStep, x, -1); + + // Retain the shortest time so far + if (t < elapsed[row][col]) { + elapsed[row][col] = t; + } + } + } + } + + // Tabulate the time for each array size and change size + + System.out.print(" N , start"); + for (int col = 0; col < stepList.length; col++) { + System.out.printf(", step=%5d", stepList[col]); + } + System.out.println(", deletion time in microseconds."); + + for (int row = 0; row < startList.length; row++) { + System.out.printf("%8d, %8d", N, startList[row]); + for (int col = 0; col < stepList.length; col++) { + double usPerCall = (1e-3 * elapsed[row][col]) / repeats; + System.out.printf(", %10.3f", usPerCall); + // System.out.printf(", %10d", elapsed[row][col]); + } + System.out.println(); + } + } + + /** + * Time trial of {@link PyByteArray#__delslice__(PyObject,PyObject,PyObject)}. Every element of + * the array of test objects will be initialised to the same value then the specified slice + * replacement will take place, with the block of repetitions timed. + * + * @param u array of test objects + * @param pyStart + * @param pyStop + * @param pyStep + * @param x value from which to initialise each test object + * @param verbose amount of output + * @return elapsed time in nanoseconds for delslice operation on array of objects + */ + private long doTimeDelslice3(PyByteArray[] u, + PyObject pyStart, + PyObject pyStop, + PyObject pyStep, + BaseBytes x, + int verbose) { + + // The call is either to do a time trial (block of test objects) or one test of correctness + int repeats = 1; + if (verbose < 0) { + // We're doing a timed trial on an array of identical objects. + repeats = u.length; + } + + // Set up clean bytearray objects + for (int i = 0; i < repeats; i++) { + u[i] = new PyByteArray(x); + } + + // Display effects (if verbose) using first element only. + PyByteArray v = u[0]; + byte[] oldStorage = v.storage; + + if (verbose >= 3) { + System.out.printf("__delslice__(%s,%s,%s)\n", pyStart, pyStop, pyStep); + System.out.println("u = " + toString(v)); + } + + // Start the clock + long beginTime = System.nanoTime(); + // Do the work lots of times + for (int i = 0; i < repeats; i++) { + u[i].__delslice__(pyStart, pyStop, pyStep); + } + // Stop the clock + long t = System.nanoTime() - beginTime; + + // Diagnostic printout + if (verbose >= 2) { + // Was there a reallocation? + boolean avAlloc = (v.storage != oldStorage); + // Justified if ... + if (v.size * 2 <= oldStorage.length) { + avAlloc = false; + } + if (v.size > oldStorage.length) { + avAlloc = false; + } + System.out.println("u'= " + toString(v) + (avAlloc ? " new" : "")); + } + + return t; + } + /* * Note that JUnit test classes extending this one inherit all the test* methods, and they will * be run by JUnit. Each test uses getInstance() methods where it might have used a constructor @@ -1056,6 +1649,6 @@ public PyByteArray getInstance(PyObject arg) throws PyException { return new PyByteArray(arg); } - - + + } -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Fri May 25 00:44:46 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Fri, 25 May 2012 00:44:46 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Add_relpath=2C_fixes_test=5F?= =?utf8?q?py=5Fcompile=2E?= Message-ID: http://hg.python.org/jython/rev/d3fd048e40bf changeset: 6665:d3fd048e40bf user: Frank Wierzbicki date: Thu May 24 15:44:40 2012 -0700 summary: Add relpath, fixes test_py_compile. files: Lib/posixpath.py | 17 ++++++++++++++++- 1 files changed, 16 insertions(+), 1 deletions(-) diff --git a/Lib/posixpath.py b/Lib/posixpath.py --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -19,7 +19,7 @@ "walk","expanduser","expandvars","normpath","abspath", "samefile", "curdir","pardir","sep","pathsep","defpath","altsep","extsep", - "devnull","realpath","supports_unicode_filenames"] + "devnull","realpath","supports_unicode_filenames", "relpath"] # strings representing various path-related bits and pieces curdir = '.' @@ -475,7 +475,22 @@ path = normpath(resolved) return path +def relpath(path, start=curdir): + """Return a relative version of a path""" + if not path: + raise ValueError("no path specified") + + start_list = [x for x in abspath(start).split(sep) if x] + path_list = [x for x in abspath(path).split(sep) if x] + + # Work out how much of the filepath is shared by start and path. + i = len(commonprefix([start_list, path_list])) + + rel_list = [pardir] * (len(start_list)-i) + path_list[i:] + if not rel_list: + return curdir + return join(*rel_list) def _ensure_str(obj): """Ensure obj is a string, otherwise raise a TypeError""" if isinstance(obj, basestring): -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Fri May 25 00:45:40 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Fri, 25 May 2012 00:45:40 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Update_for_2=2E7=2E0a2=2E?= Message-ID: http://hg.python.org/jython/rev/ac5609677c13 changeset: 6666:ac5609677c13 user: Frank Wierzbicki date: Thu May 24 15:45:34 2012 -0700 summary: Update for 2.7.0a2. files: README.txt | 7 +++++-- build.xml | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/README.txt b/README.txt --- a/README.txt +++ b/README.txt @@ -1,10 +1,13 @@ -Welcome to Jython 2.7.0a1 +Welcome to Jython 2.7.0a2 ========================= -This is the first alpha release of the 2.7.0 version of Jython. Thanks to +This is the second alpha release of the 2.7.0 version of Jython. Thanks to Adconion Media Group (http://www.adconion.com/) for sponsoring this release. Thanks to all who contributed to Jython. +This release fixes a bug that left site-packages out of the path. This caused +many problems, including a failure of ez_setup.py to work. + Please see the NEWS file for detailed release notes. The release was compiled on Ubuntu with JDK 6 and requires JDK 6 to run. diff --git a/build.xml b/build.xml --- a/build.xml +++ b/build.xml @@ -123,15 +123,15 @@ - - + + - + -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Fri May 25 00:47:15 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Fri, 25 May 2012 00:47:15 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Added_tag_2=2E7=2E0a2_for_ch?= =?utf8?q?angeset_ac5609677c13?= Message-ID: http://hg.python.org/jython/rev/9c148a201233 changeset: 6667:9c148a201233 user: Frank Wierzbicki date: Thu May 24 15:46:48 2012 -0700 summary: Added tag 2.7.0a2 for changeset ac5609677c13 files: .hgtags | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -59,3 +59,4 @@ 91332231a44804192e47ca25be334bab8ac8ea7c v2.5.2 c5567b7757e7ff086bdb10006ddbd513a727a803 v2.5.3b1 4f5e3c12edc040058d724d4469a53e8649e64420 v2.7.0a1 +ac5609677c1382f14d0e04178fe6dd4108e4c231 2.7.0a2 -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Fri May 25 13:38:23 2012 From: jython-checkins at python.org (alex.gronholm) Date: Fri, 25 May 2012 13:38:23 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Added_missing_get=5Fmagic=28?= =?utf8?q?=29_function_to_the_imp_module?= Message-ID: http://hg.python.org/jython/rev/c13e8fe91176 changeset: 6668:c13e8fe91176 user: Alex Gr?nholm date: Fri May 25 14:18:33 2012 +0300 summary: Added missing get_magic() function to the imp module files: src/org/python/modules/imp.java | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-) diff --git a/src/org/python/modules/imp.java b/src/org/python/modules/imp.java --- a/src/org/python/modules/imp.java +++ b/src/org/python/modules/imp.java @@ -266,6 +266,10 @@ return mod; } + public static PyObject get_magic() { + return new PyString("\u0003\u00f3\r\n"); + } + public static PyObject get_suffixes() { return new PyList(new PyObject[] {new PyTuple(new PyString(".py"), new PyString("r"), -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Tue May 29 03:00:52 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Tue, 29 May 2012 03:00:52 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_from=3A?= Message-ID: http://hg.python.org/jython/rev/518ed9d2c0d6 changeset: 6669:518ed9d2c0d6 user: Frank Wierzbicki date: Fri May 25 12:21:52 2012 -0700 summary: from: http://hg.python.org/cpython/Lib/test/test_warnings.py at 22db03646d9b files: Lib/test/test_warnings.py | 761 ++++++++++++++++++++++++++ 1 files changed, 761 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_warnings.py b/Lib/test/test_warnings.py new file mode 100644 --- /dev/null +++ b/Lib/test/test_warnings.py @@ -0,0 +1,761 @@ +from contextlib import contextmanager +import linecache +import os +import StringIO +import sys +import unittest +import subprocess +from test import test_support +from test.script_helper import assert_python_ok + +import warning_tests + +import warnings as original_warnings + +py_warnings = test_support.import_fresh_module('warnings', blocked=['_warnings']) +c_warnings = test_support.import_fresh_module('warnings', fresh=['_warnings']) + + at contextmanager +def warnings_state(module): + """Use a specific warnings implementation in warning_tests.""" + global __warningregistry__ + for to_clear in (sys, warning_tests): + try: + to_clear.__warningregistry__.clear() + except AttributeError: + pass + try: + __warningregistry__.clear() + except NameError: + pass + original_warnings = warning_tests.warnings + original_filters = module.filters + try: + module.filters = original_filters[:] + module.simplefilter("once") + warning_tests.warnings = module + yield + finally: + warning_tests.warnings = original_warnings + module.filters = original_filters + + +class BaseTest(unittest.TestCase): + + """Basic bookkeeping required for testing.""" + + def setUp(self): + # The __warningregistry__ needs to be in a pristine state for tests + # to work properly. + if '__warningregistry__' in globals(): + del globals()['__warningregistry__'] + if hasattr(warning_tests, '__warningregistry__'): + del warning_tests.__warningregistry__ + if hasattr(sys, '__warningregistry__'): + del sys.__warningregistry__ + # The 'warnings' module must be explicitly set so that the proper + # interaction between _warnings and 'warnings' can be controlled. + sys.modules['warnings'] = self.module + super(BaseTest, self).setUp() + + def tearDown(self): + sys.modules['warnings'] = original_warnings + super(BaseTest, self).tearDown() + + +class FilterTests(object): + + """Testing the filtering functionality.""" + + def test_error(self): + with original_warnings.catch_warnings(module=self.module) as w: + self.module.resetwarnings() + self.module.filterwarnings("error", category=UserWarning) + self.assertRaises(UserWarning, self.module.warn, + "FilterTests.test_error") + + def test_ignore(self): + with original_warnings.catch_warnings(record=True, + module=self.module) as w: + self.module.resetwarnings() + self.module.filterwarnings("ignore", category=UserWarning) + self.module.warn("FilterTests.test_ignore", UserWarning) + self.assertEqual(len(w), 0) + + def test_always(self): + with original_warnings.catch_warnings(record=True, + module=self.module) as w: + self.module.resetwarnings() + self.module.filterwarnings("always", category=UserWarning) + message = "FilterTests.test_always" + self.module.warn(message, UserWarning) + self.assertTrue(message, w[-1].message) + self.module.warn(message, UserWarning) + self.assertTrue(w[-1].message, message) + + def test_default(self): + with original_warnings.catch_warnings(record=True, + module=self.module) as w: + self.module.resetwarnings() + self.module.filterwarnings("default", category=UserWarning) + message = UserWarning("FilterTests.test_default") + for x in xrange(2): + self.module.warn(message, UserWarning) + if x == 0: + self.assertEqual(w[-1].message, message) + del w[:] + elif x == 1: + self.assertEqual(len(w), 0) + else: + raise ValueError("loop variant unhandled") + + def test_module(self): + with original_warnings.catch_warnings(record=True, + module=self.module) as w: + self.module.resetwarnings() + self.module.filterwarnings("module", category=UserWarning) + message = UserWarning("FilterTests.test_module") + self.module.warn(message, UserWarning) + self.assertEqual(w[-1].message, message) + del w[:] + self.module.warn(message, UserWarning) + self.assertEqual(len(w), 0) + + def test_once(self): + with original_warnings.catch_warnings(record=True, + module=self.module) as w: + self.module.resetwarnings() + self.module.filterwarnings("once", category=UserWarning) + message = UserWarning("FilterTests.test_once") + self.module.warn_explicit(message, UserWarning, "test_warnings.py", + 42) + self.assertEqual(w[-1].message, message) + del w[:] + self.module.warn_explicit(message, UserWarning, "test_warnings.py", + 13) + self.assertEqual(len(w), 0) + self.module.warn_explicit(message, UserWarning, "test_warnings2.py", + 42) + self.assertEqual(len(w), 0) + + def test_inheritance(self): + with original_warnings.catch_warnings(module=self.module) as w: + self.module.resetwarnings() + self.module.filterwarnings("error", category=Warning) + self.assertRaises(UserWarning, self.module.warn, + "FilterTests.test_inheritance", UserWarning) + + def test_ordering(self): + with original_warnings.catch_warnings(record=True, + module=self.module) as w: + self.module.resetwarnings() + self.module.filterwarnings("ignore", category=UserWarning) + self.module.filterwarnings("error", category=UserWarning, + append=True) + del w[:] + try: + self.module.warn("FilterTests.test_ordering", UserWarning) + except UserWarning: + self.fail("order handling for actions failed") + self.assertEqual(len(w), 0) + + def test_filterwarnings(self): + # Test filterwarnings(). + # Implicitly also tests resetwarnings(). + with original_warnings.catch_warnings(record=True, + module=self.module) as w: + self.module.filterwarnings("error", "", Warning, "", 0) + self.assertRaises(UserWarning, self.module.warn, 'convert to error') + + self.module.resetwarnings() + text = 'handle normally' + self.module.warn(text) + self.assertEqual(str(w[-1].message), text) + self.assertTrue(w[-1].category is UserWarning) + + self.module.filterwarnings("ignore", "", Warning, "", 0) + text = 'filtered out' + self.module.warn(text) + self.assertNotEqual(str(w[-1].message), text) + + self.module.resetwarnings() + self.module.filterwarnings("error", "hex*", Warning, "", 0) + self.assertRaises(UserWarning, self.module.warn, 'hex/oct') + text = 'nonmatching text' + self.module.warn(text) + self.assertEqual(str(w[-1].message), text) + self.assertTrue(w[-1].category is UserWarning) + +class CFilterTests(BaseTest, FilterTests): + module = c_warnings + +class PyFilterTests(BaseTest, FilterTests): + module = py_warnings + + +class WarnTests(unittest.TestCase): + + """Test warnings.warn() and warnings.warn_explicit().""" + + def test_message(self): + with original_warnings.catch_warnings(record=True, + module=self.module) as w: + self.module.simplefilter("once") + for i in range(4): + text = 'multi %d' %i # Different text on each call. + self.module.warn(text) + self.assertEqual(str(w[-1].message), text) + self.assertTrue(w[-1].category is UserWarning) + + def test_filename(self): + with warnings_state(self.module): + with original_warnings.catch_warnings(record=True, + module=self.module) as w: + warning_tests.inner("spam1") + self.assertEqual(os.path.basename(w[-1].filename), + "warning_tests.py") + warning_tests.outer("spam2") + self.assertEqual(os.path.basename(w[-1].filename), + "warning_tests.py") + + def test_stacklevel(self): + # Test stacklevel argument + # make sure all messages are different, so the warning won't be skipped + with warnings_state(self.module): + with original_warnings.catch_warnings(record=True, + module=self.module) as w: + warning_tests.inner("spam3", stacklevel=1) + self.assertEqual(os.path.basename(w[-1].filename), + "warning_tests.py") + warning_tests.outer("spam4", stacklevel=1) + self.assertEqual(os.path.basename(w[-1].filename), + "warning_tests.py") + + warning_tests.inner("spam5", stacklevel=2) + self.assertEqual(os.path.basename(w[-1].filename), + "test_warnings.py") + warning_tests.outer("spam6", stacklevel=2) + self.assertEqual(os.path.basename(w[-1].filename), + "warning_tests.py") + warning_tests.outer("spam6.5", stacklevel=3) + self.assertEqual(os.path.basename(w[-1].filename), + "test_warnings.py") + + warning_tests.inner("spam7", stacklevel=9999) + self.assertEqual(os.path.basename(w[-1].filename), + "sys") + + def test_missing_filename_not_main(self): + # If __file__ is not specified and __main__ is not the module name, + # then __file__ should be set to the module name. + filename = warning_tests.__file__ + try: + del warning_tests.__file__ + with warnings_state(self.module): + with original_warnings.catch_warnings(record=True, + module=self.module) as w: + warning_tests.inner("spam8", stacklevel=1) + self.assertEqual(w[-1].filename, warning_tests.__name__) + finally: + warning_tests.__file__ = filename + + def test_missing_filename_main_with_argv(self): + # If __file__ is not specified and the caller is __main__ and sys.argv + # exists, then use sys.argv[0] as the file. + if not hasattr(sys, 'argv'): + return + filename = warning_tests.__file__ + module_name = warning_tests.__name__ + try: + del warning_tests.__file__ + warning_tests.__name__ = '__main__' + with warnings_state(self.module): + with original_warnings.catch_warnings(record=True, + module=self.module) as w: + warning_tests.inner('spam9', stacklevel=1) + self.assertEqual(w[-1].filename, sys.argv[0]) + finally: + warning_tests.__file__ = filename + warning_tests.__name__ = module_name + + def test_missing_filename_main_without_argv(self): + # If __file__ is not specified, the caller is __main__, and sys.argv + # is not set, then '__main__' is the file name. + filename = warning_tests.__file__ + module_name = warning_tests.__name__ + argv = sys.argv + try: + del warning_tests.__file__ + warning_tests.__name__ = '__main__' + del sys.argv + with warnings_state(self.module): + with original_warnings.catch_warnings(record=True, + module=self.module) as w: + warning_tests.inner('spam10', stacklevel=1) + self.assertEqual(w[-1].filename, '__main__') + finally: + warning_tests.__file__ = filename + warning_tests.__name__ = module_name + sys.argv = argv + + def test_missing_filename_main_with_argv_empty_string(self): + # If __file__ is not specified, the caller is __main__, and sys.argv[0] + # is the empty string, then '__main__ is the file name. + # Tests issue 2743. + file_name = warning_tests.__file__ + module_name = warning_tests.__name__ + argv = sys.argv + try: + del warning_tests.__file__ + warning_tests.__name__ = '__main__' + sys.argv = [''] + with warnings_state(self.module): + with original_warnings.catch_warnings(record=True, + module=self.module) as w: + warning_tests.inner('spam11', stacklevel=1) + self.assertEqual(w[-1].filename, '__main__') + finally: + warning_tests.__file__ = file_name + warning_tests.__name__ = module_name + sys.argv = argv + + def test_warn_explicit_type_errors(self): + # warn_explicit() should error out gracefully if it is given objects + # of the wrong types. + # lineno is expected to be an integer. + self.assertRaises(TypeError, self.module.warn_explicit, + None, UserWarning, None, None) + # Either 'message' needs to be an instance of Warning or 'category' + # needs to be a subclass. + self.assertRaises(TypeError, self.module.warn_explicit, + None, None, None, 1) + # 'registry' must be a dict or None. + self.assertRaises((TypeError, AttributeError), + self.module.warn_explicit, + None, Warning, None, 1, registry=42) + + def test_bad_str(self): + # issue 6415 + # Warnings instance with a bad format string for __str__ should not + # trigger a bus error. + class BadStrWarning(Warning): + """Warning with a bad format string for __str__.""" + def __str__(self): + return ("A bad formatted string %(err)" % + {"err" : "there is no %(err)s"}) + + with self.assertRaises(ValueError): + self.module.warn(BadStrWarning()) + + +class CWarnTests(BaseTest, WarnTests): + module = c_warnings + + # As an early adopter, we sanity check the + # test_support.import_fresh_module utility function + def test_accelerated(self): + self.assertFalse(original_warnings is self.module) + self.assertFalse(hasattr(self.module.warn, 'func_code')) + +class PyWarnTests(BaseTest, WarnTests): + module = py_warnings + + # As an early adopter, we sanity check the + # test_support.import_fresh_module utility function + def test_pure_python(self): + self.assertFalse(original_warnings is self.module) + self.assertTrue(hasattr(self.module.warn, 'func_code')) + + +class WCmdLineTests(unittest.TestCase): + + def test_improper_input(self): + # Uses the private _setoption() function to test the parsing + # of command-line warning arguments + with original_warnings.catch_warnings(module=self.module): + self.assertRaises(self.module._OptionError, + self.module._setoption, '1:2:3:4:5:6') + self.assertRaises(self.module._OptionError, + self.module._setoption, 'bogus::Warning') + self.assertRaises(self.module._OptionError, + self.module._setoption, 'ignore:2::4:-5') + self.module._setoption('error::Warning::0') + self.assertRaises(UserWarning, self.module.warn, 'convert to error') + + def test_improper_option(self): + # Same as above, but check that the message is printed out when + # the interpreter is executed. This also checks that options are + # actually parsed at all. + rc, out, err = assert_python_ok("-Wxxx", "-c", "pass") + self.assertIn(b"Invalid -W option ignored: invalid action: 'xxx'", err) + + def test_warnings_bootstrap(self): + # Check that the warnings module does get loaded when -W + # is used (see issue #10372 for an example of silent bootstrap failure). + rc, out, err = assert_python_ok("-Wi", "-c", + "import sys; sys.modules['warnings'].warn('foo', RuntimeWarning)") + # '-Wi' was observed + self.assertFalse(out.strip()) + self.assertNotIn(b'RuntimeWarning', err) + +class CWCmdLineTests(BaseTest, WCmdLineTests): + module = c_warnings + +class PyWCmdLineTests(BaseTest, WCmdLineTests): + module = py_warnings + + +class _WarningsTests(BaseTest): + + """Tests specific to the _warnings module.""" + + module = c_warnings + + def test_filter(self): + # Everything should function even if 'filters' is not in warnings. + with original_warnings.catch_warnings(module=self.module) as w: + self.module.filterwarnings("error", "", Warning, "", 0) + self.assertRaises(UserWarning, self.module.warn, + 'convert to error') + del self.module.filters + self.assertRaises(UserWarning, self.module.warn, + 'convert to error') + + def test_onceregistry(self): + # Replacing or removing the onceregistry should be okay. + global __warningregistry__ + message = UserWarning('onceregistry test') + try: + original_registry = self.module.onceregistry + __warningregistry__ = {} + with original_warnings.catch_warnings(record=True, + module=self.module) as w: + self.module.resetwarnings() + self.module.filterwarnings("once", category=UserWarning) + self.module.warn_explicit(message, UserWarning, "file", 42) + self.assertEqual(w[-1].message, message) + del w[:] + self.module.warn_explicit(message, UserWarning, "file", 42) + self.assertEqual(len(w), 0) + # Test the resetting of onceregistry. + self.module.onceregistry = {} + __warningregistry__ = {} + self.module.warn('onceregistry test') + self.assertEqual(w[-1].message.args, message.args) + # Removal of onceregistry is okay. + del w[:] + del self.module.onceregistry + __warningregistry__ = {} + self.module.warn_explicit(message, UserWarning, "file", 42) + self.assertEqual(len(w), 0) + finally: + self.module.onceregistry = original_registry + + def test_default_action(self): + # Replacing or removing defaultaction should be okay. + message = UserWarning("defaultaction test") + original = self.module.defaultaction + try: + with original_warnings.catch_warnings(record=True, + module=self.module) as w: + self.module.resetwarnings() + registry = {} + self.module.warn_explicit(message, UserWarning, "", 42, + registry=registry) + self.assertEqual(w[-1].message, message) + self.assertEqual(len(w), 1) + self.assertEqual(len(registry), 1) + del w[:] + # Test removal. + del self.module.defaultaction + __warningregistry__ = {} + registry = {} + self.module.warn_explicit(message, UserWarning, "", 43, + registry=registry) + self.assertEqual(w[-1].message, message) + self.assertEqual(len(w), 1) + self.assertEqual(len(registry), 1) + del w[:] + # Test setting. + self.module.defaultaction = "ignore" + __warningregistry__ = {} + registry = {} + self.module.warn_explicit(message, UserWarning, "", 44, + registry=registry) + self.assertEqual(len(w), 0) + finally: + self.module.defaultaction = original + + def test_showwarning_missing(self): + # Test that showwarning() missing is okay. + text = 'del showwarning test' + with original_warnings.catch_warnings(module=self.module): + self.module.filterwarnings("always", category=UserWarning) + del self.module.showwarning + with test_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) + old_showwarning = self.module.showwarning + self.module.showwarning = 23 + try: + self.assertRaises(TypeError, self.module.warn, "Warning!") + finally: + self.module.showwarning = old_showwarning + + def test_show_warning_output(self): + # With showarning() missing, make sure that output is okay. + text = 'test show_warning' + with original_warnings.catch_warnings(module=self.module): + self.module.filterwarnings("always", category=UserWarning) + del self.module.showwarning + with test_support.captured_output('stderr') as stream: + warning_tests.inner(text) + result = stream.getvalue() + self.assertEqual(result.count('\n'), 2, + "Too many newlines in %r" % result) + first_line, second_line = result.split('\n', 1) + expected_file = os.path.splitext(warning_tests.__file__)[0] + '.py' + first_line_parts = first_line.rsplit(':', 3) + path, line, warning_class, message = first_line_parts + line = int(line) + self.assertEqual(expected_file, path) + self.assertEqual(warning_class, ' ' + UserWarning.__name__) + self.assertEqual(message, ' ' + text) + expected_line = ' ' + linecache.getline(path, line).strip() + '\n' + assert expected_line + self.assertEqual(second_line, expected_line) + + def test_filename_none(self): + # issue #12467: race condition if a warning is emitted at shutdown + globals_dict = globals() + oldfile = globals_dict['__file__'] + try: + with original_warnings.catch_warnings(module=self.module) as w: + self.module.filterwarnings("always", category=UserWarning) + globals_dict['__file__'] = None + self.module.warn('test', UserWarning) + finally: + globals_dict['__file__'] = oldfile + + +class WarningsDisplayTests(unittest.TestCase): + + """Test the displaying of warnings and the ability to overload functions + related to displaying warnings.""" + + def test_formatwarning(self): + message = "msg" + category = Warning + file_name = os.path.splitext(warning_tests.__file__)[0] + '.py' + line_num = 3 + file_line = linecache.getline(file_name, line_num).strip() + format = "%s:%s: %s: %s\n %s\n" + expect = format % (file_name, line_num, category.__name__, message, + file_line) + self.assertEqual(expect, self.module.formatwarning(message, + category, file_name, line_num)) + # Test the 'line' argument. + file_line += " for the win!" + expect = format % (file_name, line_num, category.__name__, message, + file_line) + self.assertEqual(expect, self.module.formatwarning(message, + category, file_name, line_num, file_line)) + + def test_showwarning(self): + file_name = os.path.splitext(warning_tests.__file__)[0] + '.py' + line_num = 3 + expected_file_line = linecache.getline(file_name, line_num).strip() + message = 'msg' + category = Warning + file_object = StringIO.StringIO() + expect = self.module.formatwarning(message, category, file_name, + line_num) + self.module.showwarning(message, category, file_name, line_num, + file_object) + self.assertEqual(file_object.getvalue(), expect) + # Test 'line' argument. + expected_file_line += "for the win!" + expect = self.module.formatwarning(message, category, file_name, + line_num, expected_file_line) + file_object = StringIO.StringIO() + self.module.showwarning(message, category, file_name, line_num, + file_object, expected_file_line) + self.assertEqual(expect, file_object.getvalue()) + +class CWarningsDisplayTests(BaseTest, WarningsDisplayTests): + module = c_warnings + +class PyWarningsDisplayTests(BaseTest, WarningsDisplayTests): + module = py_warnings + + +class CatchWarningTests(BaseTest): + + """Test catch_warnings().""" + + def test_catch_warnings_restore(self): + wmod = self.module + orig_filters = wmod.filters + orig_showwarning = wmod.showwarning + # Ensure both showwarning and filters are restored when recording + with wmod.catch_warnings(module=wmod, record=True): + wmod.filters = wmod.showwarning = object() + self.assertTrue(wmod.filters is orig_filters) + self.assertTrue(wmod.showwarning is orig_showwarning) + # Same test, but with recording disabled + with wmod.catch_warnings(module=wmod, record=False): + wmod.filters = wmod.showwarning = object() + self.assertTrue(wmod.filters is orig_filters) + self.assertTrue(wmod.showwarning is orig_showwarning) + + def test_catch_warnings_recording(self): + wmod = self.module + # Ensure warnings are recorded when requested + with wmod.catch_warnings(module=wmod, record=True) as w: + self.assertEqual(w, []) + self.assertTrue(type(w) is list) + wmod.simplefilter("always") + wmod.warn("foo") + self.assertEqual(str(w[-1].message), "foo") + wmod.warn("bar") + self.assertEqual(str(w[-1].message), "bar") + self.assertEqual(str(w[0].message), "foo") + self.assertEqual(str(w[1].message), "bar") + del w[:] + self.assertEqual(w, []) + # Ensure warnings are not recorded when not requested + orig_showwarning = wmod.showwarning + with wmod.catch_warnings(module=wmod, record=False) as w: + self.assertTrue(w is None) + self.assertTrue(wmod.showwarning is orig_showwarning) + + def test_catch_warnings_reentry_guard(self): + wmod = self.module + # Ensure catch_warnings is protected against incorrect usage + x = wmod.catch_warnings(module=wmod, record=True) + self.assertRaises(RuntimeError, x.__exit__) + with x: + self.assertRaises(RuntimeError, x.__enter__) + # Same test, but with recording disabled + x = wmod.catch_warnings(module=wmod, record=False) + self.assertRaises(RuntimeError, x.__exit__) + with x: + self.assertRaises(RuntimeError, x.__enter__) + + def test_catch_warnings_defaults(self): + wmod = self.module + orig_filters = wmod.filters + orig_showwarning = wmod.showwarning + # Ensure default behaviour is not to record warnings + with wmod.catch_warnings(module=wmod) as w: + self.assertTrue(w is None) + self.assertTrue(wmod.showwarning is orig_showwarning) + self.assertTrue(wmod.filters is not orig_filters) + self.assertTrue(wmod.filters is orig_filters) + if wmod is sys.modules['warnings']: + # Ensure the default module is this one + with wmod.catch_warnings() as w: + self.assertTrue(w is None) + self.assertTrue(wmod.showwarning is orig_showwarning) + self.assertTrue(wmod.filters is not orig_filters) + self.assertTrue(wmod.filters is orig_filters) + + def test_check_warnings(self): + # Explicit tests for the test_support convenience wrapper + wmod = self.module + if wmod is not sys.modules['warnings']: + return + with test_support.check_warnings(quiet=False) as w: + self.assertEqual(w.warnings, []) + wmod.simplefilter("always") + wmod.warn("foo") + self.assertEqual(str(w.message), "foo") + wmod.warn("bar") + self.assertEqual(str(w.message), "bar") + self.assertEqual(str(w.warnings[0].message), "foo") + self.assertEqual(str(w.warnings[1].message), "bar") + w.reset() + self.assertEqual(w.warnings, []) + + with test_support.check_warnings(): + # defaults to quiet=True without argument + pass + with test_support.check_warnings(('foo', UserWarning)): + wmod.warn("foo") + + with self.assertRaises(AssertionError): + with test_support.check_warnings(('', RuntimeWarning)): + # defaults to quiet=False with argument + pass + with self.assertRaises(AssertionError): + with test_support.check_warnings(('foo', RuntimeWarning)): + wmod.warn("foo") + + +class CCatchWarningTests(CatchWarningTests): + module = c_warnings + +class PyCatchWarningTests(CatchWarningTests): + module = py_warnings + + +class EnvironmentVariableTests(BaseTest): + + def test_single_warning(self): + newenv = os.environ.copy() + newenv["PYTHONWARNINGS"] = "ignore::DeprecationWarning" + p = subprocess.Popen([sys.executable, + "-c", "import sys; sys.stdout.write(str(sys.warnoptions))"], + stdout=subprocess.PIPE, env=newenv) + self.assertEqual(p.communicate()[0], "['ignore::DeprecationWarning']") + self.assertEqual(p.wait(), 0) + + def test_comma_separated_warnings(self): + newenv = os.environ.copy() + newenv["PYTHONWARNINGS"] = ("ignore::DeprecationWarning," + "ignore::UnicodeWarning") + p = subprocess.Popen([sys.executable, + "-c", "import sys; sys.stdout.write(str(sys.warnoptions))"], + stdout=subprocess.PIPE, env=newenv) + self.assertEqual(p.communicate()[0], + "['ignore::DeprecationWarning', 'ignore::UnicodeWarning']") + self.assertEqual(p.wait(), 0) + + def test_envvar_and_command_line(self): + newenv = os.environ.copy() + newenv["PYTHONWARNINGS"] = "ignore::DeprecationWarning" + p = subprocess.Popen([sys.executable, "-W" "ignore::UnicodeWarning", + "-c", "import sys; sys.stdout.write(str(sys.warnoptions))"], + stdout=subprocess.PIPE, env=newenv) + self.assertEqual(p.communicate()[0], + "['ignore::UnicodeWarning', 'ignore::DeprecationWarning']") + self.assertEqual(p.wait(), 0) + +class CEnvironmentVariableTests(EnvironmentVariableTests): + module = c_warnings + +class PyEnvironmentVariableTests(EnvironmentVariableTests): + module = py_warnings + + +def test_main(): + py_warnings.onceregistry.clear() + c_warnings.onceregistry.clear() + test_support.run_unittest(CFilterTests, PyFilterTests, + CWarnTests, PyWarnTests, + CWCmdLineTests, PyWCmdLineTests, + _WarningsTests, + CWarningsDisplayTests, PyWarningsDisplayTests, + CCatchWarningTests, PyCatchWarningTests, + CEnvironmentVariableTests, + PyEnvironmentVariableTests + ) + + +if __name__ == "__main__": + test_main() -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Tue May 29 03:00:53 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Tue, 29 May 2012 03:00:53 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Update_warnings_for_2=2E7=2E?= Message-ID: http://hg.python.org/jython/rev/b5cfa63c8967 changeset: 6670:b5cfa63c8967 user: Miki Tebeka date: Fri May 25 12:58:21 2012 -0700 summary: Update warnings for 2.7. files: ACKNOWLEDGMENTS | 1 + Lib/_warnings.py | 363 ++++++++++++++++++++ Lib/test/test_warnings.py | 89 ++- Lib/warnings.py | 3 +- src/org/python/util/jython.java | 55 ++- 5 files changed, 470 insertions(+), 41 deletions(-) diff --git a/ACKNOWLEDGMENTS b/ACKNOWLEDGMENTS --- a/ACKNOWLEDGMENTS +++ b/ACKNOWLEDGMENTS @@ -94,6 +94,7 @@ Alex Groenholm Anselm Kruis Dmitry Jemerov + Miki Tebeka Local Variables: mode: indented-text diff --git a/Lib/_warnings.py b/Lib/_warnings.py new file mode 100644 --- /dev/null +++ b/Lib/_warnings.py @@ -0,0 +1,363 @@ +"""Python part of the warnings subsystem.""" + +# Note: function level imports should *not* be used +# in this module as it may cause import lock deadlock. +# See bug 683658. +import linecache +import sys +import types + +__all__ = ["warn", "showwarning", "formatwarning", "filterwarnings", + "resetwarnings", "catch_warnings"] + +onceregistry = {} + +def warnpy3k(message, category=None, stacklevel=1): + """Issue a deprecation warning for Python 3.x related changes. + + Warnings are omitted unless Python is started with the -3 option. + """ + if sys.py3kwarning: + if category is None: + category = DeprecationWarning + warn(message, category, stacklevel+1) + +def _show_warning(message, category, filename, lineno, file=None, line=None): + """Hook to write a warning to a file; replace if you like.""" + if file is None: + file = sys.stderr + try: + file.write(formatwarning(message, category, filename, lineno, line)) + except IOError: + pass # the file (probably stderr) is invalid - this warning gets lost. +# Keep a working version around in case the deprecation of the old API is +# triggered. +showwarning = _show_warning + +def formatwarning(message, category, filename, lineno, line=None): + """Function to format a warning the standard way.""" + s = "%s:%s: %s: %s\n" % (filename, lineno, category.__name__, message) + line = linecache.getline(filename, lineno) if line is None else line + if line: + line = line.strip() + s += " %s\n" % line + return s + +def filterwarnings(action, message="", category=Warning, module="", lineno=0, + append=0): + """Insert an entry into the list of warnings filters (at the front). + + 'action' -- one of "error", "ignore", "always", "default", "module", + or "once" + 'message' -- a regex that the warning message must match + 'category' -- a class that the warning must be a subclass of + 'module' -- a regex that the module name must match + 'lineno' -- an integer line number, 0 matches all warnings + 'append' -- if true, append to the list of filters + """ + import re + assert action in ("error", "ignore", "always", "default", "module", + "once"), "invalid action: %r" % (action,) + assert isinstance(message, basestring), "message must be a string" + assert isinstance(category, (type, types.ClassType)), \ + "category must be a class" + assert issubclass(category, Warning), "category must be a Warning subclass" + assert isinstance(module, basestring), "module must be a string" + assert isinstance(lineno, int) and lineno >= 0, \ + "lineno must be an int >= 0" + item = (action, re.compile(message, re.I), category, + re.compile(module), lineno) + if append: + filters.append(item) + else: + filters.insert(0, item) + +def simplefilter(action, category=Warning, lineno=0, append=0): + """Insert a simple entry into the list of warnings filters (at the front). + + A simple filter matches all modules and messages. + 'action' -- one of "error", "ignore", "always", "default", "module", + or "once" + 'category' -- a class that the warning must be a subclass of + 'lineno' -- an integer line number, 0 matches all warnings + 'append' -- if true, append to the list of filters + """ + assert action in ("error", "ignore", "always", "default", "module", + "once"), "invalid action: %r" % (action,) + assert isinstance(lineno, int) and lineno >= 0, \ + "lineno must be an int >= 0" + item = (action, None, category, None, lineno) + if append: + filters.append(item) + else: + filters.insert(0, item) + +def resetwarnings(): + """Clear the list of warning filters, so that no filters are active.""" + filters[:] = [] + +class _OptionError(Exception): + """Exception used by option processing helpers.""" + pass + +# Helper to process -W options passed via sys.warnoptions +def _processoptions(args): + for arg in args: + try: + _setoption(arg) + except _OptionError, msg: + print >>sys.stderr, "Invalid -W option ignored:", msg + +# Helper for _processoptions() +def _setoption(arg): + import re + parts = arg.split(':') + if len(parts) > 5: + raise _OptionError("too many fields (max 5): %r" % (arg,)) + while len(parts) < 5: + parts.append('') + action, message, category, module, lineno = [s.strip() + for s in parts] + action = _getaction(action) + message = re.escape(message) + category = _getcategory(category) + module = re.escape(module) + if module: + module = module + '$' + if lineno: + try: + lineno = int(lineno) + if lineno < 0: + raise ValueError + except (ValueError, OverflowError): + raise _OptionError("invalid lineno %r" % (lineno,)) + else: + lineno = 0 + filterwarnings(action, message, category, module, lineno) + +# Helper for _setoption() +def _getaction(action): + if not action: + return "default" + if action == "all": return "always" # Alias + for a in ('default', 'always', 'ignore', 'module', 'once', 'error'): + if a.startswith(action): + return a + raise _OptionError("invalid action: %r" % (action,)) + +# Helper for _setoption() +def _getcategory(category): + import re + if not category: + return Warning + if re.match("^[a-zA-Z0-9_]+$", category): + try: + cat = eval(category) + except NameError: + raise _OptionError("unknown warning category: %r" % (category,)) + else: + i = category.rfind(".") + module = category[:i] + klass = category[i+1:] + try: + m = __import__(module, None, None, [klass]) + except ImportError: + raise _OptionError("invalid module name: %r" % (module,)) + try: + cat = getattr(m, klass) + except AttributeError: + raise _OptionError("unknown warning category: %r" % (category,)) + if not issubclass(cat, Warning): + raise _OptionError("invalid warning category: %r" % (category,)) + return cat + +onceregistry = {} + +# Code typically replaced by _warnings +def warn(message, category=None, stacklevel=1): + """Issue a warning, or maybe ignore it or raise an exception.""" + # Check if message is already a Warning object + if isinstance(message, Warning): + category = message.__class__ + # Check category argument + if category is None: + category = UserWarning + assert issubclass(category, Warning) + # Get context information + try: + caller = sys._getframe(stacklevel) + except ValueError: + globals = sys.__dict__ + lineno = 1 + else: + globals = caller.f_globals + lineno = caller.f_lineno + if '__name__' in globals: + module = globals['__name__'] + else: + module = "" + filename = globals.get('__file__') + if filename: + fnl = filename.lower() + if fnl.endswith((".pyc", ".pyo")): + filename = filename[:-1] + elif fnl.endswith("$py.class"): + filename = filename[:-9] + '.py' + else: + if module == "__main__": + try: + filename = sys.argv[0] + except (AttributeError, TypeError): + # embedded interpreters don't have sys.argv, see bug #839151 + filename = '__main__' + if not filename: + filename = module + registry = globals.setdefault("__warningregistry__", {}) + warn_explicit(message, category, filename, lineno, module, registry, + globals) + +def warn_explicit(message, category, filename, lineno, + module=None, registry=None, module_globals=None): + lineno = int(lineno) + if module is None: + module = filename or "" + if module[-3:].lower() == ".py": + module = module[:-3] # XXX What about leading pathname? + if registry is None: + registry = {} + if isinstance(message, Warning): + text = str(message) + category = message.__class__ + else: + text = message + message = category(message) + key = (text, category, lineno) + # Quick test for common case + if registry.get(key): + return + # Search the filters + for item in filters: + action, msg, cat, mod, ln = item + if ((msg is None or msg.match(text)) and + issubclass(category, cat) and + (mod is None or mod.match(module)) and + (ln == 0 or lineno == ln)): + break + else: + action = defaultaction + # Early exit actions + if action == "ignore": + registry[key] = 1 + return + + # Prime the linecache for formatting, in case the + # "file" is actually in a zipfile or something. + linecache.getlines(filename, module_globals) + + if action == "error": + raise message + # Other actions + if action == "once": + registry[key] = 1 + oncekey = (text, category) + if onceregistry.get(oncekey): + return + onceregistry[oncekey] = 1 + elif action == "always": + pass + elif action == "module": + registry[key] = 1 + altkey = (text, category, 0) + if registry.get(altkey): + return + registry[altkey] = 1 + elif action == "default": + registry[key] = 1 + else: + # Unrecognized actions are errors + raise RuntimeError( + "Unrecognized action (%r) in warnings.filters:\n %s" % + (action, item)) + # Print message and context + showwarning(message, category, filename, lineno) + + +class WarningMessage(object): + + """Holds the result of a single showwarning() call.""" + + _WARNING_DETAILS = ("message", "category", "filename", "lineno", "file", + "line") + + def __init__(self, message, category, filename, lineno, file=None, + line=None): + local_values = locals() + for attr in self._WARNING_DETAILS: + setattr(self, attr, local_values[attr]) + self._category_name = category.__name__ if category else None + + def __str__(self): + return ("{message : %r, category : %r, filename : %r, lineno : %s, " + "line : %r}" % (self.message, self._category_name, + self.filename, self.lineno, self.line)) + + +class catch_warnings(object): + + """A context manager that copies and restores the warnings filter upon + exiting the context. + + The 'record' argument specifies whether warnings should be captured by a + custom implementation of warnings.showwarning() and be appended to a list + returned by the context manager. Otherwise None is returned by the context + manager. The objects appended to the list are arguments whose attributes + mirror the arguments to showwarning(). + + The 'module' argument is to specify an alternative module to the module + named 'warnings' and imported under that name. This argument is only useful + when testing the warnings module itself. + + """ + + def __init__(self, record=False, module=None): + """Specify whether to record warnings and if an alternative module + should be used other than sys.modules['warnings']. + + For compatibility with Python 3.0, please consider all arguments to be + keyword-only. + + """ + self._record = record + self._module = sys.modules['warnings'] if module is None else module + self._entered = False + + def __repr__(self): + args = [] + if self._record: + args.append("record=True") + if self._module is not sys.modules['warnings']: + args.append("module=%r" % self._module) + name = type(self).__name__ + return "%s(%s)" % (name, ", ".join(args)) + + def __enter__(self): + if self._entered: + raise RuntimeError("Cannot enter %r twice" % self) + self._entered = True + self._filters = self._module.filters + self._module.filters = self._filters[:] + self._showwarning = self._module.showwarning + if self._record: + log = [] + def showwarning(*args, **kwargs): + log.append(WarningMessage(*args, **kwargs)) + self._module.showwarning = showwarning + return log + else: + return None + + def __exit__(self, *exc_info): + if not self._entered: + raise RuntimeError("Cannot exit %r without entering first" % self) + self._module.filters = self._filters + self._module.showwarning = self._showwarning diff --git a/Lib/test/test_warnings.py b/Lib/test/test_warnings.py --- a/Lib/test/test_warnings.py +++ b/Lib/test/test_warnings.py @@ -1,4 +1,5 @@ from contextlib import contextmanager +import marshal import linecache import os import StringIO @@ -10,6 +11,9 @@ import warning_tests +warning_tests_file = os.path.splitext(warning_tests.__file__)[0] + '.py' +warning_tests_file = warning_tests_file.replace('$py', '') # Jython + import warnings as original_warnings py_warnings = test_support.import_fresh_module('warnings', blocked=['_warnings']) @@ -519,7 +523,7 @@ self.assertEqual(result.count('\n'), 2, "Too many newlines in %r" % result) first_line, second_line = result.split('\n', 1) - expected_file = os.path.splitext(warning_tests.__file__)[0] + '.py' + expected_file = warning_tests_file first_line_parts = first_line.rsplit(':', 3) path, line, warning_class, message = first_line_parts line = int(line) @@ -551,7 +555,7 @@ def test_formatwarning(self): message = "msg" category = Warning - file_name = os.path.splitext(warning_tests.__file__)[0] + '.py' + file_name = warning_tests_file line_num = 3 file_line = linecache.getline(file_name, line_num).strip() format = "%s:%s: %s: %s\n %s\n" @@ -567,7 +571,7 @@ category, file_name, line_num, file_line)) def test_showwarning(self): - file_name = os.path.splitext(warning_tests.__file__)[0] + '.py' + file_name = warning_tests_file line_num = 3 expected_file_line = linecache.getline(file_name, line_num).strip() message = 'msg' @@ -706,35 +710,42 @@ class EnvironmentVariableTests(BaseTest): + def check_child(self, env, cmdline, expected): + newenv = os.environ.copy() + newenv["PYTHONWARNINGS"] = env + + cmd = [sys.executable] + if cmdline: + cmd.extend(["-W", cmdline]) + + cmd.extend([ + "-c", + "import sys, marshal; marshal.dump(sys.warnoptions, sys.stdout)", + ]) + + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, env=newenv) + self.assertEqual(p.wait(), 0) + child_opts = marshal.load(p.stdout) + self.assertEqual(set(child_opts), set(expected)) + def test_single_warning(self): - newenv = os.environ.copy() - newenv["PYTHONWARNINGS"] = "ignore::DeprecationWarning" - p = subprocess.Popen([sys.executable, - "-c", "import sys; sys.stdout.write(str(sys.warnoptions))"], - stdout=subprocess.PIPE, env=newenv) - self.assertEqual(p.communicate()[0], "['ignore::DeprecationWarning']") - self.assertEqual(p.wait(), 0) + self.check_child( + "ignore::DeprecationWarning", + None, + ["ignore::DeprecationWarning"]) def test_comma_separated_warnings(self): - newenv = os.environ.copy() - newenv["PYTHONWARNINGS"] = ("ignore::DeprecationWarning," - "ignore::UnicodeWarning") - p = subprocess.Popen([sys.executable, - "-c", "import sys; sys.stdout.write(str(sys.warnoptions))"], - stdout=subprocess.PIPE, env=newenv) - self.assertEqual(p.communicate()[0], - "['ignore::DeprecationWarning', 'ignore::UnicodeWarning']") - self.assertEqual(p.wait(), 0) + self.check_child( + "ignore::DeprecationWarning,ignore::UnicodeWarning", + None, + ['ignore::DeprecationWarning', 'ignore::UnicodeWarning']) def test_envvar_and_command_line(self): - newenv = os.environ.copy() - newenv["PYTHONWARNINGS"] = "ignore::DeprecationWarning" - p = subprocess.Popen([sys.executable, "-W" "ignore::UnicodeWarning", - "-c", "import sys; sys.stdout.write(str(sys.warnoptions))"], - stdout=subprocess.PIPE, env=newenv) - self.assertEqual(p.communicate()[0], - "['ignore::UnicodeWarning', 'ignore::DeprecationWarning']") - self.assertEqual(p.wait(), 0) + self.check_child( + "ignore::DeprecationWarning", + "ignore::UnicodeWarning", + ['ignore::UnicodeWarning', 'ignore::DeprecationWarning']) + class CEnvironmentVariableTests(EnvironmentVariableTests): module = c_warnings @@ -746,15 +757,21 @@ def test_main(): py_warnings.onceregistry.clear() c_warnings.onceregistry.clear() - test_support.run_unittest(CFilterTests, PyFilterTests, - CWarnTests, PyWarnTests, - CWCmdLineTests, PyWCmdLineTests, - _WarningsTests, - CWarningsDisplayTests, PyWarningsDisplayTests, - CCatchWarningTests, PyCatchWarningTests, - CEnvironmentVariableTests, - PyEnvironmentVariableTests - ) + test_support.run_unittest( + CFilterTests, + PyFilterTests, + CWarnTests, + PyWarnTests, + CWCmdLineTests, + PyWCmdLineTests, + _WarningsTests, + CWarningsDisplayTests, + PyWarningsDisplayTests, + CCatchWarningTests, + PyCatchWarningTests, + CEnvironmentVariableTests, + PyEnvironmentVariableTests + ) if __name__ == "__main__": diff --git a/Lib/warnings.py b/Lib/warnings.py --- a/Lib/warnings.py +++ b/Lib/warnings.py @@ -10,6 +10,7 @@ __all__ = ["warn", "showwarning", "formatwarning", "filterwarnings", "resetwarnings", "catch_warnings"] +onceregistry = {} def warnpy3k(message, category=None, stacklevel=1): """Issue a deprecation warning for Python 3.x related changes. @@ -205,7 +206,7 @@ if module == "__main__": try: filename = sys.argv[0] - except AttributeError: + except (AttributeError, TypeError): # embedded interpreters don't have sys.argv, see bug #839151 filename = '__main__' if not filename: diff --git a/src/org/python/util/jython.java b/src/org/python/util/jython.java --- a/src/org/python/util/jython.java +++ b/src/org/python/util/jython.java @@ -7,6 +7,8 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Properties; import java.util.zip.ZipEntry; @@ -136,6 +138,45 @@ } while (shouldRestart); } + private static List warnOptionsFromEnv() { + ArrayList opts = new ArrayList(); + + try { + String envVar = System.getenv("PYTHONWARNINGS"); + if (envVar == null) { + return opts; + } + + + for (String opt : envVar.split(",")) { + opt = opt.trim(); + if (opt.length() == 0) { + continue; + } + opts.add(opt); + } + } catch (SecurityException e) { + } + + return opts; + } + + private static List validWarnActions = Arrays.asList( + "error", "ignore", "always", "default", "module", "once", + "i"); + + private static void addWarnings(List from, PyList to) { + for (String opt : from) { + String action = opt.split(":")[0]; + if (!validWarnActions.contains(action)) { + System.err.println(String.format( + "Invalid -W option ignored: invalid action: '%s'", action)); + continue; + } + to.append(Py.newString(opt)); + } + } + public static void run(String[] args) { // Parse the command line options CommandLineOptions opts = new CommandLineOptions(); @@ -157,14 +198,20 @@ // Setup the basic python system state from these options PySystemState.initialize(PySystemState.getBaseProperties(), opts.properties, opts.argv); + PySystemState systemState = Py.getSystemState(); PyList warnoptions = new PyList(); - for (String wopt : opts.warnoptions) { - warnoptions.append(new PyString(wopt)); + addWarnings(opts.warnoptions, warnoptions); + addWarnings(warnOptionsFromEnv(), warnoptions); + systemState.setWarnoptions(warnoptions); + + // Make sure warnings module is loaded if there are warning options + // Not sure this is needed, but test_warnings.py expects this + if (warnoptions.size() > 0) { + imp.load("warnings"); } - Py.getSystemState().setWarnoptions(warnoptions); - PySystemState systemState = Py.getSystemState(); + // Decide if stdin is interactive if (!opts.fixInteractive || opts.interactive) { opts.interactive = ((PyFile)Py.defaultSystemState.stdin).isatty(); -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Tue May 29 03:00:53 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Tue, 29 May 2012 03:00:53 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Removed_unnecessary_redefini?= =?utf8?q?tions=2E?= Message-ID: http://hg.python.org/jython/rev/da72d829ed1b changeset: 6671:da72d829ed1b user: Frank Wierzbicki date: Fri May 25 13:10:03 2012 -0700 summary: Removed unnecessary redefinitions. files: Lib/_warnings.py | 11 ++++++----- Lib/warnings.py | 2 -- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Lib/_warnings.py b/Lib/_warnings.py --- a/Lib/_warnings.py +++ b/Lib/_warnings.py @@ -7,10 +7,13 @@ import sys import types -__all__ = ["warn", "showwarning", "formatwarning", "filterwarnings", - "resetwarnings", "catch_warnings"] +__all__ = ["filters", "default_action", "once_registry", "warn", + "warn_explicit"] -onceregistry = {} +once_registry = {} +onceregistry = once_registry +default_action = "default" +defaultaction = default_action def warnpy3k(message, category=None, stacklevel=1): """Issue a deprecation warning for Python 3.x related changes. @@ -171,8 +174,6 @@ raise _OptionError("invalid warning category: %r" % (category,)) return cat -onceregistry = {} - # Code typically replaced by _warnings def warn(message, category=None, stacklevel=1): """Issue a warning, or maybe ignore it or raise an exception.""" diff --git a/Lib/warnings.py b/Lib/warnings.py --- a/Lib/warnings.py +++ b/Lib/warnings.py @@ -10,8 +10,6 @@ __all__ = ["warn", "showwarning", "formatwarning", "filterwarnings", "resetwarnings", "catch_warnings"] -onceregistry = {} - def warnpy3k(message, category=None, stacklevel=1): """Issue a deprecation warning for Python 3.x related changes. -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Tue May 29 03:00:53 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Tue, 29 May 2012 03:00:53 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Add_=5Fwarnings_comment=2E?= Message-ID: http://hg.python.org/jython/rev/c29fb569809b changeset: 6672:c29fb569809b user: Frank Wierzbicki date: Mon May 28 16:07:30 2012 -0700 summary: Add _warnings comment. files: Lib/_warnings.py | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-) diff --git a/Lib/_warnings.py b/Lib/_warnings.py --- a/Lib/_warnings.py +++ b/Lib/_warnings.py @@ -1,4 +1,7 @@ -"""Python part of the warnings subsystem.""" +""" +XXX: faked out Java part of the warnings subsystem, mostly copied from + warnings.py. TODO: rewrite in Java! +""" # Note: function level imports should *not* be used # in this module as it may cause import lock deadlock. -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Tue May 29 23:10:55 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Tue, 29 May 2012 23:10:55 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Allow_None_as_arg_to_transla?= =?utf8?b?dGUu?= Message-ID: http://hg.python.org/jython/rev/7d0c725bfc11 changeset: 6673:7d0c725bfc11 user: Frank Wierzbicki date: Tue May 29 11:02:28 2012 -0700 summary: Allow None as arg to translate. files: src/org/python/core/PyString.java | 9 ++++++++- 1 files changed, 8 insertions(+), 1 deletions(-) diff --git a/src/org/python/core/PyString.java b/src/org/python/core/PyString.java --- a/src/org/python/core/PyString.java +++ b/src/org/python/core/PyString.java @@ -2141,6 +2141,10 @@ return new int[] {iStartAdjusted, iEnd, iStart}; } + public String translate() { + return str_translate(null, null); + } + public String translate(String table) { return str_translate(table, null); } @@ -2149,8 +2153,11 @@ return str_translate(table, deletechars); } - @ExposedMethod(defaults = "null", doc = BuiltinDocs.str_translate_doc) + @ExposedMethod(defaults = {"null", "null"}, doc = BuiltinDocs.str_translate_doc) final String str_translate(String table, String deletechars) { + if (table == null) { + return getString(); + } if (table.length() != 256) throw Py.ValueError( "translation table must be 256 characters long"); -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Wed May 30 05:17:20 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Wed, 30 May 2012 05:17:20 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Implemented_bytearray=2Ecoun?= =?utf8?b?dCwgcmVtb3ZlLCBpbmRleCwgcmluZGV4IGFuZCBfX2NvbnRhaW5zX18gKCdpbic=?= Message-ID: http://hg.python.org/jython/rev/826d8f4c8014 changeset: 6675:826d8f4c8014 user: Jeff Allen date: Sat May 26 23:44:47 2012 +0100 summary: Implemented bytearray.count, remove, index, rindex and __contains__ ('in' operator). Relatively easy using same apparatus created for replace. Perhaps finally have __contains__ responding correctly to its several feasible argument types. Now scoring 2 failures and 65 errors on test_bytes.py files: src/org/python/core/BaseBytes.java | 66 +++- src/org/python/core/PyByteArray.java | 234 +++++++++++++- 2 files changed, 264 insertions(+), 36 deletions(-) diff --git a/src/org/python/core/BaseBytes.java b/src/org/python/core/BaseBytes.java --- a/src/org/python/core/BaseBytes.java +++ b/src/org/python/core/BaseBytes.java @@ -1431,20 +1431,24 @@ /** * Search for the target in this byte array, returning true if found and false if not. The - * target must be compatible with the Python byte range. + * target must either convertible to an integer in the Python byte range, or capable of being + * viewed as a byte array. * * @param target byte value to search for * @return true iff found */ protected final synchronized boolean basebytes___contains__(PyObject target) { - byte t = byteCheck(target); - int jmax = offset + size; - for (int j = offset; j < jmax; j++) { - if (storage[j] == t) { - return true; - } + if (target.isIndex()) { + // Caller is treating this as an array of integers, so the value has to be in range. + byte b = byteCheck(target.asIndex()); + return index(b) >= 0; + } else { + // Caller is treating this as a byte-string and looking for substring 'target' + View targetView = getViewOrError(target); + Finder finder = new Finder(targetView); + finder.setText(this); + return finder.nextIndex() >= 0; } - return false; } /** @@ -1572,6 +1576,23 @@ */ /** + * The very simplest kind of find operation: return the index in the byte array of the first + * occurrence of the byte value + * + * @param b byte to search for + * @return index in the byte array (0..size-1) or -1 if not found + */ + protected int index(byte b) { + int limit = offset + size; + for (int p = offset; p < limit; p++) { + if (storage[p] == b) { + return p - offset; + } + } + return -1; + } + + /** * This class implements the Boyer-Moore-Horspool Algorithm for findind a pattern in text, * applied to byte arrays. The BMH algorithm uses a table of bad-character skips derived from * the pattern. The bad-character skips table tells us how far from the end of the pattern is a @@ -1921,6 +1942,29 @@ } /** + * Ready-to-expose implementation of Python count( sub [, start [, end ]] ). + * Return + * the number of non-overlapping occurrences of sub in the range [start, end]. + * Optional arguments start and end (which may be null or + * Py.None ) are interpreted as in slice notation. + * + * @param sub bytes to find + * @param ostart of slice to search + * @param oend of slice to search + * @return count of occurrences of sub within this byte array + */ + final int basebytes_count(PyObject sub, PyObject ostart, PyObject oend) { + Finder finder = new Finder(getViewOrError(sub)); + + // Convert [start:end] to integers + PySlice s = new PySlice(ostart, oend, null); + int[] index = s.indicesEx(size()); // [ start, end, 1, end-start ] + + // Make this slice the thing we count within. + return finder.count(storage, offset + index[0], index[3]); + } + + /** * Ready-to-expose implementation of Python find( sub [, start [, end ]] ). Return * the lowest index in the byte array where byte sequence sub is found, such that * sub is contained in the slice [start:end]. Arguments @@ -1931,7 +1975,7 @@ * @param sub bytes to find * @param ostart of slice to search * @param oend of slice to search - * @return index of start of ocurrence of sub within this byte array + * @return index of start of occurrence of sub within this byte array */ final int basebytes_find(PyObject sub, PyObject ostart, PyObject oend) { Finder finder = new Finder(getViewOrError(sub)); @@ -1949,7 +1993,7 @@ * @param sub bytes to find * @param ostart of slice to search * @param oend of slice to search - * @return index of start of ocurrence of sub within this byte array + * @return index of start of occurrence of sub within this byte array */ final int basebytes_rfind(PyObject sub, PyObject ostart, PyObject oend) { Finder finder = new ReverseFinder(getViewOrError(sub)); @@ -1964,7 +2008,7 @@ * @param finder for the bytes to find, sometime forwards, sometime backwards * @param ostart of slice to search * @param oend of slice to search - * @return index of start of ocurrence of sub within this byte array + * @return index of start of occurrence of sub within this byte array */ private final int find(Finder finder, PyObject ostart, PyObject oend) { diff --git a/src/org/python/core/PyByteArray.java b/src/org/python/core/PyByteArray.java --- a/src/org/python/core/PyByteArray.java +++ b/src/org/python/core/PyByteArray.java @@ -813,6 +813,17 @@ pyinsert(size, o); } + /** + * Implement to the standard Python __contains__ method, which in turn implements the + * in operator. + * + * @param o the element to search for in this bytearray. + * @return the result of the search. + **/ + public boolean __contains__(PyObject o) { + return basebytes___contains__(o); + } + @ExposedMethod(doc = BuiltinDocs.bytearray___contains___doc) final boolean bytearray___contains__(PyObject o) { return basebytes___contains__(o); @@ -823,51 +834,54 @@ return basebytes_decode(args, keywords); } + /** - * Implementation of Python find(sub). Return the lowest index in the byte array - * where byte sequence sub is found. Return -1 if sub is not found. + * Implementation of Python count(sub). + * Return + * the number of non-overlapping occurrences of sub in this byte array. * * @param sub sequence to find (of a type viewable as a byte sequence) - * @return index of start of ocurrence of sub within this byte array + * @return count of occurrences of sub within this byte array */ - public int find(PyObject sub) { - return basebytes_find(sub, null, null); + public int count(PyObject sub) { + return basebytes_count(sub, null, null); } /** - * Implementation of Python find( sub [, start ] ). Return the lowest index in the - * byte array where byte sequence sub is found, such that sub is - * contained in the slice [start:]. Return -1 if sub is not found. + * Implementation of Python count( sub [, start ] ). + * Return + * the number of non-overlapping occurrences of sub in the range [start:]. * * @param sub sequence to find (of a type viewable as a byte sequence) * @param start of slice to search - * @return index of start of ocurrence of sub within this byte array + * @return count of occurrences of sub within this byte array */ - public int find(PyObject sub, PyObject start) { - return basebytes_find(sub, start, null); + public int count(PyObject sub, PyObject start) { + return basebytes_count(sub, start, null); } /** - * Implementation of Python find( sub [, start [, end ]] ). Return the lowest index - * in the byte array where byte sequence sub is found, such that sub - * is contained in the slice [start:end]. Arguments start and - * end (which may be null or Py.None ) are interpreted as - * in slice notation. Return -1 if sub is not found. + * Implementation of Python count( sub [, start [, end ]] ). + * Return + * the number of non-overlapping occurrences of sub in the range [start, end]. + * Optional arguments start and end (which may be null or + * Py.None ) are interpreted as in slice notation. * * @param sub sequence to find (of a type viewable as a byte sequence) * @param start of slice to search * @param end of slice to search - * @return index of start of ocurrence of sub within this byte array + * @return count of occurrences of sub within this byte array */ - public int find(PyObject sub, PyObject start, PyObject end) { - return basebytes_find(sub, start, end); + public int count(PyObject sub, PyObject start, PyObject end) { + return basebytes_count(sub, start, end); } - @ExposedMethod(defaults = {"null", "null"}, doc = BuiltinDocs.bytearray_find_doc) - final int bytearray_find(PyObject sub, PyObject start, PyObject end) { - return basebytes_find(sub, start, end); + @ExposedMethod(defaults = {"null", "null"}, doc = BuiltinDocs.bytearray_count_doc) + final int bytearray_count(PyObject sub, PyObject start, PyObject end) { + return basebytes_count(sub, start, end); } + /** * Append the elements in the argument sequence to the end of the array, equivalent to: * s[len(s):len(s)] = o. The argument must be a subclass of BaseBytes or an @@ -886,6 +900,51 @@ setslice(size, size, 1, o); } + /** + * Implementation of Python find(sub). Return the lowest index in the byte array + * where byte sequence sub is found. Return -1 if sub is not found. + * + * @param sub sequence to find (of a type viewable as a byte sequence) + * @return index of start of occurrence of sub within this byte array + */ + public int find(PyObject sub) { + return basebytes_find(sub, null, null); + } + + /** + * Implementation of Python find( sub [, start ] ). Return the lowest index in the + * byte array where byte sequence sub is found, such that sub is + * contained in the slice [start:]. Return -1 if sub is not found. + * + * @param sub sequence to find (of a type viewable as a byte sequence) + * @param start of slice to search + * @return index of start of occurrence of sub within this byte array + */ + public int find(PyObject sub, PyObject start) { + return basebytes_find(sub, start, null); + } + + /** + * Implementation of Python find( sub [, start [, end ]] ). Return the lowest index + * in the byte array where byte sequence sub is found, such that sub + * is contained in the slice [start:end]. Arguments start and + * end (which may be null or Py.None ) are interpreted as + * in slice notation. Return -1 if sub is not found. + * + * @param sub sequence to find (of a type viewable as a byte sequence) + * @param start of slice to search + * @param end of slice to search + * @return index of start of occurrence of sub within this byte array + */ + public int find(PyObject sub, PyObject start, PyObject end) { + return basebytes_find(sub, start, end); + } + + @ExposedMethod(defaults = {"null", "null"}, doc = BuiltinDocs.bytearray_find_doc) + final int bytearray_find(PyObject sub, PyObject start, PyObject end) { + return basebytes_find(sub, start, end); + } + @Override public PyObject __iadd__(PyObject o) { return bytearray___iadd__(o); @@ -909,6 +968,56 @@ } /** + * Implementation of Python index(sub). + * Like {@link #find(PyObject)} + * but raise {@link Py#ValueError} if sub is not found. + * + * @param sub sequence to find (of a type viewable as a byte sequence) + * @return index of start of occurrence of sub within this byte array + */ + public int index(PyObject sub) { + return bytearray_index(sub, null, null); + } + + /** + * Implementation of Python index( sub [, start ] ). + * Like {@link #find(PyObject,PyObject)} + * but raise {@link Py#ValueError} if sub is not found. + * + * @param sub sequence to find (of a type viewable as a byte sequence) + * @param start of slice to search + * @return index of start of occurrence of sub within this byte array + */ + public int index(PyObject sub, PyObject start) { + return bytearray_index(sub, start, null); + } + + /** + * Implementation of Python index( sub [, start [, end ]] ). + * Like {@link #find(PyObject,PyObject,PyObject)} + * but raise {@link Py#ValueError} if sub is not found. + * + * @param sub sequence to find (of a type viewable as a byte sequence) + * @param start of slice to search + * @param end of slice to search + * @return index of start of occurrence of sub within this byte array + * @throws PyException ValueError if sub not found in byte array + */ + public int index(PyObject sub, PyObject start, PyObject end) throws PyException { + return bytearray_index(sub, start, end); + } + + @ExposedMethod(defaults = {"null", "null"}, doc = BuiltinDocs.bytearray_index_doc) + final int bytearray_index(PyObject sub, PyObject start, PyObject end) { + // Like find but raise a ValueError if not found + int pos = basebytes_find(sub, start, end); + if (pos<0) { + throw Py.ValueError("subsection not found"); + } + return pos; + } + + /** * Insert the argument element into the byte array at the specified index. * Same as s[index:index] = [o] if index >= 0. * @@ -936,6 +1045,32 @@ return basebytes___reduce__(); } + + /** + * Remove the first occurrence of an element from the array, equivalent to: + * del s[s.index(x)], although x must be convertable to a single byte value. The + * argument must be a PyInteger, PyLong or string of length 1. + * + * @param o the value to remove from the list. + * @throws PyException ValueError if o not found in bytearray + */ + public void remove(PyObject o) throws PyException { + bytearray_append(o); + } + + @ExposedMethod(doc = BuiltinDocs.bytearray_remove_doc) + final synchronized void bytearray_remove(PyObject o) { + // Check and extract the value, and search for it. + byte b = byteCheck(o); + int pos = index(b); + // Not finding it is an error + if (pos < 0) { + throw Py.ValueError("value not found in bytearray"); + } else { + storageDelete(pos, 1); + } + } + /** * An implementation of Python replace( old, new ), returning a * PyByteArray with all occurrences of sequence oldB replaced by @@ -975,7 +1110,7 @@ * where byte sequence sub is found. Return -1 if sub is not found. * * @param sub sequence to find (of a type viewable as a byte sequence) - * @return index of start of rightmost ocurrence of sub within this byte array + * @return index of start of rightmost occurrence of sub within this byte array */ public int rfind(PyObject sub) { return basebytes_rfind(sub, null, null); @@ -988,7 +1123,7 @@ * * @param sub sequence to find (of a type viewable as a byte sequence) * @param start of slice to search - * @return index of start of rightmost ocurrence of sub within this byte array + * @return index of start of rightmost occurrence of sub within this byte array */ public int rfind(PyObject sub, PyObject start) { return basebytes_rfind(sub, start, null); @@ -1005,7 +1140,7 @@ * @param sub sequence to find (of a type viewable as a byte sequence) * @param start of slice to search * @param end of slice to search - * @return index of start of rightmost ocurrence of sub within this byte array + * @return index of start of rightmost occurrence of sub within this byte array */ public int rfind(PyObject sub, PyObject start, PyObject end) { return basebytes_rfind(sub, start, end); @@ -1016,6 +1151,55 @@ return basebytes_rfind(sub, start, end); } + /** + * Implementation of Python rindex(sub). + * Like {@link #find(PyObject)} + * but raise {@link Py#ValueError} if sub is not found. + * + * @param sub sequence to find (of a type viewable as a byte sequence) + * @return index of start of occurrence of sub within this byte array + */ + public int rindex(PyObject sub) { + return bytearray_rindex(sub, null, null); + } + + /** + * Implementation of Python rindex( sub [, start ] ). + * Like {@link #find(PyObject,PyObject)} + * but raise {@link Py#ValueError} if sub is not found. + * + * @param sub sequence to find (of a type viewable as a byte sequence) + * @param start of slice to search + * @return index of start of occurrence of sub within this byte array + */ + public int rindex(PyObject sub, PyObject start) { + return bytearray_rindex(sub, start, null); + } + + /** + * Implementation of Python rindex( sub [, start [, end ]] ). + * Like {@link #find(PyObject,PyObject,PyObject)} + * but raise {@link Py#ValueError} if sub is not found. + * + * @param sub sequence to find (of a type viewable as a byte sequence) + * @param start of slice to search + * @param end of slice to search + * @return index of start of occurrence of sub within this byte array + */ + public int rindex(PyObject sub, PyObject start, PyObject end) { + return bytearray_rindex(sub, start, end); + } + + @ExposedMethod(defaults = {"null", "null"}, doc = BuiltinDocs.bytearray_rindex_doc) + final int bytearray_rindex(PyObject sub, PyObject start, PyObject end) { + // Like rfind but raise a ValueError if not found + int pos = basebytes_rfind(sub, start, end); + if (pos<0) { + throw Py.ValueError("subsection not found"); + } + return pos; + } + // Based on PyList and not yet properly implemented. // // @Override -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Wed May 30 05:17:20 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Wed, 30 May 2012 05:17:20 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Implement_bytearray=2Ereplac?= =?utf8?q?e=2C_improved_find=2C_rfind=2E?= Message-ID: http://hg.python.org/jython/rev/99dc9d041f9c changeset: 6674:99dc9d041f9c user: Jeff Allen date: Sat May 26 14:10:37 2012 +0100 summary: Implement bytearray.replace, improved find, rfind. Now scores 3 failures and 68 errors in test_bytes.py. Under the covers: developments to View and implementation of Finder classes that will support many search-like operations. Code for replace() and Finder was adapted from CPython bytearrayobject and stringlib. files: src/org/python/core/BaseBytes.java | 1099 +++++++++++-- src/org/python/core/PyByteArray.java | 340 ++- 2 files changed, 1144 insertions(+), 295 deletions(-) diff --git a/src/org/python/core/BaseBytes.java b/src/org/python/core/BaseBytes.java --- a/src/org/python/core/BaseBytes.java +++ b/src/org/python/core/BaseBytes.java @@ -1,6 +1,7 @@ package org.python.core; import java.util.AbstractList; +import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; @@ -13,7 +14,7 @@ * TypeError if the actual type of the object is not mutable. *

    * It is possible for a Java client to treat this class as a List<PyInteger>, obtaining - * equivalent functionality to the Python interface in a Java paradigm. The reason {@link } + * equivalent functionality to the Python interface in a Java paradigm. *

    * Subclasses must define (from {@link PySequence}): *

      @@ -138,9 +139,9 @@ } /* - * ======================================================================================== + * ============================================================================================ * Support for memoryview - * ======================================================================================== + * ============================================================================================ * * This is present in order to facilitate development of PyMemoryView which a full * implementation of bytearray would depend on, while at the same time a full implementation of @@ -207,9 +208,9 @@ } /* - * ======================================================================================== + * ============================================================================================ * Support for construction and initialisation - * ======================================================================================== + * ============================================================================================ * * Methods here help subclasses set the initial state. They are designed with bytearray in mind, * but note that from Python 3, bytes() has the same set of calls and behaviours, although in @@ -406,6 +407,18 @@ } /** + * Helper for the Java API constructor from a {@link #View}. View is (perhaps) a stop-gap while + * there is no Jython implementation of PEP 3118 (memoryview). + * + * @param value a byte-oriented view + */ + void init(View value) { + int n = value.size(); + newStorage(n); + value.copyTo(storage, offset); + } + + /** * Helper for __new__ and __init__ and the Java API constructor from * bytearray or bytes in subclasses. * @@ -565,9 +578,9 @@ } /* - * ======================================================================================== + * ============================================================================================ * Sharable storage - * ======================================================================================== + * ============================================================================================ * * The storage is provided by a byte array that may be somewhat larger than the number of bytes * actually stored, and these bytes may begin at an offset within the storage. Immutable @@ -675,9 +688,9 @@ } /* - * ======================================================================================== + * ============================================================================================ * Wrapper class to make other objects into byte arrays - * ======================================================================================== + * ============================================================================================ * * In much of the bytearray and bytes API, the "other sequence" argument will accept any type * that supports the buffer protocol, that is, the object can supply a memoryview through which @@ -723,6 +736,20 @@ * @return byte-oriented view */ public View slice(PyObject start, PyObject end); + + /** + * Copy the bytes of this view to the specified position in a destination array. + * All the bytes of the View are copied. + * @param dest destination array + * @param destPos index in the destination at which this.byteAt(0) is written + * @throws ArrayIndexOutOfBoundsException if the destination is too small + */ + public void copyTo(byte[] dest, int destPos) throws ArrayIndexOutOfBoundsException; + + /** + * The standard memoryview out of bounds message (does not refer to the underlying type). + */ + public static final String OUT_OF_BOUNDS = "index out of bounds"; } /** @@ -730,11 +757,25 @@ */ static abstract class ViewBase implements View { - // Implement Python slice semantics + /** + * Provides an implementation of {@link View#slice(PyObject, PyObject)} that implements + * Python contiguous slice semantics so that sub-classes only receive simplified requests + * involving properly-bounded integer arguments via {@link #sliceImpl(int, int)}, a call to + * {@link #byteAt(int)}, if the slice has length 1, or in the extreme case of a zero length + * slice, no call at all. + */ public View slice(PyObject ostart, PyObject oend) { PySlice s = new PySlice(ostart, oend, null); int[] index = s.indicesEx(size()); // [ start, end, 1, end-start ] - return sliceImpl(index[0], index[(index[3] > 0) ? 1 : 0]); + int len = index[3]; + // Provide efficient substitute when length is zero or one + if (len < 1) { + return new ViewOfNothing(); + } else if (len == 1) { + return new ViewOfByte(byteAt(index[0])); + } else { // General case: delegate to sub-class + return sliceImpl(index[0], index[1]); + } } /** @@ -742,8 +783,11 @@ * the default implementations of {@link #slice(int, int)} and * {@link #slice(PyObject, PyObject)} once the start and end * arguments have been reduced to simple integer indexes. It is guaranteed that - * start>=0 and size()>=end>=start when the method is called. - * Implementors are encouraged to do something more efficient than piling on anothe wrapper. + * start>=0 and size()>=end>=start+2 when the method is called. + * View objects for slices of length zero and one are dealt with internally by the + * {@link #slice(PyObject, PyObject)} method, see {@link ViewOfNothing} and + * {@link ViewOfByte}. Implementors are encouraged to do something more efficient than + * piling on another wrapper. * * @param start first element to include * @param end first element after slice, not to include @@ -751,6 +795,16 @@ */ protected abstract View sliceImpl(int start, int end); + /** + * Copy the bytes of this view to the specified position in a destination array. All the + * bytes of the View are copied. The Base implementation simply loops over byteAt(). + */ + public void copyTo(byte[] dest, int destPos) throws ArrayIndexOutOfBoundsException { + int n = this.size(), p = destPos; + for (int i = 0; i < n; i++) { + dest[p++] = byteAt(i); + } + } } /** @@ -764,9 +818,27 @@ if (b == null) { return null; } else if (b instanceof BaseBytes) { - return new ViewOfBytes((BaseBytes)b); + BaseBytes bb = (BaseBytes)b; + int len = bb.size; + // Provide efficient substitute when length is zero or one + if (len < 1) { + return new ViewOfNothing(); + } else if (len == 1) { + return new ViewOfByte(bb.byteAt(0)); + } else { // General case + return new ViewOfBytes(bb); + } } else if (b.getType() == PyString.TYPE) { - return new ViewOfString(b.asString()); + String bs = b.asString(); + int len = bs.length(); + // Provide efficient substitute when length is zero + if (len < 1) { + return new ViewOfNothing(); + } else if (len == 1) { + return new ViewOfByte(byteCheck(bs.charAt(0))); + } else { // General case + return new ViewOfString(bs); + } } return null; } @@ -790,19 +862,6 @@ } /** - * Return a wrapper providing a byte-oriented view of a slice of this object. - * - * @param ostart index of first byte to include - * @param oend index of first byte after slice - * @return byte-oriented view or null - */ - protected ViewOfBytes slice(PyObject ostart, PyObject oend) { - PySlice s = new PySlice(ostart, oend, null); - int[] index = s.indicesEx(size); // [ start, end, 1, end-start ] - return new ViewOfBytes(storage, offset + index[0], index[3]); - } - - /** * Return a wrapper providing a byte-oriented view for whatever object is passed, or raise an * exception if we don't know how. * @@ -925,12 +984,98 @@ public View sliceImpl(int start, int end) { return new ViewOfBytes(storage, offset + start, end - start); } + + /** + * Copy the bytes of this view to the specified position in a destination array. All the + * bytes of the View are copied. The view is of a byte array, so er can provide a more + * efficient implementation than the default. + */ + @Override + public void copyTo(byte[] dest, int destPos) throws ArrayIndexOutOfBoundsException { + System.arraycopy(storage, offset, dest, destPos, size); + } + } + /** + * Wrapper providing a byte-oriented view of just one byte. It looks silly, but it helps our + * efficiency and code re-use. + */ + protected static class ViewOfByte extends ViewBase { + + private byte storage; + + /** + * Create a byte-oriented view of a byte array descended from BaseBytes. + * + * @param obj + */ + public ViewOfByte(byte obj) { + this.storage = obj; + } + + public byte byteAt(int index) { + return storage; + } + + public int intAt(int index) { + return 0xff & storage; + } + + public int size() { + return 1; + } + + public View sliceImpl(int start, int end) { + return new ViewOfByte(storage); + } + + /** + * Copy the byte the specified position in a destination array. + */ + @Override + public void copyTo(byte[] dest, int destPos) throws ArrayIndexOutOfBoundsException { + dest[destPos] = storage; + } + + } + + /** + * Wrapper providing a byte-oriented view of an empty byte array or string. It looks even + * sillier than wrapping one byte, but again helps our regularity and code re-use. + */ + protected static class ViewOfNothing extends ViewBase { + + public byte byteAt(int index) { + throw Py.IndexError(OUT_OF_BOUNDS); + } + + public int intAt(int index) { + throw Py.IndexError(OUT_OF_BOUNDS); + } + + public int size() { + return 0; + } + + public View sliceImpl(int start, int end) { + return new ViewOfNothing(); + } + + /** + * Copy zero bytes the specified position, i.e. do nothing, even if dest[destPos] is out of + * bounds. + */ + @Override + public void copyTo(byte[] dest, int destPos) {} + + } + + /* - * ======================================================================================== API - * for org.python.core.PySequence - * ======================================================================================== + * ============================================================================================ + * API for org.python.core.PySequence + * ============================================================================================ */ protected PyInteger pyget(int index) { return new PyInteger(intAt(index)); @@ -966,9 +1111,9 @@ } /* - * ======================================================================================== + * ============================================================================================ * Support for Python API common to mutable and immutable subclasses - * ======================================================================================== + * ============================================================================================ */ @Override @@ -1021,51 +1166,6 @@ } -// /** -// * Comparison function between a byte array and a String returning 1, 0 or -1 as a>b, a==b, or -// * a<b respectively. The comparison is by value, using Python unsigned byte conventions for -// * the byte array and character code for elements of the string, and left-to-right (low to high -// * index). Zero bytes are significant, even at the end of the array: -// * [65,66,67]<"ABC\u0000", for example and [] is less than every -// * non-empty String, while []=="". -// * -// * @param a left-hand array in the comparison -// * @param b right-hand String in the comparison -// * @return 1, 0 or -1 as a>b, a==b, or a<b respectively -// */ -// private static int compare(BaseBytes a, String b) { -// -// // Compare elements one by one in these ranges: -// int ap = a.offset; -// int aEnd = ap + a.size; -// int bp = 0; -// int bEnd = b.length(); -// -// while (ap < aEnd) { -// if (bp >= bEnd) { -// // a is longer than b -// return 1; -// } else { -// // Compare the corresponding bytes and character codes -// int aVal = 0xff & a.storage[ap++]; -// int bVal = b.charAt(bp++); -// int diff = aVal - bVal; -// if (diff != 0) { -// return (diff < 0) ? -1 : 1; -// } -// } -// } -// -// // All the bytes matched and we reached the end of a -// if (bp < bEnd) { -// // But we didn't reach the end of b -// return -1; -// } else { -// // And the end of b at the same time, so they're equal -// return 0; -// } -// -// } /** * Comparison function between a byte array and a byte-oriented View of some other object, such @@ -1145,43 +1245,6 @@ } } -// /** -// * Comparison function between byte array types and any other object. The set of 6 -// * "rich comparison" operators are based on this. -// * -// * @param b -// * @return 1, 0 or -1 as this>b, this==b, or this<b respectively, or -2 if the comparison is -// * not implemented -// */ -// private synchronized int basebytes_cmp(PyObject b) { -// -// // This is based on PyFloat.float___cmp__() as it seems to have the same need to support -// // multiple types for other, but it is not exposed as bytearray and bytes have no __cmp__. -// -// if (this == b) { -// // Same object: quick result -// return 0; -// -// } else if (b instanceof BaseBytes) { -// return compare(this, (BaseBytes)b); -// -// } else if (b.getType() == PyString.TYPE) { -// /* -// * Necessary to permit comparison of bytearray and bytes, which in in Python 2.7 is an -// * alias of str. Remove in 3.0 -// */ -// return compare(this, ((PyString)b).asString()); -// -// } else if (b instanceof MemoryViewProtocol) { -// // XXX: Revisit when we have an implementation of memoryview -// // MemoryView mv = ((MemoryViewProtocol) other).getMemoryView(); -// return -2; -// -// } else { -// // Signifies a type mis-match. See PyObject _cmp_unsafe() and related code. -// return -2; -// } -// } /** * Fail-fast comparison function between byte array types and any other object, for when the @@ -1218,33 +1281,6 @@ } } -// /** -// * Fail-fast comparison function between byte array types and any other object, for when the -// * test is only for equality. The "rich comparison" operators __eq__ and -// * __ne__ are based on this. -// * -// * @param b -// * @return 0 if this==b, or +1 or -1 if this!=b, or -2 if the comparison is not implemented -// */ -// private synchronized int basebytes_cmpeq(PyObject b) { -// -// if (b instanceof BaseBytes) { -// if (((BaseBytes)b).size != size) { -// // Different size: can't be equal, and we don't care which is bigger -// return 1; -// } -// } else if (b.getType() == PyString.TYPE) { -// /* -// * Necessary to permit comparison of bytearray and bytes, which in in Python 2.7 is an -// * alias of str. Remove in 3.0 -// */ -// if (((PyString)b).__len__() != size) { -// // Different size: can't be equal, and we don't care which is bigger -// return 1; -// } -// } -// return basebytes_cmp(b); -// } /** * Implementation of __eq__ (equality) operator, capable of comparison with another byte array @@ -1482,44 +1518,6 @@ } /** - * Ready-to-expose implementation of Python find( sub [, start [, end ]] ). Return - * the lowest index in the byte array where byte sequence sub is found, such that - * sub is contained in the slice [start:end]. Arguments - * start and end (which may be null or - * Py.None ) are interpreted as in slice notation. Return -1 if sub is - * not found. - * - * @param sub bytes to find - * @param start of slice to search - * @param end of slice to search - * @return index of start of ocurrence of sub within this byte array - */ - final int basebytes_find(PyObject sub, PyObject start, PyObject end) { - ViewOfBytes v = this.slice(start, end); - byte[] buf = v.storage; - View vsub = getViewOrError(sub); - int n = vsub.size(); - // No point testing beyond this location, as insufficient space: - int pmax = v.offset + v.size - n; - // Use start positions from v.offset to pmax in the buffer buf - for (int p = v.offset; p < pmax; p++) { - int j; - for (j = 0; j < n; j++) { - if (buf[p + j] != vsub.byteAt(j)) { - break; - } - } - // If we tested all n bytes, that's a match. Note that the returned index is - // relative to the (operative) start of this, not relative to the start of the slice. - if (j == n) { - return p - this.offset; - } - } - // We tested all the start positions without success - return -1; - } - - /** * Convenience method to create a TypeError PyException with the message * "can't concat {type} to {toType}" * @@ -1527,7 +1525,7 @@ * @param toType * @return PyException (TypeError) as detailed */ - public static PyException ConcatenationTypeError(PyType type, PyType toType) { + static PyException ConcatenationTypeError(PyType type, PyType toType) { String fmt = "can't concat %s to %s"; return Py.TypeError(String.format(fmt, type.fastGetName(), toType.fastGetName())); } @@ -1563,9 +1561,734 @@ } /* - * ======================================================================================== API - * for Java access as byte[] - * ======================================================================================== + * ============================================================================================ + * Python API for find and replace operations + * ============================================================================================ + * + * A large part of the CPython bytearray.c is devoted to replace( old, new [, count ] ). + * The special section here reproduces that in Java, but whereas CPython makes heavy use + * of the buffer API and C memcpy(), we use View.copyTo. The logic is much the same, however, + * even down to variable names. + */ + + /** + * This class implements the Boyer-Moore-Horspool Algorithm for findind a pattern in text, + * applied to byte arrays. The BMH algorithm uses a table of bad-character skips derived from + * the pattern. The bad-character skips table tells us how far from the end of the pattern is a + * byte that might match the text byte currently aligned with the end of the pattern. For + * example, suppose the pattern (of length 6) is at position 4: + * + *
      +     *                    1         2         3
      +     *          0123456789012345678901234567890
      +     * Text:    a man, a map, a panama canal
      +     * Pattern:     panama
      +     * 
      + * + * This puts the 'm' of 'map' against the last byte 'a' of the pattern. Rather than testing the + * pattern, we will look up 'm' in the skip table. There is an 'm' just one step from the end of + * the pattern, so we will move the pattern one place to the right before trying to match it. + * This allows us to move in large strides throughthe text. + */ + protected static class Finder { + + /** + * Construct a Finder object that may be used (repeatedly) to find matches with the pattern + * in text (arrays of bytes). + * + * @param pattern A vew that presents the pattern as an array of bytes + */ + public Finder(View pattern) { + this.pattern = pattern; + } + + /** + * Mask defining how many of the bits of each byte are used when looking up the skip, used + * like: skip = skipTable[MASK & currentByte]. + */ + private static final byte MASK = 0x1f; + + /** + * Table for looking up the skip, used like: + * skip = skipTable[MASK & currentByte]. + */ + protected int[] skipTable = null; + + /** + * This method creates a compressed table of bad-character skips from the pattern. The entry + * for a given byte value tells us how far it is from the end of the pattern, being 0 for + * the actual last byte, or is equal to the length of the pattern if the byte does not occur + * in the pattern. The table is compressed in that only the least-significant bits of the + * byte index are used. In the case where 5 bits are used, the table is only 32 elements + * long, rather than (as it might be) 256 bytes, the number of distinct byte values. + */ + protected int[] calculateSkipTable() { + int[] skipTable = new int[MASK + 1]; + int m = pattern.size(); + // Default skip is the pattern length: for bytes not in the pattern. + Arrays.fill(skipTable, m); + // For each byte in the pattern, make an entry for how far it is from the end. + // The last occurrence of the byte value prevails. + for (int i = 0; i < m; i++) { + skipTable[MASK & pattern.byteAt(i)] = m - i - 1; + } + return skipTable; + } + + /** + * Set the text to be searched in successive calls to nextIndex(), where the + * text is the entire array text[]. + * + * @param text to search + */ + public void setText(byte[] text) { + setText(text, 0, text.length); + } + + /** + * Set the text to be searched in successive calls to nextIndex(), where the + * text is the entire byte array text. + * + * @param text to search + */ + public void setText(BaseBytes text) { + setText(text.storage, text.offset, text.size); + } + + /** + * Set the text to be searched in successive calls to nextIndex(), where the + * text is effectively only the bytes text[start] to + * text[start+size-1] inclusive. + * + * @param text to search + * @param start first position to consider + * @param size number of bytes within which to match + */ + public void setText(byte[] text, int start, int size) { + + this.text = text; + this.left = start; + right = start + size - pattern.size() + 1; // Last pattern position + 1 + + /* + * We defer computing the table from construction to this point mostly because + * calculateSkipTable() may be overridden. + */ + if (pattern.size() > 1 && skipTable == null) { + skipTable = calculateSkipTable(); + } + + } + + protected final View pattern; + protected byte[] text = emptyStorage; // in case we forget to setText() + protected int left = 0; // Leftmost pattern position to use + protected int right = 0; // Rightmost pattern position + 1 + + /** + * Find the next index in the text array where the pattern starts. Successive callls to + * nextIndex() return the successive (non-overlapping) occurrences of the + * pattern in the text. + * + * @return matching index or -1 if no (further) occurrences found + */ + public int nextIndex() { + int m = pattern.size(); + + if (skipTable != null) { // ... which it will not be if m>1 and setText() was called + /* + * Boyer-Moore-Horspool Algorithm using a Bloom array. Based on CPython stringlib, + * but without avoiding a proper bad character skip array. + */ + for (int i = left; i < right; /* i incremented in loop */) { + /* + * Unusually, start by looking up the skip. If text[i+m-1] matches, skip==0, + * although it will also be zero if only the least-significant bits match. + */ + int skip = skipTable[MASK & text[i + (m - 1)]]; + + if (skip == 0) { + // Possible match, but we only used the least-significant bits: check all + int j, k = i; + for (j = 0; j < m; j++) { // k = i + j + if (text[k++] != pattern.byteAt(j)) { + break; + } + } + // If we tested all m bytes, that's a match. + if (j == m) { + left = k; // Start at text[i+m] next time we're called + return i; + } + // It wasn't a match: advance by one + i += 1; + + } else { + /* + * The last byte of the pattern does not match the corresponding text byte. + * Skip tells us how far back down the pattern is a potential match, so how + * far it is safe to advance before we do another last-byte test. + */ + i += skip; + } + } + + } else if (m == 1) { + // Special case of single byte search + byte b = pattern.byteAt(0); + for (int i = left; i < right; i++) { + if (text[i] == b) { + left = i + 1; // Start at text[i+1] next time we're called + return i; + } + } + + } else { + // Special case of search for empty (m==0) byte string + int i = left; + if (i <= right) { + // It is an honorary match - even when left==right + left = i + 1; + return i; + } + } + + // All sections fall out here if they do not find a match (even m==0) + return -1; + } + + /** + * Count the non-overlapping occurrences of the pattern in the text. + * + * @param text to search + * @return number of occurrences + */ + public int count(byte[] text) { + return count(text, 0, text.length, Integer.MAX_VALUE); + } + + /** + * Count the non-overlapping occurrences of the pattern in the text, where the text is + * effectively only the bytes text[start] to text[start+size-1] + * inclusive. + * + * @param text to search + * @param start first position to consider + * @param size number of bytes within which to match + * @return number of occurrences + */ + public int count(byte[] text, int start, int size) { + return count(text, start, size, Integer.MAX_VALUE); + } + + /** + * Count the non-overlapping occurrences of the pattern in the text, where the text is + * effectively only the bytes text[start] to text[start+size-1]. + * + * @param text to search + * @param start first position to consider + * @param size number of bytes within which to match + * @param maxcount limit to number of occurrences to find + * @return number of occurrences + */ + public int count(byte[] text, int start, int size, int maxcount) { + setText(text, start, size); + int count = 0; + while (count < maxcount && nextIndex() >= 0) { + count++; + } + return count; + } + + } + + /** + * This class is the complement of {@link Finder} and implements the Boyer-Moore-Horspool + * Algorithm adapted for right-to-left search for a pattern in byte arrays. + */ + protected static class ReverseFinder extends Finder { + + /** + * Construct a ReverseFinder object that may be used (repeatedly) to find matches with the + * pattern in text (arrays of bytes). + * + * @param pattern A vew that presents the pattern as an array of bytes + */ + public ReverseFinder(View pattern) { + super(pattern); + } + + /** + * Mask defining how many of the bits of each byte are used when looking up the skip, used + * like: skip = skipTable[MASK & currentByte]. + */ + private static final byte MASK = 0x1f; + + /** + * This method creates a compressed table of bad-character skips from the pattern for + * reverse-searching. The entry for a given byte value tells us how far it is from the start + * of the pattern, being 0 for the actual first byte, or is equal to the length of the + * pattern if the byte does not occur in the pattern. The table is compressed in that only + * the least-significant bits of the byte index are used. In the case where 5 bits are used, + * the table is only 32 elements long, rather than (as it might be) 256 bytes, the number of + * distinct byte values. + */ + protected int[] calculateSkipTable() { + int[] skipTable = new int[MASK + 1]; + int m = pattern.size(); + // Default skip is the pattern length: for bytes not in the pattern. + Arrays.fill(skipTable, m); + // For each byte in the pattern, make an entry for how far it is from the start. + // The last occurrence of the byte value prevails. + for (int i = m; --i >= 0;) { + skipTable[MASK & pattern.byteAt(i)] = i; + } + return skipTable; + } + + /** + * Find the next index in the text array where the pattern starts, but working backwards. + * Successive callls to nextIndex() return the successive (non-overlapping) + * occurrences of the pattern in the text. + * + * @return matching index or -1 if no (further) occurrences found + */ + public int nextIndex() { + + int m = pattern.size(); + + if (skipTable != null) { // ... which it will not be if m>1 and setText() was called + /* + * Boyer-Moore-Horspool Algorithm using a Bloom array. Based on CPython stringlib, + * but without avoiding a proper bad character skip array. + */ + for (int i = right - 1; i >= left; /* i decremented in loop */) { + /* + * Unusually, start by looking up the skip. If text[i] matches, skip==0, + * although it will also be zero if only the least-significant bits match. + */ + int skip = skipTable[MASK & text[i]]; + + if (skip == 0) { + // Possible match, but we only used the least-significant bits: check all + int j, k = i; + for (j = 0; j < m; j++) { // k = i + j + if (text[k++] != pattern.byteAt(j)) { + break; + } + } + // If we tested all m bytes, that's a match. + if (j == m) { + right = i - m + 1; // Start at text[i-m] next time we're called + return i; + } + // It wasn't a match: move left by one + i -= 1; + + } else { + /* + * The first byte of the pattern does not match the corresponding text byte. + * Skip tells us how far up the pattern is a potential match, so how far + * left it is safe to move before we do another first-byte test. + */ + i -= skip; + } + } + + } else if (m == 1) { + // Special case of single byte search + byte b = pattern.byteAt(0); + for (int i = right; --i >= left;) { + if (text[i] == b) { + right = i; // Start at text[i-1] next time we're called + return i; + } + } + + } else { + // Special case of search for empty (m==0) byte string + int i = right; + if (--i >= left) { + // It is an honorary match - even when right==left + right = i; + return i; + } + } + + // All sections fall out here if they do not find a match (even m==0) + return -1; + } + } + + /** + * Ready-to-expose implementation of Python find( sub [, start [, end ]] ). Return + * the lowest index in the byte array where byte sequence sub is found, such that + * sub is contained in the slice [start:end]. Arguments + * start and end (which may be null or + * Py.None ) are interpreted as in slice notation. Return -1 if sub is + * not found. + * + * @param sub bytes to find + * @param ostart of slice to search + * @param oend of slice to search + * @return index of start of ocurrence of sub within this byte array + */ + final int basebytes_find(PyObject sub, PyObject ostart, PyObject oend) { + Finder finder = new Finder(getViewOrError(sub)); + return find(finder, ostart, oend); + } + + /** + * Ready-to-expose implementation of Python rfind( sub [, start [, end ]] ). Return + * the highest index in the byte array where byte sequence sub is found, such that + * sub is contained in the slice [start:end]. Arguments + * start and end (which may be null or + * Py.None ) are interpreted as in slice notation. Return -1 if sub is + * not found. + * + * @param sub bytes to find + * @param ostart of slice to search + * @param oend of slice to search + * @return index of start of ocurrence of sub within this byte array + */ + final int basebytes_rfind(PyObject sub, PyObject ostart, PyObject oend) { + Finder finder = new ReverseFinder(getViewOrError(sub)); + return find(finder, ostart, oend); + } + + /** + * Common code for Python find( sub [, start [, end ]] ) and + * rfind( sub [, start [, end ]] ). Return the lowest or highest index in the byte + * array where byte sequence used to construct finder is found. + * + * @param finder for the bytes to find, sometime forwards, sometime backwards + * @param ostart of slice to search + * @param oend of slice to search + * @return index of start of ocurrence of sub within this byte array + */ + private final int find(Finder finder, PyObject ostart, PyObject oend) { + + // Convert [start:end] to integers + PySlice s = new PySlice(ostart, oend, null); + int[] index = s.indicesEx(size()); // [ start, end, 1, end-start ] + + // Make this slice the thing we search. Note finder works with Java index in storage. + finder.setText(storage, offset + index[0], index[3]); + int result = finder.nextIndex(); + + // Compensate for the offset in returning a value + return (result < 0) ? -1 : result - offset; + } + + /** + * An almost ready-to-expose implementation of Python + * replace( old, new [, count ] ), returning a PyByteArray with all + * occurrences of sequence oldB replaced by newB. If the optional + * argument count is given, only the first count occurrences are + * replaced. + * + * @param oldB sequence to find + * @param newB relacement sequence + * @param maxcount maximum occurrences are replaced or < 0 for all + * @return result of replacement as a new PyByteArray + */ + final synchronized PyByteArray basebytes_replace(PyObject oldB, PyObject newB, int maxcount) { + + View from = getViewOrError(oldB); + View to = getViewOrError(newB); + + /* + * The logic of the first section is copied exactly from CPython in order to get the same + * behaviour. The "headline" description of replace is simple enough but the corner cases + * can be surprising: + */ + // >>> bytearray(b'hello').replace(b'',b'-') + // bytearray(b'-h-e-l-l-o-') + // >>> bytearray(b'hello').replace(b'',b'-',3) + // bytearray(b'-h-e-llo') + // >>> bytearray(b'hello').replace(b'',b'-',1) + // bytearray(b'-hello') + // >>> bytearray().replace(b'',b'-') + // bytearray(b'-') + // >>> bytearray().replace(b'',b'-',1) # ? + // bytearray(b'') + + if (maxcount < 0) { + maxcount = Integer.MAX_VALUE; + + } else if (maxcount == 0 || size == 0) { + // nothing to do; return the original bytes + return new PyByteArray(this); + } + + int from_len = from.size(); + int to_len = to.size(); + + if (maxcount == 0 || (from_len == 0 && to_len == 0)) { + // nothing to do; return the original bytes + return new PyByteArray(this); + + } else if (from_len == 0) { + // insert the 'to' bytes everywhere. + // >>> "Python".replace("", ".") + // '.P.y.t.h.o.n.' + return replace_interleave(to, maxcount); + + } else if (size == 0) { + // Special case for "".replace("", "A") == "A" + return new PyByteArray(to); + + } else if (to_len == 0) { + // Delete occurrences of the 'from' bytes + return replace_delete_substring(from, maxcount); + + } else if (from_len == to_len) { + // The result is the same size as this byte array, whatever the number of replacements. + return replace_substring_in_place(from, to, maxcount); + + } else { + // Otherwise use the generic algorithm + return replace_substring(from, to, maxcount); + } + } + + /* + * Algorithms for different cases of string replacement. CPython also has specialisations for + * when 'from' or 'to' or both are single bytes. In Java we think this is unnecessary because + * such speed gain as might be available that way is obtained by using the efficient one-byte + * View object. Because Java cannot access memory bytes directly, unlike C, there is not so much + * to be gained. + */ + + /** + * Helper for {@link #basebytes_replace(PyObject, PyObject, int)} implementing the general case + * of byte-string replacement when the new and old strings have different lengths. + * + * @param from byte-string to find and replace + * @param to replacement byte-string + * @param maxcount maximum number of replacements to make + * @return the result as a new PyByteArray + */ + private PyByteArray replace_substring(View from, View to, int maxcount) { + // size>=1, len(from)>=1, len(to)>=1, maxcount>=1 + + // Initialise a Finder for the 'from' pattern + Finder finder = new Finder(from); + + int count = finder.count(storage, offset, size, maxcount); + if (count == 0) { + // no matches + return new PyByteArray(this); + } + + int from_len = from.size(); + int to_len = to.size(); + + // Calculate length of result and check for too big + long result_len = size + count * (to_len - from_len); + if (result_len > Integer.MAX_VALUE) { + Py.OverflowError("replace bytes is too long"); + } + + // Good to go. As we know the ultimate size, we can do all our allocation in one + byte[] r = new byte[(int)result_len]; + int p = offset; // Copy-from index in this.storage + int rp = 0; // Copy-to index in r + + // Reset the Finder on the (active part of) this.storage + finder.setText(storage, p, size); + + while (count-- > 0) { + // First occurrence of 'from' bytes in storage + int q = finder.nextIndex(); + if (q < 0) { + // Never happens because we've got count right + break; + } + + // Output the stretch up to the discovered occurrence of 'from' + int length = q - p; + if (length > 0) { + System.arraycopy(storage, p, r, rp, length); + rp += length; + } + + // Skip over the occurrence of the 'from' bytes + p = q + from_len; + + // Output a copy of 'to' + to.copyTo(r, rp); + rp += to_len; + } + + // Copy the rest of the original string + int length = size + offset - p; + if (length > 0) { + System.arraycopy(storage, p, r, rp, length); + rp += length; + } + + // Make r[] the storage of a new bytearray + return new PyByteArray(r); + } + + /** + * Handle the interleaving case b'hello'.replace(b'', b'..') = b'..h..e..l..l..o..' At the call + * site we are guaranteed: size>=1, to.size()>=1, maxcount>=1 + * + * @param to the replacement bytes as a byte-oriented view + * @param maxcount upper limit on number of insertions + */ + private PyByteArray replace_interleave(View to, int maxcount) { + + // Insert one at the beginning and one after every byte, or as many as allowed + int count = size + 1; + if (maxcount < count) { + count = maxcount; + } + + int to_len = to.size(); + + // Calculate length of result and check for too big + long result_len = ((long)count) * to_len + size; + if (result_len > Integer.MAX_VALUE) { + Py.OverflowError("replace bytes is too long"); + } + + // Good to go. As we know the ultimate size, we can do all our allocation in one + byte[] r = new byte[(int)result_len]; + int p = offset; // Copy-from index in this.storage + int rp = 0; // Copy-to index in r + + // Lay the first one down (guaranteed this will occur as count>=1) + to.copyTo(r, rp); + rp += to_len; + + // And the rest + for (int i = 1; i < count; i++) { + r[rp++] = storage[p++]; + to.copyTo(r, rp); + rp += to_len; + } + + // Copy the rest of the original string + int length = size + offset - p; + if (length > 0) { + System.arraycopy(storage, p, r, rp, length); + rp += length; + } + + // Make r[] the storage of a new bytearray + return new PyByteArray(r); + } + + /** + * Helper for {@link #basebytes_replace(PyObject, PyObject, int)} implementing the special case + * of byte-string replacement when the new string has zero length, i.e. deletion. + * + * @param from byte-string to find and delete + * @param maxcount maximum number of deletions to make + * @return the result as a new PyByteArray + */ + private PyByteArray replace_delete_substring(View from, int maxcount) { + // len(self)>=1, len(from)>=1, to="", maxcount>=1 + + // Initialise a Finder for the 'from' pattern + Finder finder = new Finder(from); + + int count = finder.count(storage, offset, size, maxcount); + if (count == 0) { + // no matches + return new PyByteArray(this); + } + + int from_len = from.size(); + long result_len = size - (count * from_len); + assert (result_len >= 0); + + // Good to go. As we know the ultimate size, we can do all our allocation in one + byte[] r = new byte[(int)result_len]; + int p = offset; // Copy-from index in this.storage + int rp = 0; // Copy-to index in r + + // Reset the Finder on the (active part of) this.storage + finder.setText(storage, offset, size); + + while (count-- > 0) { + // First occurrence of 'from' bytes in storage + int q = finder.nextIndex(); + if (q < 0) { + // Never happens because we've got count right + break; + } + + // Output the stretch up to the discovered occurrence of 'from' + int length = q - p; + if (length > 0) { + System.arraycopy(storage, p, r, rp, length); + rp += length; + } + + // Skip over the occurrence of the 'from' bytes + p = q + from_len; + } + + // Copy the rest of the original string + int length = size + offset - p; + if (length > 0) { + System.arraycopy(storage, p, r, rp, length); + rp += length; + } + + // Make r[] the storage of a new bytearray + return new PyByteArray(r); + } + + /** + * Helper for {@link #basebytes_replace(PyObject, PyObject, int)} implementing the special case + * of byte-string replacement when the new and old strings have the same length. The key + * observation here is that the result has the same size as this byte array, and we know this + * even without counting how many replacements we shall make. + * + * @param from byte-string to find and replace + * @param to replacement byte-string + * @param maxcount maximum number of replacements to make + * @return the result as a new PyByteArray + */ + private PyByteArray replace_substring_in_place(View from, View to, int maxcount) { + // len(self)>=1, len(from)==len(to)>=1, maxcount>=1 + + // Initialise a Finder for the 'from' pattern + Finder finder = new Finder(from); + + int count = maxcount; + + // The result will be this.size + byte[] r = new byte[size]; + System.arraycopy(storage, offset, r, 0, size); + + // Change everything in-place: easiest if we search the destination + finder.setText(r); + + while (count-- > 0) { + int q = finder.nextIndex(); // Note q is an index into result.storage + if (q < 0) { + // Normal exit because we discover actual count as we go along + break; + } + // Overwrite with 'to' the stretch corresponding to the matched 'from' + to.copyTo(r, q); + } + + // Make r[] the storage of a new bytearray + return new PyByteArray(r); + } + + + /* + * ============================================================================================ + * Java API for access as byte[] + * ============================================================================================ * * Just the immutable case for now */ @@ -1619,12 +2342,12 @@ } /* - * ======================================================================================== API - * for java.util.List - * ======================================================================================== + * ============================================================================================ + * API for java.util.List + * ============================================================================================ */ - /** + /** * Access to the bytearray (or bytes) as a {@link java.util.List}. The List interface supplied * by BaseBytes delegates to this object. */ diff --git a/src/org/python/core/PyByteArray.java b/src/org/python/core/PyByteArray.java --- a/src/org/python/core/PyByteArray.java +++ b/src/org/python/core/PyByteArray.java @@ -52,6 +52,21 @@ init(size); } +// /** +// * Create zero-filled Python bytearray of specified size and capacity for appending to. The +// * capacity is the (minimum) storage allocated, while the size is the number of zero-filled +// * bytes it currently contains. Simple append and extend operations on a bytearray will not +// * shrink the allocated capacity, but insertions and deletions may cause it to be reallocated at +// * the size then in use. +// * +// * @param size of bytearray +// * @param capacity allocated +// */ +// public PyByteArray(int size, int capacity) { +// super(TYPE); +// setStorage(new byte[capacity], size); +// } +// /** * Construct bytearray by copying values from int[]. * @@ -62,7 +77,8 @@ } /** - * Create a new array filled exactly by a copy of the contents of the source. + * Create a new array filled exactly by a copy of the contents of the source, which is a + * bytearray (or bytes). * * @param value source of the bytes (and size) */ @@ -72,7 +88,19 @@ } /** - * Create a new array filled exactly by a copy of the contents of the source. + * Create a new array filled exactly by a copy of the contents of the source, which is a + * byte-oriented view. + * + * @param value source of the bytes (and size) + */ + PyByteArray(View value) { + super(TYPE); + init(value); + } + + /** + * Create a new array filled exactly by a copy of the contents of the source, which is a + * memoryview. * * @param value source of the bytes (and size) */ @@ -131,6 +159,16 @@ } /** + * Construct bytearray by re-using an array of byte as storage initialised by the client. + * + * @param newStorage pre-initialised storage: the caller should not keep a reference + */ + PyByteArray(byte[] newStorage) { + super(TYPE); + setStorage(newStorage); + } + + /** * Create a new bytearray object from an arbitrary Python object according to the same rules as * apply in Python to the bytearray() constructor: *
        @@ -159,9 +197,10 @@ init(arg); } - /* ======================================================================================== + + /* ============================================================================================ * API for org.python.core.PySequence - * ======================================================================================== + * ============================================================================================ */ /** @@ -561,8 +600,8 @@ } /** - * Convenience method to build (but not throw) a ValueError PyException with the message - * "attempt to assign {type} of size {valueSize} to extended slice of size {sliceSize}" + * Convenience method to build (but not throw) a ValueError PyException with the + * message "attempt to assign {type} of size {valueSize} to extended slice of size {sliceSize}" * * @param valueType * @param valueSize size of sequence being assigned to slice @@ -635,9 +674,9 @@ } - /* ======================================================================================== + /* ============================================================================================ * Python API rich comparison operations - * ======================================================================================== + * ============================================================================================ */ @Override @@ -702,9 +741,9 @@ return basebytes___gt__(other); } -/* ======================================================================================== +/* ============================================================================================ * Python API for bytearray - * ======================================================================================== + * ============================================================================================ */ @Override @@ -716,6 +755,10 @@ final synchronized PyObject bytearray___add__(PyObject o) { PyByteArray sum = null; + + // XXX re-write using View + + if (o instanceof BaseBytes) { BaseBytes ob = (BaseBytes)o; // Quick route: allocate the right size bytearray and copy the two parts in. @@ -755,7 +798,8 @@ /** * Append a single element to the end of the array, equivalent to: - * s[len(s):len(s)] = o. The argument must be a PyInteger, PyLong or string of length 1. + * s[len(s):len(s)] = o. The argument must be a PyInteger, PyLong or string of + * length 1. * * @param o the item to append to the list. */ @@ -783,7 +827,7 @@ * Implementation of Python find(sub). Return the lowest index in the byte array * where byte sequence sub is found. Return -1 if sub is not found. * - * @param sub bytes to find + * @param sub sequence to find (of a type viewable as a byte sequence) * @return index of start of ocurrence of sub within this byte array */ public int find(PyObject sub) { @@ -795,7 +839,7 @@ * byte array where byte sequence sub is found, such that sub is * contained in the slice [start:]. Return -1 if sub is not found. * - * @param sub bytes to find + * @param sub sequence to find (of a type viewable as a byte sequence) * @param start of slice to search * @return index of start of ocurrence of sub within this byte array */ @@ -810,7 +854,7 @@ * end (which may be null or Py.None ) are interpreted as * in slice notation. Return -1 if sub is not found. * - * @param sub bytes to find + * @param sub sequence to find (of a type viewable as a byte sequence) * @param start of slice to search * @param end of slice to search * @return index of start of ocurrence of sub within this byte array @@ -824,8 +868,6 @@ return basebytes_find(sub, start, end); } - - /** * Append the elements in the argument sequence to the end of the array, equivalent to: * s[len(s):len(s)] = o. The argument must be a subclass of BaseBytes or an @@ -894,7 +936,85 @@ return basebytes___reduce__(); } + /** + * An implementation of Python replace( old, new ), returning a + * PyByteArray with all occurrences of sequence oldB replaced by + * newB. + * + * @param oldB sequence to find + * @param newB relacement sequence + * @return result of replacement as a new PyByteArray + */ + public PyByteArray replace(PyObject oldB, PyObject newB) { + return basebytes_replace(oldB, newB, -1); + } + /** + * An implementation of Python replace( old, new [, count ] ), returning a + * PyByteArray with all occurrences of sequence oldB replaced by + * newB. If the optional argument count is given, only the first + * count occurrences are replaced. + * + * @param oldB sequence to find + * @param newB relacement sequence + * @param maxcount maximum occurrences are replaced or all if maxcount < 0 + * @return result of replacement as a new PyByteArray + */ + public PyByteArray replace(PyObject oldB, PyObject newB, int maxcount) { + return basebytes_replace(oldB, newB, maxcount); + } + + @ExposedMethod(defaults = "null", doc = BuiltinDocs.bytearray_replace_doc) + final PyByteArray bytearray_replace(PyObject oldB, PyObject newB, PyObject count) { + int maxcount = (count == null) ? -1 : count.asInt(); // or count.asIndex() ? + return basebytes_replace(oldB, newB, maxcount); + } + + /** + * Implementation of Python rfind(sub). Return the highest index in the byte array + * where byte sequence sub is found. Return -1 if sub is not found. + * + * @param sub sequence to find (of a type viewable as a byte sequence) + * @return index of start of rightmost ocurrence of sub within this byte array + */ + public int rfind(PyObject sub) { + return basebytes_rfind(sub, null, null); + } + + /** + * Implementation of Python rfind( sub [, start ] ). Return the highest index in + * the byte array where byte sequence sub is found, such that sub is + * contained in the slice [start:]. Return -1 if sub is not found. + * + * @param sub sequence to find (of a type viewable as a byte sequence) + * @param start of slice to search + * @return index of start of rightmost ocurrence of sub within this byte array + */ + public int rfind(PyObject sub, PyObject start) { + return basebytes_rfind(sub, start, null); + } + + /** + * Implementation of Python rfind( sub [, start [, end ]] ). Return the highest + * index in the byte array where byte sequence sub is found, such that + * sub is contained in the slice [start:end]. Arguments + * start and end (which may be null or + * Py.None ) are interpreted as in slice notation. Return -1 if sub is + * not found. + * + * @param sub sequence to find (of a type viewable as a byte sequence) + * @param start of slice to search + * @param end of slice to search + * @return index of start of rightmost ocurrence of sub within this byte array + */ + public int rfind(PyObject sub, PyObject start, PyObject end) { + return basebytes_rfind(sub, start, end); + } + + @ExposedMethod(defaults = {"null", "null"}, doc = BuiltinDocs.bytearray_rfind_doc) + final int bytearray_rfind(PyObject sub, PyObject start, PyObject end) { + return basebytes_rfind(sub, start, end); + } // Based on PyList and not yet properly implemented. // @@ -1015,9 +1135,9 @@ } /* - * ======================================================================================== + * ============================================================================================ * Manipulation of storage capacity - * ======================================================================================== + * ============================================================================================ * * Here we add to the inherited variables defining byte storage, the methods necessary to resize * it. @@ -1073,7 +1193,7 @@ } /** - * Allocate fresh storage for at least the requested number of bytes. Spare bytes are alloceted + * Allocate fresh storage for at least the requested number of bytes. Spare bytes are allocated * evenly at each end of the new storage by choice of a new value for offset. If the size needed * is zero, the "storage" allocated is the shared emptyStorage array. * @@ -1128,16 +1248,19 @@ */ private void storageReplace(int a, int d, int e) { + final int b = size - (a + d); // Count of B section final int c = e - d; // Change in overall size - if (c == 0) - { - return; // Everything stays where it is. + + if (c == 0) { + return;// Everything stays where it is. + } else if (c > 0 && b == 0) { + storageExtend(c); // This is really an extend/append operation + return; } // Compute some handy points of reference final int L = storage.length; final int f = offset; - final int b = size - (a + d); // Count of B section final int s2 = a + e + b; // Size of result s' final int L2 = recLength(s2); // Length of storage for result @@ -1344,6 +1467,93 @@ } /** + * Prepare to insert e elements at the end of the storage currently in use. If + * necessary. existing elements will be moved. The method manipulates the storage + * array contents, size and offset. It will allocate a new array + * storage if necessary for growth. If the initial storage looks like this: + * + *
        +     *               |-          s          -|
        +     * |------f------|-----------a-----------|-----------|
        +     * 
        + * + * then after the call the storage looks like this: + * + *
        +     *               |-              s'             -|
        +     * |------f------|-----------a-----------|---e---|---|
        +     * 
        + * + * or like this if e was too big for the gap, but not enough to provoke reallocation: + * + *
        +     * |-                    s'                 -|
        +     * |-----------a-----------|--------e--------|-------|
        +     * 
        + * + * or like this if was necessary to allocate more storage: + * + *
        +     * |-                        s'                       -|
        +     * |-----------a-----------|-------------e-------------|--------------|
        +     * 
        + * + * where the contents of region a have been preserved, although possbly moved, and + * the gap at the end has the requested size. this method never shrinks the total storage. The + * effect on this PyByteArray is that: + * + *
        +     * this.offset = f or 0
        +     * this.size = s' = a + e
        +     * 
        + * + * The method does not implement the Python repertoire of slice indices, or bound-check the + * sizes given, since code leading up to the call has done that. + * + * @param e size of hole to open (will be x[a, a+e-1]) where a = size before call + */ + private void storageExtend(int e) { + + if (e == 0) { + return; // Everything stays where it is. + } + + // Compute some handy points of reference + final int L = storage.length; + final int f = offset; + final int s2 = size + e; // Size of result s' + final int L2 = recLength(s2); // Length of storage for result + + if (L2 <= L) { + // Ignore recommendations to shrink and use the existing array + // If A is to stay where it is, it means E will end here: + final int g2 = f + s2; + if (g2 > L) { + // ... which unfortunately runs beyond the end of the array. + // We have to move A within the existing array to make room + if (size > 0) { + System.arraycopy(storage, offset, storage, 0, size); + } + offset = 0; + } + // New size + size = s2; + + } else { + // New storage size as recommended + byte[] newStorage = new byte[L2]; + + // Choose the new offset f'=0 to make repeated append operations quicker. + // Copy across the data from existing to new storage. + if (size > 0) { + System.arraycopy(storage, f, newStorage, 0, size); + } + setStorage(newStorage, s2); + + } + } + + /** * Delete d elements at index a by moving together the surrounding * elements. The method manipulates the storage array, size and * offset, and will allocate a new storage array if necessary, or if the deletion @@ -1491,87 +1701,3 @@ } } - - -/* - * >>> for method in dir(bytearray): - ... print method - ... - __add__ - __alloc__ - __class__ - __contains__ - __delattr__ - __delitem__ - __doc__ - __eq__ - __format__ - __ge__ - __getattribute__ - __getitem__ - __gt__ - __hash__ - __iadd__ - __imul__ - __init__ - __iter__ - __le__ - __len__ - __lt__ - __mul__ - __ne__ - __new__ - __reduce__ - __reduce_ex__ - __repr__ - __rmul__ - __setattr__ - __setitem__ - __sizeof__ - __str__ - __subclasshook__ - append - capitalize - center - count - decode - endswith - expandtabs - extend - find - fromhex - index - insert - isalnum - isalpha - isdigit - islower - isspace - istitle - isupper - join - ljust - lower - lstrip - partition - pop - remove - replace - reverse - rfind - rindex - rjust - rpartition - rsplit - rstrip - split - splitlines - startswith - strip - swapcase - title - translate - upper - zfill - >>> - */ \ No newline at end of file -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Wed May 30 05:17:20 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Wed, 30 May 2012 05:17:20 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Implement_bytearray=2Estarts?= =?utf8?q?with_and_endswith=2E?= Message-ID: http://hg.python.org/jython/rev/7e26c04c05d5 changeset: 6676:7e26c04c05d5 user: Jeff Allen date: Sun May 27 15:47:40 2012 +0100 summary: Implement bytearray.startswith and endswith. The score against test_bytes now stands at 2 failures and 61 errors. files: src/org/python/core/BaseBytes.java | 118 +++++++++++++++ src/org/python/core/PyByteArray.java | 109 +++++++++++++ 2 files changed, 227 insertions(+), 0 deletions(-) diff --git a/src/org/python/core/BaseBytes.java b/src/org/python/core/BaseBytes.java --- a/src/org/python/core/BaseBytes.java +++ b/src/org/python/core/BaseBytes.java @@ -747,9 +747,35 @@ public void copyTo(byte[] dest, int destPos) throws ArrayIndexOutOfBoundsException; /** + * Test whether this View has the given prefix, that is, that the first bytes of this View + * match all the bytes of the given prefix. By implication, the test returns false if there + * are too few bytes in this view. + * + * @param prefix pattern to match + * @return true if and only if this view has the given prefix + */ + public boolean startswith(View prefix); + + /** + * Test whether the slice [offset:] of this View has the given prefix, that is, + * that the bytes of this View from index offset match all the bytes of the + * give prefix. By implication, the test returns false if the offset puts the start or end + * of the prefix outside this view (when offset<0 or + * offset+prefix.size()>size()). Python slice semantics are not + * applied to offset. + * + * @param prefix pattern to match + * @param offset at which to start the comparison in this view + * @return true if and only if the slice [offset:] this view has the given + * prefix + */ + public boolean startswith(View prefix, int offset); + + /** * The standard memoryview out of bounds message (does not refer to the underlying type). */ public static final String OUT_OF_BOUNDS = "index out of bounds"; + } /** @@ -805,6 +831,46 @@ dest[p++] = byteAt(i); } } + + /** + * Test whether this View has the given prefix, that is, that the first bytes of this View + * match all the bytes of the given prefix. This class provides an implementation of + * {@link View#startswith(View)} that simply returns startswith(prefix,0) + */ + @Override + public boolean startswith(View prefix) { + return startswith(prefix, 0); + } + + /** + * Test whether this View has the given prefix, that is, that the first bytes of this View + * match all the bytes of the given prefix. This class provides an implementation of + * {@link View#startswith(View,int)} that loops over + * byteAt(i+offset)==prefix.byteAt(i) + */ + @Override + public boolean startswith(View prefix, int offset) { + int j = offset; // index in this + if (j < 0) { + // // Start of prefix is outside this view + return false; + } else { + int len = prefix.size(); + if (j + len > this.size()) { + // End of prefix is outside this view + return false; + } else { + // Last resort: we have actually to look at the bytes! + for (int i = 0; i < len; i++) { + if (byteAt(j++) != prefix.byteAt(i)) { + return false; + } + } + return true; // They must all have matched + } + } + } + } /** @@ -1452,6 +1518,58 @@ } /** + * Almost ready-to-expose implementation serving both Python + * startswith( prefix [, start [, end ]] ) and + * endswith( suffix [, start [, end ]] ). An extra boolean argument specifies which + * to implement on a given call, that is, whether the target is a suffix or prefix. The target + * may also be a tuple of targets. + * + * @param target prefix or suffix sequence to find (of a type viewable as a byte sequence) or a + * tuple of those. + * @param start of slice to search. + * @param end of slice to search. + * @param endswith true if we are doing endswith, false if startswith. + * @return true if and only if this bytearray ends with (one of) target. + */ + protected final synchronized boolean basebytes_starts_or_endswith(PyObject target, + PyObject start, + PyObject end, + boolean endswith) { + /* + * This cheap trick saves us from maintaining two almost identical methods and mirrors + * CPython's _bytearray_tailmatch(). + * + * Start with a view of the slice we are searching. + */ + View v = new ViewOfBytes(this).slice(start, end); + int len = v.size(); + int offset = 0; + + if (target instanceof PyTuple) { + // target is a tuple of suffixes/prefixes and only one need match + for (PyObject s : ((PyTuple)target).getList()) { + // Error if not something we can treat as a view of bytes + View vt = getViewOrError(s); + if (endswith) { + offset = len - vt.size(); + } + if (v.startswith(vt, offset)) { + return true; + } + } + return false; // None of them matched + + } else { + // Error if target is not something we can treat as a view of bytes + View vt = getViewOrError(target); + if (endswith) { + offset = len - vt.size(); + } + return v.startswith(vt, offset); + } + } + + /** * Copy the bytes of a byte array to the characters of a String with no change in ordinal value. * This could also be described as 'latin-1' decoding of the byte array to a String. * diff --git a/src/org/python/core/PyByteArray.java b/src/org/python/core/PyByteArray.java --- a/src/org/python/core/PyByteArray.java +++ b/src/org/python/core/PyByteArray.java @@ -881,6 +881,60 @@ return basebytes_count(sub, start, end); } + /** + * Implementation of Python endswith(suffix). + * + * When suffix is of a type that may be treated as an array of bytes, return + * true if and only if this bytearray ends with the suffix. + * suffix can also be a tuple of suffixes to look for. + * + * @param suffix byte array to match, or object viewable as such, or a tuple of them + * @return true if and only if this bytearray ends with the suffix (or one of them) + */ + public boolean endswith(PyObject suffix) { + return basebytes_starts_or_endswith(suffix, null, null, true); + } + + /** + * Implementation of Python endswith( suffix [, start ] ). + * + * When suffix is of a type that may be treated as an array of bytes, return + * true if and only if this bytearray ends with the suffix. + * suffix can also be a tuple of suffixes to look for. With optional + * start (which may be null or Py.None), define the + * effective bytearray to be the slice [start:] of this bytearray. + * + * @param suffix byte array to match, or object viewable as such, or a tuple of them + * @param start of slice in this bytearray to match + * @return true if and only if this[start:] ends with the suffix (or one of them) + */ + public boolean endswith(PyObject suffix, PyObject start) { + return basebytes_starts_or_endswith(suffix, start, null, true); + } + + /** + * Implementation of Python endswith( suffix [, start [, end ]] ). + * + * When suffix is of a type that may be treated as an array of bytes, return + * true if and only if this bytearray ends with the suffix. + * suffix can also be a tuple of suffixes to look for. With optional + * start and end (which may be null or + * Py.None), define the effective bytearray to be the slice + * [start:end] of this bytearray. + * + * @param suffix byte array to match, or object viewable as such, or a tuple of them + * @param start of slice in this bytearray to match + * @param end of slice in this bytearray to match + * @return true if and only if this[start:end] ends with the suffix (or one of them) + */ + public boolean endswith(PyObject suffix, PyObject start, PyObject end) { + return basebytes_starts_or_endswith(suffix, start, end, true); + } + + @ExposedMethod(defaults = {"null", "null"}, doc = BuiltinDocs.bytearray_endswith_doc) + final boolean bytearray_endswith(PyObject suffix, PyObject start, PyObject end) { + return basebytes_starts_or_endswith(suffix, start, end, true); + } /** * Append the elements in the argument sequence to the end of the array, equivalent to: @@ -1200,6 +1254,61 @@ return pos; } + /** + * Implementation of Python startswith(prefix). + * + * When prefix is of a type that may be treated as an array of bytes, return + * true if and only if this bytearray starts with the prefix. + * prefix can also be a tuple of prefixes to look for. + * + * @param prefix byte array to match, or object viewable as such, or a tuple of them + * @return true if and only if this bytearray starts with the prefix (or one of them) + */ + public boolean startswith(PyObject prefix) { + return basebytes_starts_or_endswith(prefix, null, null, false); + } + + /** + * Implementation of Python startswith( prefix [, start ] ). + * + * When prefix is of a type that may be treated as an array of bytes, return + * true if and only if this bytearray starts with the prefix. + * prefix can also be a tuple of prefixes to look for. With optional + * start (which may be null or Py.None), define the + * effective bytearray to be the slice [start:] of this bytearray. + * + * @param prefix byte array to match, or object viewable as such, or a tuple of them + * @param start of slice in this bytearray to match + * @return true if and only if this[start:] starts with the prefix (or one of them) + */ + public boolean startswith(PyObject prefix, PyObject start) { + return basebytes_starts_or_endswith(prefix, start, null, false); + } + + /** + * Implementation of Python startswith( prefix [, start [, end ]] ). + * + * When prefix is of a type that may be treated as an array of bytes, return + * true if and only if this bytearray starts with the prefix. + * prefix can also be a tuple of prefixes to look for. With optional + * start and end (which may be null or + * Py.None), define the effective bytearray to be the slice + * [start:end] of this bytearray. + * + * @param prefix byte array to match, or object viewable as such, or a tuple of them + * @param start of slice in this bytearray to match + * @param end of slice in this bytearray to match + * @return true if and only if this[start:end] starts with the prefix (or one of them) + */ + public boolean startswith(PyObject prefix, PyObject start, PyObject end) { + return basebytes_starts_or_endswith(prefix, start, end, false); + } + + @ExposedMethod(defaults = {"null", "null"}, doc = BuiltinDocs.bytearray_startswith_doc) + final boolean bytearray_startswith(PyObject prefix, PyObject start, PyObject end) { + return basebytes_starts_or_endswith(prefix, start, end, false); + } + // Based on PyList and not yet properly implemented. // // @Override -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Wed May 30 05:17:20 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Wed, 30 May 2012 05:17:20 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_TypeError_to_be_raised_by_by?= =?utf8?b?dGVhcnJheS5fX2NvbnRhaW5zX18gKCdpbicgb3BlcmF0b3IpLCByYXRoZXIgdGhh?= =?utf8?q?n?= Message-ID: http://hg.python.org/jython/rev/f81766bd2e4c changeset: 6677:f81766bd2e4c user: Jeff Allen date: Sun May 27 16:19:06 2012 +0100 summary: TypeError to be raised by bytearray.__contains__ ('in' operator), rather than NotImplementedError. Although the problem is arguably non-implementation of the buffer API, TypeError matches expectations for types that really shouldn't have the API. Now scoring 2 failures and 60 errors on test_bytes.py files: src/org/python/core/BaseBytes.java | 7 +++++-- 1 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/org/python/core/BaseBytes.java b/src/org/python/core/BaseBytes.java --- a/src/org/python/core/BaseBytes.java +++ b/src/org/python/core/BaseBytes.java @@ -937,9 +937,12 @@ protected static View getViewOrError(PyObject b) { View res = getView(b); if (res == null) { - // String fmt = "type %s doesn't support the buffer API"; // CPython String fmt = "cannot access type %s as bytes"; - throw Py.NotImplementedError(String.format(fmt, b.getType().fastGetName())); + throw Py.TypeError(String.format(fmt, b.getType().fastGetName())); + // A more honest response here would have been: + // . String fmt = "type %s doesn't support the buffer API"; // CPython + // . throw Py.NotImplementedError(String.format(fmt, b.getType().fastGetName())); + // since our inability to handle certain types is lack of a buffer API generally. } return res; } -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Wed May 30 05:17:20 2012 From: jython-checkins at python.org (frank.wierzbicki) Date: Wed, 30 May 2012 05:17:20 +0200 Subject: [Jython-checkins] =?utf8?q?jython=3A_Formatting_changes_in_BaseBy?= =?utf8?q?tes_and_PyByteArray=2E?= Message-ID: http://hg.python.org/jython/rev/7195c0b69032 changeset: 6678:7195c0b69032 user: Jeff Allen date: Sun May 27 17:03:17 2012 +0100 summary: Formatting changes in BaseBytes and PyByteArray. No code change, except where already commented out. Minor doc-comment improvements. files: src/org/python/core/BaseBytes.java | 117 +------- src/org/python/core/PyByteArray.java | 203 ++------------ 2 files changed, 52 insertions(+), 268 deletions(-) diff --git a/src/org/python/core/BaseBytes.java b/src/org/python/core/BaseBytes.java --- a/src/org/python/core/BaseBytes.java +++ b/src/org/python/core/BaseBytes.java @@ -213,7 +213,7 @@ * ============================================================================================ * * Methods here help subclasses set the initial state. They are designed with bytearray in mind, - * but note that from Python 3, bytes() has the same set of calls and behaviours, although in + * but note that from Python 3, bytes() has the same set of calls and behaviours. In * Peterson's "sort of backport" to Python 2.x, bytes is effectively an alias for str and it * shows. */ @@ -306,7 +306,7 @@ } /** - * Helper for {@linkplain #setslice(int, int, int, PyObject)}, for __new__ and + * Helper for {@link #setslice(int, int, int, PyObject)}, for __new__ and * __init__ and the Java API constructor from a text string with the specified * encoding in subclasses. This method thinly wraps a call to the codecs module and deals with * checking for PyUnicode (where the encoding argument is mandatory). @@ -511,7 +511,7 @@ Fragment curr = null; // Allocate series of fragments as needed, while the iterator runs to completion - // + for (PyObject value : iter) { if (curr == null) { // Need a new Fragment @@ -738,8 +738,9 @@ public View slice(PyObject start, PyObject end); /** - * Copy the bytes of this view to the specified position in a destination array. - * All the bytes of the View are copied. + * Copy the bytes of this view to the specified position in a destination array. All the + * bytes of the View are copied. + * * @param dest destination array * @param destPos index in the destination at which this.byteAt(0) is written * @throws ArrayIndexOutOfBoundsException if the destination is too small @@ -1235,7 +1236,6 @@ } - /** * Comparison function between a byte array and a byte-oriented View of some other object, such * as a String, returning 1, 0 or -1 as a>b, a==b, or a<b respectively. The comparison is by @@ -1314,7 +1314,6 @@ } } - /** * Fail-fast comparison function between byte array types and any other object, for when the * test is only for equality. The "rich comparison" operators __eq__ and @@ -1350,7 +1349,6 @@ } } - /** * Implementation of __eq__ (equality) operator, capable of comparison with another byte array * or bytes. Comparison with an invalid type returns null. @@ -1665,6 +1663,12 @@ return basebytes___reduce__(); } + /** + * Ready-to-expose implementation of Python __reduce__() method used in pickle (persistence) of + * Python objects. + * + * @return required tuple of type, arguments needed by init, and any user-added attributes. + */ final PyTuple basebytes___reduce__() { PyUnicode encoded = new PyUnicode(this.asEncodedString()); PyObject args = new PyTuple(encoded, getPickleEncoding()); @@ -1814,7 +1818,7 @@ /* * We defer computing the table from construction to this point mostly because - * calculateSkipTable() may be overridden. + * calculateSkipTable() may be overridden, and we want to use the right one. */ if (pattern.size() > 1 && skipTable == null) { skipTable = calculateSkipTable(); @@ -1963,6 +1967,10 @@ /** * Mask defining how many of the bits of each byte are used when looking up the skip, used * like: skip = skipTable[MASK & currentByte]. + *

        + * Note that the way this is written at the moment, if MASK is different from + * super.MASK calculateSkipTable() and nextIndex() + * must both be overridden consistently to use the local definition. */ private static final byte MASK = 0x1f; @@ -2063,8 +2071,7 @@ } /** - * Ready-to-expose implementation of Python count( sub [, start [, end ]] ). - * Return + * Ready-to-expose implementation of Python count( sub [, start [, end ]] ). Return * the number of non-overlapping occurrences of sub in the range [start, end]. * Optional arguments start and end (which may be null or * Py.None ) are interpreted as in slice notation. @@ -2108,7 +2115,7 @@ * the highest index in the byte array where byte sequence sub is found, such that * sub is contained in the slice [start:end]. Arguments * start and end (which may be null or - * Py.None ) are interpreted as in slice notation. Return -1 if sub is + * Py.None) are interpreted as in slice notation. Return -1 if sub is * not found. * * @param sub bytes to find @@ -2484,9 +2491,6 @@ * Helper to implement {@link #repeat(int)}. Use something like: * *

        -     *
        -     *
        -     *
              * @Override
              * protected PyByteArray repeat(int count) {
              *     PyByteArray ret = new PyByteArray();
        @@ -2609,8 +2613,6 @@
             }
         
             /*
        -     * @return
        -     *
              * @see java.util.List#iterator()
              */
             public Iterator iterator() {
        @@ -2618,8 +2620,6 @@
             }
         
             /*
        -     * @return
        -     *
              * @see java.util.List#toArray()
              */
             public Object[] toArray() {
        @@ -2627,10 +2627,6 @@
             }
         
             /*
        -     * @param a
        -     *
        -     * @return
        -     *
              * @see java.util.List#toArray(T[])
              */
             public  T[] toArray(T[] a) {
        @@ -2638,10 +2634,6 @@
             }
         
             /*
        -     * @param o
        -     *
        -     * @return
        -     *
              * @see java.util.List#add(java.lang.Object)
              */
             public boolean add(PyInteger o) {
        @@ -2649,10 +2641,6 @@
             }
         
             /*
        -     * @param o
        -     *
        -     * @return
        -     *
              * @see java.util.List#remove(java.lang.Object)
              */
             public boolean remove(Object o) {
        @@ -2660,10 +2648,6 @@
             }
         
             /*
        -     * @param c
        -     *
        -     * @return
        -     *
              * @see java.util.List#containsAll(java.util.Collection)
              */
             public boolean containsAll(Collection c) {
        @@ -2671,10 +2655,6 @@
             }
         
             /*
        -     * @param c
        -     *
        -     * @return
        -     *
              * @see java.util.List#addAll(java.util.Collection)
              */
             public boolean addAll(Collection c) {
        @@ -2682,12 +2662,6 @@
             }
         
             /*
        -     * @param index
        -     *
        -     * @param c
        -     *
        -     * @return
        -     *
              * @see java.util.List#addAll(int, java.util.Collection)
              */
             public boolean addAll(int index, Collection c) {
        @@ -2695,10 +2669,6 @@
             }
         
             /*
        -     * @param c
        -     *
        -     * @return
        -     *
              * @see java.util.List#removeAll(java.util.Collection)
              */
             public boolean removeAll(Collection c) {
        @@ -2706,10 +2676,6 @@
             }
         
             /*
        -     * @param c
        -     *
        -     * @return
        -     *
              * @see java.util.List#retainAll(java.util.Collection)
              */
             public boolean retainAll(Collection c) {
        @@ -2717,7 +2683,6 @@
             }
         
             /*
        -     *
              * @see java.util.List#clear()
              */
             public void clear() {
        @@ -2725,10 +2690,6 @@
             }
         
             /*
        -     * @param o
        -     *
        -     * @return
        -     *
              * @see java.util.List#equals(java.lang.Object)
              */
             public boolean equals(Object o) {
        @@ -2736,8 +2697,6 @@
             }
         
             /*
        -     * @return
        -     *
              * @see java.util.List#hashCode()
              */
             public int hashCode() {
        @@ -2745,10 +2704,6 @@
             }
         
             /*
        -     * @param index
        -     *
        -     * @return
        -     *
              * @see java.util.List#get(int)
              */
             public PyInteger get(int index) {
        @@ -2756,12 +2711,6 @@
             }
         
             /*
        -     * @param index
        -     *
        -     * @param element
        -     *
        -     * @return
        -     *
              * @see java.util.List#set(int, java.lang.Object)
              */
             public PyInteger set(int index, PyInteger element) {
        @@ -2769,10 +2718,6 @@
             }
         
             /*
        -     * @param index
        -     *
        -     * @param element
        -     *
              * @see java.util.List#add(int, java.lang.Object)
              */
             public void add(int index, PyInteger element) {
        @@ -2780,10 +2725,6 @@
             }
         
             /*
        -     * @param index
        -     *
        -     * @return
        -     *
              * @see java.util.List#remove(int)
              */
             public PyInteger remove(int index) {
        @@ -2791,10 +2732,6 @@
             }
         
             /*
        -     * @param o
        -     *
        -     * @return
        -     *
              * @see java.util.List#indexOf(java.lang.Object)
              */
             public int indexOf(Object o) {
        @@ -2802,10 +2739,6 @@
             }
         
             /*
        -     * @param o
        -     *
        -     * @return
        -     *
              * @see java.util.List#lastIndexOf(java.lang.Object)
              */
             public int lastIndexOf(Object o) {
        @@ -2813,8 +2746,6 @@
             }
         
             /*
        -     * @return
        -     *
              * @see java.util.List#listIterator()
              */
             public ListIterator listIterator() {
        @@ -2822,10 +2753,6 @@
             }
         
             /*
        -     * @param index
        -     *
        -     * @return
        -     *
              * @see java.util.List#listIterator(int)
              */
             public ListIterator listIterator(int index) {
        @@ -2833,12 +2760,6 @@
             }
         
             /*
        -     * @param fromIndex
        -     *
        -     * @param toIndex
        -     *
        -     * @return
        -     *
              * @see java.util.List#subList(int, int)
              */
             public List subList(int fromIndex, int toIndex) {
        diff --git a/src/org/python/core/PyByteArray.java b/src/org/python/core/PyByteArray.java
        --- a/src/org/python/core/PyByteArray.java
        +++ b/src/org/python/core/PyByteArray.java
        @@ -197,7 +197,6 @@
                 init(arg);
             }
         
        -
             /* ============================================================================================
              * API for org.python.core.PySequence
              * ============================================================================================
        @@ -362,7 +361,6 @@
                      * it isn't one, we get an exception about not being iterable, or about the values.
                      */
                     setslice(start, stop, step, value.asIterable());
        -
                 }
             }
         
        @@ -437,30 +435,6 @@
             private void setslice(int start, int stop, int step, MemoryView value) throws PyException {
                 // XXX Support memoryview once means of access to bytes is defined
                 throw Py.NotImplementedError("memoryview not yet supported in bytearray");
        -//        String format = value.get_format();
        -//        boolean isBytes = format == null || "B".equals(format);
        -//        if (value.get_ndim() != 1 || !isBytes) {
        -//            throw Py.TypeError("memoryview value must be byte-oriented");
        -//        } else {
        -//            // Dimensions are given as a PyTuple (although only one)
        -//            int len = value.get_shape().pyget(0).asInt();
        -//            if (step == 1) {
        -//                // Delete this[start:stop] and open a space of the right size
        -//                storageReplace(start, stop - start, len);
        -//                // System.arraycopy(value.storage, value.offset, storage, start
        -//                // + offset, len);
        -//            } else {
        -//                // This is an extended slice which means we are replacing elements
        -//                int n = sliceLength(start, stop, step);
        -//                if (n != len) {
        -//                    throw SliceSizeError("bytes", len, n);
        -//                }
        -//                // int no = n + value.offset;
        -//                // for (int io = start + offset, jo = value.offset; jo < no; io += step, jo++) {
        -//                // storage[io] = value.storage[jo]; // Assign this[i] = value[j]
        -//                // }
        -//            }
        -//        }
             }
         
             /**
        @@ -650,10 +624,11 @@
                 PyObject errors = ap.getPyObjectByType(2, PyBaseString.TYPE, null);
         
                 /*
        -         * This whole method is modelled on CPython (see Objects/bytearrayobject.c : bytes_init())
        -         * but reorganised somewhat to maximise re-use with the implementation of assignment to a
        -         * slice, which essentially has to construct a bytearray from the right-hand side.
        -         * Hopefully, it still tries the same things in the same order and fails in the same way.
        +         * This method and the related init()s are modelled on CPython (see
        +         * Objects/bytearrayobject.c : bytes_init()) but reorganised somewhat to maximise re-use
        +         * with the implementation of assignment to a slice, which essentially has to construct a
        +         * bytearray from the right-hand side. Hopefully, it still tries the same things in the same
        +         * order and fails in the same way.
                  */
         
                 if (encoding != null || errors != null) {
        @@ -783,7 +758,6 @@
                 return sum;
             }
         
        -
             /**
              * Returns the number of bytes actually allocated.
              */
        @@ -834,11 +808,9 @@
                 return basebytes_decode(args, keywords);
             }
         
        -
             /**
        -     * Implementation of Python count(sub).
        -     *  Return
        -     * the number of non-overlapping occurrences of sub in this byte array.
        +     * Implementation of Python count(sub). Return the number of non-overlapping
        +     * occurrences of sub in this byte array.
              *
              * @param sub sequence to find (of a type viewable as a byte sequence)
              * @return count of occurrences of sub within this byte array
        @@ -848,9 +820,8 @@
             }
         
             /**
        -     * Implementation of Python count( sub [, start ] ).
        -     *  Return
        -     * the number of non-overlapping occurrences of sub in the range [start:].
        +     * Implementation of Python count( sub [, start ] ). Return the number of
        +     * non-overlapping occurrences of sub in the range [start:].
              *
              * @param sub sequence to find (of a type viewable as a byte sequence)
              * @param start of slice to search
        @@ -861,10 +832,9 @@
             }
         
             /**
        -     * Implementation of Python count( sub [, start [, end ]] ).
        -     *  Return
        -     * the number of non-overlapping occurrences of sub in the range [start, end].
        -     * Optional arguments start and end (which may be null or
        +     * Implementation of Python count( sub [, start [, end ]] ). Return the number of
        +     * non-overlapping occurrences of sub in the range [start, end]. Optional arguments
        +     * start and end (which may be null or
              * Py.None ) are interpreted as in slice notation.
              *
              * @param sub sequence to find (of a type viewable as a byte sequence)
        @@ -1022,9 +992,8 @@
             }
         
             /**
        -     * Implementation of Python index(sub).
        -     * Like {@link #find(PyObject)}
        -     * but raise {@link Py#ValueError} if sub is not found.
        +     * Implementation of Python index(sub). Like {@link #find(PyObject)} but raise
        +     * {@link Py#ValueError} if sub is not found.
              *
              * @param sub sequence to find (of a type viewable as a byte sequence)
              * @return index of start of occurrence of sub within this byte array
        @@ -1034,9 +1003,9 @@
             }
         
             /**
        -     * Implementation of Python index( sub [, start ] ).
        -     * Like {@link #find(PyObject,PyObject)}
        -     * but raise {@link Py#ValueError} if sub is not found.
        +     * Implementation of Python index( sub [, start ] ). Like
        +     * {@link #find(PyObject,PyObject)} but raise {@link Py#ValueError} if sub is not
        +     * found.
              *
              * @param sub sequence to find (of a type viewable as a byte sequence)
              * @param start of slice to search
        @@ -1047,9 +1016,9 @@
             }
         
             /**
        -     * Implementation of Python index( sub [, start [, end ]] ).
        -     * Like {@link #find(PyObject,PyObject,PyObject)}
        -     * but raise {@link Py#ValueError} if sub is not found.
        +     * Implementation of Python index( sub [, start [, end ]] ). Like
        +     * {@link #find(PyObject,PyObject,PyObject)} but raise {@link Py#ValueError} if sub
        +     * is not found.
              *
              * @param sub sequence to find (of a type viewable as a byte sequence)
              * @param start of slice to search
        @@ -1065,15 +1034,15 @@
             final int bytearray_index(PyObject sub, PyObject start, PyObject end) {
                 // Like find but raise a ValueError if not found
                 int pos = basebytes_find(sub, start, end);
        -        if (pos<0) {
        +        if (pos < 0) {
                     throw Py.ValueError("subsection not found");
                 }
                 return pos;
             }
         
             /**
        -     * Insert the argument element into the byte array at the specified index.
        -     * Same as s[index:index] = [o] if index >= 0.
        +     * Insert the argument element into the byte array at the specified index. Same as
        +     * s[index:index] = [o] if index >= 0.
              *
              * @param index the position where the element will be inserted.
              * @param value the element to insert.
        @@ -1088,7 +1057,6 @@
                 pyinsert(boundToSequence(index.asIndex()), value);
             }
         
        -
             @ExposedMethod(doc = BuiltinDocs.bytearray___len___doc)
             final int bytearray___len__() {
                 return __len__();
        @@ -1099,7 +1067,6 @@
                 return basebytes___reduce__();
             }
         
        -
             /**
              * Remove the first occurrence of an element from the array, equivalent to:
              * del s[s.index(x)], although x must be convertable to a single byte value. The
        @@ -1206,9 +1173,8 @@
             }
         
             /**
        -     * Implementation of Python rindex(sub).
        -     * Like {@link #find(PyObject)}
        -     * but raise {@link Py#ValueError} if sub is not found.
        +     * Implementation of Python rindex(sub). Like {@link #find(PyObject)} but raise
        +     * {@link Py#ValueError} if sub is not found.
              *
              * @param sub sequence to find (of a type viewable as a byte sequence)
              * @return index of start of occurrence of sub within this byte array
        @@ -1218,9 +1184,9 @@
             }
         
             /**
        -     * Implementation of Python rindex( sub [, start ] ).
        -     * Like {@link #find(PyObject,PyObject)}
        -     * but raise {@link Py#ValueError} if sub is not found.
        +     * Implementation of Python rindex( sub [, start ] ). Like
        +     * {@link #find(PyObject,PyObject)} but raise {@link Py#ValueError} if sub is not
        +     * found.
              *
              * @param sub sequence to find (of a type viewable as a byte sequence)
              * @param start of slice to search
        @@ -1231,9 +1197,9 @@
             }
         
             /**
        -     * Implementation of Python rindex( sub [, start [, end ]] ).
        -     * Like {@link #find(PyObject,PyObject,PyObject)}
        -     * but raise {@link Py#ValueError} if sub is not found.
        +     * Implementation of Python rindex( sub [, start [, end ]] ). Like
        +     * {@link #find(PyObject,PyObject,PyObject)} but raise {@link Py#ValueError} if sub
        +     * is not found.
              *
              * @param sub sequence to find (of a type viewable as a byte sequence)
              * @param start of slice to search
        @@ -1248,7 +1214,7 @@
             final int bytearray_rindex(PyObject sub, PyObject start, PyObject end) {
                 // Like rfind but raise a ValueError if not found
                 int pos = basebytes_rfind(sub, start, end);
        -        if (pos<0) {
        +        if (pos < 0) {
                     throw Py.ValueError("subsection not found");
                 }
                 return pos;
        @@ -1309,114 +1275,11 @@
                 return basebytes_starts_or_endswith(prefix, start, end, false);
             }
         
        -// Based on PyList and not yet properly implemented.
        -//
        -//    @Override
        -//    public PyObject __imul__(PyObject o) {
        -//        return bytearray___imul__(o);
        -//    }
        -//
        -//    @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___imul___doc)
        -//    final synchronized PyObject bytearray___imul__(PyObject o) {
        -//        if (!o.isIndex()) {
        -//            return null;
        -//        }
        -//        int count = o.asIndex(Py.OverflowError);
        -//
        -//        int size = size();
        -//        if (size == 0 || count == 1) {
        -//            return this;
        -//        }
        -//
        -//        if (count < 1) {
        -//            clear();
        -//            return this;
        -//        }
        -//
        -//        if (size > Integer.MAX_VALUE / count) {
        -//            throw Py.MemoryError("");
        -//        }
        -//
        -//        int newSize = size * count;
        -//        if (storage instanceof ArrayList) {
        -//            ((ArrayList) storage).ensureCapacity(newSize);
        -//        }
        -//        List oldList = new ArrayList(storage);
        -//        for (int i = 1; i < count; i++) {
        -//            storage.addAll(oldList);
        -//        }
        -//        gListAllocatedStatus = storage.size(); // now omit?
        -//        return this;
        -//    }
        -//
        -//    @Override
        -//    public PyObject __mul__(PyObject o) {
        -//        return bytearray___mul__(o);
        -//    }
        -//
        -//    @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___mul___doc)
        -//    final synchronized PyObject bytearray___mul__(PyObject o) {
        -//        if (!o.isIndex()) {
        -//            return null;
        -//        }
        -//        return repeat(o.asIndex(Py.OverflowError));
        -//    }
        -//
        -//    @Override
        -//    public PyObject __rmul__(PyObject o) {
        -//        return bytearray___rmul__(o);
        -//    }
        -//
        -//    @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___rmul___doc)
        -//    final synchronized PyObject bytearray___rmul__(PyObject o) {
        -//        if (!o.isIndex()) {
        -//            return null;
        -//        }
        -//        return repeat(o.asIndex(Py.OverflowError));
        -//    }
        -//
        -//    @ExposedMethod(doc = BuiltinDocs.bytearray___contains___doc)
        -//    final synchronized boolean bytearray___contains__(PyObject o) {
        -//        return object___contains__(o);
        -//    }
        -//
        -//    @ExposedMethod(doc = BuiltinDocs.bytearray___delitem___doc)
        -//    final synchronized void bytearray___delitem__(PyObject index) {
        -//        seq___delitem__(index);
        -//    }
        -//
             @ExposedMethod(doc = BuiltinDocs.bytearray___setitem___doc)
             final synchronized void bytearray___setitem__(PyObject o, PyObject def) {
                 seq___setitem__(o, def);
             }
         
        -//    @ExposedMethod(doc = BuiltinDocs.bytearray___getitem___doc)
        -//    final synchronized PyObject bytearray___getitem__(PyObject o) {
        -//        PyObject ret = seq___finditem__(o);
        -//        if (ret == null) {
        -//            throw Py.IndexError("index out of range: " + o);
        -//        }
        -//        return ret;
        -//    }
        -//
        -//    @Override
        -//    public PyObject __iter__() {
        -//        return bytearray___iter__();
        -//    }
        -//
        -//    @ExposedMethod(doc = BuiltinDocs.bytearray___iter___doc)
        -//    public synchronized PyObject bytearray___iter__() {
        -//        return new PyFastSequenceIter(this);
        -//    }
        -//
        -//    @Override
        -//    protected String unsupportedopMessage(String op, PyObject o2) {
        -//        if (op.equals("+")) {
        -//            return "can only concatenate storage (not \"{2}\") to storage";
        -//        }
        -//        return super.unsupportedopMessage(op, o2);
        -//    }
        -//
             @Override
             public String toString() {
                 return bytearray_toString();
        @@ -1424,7 +1287,7 @@
         
             @ExposedMethod(names = {"__repr__", "__str__"}, doc = BuiltinDocs.bytearray___repr___doc)
             final synchronized String bytearray_toString() {
        -        return "bytearray(b'" + asEscapedString()  + "')";
        +        return "bytearray(b'" + asEscapedString() + "')";
             }
         
             /*
        
        -- 
        Repository URL: http://hg.python.org/jython