summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/c-api/concrete.rst1
-rw-r--r--Doc/c-api/decimal.rst231
-rw-r--r--Include/pydecimal.h182
-rw-r--r--Lib/test/test_decimal.py176
-rw-r--r--Misc/NEWS.d/next/Library/2021-03-21-17-02-52.bpo-43422.POk6cU.rst1
-rw-r--r--Modules/_decimal/_decimal.c200
-rw-r--r--Modules/_decimal/tests/bench.py9
-rw-r--r--Modules/_decimal/tests/deccheck.py81
-rw-r--r--Modules/_decimal/tests/formathelper.py5
-rw-r--r--Modules/_testcapimodule.c253
10 files changed, 20 insertions, 1119 deletions
diff --git a/Doc/c-api/concrete.rst b/Doc/c-api/concrete.rst
index bf263d6..c1d9fa1 100644
--- a/Doc/c-api/concrete.rst
+++ b/Doc/c-api/concrete.rst
@@ -115,4 +115,3 @@ Other Objects
coro.rst
contextvars.rst
datetime.rst
- decimal.rst
diff --git a/Doc/c-api/decimal.rst b/Doc/c-api/decimal.rst
deleted file mode 100644
index 94cc4a7..0000000
--- a/Doc/c-api/decimal.rst
+++ /dev/null
@@ -1,231 +0,0 @@
-.. sectionauthor:: Stefan Krah
-
-.. highlight:: c
-
-
-Decimal capsule API
-===================
-
-Capsule API functions can be used in the same manner as regular library
-functions, provided that the API has been initialized.
-
-
-Initialize
-----------
-
-Typically, a C extension module that uses the decimal API will do these
-steps in its init function:
-
-.. code-block:: c
-
- #include "pydecimal.h"
-
- static int decimal_initialized = 0;
- if (!decimal_initialized) {
- if (import_decimal() < 0) {
- return NULL;
- }
-
- decimal_initialized = 1;
- }
-
-
-Type checking, predicates, accessors
-------------------------------------
-
-.. c:function:: int PyDec_TypeCheck(const PyObject *dec)
-
- Return 1 if ``dec`` is a Decimal, 0 otherwise. This function does not set
- any exceptions.
-
-
-.. c:function:: int PyDec_IsSpecial(const PyObject *dec)
-
- Return 1 if ``dec`` is ``NaN``, ``sNaN`` or ``Infinity``, 0 otherwise.
-
- Set TypeError and return -1 if ``dec`` is not a Decimal. It is guaranteed that
- this is the only failure mode, so if ``dec`` has already been type-checked, no
- errors can occur and the function can be treated as a simple predicate.
-
-
-.. c:function:: int PyDec_IsNaN(const PyObject *dec)
-
- Return 1 if ``dec`` is ``NaN`` or ``sNaN``, 0 otherwise.
-
- Set TypeError and return -1 if ``dec`` is not a Decimal. It is guaranteed that
- this is the only failure mode, so if ``dec`` has already been type-checked, no
- errors can occur and the function can be treated as a simple predicate.
-
-
-.. c:function:: int PyDec_IsInfinite(const PyObject *dec)
-
- Return 1 if ``dec`` is ``Infinity``, 0 otherwise.
-
- Set TypeError and return -1 if ``dec`` is not a Decimal. It is guaranteed that
- this is the only failure mode, so if ``dec`` has already been type-checked, no
- errors can occur and the function can be treated as a simple predicate.
-
-
-.. c:function:: int64_t PyDec_GetDigits(const PyObject *dec)
-
- Return the number of digits in the coefficient. For ``Infinity``, the
- number of digits is always zero. Typically, the same applies to ``NaN``
- and ``sNaN``, but both of these can have a payload that is equivalent to
- a coefficient. Therefore, ``NaNs`` can have a nonzero return value.
-
- Set TypeError and return -1 if ``dec`` is not a Decimal. It is guaranteed that
- this is the only failure mode, so if ``dec`` has already been type-checked, no
- errors can occur and the function can be treated as a simple accessor.
-
-
-Exact conversions between decimals and primitive C types
---------------------------------------------------------
-
-This API supports conversions for decimals with a coefficient up to 38 digits.
-
-Data structures
-~~~~~~~~~~~~~~~
-
-The conversion functions use the following status codes and data structures:
-
-.. code-block:: c
-
- /* status cases for getting a triple */
- enum mpd_triple_class {
- MPD_TRIPLE_NORMAL,
- MPD_TRIPLE_INF,
- MPD_TRIPLE_QNAN,
- MPD_TRIPLE_SNAN,
- MPD_TRIPLE_ERROR,
- };
-
- typedef struct {
- enum mpd_triple_class tag;
- uint8_t sign;
- uint64_t hi;
- uint64_t lo;
- int64_t exp;
- } mpd_uint128_triple_t;
-
-The status cases are explained below. ``sign`` is 0 for positive and 1 for negative.
-``((uint128_t)hi << 64) + lo`` is the coefficient, ``exp`` is the exponent.
-
-The data structure is called "triple" because the decimal triple (sign, coeff, exp)
-is an established term and (``hi``, ``lo``) represents a single ``uint128_t`` coefficient.
-
-
-Functions
-~~~~~~~~~
-
-.. c:function:: mpd_uint128_triple_t PyDec_AsUint128Triple(const PyObject *dec)
-
- Convert a decimal to a triple. As above, it is guaranteed that the only
- Python failure mode is a TypeError, checks can be omitted if the type is
- known.
-
- For simplicity, the usage of the function and all special cases are
- explained in code form and comments:
-
-.. code-block:: c
-
- triple = PyDec_AsUint128Triple(dec);
- switch (triple.tag) {
- case MPD_TRIPLE_QNAN:
- /*
- * Success: handle a quiet NaN.
- * 1) triple.sign is 0 or 1.
- * 2) triple.exp is always 0.
- * 3) If triple.hi or triple.lo are nonzero, the NaN has a payload.
- */
- break;
-
- case MPD_TRIPLE_SNAN:
- /*
- * Success: handle a signaling NaN.
- * 1) triple.sign is 0 or 1.
- * 2) triple.exp is always 0.
- * 3) If triple.hi or triple.lo are nonzero, the sNaN has a payload.
- */
- break;
-
- case MPD_TRIPLE_INF:
- /*
- * Success: handle Infinity.
- * 1) triple.sign is 0 or 1.
- * 2) triple.exp is always 0.
- * 3) triple.hi and triple.lo are always zero.
- */
- break;
-
- case MPD_TRIPLE_NORMAL:
- /* Success: handle a finite value. */
- break;
-
- case MPD_TRIPLE_ERROR:
- /* TypeError check: can be omitted if the type of dec is known. */
- if (PyErr_Occurred()) {
- return NULL;
- }
-
- /* Too large for conversion. PyDec_AsUint128Triple() does not set an
- exception so applications can choose themselves. Typically this
- would be a ValueError. */
- PyErr_SetString(PyExc_ValueError,
- "value out of bounds for a uint128 triple");
- return NULL;
- }
-
-.. c:function:: PyObject *PyDec_FromUint128Triple(const mpd_uint128_triple_t *triple)
-
- Create a decimal from a triple. The following rules must be observed for
- initializing the triple:
-
- 1) ``triple.sign`` must always be 0 (for positive) or 1 (for negative).
-
- 2) ``MPD_TRIPLE_QNAN``: ``triple.exp`` must be 0. If ``triple.hi`` or ``triple.lo``
- are nonzero, create a ``NaN`` with a payload.
-
- 3) ``MPD_TRIPLE_SNAN``: ``triple.exp`` must be 0. If ``triple.hi`` or ``triple.lo``
- are nonzero, create an ``sNaN`` with a payload.
-
- 4) ``MPD_TRIPLE_INF``: ``triple.exp``, ``triple.hi`` and ``triple.lo`` must be zero.
-
- 5) ``MPD_TRIPLE_NORMAL``: ``MPD_MIN_ETINY + 38 < triple.exp < MPD_MAX_EMAX - 38``.
- ``triple.hi`` and ``triple.lo`` can be chosen freely.
-
- 6) ``MPD_TRIPLE_ERROR``: It is always an error to set this tag.
-
-
- If one of the above conditions is not met, the function returns ``NaN`` if
- the ``InvalidOperation`` trap is not set in the thread local context. Otherwise,
- it sets the ``InvalidOperation`` exception and returns NULL.
-
- Additionally, though extremely unlikely give the small allocation sizes,
- the function can set ``MemoryError`` and return ``NULL``.
-
-
-Advanced API
-------------
-
-This API enables the use of ``libmpdec`` functions. Since Python is compiled with
-hidden symbols, the API requires an external libmpdec and the ``mpdecimal.h``
-header.
-
-
-Functions
-~~~~~~~~~
-
-.. c:function:: PyObject *PyDec_Alloc(void)
-
- Return a new decimal that can be used in the ``result`` position of ``libmpdec``
- functions.
-
-.. c:function:: mpd_t *PyDec_Get(PyObject *v)
-
- Get a pointer to the internal ``mpd_t`` of the decimal. Decimals are immutable,
- so this function must only be used on a new Decimal that has been created by
- PyDec_Alloc().
-
-.. c:function:: const mpd_t *PyDec_GetConst(const PyObject *v)
-
- Get a pointer to the constant internal ``mpd_t`` of the decimal.
diff --git a/Include/pydecimal.h b/Include/pydecimal.h
deleted file mode 100644
index f82e41f..0000000
--- a/Include/pydecimal.h
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (c) 2020 Stefan Krah. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-
-#ifndef CPYTHON_DECIMAL_H_
-#define CPYTHON_DECIMAL_H_
-
-
-#include <Python.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/****************************************************************************/
-/* Libmpdec API */
-/****************************************************************************/
-
-#ifndef LIBMPDEC_MPDECIMAL_H_
-struct mpd_t; /* ABI-stable in the libmpdec-2.x series */
-
-/* status cases for getting a triple */
-enum mpd_triple_class {
- MPD_TRIPLE_NORMAL,
- MPD_TRIPLE_INF,
- MPD_TRIPLE_QNAN,
- MPD_TRIPLE_SNAN,
- MPD_TRIPLE_ERROR,
-};
-
-typedef struct {
- enum mpd_triple_class tag;
- uint8_t sign;
- uint64_t hi;
- uint64_t lo;
- int64_t exp;
-} mpd_uint128_triple_t;
-#endif
-
-
-/****************************************************************************/
-/* Capsule API */
-/****************************************************************************/
-
-#define PyDec_CAPSULE_NAME "_decimal._API"
-
-/* Simple API */
-#define PyDec_TypeCheck_INDEX 0
-#define PyDec_TypeCheck_RETURN int
-#define PyDec_TypeCheck_ARGS (const PyObject *)
-
-#define PyDec_IsSpecial_INDEX 1
-#define PyDec_IsSpecial_RETURN int
-#define PyDec_IsSpecial_ARGS (const PyObject *)
-
-#define PyDec_IsNaN_INDEX 2
-#define PyDec_IsNaN_RETURN int
-#define PyDec_IsNaN_ARGS (const PyObject *)
-
-#define PyDec_IsInfinite_INDEX 3
-#define PyDec_IsInfinite_RETURN int
-#define PyDec_IsInfinite_ARGS (const PyObject *)
-
-#define PyDec_GetDigits_INDEX 4
-#define PyDec_GetDigits_RETURN int64_t
-#define PyDec_GetDigits_ARGS (const PyObject *)
-
-#define PyDec_AsUint128Triple_INDEX 5
-#define PyDec_AsUint128Triple_RETURN mpd_uint128_triple_t
-#define PyDec_AsUint128Triple_ARGS (const PyObject *)
-
-#define PyDec_FromUint128Triple_INDEX 6
-#define PyDec_FromUint128Triple_RETURN PyObject *
-#define PyDec_FromUint128Triple_ARGS (const mpd_uint128_triple_t *triple)
-
-/* Advanced API */
-#define PyDec_Alloc_INDEX 7
-#define PyDec_Alloc_RETURN PyObject *
-#define PyDec_Alloc_ARGS (void)
-
-#define PyDec_Get_INDEX 8
-#define PyDec_Get_RETURN mpd_t *
-#define PyDec_Get_ARGS (PyObject *)
-
-#define PyDec_GetConst_INDEX 9
-#define PyDec_GetConst_RETURN const mpd_t *
-#define PyDec_GetConst_ARGS (const PyObject *)
-
-#define CPYTHON_DECIMAL_MAX_API 10
-
-
-#ifdef CPYTHON_DECIMAL_MODULE
-/* Simple API */
-static PyDec_TypeCheck_RETURN PyDec_TypeCheck PyDec_TypeCheck_ARGS;
-static PyDec_IsSpecial_RETURN PyDec_IsSpecial PyDec_IsSpecial_ARGS;
-static PyDec_IsNaN_RETURN PyDec_IsNaN PyDec_IsNaN_ARGS;
-static PyDec_IsInfinite_RETURN PyDec_IsInfinite PyDec_IsInfinite_ARGS;
-static PyDec_GetDigits_RETURN PyDec_GetDigits PyDec_GetDigits_ARGS;
-static PyDec_AsUint128Triple_RETURN PyDec_AsUint128Triple PyDec_AsUint128Triple_ARGS;
-static PyDec_FromUint128Triple_RETURN PyDec_FromUint128Triple PyDec_FromUint128Triple_ARGS;
-
-/* Advanced API */
-static PyDec_Alloc_RETURN PyDec_Alloc PyDec_Alloc_ARGS;
-static PyDec_Get_RETURN PyDec_Get PyDec_Get_ARGS;
-static PyDec_GetConst_RETURN PyDec_GetConst PyDec_GetConst_ARGS;
-#else
-static void **_decimal_api;
-
-/* Simple API */
-#define PyDec_TypeCheck \
- (*(PyDec_TypeCheck_RETURN (*)PyDec_TypeCheck_ARGS) _decimal_api[PyDec_TypeCheck_INDEX])
-
-#define PyDec_IsSpecial \
- (*(PyDec_IsSpecial_RETURN (*)PyDec_IsSpecial_ARGS) _decimal_api[PyDec_IsSpecial_INDEX])
-
-#define PyDec_IsNaN \
- (*(PyDec_IsNaN_RETURN (*)PyDec_IsNaN_ARGS) _decimal_api[PyDec_IsNaN_INDEX])
-
-#define PyDec_IsInfinite \
- (*(PyDec_IsInfinite_RETURN (*)PyDec_IsInfinite_ARGS) _decimal_api[PyDec_IsInfinite_INDEX])
-
-#define PyDec_GetDigits \
- (*(PyDec_GetDigits_RETURN (*)PyDec_GetDigits_ARGS) _decimal_api[PyDec_GetDigits_INDEX])
-
-#define PyDec_AsUint128Triple \
- (*(PyDec_AsUint128Triple_RETURN (*)PyDec_AsUint128Triple_ARGS) _decimal_api[PyDec_AsUint128Triple_INDEX])
-
-#define PyDec_FromUint128Triple \
- (*(PyDec_FromUint128Triple_RETURN (*)PyDec_FromUint128Triple_ARGS) _decimal_api[PyDec_FromUint128Triple_INDEX])
-
-/* Advanced API */
-#define PyDec_Alloc \
- (*(PyDec_Alloc_RETURN (*)PyDec_Alloc_ARGS) _decimal_api[PyDec_Alloc_INDEX])
-
-#define PyDec_Get \
- (*(PyDec_Get_RETURN (*)PyDec_Get_ARGS) _decimal_api[PyDec_Get_INDEX])
-
-#define PyDec_GetConst \
- (*(PyDec_GetConst_RETURN (*)PyDec_GetConst_ARGS) _decimal_api[PyDec_GetConst_INDEX])
-
-
-static int
-import_decimal(void)
-{
- _decimal_api = (void **)PyCapsule_Import(PyDec_CAPSULE_NAME, 0);
- if (_decimal_api == NULL) {
- return -1;
- }
-
- return 0;
-}
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* CPYTHON_DECIMAL_H_ */
diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py
index dbd58e8..6aa4a9b 100644
--- a/Lib/test/test_decimal.py
+++ b/Lib/test/test_decimal.py
@@ -43,13 +43,6 @@ import random
import inspect
import threading
-from _testcapi import decimal_is_special
-from _testcapi import decimal_is_nan
-from _testcapi import decimal_is_infinite
-from _testcapi import decimal_get_digits
-from _testcapi import decimal_as_triple
-from _testcapi import decimal_from_triple
-
C = import_fresh_module('decimal', fresh=['_decimal'])
P = import_fresh_module('decimal', blocked=['_decimal'])
@@ -4758,175 +4751,6 @@ class CFunctionality(unittest.TestCase):
self.assertEqual(C.DecTraps,
C.DecErrors|C.DecOverflow|C.DecUnderflow)
- def test_decimal_api_predicates(self):
- # Capsule API
-
- d = C.Decimal("0")
- self.assertFalse(decimal_is_special(d))
- self.assertFalse(decimal_is_nan(d))
- self.assertFalse(decimal_is_infinite(d))
-
- d = C.Decimal("NaN")
- self.assertTrue(decimal_is_special(d))
- self.assertTrue(decimal_is_nan(d))
- self.assertFalse(decimal_is_infinite(d))
-
- d = C.Decimal("sNaN")
- self.assertTrue(decimal_is_special(d))
- self.assertTrue(decimal_is_nan(d))
- self.assertFalse(decimal_is_infinite(d))
-
- d = C.Decimal("inf")
- self.assertTrue(decimal_is_special(d))
- self.assertFalse(decimal_is_nan(d))
- self.assertTrue(decimal_is_infinite(d))
-
- def test_decimal_api_get_digits(self):
- # Capsule API
-
- d = C.Decimal("0")
- self.assertEqual(decimal_get_digits(d), 1)
-
- d = C.Decimal("1234567890")
- self.assertEqual(decimal_get_digits(d), 10)
-
- d = C.Decimal("inf")
- self.assertEqual(decimal_get_digits(d), 0)
-
- d = C.Decimal("NaN")
- self.assertEqual(decimal_get_digits(d), 0)
-
- d = C.Decimal("sNaN")
- self.assertEqual(decimal_get_digits(d), 0)
-
- d = C.Decimal("NaN1234567890")
- self.assertEqual(decimal_get_digits(d), 10)
-
- d = C.Decimal("sNaN1234567890")
- self.assertEqual(decimal_get_digits(d), 10)
-
- def test_decimal_api_triple(self):
- # Capsule API
-
- def as_triple(d):
- """Convert a decimal to a decimal triple with a split uint128_t
- coefficient:
-
- (sign, hi, lo, exp)
-
- It is called 'triple' because (hi, lo) are regarded as a single
- uint128_t that is split because not all compilers support uint128_t.
- """
- sign, digits, exp = d.as_tuple()
-
- s = "".join(str(d) for d in digits)
- coeff = int(s) if s else 0
-
- if coeff < 0 or coeff >= 2**128:
- raise ValueError("value out of bounds for a uint128 triple");
-
- hi, lo = divmod(coeff, 2**64)
- return (sign, hi, lo, exp)
-
- def from_triple(triple):
- """Convert a decimal triple with a split uint128_t coefficient to a string.
- """
- sign, hi, lo, exp = triple
- coeff = hi * 2**64 + lo
-
- if coeff < 0 or coeff >= 2**128:
- raise ValueError("value out of bounds for a uint128 triple");
-
- digits = tuple(int(c) for c in str(coeff))
-
- return P.Decimal((sign, digits, exp))
-
- signs = ["", "-"]
-
- coefficients = [
- "000000000000000000000000000000000000000",
-
- "299999999999999999999999999999999999999",
- "299999999999999999990000000000000000000",
- "200000000000000000009999999999999999999",
- "000000000000000000009999999999999999999",
-
- "299999999999999999999999999999000000000",
- "299999999999999999999000000000999999999",
- "299999999999000000000999999999999999999",
- "299000000000999999999999999999999999999",
- "000999999999999999999999999999999999999",
-
- "300000000000000000000000000000000000000",
- "310000000000000000001000000000000000000",
- "310000000000000000000000000000000000000",
- "300000000000000000001000000000000000000",
-
- "340100000000100000000100000000100000000",
- "340100000000100000000100000000000000000",
- "340100000000100000000000000000100000000",
- "340100000000000000000100000000100000000",
- "340000000000100000000100000000100000000",
-
- "340282366920938463463374607431768211455",
- ]
-
- exponents = [
- "E+0", "E+1", "E-1",
- "E+%s" % str(C.MAX_EMAX-38),
- "E-%s" % str(C.MIN_ETINY+38),
- ]
-
- for sign in signs:
- for coeff in coefficients:
- for exp in exponents:
- s = sign + coeff + exp
-
- ctriple = decimal_as_triple(C.Decimal(s))
- ptriple = as_triple(P.Decimal(s))
- self.assertEqual(ctriple, ptriple)
-
- c = decimal_from_triple(ctriple)
- p = decimal_from_triple(ptriple)
- self.assertEqual(str(c), str(p))
-
- for s in ["NaN", "-NaN", "sNaN", "-sNaN", "NaN123", "sNaN123", "inf", "-inf"]:
- ctriple = decimal_as_triple(C.Decimal(s))
- ptriple = as_triple(P.Decimal(s))
- self.assertEqual(ctriple, ptriple)
-
- c = decimal_from_triple(ctriple)
- p = decimal_from_triple(ptriple)
- self.assertEqual(str(c), str(p))
-
- def test_decimal_api_errors(self):
- # Capsule API
-
- self.assertRaises(TypeError, decimal_as_triple, "X")
- self.assertRaises(ValueError, decimal_as_triple, C.Decimal(2**128))
- self.assertRaises(ValueError, decimal_as_triple, C.Decimal(-2**128))
-
- self.assertRaises(TypeError, decimal_from_triple, "X")
- self.assertRaises(ValueError, decimal_from_triple, ())
- self.assertRaises(ValueError, decimal_from_triple, (1, 2, 3, 4, 5))
- self.assertRaises(ValueError, decimal_from_triple, (2**8, 0, 0, 0))
- self.assertRaises(OverflowError, decimal_from_triple, (0, 2**64, 0, 0))
- self.assertRaises(OverflowError, decimal_from_triple, (0, 0, 2**64, 0))
- self.assertRaises(OverflowError, decimal_from_triple, (0, 0, 0, 2**63))
- self.assertRaises(OverflowError, decimal_from_triple, (0, 0, 0, -2**63-1))
- self.assertRaises(ValueError, decimal_from_triple, (0, 0, 0, "X"))
- self.assertRaises(TypeError, decimal_from_triple, (0, 0, 0, ()))
-
- with C.localcontext(C.Context()):
- self.assertRaises(C.InvalidOperation, decimal_from_triple, (2, 0, 0, 0))
- self.assertRaises(C.InvalidOperation, decimal_from_triple, (0, 0, 0, 2**63-1))
- self.assertRaises(C.InvalidOperation, decimal_from_triple, (0, 0, 0, -2**63))
-
- self.assertRaises(TypeError, decimal_is_special, "X")
- self.assertRaises(TypeError, decimal_is_nan, "X")
- self.assertRaises(TypeError, decimal_is_infinite, "X")
- self.assertRaises(TypeError, decimal_get_digits, "X")
-
class CWhitebox(unittest.TestCase):
"""Whitebox testing for _decimal"""
diff --git a/Misc/NEWS.d/next/Library/2021-03-21-17-02-52.bpo-43422.POk6cU.rst b/Misc/NEWS.d/next/Library/2021-03-21-17-02-52.bpo-43422.POk6cU.rst
new file mode 100644
index 0000000..4de3060
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2021-03-21-17-02-52.bpo-43422.POk6cU.rst
@@ -0,0 +1 @@
+Revert the _decimal C API which was added in bpo-41324.
diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c
index 664d45a..83e237d 100644
--- a/Modules/_decimal/_decimal.c
+++ b/Modules/_decimal/_decimal.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008-2020 Stefan Krah. All rights reserved.
+ * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -33,8 +33,6 @@
#include <stdlib.h>
-#define CPYTHON_DECIMAL_MODULE
-#include "pydecimal.h"
#include "docstrings.h"
@@ -5570,175 +5568,6 @@ static PyTypeObject PyDecContext_Type =
};
-/****************************************************************************/
-/* C-API */
-/****************************************************************************/
-
-/* Simple API */
-static int
-PyDec_TypeCheck(const PyObject *v)
-{
- return PyDec_Check(v);
-}
-
-static int
-PyDec_IsSpecial(const PyObject *v)
-{
- if (!PyDec_Check(v)) {
- PyErr_SetString(PyExc_TypeError,
- "PyDec_IsSpecial: argument must be a Decimal");
- return -1;
- }
-
- return mpd_isspecial(MPD(v));
-}
-
-static int
-PyDec_IsNaN(const PyObject *v)
-{
- if (!PyDec_Check(v)) {
- PyErr_SetString(PyExc_TypeError,
- "PyDec_IsNaN: argument must be a Decimal");
- return -1;
- }
-
- return mpd_isnan(MPD(v));
-}
-
-static int
-PyDec_IsInfinite(const PyObject *v)
-{
- if (!PyDec_Check(v)) {
- PyErr_SetString(PyExc_TypeError,
- "PyDec_IsInfinite: argument must be a Decimal");
- return -1;
- }
-
- return mpd_isinfinite(MPD(v));
-}
-
-static int64_t
-PyDec_GetDigits(const PyObject *v)
-{
- if (!PyDec_Check(v)) {
- PyErr_SetString(PyExc_TypeError,
- "PyDec_GetDigits: argument must be a Decimal");
- return -1;
- }
-
- return MPD(v)->digits;
-}
-
-static mpd_uint128_triple_t
-PyDec_AsUint128Triple(const PyObject *v)
-{
- if (!PyDec_Check(v)) {
- mpd_uint128_triple_t triple = { MPD_TRIPLE_ERROR, 0, 0, 0, 0 };
- PyErr_SetString(PyExc_TypeError,
- "PyDec_AsUint128Triple: argument must be a Decimal");
- return triple;
- }
-
- return mpd_as_uint128_triple(MPD(v));
-}
-
-static PyObject *
-PyDec_FromUint128Triple(const mpd_uint128_triple_t *triple)
-{
- PyObject *context;
- PyObject *result;
- uint32_t status = 0;
-
- CURRENT_CONTEXT(context);
-
- result = dec_alloc();
- if (result == NULL) {
- return NULL;
- }
-
- if (mpd_from_uint128_triple(MPD(result), triple, &status) < 0) {
- if (dec_addstatus(context, status)) {
- Py_DECREF(result);
- return NULL;
- }
- }
-
- return result;
-}
-
-/* Advanced API */
-static PyObject *
-PyDec_Alloc(void)
-{
- return dec_alloc();
-}
-
-static mpd_t *
-PyDec_Get(PyObject *v)
-{
- if (!PyDec_Check(v)) {
- PyErr_SetString(PyExc_TypeError,
- "PyDec_Get: argument must be a Decimal");
- return NULL;
- }
-
- return MPD(v);
-}
-
-static const mpd_t *
-PyDec_GetConst(const PyObject *v)
-{
- if (!PyDec_Check(v)) {
- PyErr_SetString(PyExc_TypeError,
- "PyDec_GetConst: argument must be a Decimal");
- return NULL;
- }
-
- return MPD(v);
-}
-
-static void
-destroy_api(PyObject *capsule)
-{
- void *capi = PyCapsule_GetPointer(capsule, PyDec_CAPSULE_NAME);
- PyMem_Free(capi);
-}
-
-static PyObject *
-init_api(void)
-{
- void **_decimal_api = PyMem_Calloc(CPYTHON_DECIMAL_MAX_API, sizeof(void *));
- if (_decimal_api == NULL) {
- PyErr_NoMemory();
- return NULL;
- }
-
- /* Simple API */
- _decimal_api[PyDec_TypeCheck_INDEX] = (void *)PyDec_TypeCheck;
- _decimal_api[PyDec_IsSpecial_INDEX] = (void *)PyDec_IsSpecial;
- _decimal_api[PyDec_IsNaN_INDEX] = (void *)PyDec_IsNaN;
- _decimal_api[PyDec_IsInfinite_INDEX] = (void *)PyDec_IsInfinite;
- _decimal_api[PyDec_GetDigits_INDEX] = (void *)PyDec_GetDigits;
- _decimal_api[PyDec_AsUint128Triple_INDEX] = (void *)PyDec_AsUint128Triple;
- _decimal_api[PyDec_FromUint128Triple_INDEX] = (void *)PyDec_FromUint128Triple;
-
- /* Advanced API */
- _decimal_api[PyDec_Alloc_INDEX] = (void *)PyDec_Alloc;
- _decimal_api[PyDec_Get_INDEX] = (void *)PyDec_Get;
- _decimal_api[PyDec_GetConst_INDEX] = (void *)PyDec_GetConst;
-
- PyObject *capsule = PyCapsule_New(_decimal_api, PyDec_CAPSULE_NAME, destroy_api);
- if (!capsule) {
- PyMem_Free(_decimal_api);
- }
- return capsule;
-}
-
-
-/****************************************************************************/
-/* Module */
-/****************************************************************************/
-
static PyMethodDef _decimal_methods [] =
{
{ "getcontext", (PyCFunction)PyDec_GetCurrentContext, METH_NOARGS, doc_getcontext},
@@ -5849,27 +5678,17 @@ PyInit__decimal(void)
DecCondMap *cm;
struct ssize_constmap *ssize_cm;
struct int_constmap *int_cm;
- static PyObject *capsule = NULL;
- static int initialized = 0;
int i;
/* Init libmpdec */
- if (!initialized) {
- mpd_traphandler = dec_traphandler;
- mpd_mallocfunc = PyMem_Malloc;
- mpd_reallocfunc = PyMem_Realloc;
- mpd_callocfunc = mpd_callocfunc_em;
- mpd_free = PyMem_Free;
- mpd_setminalloc(_Py_DEC_MINALLOC);
-
- capsule = init_api();
- if (capsule == NULL) {
- return NULL;
- }
+ mpd_traphandler = dec_traphandler;
+ mpd_mallocfunc = PyMem_Malloc;
+ mpd_reallocfunc = PyMem_Realloc;
+ mpd_callocfunc = mpd_callocfunc_em;
+ mpd_free = PyMem_Free;
+ mpd_setminalloc(_Py_DEC_MINALLOC);
- initialized = 1;
- }
/* Init external C-API functions */
_py_long_multiply = PyLong_Type.tp_as_number->nb_multiply;
@@ -6094,10 +5913,6 @@ PyInit__decimal(void)
CHECK_INT(PyModule_AddStringConstant(m, "__version__", "1.70"));
CHECK_INT(PyModule_AddStringConstant(m, "__libmpdec_version__", mpd_version()));
- /* Add capsule API */
- if (PyModule_AddObjectRef(m, "_API", capsule) < 0) {
- goto error;
- }
return m;
@@ -6121,7 +5936,6 @@ error:
Py_CLEAR(basic_context_template); /* GCOV_NOT_REACHED */
Py_CLEAR(extended_context_template); /* GCOV_NOT_REACHED */
Py_CLEAR(m); /* GCOV_NOT_REACHED */
- Py_CLEAR(capsule); /* GCOV_NOT_REACHED */
return NULL; /* GCOV_NOT_REACHED */
}
diff --git a/Modules/_decimal/tests/bench.py b/Modules/_decimal/tests/bench.py
index 88fd7b5..3726db1 100644
--- a/Modules/_decimal/tests/bench.py
+++ b/Modules/_decimal/tests/bench.py
@@ -7,10 +7,13 @@
import time
+try:
+ from test.support import import_fresh_module
+except ImportError:
+ from test.test_support import import_fresh_module
-import _decimal as C
-import _pydecimal as P
-
+C = import_fresh_module('decimal', fresh=['_decimal'])
+P = import_fresh_module('decimal', blocked=['_decimal'])
#
# NOTE: This is the pi function from the decimal documentation, modified
diff --git a/Modules/_decimal/tests/deccheck.py b/Modules/_decimal/tests/deccheck.py
index 5de57d1..98ecd50 100644
--- a/Modules/_decimal/tests/deccheck.py
+++ b/Modules/_decimal/tests/deccheck.py
@@ -47,18 +47,14 @@ from subprocess import PIPE, STDOUT
from queue import Queue, Empty
from threading import Thread, Event, Lock
+from test.support import import_fresh_module
from randdec import randfloat, all_unary, all_binary, all_ternary
from randdec import unary_optarg, binary_optarg, ternary_optarg
from formathelper import rand_format, rand_locale
from _pydecimal import _dec_from_triple
-from _testcapi import decimal_as_triple
-from _testcapi import decimal_from_triple
-
-import _decimal as C
-import _pydecimal as P
-
-
+C = import_fresh_module('decimal', fresh=['_decimal'])
+P = import_fresh_module('decimal', blocked=['_decimal'])
EXIT_STATUS = 0
@@ -162,45 +158,6 @@ TernaryRestricted = ['__pow__', 'context.power']
# ======================================================================
-# Triple tests
-# ======================================================================
-
-def c_as_triple(dec):
- sign, hi, lo, exp = decimal_as_triple(dec)
-
- coeff = hi * 2**64 + lo
- return (sign, coeff, exp)
-
-def c_from_triple(triple):
- sign, coeff, exp = triple
-
- hi = coeff // 2**64
- lo = coeff % 2**64
- return decimal_from_triple((sign, hi, lo, exp))
-
-def p_as_triple(dec):
- sign, digits, exp = dec.as_tuple()
-
- s = "".join(str(d) for d in digits)
- coeff = int(s) if s else 0
-
- if coeff < 0 or coeff >= 2**128:
- raise ValueError("value out of bounds for a uint128 triple")
-
- return (sign, coeff, exp)
-
-def p_from_triple(triple):
- sign, coeff, exp = triple
-
- if coeff < 0 or coeff >= 2**128:
- raise ValueError("value out of bounds for a uint128 triple")
-
- digits = tuple(int(c) for c in str(coeff))
-
- return P.Decimal((sign, digits, exp))
-
-
-# ======================================================================
# Unified Context
# ======================================================================
@@ -893,44 +850,12 @@ def verify(t, stat):
t.presults.append(str(t.rp.imag))
t.presults.append(str(t.rp.real))
- ctriple = None
- if str(t.rc) == str(t.rp): # see skip handler
- try:
- ctriple = c_as_triple(t.rc)
- except ValueError:
- try:
- ptriple = p_as_triple(t.rp)
- except ValueError:
- pass
- else:
- raise RuntimeError("ValueError not raised")
- else:
- cres = c_from_triple(ctriple)
- t.cresults.append(ctriple)
- t.cresults.append(str(cres))
-
- ptriple = p_as_triple(t.rp)
- pres = p_from_triple(ptriple)
- t.presults.append(ptriple)
- t.presults.append(str(pres))
-
if t.with_maxcontext and isinstance(t.rmax, C.Decimal):
t.maxresults.append(t.rmax.to_eng_string())
t.maxresults.append(t.rmax.as_tuple())
t.maxresults.append(str(t.rmax.imag))
t.maxresults.append(str(t.rmax.real))
- if ctriple is not None:
- # NaN payloads etc. depend on precision and clamp.
- if all_nan(t.rc) and all_nan(t.rmax):
- t.maxresults.append(ctriple)
- t.maxresults.append(str(cres))
- else:
- maxtriple = c_as_triple(t.rmax)
- maxres = c_from_triple(maxtriple)
- t.maxresults.append(maxtriple)
- t.maxresults.append(str(maxres))
-
nc = t.rc.number_class().lstrip('+-s')
stat[nc] += 1
else:
diff --git a/Modules/_decimal/tests/formathelper.py b/Modules/_decimal/tests/formathelper.py
index 482e02a..19b2aad 100644
--- a/Modules/_decimal/tests/formathelper.py
+++ b/Modules/_decimal/tests/formathelper.py
@@ -31,10 +31,11 @@
import os, sys, locale, random
import platform, subprocess
+from test.support import import_fresh_module
from distutils.spawn import find_executable
-import _decimal as C
-import _pydecimal as P
+C = import_fresh_module('decimal', fresh=['_decimal'])
+P = import_fresh_module('decimal', blocked=['_decimal'])
windows_lang_strings = [
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index ed59c32..20107f2 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -19,7 +19,6 @@
#include "Python.h"
#include "datetime.h"
-#include "pydecimal.h"
#include "marshal.h"
#include "structmember.h" // PyMemberDef
#include <float.h>
@@ -2764,252 +2763,6 @@ test_PyDateTime_DELTA_GET(PyObject *self, PyObject *obj)
return Py_BuildValue("(lll)", days, seconds, microseconds);
}
-/* Test decimal API */
-static int decimal_initialized = 0;
-static PyObject *
-decimal_is_special(PyObject *module, PyObject *dec)
-{
- int is_special;
-
- (void)module;
- if (!decimal_initialized) {
- if (import_decimal() < 0) {
- return NULL;
- }
-
- decimal_initialized = 1;
- }
-
- is_special = PyDec_IsSpecial(dec);
- if (is_special < 0) {
- return NULL;
- }
-
- return PyBool_FromLong(is_special);
-}
-
-static PyObject *
-decimal_is_nan(PyObject *module, PyObject *dec)
-{
- int is_nan;
-
- (void)module;
- if (!decimal_initialized) {
- if (import_decimal() < 0) {
- return NULL;
- }
-
- decimal_initialized = 1;
- }
-
- is_nan = PyDec_IsNaN(dec);
- if (is_nan < 0) {
- return NULL;
- }
-
- return PyBool_FromLong(is_nan);
-}
-
-static PyObject *
-decimal_is_infinite(PyObject *module, PyObject *dec)
-{
- int is_infinite;
-
- (void)module;
- if (!decimal_initialized) {
- if (import_decimal() < 0) {
- return NULL;
- }
-
- decimal_initialized = 1;
- }
-
- is_infinite = PyDec_IsInfinite(dec);
- if (is_infinite < 0) {
- return NULL;
- }
-
- return PyBool_FromLong(is_infinite);
-}
-
-static PyObject *
-decimal_get_digits(PyObject *module, PyObject *dec)
-{
- int64_t digits;
-
- (void)module;
- if (!decimal_initialized) {
- if (import_decimal() < 0) {
- return NULL;
- }
-
- decimal_initialized = 1;
- }
-
- digits = PyDec_GetDigits(dec);
- if (digits < 0) {
- return NULL;
- }
-
- return PyLong_FromLongLong(digits);
-}
-
-static PyObject *
-decimal_as_triple(PyObject *module, PyObject *dec)
-{
- PyObject *tuple = NULL;
- PyObject *sign, *hi, *lo;
- mpd_uint128_triple_t triple;
-
- (void)module;
- if (!decimal_initialized) {
- if (import_decimal() < 0) {
- return NULL;
- }
-
- decimal_initialized = 1;
- }
-
- triple = PyDec_AsUint128Triple(dec);
- if (triple.tag == MPD_TRIPLE_ERROR && PyErr_Occurred()) {
- return NULL;
- }
-
- sign = PyLong_FromUnsignedLong(triple.sign);
- if (sign == NULL) {
- return NULL;
- }
-
- hi = PyLong_FromUnsignedLongLong(triple.hi);
- if (hi == NULL) {
- Py_DECREF(sign);
- return NULL;
- }
-
- lo = PyLong_FromUnsignedLongLong(triple.lo);
- if (lo == NULL) {
- Py_DECREF(hi);
- Py_DECREF(sign);
- return NULL;
- }
-
- switch (triple.tag) {
- case MPD_TRIPLE_QNAN:
- assert(triple.exp == 0);
- tuple = Py_BuildValue("(OOOs)", sign, hi, lo, "n");
- break;
-
- case MPD_TRIPLE_SNAN:
- assert(triple.exp == 0);
- tuple = Py_BuildValue("(OOOs)", sign, hi, lo, "N");
- break;
-
- case MPD_TRIPLE_INF:
- assert(triple.hi == 0);
- assert(triple.lo == 0);
- assert(triple.exp == 0);
- tuple = Py_BuildValue("(OOOs)", sign, hi, lo, "F");
- break;
-
- case MPD_TRIPLE_NORMAL:
- tuple = Py_BuildValue("(OOOL)", sign, hi, lo, triple.exp);
- break;
-
- case MPD_TRIPLE_ERROR:
- PyErr_SetString(PyExc_ValueError,
- "value out of bounds for a uint128 triple");
- break;
-
- default:
- PyErr_SetString(PyExc_RuntimeError,
- "decimal_as_triple: internal error: unexpected tag");
- break;
- }
-
- Py_DECREF(lo);
- Py_DECREF(hi);
- Py_DECREF(sign);
-
- return tuple;
-}
-
-static PyObject *
-decimal_from_triple(PyObject *module, PyObject *tuple)
-{
- mpd_uint128_triple_t triple = { MPD_TRIPLE_ERROR, 0, 0, 0, 0 };
- PyObject *exp;
- unsigned long sign;
-
- (void)module;
- if (!decimal_initialized) {
- if (import_decimal() < 0) {
- return NULL;
- }
-
- decimal_initialized = 1;
- }
-
- if (!PyTuple_Check(tuple)) {
- PyErr_SetString(PyExc_TypeError, "argument must be a tuple");
- return NULL;
- }
-
- if (PyTuple_GET_SIZE(tuple) != 4) {
- PyErr_SetString(PyExc_ValueError, "tuple size must be 4");
- return NULL;
- }
-
- sign = PyLong_AsUnsignedLong(PyTuple_GET_ITEM(tuple, 0));
- if (sign == (unsigned long)-1 && PyErr_Occurred()) {
- return NULL;
- }
- if (sign > UINT8_MAX) {
- PyErr_SetString(PyExc_ValueError, "sign must be 0 or 1");
- return NULL;
- }
- triple.sign = (uint8_t)sign;
-
- triple.hi = PyLong_AsUnsignedLongLong(PyTuple_GET_ITEM(tuple, 1));
- if (triple.hi == (unsigned long long)-1 && PyErr_Occurred()) {
- return NULL;
- }
-
- triple.lo = PyLong_AsUnsignedLongLong(PyTuple_GET_ITEM(tuple, 2));
- if (triple.lo == (unsigned long long)-1 && PyErr_Occurred()) {
- return NULL;
- }
-
- exp = PyTuple_GET_ITEM(tuple, 3);
- if (PyLong_Check(exp)) {
- triple.tag = MPD_TRIPLE_NORMAL;
- triple.exp = PyLong_AsLongLong(exp);
- if (triple.exp == -1 && PyErr_Occurred()) {
- return NULL;
- }
- }
- else if (PyUnicode_Check(exp)) {
- if (PyUnicode_CompareWithASCIIString(exp, "F") == 0) {
- triple.tag = MPD_TRIPLE_INF;
- }
- else if (PyUnicode_CompareWithASCIIString(exp, "n") == 0) {
- triple.tag = MPD_TRIPLE_QNAN;
- }
- else if (PyUnicode_CompareWithASCIIString(exp, "N") == 0) {
- triple.tag = MPD_TRIPLE_SNAN;
- }
- else {
- PyErr_SetString(PyExc_ValueError, "not a valid exponent");
- return NULL;
- }
- }
- else {
- PyErr_SetString(PyExc_TypeError, "exponent must be int or string");
- return NULL;
- }
-
- return PyDec_FromUint128Triple(&triple);
-}
-
/* test_thread_state spawns a thread of its own, and that thread releases
* `thread_done` when it's finished. The driver code has to know when the
* thread finishes, because the thread uses a PyObject (the callable) that
@@ -5725,12 +5478,6 @@ static PyMethodDef TestMethods[] = {
{"PyDateTime_DATE_GET", test_PyDateTime_DATE_GET, METH_O},
{"PyDateTime_TIME_GET", test_PyDateTime_TIME_GET, METH_O},
{"PyDateTime_DELTA_GET", test_PyDateTime_DELTA_GET, METH_O},
- {"decimal_is_special", decimal_is_special, METH_O},
- {"decimal_is_nan", decimal_is_nan, METH_O},
- {"decimal_is_infinite", decimal_is_infinite, METH_O},
- {"decimal_get_digits", decimal_get_digits, METH_O},
- {"decimal_as_triple", decimal_as_triple, METH_O},
- {"decimal_from_triple", decimal_from_triple, METH_O},
{"test_list_api", test_list_api, METH_NOARGS},
{"test_dict_iteration", test_dict_iteration, METH_NOARGS},
{"dict_getitem_knownhash", dict_getitem_knownhash, METH_VARARGS},