summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--psycopg/adapter_asis.c2
-rw-r--r--psycopg/adapter_binary.c10
-rw-r--r--psycopg/adapter_datetime.c16
-rw-r--r--psycopg/adapter_list.c10
-rw-r--r--psycopg/adapter_pboolean.c8
-rw-r--r--psycopg/adapter_pdecimal.c6
-rw-r--r--psycopg/adapter_pfloat.c4
-rw-r--r--psycopg/adapter_qstring.c7
-rw-r--r--psycopg/connection_int.c35
-rw-r--r--psycopg/connection_type.c2
-rw-r--r--psycopg/cursor_type.c36
-rw-r--r--psycopg/lobject_type.c2
-rw-r--r--psycopg/microprotocols.c8
-rw-r--r--psycopg/notify_type.c7
-rw-r--r--psycopg/pqpath.c11
-rw-r--r--psycopg/psycopgmodule.c143
-rw-r--r--psycopg/python.h62
-rw-r--r--psycopg/typecast.c24
-rw-r--r--psycopg/typecast_basic.c17
-rw-r--r--psycopg/typecast_binary.c24
-rw-r--r--psycopg/xid_type.c16
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;
}