diff --git a/NEWS b/NEWS index e61423d0..5c752c6f 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,9 @@ What's new in psycopg 2.4 * New features and changes: + - Added support for Python 3.1 and 3.2. + - Adapt types 'bytearray' (from Python 2.6) and 'memoryview' (from Python + 2.7) to bytea data type. - Added `register_composite()` function to cast PostgreSQL composite types into Python tuples/namedtuples. - More efficient iteration on named cursors. diff --git a/doc/src/usage.rst b/doc/src/usage.rst index 60a2428c..2f16a982 100644 --- a/doc/src/usage.rst +++ b/doc/src/usage.rst @@ -244,12 +244,27 @@ the SQL string that would be sent to the database. .. index:: single: Buffer; Adaptation single: bytea; Adaptation + single: bytes; Adaptation + single: bytearray; Adaptation + single: memoryview; Adaptation single: Binary string -- Binary types: Python types such as `!bytes`, `!bytearray`, `!buffer`, - `!memoryview` are converted in PostgreSQL binary string syntax, suitable for - :sql:`bytea` fields. Received data is returned as `!buffer` (in Python 2) or - `!memoryview` (in Python 3). +- Binary types: Python types representing binary objects are converted in + PostgreSQL binary string syntax, suitable for :sql:`bytea` fields. Such + types are `!buffer` (only available in Python 2), `!memoryview` (available + from Python 2.7), `!bytearray` (available from Python 2.6) and `!bytes` + (only form Python 3: the name is available from Python 2.6 but it's only an + alias for the type `!str`). Received data is returned as `!buffer` (in + Python 2) or `!memoryview` (in Python 3). + + .. note:: + + In Python 2, if you have binary data in a `!str` object, you can pass them + to a :sql:`bytea` field using the `psycopg2.Binary` wrapper:: + + mypic = open('picture.png', 'rb').read() + curs.execute("insert into blobs (file) values (%s)", + (psycopg2.Binary(mypic),)) .. warning:: diff --git a/psycopg/adapter_binary.c b/psycopg/adapter_binary.c index d54d17a2..31b25026 100644 --- a/psycopg/adapter_binary.c +++ b/psycopg/adapter_binary.c @@ -61,8 +61,11 @@ binary_quote(binaryObject *self) if (Bytes_Check(self->wrapped) #if PY_MAJOR_VERSION < 3 || PyBuffer_Check(self->wrapped) -#else +#endif +#if PY_MAJOR_VERSION >= 3 || PY_MINOR_VERSION >= 6 || PyByteArray_Check(self->wrapped) +#endif +#if PY_MAJOR_VERSION >= 3 || PY_MINOR_VERSION >= 7 || PyMemoryView_Check(self->wrapped) #endif ) { diff --git a/psycopg/psycopgmodule.c b/psycopg/psycopgmodule.c index f91483df..7b6a3346 100644 --- a/psycopg/psycopgmodule.c +++ b/psycopg/psycopgmodule.c @@ -315,17 +315,26 @@ psyco_adapters_init(PyObject *mod) microprotocols_add(&PyLong_Type, NULL, (PyObject*)&asisType); microprotocols_add(&PyBool_Type, NULL, (PyObject*)&pbooleanType); + /* strings */ #if PY_MAJOR_VERSION < 3 microprotocols_add(&PyString_Type, NULL, (PyObject*)&qstringType); #endif microprotocols_add(&PyUnicode_Type, NULL, (PyObject*)&qstringType); + + /* binary */ #if PY_MAJOR_VERSION < 3 microprotocols_add(&PyBuffer_Type, NULL, (PyObject*)&binaryType); #else microprotocols_add(&PyBytes_Type, NULL, (PyObject*)&binaryType); +#endif + +#if PY_MAJOR_VERSION >= 3 || PY_MINOR_VERSION >= 6 microprotocols_add(&PyByteArray_Type, NULL, (PyObject*)&binaryType); +#endif +#if PY_MAJOR_VERSION >= 3 || PY_MINOR_VERSION >= 7 microprotocols_add(&PyMemoryView_Type, NULL, (PyObject*)&binaryType); #endif + microprotocols_add(&PyList_Type, NULL, (PyObject*)&listType); if ((type = (PyTypeObject*)psyco_GetDecimalType()) != NULL) diff --git a/tests/types_basic.py b/tests/types_basic.py index ab90502a..f956f90a 100755 --- a/tests/types_basic.py +++ b/tests/types_basic.py @@ -207,27 +207,39 @@ class TypesBasicTests(unittest.TestCase): o2 = self.execute("select %s;", (o1,)) self.assertEqual(memoryview, type(o2[0])) - @testutils.skip_before_python(3) + @testutils.skip_before_python(2, 6) def testAdaptBytearray(self): o1 = bytearray(range(256)) o2 = self.execute("select %s;", (o1,)) - self.assertEqual(memoryview, type(o2)) + if sys.version_info[0] < 3: + self.assertEqual(buffer, type(o2)) + else: + self.assertEqual(memoryview, type(o2)) # Test with an empty buffer o1 = bytearray([]) o2 = self.execute("select %s;", (o1,)) - self.assertEqual(memoryview, type(o2)) + if sys.version_info[0] < 3: + self.assertEqual(buffer, type(o2)) + else: + self.assertEqual(memoryview, type(o2)) - @testutils.skip_before_python(3) + @testutils.skip_before_python(2, 7) def testAdaptMemoryview(self): - o1 = memoryview(bytes(range(256))) + o1 = memoryview(bytearray(range(256))) o2 = self.execute("select %s;", (o1,)) - self.assertEqual(memoryview, type(o2)) + if sys.version_info[0] < 3: + self.assertEqual(buffer, type(o2)) + else: + self.assertEqual(memoryview, type(o2)) # Test with an empty buffer - o1 = memoryview(bytes([])) + o1 = memoryview(bytearray([])) o2 = self.execute("select %s;", (o1,)) - self.assertEqual(memoryview, type(o2)) + if sys.version_info[0] < 3: + self.assertEqual(buffer, type(o2)) + else: + self.assertEqual(memoryview, type(o2)) class AdaptSubclassTest(unittest.TestCase):