diff options
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/ctypes/test/test_numbers.py | 18 | ||||
-rw-r--r-- | Lib/decimal.py | 2 | ||||
-rw-r--r-- | Lib/pprint.py | 10 | ||||
-rwxr-xr-x | Lib/rational.py | 72 | ||||
-rw-r--r-- | Lib/test/crashers/loosing_dict_ref.py | 21 | ||||
-rw-r--r-- | Lib/test/test_decimal.py | 11 | ||||
-rw-r--r-- | Lib/test/test_descr.py | 24 | ||||
-rw-r--r-- | Lib/test/test_grammar.py | 9 | ||||
-rw-r--r-- | Lib/test/test_pprint.py | 192 | ||||
-rw-r--r-- | Lib/test/test_rational.py | 17 | ||||
-rw-r--r-- | Lib/test/test_set.py | 106 | ||||
-rw-r--r-- | Lib/test/test_urllib2net.py | 60 | ||||
-rw-r--r-- | Lib/threading.py | 33 |
13 files changed, 492 insertions, 83 deletions
diff --git a/Lib/ctypes/test/test_numbers.py b/Lib/ctypes/test/test_numbers.py index 680d29d..16e69cb 100644 --- a/Lib/ctypes/test/test_numbers.py +++ b/Lib/ctypes/test/test_numbers.py @@ -105,15 +105,31 @@ class NumberTestCase(unittest.TestCase): def test_floats(self): # c_float and c_double can be created from # Python int, long and float + class FloatLike(object): + def __float__(self): + return 2.0 + f = FloatLike() for t in float_types: self.failUnlessEqual(t(2.0).value, 2.0) self.failUnlessEqual(t(2).value, 2.0) self.failUnlessEqual(t(2).value, 2.0) + self.failUnlessEqual(t(f).value, 2.0) def test_integers(self): - # integers cannot be constructed from floats + class FloatLike(object): + def __float__(self): + return 2.0 + f = FloatLike() + class IntLike(object): + def __int__(self): + return 2 + i = IntLike() + # integers cannot be constructed from floats, + # but from integer-like objects for t in signed_types + unsigned_types: self.assertRaises(TypeError, t, 3.14) + self.assertRaises(TypeError, t, f) + self.failUnlessEqual(t(i).value, 2) def test_sizes(self): for t in signed_types + unsigned_types + float_types + bool_types: diff --git a/Lib/decimal.py b/Lib/decimal.py index 51758f2..1652914 100644 --- a/Lib/decimal.py +++ b/Lib/decimal.py @@ -1451,6 +1451,8 @@ class Decimal(_numbers.Real, _numbers.Inexact): else: return s*int(self._int[:self._exp] or '0') + __trunc__ = __int__ + def _fix_nan(self, context): """Decapitate the payload of a NaN to fit the context""" payload = self._int diff --git a/Lib/pprint.py b/Lib/pprint.py index 09b4a9f..b903391 100644 --- a/Lib/pprint.py +++ b/Lib/pprint.py @@ -164,25 +164,31 @@ class PrettyPrinter: (issubclass(typ, set) and r is set.__repr__) or (issubclass(typ, frozenset) and r is frozenset.__repr__) ): + length = _len(object) if issubclass(typ, list): write('[') endchar = ']' elif issubclass(typ, set): + if not length: + write('set()') + return write('{') endchar = '}' object = sorted(object) indent += 4 elif issubclass(typ, frozenset): + if not length: + write('frozenset()') + return write('frozenset([') endchar = '])' object = sorted(object) - indent += 9 + indent += 10 else: write('(') endchar = ')' if self._indent_per_level > 1: write((self._indent_per_level - 1) * ' ') - length = _len(object) if length: context[objid] = 1 indent = indent + self._indent_per_level diff --git a/Lib/rational.py b/Lib/rational.py index 4a56cf2..89b622c 100755 --- a/Lib/rational.py +++ b/Lib/rational.py @@ -13,7 +13,7 @@ __all__ = ["Rational"] RationalAbc = numbers.Rational -def _gcd(a, b): +def _gcd(a, b): # XXX This is a useful function. Consider making it public. """Calculate the Greatest Common Divisor. Unless b==0, the result will have the same sign as b (so that when @@ -39,6 +39,8 @@ def _binary_float_to_ratio(x): >>> _binary_float_to_ratio(-.25) (-1, 4) """ + # XXX Consider moving this to to floatobject.c + # with a name like float.as_intger_ratio() if x == 0: return 0, 1 @@ -79,6 +81,10 @@ def _binary_float_to_ratio(x): _RATIONAL_FORMAT = re.compile( r'^\s*(?P<sign>[-+]?)(?P<num>\d+)(?:/(?P<denom>\d+))?\s*$') +# XXX Consider accepting decimal strings as input since they are exact. +# Rational("2.01") --> s="2.01" ; Rational.from_decimal(Decimal(s)) --> Rational(201, 100)" +# If you want to avoid going through the decimal module, just parse the string directly: +# s.partition('.') --> ('2', '.', '01') --> Rational(int('2'+'01'), 10**len('01')) --> Rational(201, 100) class Rational(RationalAbc): """This class implements rational numbers. @@ -93,7 +99,7 @@ class Rational(RationalAbc): """ - __slots__ = ('_numerator', '_denominator') + __slots__ = ('numerator', 'denominator') # We're immutable, so use __new__ not __init__ def __new__(cls, numerator=0, denominator=1): @@ -133,8 +139,8 @@ class Rational(RationalAbc): raise ZeroDivisionError('Rational(%s, 0)' % numerator) g = _gcd(numerator, denominator) - self._numerator = int(numerator // g) - self._denominator = int(denominator // g) + self.numerator = int(numerator // g) + self.denominator = int(denominator // g) return self @classmethod @@ -192,29 +198,22 @@ class Rational(RationalAbc): n, d = d, n return cf - @classmethod - def approximate_from_float(cls, f, max_denominator): - 'Best rational approximation to f with a denominator <= max_denominator' + def approximate(self, max_denominator): + 'Best rational approximation with a denominator <= max_denominator' # XXX First cut at algorithm # Still needs rounding rules as specified at # http://en.wikipedia.org/wiki/Continued_fraction - cf = cls.from_float(f).as_continued_fraction() + if self.denominator <= max_denominator: + return self + cf = self.as_continued_fraction() result = Rational(0) for i in range(1, len(cf)): - new = cls.from_continued_fraction(cf[:i]) + new = self.from_continued_fraction(cf[:i]) if new.denominator > max_denominator: break result = new return result - @property - def numerator(a): - return a._numerator - - @property - def denominator(a): - return a._denominator - def __repr__(self): """repr(self)""" return ('Rational(%r,%r)' % (self.numerator, self.denominator)) @@ -226,6 +225,16 @@ class Rational(RationalAbc): else: return '%s/%s' % (self.numerator, self.denominator) + """ XXX This section needs a lot more commentary + + * Explain the typical sequence of checks, calls, and fallbacks. + * Explain the subtle reasons why this logic was needed. + * It is not clear how common cases are handled (for example, how + does the ratio of two huge integers get converted to a float + without overflowing the long-->float conversion. + + """ + def _operator_fallbacks(monomorphic_operator, fallback_operator): """Generates forward and reverse operators given a purely-rational operator and a function from the operator module. @@ -299,18 +308,15 @@ class Rational(RationalAbc): """a // b""" return math.floor(a / b) - @classmethod - def _mod(cls, a, b): - div = a // b - return a - b * div - def __mod__(a, b): """a % b""" - return a._mod(a, b) + div = a // b + return a - b * div def __rmod__(b, a): """a % b""" - return b._mod(a, b) + div = a // b + return a - b * div def __pow__(a, b): """a ** b @@ -369,6 +375,8 @@ class Rational(RationalAbc): else: return a.numerator // a.denominator + __int__ = __trunc__ + def __floor__(a): """Will be math.floor(a) in 3.0.""" return a.numerator // a.denominator @@ -410,6 +418,7 @@ class Rational(RationalAbc): float must have the same hash as that float. """ + # XXX since this method is expensive, consider caching the result if self.denominator == 1: # Get integers right. return hash(self.numerator) @@ -481,3 +490,18 @@ class Rational(RationalAbc): def __bool__(a): """a != 0""" return a.numerator != 0 + + # support for pickling, copy, and deepcopy + + def __reduce__(self): + return (self.__class__, (str(self),)) + + def __copy__(self): + if type(self) == Rational: + return self # I'm immutable; therefore I am my own clone + return self.__class__(self.numerator, self.denominator) + + def __deepcopy__(self, memo): + if type(self) == Rational: + return self # My components are also immutable + return self.__class__(self.numerator, self.denominator) diff --git a/Lib/test/crashers/loosing_dict_ref.py b/Lib/test/crashers/loosing_dict_ref.py deleted file mode 100644 index f44370b..0000000 --- a/Lib/test/crashers/loosing_dict_ref.py +++ /dev/null @@ -1,21 +0,0 @@ - -# http://python.org/sf/1303614 - -class Strange(object): - def __hash__(self): - return hash('hello') - - def __eq__(self, other): - x.__dict__ = {} # the old x.__dict__ is deallocated - return False - - -class X(object): - pass - -if __name__ == '__main__': - v = 123 - x = X() - x.__dict__ = {Strange(): 42, - 'hello': v+456} - x.hello # segfault: the above dict is accessed after it's deallocated diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index 83fb337..1d3765a 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -1141,6 +1141,7 @@ class DecimalUsabilityTest(unittest.TestCase): checkSameDec("__floordiv__", True) checkSameDec("__hash__") checkSameDec("__int__") + checkSameDec("__trunc__") checkSameDec("__mod__", True) checkSameDec("__mul__", True) checkSameDec("__neg__") @@ -1204,6 +1205,16 @@ class DecimalPythonAPItests(unittest.TestCase): r = d.to_integral(ROUND_DOWN) self.assertEqual(Decimal(int(d)), r) + def test_trunc(self): + for x in range(-250, 250): + s = '%0.2f' % (x / 100.0) + # should work the same as for floats + self.assertEqual(int(Decimal(s)), int(float(s))) + # should work the same as to_integral in the ROUND_DOWN mode + d = Decimal(s) + r = d.to_integral(ROUND_DOWN) + self.assertEqual(Decimal(trunc(d)), r) + class ContextAPItests(unittest.TestCase): def test_pickle(self): diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 028be3d..8670ff9 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -4248,6 +4248,29 @@ def test_borrowed_ref_4_segfault(): finally: builtins.__import__ = orig_import +def test_losing_dict_ref_segfault(): + # This used to segfault; + # derived from issue #1303614, test67.py + if verbose: + print("Testing losing dict ref segfault...") + + class Strange(object): + def __hash__(self): + return hash('hello') + + def __eq__(self, other): + x.__dict__ = {} # the old x.__dict__ is deallocated + return False + + class X(object): + pass + + v = 123 + x = X() + x.__dict__ = {Strange(): 42, 'hello': v+456} + x.hello + + def test_main(): weakref_segfault() # Must be first, somehow wrapper_segfault() # NB This one is slow @@ -4348,6 +4371,7 @@ def test_main(): test_weakref_in_del_segfault() test_borrowed_ref_3_segfault() test_borrowed_ref_4_segfault() + test_losing_dict_ref_segfault() if verbose: print("All OK") diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index 0777307..04aedd5 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -500,6 +500,15 @@ class GrammarTests(unittest.TestCase): while 0: pass else: pass + # Issue1920: "while 0" is optimized away, + # ensure that the "else" clause is still present. + x = 0 + while 0: + x = 1 + else: + x = 2 + self.assertEquals(x, 2) + def testFor(self): # 'for' exprlist 'in' exprlist ':' suite ['else' ':' suite] for i in 1, 2, 3: pass diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py index 8698907..8d75d58 100644 --- a/Lib/test/test_pprint.py +++ b/Lib/test/test_pprint.py @@ -1,6 +1,7 @@ import pprint import test.test_support import unittest +import test.test_set # list, tuple and dict subclasses that do or don't overwrite __repr__ class list2(list): @@ -189,6 +190,197 @@ class QueryTestCase(unittest.TestCase): others.should.not.be: like.this}""" self.assertEqual(DottedPrettyPrinter().pformat(o), exp) + def test_set_reprs(self): + self.assertEqual(pprint.pformat(set()), 'set()') + self.assertEqual(pprint.pformat(set(range(3))), '{0, 1, 2}') + self.assertEqual(pprint.pformat(frozenset()), 'frozenset()') + self.assertEqual(pprint.pformat(frozenset(range(3))), 'frozenset({0, 1, 2})') + cube_repr_tgt = """\ +{frozenset(): frozenset({frozenset({2}), frozenset({0}), frozenset({1})}), + frozenset({0}): frozenset([frozenset(), + frozenset({0, 2}), + frozenset({0, 1})]), + frozenset({1}): frozenset([frozenset(), + frozenset({1, 2}), + frozenset({0, 1})]), + frozenset({2}): frozenset([frozenset(), + frozenset({1, 2}), + frozenset({0, 2})]), + frozenset({1, 2}): frozenset([frozenset({2}), + frozenset({1}), + frozenset({0, 1, 2})]), + frozenset({0, 2}): frozenset([frozenset({2}), + frozenset({0}), + frozenset({0, 1, 2})]), + frozenset({0, 1}): frozenset([frozenset({0}), + frozenset({1}), + frozenset({0, 1, 2})]), + frozenset({0, 1, 2}): frozenset([frozenset({1, 2}), + frozenset({0, 2}), + frozenset({0, 1})])}""" + cube = test.test_set.cube(3) + self.assertEqual(pprint.pformat(cube), cube_repr_tgt) + cubo_repr_tgt = """\ +{frozenset({frozenset({0, 2}), frozenset({0})}): frozenset([frozenset([frozenset([0, + 2]), + frozenset([0, + 1, + 2])]), + frozenset([frozenset([0]), + frozenset([0, + 1])]), + frozenset([frozenset(), + frozenset([0])]), + frozenset([frozenset([2]), + frozenset([0, + 2])])]), + frozenset({frozenset({0, 1}), frozenset({1})}): frozenset([frozenset([frozenset([0, + 1]), + frozenset([0, + 1, + 2])]), + frozenset([frozenset([0]), + frozenset([0, + 1])]), + frozenset([frozenset([1]), + frozenset([1, + 2])]), + frozenset([frozenset(), + frozenset([1])])]), + frozenset({frozenset({1, 2}), frozenset({1})}): frozenset([frozenset([frozenset([1, + 2]), + frozenset([0, + 1, + 2])]), + frozenset([frozenset([2]), + frozenset([1, + 2])]), + frozenset([frozenset(), + frozenset([1])]), + frozenset([frozenset([1]), + frozenset([0, + 1])])]), + frozenset({frozenset({1, 2}), frozenset({2})}): frozenset([frozenset([frozenset([1, + 2]), + frozenset([0, + 1, + 2])]), + frozenset([frozenset([1]), + frozenset([1, + 2])]), + frozenset([frozenset([2]), + frozenset([0, + 2])]), + frozenset([frozenset(), + frozenset([2])])]), + frozenset({frozenset(), frozenset({0})}): frozenset([frozenset([frozenset([0]), + frozenset([0, + 1])]), + frozenset([frozenset([0]), + frozenset([0, + 2])]), + frozenset([frozenset(), + frozenset([1])]), + frozenset([frozenset(), + frozenset([2])])]), + frozenset({frozenset(), frozenset({1})}): frozenset([frozenset([frozenset(), + frozenset([0])]), + frozenset([frozenset([1]), + frozenset([1, + 2])]), + frozenset([frozenset(), + frozenset([2])]), + frozenset([frozenset([1]), + frozenset([0, + 1])])]), + frozenset({frozenset({2}), frozenset()}): frozenset([frozenset([frozenset([2]), + frozenset([1, + 2])]), + frozenset([frozenset(), + frozenset([0])]), + frozenset([frozenset(), + frozenset([1])]), + frozenset([frozenset([2]), + frozenset([0, + 2])])]), + frozenset({frozenset({0, 1, 2}), frozenset({0, 1})}): frozenset([frozenset([frozenset([1, + 2]), + frozenset([0, + 1, + 2])]), + frozenset([frozenset([0, + 2]), + frozenset([0, + 1, + 2])]), + frozenset([frozenset([0]), + frozenset([0, + 1])]), + frozenset([frozenset([1]), + frozenset([0, + 1])])]), + frozenset({frozenset({0}), frozenset({0, 1})}): frozenset([frozenset([frozenset(), + frozenset([0])]), + frozenset([frozenset([0, + 1]), + frozenset([0, + 1, + 2])]), + frozenset([frozenset([0]), + frozenset([0, + 2])]), + frozenset([frozenset([1]), + frozenset([0, + 1])])]), + frozenset({frozenset({2}), frozenset({0, 2})}): frozenset([frozenset([frozenset([0, + 2]), + frozenset([0, + 1, + 2])]), + frozenset([frozenset([2]), + frozenset([1, + 2])]), + frozenset([frozenset([0]), + frozenset([0, + 2])]), + frozenset([frozenset(), + frozenset([2])])]), + frozenset({frozenset({0, 1, 2}), frozenset({0, 2})}): frozenset([frozenset([frozenset([1, + 2]), + frozenset([0, + 1, + 2])]), + frozenset([frozenset([0, + 1]), + frozenset([0, + 1, + 2])]), + frozenset([frozenset([0]), + frozenset([0, + 2])]), + frozenset([frozenset([2]), + frozenset([0, + 2])])]), + frozenset({frozenset({1, 2}), frozenset({0, 1, 2})}): frozenset([frozenset([frozenset([0, + 2]), + frozenset([0, + 1, + 2])]), + frozenset([frozenset([0, + 1]), + frozenset([0, + 1, + 2])]), + frozenset([frozenset([2]), + frozenset([1, + 2])]), + frozenset([frozenset([1]), + frozenset([1, + 2])])])}""" + + cubo = test.test_set.linegraph(cube) + self.assertEqual(pprint.pformat(cubo), cubo_repr_tgt) + class DottedPrettyPrinter(pprint.PrettyPrinter): diff --git a/Lib/test/test_rational.py b/Lib/test/test_rational.py index 1bd1814..49868dd 100644 --- a/Lib/test/test_rational.py +++ b/Lib/test/test_rational.py @@ -6,6 +6,8 @@ import math import operator import rational import unittest +from copy import copy, deepcopy +from cPickle import dumps, loads R = rational.Rational def _components(r): @@ -153,16 +155,17 @@ class RationalTest(unittest.TestCase): [-4, 1, 6, 15, 1, 292, 1, 1, 1, 2, 1, 3, 1, 14, 3, 3]) self.assertEqual(R(0).as_continued_fraction(), [0]) - def testApproximateFromFloat(self): - self.assertEqual(R.approximate_from_float(math.pi, 10000), R(355, 113)) - self.assertEqual(R.approximate_from_float(-math.pi, 10000), R(-355, 113)) - self.assertEqual(R.approximate_from_float(0.0, 10000), R(0)) + def testApproximateFrom(self): + self.assertEqual(R.from_float(math.pi).approximate(10000), R(355, 113)) + self.assertEqual(R.from_float(-math.pi).approximate(10000), R(-355, 113)) + self.assertEqual(R.from_float(0.0).approximate(10000), R(0)) def testConversions(self): self.assertTypedEquals(-1, trunc(R(-11, 10))) self.assertTypedEquals(-2, math.floor(R(-11, 10))) self.assertTypedEquals(-1, math.ceil(R(-11, 10))) self.assertTypedEquals(-1, math.ceil(R(-10, 10))) + self.assertTypedEquals(-1, int(R(-11, 10))) self.assertTypedEquals(0, round(R(-1, 10))) self.assertTypedEquals(0, round(R(-5, 10))) @@ -360,6 +363,12 @@ class RationalTest(unittest.TestCase): s += num / fact * sign self.assertAlmostEquals(math.cos(1), s) + def test_copy_deepcopy_pickle(self): + r = R(13, 7) + self.assertEqual(r, loads(dumps(r))) + self.assertEqual(id(r), id(copy(r))) + self.assertEqual(id(r), id(deepcopy(r))) + def test_main(): run_unittest(RationalTest) diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py index e761f19..9e3d64f 100644 --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -8,6 +8,7 @@ import os from random import randrange, shuffle import sys import warnings +import collections class PassThru(Exception): pass @@ -1605,6 +1606,110 @@ class TestVariousIteratorArgs(unittest.TestCase): self.assertRaises(TypeError, getattr(set('january'), methname), N(data)) self.assertRaises(ZeroDivisionError, getattr(set('january'), methname), E(data)) +# Application tests (based on David Eppstein's graph recipes ==================================== + +def powerset(U): + """Generates all subsets of a set or sequence U.""" + U = iter(U) + try: + x = frozenset([next(U)]) + for S in powerset(U): + yield S + yield S | x + except StopIteration: + yield frozenset() + +def cube(n): + """Graph of n-dimensional hypercube.""" + singletons = [frozenset([x]) for x in range(n)] + return dict([(x, frozenset([x^s for s in singletons])) + for x in powerset(range(n))]) + +def linegraph(G): + """Graph, the vertices of which are edges of G, + with two vertices being adjacent iff the corresponding + edges share a vertex.""" + L = {} + for x in G: + for y in G[x]: + nx = [frozenset([x,z]) for z in G[x] if z != y] + ny = [frozenset([y,z]) for z in G[y] if z != x] + L[frozenset([x,y])] = frozenset(nx+ny) + return L + +def faces(G): + 'Return a set of faces in G. Where a face is a set of vertices on that face' + # currently limited to triangles,squares, and pentagons + f = set() + for v1, edges in G.items(): + for v2 in edges: + for v3 in G[v2]: + if v1 == v3: + continue + if v1 in G[v3]: + f.add(frozenset([v1, v2, v3])) + else: + for v4 in G[v3]: + if v4 == v2: + continue + if v1 in G[v4]: + f.add(frozenset([v1, v2, v3, v4])) + else: + for v5 in G[v4]: + if v5 == v3 or v5 == v2: + continue + if v1 in G[v5]: + f.add(frozenset([v1, v2, v3, v4, v5])) + return f + + +class TestGraphs(unittest.TestCase): + + def test_cube(self): + + g = cube(3) # vert --> {v1, v2, v3} + vertices1 = set(g) + self.assertEqual(len(vertices1), 8) # eight vertices + for edge in g.values(): + self.assertEqual(len(edge), 3) # each vertex connects to three edges + vertices2 = set(v for edges in g.values() for v in edges) + self.assertEqual(vertices1, vertices2) # edge vertices in original set + + cubefaces = faces(g) + self.assertEqual(len(cubefaces), 6) # six faces + for face in cubefaces: + self.assertEqual(len(face), 4) # each face is a square + + def test_cuboctahedron(self): + + # http://en.wikipedia.org/wiki/Cuboctahedron + # 8 triangular faces and 6 square faces + # 12 indentical vertices each connecting a triangle and square + + g = cube(3) + cuboctahedron = linegraph(g) # V( --> {V1, V2, V3, V4} + self.assertEqual(len(cuboctahedron), 12)# twelve vertices + + vertices = set(cuboctahedron) + for edges in cuboctahedron.values(): + self.assertEqual(len(edges), 4) # each vertex connects to four other vertices + othervertices = set(edge for edges in cuboctahedron.values() for edge in edges) + self.assertEqual(vertices, othervertices) # edge vertices in original set + + cubofaces = faces(cuboctahedron) + facesizes = collections.defaultdict(int) + for face in cubofaces: + facesizes[len(face)] += 1 + self.assertEqual(facesizes[3], 8) # eight triangular faces + self.assertEqual(facesizes[4], 6) # six square faces + + for vertex in cuboctahedron: + edge = vertex # Cuboctahedron vertices are edges in Cube + self.assertEqual(len(edge), 2) # Two cube vertices define an edge + for cubevert in edge: + self.assert_(cubevert in g) + + #============================================================================== def test_main(verbose=None): @@ -1644,6 +1749,7 @@ def test_main(verbose=None): TestCopyingNested, TestIdentities, TestVariousIteratorArgs, + TestGraphs, ) test_support.run_unittest(*test_classes) diff --git a/Lib/test/test_urllib2net.py b/Lib/test/test_urllib2net.py index fae7e4d..109dd1b 100644 --- a/Lib/test/test_urllib2net.py +++ b/Lib/test/test_urllib2net.py @@ -10,6 +10,20 @@ import sys import os import mimetools + +def _urlopen_with_retry(host, *args, **kwargs): + # Connecting to remote hosts is flaky. Make it more robust + # by retrying the connection several times. + for i in range(3): + try: + return urllib2.urlopen(host, *args, **kwargs) + except urllib2.URLError as last_exc: + continue + except: + raise + raise last_exc + + class URLTimeoutTest(unittest.TestCase): TIMEOUT = 10.0 @@ -21,7 +35,7 @@ class URLTimeoutTest(unittest.TestCase): socket.setdefaulttimeout(None) def testURLread(self): - f = urllib2.urlopen("http://www.python.org/") + f = _urlopen_with_retry("http://www.python.org/") x = f.read() @@ -42,7 +56,7 @@ class AuthTests(unittest.TestCase): # # # failure # try: -# urllib2.urlopen(test_url) +# _urlopen_with_retry(test_url) # except urllib2.HTTPError, exc: # self.assertEqual(exc.code, 401) # else: @@ -54,7 +68,7 @@ class AuthTests(unittest.TestCase): # test_user, test_password) # opener = urllib2.build_opener(auth_handler) # f = opener.open('http://localhost/') -# response = urllib2.urlopen("http://www.python.org/") +# response = _urlopen_with_retry("http://www.python.org/") # # # The 'userinfo' URL component is deprecated by RFC 3986 for security # # reasons, let's not implement it! (it's already implemented for proxy @@ -73,7 +87,7 @@ class CloseSocketTest(unittest.TestCase): # underlying socket # delve deep into response to fetch socket._socketobject - response = urllib2.urlopen("http://www.python.org/") + response = _urlopen_with_retry("http://www.python.org/") abused_fileobject = response.fp httpresponse = abused_fileobject.raw self.assert_(httpresponse.__class__ is httplib.HTTPResponse) @@ -100,7 +114,7 @@ class urlopenNetworkTests(unittest.TestCase): def test_basic(self): # Simple test expected to pass. - open_url = urllib2.urlopen("http://www.python.org/") + open_url = _urlopen_with_retry("http://www.python.org/") for attr in ("read", "close", "info", "geturl"): self.assert_(hasattr(open_url, attr), "object returned from " "urlopen lacks the %s attribute" % attr) @@ -111,7 +125,7 @@ class urlopenNetworkTests(unittest.TestCase): def test_info(self): # Test 'info'. - open_url = urllib2.urlopen("http://www.python.org/") + open_url = _urlopen_with_retry("http://www.python.org/") try: info_obj = open_url.info() finally: @@ -124,7 +138,7 @@ class urlopenNetworkTests(unittest.TestCase): def test_geturl(self): # Make sure same URL as opened is returned by geturl. URL = "http://www.python.org/" - open_url = urllib2.urlopen(URL) + open_url = _urlopen_with_retry(URL) try: gotten_url = open_url.geturl() finally: @@ -155,7 +169,7 @@ class OtherNetworkTests(unittest.TestCase): def test_range (self): req = urllib2.Request("http://www.python.org", headers={'Range': 'bytes=20-39'}) - result = urllib2.urlopen(req) + result = _urlopen_with_retry(req) data = result.read() self.assertEqual(len(data), 20) @@ -182,7 +196,7 @@ class OtherNetworkTests(unittest.TestCase): 'file:'+sanepathname2url(os.path.abspath(TESTFN)), ('file:///nonsensename/etc/passwd', None, urllib2.URLError), ] - self._test_urls(urls, self._extra_handlers()) + self._test_urls(urls, self._extra_handlers(), urllib2.urlopen) finally: os.remove(TESTFN) @@ -224,7 +238,7 @@ class OtherNetworkTests(unittest.TestCase): ## self._test_urls(urls, self._extra_handlers()+[bauth, dauth]) - def _test_urls(self, urls, handlers): + def _test_urls(self, urls, handlers, urlopen=_urlopen_with_retry): import socket import time import logging @@ -239,7 +253,7 @@ class OtherNetworkTests(unittest.TestCase): req = expected_err = None debug(url) try: - f = urllib2.urlopen(url, req) + f = urlopen(url, req) except EnvironmentError as err: debug(err) if expected_err: @@ -265,47 +279,47 @@ class OtherNetworkTests(unittest.TestCase): class TimeoutTest(unittest.TestCase): def test_http_basic(self): - u = urllib2.urlopen("http://www.python.org") + u = _urlopen_with_retry("http://www.python.org") self.assertTrue(u.fp.raw.fp._sock.gettimeout() is None) def test_http_NoneWithdefault(self): prev = socket.getdefaulttimeout() socket.setdefaulttimeout(60) try: - u = urllib2.urlopen("http://www.python.org", timeout=None) + u = _urlopen_with_retry("http://www.python.org", timeout=None) self.assertTrue(u.fp.raw.fp._sock.gettimeout(), 60) finally: socket.setdefaulttimeout(prev) def test_http_Value(self): - u = urllib2.urlopen("http://www.python.org", timeout=120) + u = _urlopen_with_retry("http://www.python.org", timeout=120) self.assertEqual(u.fp.raw.fp._sock.gettimeout(), 120) def test_http_NoneNodefault(self): - u = urllib2.urlopen("http://www.python.org", timeout=None) + u = _urlopen_with_retry("http://www.python.org", timeout=None) self.assertTrue(u.fp.raw.fp._sock.gettimeout() is None) + FTP_HOST = "ftp://ftp.mirror.nl/pub/mirror/gnu/" + def test_ftp_basic(self): - u = urllib2.urlopen("ftp://ftp.mirror.nl/pub/mirror/gnu/") + u = _urlopen_with_retry(self.FTP_HOST) self.assertTrue(u.fp.fp.raw._sock.gettimeout() is None) def test_ftp_NoneWithdefault(self): prev = socket.getdefaulttimeout() socket.setdefaulttimeout(60) try: - u = urllib2.urlopen("ftp://ftp.mirror.nl/pub/mirror/gnu/", - timeout=None) - self.assertEqual(u.fp.fp.raw._sock.gettimeout(), 60) + u = _urlopen_with_retry(self.FTP_HOST, timeout=None) + self.assertEqual(u.fp.fp._sock.gettimeout(), 60) finally: socket.setdefaulttimeout(prev) def test_ftp_NoneNodefault(self): - u = urllib2.urlopen("ftp://ftp.mirror.nl/pub/mirror/gnu/", - timeout=None) - self.assertTrue(u.fp.fp.raw._sock.gettimeout() is None) + u = _urlopen_with_retry(self.FTP_HOST, timeout=None) + self.assertTrue(u.fp.fp._sock.gettimeout() is None) def test_ftp_Value(self): - u = urllib2.urlopen("ftp://ftp.mirror.nl/pub/mirror/gnu/", timeout=60) + u = _urlopen_with_retry(self.FTP_HOST, timeout=60) self.assertEqual(u.fp.fp.raw._sock.gettimeout(), 60) diff --git a/Lib/threading.py b/Lib/threading.py index 9bd112d..a20ff06 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -346,18 +346,27 @@ class _Event(_Verbose): return self._flag def set(self): - with self._cond: + self._cond.acquire() + try: self._flag = True self._cond.notifyAll() + finally: + self._cond.release() def clear(self): - with self._cond: + self._cond.acquire() + try: self._flag = False + finally: + self._cond.release() def wait(self, timeout=None): - with self._cond: + self._cond.acquire() + try: if not self._flag: self._cond.wait(timeout) + finally: + self._cond.release() # Helper to generate new thread names _counter = 0 @@ -524,9 +533,10 @@ class Thread(_Verbose): pass def _stop(self): - with self._block: - self._stopped = True - self._block.notifyAll() + self._block.acquire() + self._stopped = True + self._block.notifyAll() + self._block.release() def _delete(self): "Remove current thread from the dict of currently running threads." @@ -552,12 +562,15 @@ class Thread(_Verbose): # since it isn't if dummy_threading is *not* being used then don't # hide the exception. - with _active_limbo_lock: + _active_limbo_lock.acquire() + try: try: del _active[_get_ident()] except KeyError: if 'dummy_threading' not in _sys.modules: raise + finally: + _active_limbo_lock.release() def join(self, timeout=None): if not self._initialized: @@ -570,7 +583,9 @@ class Thread(_Verbose): if __debug__: if not self._stopped: self._note("%s.join(): waiting until thread stops", self) - with self._block: + + self._block.acquire() + try: if timeout is None: while not self._stopped: self._block.wait() @@ -588,6 +603,8 @@ class Thread(_Verbose): else: if __debug__: self._note("%s.join(): thread stopped", self) + finally: + self._block.release() def getName(self): assert self._initialized, "Thread.__init__() not called" |