diff options
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/numbers.py | 393 | ||||
-rw-r--r-- | Lib/test/test_abstract_numbers.py | 62 | ||||
-rw-r--r-- | Lib/test/test_builtin.py | 75 | ||||
-rw-r--r-- | Lib/test/test_long.py | 4 | ||||
-rw-r--r-- | Lib/test/test_math.py | 26 | ||||
-rw-r--r-- | Lib/test/test_unittest.py | 23 | ||||
-rw-r--r-- | Lib/unittest.py | 4 |
7 files changed, 582 insertions, 5 deletions
diff --git a/Lib/numbers.py b/Lib/numbers.py new file mode 100644 index 0000000..d23fa34 --- /dev/null +++ b/Lib/numbers.py @@ -0,0 +1,393 @@ +# Copyright 2007 Google, Inc. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. + +"""Abstract Base Classes (ABCs) for numbers, according to PEP 3141. + +TODO: Fill out more detailed documentation on the operators.""" + +from abc import ABCMeta, abstractmethod, abstractproperty + +__all__ = ["Number", "Exact", "Inexact", + "Complex", "Real", "Rational", "Integral", + ] + + +class Number(object): + """All numbers inherit from this class. + + If you just want to check if an argument x is a number, without + caring what kind, use isinstance(x, Number). + """ + __metaclass__ = ABCMeta + + +class Exact(Number): + """Operations on instances of this type are exact. + + As long as the result of a homogenous operation is of the same + type, you can assume that it was computed exactly, and there are + no round-off errors. Laws like commutativity and associativity + hold. + """ + +Exact.register(int) +Exact.register(long) + + +class Inexact(Number): + """Operations on instances of this type are inexact. + + Given X, an instance of Inexact, it is possible that (X + -X) + 3 + == 3, but X + (-X + 3) == 0. The exact form this error takes will + vary by type, but it's generally unsafe to compare this type for + equality. + """ + +Inexact.register(complex) +Inexact.register(float) +# Inexact.register(decimal.Decimal) + + +class Complex(Number): + """Complex defines the operations that work on the builtin complex type. + + In short, those are: a conversion to complex, .real, .imag, +, -, + *, /, abs(), .conjugate, ==, and !=. + + If it is given heterogenous arguments, and doesn't have special + knowledge about them, it should fall back to the builtin complex + type as described below. + """ + + @abstractmethod + def __complex__(self): + """Return a builtin complex instance. Called for complex(self).""" + + def __bool__(self): + """True if self != 0. Called for bool(self).""" + return self != 0 + + @abstractproperty + def real(self): + """Retrieve the real component of this number. + + This should subclass Real. + """ + raise NotImplementedError + + @abstractproperty + def imag(self): + """Retrieve the real component of this number. + + This should subclass Real. + """ + raise NotImplementedError + + @abstractmethod + def __add__(self, other): + """self + other""" + raise NotImplementedError + + @abstractmethod + def __radd__(self, other): + """other + self""" + raise NotImplementedError + + @abstractmethod + def __neg__(self): + """-self""" + raise NotImplementedError + + def __pos__(self): + """+self""" + raise NotImplementedError + + def __sub__(self, other): + """self - other""" + return self + -other + + def __rsub__(self, other): + """other - self""" + return -self + other + + @abstractmethod + def __mul__(self, other): + """self * other""" + raise NotImplementedError + + @abstractmethod + def __rmul__(self, other): + """other * self""" + raise NotImplementedError + + @abstractmethod + def __div__(self, other): + """self / other; should promote to float or complex when necessary.""" + raise NotImplementedError + + @abstractmethod + def __rdiv__(self, other): + """other / self""" + raise NotImplementedError + + @abstractmethod + def __pow__(self, exponent): + """self**exponent; should promote to float or complex when necessary.""" + raise NotImplementedError + + @abstractmethod + def __rpow__(self, base): + """base ** self""" + raise NotImplementedError + + @abstractmethod + def __abs__(self): + """Returns the Real distance from 0. Called for abs(self).""" + raise NotImplementedError + + @abstractmethod + def conjugate(self): + """(x+y*i).conjugate() returns (x-y*i).""" + raise NotImplementedError + + @abstractmethod + def __eq__(self, other): + """self == other""" + raise NotImplementedError + + # __ne__ is inherited from object and negates whatever __eq__ does. + +Complex.register(complex) + + +class Real(Complex): + """To Complex, Real adds the operations that work on real numbers. + + In short, those are: a conversion to float, trunc(), divmod, + %, <, <=, >, and >=. + + Real also provides defaults for the derived operations. + """ + + @abstractmethod + def __float__(self): + """Any Real can be converted to a native float object. + + Called for float(self).""" + raise NotImplementedError + + @abstractmethod + def __trunc__(self): + """trunc(self): Truncates self to an Integral. + + Returns an Integral i such that: + * i>0 iff self>0; + * abs(i) <= abs(self); + * for any Integral j satisfying the first two conditions, + abs(i) >= abs(j) [i.e. i has "maximal" abs among those]. + i.e. "truncate towards 0". + """ + raise NotImplementedError + + @abstractmethod + def __floor__(self): + """Finds the greatest Integral <= self.""" + raise NotImplementedError + + @abstractmethod + def __ceil__(self): + """Finds the least Integral >= self.""" + raise NotImplementedError + + @abstractmethod + def __round__(self, ndigits=None): + """Rounds self to ndigits decimal places, defaulting to 0. + + If ndigits is omitted or None, returns an Integral, otherwise + returns a Real. Rounds half toward even. + """ + raise NotImplementedError + + def __divmod__(self, other): + """divmod(self, other): The pair (self // other, self % other). + + Sometimes this can be computed faster than the pair of + operations. + """ + return (self // other, self % other) + + def __rdivmod__(self, other): + """divmod(other, self): The pair (self // other, self % other). + + Sometimes this can be computed faster than the pair of + operations. + """ + return (other // self, other % self) + + @abstractmethod + def __floordiv__(self, other): + """self // other: The floor() of self/other.""" + raise NotImplementedError + + @abstractmethod + def __rfloordiv__(self, other): + """other // self: The floor() of other/self.""" + raise NotImplementedError + + @abstractmethod + def __mod__(self, other): + """self % other""" + raise NotImplementedError + + @abstractmethod + def __rmod__(self, other): + """other % self""" + raise NotImplementedError + + @abstractmethod + def __lt__(self, other): + """self < other + + < on Reals defines a total ordering, except perhaps for NaN.""" + raise NotImplementedError + + @abstractmethod + def __le__(self, other): + """self <= other""" + raise NotImplementedError + + # Concrete implementations of Complex abstract methods. + def __complex__(self): + """complex(self) == complex(float(self), 0)""" + return complex(float(self)) + + @property + def real(self): + """Real numbers are their real component.""" + return +self + + @property + def imag(self): + """Real numbers have no imaginary component.""" + return 0 + + def conjugate(self): + """Conjugate is a no-op for Reals.""" + return +self + +Real.register(float) +# Real.register(decimal.Decimal) + + +class Rational(Real, Exact): + """.numerator and .denominator should be in lowest terms.""" + + @abstractproperty + def numerator(self): + raise NotImplementedError + + @abstractproperty + def denominator(self): + raise NotImplementedError + + # Concrete implementation of Real's conversion to float. + def __float__(self): + """float(self) = self.numerator / self.denominator""" + return self.numerator / self.denominator + + +class Integral(Rational): + """Integral adds a conversion to long and the bit-string operations.""" + + @abstractmethod + def __long__(self): + """long(self)""" + raise NotImplementedError + + def __index__(self): + """index(self)""" + return long(self) + + @abstractmethod + def __pow__(self, exponent, modulus=None): + """self ** exponent % modulus, but maybe faster. + + Accept the modulus argument if you want to support the + 3-argument version of pow(). Raise a TypeError if exponent < 0 + or any argument isn't Integral. Otherwise, just implement the + 2-argument version described in Complex. + """ + raise NotImplementedError + + @abstractmethod + def __lshift__(self, other): + """self << other""" + raise NotImplementedError + + @abstractmethod + def __rlshift__(self, other): + """other << self""" + raise NotImplementedError + + @abstractmethod + def __rshift__(self, other): + """self >> other""" + raise NotImplementedError + + @abstractmethod + def __rrshift__(self, other): + """other >> self""" + raise NotImplementedError + + @abstractmethod + def __and__(self, other): + """self & other""" + raise NotImplementedError + + @abstractmethod + def __rand__(self, other): + """other & self""" + raise NotImplementedError + + @abstractmethod + def __xor__(self, other): + """self ^ other""" + raise NotImplementedError + + @abstractmethod + def __rxor__(self, other): + """other ^ self""" + raise NotImplementedError + + @abstractmethod + def __or__(self, other): + """self | other""" + raise NotImplementedError + + @abstractmethod + def __ror__(self, other): + """other | self""" + raise NotImplementedError + + @abstractmethod + def __invert__(self): + """~self""" + raise NotImplementedError + + # Concrete implementations of Rational and Real abstract methods. + def __float__(self): + """float(self) == float(long(self))""" + return float(long(self)) + + @property + def numerator(self): + """Integers are their own numerators.""" + return +self + + @property + def denominator(self): + """Integers have a denominator of 1.""" + return 1 + +Integral.register(int) +Integral.register(long) diff --git a/Lib/test/test_abstract_numbers.py b/Lib/test/test_abstract_numbers.py new file mode 100644 index 0000000..19bfc79 --- /dev/null +++ b/Lib/test/test_abstract_numbers.py @@ -0,0 +1,62 @@ +"""Unit tests for numbers.py.""" + +import unittest +from test import test_support +from numbers import Number +from numbers import Exact, Inexact +from numbers import Complex, Real, Rational, Integral +import operator + +class TestNumbers(unittest.TestCase): + def test_int(self): + self.failUnless(issubclass(int, Integral)) + self.failUnless(issubclass(int, Complex)) + self.failUnless(issubclass(int, Exact)) + self.failIf(issubclass(int, Inexact)) + + self.assertEqual(7, int(7).real) + self.assertEqual(0, int(7).imag) + self.assertEqual(7, int(7).conjugate()) + self.assertEqual(7, int(7).numerator) + self.assertEqual(1, int(7).denominator) + + def test_long(self): + self.failUnless(issubclass(long, Integral)) + self.failUnless(issubclass(long, Complex)) + self.failUnless(issubclass(long, Exact)) + self.failIf(issubclass(long, Inexact)) + + self.assertEqual(7, long(7).real) + self.assertEqual(0, long(7).imag) + self.assertEqual(7, long(7).conjugate()) + self.assertEqual(7, long(7).numerator) + self.assertEqual(1, long(7).denominator) + + def test_float(self): + self.failIf(issubclass(float, Rational)) + self.failUnless(issubclass(float, Real)) + self.failIf(issubclass(float, Exact)) + self.failUnless(issubclass(float, Inexact)) + + self.assertEqual(7.3, float(7.3).real) + self.assertEqual(0, float(7.3).imag) + self.assertEqual(7.3, float(7.3).conjugate()) + + def test_complex(self): + self.failIf(issubclass(complex, Real)) + self.failUnless(issubclass(complex, Complex)) + self.failIf(issubclass(complex, Exact)) + self.failUnless(issubclass(complex, Inexact)) + + c1, c2 = complex(3, 2), complex(4,1) + # XXX: This is not ideal, but see the comment in builtin_trunc(). + self.assertRaises(AttributeError, trunc, c1) + self.assertRaises(TypeError, float, c1) + self.assertRaises(TypeError, int, c1) + +def test_main(): + test_support.run_unittest(TestNumbers) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 52178c8..c5a1cc3 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1450,11 +1450,13 @@ class BuiltinTest(unittest.TestCase): else: self.assertAlmostEqual(pow(x, y, z), 24.0) + self.assertAlmostEqual(pow(-1, 0.5), 1j) + self.assertAlmostEqual(pow(-1, 1./3), 0.5 + 0.8660254037844386j) + self.assertRaises(TypeError, pow, -1, -2, 3) self.assertRaises(ValueError, pow, 1, 2, 0) self.assertRaises(TypeError, pow, -1L, -2L, 3L) self.assertRaises(ValueError, pow, 1L, 2L, 0L) - self.assertRaises(ValueError, pow, -342.43, 0.234) self.assertRaises(TypeError, pow) @@ -1622,6 +1624,7 @@ class BuiltinTest(unittest.TestCase): def test_round(self): self.assertEqual(round(0.0), 0.0) + self.assertEqual(type(round(0.0)), float) # Will be int in 3.0. self.assertEqual(round(1.0), 1.0) self.assertEqual(round(10.0), 10.0) self.assertEqual(round(1000000000.0), 1000000000.0) @@ -1650,12 +1653,50 @@ class BuiltinTest(unittest.TestCase): self.assertEqual(round(-999999999.9), -1000000000.0) self.assertEqual(round(-8.0, -1), -10.0) + self.assertEqual(type(round(-8.0, -1)), float) + + self.assertEqual(type(round(-8.0, 0)), float) + self.assertEqual(type(round(-8.0, 1)), float) + + # Check even / odd rounding behaviour + self.assertEqual(round(5.5), 6) + self.assertEqual(round(6.5), 6) + self.assertEqual(round(-5.5), -6) + self.assertEqual(round(-6.5), -6) + + # Check behavior on ints + self.assertEqual(round(0), 0) + self.assertEqual(round(8), 8) + self.assertEqual(round(-8), -8) + self.assertEqual(type(round(0)), float) # Will be int in 3.0. + self.assertEqual(type(round(-8, -1)), float) + self.assertEqual(type(round(-8, 0)), float) + self.assertEqual(type(round(-8, 1)), float) # test new kwargs self.assertEqual(round(number=-8.0, ndigits=-1), -10.0) self.assertRaises(TypeError, round) + # test generic rounding delegation for reals + class TestRound(object): + def __round__(self): + return 23 + + class TestNoRound(object): + pass + + self.assertEqual(round(TestRound()), 23) + + self.assertRaises(TypeError, round, 1, 2, 3) + # XXX: This is not ideal, but see the comment in builtin_round(). + self.assertRaises(AttributeError, round, TestNoRound()) + + t = TestNoRound() + t.__round__ = lambda *args: args + self.assertEquals((), round(t)) + self.assertEquals((0,), round(t, 0)) + def test_setattr(self): setattr(sys, 'spam', 1) self.assertEqual(sys.spam, 1) @@ -1697,6 +1738,38 @@ class BuiltinTest(unittest.TestCase): raise ValueError self.assertRaises(ValueError, sum, BadSeq()) + def test_trunc(self): + + self.assertEqual(trunc(1), 1) + self.assertEqual(trunc(-1), -1) + self.assertEqual(type(trunc(1)), int) + self.assertEqual(type(trunc(1.5)), int) + self.assertEqual(trunc(1.5), 1) + self.assertEqual(trunc(-1.5), -1) + self.assertEqual(trunc(1.999999), 1) + self.assertEqual(trunc(-1.999999), -1) + self.assertEqual(trunc(-0.999999), -0) + self.assertEqual(trunc(-100.999), -100) + + class TestTrunc(object): + def __trunc__(self): + return 23 + + class TestNoTrunc(object): + pass + + self.assertEqual(trunc(TestTrunc()), 23) + + self.assertRaises(TypeError, trunc) + self.assertRaises(TypeError, trunc, 1, 2) + # XXX: This is not ideal, but see the comment in builtin_trunc(). + self.assertRaises(AttributeError, trunc, TestNoTrunc()) + + t = TestNoTrunc() + t.__trunc__ = lambda *args: args + self.assertEquals((), trunc(t)) + self.assertRaises(TypeError, trunc, t, 0) + def test_tuple(self): self.assertEqual(tuple(()), ()) t0_3 = (0, 1, 2, 3) diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py index ae132ad..29515c7 100644 --- a/Lib/test/test_long.py +++ b/Lib/test/test_long.py @@ -385,7 +385,9 @@ class LongTest(unittest.TestCase): "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)"]: + # math.floor() of an int returns an int now + ##"math.floor(huge)", "math.floor(mhuge)", + ]: self.assertRaises(OverflowError, eval, test, namespace) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index d86298d..98e4623 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -58,6 +58,19 @@ class MathTests(unittest.TestCase): self.ftest('ceil(-1.0)', math.ceil(-1.0), -1) self.ftest('ceil(-1.5)', math.ceil(-1.5), -1) + class TestCeil(object): + def __ceil__(self): + return 42 + class TestNoCeil(object): + pass + self.ftest('ceil(TestCeil())', math.ceil(TestCeil()), 42) + self.assertRaises(TypeError, math.ceil, TestNoCeil()) + + t = TestNoCeil() + t.__ceil__ = lambda *args: args + self.assertRaises(TypeError, math.ceil, t) + self.assertRaises(TypeError, math.ceil, t, 0) + def testCos(self): self.assertRaises(TypeError, math.cos) self.ftest('cos(-pi/2)', math.cos(-math.pi/2), 0) @@ -101,6 +114,19 @@ class MathTests(unittest.TestCase): self.ftest('floor(1.23e167)', math.floor(1.23e167), 1.23e167) self.ftest('floor(-1.23e167)', math.floor(-1.23e167), -1.23e167) + class TestFloor(object): + def __floor__(self): + return 42 + class TestNoFloor(object): + pass + self.ftest('floor(TestFloor())', math.floor(TestFloor()), 42) + self.assertRaises(TypeError, math.floor, TestNoFloor()) + + t = TestNoFloor() + t.__floor__ = lambda *args: args + self.assertRaises(TypeError, math.floor, t) + self.assertRaises(TypeError, math.floor, t, 0) + def testFmod(self): self.assertRaises(TypeError, math.fmod) self.ftest('fmod(10,1)', math.fmod(10,1), 0) diff --git a/Lib/test/test_unittest.py b/Lib/test/test_unittest.py index 9dfed7b..5a82780 100644 --- a/Lib/test/test_unittest.py +++ b/Lib/test/test_unittest.py @@ -2264,13 +2264,34 @@ class Test_TestCase(TestCase, TestEquality, TestHashing): expected = ['startTest', 'test', 'stopTest'] self.assertEqual(events, expected) +class Test_Assertions(TestCase): + def test_AlmostEqual(self): + self.failUnlessAlmostEqual(1.00000001, 1.0) + self.failIfAlmostEqual(1.0000001, 1.0) + self.assertRaises(AssertionError, + self.failUnlessAlmostEqual, 1.0000001, 1.0) + self.assertRaises(AssertionError, + self.failIfAlmostEqual, 1.00000001, 1.0) + + self.failUnlessAlmostEqual(1.1, 1.0, places=0) + self.assertRaises(AssertionError, + self.failUnlessAlmostEqual, 1.1, 1.0, places=1) + + self.failUnlessAlmostEqual(0, .1+.1j, places=0) + self.failIfAlmostEqual(0, .1+.1j, places=1) + self.assertRaises(AssertionError, + self.failUnlessAlmostEqual, 0, .1+.1j, places=1) + self.assertRaises(AssertionError, + self.failIfAlmostEqual, 0, .1+.1j, places=0) + ###################################################################### ## Main ###################################################################### def test_main(): test_support.run_unittest(Test_TestCase, Test_TestLoader, - Test_TestSuite, Test_TestResult, Test_FunctionTestCase) + Test_TestSuite, Test_TestResult, Test_FunctionTestCase, + Test_Assertions) if __name__ == "__main__": test_main() diff --git a/Lib/unittest.py b/Lib/unittest.py index 7ea240c..eccefe6 100644 --- a/Lib/unittest.py +++ b/Lib/unittest.py @@ -358,7 +358,7 @@ class TestCase: Note that decimal places (from zero) are usually not the same as significant digits (measured from the most signficant digit). """ - if round(second-first, places) != 0: + if round(abs(second-first), places) != 0: raise self.failureException, \ (msg or '%r != %r within %r places' % (first, second, places)) @@ -370,7 +370,7 @@ class TestCase: Note that decimal places (from zero) are usually not the same as significant digits (measured from the most signficant digit). """ - if round(second-first, places) == 0: + if round(abs(second-first), places) == 0: raise self.failureException, \ (msg or '%r == %r within %r places' % (first, second, places)) |