From 4bb6f9cef2ac2631b2af881d945b770b64fa23bd Mon Sep 17 00:00:00 2001 From: Oleksandr Shulgin Date: Mon, 1 Jun 2015 18:05:11 +0200 Subject: [PATCH 1/5] Add libpq version discovery --- doc/src/extensions.rst | 10 ++++++++++ doc/src/module.rst | 6 ++++++ lib/__init__.py | 2 +- lib/extensions.py | 2 +- psycopg/psycopgmodule.c | 16 ++++++++++++++++ setup.py | 3 +++ tests/testutils.py | 37 +++++++++++++++++++++++++++++++++++++ 7 files changed, 74 insertions(+), 2 deletions(-) diff --git a/doc/src/extensions.rst b/doc/src/extensions.rst index dea10417..3f010665 100644 --- a/doc/src/extensions.rst +++ b/doc/src/extensions.rst @@ -197,6 +197,16 @@ functionalities defined by the |DBAPI|_. .. versionadded:: 2.2.0 +.. function:: libpq_version() + + Query actual ``libpq`` version loaded. + + This function throws `NotSupportedError` if it was compiled with + ``libpq < 9.1``. + + .. seealso:: libpq docs for `PQlibVersion()`__ + + .. __: http://www.postgresql.org/docs/current/static/libpq-misc.html#LIBPQ-PQLIBVERSION .. _sql-adaptation-objects: diff --git a/doc/src/module.rst b/doc/src/module.rst index 8de9f87e..bd6bcf45 100644 --- a/doc/src/module.rst +++ b/doc/src/module.rst @@ -109,6 +109,12 @@ The module interface respects the standard defined in the |DBAPI|_. by the interface. For `psycopg2` is ``pyformat``. See also :ref:`query-parameters`. +.. data:: __libpq_version__ + + Integer contant containing the version of ``libpq`` this `psycopg2` + module was compiled with. If this value is ``>= 90100`` then you + may query for the actually loaded version of libpq using + `~psycopg2.extensions.libpq_version()`. .. index:: diff --git a/lib/__init__.py b/lib/__init__.py index cf8c06ae..994b15a8 100644 --- a/lib/__init__.py +++ b/lib/__init__.py @@ -57,7 +57,7 @@ from psycopg2._psycopg import IntegrityError, InterfaceError, InternalError from psycopg2._psycopg import NotSupportedError, OperationalError from psycopg2._psycopg import _connect, apilevel, threadsafety, paramstyle -from psycopg2._psycopg import __version__ +from psycopg2._psycopg import __version__, __libpq_version__ from psycopg2 import tz diff --git a/lib/extensions.py b/lib/extensions.py index 216d8ad2..c40e3369 100644 --- a/lib/extensions.py +++ b/lib/extensions.py @@ -56,7 +56,7 @@ try: except ImportError: pass -from psycopg2._psycopg import adapt, adapters, encodings, connection, cursor, lobject, Xid +from psycopg2._psycopg import adapt, adapters, encodings, connection, cursor, lobject, Xid, libpq_version from psycopg2._psycopg import string_types, binary_types, new_type, new_array_type, register_type from psycopg2._psycopg import ISQLQuote, Notify, Diagnostics, Column diff --git a/psycopg/psycopgmodule.c b/psycopg/psycopgmodule.c index 61e2de57..63abb03d 100644 --- a/psycopg/psycopgmodule.c +++ b/psycopg/psycopgmodule.c @@ -300,6 +300,19 @@ exit: return rv; } +#define psyco_libpq_version_doc "Query actual libpq version loaded." + +static PyObject* +psyco_libpq_version(PyObject *self) +{ +#if PG_VERSION_HEX >= 0x090100 + return PyInt_FromLong(PQlibVersion()); +#else + PyErr_SetString(NotSupportedError, "version discovery is not supported in libpq < 9.1"); + return NULL; +#endif +} + /* psyco_encodings_fill Fill the module's postgresql<->python encoding table */ @@ -704,6 +717,8 @@ static PyMethodDef psycopgMethods[] = { METH_VARARGS|METH_KEYWORDS, typecast_from_python_doc}, {"new_array_type", (PyCFunction)typecast_array_from_python, METH_VARARGS|METH_KEYWORDS, typecast_array_from_python_doc}, + {"libpq_version", (PyCFunction)psyco_libpq_version, + METH_NOARGS, psyco_libpq_version_doc}, {"Date", (PyCFunction)psyco_Date, METH_VARARGS, psyco_Date_doc}, @@ -899,6 +914,7 @@ INIT_MODULE(_psycopg)(void) /* set some module's parameters */ PyModule_AddStringConstant(module, "__version__", PSYCOPG_VERSION); PyModule_AddStringConstant(module, "__doc__", "psycopg PostgreSQL driver"); + PyModule_AddIntConstant(module, "__libpq_version__", PG_VERSION_NUM); PyModule_AddObject(module, "apilevel", Text_FromUTF8(APILEVEL)); PyModule_AddObject(module, "threadsafety", PyInt_FromLong(THREADSAFETY)); PyModule_AddObject(module, "paramstyle", Text_FromUTF8(PARAMSTYLE)); diff --git a/setup.py b/setup.py index fc4f1711..e42a5c1a 100644 --- a/setup.py +++ b/setup.py @@ -416,6 +416,9 @@ class psycopg_build_ext(build_ext): % pgversion) sys.exit(1) + define_macros.append(("PG_VERSION_NUM", "%d%02d%02d" % + (pgmajor, pgminor, pgpatch))) + define_macros.append(("PG_VERSION_HEX", "0x%02X%02X%02X" % (pgmajor, pgminor, pgpatch))) diff --git a/tests/testutils.py b/tests/testutils.py index 6a784320..987bd7b6 100644 --- a/tests/testutils.py +++ b/tests/testutils.py @@ -236,6 +236,43 @@ def skip_after_postgres(*ver): return skip_after_postgres__ return skip_after_postgres_ +def libpq_version(): + import psycopg2 + v = psycopg2.__libpq_version__ + if v >= 90100: + v = psycopg2.extensions.libpq_version() + return v + +def skip_before_libpq(*ver): + """Skip a test if libpq we're linked to is older than a certain version.""" + ver = ver + (0,) * (3 - len(ver)) + def skip_before_libpq_(f): + @wraps(f) + def skip_before_libpq__(self): + v = libpq_version() + if v < int("%d%02d%02d" % ver): + return self.skipTest("skipped because libpq %d" % v) + else: + return f(self) + + return skip_before_libpq__ + return skip_before_libpq_ + +def skip_after_libpq(*ver): + """Skip a test if libpq we're linked to is newer than a certain version.""" + ver = ver + (0,) * (3 - len(ver)) + def skip_after_libpq_(f): + @wraps(f) + def skip_after_libpq__(self): + v = libpq_version() + if v >= int("%d%02d%02d" % ver): + return self.skipTest("skipped because libpq %s" % v) + else: + return f(self) + + return skip_after_libpq__ + return skip_after_libpq_ + def skip_before_python(*ver): """Skip a test on Python before a certain version.""" def skip_before_python_(f): From ffd98a82c04642b73d76a4e60f8f58c355ae1126 Mon Sep 17 00:00:00 2001 From: Oleksandr Shulgin Date: Tue, 2 Jun 2015 11:12:16 +0200 Subject: [PATCH 2/5] Add test for libpq_version --- tests/test_module.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/test_module.py b/tests/test_module.py index 608f703d..62b85ee2 100755 --- a/tests/test_module.py +++ b/tests/test_module.py @@ -320,6 +320,15 @@ import _psycopg self.assertEqual(0, proc.returncode) +class TestVersionDiscovery(unittest.TestCase): + def test_libpq_version(self): + self.assertTrue(type(psycopg2.__libpq_version__) is int) + try: + self.assertTrue(type(psycopg2.extensions.libpq_version()) is int) + except NotSupportedError: + self.assertTrue(psycopg2.__libpq_version__ < 90100) + + def test_suite(): return unittest.TestLoader().loadTestsFromName(__name__) From 73d17e3c5e80c66738435cd6733e999cb9c21ca7 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Tue, 2 Jun 2015 10:54:08 +0100 Subject: [PATCH 3/5] Dropped PG_VERSION_HEX constant At PostgreSQL 10.0 it would have become awkward. --- psycopg/adapter_binary.c | 2 +- psycopg/lobject_int.c | 4 ++-- psycopg/lobject_type.c | 6 +++--- psycopg/psycopgmodule.c | 4 ++-- psycopg/utils.c | 2 +- setup.py | 3 --- 6 files changed, 9 insertions(+), 12 deletions(-) diff --git a/psycopg/adapter_binary.c b/psycopg/adapter_binary.c index 485dc5a4..597048d2 100644 --- a/psycopg/adapter_binary.c +++ b/psycopg/adapter_binary.c @@ -39,7 +39,7 @@ static unsigned char * binary_escape(unsigned char *from, size_t from_length, size_t *to_length, PGconn *conn) { -#if PG_VERSION_HEX >= 0x080104 +#if PG_VERSION_NUM >= 80104 if (conn) return PQescapeByteaConn(conn, from, from_length, to_length); else diff --git a/psycopg/lobject_int.c b/psycopg/lobject_int.c index 6b55d42b..8788c100 100644 --- a/psycopg/lobject_int.c +++ b/psycopg/lobject_int.c @@ -474,7 +474,7 @@ lobject_export(lobjectObject *self, const char *filename) return retvalue; } -#if PG_VERSION_HEX >= 0x080300 +#if PG_VERSION_NUM >= 80300 RAISES_NEG int lobject_truncate(lobjectObject *self, size_t len) @@ -511,4 +511,4 @@ lobject_truncate(lobjectObject *self, size_t len) } -#endif /* PG_VERSION_HEX >= 0x080300 */ +#endif /* PG_VERSION_NUM >= 80300 */ diff --git a/psycopg/lobject_type.c b/psycopg/lobject_type.c index ec95b5cf..a43325d4 100644 --- a/psycopg/lobject_type.c +++ b/psycopg/lobject_type.c @@ -266,7 +266,7 @@ psyco_lobj_get_closed(lobjectObject *self, void *closure) return closed; } -#if PG_VERSION_HEX >= 0x080300 +#if PG_VERSION_NUM >= 80300 #define psyco_lobj_truncate_doc \ "truncate(len=0) -- Truncate large object to given size." @@ -327,10 +327,10 @@ static struct PyMethodDef lobjectObject_methods[] = { METH_NOARGS, psyco_lobj_unlink_doc}, {"export",(PyCFunction)psyco_lobj_export, METH_VARARGS, psyco_lobj_export_doc}, -#if PG_VERSION_HEX >= 0x080300 +#if PG_VERSION_NUM >= 80300 {"truncate",(PyCFunction)psyco_lobj_truncate, METH_VARARGS, psyco_lobj_truncate_doc}, -#endif /* PG_VERSION_HEX >= 0x080300 */ +#endif /* PG_VERSION_NUM >= 80300 */ {NULL} }; diff --git a/psycopg/psycopgmodule.c b/psycopg/psycopgmodule.c index 63abb03d..34fc25e8 100644 --- a/psycopg/psycopgmodule.c +++ b/psycopg/psycopgmodule.c @@ -185,7 +185,7 @@ psyco_libcrypto_threads_init(void) if (PyImport_ImportModule("ssl") != NULL) { /* disable libcrypto setup in libpq, so it won't stomp on the callbacks that have already been set up */ -#if PG_VERSION_HEX >= 0x080400 +#if PG_VERSION_NUM >= 80400 PQinitOpenSSL(1, 0); #endif } @@ -305,7 +305,7 @@ exit: static PyObject* psyco_libpq_version(PyObject *self) { -#if PG_VERSION_HEX >= 0x090100 +#if PG_VERSION_NUM >= 90100 return PyInt_FromLong(PQlibVersion()); #else PyErr_SetString(NotSupportedError, "version discovery is not supported in libpq < 9.1"); diff --git a/psycopg/utils.c b/psycopg/utils.c index 6b035cfa..836f6129 100644 --- a/psycopg/utils.c +++ b/psycopg/utils.c @@ -62,7 +62,7 @@ psycopg_escape_string(connectionObject *conn, const char *from, Py_ssize_t len, } { - #if PG_VERSION_HEX >= 0x080104 + #if PG_VERSION_NUM >= 80104 int err; if (conn && conn->pgconn) ql = PQescapeStringConn(conn->pgconn, to+eq+1, from, len, &err); diff --git a/setup.py b/setup.py index e42a5c1a..2de8c5ef 100644 --- a/setup.py +++ b/setup.py @@ -419,9 +419,6 @@ class psycopg_build_ext(build_ext): define_macros.append(("PG_VERSION_NUM", "%d%02d%02d" % (pgmajor, pgminor, pgpatch))) - define_macros.append(("PG_VERSION_HEX", "0x%02X%02X%02X" % - (pgmajor, pgminor, pgpatch))) - # enable lo64 if libpq >= 9.3 and Python 64 bits if (pgmajor, pgminor) >= (9, 3) and is_py_64(): define_macros.append(("HAVE_LO64", "1")) From 5a21da43ee240d66d5fdefd3476a5329e6d730a9 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Tue, 2 Jun 2015 11:01:10 +0100 Subject: [PATCH 4/5] Mention libpq version inspection in news file --- NEWS | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/NEWS b/NEWS index 3e8864f0..de5ead30 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,17 @@ Current release --------------- +What's new in psycopg 2.7 +------------------------- + +New features: + +- Added `~psycopg2.__libpq_version__` and + `~psycopg2.extensions.libpq_version()` to inspect the version of the + ``libpq`` library the module was compiled/loaded with + (:tickets:`#35, #323`). + + What's new in psycopg 2.6.1 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ From c2955fb8fc95afd288af22444be8fa11e4132717 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Tue, 2 Jun 2015 11:14:22 +0100 Subject: [PATCH 5/5] Version function/constant docs improved --- doc/src/extensions.rst | 10 ++++++---- doc/src/module.rst | 9 +++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/doc/src/extensions.rst b/doc/src/extensions.rst index 3f010665..84e12412 100644 --- a/doc/src/extensions.rst +++ b/doc/src/extensions.rst @@ -199,12 +199,14 @@ functionalities defined by the |DBAPI|_. .. function:: libpq_version() - Query actual ``libpq`` version loaded. + Return the version number of the ``libpq`` dynamic library loaded as an + integer, in the same format of `~connection.server_version`. - This function throws `NotSupportedError` if it was compiled with - ``libpq < 9.1``. + Raise `~psycopg2.NotSupportedError` if the ``psycopg2`` module was + compiled with a ``libpq`` version lesser than 9.1 (which can be detected + by the `~psycopg2.__libpq_version__` constant). - .. seealso:: libpq docs for `PQlibVersion()`__ + .. seealso:: libpq docs for `PQlibVersion()`__. .. __: http://www.postgresql.org/docs/current/static/libpq-misc.html#LIBPQ-PQLIBVERSION diff --git a/doc/src/module.rst b/doc/src/module.rst index bd6bcf45..bd121e9d 100644 --- a/doc/src/module.rst +++ b/doc/src/module.rst @@ -111,10 +111,11 @@ The module interface respects the standard defined in the |DBAPI|_. .. data:: __libpq_version__ - Integer contant containing the version of ``libpq`` this `psycopg2` - module was compiled with. If this value is ``>= 90100`` then you - may query for the actually loaded version of libpq using - `~psycopg2.extensions.libpq_version()`. + Integer constant reporting the version of the ``libpq`` library this + ``psycopg2`` module was compiled with (in the same format of + `~connection.server_version`). If this value is lesser than ``90100`` + then you may query the version of the actually loaded library using the + `~psycopg2.extensions.libpq_version()` function. .. index::