diff options
-rw-r--r-- | Lib/test/test_builtin.py | 13 | ||||
-rw-r--r-- | Python/bltinmodule.c | 28 |
2 files changed, 39 insertions, 2 deletions
diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index d5fc85fe3e8..37ea8ba8852 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1474,6 +1474,19 @@ class BuiltinTest(unittest.TestCase): self.assertRaises(TypeError, round) + # test generic rounding delegation for reals + class TestRound: + def __round__(self): + return 23 + + class TestNoRound: + pass + + self.assertEqual(round(TestRound()), 23) + + self.assertRaises(TypeError, round, 1, 2, 3) + self.assertRaises(TypeError, round, TestNoRound()) + def test_setattr(self): setattr(sys, 'spam', 1) self.assertEqual(sys.spam, 1) diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 2c163a16e85..b55dd519480 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1378,10 +1378,34 @@ builtin_round(PyObject *self, PyObject *args, PyObject *kwds) int ndigits = 0; int i; static char *kwlist[] = {"number", "ndigits", 0}; + PyObject* real; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "d|i:round", - kwlist, &number, &ndigits)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|i:round", + kwlist, &real, &ndigits)) return NULL; + + if (ndigits == 0) { + PyObject *res; + PyObject *d = PyObject_GetAttrString(real, "__round__"); + if (d == NULL && !PyFloat_Check(real)) { + PyErr_SetString(PyExc_TypeError, + "round() argument must have __round__ attribute or be a float"); + return NULL; + } + if (d == NULL) { + PyErr_Clear(); + } else { + res = PyObject_CallFunction(d, ""); + Py_DECREF(d); + return res; + } + } else if (!PyFloat_Check(real)) { + PyErr_SetString(PyExc_TypeError, + "round() argument must have __round__ attribute or be a float"); + return NULL; + } + + number = PyFloat_AsDouble(real); f = 1.0; i = abs(ndigits); while (--i >= 0) |