summaryrefslogtreecommitdiffstats
path: root/Lib/test
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/test')
-rw-r--r--Lib/test/test_capi/test_abstract.py7
-rw-r--r--Lib/test/test_capi/test_number.py335
2 files changed, 335 insertions, 7 deletions
diff --git a/Lib/test/test_capi/test_abstract.py b/Lib/test/test_capi/test_abstract.py
index 3a8c224..6a62681 100644
--- a/Lib/test/test_capi/test_abstract.py
+++ b/Lib/test/test_capi/test_abstract.py
@@ -994,13 +994,6 @@ class CAPITest(unittest.TestCase):
self.assertRaises(TypeError, xtuple, 42)
self.assertRaises(SystemError, xtuple, NULL)
- def test_number_check(self):
- number_check = _testlimitedcapi.number_check
- self.assertTrue(number_check(1 + 1j))
- self.assertTrue(number_check(1))
- self.assertTrue(number_check(0.5))
- self.assertFalse(number_check("1 + 1j"))
-
def test_object_generichash(self):
# Test PyObject_GenericHash()
generichash = _testcapi.object_generichash
diff --git a/Lib/test/test_capi/test_number.py b/Lib/test/test_capi/test_number.py
new file mode 100644
index 0000000..3c1f0f2
--- /dev/null
+++ b/Lib/test/test_capi/test_number.py
@@ -0,0 +1,335 @@
+import itertools
+import operator
+import sys
+import unittest
+import warnings
+
+from test.support import cpython_only, import_helper
+
+_testcapi = import_helper.import_module('_testcapi')
+from _testcapi import PY_SSIZE_T_MAX, PY_SSIZE_T_MIN
+
+try:
+ from _testbuffer import ndarray
+except ImportError:
+ ndarray = None
+
+NULL = None
+
+class BadDescr:
+ def __get__(self, obj, objtype=None):
+ raise RuntimeError
+
+class WithDunder:
+ def _meth(self, *args):
+ if self.val:
+ return self.val
+ if self.exc:
+ raise self.exc
+ @classmethod
+ def with_val(cls, val):
+ obj = super().__new__(cls)
+ obj.val = val
+ obj.exc = None
+ setattr(cls, cls.methname, cls._meth)
+ return obj
+
+ @classmethod
+ def with_exc(cls, exc):
+ obj = super().__new__(cls)
+ obj.val = None
+ obj.exc = exc
+ setattr(cls, cls.methname, cls._meth)
+ return obj
+
+class HasBadAttr:
+ def __new__(cls):
+ obj = super().__new__(cls)
+ setattr(cls, cls.methname, BadDescr())
+ return obj
+
+
+class IndexLike(WithDunder):
+ methname = '__index__'
+
+class IntLike(WithDunder):
+ methname = '__int__'
+
+class FloatLike(WithDunder):
+ methname = '__float__'
+
+
+def subclassof(base):
+ return type(base.__name__ + 'Subclass', (base,), {})
+
+
+class SomeError(Exception):
+ pass
+
+class OtherError(Exception):
+ pass
+
+
+class CAPITest(unittest.TestCase):
+ def test_check(self):
+ # Test PyNumber_Check()
+ check = _testcapi.number_check
+
+ self.assertTrue(check(1))
+ self.assertTrue(check(IndexLike.with_val(1)))
+ self.assertTrue(check(IntLike.with_val(99)))
+ self.assertTrue(check(0.5))
+ self.assertTrue(check(FloatLike.with_val(4.25)))
+ self.assertTrue(check(1+2j))
+
+ self.assertFalse(check([]))
+ self.assertFalse(check("abc"))
+ self.assertFalse(check(object()))
+ self.assertFalse(check(NULL))
+
+ def test_unary_ops(self):
+ methmap = {'__neg__': _testcapi.number_negative, # PyNumber_Negative()
+ '__pos__': _testcapi.number_positive, # PyNumber_Positive()
+ '__abs__': _testcapi.number_absolute, # PyNumber_Absolute()
+ '__invert__': _testcapi.number_invert} # PyNumber_Invert()
+
+ for name, func in methmap.items():
+ # Generic object, has no tp_as_number structure
+ self.assertRaises(TypeError, func, object())
+
+ # C-API function accepts NULL
+ self.assertRaises(SystemError, func, NULL)
+
+ # Behave as corresponding unary operation
+ op = getattr(operator, name)
+ for x in [0, 42, -1, 3.14, 1+2j]:
+ try:
+ op(x)
+ except TypeError:
+ self.assertRaises(TypeError, func, x)
+ else:
+ self.assertEqual(func(x), op(x))
+
+ def test_binary_ops(self):
+ methmap = {'__add__': _testcapi.number_add, # PyNumber_Add()
+ '__sub__': _testcapi.number_subtract, # PyNumber_Subtract()
+ '__mul__': _testcapi.number_multiply, # PyNumber_Multiply()
+ '__matmul__': _testcapi.number_matrixmultiply, # PyNumber_MatrixMultiply()
+ '__floordiv__': _testcapi.number_floordivide, # PyNumber_FloorDivide()
+ '__truediv__': _testcapi.number_truedivide, # PyNumber_TrueDivide()
+ '__mod__': _testcapi.number_remainder, # PyNumber_Remainder()
+ '__divmod__': _testcapi.number_divmod, # PyNumber_Divmod()
+ '__lshift__': _testcapi.number_lshift, # PyNumber_Lshift()
+ '__rshift__': _testcapi.number_rshift, # PyNumber_Rshift()
+ '__and__': _testcapi.number_and, # PyNumber_And()
+ '__xor__': _testcapi.number_xor, # PyNumber_Xor()
+ '__or__': _testcapi.number_or, # PyNumber_Or()
+ '__pow__': _testcapi.number_power, # PyNumber_Power()
+ '__iadd__': _testcapi.number_inplaceadd, # PyNumber_InPlaceAdd()
+ '__isub__': _testcapi.number_inplacesubtract, # PyNumber_InPlaceSubtract()
+ '__imul__': _testcapi.number_inplacemultiply, # PyNumber_InPlaceMultiply()
+ '__imatmul__': _testcapi.number_inplacematrixmultiply, # PyNumber_InPlaceMatrixMultiply()
+ '__ifloordiv__': _testcapi.number_inplacefloordivide, # PyNumber_InPlaceFloorDivide()
+ '__itruediv__': _testcapi.number_inplacetruedivide, # PyNumber_InPlaceTrueDivide()
+ '__imod__': _testcapi.number_inplaceremainder, # PyNumber_InPlaceRemainder()
+ '__ilshift__': _testcapi.number_inplacelshift, # PyNumber_InPlaceLshift()
+ '__irshift__': _testcapi.number_inplacershift, # PyNumber_InPlaceRshift()
+ '__iand__': _testcapi.number_inplaceand, # PyNumber_InPlaceAnd()
+ '__ixor__': _testcapi.number_inplacexor, # PyNumber_InPlaceXor()
+ '__ior__': _testcapi.number_inplaceor, # PyNumber_InPlaceOr()
+ '__ipow__': _testcapi.number_inplacepower, # PyNumber_InPlacePower()
+ }
+
+ for name, func in methmap.items():
+ cases = [0, 42, 3.14, -1, 123, 1+2j]
+
+ # Generic object, has no tp_as_number structure
+ for x in cases:
+ self.assertRaises(TypeError, func, object(), x)
+ self.assertRaises(TypeError, func, x, object())
+
+ # Behave as corresponding binary operation
+ op = getattr(operator, name, divmod)
+ for x, y in itertools.combinations(cases, 2):
+ try:
+ op(x, y)
+ except (TypeError, ValueError, ZeroDivisionError) as exc:
+ self.assertRaises(exc.__class__, func, x, y)
+ else:
+ self.assertEqual(func(x, y), op(x, y))
+
+ # CRASHES func(NULL, object())
+ # CRASHES func(object(), NULL)
+
+ @unittest.skipIf(ndarray is None, "needs _testbuffer")
+ def test_misc_add(self):
+ # PyNumber_Add(), PyNumber_InPlaceAdd()
+ add = _testcapi.number_add
+ inplaceadd = _testcapi.number_inplaceadd
+
+ # test sq_concat/sq_inplace_concat slots
+ a, b, r = [1, 2], [3, 4], [1, 2, 3, 4]
+ self.assertEqual(add(a, b), r)
+ self.assertEqual(a, [1, 2])
+ self.assertRaises(TypeError, add, ndarray([1], (1,)), 2)
+ a, b, r = [1, 2], [3, 4], [1, 2, 3, 4]
+ self.assertEqual(inplaceadd(a, b), r)
+ self.assertEqual(a, r)
+ self.assertRaises(TypeError, inplaceadd, ndarray([1], (1,)), 2)
+
+ @unittest.skipIf(ndarray is None, "needs _testbuffer")
+ def test_misc_multiply(self):
+ # PyNumber_Multiply(), PyNumber_InPlaceMultiply()
+ multiply = _testcapi.number_multiply
+ inplacemultiply = _testcapi.number_inplacemultiply
+
+ # test sq_repeat/sq_inplace_repeat slots
+ a, b, r = [1], 2, [1, 1]
+ self.assertEqual(multiply(a, b), r)
+ self.assertEqual((a, b), ([1], 2))
+ self.assertEqual(multiply(b, a), r)
+ self.assertEqual((a, b), ([1], 2))
+ self.assertEqual(multiply([1], -1), [])
+ self.assertRaises(TypeError, multiply, ndarray([1], (1,)), 2)
+ self.assertRaises(TypeError, multiply, [1], 0.5)
+ self.assertRaises(OverflowError, multiply, [1], PY_SSIZE_T_MAX + 1)
+ self.assertRaises(MemoryError, multiply, [1, 2], PY_SSIZE_T_MAX//2 + 1)
+ a, b, r = [1], 2, [1, 1]
+ self.assertEqual(inplacemultiply(a, b), r)
+ self.assertEqual((a, b), (r, 2))
+ a = [1]
+ self.assertEqual(inplacemultiply(b, a), r)
+ self.assertEqual((a, b), ([1], 2))
+ self.assertRaises(TypeError, inplacemultiply, ndarray([1], (1,)), 2)
+ self.assertRaises(OverflowError, inplacemultiply, [1], PY_SSIZE_T_MAX + 1)
+ self.assertRaises(MemoryError, inplacemultiply, [1, 2], PY_SSIZE_T_MAX//2 + 1)
+
+ def test_misc_power(self):
+ # PyNumber_Power()
+ power = _testcapi.number_power
+
+ class HasPow(WithDunder):
+ methname = '__pow__'
+
+ # ternary op
+ self.assertEqual(power(4, 11, 5), pow(4, 11, 5))
+ self.assertRaises(TypeError, power, 4, 11, 1.25)
+ self.assertRaises(TypeError, power, 4, 11, HasPow.with_val(NotImplemented))
+ self.assertRaises(TypeError, power, 4, 11, object())
+
+ @cpython_only
+ def test_rshift_print(self):
+ # This tests correct syntax hint for py2 redirection (>>).
+ rshift = _testcapi.number_rshift
+
+ with self.assertRaises(TypeError) as context:
+ rshift(print, 42)
+ self.assertIn('Did you mean "print(<message>, '
+ 'file=<output_stream>)"?', str(context.exception))
+ with self.assertRaises(TypeError) as context:
+ rshift(max, sys.stderr)
+ self.assertNotIn('Did you mean ', str(context.exception))
+ with self.assertRaises(TypeError) as context:
+ rshift(1, "spam")
+
+ def test_long(self):
+ # Test PyNumber_Long()
+ long = _testcapi.number_long
+
+ self.assertEqual(long(42), 42)
+ self.assertEqual(long(1.25), 1)
+ self.assertEqual(long("42"), 42)
+ self.assertEqual(long(b"42"), 42)
+ self.assertEqual(long(bytearray(b"42")), 42)
+ self.assertEqual(long(memoryview(b"42")), 42)
+ self.assertEqual(long(IndexLike.with_val(99)), 99)
+ self.assertEqual(long(IntLike.with_val(99)), 99)
+
+ self.assertRaises(TypeError, long, IntLike.with_val(1.0))
+ with warnings.catch_warnings():
+ warnings.simplefilter("error", DeprecationWarning)
+ self.assertRaises(DeprecationWarning, long, IntLike.with_val(True))
+ with self.assertWarns(DeprecationWarning):
+ self.assertEqual(long(IntLike.with_val(True)), 1)
+ self.assertRaises(RuntimeError, long, IntLike.with_exc(RuntimeError))
+
+ self.assertRaises(TypeError, long, 1j)
+ self.assertRaises(TypeError, long, object())
+ self.assertRaises(SystemError, long, NULL)
+
+ def test_float(self):
+ # Test PyNumber_Float()
+ float_ = _testcapi.number_float
+
+ self.assertEqual(float_(1.25), 1.25)
+ self.assertEqual(float_(123), 123.)
+ self.assertEqual(float_("1.25"), 1.25)
+
+ self.assertEqual(float_(FloatLike.with_val(4.25)), 4.25)
+ self.assertEqual(float_(IndexLike.with_val(99)), 99.0)
+ self.assertEqual(float_(IndexLike.with_val(-1)), -1.0)
+
+ self.assertRaises(TypeError, float_, FloatLike.with_val(687))
+ with warnings.catch_warnings():
+ warnings.simplefilter("error", DeprecationWarning)
+ self.assertRaises(DeprecationWarning, float_, FloatLike.with_val(subclassof(float)(4.25)))
+ with self.assertWarns(DeprecationWarning):
+ self.assertEqual(float_(FloatLike.with_val(subclassof(float)(4.25))), 4.25)
+ self.assertRaises(RuntimeError, float_, FloatLike.with_exc(RuntimeError))
+
+ self.assertRaises(TypeError, float_, IndexLike.with_val(1.25))
+ self.assertRaises(OverflowError, float_, IndexLike.with_val(2**2000))
+
+ self.assertRaises(TypeError, float_, 1j)
+ self.assertRaises(TypeError, float_, object())
+ self.assertRaises(SystemError, float_, NULL)
+
+ def test_index(self):
+ # Test PyNumber_Index()
+ index = _testcapi.number_index
+
+ self.assertEqual(index(11), 11)
+
+ with warnings.catch_warnings():
+ warnings.simplefilter("error", DeprecationWarning)
+ self.assertRaises(DeprecationWarning, index, IndexLike.with_val(True))
+ with self.assertWarns(DeprecationWarning):
+ self.assertEqual(index(IndexLike.with_val(True)), 1)
+ self.assertRaises(TypeError, index, IndexLike.with_val(1.0))
+ self.assertRaises(RuntimeError, index, IndexLike.with_exc(RuntimeError))
+
+ self.assertRaises(TypeError, index, 1.25)
+ self.assertRaises(TypeError, index, "42")
+ self.assertRaises(TypeError, index, object())
+ self.assertRaises(SystemError, index, NULL)
+
+ def test_tobase(self):
+ # Test PyNumber_ToBase()
+ tobase = _testcapi.number_tobase
+
+ self.assertEqual(tobase(10, 2), bin(10))
+ self.assertEqual(tobase(11, 8), oct(11))
+ self.assertEqual(tobase(16, 10), str(16))
+ self.assertEqual(tobase(13, 16), hex(13))
+
+ self.assertRaises(SystemError, tobase, NULL, 2)
+ self.assertRaises(SystemError, tobase, 2, 3)
+ self.assertRaises(TypeError, tobase, 1.25, 2)
+ self.assertRaises(TypeError, tobase, "42", 2)
+
+ def test_asssizet(self):
+ # Test PyNumber_AsSsize_t()
+ asssizet = _testcapi.number_asssizet
+
+ for n in [*range(-6, 7), PY_SSIZE_T_MIN, PY_SSIZE_T_MAX]:
+ self.assertEqual(asssizet(n, OverflowError), n)
+ self.assertEqual(asssizet(PY_SSIZE_T_MAX+10, NULL), PY_SSIZE_T_MAX)
+ self.assertEqual(asssizet(PY_SSIZE_T_MIN-10, NULL), PY_SSIZE_T_MIN)
+
+ self.assertRaises(OverflowError, asssizet, PY_SSIZE_T_MAX + 10, OverflowError)
+ self.assertRaises(RuntimeError, asssizet, PY_SSIZE_T_MAX + 10, RuntimeError)
+ self.assertRaises(SystemError, asssizet, NULL, TypeError)
+
+
+if __name__ == "__main__":
+ unittest.main()