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.
This commit is contained in:
Daniele Varrazzo 2018-11-16 18:11:49 +00:00
parent 654be4784c
commit 73a680f45d
3 changed files with 34 additions and 2 deletions

1
NEWS
View File

@ -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

View File

@ -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;
}

View File

@ -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):