Commit 59a4a93f authored by Stefan Krah's avatar Stefan Krah

Issue #16422: Use strings for rounding mode constants for better readability

and pickling compatibility.
parent 0ad344a8
This diff is collapsed.
......@@ -150,6 +150,9 @@ Core and Builtins
Library
-------
- Issue #16422: Use strings for rounding mode constants for better readability
and pickling compatibility.
- Issue #15861: tkinter now correctly works with lists and tuples containing
strings with whitespaces, backslashes or unbalanced braces.
......
......@@ -202,6 +202,13 @@ static const char *dec_signal_string[MPD_NUM_FLAGS] = {
"Underflow",
};
#ifdef EXTRA_FUNCTIONALITY
#define _PY_DEC_ROUND_GUARD MPD_ROUND_GUARD
#else
#define _PY_DEC_ROUND_GUARD (MPD_ROUND_GUARD-1)
#endif
static PyObject *round_map[_PY_DEC_ROUND_GUARD];
static const char *invalid_rounding_err =
"valid values for rounding are:\n\
[ROUND_CEILING, ROUND_FLOOR, ROUND_UP, ROUND_DOWN,\n\
......@@ -249,13 +256,6 @@ type_error_int(const char *mesg)
return -1;
}
static PyObject *
type_error_ptr(const char *mesg)
{
PyErr_SetString(PyExc_TypeError, mesg);
return NULL;
}
static int
runtime_error_int(const char *mesg)
{
......@@ -502,6 +502,27 @@ dec_addstatus(PyObject *context, uint32_t status)
return 0;
}
static int
getround(PyObject *v)
{
int i;
if (PyUnicode_Check(v)) {
for (i = 0; i < _PY_DEC_ROUND_GUARD; i++) {
if (v == round_map[i]) {
return i;
}
}
for (i = 0; i < _PY_DEC_ROUND_GUARD; i++) {
if (PyUnicode_Compare(v, round_map[i]) == 0) {
return i;
}
}
}
return type_error_int(invalid_rounding_err);
}
/******************************************************************************/
/* SignalDict Object */
......@@ -715,7 +736,6 @@ context_get##mem(PyObject *self, void *closure UNUSED) \
Dec_CONTEXT_GET_SSIZE(prec)
Dec_CONTEXT_GET_SSIZE(emax)
Dec_CONTEXT_GET_SSIZE(emin)
Dec_CONTEXT_GET_SSIZE(round)
Dec_CONTEXT_GET_SSIZE(clamp)
#ifdef EXTRA_FUNCTIONALITY
......@@ -723,6 +743,15 @@ Dec_CONTEXT_GET_ULONG(traps)
Dec_CONTEXT_GET_ULONG(status)
#endif
static PyObject *
context_getround(PyObject *self, void *closure UNUSED)
{
int i = mpd_getround(CTX(self));
Py_INCREF(round_map[i]);
return round_map[i];
}
static PyObject *
context_getcapitals(PyObject *self, void *closure UNUSED)
{
......@@ -875,17 +904,16 @@ static int
context_setround(PyObject *self, PyObject *value, void *closure UNUSED)
{
mpd_context_t *ctx;
mpd_ssize_t x;
int x;
x = PyLong_AsSsize_t(value);
if (x == -1 && PyErr_Occurred()) {
x = getround(value);
if (x == -1) {
return -1;
}
BOUNDS_CHECK(x, INT_MIN, INT_MAX);
ctx = CTX(self);
if (!mpd_qsetround(ctx, (int)x)) {
return type_error_int(invalid_rounding_err);
if (!mpd_qsetround(ctx, x)) {
INTERNAL_ERROR_INT("context_setround"); /* GCOV_NOT_REACHED */
}
return 0;
......@@ -1207,33 +1235,6 @@ context_dealloc(PyDecContextObject *self)
Py_TYPE(self)->tp_free(self);
}
static int
getround(PyObject *v)
{
const char *s;
long x;
int i;
if (PyLong_Check(v)) {
x = PyLong_AsLong(v);
if (x == -1 && PyErr_Occurred()) {
return -1;
}
BOUNDS_CHECK(x, 0, INT_MAX);
return (int)x;
}
else if (PyUnicode_Check(v)) {
for (i = 0; i < MPD_ROUND_GUARD; i++) {
s = mpd_round_string[i];
if (PyUnicode_CompareWithASCIIString(v, s) == 0) {
return i;
}
}
}
return type_error_int("invalid rounding mode");
}
static int
context_init(PyObject *self, PyObject *args, PyObject *kwds)
{
......@@ -1264,6 +1265,9 @@ context_init(PyObject *self, PyObject *args, PyObject *kwds)
if (prec != Py_None && context_setprec(self, prec, NULL) < 0) {
return -1;
}
if (rounding != Py_None && context_setround(self, rounding, NULL) < 0) {
return -1;
}
if (emin != Py_None && context_setemin(self, emin, NULL) < 0) {
return -1;
}
......@@ -1277,16 +1281,6 @@ context_init(PyObject *self, PyObject *args, PyObject *kwds)
return -1;
}
if (rounding != Py_None) {
int x = getround(rounding);
if (x < 0) {
return -1;
}
if (!mpd_qsetround(CTX(self), x)) {
return type_error_int(invalid_rounding_err);
}
}
if (traps != Py_None) {
if (PyList_Check(traps)) {
ret = context_settraps_list(self, traps);
......@@ -3345,7 +3339,7 @@ PyDec_ToIntegralValue(PyObject *dec, PyObject *args, PyObject *kwds)
return NULL;
}
if (!mpd_qsetround(&workctx, round)) {
return type_error_ptr(invalid_rounding_err);
INTERNAL_ERROR_PTR("PyDec_ToIntegralValue"); /* GCOV_NOT_REACHED */
}
}
......@@ -3386,7 +3380,7 @@ PyDec_ToIntegralExact(PyObject *dec, PyObject *args, PyObject *kwds)
return NULL;
}
if (!mpd_qsetround(&workctx, round)) {
return type_error_ptr(invalid_rounding_err);
INTERNAL_ERROR_PTR("PyDec_ToIntegralExact"); /* GCOV_NOT_REACHED */
}
}
......@@ -4187,7 +4181,7 @@ dec_mpd_qquantize(PyObject *v, PyObject *args, PyObject *kwds)
return NULL;
}
if (!mpd_qsetround(&workctx, round)) {
return type_error_ptr(invalid_rounding_err);
INTERNAL_ERROR_PTR("dec_mpd_qquantize"); /* GCOV_NOT_REACHED */
}
}
......@@ -5419,17 +5413,6 @@ static struct int_constmap int_constants [] = {
{"DECIMAL64", MPD_DECIMAL64},
{"DECIMAL128", MPD_DECIMAL128},
{"IEEE_CONTEXT_MAX_BITS", MPD_IEEE_CONTEXT_MAX_BITS},
#endif
{"ROUND_CEILING", MPD_ROUND_CEILING},
{"ROUND_FLOOR", MPD_ROUND_FLOOR},
{"ROUND_UP", MPD_ROUND_UP},
{"ROUND_DOWN", MPD_ROUND_DOWN},
{"ROUND_HALF_UP", MPD_ROUND_HALF_UP},
{"ROUND_HALF_DOWN", MPD_ROUND_HALF_DOWN},
{"ROUND_HALF_EVEN", MPD_ROUND_HALF_EVEN},
{"ROUND_05UP", MPD_ROUND_05UP},
#ifdef EXTRA_FUNCTIONALITY
{"ROUND_TRUNC", MPD_ROUND_TRUNC},
/* int condition flags */
{"DecClamped", MPD_Clamped},
{"DecConversionSyntax", MPD_Conversion_syntax},
......@@ -5680,6 +5663,13 @@ PyInit__decimal(void)
int_cm->val));
}
/* Init string constants */
for (i = 0; i < _PY_DEC_ROUND_GUARD; i++) {
ASSIGN_PTR(round_map[i], PyUnicode_InternFromString(mpd_round_string[i]));
Py_INCREF(round_map[i]);
CHECK_INT(PyModule_AddObject(m, mpd_round_string[i], round_map[i]));
}
/* Add specification version number */
CHECK_INT(PyModule_AddStringConstant(m, "__version__", " 1.70"));
......
......@@ -158,17 +158,9 @@ CondMap = {
C.FloatOperation: P.FloatOperation,
}
RoundMap = {
C.ROUND_UP: P.ROUND_UP,
C.ROUND_DOWN: P.ROUND_DOWN,
C.ROUND_CEILING: P.ROUND_CEILING,
C.ROUND_FLOOR: P.ROUND_FLOOR,
C.ROUND_HALF_UP: P.ROUND_HALF_UP,
C.ROUND_HALF_DOWN: P.ROUND_HALF_DOWN,
C.ROUND_HALF_EVEN: P.ROUND_HALF_EVEN,
C.ROUND_05UP: P.ROUND_05UP
}
RoundModes = RoundMap.items()
RoundModes = [C.ROUND_UP, C.ROUND_DOWN, C.ROUND_CEILING, C.ROUND_FLOOR,
C.ROUND_HALF_UP, C.ROUND_HALF_DOWN, C.ROUND_HALF_EVEN,
C.ROUND_05UP]
class Context(object):
......@@ -183,7 +175,7 @@ class Context(object):
self.p.prec = self.c.prec
self.p.Emin = self.c.Emin
self.p.Emax = self.c.Emax
self.p.rounding = RoundMap[self.c.rounding]
self.p.rounding = self.c.rounding
self.p.capitals = self.c.capitals
self.settraps([sig for sig in self.c.traps if self.c.traps[sig]])
self.setstatus([sig for sig in self.c.flags if self.c.flags[sig]])
......@@ -217,12 +209,12 @@ class Context(object):
self.p.Emax = val
def getround(self):
assert(self.c.rounding == RoundMap[self.p.rounding])
assert(self.c.rounding == self.p.rounding)
return self.c.rounding
def setround(self, val):
self.c.rounding = val
self.p.rounding = RoundMap[val]
self.p.rounding = val
def getcapitals(self):
assert(self.c.capitals == self.p.capitals)
......@@ -627,8 +619,12 @@ def convert(t, convstr=True):
context.clear_status()
if not t.contextfunc and i == 0 or \
convstr and isinstance(op, str):
if op in RoundModes:
t.cop.append(op)
t.pop.append(op)
elif not t.contextfunc and i == 0 or \
convstr and isinstance(op, str):
try:
c = C.Decimal(op)
cex = None
......@@ -662,10 +658,6 @@ def convert(t, convstr=True):
t.cop.append(op.c)
t.pop.append(op.p)
elif op in RoundModes:
t.cop.append(op[0])
t.pop.append(op[1])
else:
t.cop.append(op)
t.pop.append(op)
......@@ -809,7 +801,7 @@ def test_method(method, testspecs, testfunc):
log(" prec: %d emin: %d emax: %d",
(context.prec, context.Emin, context.Emax))
restr_range = 9999 if context.Emax > 9999 else context.Emax+99
for rounding in sorted(RoundMap):
for rounding in RoundModes:
context.rounding = rounding
context.capitals = random.randrange(2)
if spec['clamp'] == 'rand':
......@@ -941,7 +933,7 @@ def test_round(method, prec, exprange, restricted_range, itr, stat):
def test_from_float(method, prec, exprange, restricted_range, itr, stat):
"""Iterate the __float__ method through many test cases."""
for rounding in sorted(RoundMap):
for rounding in RoundModes:
context.rounding = rounding
for i in range(1000):
f = randfloat()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment