From c8ec747903a4d0063d88fdd38cb18b50303daaa7 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Sat, 4 Jun 2011 14:16:24 +0100 Subject: [PATCH] Don't fail import if mx.DateTime module is not found at import time A better fix for ticket #53. --- NEWS | 4 ++-- psycopg/adapter_datetime.h | 4 ++-- psycopg/adapter_mxdatetime.c | 8 +++++--- psycopg/psycopgmodule.c | 38 ++++++++++++++++++++++++++--------- psycopg/typecast.c | 15 +++++++------- psycopg/typecast_mxdatetime.c | 8 ++++++-- setup.py | 19 +++++++----------- 7 files changed, 59 insertions(+), 37 deletions(-) diff --git a/NEWS b/NEWS index a14cbf33..2d0d076c 100644 --- a/NEWS +++ b/NEWS @@ -9,8 +9,8 @@ What's new in psycopg 2.4.2 default isolation level. - Fixed bug with multithread code potentially causing loss of sync with the server communication or lock of the client (ticket #55). - - Don't build mx.DateTime support if the module can't be imported - (ticket #53). + - Don't fail import if mx.DateTime module can't be found, even if its + support was built (ticket #53). - Fixed escape for negative numbers prefixed by minus operator (ticket #57). diff --git a/psycopg/adapter_datetime.h b/psycopg/adapter_datetime.h index dd59e9b5..09abd88f 100644 --- a/psycopg/adapter_datetime.h +++ b/psycopg/adapter_datetime.h @@ -45,11 +45,11 @@ typedef struct { } pydatetimeObject; +HIDDEN int psyco_adapter_datetime_init(void); + /* functions exported to psycopgmodule.c */ #ifdef PSYCOPG_DEFAULT_PYDATETIME -HIDDEN int psyco_adapter_datetime_init(void); - HIDDEN PyObject *psyco_Date(PyObject *module, PyObject *args); #define psyco_Date_doc \ "Date(year, month, day) -> new date\n\n" \ diff --git a/psycopg/adapter_mxdatetime.c b/psycopg/adapter_mxdatetime.c index 793dfba2..abe73f86 100644 --- a/psycopg/adapter_mxdatetime.c +++ b/psycopg/adapter_mxdatetime.c @@ -26,7 +26,6 @@ #define PSYCOPG_MODULE #include "psycopg/psycopg.h" -/* TODO: check if still compiles ok: I have no mx on this box */ #include "psycopg/adapter_mxdatetime.h" #include "psycopg/microprotocols_proto.h" @@ -34,13 +33,16 @@ #include +/* Return 0 on success, -1 on failure, but don't set an exception */ + int psyco_adapter_mxdatetime_init(void) { Dprintf("psyco_adapter_mxdatetime_init: mx.DateTime init"); - if(mxDateTime_ImportModuleAndAPI()) { - PyErr_SetString(PyExc_ImportError, "mx.DateTime initialization failed"); + if (mxDateTime_ImportModuleAndAPI()) { + Dprintf("psyco_adapter_mxdatetime_init: mx.DateTime initialization failed"); + PyErr_Clear(); return -1; } return 0; diff --git a/psycopg/psycopgmodule.c b/psycopg/psycopgmodule.c index c5daefd5..f37a98e7 100644 --- a/psycopg/psycopgmodule.c +++ b/psycopg/psycopgmodule.c @@ -360,10 +360,16 @@ psyco_adapters_init(PyObject *mod) #ifdef HAVE_MXDATETIME /* as above, we use the callable objects from the psycopg module */ - call = PyMapping_GetItemString(mod, "TimestampFromMx"); - microprotocols_add(mxDateTime.DateTime_Type, NULL, call); - call = PyMapping_GetItemString(mod, "TimeFromMx"); - microprotocols_add(mxDateTime.DateTimeDelta_Type, NULL, call); + if (NULL != (call = PyMapping_GetItemString(mod, "TimestampFromMx"))) { + microprotocols_add(mxDateTime.DateTime_Type, NULL, call); + + /* if we found the above, we have this too. */ + call = PyMapping_GetItemString(mod, "TimeFromMx"); + microprotocols_add(mxDateTime.DateTimeDelta_Type, NULL, call); + } + else { + PyErr_Clear(); + } #endif } @@ -792,6 +798,7 @@ static PyMethodDef psycopgMethods[] = { METH_VARARGS, psyco_IntervalFromPy_doc}, #ifdef HAVE_MXDATETIME + /* to be deleted if not found at import time */ {"DateFromMx", (PyCFunction)psyco_DateFromMx, METH_VARARGS, psyco_DateFromMx_doc}, {"TimeFromMx", (PyCFunction)psyco_TimeFromMx, @@ -885,12 +892,16 @@ INIT_MODULE(_psycopg)(void) #ifdef HAVE_MXDATETIME Py_TYPE(&mxdatetimeType) = &PyType_Type; if (PyType_Ready(&mxdatetimeType) == -1) goto exit; - if (mxDateTime_ImportModuleAndAPI() != 0) { - Dprintf("initpsycopg: why marc hide mx.DateTime again?!"); - PyErr_SetString(PyExc_ImportError, "can't import mx.DateTime module"); + if (0 != mxDateTime_ImportModuleAndAPI()) { + PyErr_Clear(); + + /* only fail if the mx typacaster should have been the default */ +#ifdef PSYCOPG_DEFAULT_MXDATETIME + PyErr_SetString(PyExc_ImportError, + "can't import mx.DateTime module (requested as default adapter)"); goto exit; +#endif } - if (psyco_adapter_mxdatetime_init()) { goto exit; } #endif /* import python builtin datetime module, if available */ @@ -967,6 +978,16 @@ INIT_MODULE(_psycopg)(void) /* encodings dictionary in module dictionary */ PyModule_AddObject(module, "encodings", psycoEncodings); +#ifdef HAVE_MXDATETIME + /* If we can't find mx.DateTime objects at runtime, + * remove them from the module (and, as consequence, from the adapters). */ + if (0 != psyco_adapter_mxdatetime_init()) { + PyDict_DelItemString(dict, "DateFromMx"); + PyDict_DelItemString(dict, "TimeFromMx"); + PyDict_DelItemString(dict, "TimestampFromMx"); + PyDict_DelItemString(dict, "IntervalFromMx"); + } +#endif /* initialize default set of typecasters */ typecast_init(dict); @@ -999,7 +1020,6 @@ INIT_MODULE(_psycopg)(void) lobjectType.tp_alloc = PyType_GenericAlloc; #endif - #ifdef HAVE_MXDATETIME mxdatetimeType.tp_alloc = PyType_GenericAlloc; #endif diff --git a/psycopg/typecast.c b/psycopg/typecast.c index ba3871e2..56a203dc 100644 --- a/psycopg/typecast.c +++ b/psycopg/typecast.c @@ -292,13 +292,14 @@ typecast_init(PyObject *dict) /* register the date/time typecasters with their original names */ #ifdef HAVE_MXDATETIME - if (psyco_typecast_mxdatetime_init()) { return -1; } - for (i = 0; typecast_mxdatetime[i].name != NULL; i++) { - typecastObject *t; - Dprintf("typecast_init: initializing %s", typecast_mxdatetime[i].name); - t = (typecastObject *)typecast_from_c(&(typecast_mxdatetime[i]), dict); - if (t == NULL) return -1; - PyDict_SetItem(dict, t->name, (PyObject *)t); + if (0 == psyco_typecast_mxdatetime_init()) { + for (i = 0; typecast_mxdatetime[i].name != NULL; i++) { + typecastObject *t; + Dprintf("typecast_init: initializing %s", typecast_mxdatetime[i].name); + t = (typecastObject *)typecast_from_c(&(typecast_mxdatetime[i]), dict); + if (t == NULL) return -1; + PyDict_SetItem(dict, t->name, (PyObject *)t); + } } #endif diff --git a/psycopg/typecast_mxdatetime.c b/psycopg/typecast_mxdatetime.c index a3a95fa6..e61224df 100644 --- a/psycopg/typecast_mxdatetime.c +++ b/psycopg/typecast_mxdatetime.c @@ -25,13 +25,17 @@ #include "mxDateTime.h" + +/* Return 0 on success, -1 on failure, but don't set an exception */ + static int psyco_typecast_mxdatetime_init(void) { Dprintf("psyco_typecast_mxdatetime_init: mx.DateTime init"); - if(mxDateTime_ImportModuleAndAPI()) { - PyErr_SetString(PyExc_ImportError, "mx.DateTime initialization failed"); + if (mxDateTime_ImportModuleAndAPI()) { + Dprintf("psyco_typecast_mxdatetime_init: mx.DateTime initialization failed"); + PyErr_Clear(); return -1; } return 0; diff --git a/setup.py b/setup.py index 45b1a90c..6f06ad10 100644 --- a/setup.py +++ b/setup.py @@ -450,18 +450,13 @@ if parser.has_option('build_ext', 'mx_include_dir'): else: mxincludedir = os.path.join(get_python_inc(plat_specific=1), "mx") if os.path.exists(mxincludedir): - # Check if mx.datetime is importable at all: see ticket #53 - try: - import mx.DateTime - except ImportError: - pass - else: - include_dirs.append(mxincludedir) - define_macros.append(('HAVE_MXDATETIME','1')) - sources.append('adapter_mxdatetime.c') - depends.extend(['adapter_mxdatetime.h', 'typecast_mxdatetime.c']) - have_mxdatetime = True - version_flags.append('mx') + # Build the support for mx: we will check at runtime if it can be imported + include_dirs.append(mxincludedir) + define_macros.append(('HAVE_MXDATETIME','1')) + sources.append('adapter_mxdatetime.c') + depends.extend(['adapter_mxdatetime.h', 'typecast_mxdatetime.c']) + have_mxdatetime = True + version_flags.append('mx') # now decide which package will be the default for date/time typecasts if have_pydatetime and (use_pydatetime or not have_mxdatetime):