Here's the patch to support Python >=3.1 with PL/Python.  The
compatibility code is mostly in line with the usual 2->3 C porting
practice and is documented inline.

I needed to create an arguably weird hack to manage the regression
tests.  Instead of creating a new expected file for pretty much every
test file and also for some input files (where Python syntax had
changed), a sed script creates a complete Python 3 compatible set of
input and output files.  Doesn't look pretty but works quite well.  If
anyone has a better idea, please let me know.
diff --git a/src/pl/plpython/Makefile b/src/pl/plpython/Makefile
index 373bc79..840b874 100644
--- a/src/pl/plpython/Makefile
+++ b/src/pl/plpython/Makefile
@@ -88,8 +88,32 @@ installdirs: installdirs-lib
 
 uninstall: uninstall-lib
 
+ifneq (,$(findstring 3.,$(python_version)))
+# Adjust regression tests for Python 3 compatibility
+prep3:
+	mkdir -p 3 3/sql 3/expected
+	for file in $(srcdir)/sql/* $(srcdir)/expected/*; do \
+	  sed -r -e 's/except ([[:alpha:].]+), ?(\w+):/except \1 as \2:/g' \
+	         -e "s/<type 'exceptions\.(\w+)'>/<class '\1'>/g" \
+	         -e "s/<type 'long'>/<class 'int'>/g" \
+	         -e "s/\b([0-9]+)L\b/\1/g" \
+	         -e 's/\bu"/"/g' \
+	         -e "s/\bu'/'/g" \
+	         -e "s/def next\b/def __next__/g" \
+	    $$file >`echo $$file | sed 's,$(srcdir),3,'`; \
+	done
+
+clean3:
+	rm -rf 3/
+
+installcheck: submake prep3
+	cd 3 && $(top_builddir)/../src/test/regress/pg_regress --inputdir=. --psqldir=$(PSQLDIR) $(REGRESS_OPTS) $(REGRESS)
+
+clean: clean3
+else
 installcheck: submake
 	$(top_builddir)/src/test/regress/pg_regress --inputdir=$(srcdir) --psqldir=$(PSQLDIR) $(REGRESS_OPTS) $(REGRESS)
+endif
 
 .PHONY: submake
 submake:
diff --git a/src/pl/plpython/expected/README b/src/pl/plpython/expected/README
index 47f31e8..a187937 100644
--- a/src/pl/plpython/expected/README
+++ b/src/pl/plpython/expected/README
@@ -8,3 +8,5 @@ plpython_unicode_0.out		any version, when server encoding != SQL_ASCII and clien
 plpython_unicode_2.out		Python 2.2
 plpython_unicode_3.out		Python 2.3, 2.4
 plpython_unicode_5.out		Python 2.5, 2.6
+
+plpython_types_3.out		Python 3.1
diff --git a/src/pl/plpython/expected/plpython_types_3.out b/src/pl/plpython/expected/plpython_types_3.out
new file mode 100644
index 0000000..3fcb0f4
--- /dev/null
+++ b/src/pl/plpython/expected/plpython_types_3.out
@@ -0,0 +1,479 @@
+--
+-- Test data type behavior
+--
+--
+-- Base/common types
+--
+CREATE FUNCTION test_type_conversion_bool(x bool) RETURNS bool AS $$
+plpy.info(x, type(x))
+return x
+$$ LANGUAGE plpythonu;
+SELECT * FROM test_type_conversion_bool(true);
+INFO:  (True, <class 'bool'>)
+CONTEXT:  PL/Python function "test_type_conversion_bool"
+ test_type_conversion_bool 
+---------------------------
+ t
+(1 row)
+
+SELECT * FROM test_type_conversion_bool(false);
+INFO:  (False, <class 'bool'>)
+CONTEXT:  PL/Python function "test_type_conversion_bool"
+ test_type_conversion_bool 
+---------------------------
+ f
+(1 row)
+
+SELECT * FROM test_type_conversion_bool(null);
+INFO:  (None, <class 'NoneType'>)
+CONTEXT:  PL/Python function "test_type_conversion_bool"
+ test_type_conversion_bool 
+---------------------------
+ 
+(1 row)
+
+-- test various other ways to express Booleans in Python
+CREATE FUNCTION test_type_conversion_bool_other(n int) RETURNS bool AS $$
+# numbers
+if n == 0:
+   ret = 0
+elif n == 1:
+   ret = 5
+# strings
+elif n == 2:
+   ret = ''
+elif n == 3:
+   ret = 'fa' # true in Python, false in PostgreSQL
+# containers
+elif n == 4:
+   ret = []
+elif n == 5:
+   ret = [0]
+plpy.info(ret, not not ret)
+return ret
+$$ LANGUAGE plpythonu;
+SELECT * FROM test_type_conversion_bool_other(0);
+INFO:  (0, False)
+CONTEXT:  PL/Python function "test_type_conversion_bool_other"
+ test_type_conversion_bool_other 
+---------------------------------
+ f
+(1 row)
+
+SELECT * FROM test_type_conversion_bool_other(1);
+INFO:  (5, True)
+CONTEXT:  PL/Python function "test_type_conversion_bool_other"
+ test_type_conversion_bool_other 
+---------------------------------
+ t
+(1 row)
+
+SELECT * FROM test_type_conversion_bool_other(2);
+INFO:  ('', False)
+CONTEXT:  PL/Python function "test_type_conversion_bool_other"
+ test_type_conversion_bool_other 
+---------------------------------
+ f
+(1 row)
+
+SELECT * FROM test_type_conversion_bool_other(3);
+INFO:  ('fa', True)
+CONTEXT:  PL/Python function "test_type_conversion_bool_other"
+ test_type_conversion_bool_other 
+---------------------------------
+ t
+(1 row)
+
+SELECT * FROM test_type_conversion_bool_other(4);
+INFO:  ([], False)
+CONTEXT:  PL/Python function "test_type_conversion_bool_other"
+ test_type_conversion_bool_other 
+---------------------------------
+ f
+(1 row)
+
+SELECT * FROM test_type_conversion_bool_other(5);
+INFO:  ([0], True)
+CONTEXT:  PL/Python function "test_type_conversion_bool_other"
+ test_type_conversion_bool_other 
+---------------------------------
+ t
+(1 row)
+
+CREATE FUNCTION test_type_conversion_char(x char) RETURNS char AS $$
+plpy.info(x, type(x))
+return x
+$$ LANGUAGE plpythonu;
+SELECT * FROM test_type_conversion_char('a');
+INFO:  ('a', <class 'str'>)
+CONTEXT:  PL/Python function "test_type_conversion_char"
+ test_type_conversion_char 
+---------------------------
+ a
+(1 row)
+
+SELECT * FROM test_type_conversion_char(null);
+INFO:  (None, <class 'NoneType'>)
+CONTEXT:  PL/Python function "test_type_conversion_char"
+ test_type_conversion_char 
+---------------------------
+ 
+(1 row)
+
+CREATE FUNCTION test_type_conversion_int2(x int2) RETURNS int2 AS $$
+plpy.info(x, type(x))
+return x
+$$ LANGUAGE plpythonu;
+SELECT * FROM test_type_conversion_int2(100::int2);
+INFO:  (100, <class 'int'>)
+CONTEXT:  PL/Python function "test_type_conversion_int2"
+ test_type_conversion_int2 
+---------------------------
+                       100
+(1 row)
+
+SELECT * FROM test_type_conversion_int2(-100::int2);
+INFO:  (-100, <class 'int'>)
+CONTEXT:  PL/Python function "test_type_conversion_int2"
+ test_type_conversion_int2 
+---------------------------
+                      -100
+(1 row)
+
+SELECT * FROM test_type_conversion_int2(null);
+INFO:  (None, <class 'NoneType'>)
+CONTEXT:  PL/Python function "test_type_conversion_int2"
+ test_type_conversion_int2 
+---------------------------
+                          
+(1 row)
+
+CREATE FUNCTION test_type_conversion_int4(x int4) RETURNS int4 AS $$
+plpy.info(x, type(x))
+return x
+$$ LANGUAGE plpythonu;
+SELECT * FROM test_type_conversion_int4(100);
+INFO:  (100, <class 'int'>)
+CONTEXT:  PL/Python function "test_type_conversion_int4"
+ test_type_conversion_int4 
+---------------------------
+                       100
+(1 row)
+
+SELECT * FROM test_type_conversion_int4(-100);
+INFO:  (-100, <class 'int'>)
+CONTEXT:  PL/Python function "test_type_conversion_int4"
+ test_type_conversion_int4 
+---------------------------
+                      -100
+(1 row)
+
+SELECT * FROM test_type_conversion_int4(null);
+INFO:  (None, <class 'NoneType'>)
+CONTEXT:  PL/Python function "test_type_conversion_int4"
+ test_type_conversion_int4 
+---------------------------
+                          
+(1 row)
+
+CREATE FUNCTION test_type_conversion_int8(x int8) RETURNS int8 AS $$
+plpy.info(x, type(x))
+return x
+$$ LANGUAGE plpythonu;
+SELECT * FROM test_type_conversion_int8(100);
+INFO:  (100, <class 'int'>)
+CONTEXT:  PL/Python function "test_type_conversion_int8"
+ test_type_conversion_int8 
+---------------------------
+                       100
+(1 row)
+
+SELECT * FROM test_type_conversion_int8(-100);
+INFO:  (-100, <class 'int'>)
+CONTEXT:  PL/Python function "test_type_conversion_int8"
+ test_type_conversion_int8 
+---------------------------
+                      -100
+(1 row)
+
+SELECT * FROM test_type_conversion_int8(5000000000);
+INFO:  (5000000000, <class 'int'>)
+CONTEXT:  PL/Python function "test_type_conversion_int8"
+ test_type_conversion_int8 
+---------------------------
+                5000000000
+(1 row)
+
+SELECT * FROM test_type_conversion_int8(null);
+INFO:  (None, <class 'NoneType'>)
+CONTEXT:  PL/Python function "test_type_conversion_int8"
+ test_type_conversion_int8 
+---------------------------
+                          
+(1 row)
+
+CREATE FUNCTION test_type_conversion_numeric(x numeric) RETURNS numeric AS $$
+plpy.info(x, type(x))
+return x
+$$ LANGUAGE plpythonu;
+/* The current implementation converts numeric to float. */
+SELECT * FROM test_type_conversion_numeric(100);
+INFO:  (100.0, <class 'float'>)
+CONTEXT:  PL/Python function "test_type_conversion_numeric"
+ test_type_conversion_numeric 
+------------------------------
+                        100.0
+(1 row)
+
+SELECT * FROM test_type_conversion_numeric(-100);
+INFO:  (-100.0, <class 'float'>)
+CONTEXT:  PL/Python function "test_type_conversion_numeric"
+ test_type_conversion_numeric 
+------------------------------
+                       -100.0
+(1 row)
+
+SELECT * FROM test_type_conversion_numeric(5000000000.5);
+INFO:  (5000000000.5, <class 'float'>)
+CONTEXT:  PL/Python function "test_type_conversion_numeric"
+ test_type_conversion_numeric 
+------------------------------
+                 5000000000.5
+(1 row)
+
+SELECT * FROM test_type_conversion_numeric(null);
+INFO:  (None, <class 'NoneType'>)
+CONTEXT:  PL/Python function "test_type_conversion_numeric"
+ test_type_conversion_numeric 
+------------------------------
+                             
+(1 row)
+
+CREATE FUNCTION test_type_conversion_float4(x float4) RETURNS float4 AS $$
+plpy.info(x, type(x))
+return x
+$$ LANGUAGE plpythonu;
+SELECT * FROM test_type_conversion_float4(100);
+INFO:  (100.0, <class 'float'>)
+CONTEXT:  PL/Python function "test_type_conversion_float4"
+ test_type_conversion_float4 
+-----------------------------
+                         100
+(1 row)
+
+SELECT * FROM test_type_conversion_float4(-100);
+INFO:  (-100.0, <class 'float'>)
+CONTEXT:  PL/Python function "test_type_conversion_float4"
+ test_type_conversion_float4 
+-----------------------------
+                        -100
+(1 row)
+
+SELECT * FROM test_type_conversion_float4(5000.5);
+INFO:  (5000.5, <class 'float'>)
+CONTEXT:  PL/Python function "test_type_conversion_float4"
+ test_type_conversion_float4 
+-----------------------------
+                      5000.5
+(1 row)
+
+SELECT * FROM test_type_conversion_float4(null);
+INFO:  (None, <class 'NoneType'>)
+CONTEXT:  PL/Python function "test_type_conversion_float4"
+ test_type_conversion_float4 
+-----------------------------
+                            
+(1 row)
+
+CREATE FUNCTION test_type_conversion_float8(x float8) RETURNS float8 AS $$
+plpy.info(x, type(x))
+return x
+$$ LANGUAGE plpythonu;
+SELECT * FROM test_type_conversion_float8(100);
+INFO:  (100.0, <class 'float'>)
+CONTEXT:  PL/Python function "test_type_conversion_float8"
+ test_type_conversion_float8 
+-----------------------------
+                         100
+(1 row)
+
+SELECT * FROM test_type_conversion_float8(-100);
+INFO:  (-100.0, <class 'float'>)
+CONTEXT:  PL/Python function "test_type_conversion_float8"
+ test_type_conversion_float8 
+-----------------------------
+                        -100
+(1 row)
+
+SELECT * FROM test_type_conversion_float8(5000000000.5);
+INFO:  (5000000000.5, <class 'float'>)
+CONTEXT:  PL/Python function "test_type_conversion_float8"
+ test_type_conversion_float8 
+-----------------------------
+                5000000000.5
+(1 row)
+
+SELECT * FROM test_type_conversion_float8(null);
+INFO:  (None, <class 'NoneType'>)
+CONTEXT:  PL/Python function "test_type_conversion_float8"
+ test_type_conversion_float8 
+-----------------------------
+                            
+(1 row)
+
+CREATE FUNCTION test_type_conversion_text(x text) RETURNS text AS $$
+plpy.info(x, type(x))
+return x
+$$ LANGUAGE plpythonu;
+SELECT * FROM test_type_conversion_text('hello world');
+INFO:  ('hello world', <class 'str'>)
+CONTEXT:  PL/Python function "test_type_conversion_text"
+ test_type_conversion_text 
+---------------------------
+ hello world
+(1 row)
+
+SELECT * FROM test_type_conversion_text(null);
+INFO:  (None, <class 'NoneType'>)
+CONTEXT:  PL/Python function "test_type_conversion_text"
+ test_type_conversion_text 
+---------------------------
+ 
+(1 row)
+
+CREATE FUNCTION test_type_conversion_bytea(x bytea) RETURNS bytea AS $$
+plpy.info(x, type(x))
+return x
+$$ LANGUAGE plpythonu;
+SELECT * FROM test_type_conversion_bytea('hello world');
+INFO:  (b'hello world', <class 'bytes'>)
+CONTEXT:  PL/Python function "test_type_conversion_bytea"
+ test_type_conversion_bytea 
+----------------------------
+ \x68656c6c6f20776f726c64
+(1 row)
+
+SELECT * FROM test_type_conversion_bytea(E'null\\000byte');
+INFO:  (b'null\x00byte', <class 'bytes'>)
+CONTEXT:  PL/Python function "test_type_conversion_bytea"
+ test_type_conversion_bytea 
+----------------------------
+ \x6e756c6c0062797465
+(1 row)
+
+SELECT * FROM test_type_conversion_bytea(null);
+INFO:  (None, <class 'NoneType'>)
+CONTEXT:  PL/Python function "test_type_conversion_bytea"
+ test_type_conversion_bytea 
+----------------------------
+ 
+(1 row)
+
+CREATE FUNCTION test_type_marshal() RETURNS bytea AS $$
+import marshal
+return marshal.dumps('hello world')
+$$ LANGUAGE plpythonu;
+CREATE FUNCTION test_type_unmarshal(x bytea) RETURNS text AS $$
+import marshal
+try:
+    return marshal.loads(x)
+except ValueError as e:
+    return 'FAILED: ' + str(e)
+$$ LANGUAGE plpythonu;
+SELECT test_type_unmarshal(x) FROM test_type_marshal() x;
+ test_type_unmarshal 
+---------------------
+ hello world
+(1 row)
+
+--
+-- Domains
+--
+CREATE DOMAIN booltrue AS bool CHECK (VALUE IS TRUE OR VALUE IS NULL);
+CREATE FUNCTION test_type_conversion_booltrue(x booltrue, y bool) RETURNS booltrue AS $$
+return y
+$$ LANGUAGE plpythonu;
+SELECT * FROM test_type_conversion_booltrue(true, true);
+ test_type_conversion_booltrue 
+-------------------------------
+ t
+(1 row)
+
+SELECT * FROM test_type_conversion_booltrue(false, true);
+ERROR:  value for domain booltrue violates check constraint "booltrue_check"
+SELECT * FROM test_type_conversion_booltrue(true, false);
+ERROR:  value for domain booltrue violates check constraint "booltrue_check"
+CONTEXT:  while creating return value
+PL/Python function "test_type_conversion_booltrue"
+CREATE DOMAIN uint2 AS int2 CHECK (VALUE >= 0);
+CREATE FUNCTION test_type_conversion_uint2(x uint2, y int) RETURNS uint2 AS $$
+plpy.info(x, type(x))
+return y
+$$ LANGUAGE plpythonu;
+SELECT * FROM test_type_conversion_uint2(100::uint2, 50);
+INFO:  (100, <class 'int'>)
+CONTEXT:  PL/Python function "test_type_conversion_uint2"
+ test_type_conversion_uint2 
+----------------------------
+                         50
+(1 row)
+
+SELECT * FROM test_type_conversion_uint2(100::uint2, -50);
+INFO:  (100, <class 'int'>)
+CONTEXT:  PL/Python function "test_type_conversion_uint2"
+ERROR:  value for domain uint2 violates check constraint "uint2_check"
+CONTEXT:  while creating return value
+PL/Python function "test_type_conversion_uint2"
+SELECT * FROM test_type_conversion_uint2(null, 1);
+INFO:  (None, <class 'NoneType'>)
+CONTEXT:  PL/Python function "test_type_conversion_uint2"
+ test_type_conversion_uint2 
+----------------------------
+                          1
+(1 row)
+
+CREATE DOMAIN nnint AS int CHECK (VALUE IS NOT NULL);
+CREATE FUNCTION test_type_conversion_nnint(x nnint, y int) RETURNS nnint AS $$
+return y
+$$ LANGUAGE plpythonu;
+SELECT * FROM test_type_conversion_nnint(10, 20);
+ test_type_conversion_nnint 
+----------------------------
+                         20
+(1 row)
+
+SELECT * FROM test_type_conversion_nnint(null, 20);
+ERROR:  value for domain nnint violates check constraint "nnint_check"
+SELECT * FROM test_type_conversion_nnint(10, null);
+ERROR:  value for domain nnint violates check constraint "nnint_check"
+CONTEXT:  while creating return value
+PL/Python function "test_type_conversion_nnint"
+CREATE DOMAIN bytea10 AS bytea CHECK (octet_length(VALUE) = 10 AND VALUE IS NOT NULL);
+CREATE FUNCTION test_type_conversion_bytea10(x bytea10, y bytea) RETURNS bytea10 AS $$
+plpy.info(x, type(x))
+return y
+$$ LANGUAGE plpythonu;
+SELECT * FROM test_type_conversion_bytea10('hello wold', 'hello wold');
+INFO:  (b'hello wold', <class 'bytes'>)
+CONTEXT:  PL/Python function "test_type_conversion_bytea10"
+ test_type_conversion_bytea10 
+------------------------------
+ \x68656c6c6f20776f6c64
+(1 row)
+
+SELECT * FROM test_type_conversion_bytea10('hello world', 'hello wold');
+ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
+SELECT * FROM test_type_conversion_bytea10('hello word', 'hello world');
+INFO:  (b'hello word', <class 'bytes'>)
+CONTEXT:  PL/Python function "test_type_conversion_bytea10"
+ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
+CONTEXT:  while creating return value
+PL/Python function "test_type_conversion_bytea10"
+SELECT * FROM test_type_conversion_bytea10(null, 'hello word');
+ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
+SELECT * FROM test_type_conversion_bytea10('hello word', null);
+INFO:  (b'hello word', <class 'bytes'>)
+CONTEXT:  PL/Python function "test_type_conversion_bytea10"
+ERROR:  value for domain bytea10 violates check constraint "bytea10_check"
+CONTEXT:  while creating return value
+PL/Python function "test_type_conversion_bytea10"
diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c
index 6fd4aca..0d6c261 100644
--- a/src/pl/plpython/plpython.c
+++ b/src/pl/plpython/plpython.c
@@ -40,6 +40,48 @@ typedef int Py_ssize_t;
 #define PyBool_FromLong(x) PyInt_FromLong(x)
 #endif
 
+/*
+ * Python 2/3 strings/unicode/bytes handling.  Python 2 has strings
+ * and unicode, Python 3 has strings, which are unicode on the C
+ * level, and bytes.  The porting convention, which is similarly used
+ * in Python 2.6, is that "Unicode" is always unicode, and "Bytes" are
+ * bytes in Python 3 and strings in Python 2.  Since we keep
+ * supporting Python 2 and its usual strings, we provide a
+ * compatibility layer for Python 3 that when asked to convert a C
+ * string to a Python string it converts the C string from the
+ * PostgreSQL server encoding to a Python Unicode object.
+ */
+
+#if PY_VERSION_HEX < 0x02060000
+/* This is exactly the compatibility layer that Python 2.6 uses. */
+#define PyBytes_AsString PyString_AsString
+#define PyBytes_FromStringAndSize PyString_FromStringAndSize
+#define PyBytes_Size PyString_Size
+#define PyObject_Bytes PyObject_Str
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+#define PyString_Check(x) 0
+#define PyString_AsString(x) PLyUnicode_AsString(x)
+#define PyString_FromString(x) PLyUnicode_FromString(x)
+#endif
+
+/*
+ * Python 3 only has long.
+ */
+#if PY_MAJOR_VERSION >= 3
+#define PyInt_FromLong(x) PyLong_FromLong(x)
+#endif
+
+/*
+ * PyVarObject_HEAD_INIT was added in Python 2.6.  Its use is
+ * necessary to handle both Python 2 and 3.  This replacement
+ * definition is for Python <=2.5
+ */
+#ifndef PyVarObject_HEAD_INIT
+#define PyVarObject_HEAD_INIT(type, size) 		\
+		PyObject_HEAD_INIT(type) size,
+#endif
 
 #include "postgres.h"
 
@@ -240,7 +282,11 @@ static char *PLy_strdup(const char *);
 static void PLy_free(void *);
 
 static PyObject*PLyUnicode_Str(PyObject *unicode);
+static PyObject*PLyUnicode_Bytes(PyObject *unicode);
 static char *PLyUnicode_AsString(PyObject *unicode);
+#if PY_MAJOR_VERSION >= 3
+static PyObject *PLyUnicode_FromString(const char *s);
+#endif
 
 /* sub handlers for functions and triggers */
 static Datum PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure *);
@@ -282,7 +328,7 @@ static PyObject *PLyFloat_FromNumeric(PLyDatumToOb *arg, Datum d);
 static PyObject *PLyInt_FromInt16(PLyDatumToOb *arg, Datum d);
 static PyObject *PLyInt_FromInt32(PLyDatumToOb *arg, Datum d);
 static PyObject *PLyLong_FromInt64(PLyDatumToOb *arg, Datum d);
-static PyObject *PLyString_FromBytea(PLyDatumToOb *arg, Datum d);
+static PyObject *PLyBytes_FromBytea(PLyDatumToOb *arg, Datum d);
 static PyObject *PLyString_FromDatum(PLyDatumToOb *arg, Datum d);
 
 static PyObject *PLyDict_FromTuple(PLyTypeInfo *, HeapTuple, TupleDesc);
@@ -1723,7 +1769,7 @@ PLy_input_datum_func2(PLyDatumToOb *arg, Oid typeOid, HeapTuple typeTup)
 			arg->func = PLyLong_FromInt64;
 			break;
 		case BYTEAOID:
-			arg->func = PLyString_FromBytea;
+			arg->func = PLyBytes_FromBytea;
 			break;
 		default:
 			arg->func = PLyString_FromDatum;
@@ -1814,13 +1860,13 @@ PLyLong_FromInt64(PLyDatumToOb *arg, Datum d)
 }
 
 static PyObject *
-PLyString_FromBytea(PLyDatumToOb *arg, Datum d)
+PLyBytes_FromBytea(PLyDatumToOb *arg, Datum d)
 {
 	text     *txt = DatumGetByteaP(d);
 	char     *str = VARDATA(txt);
 	size_t    size = VARSIZE(txt) - VARHDRSZ;
 
-	return PyString_FromStringAndSize(str, size);
+	return PyBytes_FromStringAndSize(str, size);
 }
 
 static PyObject *
@@ -1917,14 +1963,14 @@ PLyObject_ToBytea(PLyTypeInfo *info,
 
 	Assert(plrv != Py_None);
 
-	plrv_so = PyObject_Str(plrv);
+	plrv_so = PyObject_Bytes(plrv);
 	if (!plrv_so)
-		PLy_elog(ERROR, "could not create string representation of Python object");
+		PLy_elog(ERROR, "could not create bytes representation of Python object");
 
 	PG_TRY();
 	{
-		char *plrv_sc = PyString_AsString(plrv_so);
-		size_t len = PyString_Size(plrv_so);
+		char *plrv_sc = PyBytes_AsString(plrv_so);
+		size_t len = PyBytes_Size(plrv_so);
 		size_t size = len + VARHDRSZ;
 		bytea *result = palloc(size);
 
@@ -1956,22 +2002,30 @@ PLyObject_ToDatum(PLyTypeInfo *info,
 				  PLyObToDatum *arg,
 				  PyObject *plrv)
 {
-	PyObject *volatile plrv_so = NULL;
+	PyObject *volatile plrv_bo = NULL;
 	Datum     rv;
 
 	Assert(plrv != Py_None);
 
 	if (PyUnicode_Check(plrv))
-		plrv_so = PLyUnicode_Str(plrv);
+		plrv_bo = PLyUnicode_Bytes(plrv);
 	else
-		plrv_so = PyObject_Str(plrv);
-	if (!plrv_so)
+	{
+#if PY_MAJOR_VERSION >= 3
+		PyObject *s = PyObject_Str(plrv);
+		plrv_bo = PLyUnicode_Bytes(s);
+		Py_XDECREF(s);
+#else
+		plrv_bo = PyObject_Str(plrv);
+#endif
+	}
+	if (!plrv_bo)
 		PLy_elog(ERROR, "could not create string representation of Python object");
 
 	PG_TRY();
 	{
-		char *plrv_sc = PyString_AsString(plrv_so);
-		size_t plen = PyString_Size(plrv_so);
+		char *plrv_sc = PyBytes_AsString(plrv_bo);
+		size_t plen = PyBytes_Size(plrv_bo);
 		size_t slen = strlen(plrv_sc);
 
 		if (slen < plen)
@@ -1984,12 +2038,12 @@ PLyObject_ToDatum(PLyTypeInfo *info,
 	}
 	PG_CATCH();
 	{
-		Py_XDECREF(plrv_so);
+		Py_XDECREF(plrv_bo);
 		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
-	Py_XDECREF(plrv_so);
+	Py_XDECREF(plrv_bo);
 
 	return rv;
 }
@@ -2241,8 +2295,7 @@ static PyMethodDef PLy_plan_methods[] = {
 };
 
 static PyTypeObject PLy_PlanType = {
-	PyObject_HEAD_INIT(NULL)
-	0,							/* ob_size */
+	PyVarObject_HEAD_INIT(NULL, 0)
 	"PLyPlan",					/* tp_name */
 	sizeof(PLyPlanObject),		/* tp_size */
 	0,							/* tp_itemsize */
@@ -2293,8 +2346,7 @@ static PyMethodDef PLy_result_methods[] = {
 };
 
 static PyTypeObject PLy_ResultType = {
-	PyObject_HEAD_INIT(NULL)
-	0,							/* ob_size */
+	PyVarObject_HEAD_INIT(NULL, 0)
 	"PLyResult",				/* tp_name */
 	sizeof(PLyResultObject),	/* tp_size */
 	0,							/* tp_itemsize */
@@ -2353,6 +2405,15 @@ static PyMethodDef PLy_methods[] = {
 	{NULL, NULL, 0, NULL}
 };
 
+#if PY_MAJOR_VERSION >= 3
+static PyModuleDef PLy_module = {
+	PyModuleDef_HEAD_INIT,		/* m_base */
+	"plpy",						/* m_name */
+	NULL,						/* m_doc */
+	-1,							/* m_size */
+	PLy_methods,				/* m_methods */
+};
+#endif
 
 /* plan object methods */
 static PyObject *
@@ -2940,6 +3001,15 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
  * language handler and interpreter initialization
  */
 
+#if PY_MAJOR_VERSION >= 3
+static PyMODINIT_FUNC
+PyInit_plpy(void)
+{
+	return PyModule_Create(&PLy_module);
+}
+#endif
+
+
 /*
  * _PG_init()			- library load-time initialization
  *
@@ -2956,7 +3026,13 @@ _PG_init(void)
 
 	pg_bindtextdomain(TEXTDOMAIN);
 
+#if PY_MAJOR_VERSION >= 3
+	PyImport_AppendInittab("plpy", PyInit_plpy);
+#endif
 	Py_Initialize();
+#if PY_MAJOR_VERSION >= 3
+	PyImport_ImportModule("plpy");
+#endif
 	PLy_init_interp();
 	PLy_init_plpy();
 	if (PyErr_Occurred())
@@ -3002,7 +3078,11 @@ PLy_init_plpy(void)
 	if (PyType_Ready(&PLy_ResultType) < 0)
 		elog(ERROR, "could not initialize PLy_ResultType");
 
+#if PY_MAJOR_VERSION >= 3
+	plpy = PyModule_Create(&PLy_module);
+#else
 	plpy = Py_InitModule("plpy", PLy_methods);
+#endif
 	plpy_dict = PyModule_GetDict(plpy);
 
 	/* PyDict_SetItemString(plpy, "PlanType", (PyObject *) &PLy_PlanType); */
@@ -3348,12 +3428,29 @@ PLy_free(void *ptr)
 }
 
 /*
- * Convert a Python unicode object to a Python string object in
+ * Convert a Unicode object to a Python string.
+ */
+static PyObject*
+PLyUnicode_Str(PyObject *unicode)
+{
+#if PY_MAJOR_VERSION >= 3
+	/* In Python 3, this is a noop. */
+	Py_INCREF(unicode);
+	return unicode;
+#else
+	/* In Python 2, this means converting the Unicode to bytes in the
+	 * server encoding. */
+	return PLyUnicode_Bytes(unicode);
+#endif
+}
+
+/*
+ * Convert a Python unicode object to a Python string/bytes object in
  * PostgreSQL server encoding.  Reference ownership is passed to the
  * caller.
  */
 static PyObject*
-PLyUnicode_Str(PyObject *unicode)
+PLyUnicode_Bytes(PyObject *unicode)
 {
 	PyObject *rv;
 	const char *serverenc;
@@ -3375,13 +3472,44 @@ PLyUnicode_Str(PyObject *unicode)
 /*
  * Convert a Python unicode object to a C string in PostgreSQL server
  * encoding.  No Python object reference is passed out of this
- * function.
+ * function.  The result is palloc'ed.
+ *
+ * Note that this function is disguised as PyString_AsString() when
+ * using Python 3.  That function retuns a pointer into the internal
+ * memory of the argument, which isn't exactly the interface of this
+ * function.  But in either case you get a rather short-lived
+ * reference that you ought to better leave alone.
  */
 static char *
 PLyUnicode_AsString(PyObject *unicode)
 {
-	PyObject *o = PLyUnicode_Str(unicode);
-	char *rv = PyString_AsString(o);
+	PyObject *o = PLyUnicode_Bytes(unicode);
+	char *rv = pstrdup(PyBytes_AsString(o));
 	Py_XDECREF(o);
 	return rv;
 }
+
+#if PY_MAJOR_VERSION >= 3
+/*
+ * Convert a C string in the PostgreSQL server encoding to a Python
+ * unicode object.  Reference ownership is passed to the caller.
+ */
+static PyObject *
+PLyUnicode_FromString(const char *s)
+{
+    char       *utf8string;
+	PyObject   *o;
+
+    utf8string = (char *) pg_do_encoding_conversion((unsigned char *) s,
+													strlen(s),
+													GetDatabaseEncoding(),
+													PG_UTF8);
+
+	o = PyUnicode_FromString(utf8string);
+
+    if (utf8string != s)
+        pfree(utf8string);
+
+	return o;
+}
+#endif /* PY_MAJOR_VERSION >= 3 */
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to