Merged revisions 60441-60474 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk

........
  r60441 | christian.heimes | 2008-01-30 12:46:00 +0100 (Wed, 30 Jan 2008) | 1 line

  Removed unused var
........
  r60448 | christian.heimes | 2008-01-30 18:21:22 +0100 (Wed, 30 Jan 2008) | 1 line

  Fixed some references leaks in sys.
........
  r60450 | christian.heimes | 2008-01-30 19:58:29 +0100 (Wed, 30 Jan 2008) | 1 line

  The previous change was causing a segfault after multiple calls to Py_Initialize() and Py_Finalize().
........
  r60463 | raymond.hettinger | 2008-01-30 23:17:31 +0100 (Wed, 30 Jan 2008) | 1 line

  Update itertool recipes
........
  r60464 | christian.heimes | 2008-01-30 23:54:18 +0100 (Wed, 30 Jan 2008) | 1 line

  Bug #1234: Fixed semaphore errors on AIX 5.2
........
  r60469 | raymond.hettinger | 2008-01-31 02:38:15 +0100 (Thu, 31 Jan 2008) | 6 lines

  Fix defect in __ixor__ which would get the wrong
  answer if the input iterable had a duplicate element
  (two calls to toggle() reverse each other).  Borrow
  the correct code from sets.py.
........
  r60470 | raymond.hettinger | 2008-01-31 02:42:11 +0100 (Thu, 31 Jan 2008) | 1 line

  Missing return
........
  r60471 | jeffrey.yasskin | 2008-01-31 08:44:11 +0100 (Thu, 31 Jan 2008) | 4 lines

  Added more documentation on how mixed-mode arithmetic should be implemented. I
  also noticed and fixed a bug in Rational's forward operators (they were
  claiming all instances of numbers.Rational instead of just the concrete types).
........
diff --git a/Lib/_abcoll.py b/Lib/_abcoll.py
index 4ce3df4..005f437 100644
--- a/Lib/_abcoll.py
+++ b/Lib/_abcoll.py
@@ -300,16 +300,6 @@
         self.discard(value)
         return value
 
-    def toggle(self, value):
-        """Return True if it was added, False if deleted."""
-        # XXX This implementation is not thread-safe
-        if value in self:
-            self.discard(value)
-            return False
-        else:
-            self.add(value)
-            return True
-
     def clear(self):
         """This is slow (creates N new iterators!) but effective."""
         try:
@@ -330,9 +320,13 @@
         return self
 
     def __ixor__(self, it: Iterable):
-        # This calls toggle(), so if that is overridded, we call the override
+        if not isinstance(it, Set):
+            it = self._from_iterable(it)
         for value in it:
-            self.toggle(it)
+            if value in self:
+                self.discard(value)
+            else:
+                self.add(value)
         return self
 
     def __isub__(self, it: Iterable):
diff --git a/Lib/numbers.py b/Lib/numbers.py
index 6c3c3e1..4dd5ca7 100644
--- a/Lib/numbers.py
+++ b/Lib/numbers.py
@@ -291,7 +291,13 @@
 
     # Concrete implementation of Real's conversion to float.
     def __float__(self):
-        """float(self) = self.numerator / self.denominator"""
+        """float(self) = self.numerator / self.denominator
+
+        It's important that this conversion use the integer's "true"
+        division rather than casting one side to float before dividing
+        so that ratios of huge integers convert without overflowing.
+
+        """
         return self.numerator / self.denominator
 
 
diff --git a/Lib/rational.py b/Lib/rational.py
index 8de8f23..06002a3 100755
--- a/Lib/rational.py
+++ b/Lib/rational.py
@@ -178,16 +178,6 @@
         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.
@@ -195,10 +185,82 @@
         Use this like:
         __op__, __rop__ = _operator_fallbacks(just_rational_op, operator.op)
 
+        In general, we want to implement the arithmetic operations so
+        that mixed-mode operations either call an implementation whose
+        author knew about the types of both arguments, or convert both
+        to the nearest built in type and do the operation there. In
+        Rational, that means that we define __add__ and __radd__ as:
+
+            def __add__(self, other):
+                if isinstance(other, (int, Rational)):
+                    # Do the real operation.
+                    return Rational(self.numerator * other.denominator +
+                                    other.numerator * self.denominator,
+                                    self.denominator * other.denominator)
+                # float and complex don't follow this protocol, and
+                # Rational knows about them, so special case them.
+                elif isinstance(other, float):
+                    return float(self) + other
+                elif isinstance(other, complex):
+                    return complex(self) + other
+                else:
+                    # Let the other type take over.
+                    return NotImplemented
+
+            def __radd__(self, other):
+                # radd handles more types than add because there's
+                # nothing left to fall back to.
+                if isinstance(other, RationalAbc):
+                    return Rational(self.numerator * other.denominator +
+                                    other.numerator * self.denominator,
+                                    self.denominator * other.denominator)
+                elif isinstance(other, Real):
+                    return float(other) + float(self)
+                elif isinstance(other, Complex):
+                    return complex(other) + complex(self)
+                else:
+                    return NotImplemented
+
+
+        There are 5 different cases for a mixed-type addition on
+        Rational. I'll refer to all of the above code that doesn't
+        refer to Rational, float, or complex as "boilerplate". 'r'
+        will be an instance of Rational, which is a subtype of
+        RationalAbc (r : Rational <: RationalAbc), and b : B <:
+        Complex. The first three involve 'r + b':
+
+            1. If B <: Rational, int, float, or complex, we handle
+               that specially, and all is well.
+            2. If Rational falls back to the boilerplate code, and it
+               were to return a value from __add__, we'd miss the
+               possibility that B defines a more intelligent __radd__,
+               so the boilerplate should return NotImplemented from
+               __add__. In particular, we don't handle RationalAbc
+               here, even though we could get an exact answer, in case
+               the other type wants to do something special.
+            3. If B <: Rational, Python tries B.__radd__ before
+               Rational.__add__. This is ok, because it was
+               implemented with knowledge of Rational, so it can
+               handle those instances before delegating to Real or
+               Complex.
+
+        The next two situations describe 'b + r'. We assume that b
+        didn't know about Rational in its implementation, and that it
+        uses similar boilerplate code:
+
+            4. If B <: RationalAbc, then __radd_ converts both to the
+               builtin rational type (hey look, that's us) and
+               proceeds.
+            5. Otherwise, __radd__ tries to find the nearest common
+               base ABC, and fall back to its builtin type. Since this
+               class doesn't subclass a concrete type, there's no
+               implementation to fall back to, so we need to try as
+               hard as possible to return an actual value, or the user
+               will get a TypeError.
+
         """
         def forward(a, b):
-            if isinstance(b, RationalAbc):
-                # Includes ints.
+            if isinstance(b, (int, Rational)):
                 return monomorphic_operator(a, b)
             elif isinstance(b, float):
                 return fallback_operator(float(a), b)