summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Lib/ctypes/test/test_numbers.py18
-rw-r--r--Lib/decimal.py2
-rw-r--r--Lib/pprint.py10
-rwxr-xr-xLib/rational.py72
-rw-r--r--Lib/test/crashers/loosing_dict_ref.py21
-rw-r--r--Lib/test/test_decimal.py11
-rw-r--r--Lib/test/test_descr.py24
-rw-r--r--Lib/test/test_grammar.py9
-rw-r--r--Lib/test/test_pprint.py192
-rw-r--r--Lib/test/test_rational.py17
-rw-r--r--Lib/test/test_set.py106
-rw-r--r--Lib/test/test_urllib2net.py60
-rw-r--r--Lib/threading.py33
-rw-r--r--Modules/_ctypes/_ctypes.c166
-rw-r--r--Modules/_ctypes/cfield.c36
-rw-r--r--Modules/mathmodule.c5
-rw-r--r--Objects/floatobject.c5
-rw-r--r--Objects/object.c5
-rw-r--r--Python/compile.c5
19 files changed, 675 insertions, 122 deletions
diff --git a/Lib/ctypes/test/test_numbers.py b/Lib/ctypes/test/test_numbers.py
index 680d29d5e4..16e69cb24c 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 51758f269e..1652914e6f 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 09b4a9f41e..b903391db2 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 4a56cf20fa..89b622c285 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 f44370b9a5..0000000000
--- 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 83fb337268..1d3765ab05 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 028be3dbc1..8670ff9ccf 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 0777307b9f..04aedd536e 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 869890714d..8d75d58fe6 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 1bd18142c6..49868dd2cc 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 e761f19c1e..9e3d64ff23 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 fae7e4d2bf..109dd1b9c5 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 9bd112d775..a20ff0636a 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"
diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c
index c6098b9b56..49f55c85d9 100644
--- a/Modules/_ctypes/_ctypes.c
+++ b/Modules/_ctypes/_ctypes.c
@@ -122,12 +122,134 @@ bytes(cdata)
PyObject *PyExc_ArgError;
static PyTypeObject Simple_Type;
-PyObject *array_types_cache;
char *conversion_mode_encoding = NULL;
char *conversion_mode_errors = NULL;
+/****************************************************************/
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *key;
+ PyObject *dict;
+} DictRemoverObject;
+
+static void
+_DictRemover_dealloc(PyObject *_self)
+{
+ DictRemoverObject *self = (DictRemoverObject *)_self;
+ Py_XDECREF(self->key);
+ Py_XDECREF(self->dict);
+ Py_TYPE(self)->tp_free(_self);
+}
+
+static PyObject *
+_DictRemover_call(PyObject *_self, PyObject *args, PyObject *kw)
+{
+ DictRemoverObject *self = (DictRemoverObject *)_self;
+ if (self->key && self->dict) {
+ if (-1 == PyDict_DelItem(self->dict, self->key))
+ /* XXX Error context */
+ PyErr_WriteUnraisable(Py_None);
+ Py_DECREF(self->key);
+ self->key = NULL;
+ Py_DECREF(self->dict);
+ self->dict = NULL;
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyTypeObject DictRemover_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "_ctypes.DictRemover", /* tp_name */
+ sizeof(DictRemoverObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ _DictRemover_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ _DictRemover_call, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+/* XXX should participate in GC? */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ "deletes a key from a dictionary", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyType_GenericNew, /* tp_new */
+ 0, /* tp_free */
+};
+
+int
+PyDict_SetItemProxy(PyObject *dict, PyObject *key, PyObject *item)
+{
+ PyObject *obj;
+ DictRemoverObject *remover;
+ PyObject *proxy;
+ int result;
+
+ obj = PyObject_CallObject((PyObject *)&DictRemover_Type, NULL);
+ if (obj == NULL)
+ return -1;
+
+ remover = (DictRemoverObject *)obj;
+ assert(remover->key == NULL);
+ assert(remover->dict == NULL);
+ Py_INCREF(key);
+ remover->key = key;
+ Py_INCREF(dict);
+ remover->dict = dict;
+
+ proxy = PyWeakref_NewProxy(item, obj);
+ Py_DECREF(obj);
+ if (proxy == NULL)
+ return -1;
+
+ result = PyDict_SetItem(dict, key, proxy);
+ Py_DECREF(proxy);
+ return result;
+}
+
+PyObject *
+PyDict_GetItemProxy(PyObject *dict, PyObject *key)
+{
+ PyObject *result;
+ PyObject *item = PyDict_GetItem(dict, key);
+
+ if (item == NULL)
+ return NULL;
+ if (!PyWeakref_CheckProxy(item))
+ return item;
+ result = PyWeakref_GET_OBJECT(item);
+ if (result == Py_None)
+ return NULL;
+ return result;
+}
+
/******************************************************************/
/*
StructType_Type - a meta type/class. Creating a new class using this one as
@@ -2381,7 +2503,7 @@ _CData_set(CDataObject *dst, PyObject *type, SETFUNC setfunc, PyObject *value,
only it's object list. So we create a tuple, containing
b_objects list PLUS the array itself, and return that!
*/
- return Py_BuildValue("(OO)", keep, value);
+ return PyTuple_Pack(2, keep, value);
}
PyErr_Format(PyExc_TypeError,
"incompatible types, %s instance instead of %s instance",
@@ -3993,19 +4115,30 @@ PyTypeObject Array_Type = {
PyObject *
CreateArrayType(PyObject *itemtype, Py_ssize_t length)
{
+ static PyObject *cache;
PyObject *key;
PyObject *result;
char name[256];
+ PyObject *len;
- key = Py_BuildValue("(On)", itemtype, length);
+ if (cache == NULL) {
+ cache = PyDict_New();
+ if (cache == NULL)
+ return NULL;
+ }
+ len = PyLong_FromSsize_t(length);
+ if (len == NULL)
+ return NULL;
+ key = PyTuple_Pack(2, itemtype, len);
+ Py_DECREF(len);
if (!key)
return NULL;
- result = PyObject_GetItem(array_types_cache, key);
+ result = PyDict_GetItemProxy(cache, key);
if (result) {
+ Py_INCREF(result);
Py_DECREF(key);
return result;
- } else
- PyErr_Clear();
+ }
if (!PyType_Check(itemtype)) {
PyErr_SetString(PyExc_TypeError,
@@ -4029,9 +4162,11 @@ CreateArrayType(PyObject *itemtype, Py_ssize_t length)
"_type_",
itemtype
);
- if (!result)
+ if (result == NULL) {
+ Py_DECREF(key);
return NULL;
- if (-1 == PyObject_SetItem(array_types_cache, key, result)) {
+ }
+ if (-1 == PyDict_SetItemProxy(cache, key, result)) {
Py_DECREF(key);
Py_DECREF(result);
return NULL;
@@ -4792,7 +4927,6 @@ PyMODINIT_FUNC
init_ctypes(void)
{
PyObject *m;
- PyObject *weakref;
/* Note:
ob_type is the metatype (the 'type'), defaults to PyType_Type,
@@ -4805,16 +4939,6 @@ init_ctypes(void)
if (!m)
return;
- weakref = PyImport_ImportModule("weakref");
- if (weakref == NULL)
- return;
- array_types_cache = PyObject_CallMethod(weakref,
- "WeakValueDictionary",
- NULL);
- if (array_types_cache == NULL)
- return;
- Py_DECREF(weakref);
-
if (PyType_Ready(&PyCArg_Type) < 0)
return;
@@ -4910,6 +5034,10 @@ init_ctypes(void)
* Other stuff
*/
+ DictRemover_Type.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&DictRemover_Type) < 0)
+ return;
+
#ifdef MS_WIN32
if (create_comerror() < 0)
return;
diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c
index f4056bf5bd..f7c78f3546 100644
--- a/Modules/_ctypes/cfield.c
+++ b/Modules/_ctypes/cfield.c
@@ -334,10 +334,10 @@ static int
get_long(PyObject *v, long *p)
{
long x;
- if (!PyLong_Check(v)) {
- PyErr_Format(PyExc_TypeError,
- "int expected instead of %s instance",
- v->ob_type->tp_name);
+
+ if (PyFloat_Check(v)) {
+ PyErr_SetString(PyExc_TypeError,
+ "int expected instead of float");
return -1;
}
x = PyLong_AsUnsignedLongMask(v);
@@ -353,10 +353,10 @@ static int
get_ulong(PyObject *v, unsigned long *p)
{
unsigned long x;
- if (!PyLong_Check(v)) {
- PyErr_Format(PyExc_TypeError,
- "int expected instead of %s instance",
- v->ob_type->tp_name);
+
+ if (PyFloat_Check(v)) {
+ PyErr_SetString(PyExc_TypeError,
+ "int expected instead of float");
return -1;
}
x = PyLong_AsUnsignedLongMask(v);
@@ -374,11 +374,10 @@ static int
get_longlong(PyObject *v, PY_LONG_LONG *p)
{
PY_LONG_LONG x;
- if (!PyLong_Check(v)) {
- PyErr_Format(PyExc_TypeError,
- "int expected instead of %s instance",
- v->ob_type->tp_name);
- return -1;
+ if (PyFloat_Check(v)) {
+ PyErr_SetString(PyExc_TypeError,
+ "int expected instead of float");
+ return -1;
}
x = PyLong_AsUnsignedLongLongMask(v);
if (x == -1 && PyErr_Occurred())
@@ -393,12 +392,11 @@ static int
get_ulonglong(PyObject *v, unsigned PY_LONG_LONG *p)
{
unsigned PY_LONG_LONG x;
- if (!PyLong_Check(v)) {
- PyErr_Format(PyExc_TypeError,
- "int expected instead of %s instance",
- v->ob_type->tp_name);
- return -1;
- }
+ if (PyFloat_Check(v)) {
+ PyErr_SetString(PyExc_TypeError,
+ "int expected instead of float");
+ return -1;
+ }
x = PyLong_AsUnsignedLongLongMask(v);
if (x == -1 && PyErr_Occurred())
return -1;
diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c
index b773f706e6..473068015b 100644
--- a/Modules/mathmodule.c
+++ b/Modules/mathmodule.c
@@ -12,6 +12,11 @@ extern double modf (double, double *);
#endif /* __STDC__ */
#endif /* _MSC_VER */
+#ifdef _OSF_SOURCE
+/* OSF1 5.1 doesn't make this available with XOPEN_SOURCE_EXTENDED defined */
+extern double copysign(double, double);
+#endif
+
/* Call is_error when errno != 0, and where x is the result libm
* returned. is_error will usually set up an exception and return
* true (1), but may return false (0) without setting up an exception.
diff --git a/Objects/floatobject.c b/Objects/floatobject.c
index a832c5d05e..859abf00a0 100644
--- a/Objects/floatobject.c
+++ b/Objects/floatobject.c
@@ -17,6 +17,11 @@ extern double fmod(double, double);
extern double pow(double, double);
#endif
+#ifdef _OSF_SOURCE
+/* OSF1 5.1 doesn't make this available with XOPEN_SOURCE_EXTENDED defined */
+extern int finite(double);
+#endif
+
/* Special free list -- see comments for same code in intobject.c. */
#define BLOCK_SIZE 1000 /* 1K less typical malloc overhead */
#define BHEAD_SIZE 8 /* Enough for a 64-bit pointer */
diff --git a/Objects/object.c b/Objects/object.c
index 587e806337..0d317b2388 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -1004,12 +1004,15 @@ PyObject_GenericGetAttr(PyObject *obj, PyObject *name)
dictptr = (PyObject **) ((char *)obj + dictoffset);
dict = *dictptr;
if (dict != NULL) {
+ Py_INCREF(dict);
res = PyDict_GetItem(dict, name);
if (res != NULL) {
Py_INCREF(res);
Py_XDECREF(descr);
+ Py_DECREF(dict);
goto done;
}
+ Py_DECREF(dict);
}
}
@@ -1076,12 +1079,14 @@ PyObject_GenericSetAttr(PyObject *obj, PyObject *name, PyObject *value)
*dictptr = dict;
}
if (dict != NULL) {
+ Py_INCREF(dict);
if (value == NULL)
res = PyDict_DelItem(dict, name);
else
res = PyDict_SetItem(dict, name, value);
if (res < 0 && PyErr_ExceptionMatches(PyExc_KeyError))
PyErr_SetObject(PyExc_AttributeError, name);
+ Py_DECREF(dict);
goto done;
}
}
diff --git a/Python/compile.c b/Python/compile.c
index e1f2a5581c..347a19281e 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -1737,8 +1737,11 @@ compiler_while(struct compiler *c, stmt_ty s)
basicblock *loop, *orelse, *end, *anchor = NULL;
int constant = expr_constant(s->v.While.test);
- if (constant == 0)
+ if (constant == 0) {
+ if (s->v.While.orelse)
+ VISIT_SEQ(c, stmt, s->v.While.orelse);
return 1;
+ }
loop = compiler_new_block(c);
end = compiler_new_block(c);
if (constant == -1) {