From 73a680f45da11f3e839e52d7b94ba010cd1695ec Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Fri, 16 Nov 2018 18:11:49 +0000 Subject: [PATCH] Convert int subclasses to long before adapting Fixes adaptation of int/long subclasses whose str() is not the number, such IntEnum Close #591 Note that I thought it would have needed a new adapter, so I considered it a new feature. But it is more a shortcoming of the int adapter failing to do something reasonable (poor Liskov, always mistreated) so I may actually backport it if there is a new 2.7 release. --- NEWS | 1 + psycopg/adapter_pint.c | 23 +++++++++++++++++++++-- tests/test_types_basic.py | 12 ++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 90477661..3f7b6088 100644 --- a/NEWS +++ b/NEWS @@ -18,6 +18,7 @@ New features: structure (:ticket:`#782`). - `~psycopg2.sql.Identifier` can represent qualified names in SQL composition (:ticket:`#732`). +- Fixed adaptation of numeric subclasses such as `IntEnum` (:ticket:`591`). - `!str()` on `~psycopg2.extras.Range` produces a human-readable representation (:ticket:`#773`). - `~psycopg2.extras.DictCursor` and `~psycopg2.extras.RealDictCursor` rows diff --git a/psycopg/adapter_pint.c b/psycopg/adapter_pint.c index 18c74b28..72bbdbfe 100644 --- a/psycopg/adapter_pint.c +++ b/psycopg/adapter_pint.c @@ -35,8 +35,27 @@ static PyObject * pint_getquoted(pintObject *self, PyObject *args) { - PyObject *res; - if (!(res = PyObject_Str(self->wrapped))) { + PyObject *res = NULL; + + /* Convert subclass to int to handle IntEnum and other subclasses + * whose str() is not the number. */ + if (PyLong_CheckExact(self->wrapped) +#if PY_MAJOR_VERSION < 2 + || PyInt_CheckExact(self->wrapped) +#endif + ) { + res = PyObject_Str(self->wrapped); + } else { + PyObject *tmp; + if (!(tmp = PyObject_CallFunctionObjArgs( + (PyObject *)&PyLong_Type, self->wrapped, NULL))) { + goto exit; + } + res = PyObject_Str(tmp); + Py_DECREF(tmp); + } + + if (!res) { goto exit; } diff --git a/tests/test_types_basic.py b/tests/test_types_basic.py index 499fdd18..626ac744 100755 --- a/tests/test_types_basic.py +++ b/tests/test_types_basic.py @@ -382,6 +382,18 @@ class TypesBasicTests(ConnectingTestCase): a = self.execute("select '{10:20:30:40:50:60}'::macaddr[]") self.assertEqual(a, ['10:20:30:40:50:60']) + @testutils.skip_before_python(3, 4) + def testIntEnum(self): + from enum import IntEnum + + class Color(IntEnum): + RED = 1 + GREEN = 2 + BLUE = 4 + + a = self.execute("select %s", (Color.GREEN,)) + self.assertEqual(a, Color.GREEN) + class AdaptSubclassTest(unittest.TestCase): def test_adapt_subtype(self):