aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_builtin.py13
-rw-r--r--Python/bltinmodule.c28
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)