diff options
-rw-r--r-- | psycopg/adapter_asis.c | 2 | ||||
-rw-r--r-- | psycopg/adapter_binary.c | 10 | ||||
-rw-r--r-- | psycopg/adapter_datetime.c | 16 | ||||
-rw-r--r-- | psycopg/adapter_list.c | 10 | ||||
-rw-r--r-- | psycopg/adapter_pboolean.c | 8 | ||||
-rw-r--r-- | psycopg/adapter_pdecimal.c | 6 | ||||
-rw-r--r-- | psycopg/adapter_pfloat.c | 4 | ||||
-rw-r--r-- | psycopg/adapter_qstring.c | 7 | ||||
-rw-r--r-- | psycopg/connection_int.c | 35 | ||||
-rw-r--r-- | psycopg/connection_type.c | 2 | ||||
-rw-r--r-- | psycopg/cursor_type.c | 36 | ||||
-rw-r--r-- | psycopg/lobject_type.c | 2 | ||||
-rw-r--r-- | psycopg/microprotocols.c | 8 | ||||
-rw-r--r-- | psycopg/notify_type.c | 7 | ||||
-rw-r--r-- | psycopg/pqpath.c | 11 | ||||
-rw-r--r-- | psycopg/psycopgmodule.c | 143 | ||||
-rw-r--r-- | psycopg/python.h | 62 | ||||
-rw-r--r-- | psycopg/typecast.c | 24 | ||||
-rw-r--r-- | psycopg/typecast_basic.c | 17 | ||||
-rw-r--r-- | psycopg/typecast_binary.c | 24 | ||||
-rw-r--r-- | psycopg/xid_type.c | 16 |
21 files changed, 335 insertions, 115 deletions
diff --git a/psycopg/adapter_asis.c b/psycopg/adapter_asis.c index 0d0a7f6..1aa1d0d 100644 --- a/psycopg/adapter_asis.c +++ b/psycopg/adapter_asis.c @@ -38,7 +38,7 @@ static PyObject * asis_str(asisObject *self) { if (self->wrapped == Py_None) { - return PyString_FromString("NULL"); + return Text_FromUTF8("NULL"); } else { return PyObject_Str(self->wrapped); diff --git a/psycopg/adapter_binary.c b/psycopg/adapter_binary.c index cf31ccb..2210507 100644 --- a/psycopg/adapter_binary.c +++ b/psycopg/adapter_binary.c @@ -58,7 +58,13 @@ binary_quote(binaryObject *self) size_t len = 0; /* if we got a plain string or a buffer we escape it and save the buffer */ - if (PyString_Check(self->wrapped) || PyBuffer_Check(self->wrapped)) { + if (Bytes_Check(self->wrapped) +#if PY_MAJOR_VERSION < 3 + || PyBuffer_Check(self->wrapped) +#else + || PyMemoryView_Check(self->wrapped) +#endif + ) { /* escape and build quoted buffer */ if (PyObject_AsReadBuffer(self->wrapped, (const void **)&buffer, &buffer_len) < 0) @@ -76,7 +82,7 @@ binary_quote(binaryObject *self) (self->conn && ((connectionObject*)self->conn)->equote) ? "E'%s'::bytea" : "'%s'::bytea" , to); else - self->buffer = PyString_FromString("''::bytea"); + self->buffer = Text_FromUTF8("''::bytea"); PQfreemem(to); } diff --git a/psycopg/adapter_datetime.c b/psycopg/adapter_datetime.c index f640684..19fd97e 100644 --- a/psycopg/adapter_datetime.c +++ b/psycopg/adapter_datetime.c @@ -62,26 +62,30 @@ pydatetime_str(pydatetimeObject *self) if (self->type <= PSYCO_DATETIME_TIMESTAMP) { PyObject *tz; - /* Select the right PG type to cast into. */ + /* Select the right PG type to cast into. + * fmt contains %s in Py2 and %U in Py3, + * So it can be used with the Text_FromFormatS macro */ char *fmt = NULL; switch (self->type) { case PSYCO_DATETIME_TIME: - fmt = "'%s'::time"; + fmt = "'" Text_S "'::time"; break; case PSYCO_DATETIME_DATE: - fmt = "'%s'::date"; + fmt = "'" Text_S "'::date"; break; case PSYCO_DATETIME_TIMESTAMP: tz = PyObject_GetAttrString(self->wrapped, "tzinfo"); if (!tz) { return NULL; } - fmt = (tz == Py_None) ? "'%s'::timestamp" : "'%s'::timestamptz"; + fmt = (tz == Py_None) + ? "'" Text_S "'::timestamp" + : "'" Text_S "'::timestamptz"; Py_DECREF(tz); break; } iso = PyObject_CallMethod(self->wrapped, "isoformat", NULL); if (iso) { - res = PyString_FromFormat(fmt, PyString_AsString(iso)); + res = Text_FromFormatS(fmt, iso); Py_DECREF(iso); } return res; @@ -89,7 +93,7 @@ pydatetime_str(pydatetimeObject *self) else { PyDateTime_Delta *obj = (PyDateTime_Delta*)self->wrapped; - char buffer[8]; + char buffer[8]; int i; int a = obj->microseconds; diff --git a/psycopg/adapter_list.c b/psycopg/adapter_list.c index 8bd6688..8973e9d 100644 --- a/psycopg/adapter_list.c +++ b/psycopg/adapter_list.c @@ -45,7 +45,7 @@ list_quote(listObject *self) /* empty arrays are converted to NULLs (still searching for a way to insert an empty array in postgresql */ - if (len == 0) return PyString_FromString("'{}'"); + if (len == 0) return Text_FromUTF8("'{}'"); tmp = PyTuple_New(len); @@ -53,7 +53,7 @@ list_quote(listObject *self) PyObject *quoted; PyObject *wrapped = PyList_GET_ITEM(self->wrapped, i); if (wrapped == Py_None) - quoted = PyString_FromString("NULL"); + quoted = Text_FromUTF8("NULL"); else quoted = microprotocol_getquoted(wrapped, (connectionObject*)self->connection); @@ -67,11 +67,15 @@ list_quote(listObject *self) /* now that we have a tuple of adapted objects we just need to join them and put "ARRAY[] around the result */ - str = PyString_FromString(", "); + str = Text_FromUTF8(", "); joined = PyObject_CallMethod(str, "join", "(O)", tmp); if (joined == NULL) goto error; +#if PY_MAJOR_VERSION < 3 res = PyString_FromFormat("ARRAY[%s]", PyString_AsString(joined)); +#else + res = PyUnicode_FromFormat("ARRAY[%U]", joined); +#endif error: Py_XDECREF(tmp); diff --git a/psycopg/adapter_pboolean.c b/psycopg/adapter_pboolean.c index a74d9f3..cdd3ef4 100644 --- a/psycopg/adapter_pboolean.c +++ b/psycopg/adapter_pboolean.c @@ -39,17 +39,17 @@ pboolean_str(pbooleanObject *self) { #ifdef PSYCOPG_NEW_BOOLEAN if (PyObject_IsTrue(self->wrapped)) { - return PyString_FromString("true"); + return Text_FromUTF8("true"); } else { - return PyString_FromString("false"); + return Text_FromUTF8("false"); } #else if (PyObject_IsTrue(self->wrapped)) { - return PyString_FromString("'t'"); + return Text_FromUTF8("'t'"); } else { - return PyString_FromString("'f'"); + return Text_FromUTF8("'f'"); } #endif } diff --git a/psycopg/adapter_pdecimal.c b/psycopg/adapter_pdecimal.c index ed79ea1..fa4acfa 100644 --- a/psycopg/adapter_pdecimal.c +++ b/psycopg/adapter_pdecimal.c @@ -45,7 +45,7 @@ pdecimal_str(pdecimalObject *self) goto end; } else if (check) { - res = PyString_FromString("'NaN'::numeric"); + res = Text_FromUTF8("'NaN'::numeric"); goto end; } @@ -57,7 +57,7 @@ pdecimal_str(pdecimalObject *self) goto end; } if (PyObject_IsTrue(check)) { - res = PyString_FromString("'NaN'::numeric"); + res = Text_FromUTF8("'NaN'::numeric"); goto end; } @@ -66,7 +66,7 @@ pdecimal_str(pdecimalObject *self) goto end; } if (PyObject_IsTrue(check)) { - res = PyString_FromString("'NaN'::numeric"); + res = Text_FromUTF8("'NaN'::numeric"); goto end; } diff --git a/psycopg/adapter_pfloat.c b/psycopg/adapter_pfloat.c index f6ad636..3260d2c 100644 --- a/psycopg/adapter_pfloat.c +++ b/psycopg/adapter_pfloat.c @@ -40,9 +40,9 @@ pfloat_str(pfloatObject *self) { double n = PyFloat_AsDouble(self->wrapped); if (isnan(n)) - return PyString_FromString("'NaN'::float"); + return Text_FromUTF8("'NaN'::float"); else if (isinf(n)) - return PyString_FromString("'Infinity'::float"); + return Text_FromUTF8("'Infinity'::float"); else return PyObject_Repr(self->wrapped); } diff --git a/psycopg/adapter_qstring.c b/psycopg/adapter_qstring.c index 72240c8..c1082d8 100644 --- a/psycopg/adapter_qstring.c +++ b/psycopg/adapter_qstring.c @@ -54,6 +54,7 @@ qstring_quote(qstringObject *self) if (str == NULL) return NULL; } +#if PY_MAJOR_VERSION < 3 /* if the wrapped object is a simple string, we don't know how to (re)encode it, so we pass it as-is */ else if (PyString_Check(self->wrapped)) { @@ -61,6 +62,7 @@ qstring_quote(qstringObject *self) /* INCREF to make it ref-wise identical to unicode one */ Py_INCREF(str); } +#endif /* if the wrapped object is not a string, this is an error */ else { @@ -70,7 +72,7 @@ qstring_quote(qstringObject *self) } /* encode the string into buffer */ - PyString_AsStringAndSize(str, &s, &len); + Bytes_AsStringAndSize(str, &s, &len); /* Call qstring_escape with the GIL released, then reacquire the GIL before verifying that the results can fit into a Python string; raise @@ -94,7 +96,8 @@ qstring_quote(qstringObject *self) return NULL; } - self->buffer = PyString_FromStringAndSize(buffer, qlen); + /* XXX need to decode in connection's encoding in 3.0 */ + self->buffer = Text_FromUTF8AndSize(buffer, qlen); PyMem_Free(buffer); Py_DECREF(str); diff --git a/psycopg/connection_int.c b/psycopg/connection_int.c index aa4eca7..2188993 100644 --- a/psycopg/connection_int.c +++ b/psycopg/connection_int.c @@ -76,7 +76,8 @@ conn_notice_process(connectionObject *self) while (notice != NULL) { PyObject *msg; - msg = PyString_FromString(notice->message); + /* XXX possible other encode I think */ + msg = Text_FromUTF8(notice->message); Dprintf("conn_notice_process: %s", notice->message); @@ -145,8 +146,9 @@ conn_notifies_process(connectionObject *self) (int) pgn->be_pid, pgn->relname); if (!(pid = PyInt_FromLong((long)pgn->be_pid))) { goto error; } - if (!(channel = PyString_FromString(pgn->relname))) { goto error; } - if (!(payload = PyString_FromString(pgn->extra))) { goto error; } + /* XXX in the connection encoding? */ + if (!(channel = Text_FromUTF8(pgn->relname))) { goto error; } + if (!(payload = Text_FromUTF8(pgn->extra))) { goto error; } if (!(notify = PyObject_CallFunctionObjArgs((PyObject *)&NotifyType, pid, channel, payload, NULL))) { @@ -222,15 +224,35 @@ conn_encoding_to_codec(const char *enc) { char *tmp; Py_ssize_t size; - PyObject *pyenc; + PyObject *pyenc = NULL; + PyObject *pybenc = NULL; char *rv = NULL; + /* Find the Py codec name from the PG encoding */ if (!(pyenc = PyDict_GetItemString(psycoEncodings, enc))) { PyErr_Format(OperationalError, "no Python codec for client encoding '%s'", enc); goto exit; } - if (-1 == PyString_AsStringAndSize(pyenc, &tmp, &size)) { + + /* Convert the codec in a bytes string to extract the c string. + * At the end of the block we have pybenc with a new ref. */ + if (PyUnicode_Check(pyenc)) { + if (!(pybenc = PyUnicode_AsEncodedString(pyenc, "ascii", NULL))) { + goto exit; + } + } + else if (Bytes_Check(pyenc)) { + Py_INCREF(pyenc); + pybenc = pyenc; + } + else { + PyErr_Format(PyExc_TypeError, "bad type for encoding: %s", + Py_TYPE(pyenc)->tp_name); + goto exit; + } + + if (-1 == Bytes_AsStringAndSize(pybenc, &tmp, &size)) { goto exit; } @@ -239,6 +261,7 @@ conn_encoding_to_codec(const char *enc) exit: /* pyenc is borrowed: no decref. */ + Py_XDECREF(pybenc); return rv; } @@ -1027,7 +1050,7 @@ conn_tpc_command(connectionObject *self, const char *cmd, XidObject *xid) /* convert the xid into PostgreSQL transaction id while keeping the GIL */ if (!(tid = xid_get_tid(xid))) { goto exit; } - if (!(ctid = PyString_AsString(tid))) { goto exit; } + if (!(ctid = Bytes_AsString(tid))) { goto exit; } Py_BEGIN_ALLOW_THREADS; pthread_mutex_lock(&self->lock); diff --git a/psycopg/connection_type.c b/psycopg/connection_type.c index 9e799be..49c6b94 100644 --- a/psycopg/connection_type.c +++ b/psycopg/connection_type.c @@ -498,7 +498,7 @@ psyco_conn_get_parameter_status(connectionObject *self, PyObject *args) Py_INCREF(Py_None); return Py_None; } - return PyString_FromString(val); + return Text_FromUTF8(val); } diff --git a/psycopg/cursor_type.c b/psycopg/cursor_type.c index d9e925f..4bf5313 100644 --- a/psycopg/cursor_type.c +++ b/psycopg/cursor_type.c @@ -87,7 +87,7 @@ _mogrify(PyObject *var, PyObject *fmt, connectionObject *conn, PyObject **new) just before returning. we also init *new to NULL to exit with an error if we can't complete the mogrification */ n = *new = NULL; - c = PyString_AsString(fmt); + c = Bytes_AsString(fmt); while(*c) { /* handle plain percent symbol in format string */ @@ -116,7 +116,7 @@ _mogrify(PyObject *var, PyObject *fmt, connectionObject *conn, PyObject **new) for (d = c + 2; *d && *d != ')'; d++); if (*d == ')') { - key = PyString_FromStringAndSize(c+2, (Py_ssize_t) (d-c-2)); + key = Bytes_FromStringAndSize(c+2, (Py_ssize_t) (d-c-2)); value = PyObject_GetItem(var, key); /* key has refcnt 1, value the original value + 1 */ @@ -144,7 +144,7 @@ _mogrify(PyObject *var, PyObject *fmt, connectionObject *conn, PyObject **new) optimization over the adapting code and can go away in the future if somebody finds a None adapter usefull. */ if (value == Py_None) { - t = PyString_FromString("NULL"); + t = Text_FromUTF8("NULL"); PyDict_SetItem(n, key, t); /* t is a new object, refcnt = 1, key is at 2 */ @@ -220,7 +220,7 @@ _mogrify(PyObject *var, PyObject *fmt, connectionObject *conn, PyObject **new) d = c+1; if (value == Py_None) { - PyTuple_SET_ITEM(n, index, PyString_FromString("NULL")); + PyTuple_SET_ITEM(n, index, Text_FromUTF8("NULL")); while (*d && !isalpha(*d)) d++; if (*d) *d = 's'; Py_DECREF(value); @@ -267,7 +267,7 @@ static PyObject *_psyco_curs_validate_sql_basic( goto fail; } - if (PyString_Check(sql)) { + if (Bytes_Check(sql)) { /* Necessary for ref-count symmetry with the unicode case: */ Py_INCREF(sql); } @@ -314,7 +314,7 @@ _psyco_curs_merge_query_args(cursorObject *self, the curren exception (we will later restore it if the type or the strings do not match.) */ - if (!(fquery = PyString_Format(query, args))) { + if (!(fquery = Text_Format(query, args))) { PyObject *err, *arg, *trace; int pe = 0; @@ -327,7 +327,7 @@ _psyco_curs_merge_query_args(cursorObject *self, if (PyObject_HasAttrString(arg, "args")) { PyObject *args = PyObject_GetAttrString(arg, "args"); PyObject *str = PySequence_GetItem(args, 0); - const char *s = PyString_AS_STRING(str); + const char *s = Bytes_AS_STRING(str); Dprintf("psyco_curs_execute: -> %s", s); @@ -397,9 +397,15 @@ _psyco_curs_execute(cursorObject *self, } if (self->name != NULL) { + #if PY_MAJOR_VERSION < 3 self->query = PyString_FromFormat( "DECLARE %s CURSOR WITHOUT HOLD FOR %s", self->name, PyString_AS_STRING(fquery)); + #else + self->query = PyUnicode_FromFormat( + "DECLARE %s CURSOR WITHOUT HOLD FOR %U", + self->name, fquery); + #endif Py_DECREF(fquery); } else { @@ -408,9 +414,15 @@ _psyco_curs_execute(cursorObject *self, } else { if (self->name != NULL) { + #if PY_MAJOR_VERSION < 3 self->query = PyString_FromFormat( "DECLARE %s CURSOR WITHOUT HOLD FOR %s", self->name, PyString_AS_STRING(operation)); + #else + self->query = PyUnicode_FromFormat( + "DECLARE %s CURSOR WITHOUT HOLD FOR %U", + self->name, operation); + #endif } else { /* Transfer reference ownership of the str in operation to @@ -423,7 +435,7 @@ _psyco_curs_execute(cursorObject *self, /* At this point, the SQL statement must be str, not unicode */ - res = pq_execute(self, PyString_AS_STRING(self->query), async); + res = pq_execute(self, Bytes_AS_STRING(self->query), async); Dprintf("psyco_curs_execute: res = %d, pgres = %p", res, self->pgres); if (res == -1) { goto fail; } @@ -950,7 +962,7 @@ psyco_curs_callproc(cursorObject *self, PyObject *args, PyObject *kwargs) sql[sl-2] = ')'; sql[sl-1] = '\0'; - operation = PyString_FromString(sql); + operation = Text_FromUTF8(sql); PyMem_Free((void*)sql); if (_psyco_curs_execute(self, operation, parameters, self->conn->async)) { @@ -1105,14 +1117,14 @@ static int _psyco_curs_copy_columns(PyObject *columns, char *columnlist) columnlist[0] = '('; while ((col = PyIter_Next(coliter)) != NULL) { - if (!PyString_Check(col)) { + if (!Bytes_Check(col)) { Py_DECREF(col); Py_DECREF(coliter); PyErr_SetString(PyExc_ValueError, "elements in column list must be strings"); return -1; } - PyString_AsStringAndSize(col, &colname, &collen); + Bytes_AsStringAndSize(col, &colname, &collen); if (offset + collen > DEFAULT_COPYBUFF - 2) { Py_DECREF(col); Py_DECREF(coliter); @@ -1417,7 +1429,7 @@ psyco_curs_copy_expert(cursorObject *self, PyObject *args, PyObject *kwargs) self->copyfile = file; /* At this point, the SQL statement must be str, not unicode */ - if (pq_execute(self, PyString_AS_STRING(sql), 0) != 1) { goto fail; } + if (pq_execute(self, Bytes_AS_STRING(sql), 0) != 1) { goto fail; } res = Py_None; Py_INCREF(res); diff --git a/psycopg/lobject_type.c b/psycopg/lobject_type.c index d07becb..0feffbc 100644 --- a/psycopg/lobject_type.c +++ b/psycopg/lobject_type.c @@ -119,7 +119,7 @@ psyco_lobj_read(lobjectObject *self, PyObject *args) return NULL; } - res = PyString_FromStringAndSize(buffer, size); + res = Bytes_FromStringAndSize(buffer, size); PyMem_Free(buffer); return res; diff --git a/psycopg/microprotocols.c b/psycopg/microprotocols.c index f41d85f..067729f 100644 --- a/psycopg/microprotocols.c +++ b/psycopg/microprotocols.c @@ -83,7 +83,11 @@ _get_superclass_adapter(PyObject *obj, PyObject *proto) Py_ssize_t i, ii; type = Py_TYPE(obj); - if (!((Py_TPFLAGS_HAVE_CLASS & type->tp_flags) && type->tp_mro)) { + if (!( +#if PY_MAJOR_VERSION < 3 + (Py_TPFLAGS_HAVE_CLASS & type->tp_flags) && +#endif + type->tp_mro)) { /* has no mro */ return NULL; } @@ -134,7 +138,7 @@ microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt) /* None is always adapted to NULL */ if (obj == Py_None) - return PyString_FromString("NULL"); + return Text_FromUTF8("NULL"); Dprintf("microprotocols_adapt: trying to adapt %s", Py_TYPE(obj)->tp_name); diff --git a/psycopg/notify_type.c b/psycopg/notify_type.c index 3d2308a..e68daa3 100644 --- a/psycopg/notify_type.c +++ b/psycopg/notify_type.c @@ -78,7 +78,8 @@ notify_init(NotifyObject *self, PyObject *args, PyObject *kwargs) } if (!payload) { - payload = PyString_FromStringAndSize("", 0); + /* XXX review encoding */ + payload = Text_FromUTF8AndSize("", 0); } Py_CLEAR(self->pid); @@ -213,7 +214,7 @@ notify_repr(NotifyObject *self) PyObject *format = NULL; PyObject *args = NULL; - if (!(format = PyString_FromString("Notify(%r, %r, %r)"))) { + if (!(format = Text_FromUTF8("Notify(%r, %r, %r)"))) { goto exit; } @@ -225,7 +226,7 @@ notify_repr(NotifyObject *self) Py_INCREF(self->payload); PyTuple_SET_ITEM(args, 2, self->payload); - rv = PyString_Format(format, args); + rv = Text_Format(format, args); exit: Py_XDECREF(args); diff --git a/psycopg/pqpath.c b/psycopg/pqpath.c index e15fc7a..745919b 100644 --- a/psycopg/pqpath.c +++ b/psycopg/pqpath.c @@ -980,14 +980,15 @@ _pq_fetch_tuples(cursorObject *curs) } Dprintf("_pq_fetch_tuples: using cast at %p (%s) for type %d", - cast, PyString_AS_STRING(((typecastObject*)cast)->name), + cast, Bytes_AS_STRING(((typecastObject*)cast)->name), PQftype(curs->pgres,i)); Py_INCREF(cast); PyTuple_SET_ITEM(curs->casts, i, cast); /* 1/ fill the other fields */ PyTuple_SET_ITEM(dtitem, 0, - PyString_FromString(PQfname(curs->pgres, i))); + /* XXX guaranteed to be ASCII/UTF8? */ + Text_FromUTF8(PQfname(curs->pgres, i))); PyTuple_SET_ITEM(dtitem, 1, type); /* 2/ display size is the maximum size of this field result tuples. */ @@ -1066,13 +1067,13 @@ _pq_copy_in_v3(cursorObject *curs) while (1) { o = PyObject_CallFunctionObjArgs(func, size, NULL); - if (!(o && PyString_Check(o) && (length = PyString_GET_SIZE(o)) != -1)) { + if (!(o && Bytes_Check(o) && (length = Bytes_GET_SIZE(o)) != -1)) { error = 1; } if (length == 0 || length > INT_MAX || error == 1) break; Py_BEGIN_ALLOW_THREADS; - res = PQputCopyData(curs->conn->pgconn, PyString_AS_STRING(o), + res = PQputCopyData(curs->conn->pgconn, Bytes_AS_STRING(o), /* Py_ssize_t->int cast was validated above */ (int) length); Dprintf("_pq_copy_in_v3: sent %d bytes of data; res = %d", @@ -1219,7 +1220,7 @@ pq_fetch(cursorObject *curs) /* backend status message */ Py_XDECREF(curs->pgstatus); - curs->pgstatus = PyString_FromString(PQcmdStatus(curs->pgres)); + curs->pgstatus = Text_FromUTF8(PQcmdStatus(curs->pgres)); switch(pgstatus) { diff --git a/psycopg/psycopgmodule.c b/psycopg/psycopgmodule.c index 9b3f54c..568cf6e 100644 --- a/psycopg/psycopgmodule.c +++ b/psycopg/psycopgmodule.c @@ -126,17 +126,38 @@ psyco_connect(PyObject *self, PyObject *args, PyObject *keywds) return NULL; } +#if PY_MAJOR_VERSION < 3 if (pyport && PyString_Check(pyport)) { - PyObject *pyint = PyInt_FromString(PyString_AsString(pyport), NULL, 10); - if (!pyint) goto fail; - /* Must use PyInt_AsLong rather than PyInt_AS_LONG, because - * PyInt_FromString can return a PyLongObject: */ - iport = PyInt_AsLong(pyint); - Py_DECREF(pyint); + PyObject *pyint = PyInt_FromString(PyString_AsString(pyport), NULL, 10); + if (!pyint) goto fail; + /* Must use PyInt_AsLong rather than PyInt_AS_LONG, because + * PyInt_FromString can return a PyLongObject: */ + iport = PyInt_AsLong(pyint); + Py_DECREF(pyint); + if (iport == -1 && PyErr_Occurred()) + goto fail; } else if (pyport && PyInt_Check(pyport)) { - iport = PyInt_AsLong(pyport); + iport = PyInt_AsLong(pyport); + if (iport == -1 && PyErr_Occurred()) + goto fail; } +#else + if (pyport && PyUnicode_Check(pyport)) { + PyObject *pyint = PyObject_CallFunction((PyObject*)&PyLong_Type, + "Oi", pyport, 10); + if (!pyint) goto fail; + iport = PyLong_AsLong(pyint); + Py_DECREF(pyint); + if (iport == -1 && PyErr_Occurred()) + goto fail; + } + else if (pyport && PyLong_Check(pyport)) { + iport = PyLong_AsLong(pyport); + if (iport == -1 && PyErr_Occurred()) + goto fail; + } +#endif else if (pyport != NULL) { PyErr_SetString(PyExc_TypeError, "port must be a string or int"); goto fail; @@ -288,13 +309,23 @@ psyco_adapters_init(PyObject *mod) PyTypeObject *type; microprotocols_add(&PyFloat_Type, NULL, (PyObject*)&pfloatType); +#if PY_MAJOR_VERSION < 3 microprotocols_add(&PyInt_Type, NULL, (PyObject*)&asisType); +#endif microprotocols_add(&PyLong_Type, NULL, (PyObject*)&asisType); microprotocols_add(&PyBool_Type, NULL, (PyObject*)&pbooleanType); +#if PY_MAJOR_VERSION < 3 microprotocols_add(&PyString_Type, NULL, (PyObject*)&qstringType); +#endif microprotocols_add(&PyUnicode_Type, NULL, (PyObject*)&qstringType); +#if PY_MAJOR_VERSION < 3 microprotocols_add(&PyBuffer_Type, NULL, (PyObject*)&binaryType); +#else + microprotocols_add(&PyBytes_Type, NULL, (PyObject*)&binaryType); + microprotocols_add(&PyByteArray_Type, NULL, (PyObject*)&binaryType); + microprotocols_add(&PyMemoryView_Type, NULL, (PyObject*)&binaryType); +#endif microprotocols_add(&PyList_Type, NULL, (PyObject*)&listType); if ((type = (PyTypeObject*)psyco_GetDecimalType()) != NULL) @@ -407,7 +438,7 @@ static void psyco_encodings_fill(PyObject *dict) encodingPair *enc; for (enc = encodings; enc->pgenc != NULL; enc++) { - PyObject *value = PyString_FromString(enc->pyenc); + PyObject *value = Text_FromUTF8(enc->pyenc); PyDict_SetItemString(dict, enc->pgenc, value); Py_DECREF(value); } @@ -472,12 +503,18 @@ psyco_errors_init(void) dict = PyDict_New(); if (exctable[i].docstr) { - str = PyString_FromString(exctable[i].docstr); + str = Text_FromUTF8(exctable[i].docstr); PyDict_SetItemString(dict, "__doc__", str); } - if (exctable[i].base == 0) + if (exctable[i].base == 0) { + #if PY_MAJOR_VERSION < 3 base = PyExc_StandardError; + #else + /* StandardError is gone in 3.0 */ + base = NULL; + #endif + } else base = *exctable[i].base; @@ -546,13 +583,16 @@ psyco_set_error(PyObject *exc, PyObject *curs, const char *msg, if (err) { if (pgerror) { - t = PyString_FromString(pgerror); + /* XXX is this always ASCII? If not, it needs + to be decoded properly for Python 3. */ + t = Text_FromUTF8(pgerror); PyObject_SetAttrString(err, "pgerror", t); Py_DECREF(t); } if (pgcode) { - t = PyString_FromString(pgcode); + /* XXX likewise */ + t = Text_FromUTF8(pgcode); PyObject_SetAttrString(err, "pgcode", t); Py_DECREF(t); } @@ -703,12 +743,26 @@ static PyMethodDef psycopgMethods[] = { {NULL, NULL, 0, NULL} /* Sentinel */ }; +#if PY_MAJOR_VERSION > 2 +static struct PyModuleDef psycopgmodule = { + PyModuleDef_HEAD_INIT, + "_psycopg", + NULL, + -1, + psycopgMethods, + NULL, + NULL, + NULL, + NULL +}; +#endif + PyMODINIT_FUNC -init_psycopg(void) +INIT_MODULE(_psycopg)(void) { static void *PSYCOPG_API[PSYCOPG_API_pointers]; - PyObject *module, *dict; + PyObject *module = NULL, *dict; PyObject *c_api_object; #ifdef PSYCOPG_DEBUG @@ -734,36 +788,36 @@ init_psycopg(void) Py_TYPE(&NotifyType) = &PyType_Type; Py_TYPE(&XidType) = &PyType_Type; - if (PyType_Ready(&connectionType) == -1) return; - if (PyType_Ready(&cursorType) == -1) return; - if (PyType_Ready(&typecastType) == -1) return; - if (PyType_Ready(&qstringType) == -1) return; - if (PyType_Ready(&binaryType) == -1) return; - if (PyType_Ready(&isqlquoteType) == -1) return; - if (PyType_Ready(&pbooleanType) == -1) return; - if (PyType_Ready(&pfloatType) == -1) return; - if (PyType_Ready(&pdecimalType) == -1) return; - if (PyType_Ready(&asisType) == -1) return; - if (PyType_Ready(&listType) == -1) return; - if (PyType_Ready(&chunkType) == -1) return; - if (PyType_Ready(&NotifyType) == -1) return; - if (PyType_Ready(&XidType) == -1) return; + if (PyType_Ready(&connectionType) == -1) goto exit; + if (PyType_Ready(&cursorType) == -1) goto exit; + if (PyType_Ready(&typecastType) == -1) goto exit; + if (PyType_Ready(&qstringType) == -1) goto exit; + if (PyType_Ready(&binaryType) == -1) goto exit; + if (PyType_Ready(&isqlquoteType) == -1) goto exit; + if (PyType_Ready(&pbooleanType) == -1) goto exit; + if (PyType_Ready(&pfloatType) == -1) goto exit; + if (PyType_Ready(&pdecimalType) == -1) goto exit; + if (PyType_Ready(&asisType) == -1) goto exit; + if (PyType_Ready(&listType) == -1) goto exit; + if (PyType_Ready(&chunkType) == -1) goto exit; + if (PyType_Ready(&NotifyType) == -1) goto exit; + if (PyType_Ready(&XidType) == -1) goto exit; #ifdef PSYCOPG_EXTENSIONS Py_TYPE(&lobjectType) = &PyType_Type; - if (PyType_Ready(&lobjectType) == -1) return; + if (PyType_Ready(&lobjectType) == -1) goto exit; #endif /* import mx.DateTime module, if necessary */ #ifdef HAVE_MXDATETIME Py_TYPE(&mxdatetimeType) = &PyType_Type; - if (PyType_Ready(&mxdatetimeType) == -1) return; + 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"); - return; + goto exit; } - if (psyco_adapter_mxdatetime_init()) { return; } + if (psyco_adapter_mxdatetime_init()) { goto exit; } #endif /* import python builtin datetime module, if available */ @@ -771,22 +825,22 @@ init_psycopg(void) if (pyDateTimeModuleP == NULL) { Dprintf("initpsycopg: can't import datetime module"); PyErr_SetString(PyExc_ImportError, "can't import datetime module"); - return; + goto exit; } /* Initialize the PyDateTimeAPI everywhere is used */ PyDateTime_IMPORT; - if (psyco_adapter_datetime_init()) { return; } + if (psyco_adapter_datetime_init()) { goto exit; } Py_TYPE(&pydatetimeType) = &PyType_Type; - if (PyType_Ready(&pydatetimeType) == -1) return; + if (PyType_Ready(&pydatetimeType) == -1) goto exit; /* import psycopg2.tz anyway (TODO: replace with C-level module?) */ pyPsycopgTzModule = PyImport_ImportModule("psycopg2.tz"); if (pyPsycopgTzModule == NULL) { Dprintf("initpsycopg: can't import psycopg2.tz module"); PyErr_SetString(PyExc_ImportError, "can't import psycopg2.tz module"); - return; + goto exit; } pyPsycopgTzLOCAL = PyObject_GetAttrString(pyPsycopgTzModule, "LOCAL"); @@ -794,7 +848,13 @@ init_psycopg(void) PyObject_GetAttrString(pyPsycopgTzModule, "FixedOffsetTimezone"); /* initialize the module and grab module's dictionary */ +#if PY_MAJOR_VERSION < 3 module = Py_InitModule("_psycopg", psycopgMethods); +#else + module = PyModule_Create(&psycopgmodule); +#endif + if (!module) { goto exit; } + dict = PyModule_GetDict(module); /* initialize all the module's exported functions */ @@ -812,9 +872,9 @@ init_psycopg(void) /* set some module's parameters */ PyModule_AddStringConstant(module, "__version__", PSYCOPG_VERSION); PyModule_AddStringConstant(module, "__doc__", "psycopg PostgreSQL driver"); - PyModule_AddObject(module, "apilevel", PyString_FromString(APILEVEL)); + PyModule_AddObject(module, "apilevel", Text_FromUTF8(APILEVEL)); PyModule_AddObject(module, "threadsafety", PyInt_FromLong(THREADSAFETY)); - PyModule_AddObject(module, "paramstyle", PyString_FromString(PARAMSTYLE)); + PyModule_AddObject(module, "paramstyle", Text_FromUTF8(PARAMSTYLE)); /* put new types in module dictionary */ PyModule_AddObject(module, "connection", (PyObject*)&connectionType); @@ -866,4 +926,11 @@ init_psycopg(void) #endif Dprintf("initpsycopg: module initialization complete"); + +exit: +#if PY_MAJOR_VERSION > 2 + return module; +#else + return; +#endif } diff --git a/psycopg/python.h b/psycopg/python.h index b514c2c..8fee7fd 100644 --- a/psycopg/python.h +++ b/psycopg/python.h @@ -74,4 +74,66 @@ #define FORMAT_CODE_SIZE_T "%zu" #endif +/* Abstract from text type. Only supported for ASCII and UTF-8 */ +#if PY_MAJOR_VERSION < 3 +#define Text_Type PyString_Type +#define Text_Check(s) PyString_Check(s) +#define Text_Format(f,a) PyString_Format(f,a) +#define Text_FromUTF8(s) PyString_FromString(s) +#define Text_FromUTF8AndSize(s,n) PyString_FromStringAndSize(s,n) +/* f must contain exactly a %s placeholder */ +#define Text_FromFormatS(f,s) PyString_FromFormat(f, PyString_AsString(s)) +#define Text_S "%s" +#else +#define Text_Type PyUnicode_Type +#define Text_Check(s) PyUnicode_Check(s) +#define Text_Format(f,a) PyUnicode_Format(f,a) +#define Text_FromUTF8(s) PyUnicode_FromString(s) +#define Text_FromUTF8AndSize(s,n) PyUnicode_FromStringAndSize(s,n) +/* f must contain exactly a %U placeholder */ +#define Text_FromFormatS(f,s) PyUnicode_FromFormat(f, s) +#define Text_S "%U" +#endif + +#if PY_MAJOR_VERSION > 2 +#define PyInt_Type PyLong_Type +#define PyInt_AsLong PyLong_AsLong +#define PyInt_FromLong PyLong_FromLong +#define PyInt_FromSsize_t PyLong_FromSsize_t +#define PyString_FromFormat PyUnicode_FromFormat +#define Py_TPFLAGS_HAVE_ITER 0L +#define Py_TPFLAGS_HAVE_RICHCOMPARE 0L +#ifndef PyNumber_Int +#define PyNumber_Int PyNumber_Long +#endif +#endif /* PY_MAJOR_VERSION > 2 */ + +#if PY_MAJOR_VERSION < 3 +/* XXX BytesType -> Bytes_Type */ +#define BytesType PyString_Type +#define Bytes_Check PyString_Check +#define Bytes_AS_STRING PyString_AS_STRING +#define Bytes_GET_SIZE PyString_GET_SIZE +#define Bytes_Size PyString_Size +#define Bytes_AsString PyString_AsString +#define Bytes_AsStringAndSize PyString_AsStringAndSize +#define Bytes_FromStringAndSize PyString_FromStringAndSize +#else +#define BytesType PyBytes_Type +#define Bytes_Check PyBytes_Check +#define Bytes_AS_STRING PyBytes_AS_STRING +#define Bytes_GET_SIZE PyBytes_GET_SIZE +#define Bytes_Size PyBytes_Size +#define Bytes_AsString PyBytes_AsString +#define Bytes_AsStringAndSize PyBytes_AsStringAndSize +#define Bytes_FromStringAndSize PyBytes_FromStringAndSize +#endif + +/* Mangle the module name into the name of the module init function */ +#if PY_MAJOR_VERSION > 2 +#define INIT_MODULE(m) PyInit_ ## m +#else +#define INIT_MODULE(m) init ## m +#endif + #endif /* !defined(PSYCOPG_PYTHON_H) */ diff --git a/psycopg/typecast.c b/psycopg/typecast.c index 47c3c58..a58f3a1 100644 --- a/psycopg/typecast.c +++ b/psycopg/typecast.c @@ -429,24 +429,22 @@ typecast_del(void *self) static PyObject * typecast_call(PyObject *obj, PyObject *args, PyObject *kwargs) { - PyObject *string, *cursor; + char *string; + Py_ssize_t length; + PyObject *cursor; - if (!PyArg_ParseTuple(args, "OO", &string, &cursor)) { + if (!PyArg_ParseTuple(args, "z#O", &string, &length, &cursor)) { return NULL; } // If the string is not a string but a None value we're being called - // from a Python-defined caster. There is no need to convert, just - // return it. - - if (string == Py_None) { - Py_INCREF(string); - return string; + // from a Python-defined caster. + if (!string) { + Py_INCREF(Py_None); + return Py_None; } - return typecast_cast(obj, - PyString_AsString(string), PyString_Size(string), - cursor); + return typecast_cast(obj, string, length, cursor); } PyTypeObject typecastType = { @@ -560,7 +558,7 @@ typecast_from_python(PyObject *self, PyObject *args, PyObject *keywds) if (!PyArg_ParseTupleAndKeywords(args, keywds, "O!|O!OO", kwlist, &PyTuple_Type, &v, - &PyString_Type, &name, + &Text_Type, &name, &cast, &base)) { return NULL; } @@ -585,7 +583,7 @@ typecast_from_c(typecastObject_initlist *type, PyObject *dict) } } - name = PyString_FromString(type->name); + name = Text_FromUTF8(type->name); if (!name) goto end; while (type->values[len] != 0) len++; diff --git a/psycopg/typecast_basic.c b/psycopg/typecast_basic.c index 634fc45..20c7b81 100644 --- a/psycopg/typecast_basic.c +++ b/psycopg/typecast_basic.c @@ -25,6 +25,7 @@ /** INTEGER - cast normal integers (4 bytes) to python int **/ +#if PY_MAJOR_VERSION < 3 static PyObject * typecast_INTEGER_cast(const char *s, Py_ssize_t len, PyObject *curs) { @@ -37,6 +38,9 @@ typecast_INTEGER_cast(const char *s, Py_ssize_t len, PyObject *curs) } return PyInt_FromString((char *)s, NULL, 0); } +#else +#define typecast_INTEGER_cast typecast_LONGINTEGER_cast +#endif /** LONGINTEGER - cast long integers (8 bytes) to python long **/ @@ -59,23 +63,30 @@ static PyObject * typecast_FLOAT_cast(const char *s, Py_ssize_t len, PyObject *curs) { PyObject *str = NULL, *flo = NULL; - char *pend; if (s == NULL) {Py_INCREF(Py_None); return Py_None;} - str = PyString_FromStringAndSize(s, len); - flo = PyFloat_FromString(str, &pend); + str = Text_FromUTF8AndSize(s, len); +#if PY_MAJOR_VERSION < 3 + flo = PyFloat_FromString(str, NULL); +#else + flo = PyFloat_FromString(str); +#endif Py_DECREF(str); return flo; } /** STRING - cast strings of any type to python string **/ +#if PY_MAJOR_VERSION < 3 static PyObject * typecast_STRING_cast(const char *s, Py_ssize_t len, PyObject *curs) { if (s == NULL) {Py_INCREF(Py_None); return Py_None;} return PyString_FromStringAndSize(s, len); } +#else +#define typecast_STRING_cast typecast_UNICODE_cast +#endif /** UNICODE - cast strings of any type to a python unicode object **/ diff --git a/psycopg/typecast_binary.c b/psycopg/typecast_binary.c index 261fc6f..e93aad5 100644 --- a/psycopg/typecast_binary.c +++ b/psycopg/typecast_binary.c @@ -53,6 +53,9 @@ chunk_repr(chunkObject *self) ); } +#if PY_MAJOR_VERSION < 3 + +/* XXX support 3.0 buffer protocol */ static Py_ssize_t chunk_getreadbuffer(chunkObject *self, Py_ssize_t segment, void **ptr) { @@ -82,6 +85,22 @@ static PyBufferProcs chunk_as_buffer = (charbufferproc) NULL }; +#else + +/* 3.0 buffer interface */ +int chunk_getbuffer(PyObject *_self, Py_buffer *view, int flags) +{ + chunkObject *self = (chunkObject*)_self; + return PyBuffer_FillInfo(view, _self, self->base, self->len, 1, flags); +} +static PyBufferProcs chunk_as_buffer = +{ + chunk_getbuffer, + NULL, +}; + +#endif + #define chunk_doc "memory chunk" PyTypeObject chunkType = { @@ -156,8 +175,13 @@ typecast_BINARY_cast(const char *s, Py_ssize_t l, PyObject *curs) /* size_t->Py_ssize_t cast was validated above: */ chunk->len = (Py_ssize_t) len; +#if PY_MAJOR_VERSION < 3 if ((res = PyBuffer_FromObject((PyObject *)chunk, 0, chunk->len)) == NULL) goto fail; +#else + if ((res = PyMemoryView_FromObject((PyObject*)chunk)) == NULL) + goto fail; +#endif /* PyBuffer_FromObject() created a new reference. We'll release our * reference held in 'chunk' in the 'cleanup' clause. */ diff --git a/psycopg/xid_type.c b/psycopg/xid_type.c index b725135..51cd449 100644 --- a/psycopg/xid_type.c +++ b/psycopg/xid_type.c @@ -150,11 +150,11 @@ xid_init(XidObject *self, PyObject *args, PyObject *kwargs) Py_XDECREF(tmp); tmp = self->gtrid; - self->gtrid = PyString_FromString(gtrid); + self->gtrid = Text_FromUTF8(gtrid); Py_XDECREF(tmp); tmp = self->bqual; - self->bqual = PyString_FromString(bqual); + self->bqual = Text_FromUTF8(bqual); Py_XDECREF(tmp); return 0; @@ -233,7 +233,7 @@ xid_repr(XidObject *self) PyObject *args = NULL; if (Py_None == self->format_id) { - if (!(format = PyString_FromString("<Xid: %r (unparsed)>"))) { + if (!(format = Text_FromUTF8("<Xid: %r (unparsed)>"))) { goto exit; } if (!(args = PyTuple_New(1))) { goto exit; } @@ -241,7 +241,7 @@ xid_repr(XidObject *self) PyTuple_SET_ITEM(args, 0, self->gtrid); } else { - if (!(format = PyString_FromString("<Xid: (%r, %r, %r)>"))) { + if (!(format = Text_FromUTF8("<Xid: (%r, %r, %r)>"))) { goto exit; } if (!(args = PyTuple_New(3))) { goto exit; } @@ -253,7 +253,7 @@ xid_repr(XidObject *self) PyTuple_SET_ITEM(args, 2, self->bqual); } - rv = PyString_Format(format, args); + rv = Text_Format(format, args); exit: Py_XDECREF(args); @@ -457,7 +457,7 @@ xid_get_tid(XidObject *self) if (!(ebqual = _xid_encode64(self->bqual))) { goto exit; } /* rv = "%d_%s_%s" % (format_id, egtrid, ebqual) */ - if (!(format = PyString_FromString("%d_%s_%s"))) { goto exit; } + if (!(format = Text_FromUTF8("%d_%s_%s"))) { goto exit; } if (!(args = PyTuple_New(3))) { goto exit; } Py_INCREF(self->format_id); @@ -465,7 +465,7 @@ xid_get_tid(XidObject *self) PyTuple_SET_ITEM(args, 1, egtrid); egtrid = NULL; PyTuple_SET_ITEM(args, 2, ebqual); ebqual = NULL; - if (!(rv = PyString_Format(format, args))) { goto exit; } + if (!(rv = Text_Format(format, args))) { goto exit; } } exit: @@ -621,7 +621,7 @@ XidObject * xid_from_string(PyObject *str) { XidObject *rv; - if (!(PyString_Check(str) || PyUnicode_Check(str))) { + if (!(Bytes_Check(str) || PyUnicode_Check(str))) { PyErr_SetString(PyExc_TypeError, "not a valid transaction id"); return NULL; } |