]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-111495: Add tests for PyFloat C API (GH-111624)
authorSergey B Kirpichev <skirpichev@gmail.com>
Sun, 5 Nov 2023 07:20:12 +0000 (10:20 +0300)
committerGitHub <noreply@github.com>
Sun, 5 Nov 2023 07:20:12 +0000 (09:20 +0200)
Lib/test/test_capi/test_float.py [new file with mode: 0644]
Modules/_testcapi/float.c

diff --git a/Lib/test/test_capi/test_float.py b/Lib/test/test_capi/test_float.py
new file mode 100644 (file)
index 0000000..95635e8
--- /dev/null
@@ -0,0 +1,117 @@
+import math
+import sys
+import unittest
+import warnings
+
+from test.test_capi.test_getargs import (Float, FloatSubclass, FloatSubclass2,
+                                         BadIndex2, BadFloat2, Index, BadIndex,
+                                         BadFloat)
+from test.support import import_helper
+
+_testcapi = import_helper.import_module('_testcapi')
+
+NULL = None
+
+
+class CAPIFloatTest(unittest.TestCase):
+    def test_check(self):
+        # Test PyFloat_Check()
+        check = _testcapi.float_check
+
+        self.assertTrue(check(4.25))
+        self.assertTrue(check(FloatSubclass(4.25)))
+        self.assertFalse(check(Float()))
+        self.assertFalse(check(3))
+        self.assertFalse(check(object()))
+
+        # CRASHES check(NULL)
+
+    def test_checkexact(self):
+        # Test PyFloat_CheckExact()
+        checkexact = _testcapi.float_checkexact
+
+        self.assertTrue(checkexact(4.25))
+        self.assertFalse(checkexact(FloatSubclass(4.25)))
+        self.assertFalse(checkexact(Float()))
+        self.assertFalse(checkexact(3))
+        self.assertFalse(checkexact(object()))
+
+        # CRASHES checkexact(NULL)
+
+    def test_fromstring(self):
+        # Test PyFloat_FromString()
+        fromstring = _testcapi.float_fromstring
+
+        self.assertEqual(fromstring("4.25"), 4.25)
+        self.assertEqual(fromstring(b"4.25"), 4.25)
+        self.assertRaises(ValueError, fromstring, "4.25\0")
+        self.assertRaises(ValueError, fromstring, b"4.25\0")
+
+        self.assertEqual(fromstring(bytearray(b"4.25")), 4.25)
+
+        self.assertEqual(fromstring(memoryview(b"4.25")), 4.25)
+        self.assertEqual(fromstring(memoryview(b"4.255")[:-1]), 4.25)
+        self.assertRaises(TypeError, fromstring, memoryview(b"4.25")[::2])
+
+        self.assertRaises(TypeError, fromstring, 4.25)
+
+        # CRASHES fromstring(NULL)
+
+    def test_fromdouble(self):
+        # Test PyFloat_FromDouble()
+        fromdouble = _testcapi.float_fromdouble
+
+        self.assertEqual(fromdouble(4.25), 4.25)
+
+    def test_asdouble(self):
+        # Test PyFloat_AsDouble()
+        asdouble = _testcapi.float_asdouble
+
+        class BadFloat3:
+            def __float__(self):
+                raise RuntimeError
+
+        self.assertEqual(asdouble(4.25), 4.25)
+        self.assertEqual(asdouble(-1.0), -1.0)
+        self.assertEqual(asdouble(42), 42.0)
+        self.assertEqual(asdouble(-1), -1.0)
+        self.assertEqual(asdouble(2**1000), float(2**1000))
+
+        self.assertEqual(asdouble(FloatSubclass(4.25)), 4.25)
+        self.assertEqual(asdouble(FloatSubclass2(4.25)), 4.25)
+        self.assertEqual(asdouble(Index()), 99.)
+
+        self.assertRaises(TypeError, asdouble, BadIndex())
+        self.assertRaises(TypeError, asdouble, BadFloat())
+        self.assertRaises(RuntimeError, asdouble, BadFloat3())
+        with self.assertWarns(DeprecationWarning):
+            self.assertEqual(asdouble(BadIndex2()), 1.)
+        with self.assertWarns(DeprecationWarning):
+            self.assertEqual(asdouble(BadFloat2()), 4.25)
+        with warnings.catch_warnings():
+            warnings.simplefilter("error", DeprecationWarning)
+            self.assertRaises(DeprecationWarning, asdouble, BadFloat2())
+        self.assertRaises(TypeError, asdouble, object())
+        self.assertRaises(TypeError, asdouble, NULL)
+
+    def test_getinfo(self):
+        # Test PyFloat_GetInfo()
+        getinfo = _testcapi.float_getinfo
+
+        self.assertEqual(getinfo(), sys.float_info)
+
+    def test_getmax(self):
+        # Test PyFloat_GetMax()
+        getmax = _testcapi.float_getmax
+
+        self.assertEqual(getmax(), sys.float_info.max)
+
+    def test_getmin(self):
+        # Test PyFloat_GetMax()
+        getmin = _testcapi.float_getmin
+
+        self.assertEqual(getmin(), sys.float_info.min)
+
+
+if __name__ == "__main__":
+    unittest.main()
index 2a7d9799ae8bd15ea8ac7cb814f8376007493761..4fcbaf3bb2aa1e1c124541567d0d3c8263de3906 100644 (file)
@@ -2,9 +2,75 @@
 #define PYTESTCAPI_NEED_INTERNAL_API
 
 #include "parts.h"
+#include "util.h"
 #include "clinic/float.c.h"
 
 
+static PyObject *
+float_check(PyObject *Py_UNUSED(module), PyObject *obj)
+{
+    NULLABLE(obj);
+    return PyLong_FromLong(PyFloat_Check(obj));
+}
+
+static PyObject *
+float_checkexact(PyObject *Py_UNUSED(module), PyObject *obj)
+{
+    NULLABLE(obj);
+    return PyLong_FromLong(PyFloat_CheckExact(obj));
+}
+
+static PyObject *
+float_fromstring(PyObject *Py_UNUSED(module), PyObject *obj)
+{
+    NULLABLE(obj);
+    return PyFloat_FromString(obj);
+}
+
+static PyObject *
+float_fromdouble(PyObject *Py_UNUSED(module), PyObject *obj)
+{
+    double d;
+
+    if (!PyArg_Parse(obj, "d", &d)) {
+        return NULL;
+    }
+
+    return PyFloat_FromDouble(d);
+}
+
+static PyObject *
+float_asdouble(PyObject *Py_UNUSED(module), PyObject *obj)
+{
+    double d;
+
+    NULLABLE(obj);
+    d = PyFloat_AsDouble(obj);
+    if (d == -1. && PyErr_Occurred()) {
+        return NULL;
+    }
+
+    return PyFloat_FromDouble(d);
+}
+
+static PyObject *
+float_getinfo(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(arg))
+{
+    return PyFloat_GetInfo();
+}
+
+static PyObject *
+float_getmax(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(arg))
+{
+    return PyFloat_FromDouble(PyFloat_GetMax());
+}
+
+static PyObject *
+float_getmin(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(arg))
+{
+    return PyFloat_FromDouble(PyFloat_GetMin());
+}
+
 /*[clinic input]
 module _testcapi
 [clinic start generated code]*/
@@ -99,6 +165,14 @@ _testcapi_float_unpack_impl(PyObject *module, const char *data,
 }
 
 static PyMethodDef test_methods[] = {
+    {"float_check", float_check, METH_O},
+    {"float_checkexact", float_checkexact, METH_O},
+    {"float_fromstring", float_fromstring, METH_O},
+    {"float_fromdouble", float_fromdouble, METH_O},
+    {"float_asdouble", float_asdouble, METH_O},
+    {"float_getinfo", float_getinfo, METH_NOARGS},
+    {"float_getmax", float_getmax, METH_NOARGS},
+    {"float_getmin", float_getmin, METH_NOARGS},
     _TESTCAPI_FLOAT_PACK_METHODDEF
     _TESTCAPI_FLOAT_UNPACK_METHODDEF
     {NULL},