From 34ad28d225771541e3a294bf3782be0839575148 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Wed, 11 Mar 2015 17:01:53 +0100
Subject: [PATCH 2/2] [TMP] Port ldb to Python 3

---
 buildtools/wafsamba/samba_utils.py |   4 +-
 lib/ldb/pyldb.c                    | 398 ++++++++++++++++++++-----------------
 lib/ldb/pyldb_util.c               |   5 +-
 lib/ldb/tests/python/api.py        | 210 ++++++++++---------
 lib/ldb/tests/test-tdb.sh          |   5 -
 lib/ldb/wscript                    |  38 +++-
 6 files changed, 369 insertions(+), 291 deletions(-)

diff --git a/buildtools/wafsamba/samba_utils.py b/buildtools/wafsamba/samba_utils.py
index 4588be9..6cd7c8e 100644
--- a/buildtools/wafsamba/samba_utils.py
+++ b/buildtools/wafsamba/samba_utils.py
@@ -386,7 +386,7 @@ def RUN_COMMAND(cmd,
     return -1
 
 
-def RUN_PYTHON_TESTS(testfiles, pythonpath=None):
+def RUN_PYTHON_TESTS(testfiles, pythonpath=None, setup_cmd=None):
     env = LOAD_ENVIRONMENT()
     if pythonpath is None:
         pythonpath = os.path.join(Utils.g_module.blddir, 'python')
@@ -394,6 +394,8 @@ def RUN_PYTHON_TESTS(testfiles, pythonpath=None):
     for interp in env.python_interpreters:
         for testfile in testfiles:
             cmd = "PYTHONPATH=%s %s %s" % (pythonpath, interp, testfile)
+            if setup_cmd:
+                cmd = '%s ; %s' % (setup_cmd, cmd)
             print('Running Python testwith %s: %s' % (interp, testfile))
             ret = RUN_COMMAND(cmd)
             if ret:
diff --git a/lib/ldb/pyldb.c b/lib/ldb/pyldb.c
index f18e06e..8b6cda7 100644
--- a/lib/ldb/pyldb.c
+++ b/lib/ldb/pyldb.c
@@ -29,10 +29,10 @@
 */
 
 #include <Python.h>
+#include "py3compat.h"
 #include "ldb_private.h"
 #include "pyldb.h"
 
-void initldb(void);
 static PyObject *PyLdbMessage_FromMessage(struct ldb_message *msg);
 static PyObject *PyExc_LdbError;
 
@@ -76,9 +76,9 @@ static PyObject *py_ldb_control_str(PyLdbControlObject *self)
 			PyErr_NoMemory();
 			return NULL;
 		}
-		return PyString_FromString(control);
+		return PyStr_FromString(control);
 	} else {
-		return PyString_FromFormat("ldb control");
+		return PyStr_FromFormat("ldb control");
 	}
 }
 
@@ -93,7 +93,7 @@ static void py_ldb_control_dealloc(PyLdbControlObject *self)
 
 static PyObject *py_ldb_control_get_oid(PyLdbControlObject *self)
 {
-	return PyString_FromString(self->data->oid);
+	return PyStr_FromString(self->data->oid);
 }
 
 static PyObject *py_ldb_control_get_critical(PyLdbControlObject *self)
@@ -191,7 +191,7 @@ static void PyErr_SetLdbError(PyObject *error, int ret, struct ldb_context *ldb_
 
 static PyObject *PyObject_FromLdbValue(const struct ldb_val *val)
 {
-	return PyString_FromStringAndSize((const char *)val->data, val->length);
+	return PyBytes_FromStringAndSize((const char *)val->data, val->length);
 }
 
 /**
@@ -317,7 +317,7 @@ static PyObject *PyLdbResult_FromResult(struct ldb_result *result)
 	}
 
 	for (i = 0;result->refs && result->refs[i]; i++) {
-		PyList_SetItem(referals, i, PyString_FromString(result->refs[i]));
+		PyList_SetItem(referals, i, PyStr_FromString(result->refs[i]));
 	}
 	ret->referals = referals;
 	return (PyObject *)ret;
@@ -375,22 +375,22 @@ static PyObject *py_ldb_dn_is_null(PyLdbDnObject *self)
  
 static PyObject *py_ldb_dn_get_casefold(PyLdbDnObject *self)
 {
-	return PyString_FromString(ldb_dn_get_casefold(self->dn));
+	return PyStr_FromString(ldb_dn_get_casefold(self->dn));
 }
 
 static PyObject *py_ldb_dn_get_linearized(PyLdbDnObject *self)
 {
-	return PyString_FromString(ldb_dn_get_linearized(self->dn));
+	return PyStr_FromString(ldb_dn_get_linearized(self->dn));
 }
 
 static PyObject *py_ldb_dn_canonical_str(PyLdbDnObject *self)
 {
-	return PyString_FromString(ldb_dn_canonical_string(self->dn, self->dn));
+	return PyStr_FromString(ldb_dn_canonical_string(self->dn, self->dn));
 }
 
 static PyObject *py_ldb_dn_canonical_ex_str(PyLdbDnObject *self)
 {
-	return PyString_FromString(ldb_dn_canonical_ex_string(self->dn, self->dn));
+	return PyStr_FromString(ldb_dn_canonical_ex_string(self->dn, self->dn));
 }
 
 static PyObject *py_ldb_dn_extended_str(PyLdbDnObject *self, PyObject *args, PyObject *kwargs)
@@ -401,7 +401,7 @@ static PyObject *py_ldb_dn_extended_str(PyLdbDnObject *self, PyObject *args, PyO
 					 discard_const_p(char *, kwnames),
 					 &mode))
 		return NULL;
-	return PyString_FromString(ldb_dn_get_extended_linearized(self->dn, self->dn, mode));
+	return PyStr_FromString(ldb_dn_get_extended_linearized(self->dn, self->dn, mode));
 }
 
 static PyObject *py_ldb_dn_get_extended_component(PyLdbDnObject *self, PyObject *args)
@@ -416,7 +416,7 @@ static PyObject *py_ldb_dn_get_extended_component(PyLdbDnObject *self, PyObject
 		Py_RETURN_NONE;
 	}
 
-	return PyString_FromStringAndSize((const char *)val->data, val->length);
+	return PyBytes_FromStringAndSize((const char *)val->data, val->length);
 }
 
 static PyObject *py_ldb_dn_set_extended_component(PyLdbDnObject *self, PyObject *args)
@@ -424,6 +424,7 @@ static PyObject *py_ldb_dn_set_extended_component(PyLdbDnObject *self, PyObject
 	char *name;
 	PyObject *value;
 	int err;
+	Py_ssize_t length;
 
 	if (!PyArg_ParseTuple(args, "sO", &name, &value))
 		return NULL;
@@ -432,12 +433,12 @@ static PyObject *py_ldb_dn_set_extended_component(PyLdbDnObject *self, PyObject
 		err = ldb_dn_set_extended_component(self->dn, name, NULL);
 	} else {
 		struct ldb_val val;
-		if (!PyString_Check(value)) {
+		if (!PyStr_Check(value)) {
 			PyErr_SetString(PyExc_TypeError, "Expected a string argument");
 			return NULL;
 		}
-		val.data = (uint8_t *)PyString_AsString(value);
-		val.length = PyString_Size(value);
+		val.data = (uint8_t *)PyStr_AsUTF8AndSize(value, &length);
+		val.length = length;
 		err = ldb_dn_set_extended_component(self->dn, name, &val);
 	}
 
@@ -451,7 +452,14 @@ static PyObject *py_ldb_dn_set_extended_component(PyLdbDnObject *self, PyObject
 
 static PyObject *py_ldb_dn_repr(PyLdbDnObject *self)
 {
-	return PyString_FromFormat("Dn(%s)", PyObject_REPR(PyString_FromString(ldb_dn_get_linearized(self->dn))));
+	PyObject *str = PyStr_FromString(ldb_dn_get_linearized(self->dn));
+	PyObject *repr = PyObject_Repr(str);
+	PyObject *result = PyStr_FromFormat("Dn(%s)", PyStr_AsString(repr));
+	if (result == NULL) {
+		Py_XDECREF(str);
+		Py_XDECREF(repr);
+	}
+	return result;
 }
 
 static PyObject *py_ldb_dn_check_special(PyLdbDnObject *self, PyObject *args)
@@ -464,13 +472,14 @@ static PyObject *py_ldb_dn_check_special(PyLdbDnObject *self, PyObject *args)
 	return PyBool_FromLong(ldb_dn_check_special(self->dn, name));
 }
 
-static int py_ldb_dn_compare(PyLdbDnObject *dn1, PyLdbDnObject *dn2)
+static PyObject *py_ldb_dn_richcompare(PyLdbDnObject *dn1, PyLdbDnObject *dn2, int op)
 {
 	int ret;
-	ret = ldb_dn_compare(dn1->dn, dn2->dn);
-	if (ret < 0) ret = -1;
-	if (ret > 0) ret = 1;
-	return ret;
+	if (pyldb_Dn_Check(dn1) && pyldb_Dn_Check(dn2)) {
+		ret = ldb_dn_compare(dn1->dn, dn2->dn);
+		return PY_RICHCMP(ret, 0, op);
+	}
+	Py_RETURN_NOTIMPLEMENTED;
 }
 
 static PyObject *py_ldb_dn_get_parent(PyLdbDnObject *self)
@@ -572,7 +581,7 @@ static PyObject *py_ldb_dn_get_component_name(PyLdbDnObject *self, PyObject *arg
 		Py_RETURN_NONE;
 	}
 
-	return PyString_FromString(name);
+	return PyStr_FromString(name);
 }
 
 static PyObject *py_ldb_dn_get_component_value(PyLdbDnObject *self, PyObject *args)
@@ -601,17 +610,18 @@ static PyObject *py_ldb_dn_set_component(PyLdbDnObject *self, PyObject *args)
 	PyObject *value = Py_None;
 	struct ldb_val val = { NULL, };
 	int err;
+	Py_ssize_t length;
 
 	if (!PyArg_ParseTuple(args, "IsO", &num, &name, &value))
 		return NULL;
 
 	if (value != Py_None) {
-		if (!PyString_Check(value)) {
+		if (!PyStr_Check(value)) {
 			PyErr_SetString(PyExc_TypeError, "Expected a string argument");
 			return NULL;
 		}
-		val.data = (uint8_t *)PyString_AsString(value);
-		val.length = PyString_Size(value);
+		val.data = (uint8_t *)PyStr_AsUTF8AndSize(value, &length);
+		val.length = length;
 	}
 
 	err = ldb_dn_set_component(self->dn, num, name, val);
@@ -635,7 +645,7 @@ static PyObject *py_ldb_dn_get_rdn_name(PyLdbDnObject *self)
 		Py_RETURN_NONE;
 	}
 
-	return PyString_FromString(name);
+	return PyStr_FromString(name);
 }
 
 static PyObject *py_ldb_dn_get_rdn_value(PyLdbDnObject *self)
@@ -822,7 +832,7 @@ static PyTypeObject PyLdbDn = {
 	.tp_methods = py_ldb_dn_methods,
 	.tp_str = (reprfunc)py_ldb_dn_get_linearized,
 	.tp_repr = (reprfunc)py_ldb_dn_repr,
-	.tp_compare = (cmpfunc)py_ldb_dn_compare,
+	.tp_richcompare = (richcmpfunc)py_ldb_dn_richcompare,
 	.tp_as_sequence = &py_ldb_dn_seq,
 	.tp_doc = "A LDB distinguished name.",
 	.tp_new = py_ldb_dn_new,
@@ -836,7 +846,7 @@ static void py_ldb_debug(void *context, enum ldb_debug_level level, const char *
 static void py_ldb_debug(void *context, enum ldb_debug_level level, const char *fmt, va_list ap)
 {
 	PyObject *fn = (PyObject *)context;
-	PyObject_CallFunction(fn, discard_const_p(char, "(i,O)"), level, PyString_FromFormatV(fmt, ap));
+	PyObject_CallFunction(fn, discard_const_p(char, "(i,O)"), level, PyStr_FromFormatV(fmt, ap));
 }
 
 static PyObject *py_ldb_set_debug(PyObject *self, PyObject *args)
@@ -926,7 +936,7 @@ static PyObject *py_ldb_setup_wellknown_attributes(PyLdbObject *self)
 
 static PyObject *py_ldb_repr(PyLdbObject *self)
 {
-	return PyString_FromFormat("<ldb connection>");
+	return PyStr_FromFormat("<ldb connection>");
 }
 
 static PyObject *py_ldb_get_root_basedn(PyLdbObject *self)
@@ -962,11 +972,12 @@ static PyObject *py_ldb_get_default_basedn(PyLdbObject *self)
 	return py_ldb_dn_copy(dn);
 }
 
-static const char **PyList_AsStringList(TALLOC_CTX *mem_ctx, PyObject *list, 
+static const char **PyList_AsStrList(TALLOC_CTX *mem_ctx, PyObject *list,
 					const char *paramname)
 {
 	const char **ret;
-	Py_ssize_t i;
+	Py_ssize_t i, length;
+	char *content;
 	if (!PyList_Check(list)) {
 		PyErr_Format(PyExc_TypeError, "%s is not a list", paramname);
 		return NULL;
@@ -979,12 +990,12 @@ static const char **PyList_AsStringList(TALLOC_CTX *mem_ctx, PyObject *list,
 
 	for (i = 0; i < PyList_Size(list); i++) {
 		PyObject *item = PyList_GetItem(list, i);
-		if (!PyString_Check(item)) {
+		if (!PyStr_Check(item)) {
 			PyErr_Format(PyExc_TypeError, "%s should be strings", paramname);
 			return NULL;
 		}
-		ret[i] = talloc_strndup(ret, PyString_AsString(item),
-					PyString_Size(item));
+		content = PyStr_AsUTF8AndSize(item, &length);
+		ret[i] = talloc_strndup(ret, content, length);
 	}
 	ret[i] = NULL;
 	return ret;
@@ -1010,7 +1021,7 @@ static int py_ldb_init(PyLdbObject *self, PyObject *args, PyObject *kwargs)
 	if (py_options == Py_None) {
 		options = NULL;
 	} else {
-		options = PyList_AsStringList(ldb, py_options, "options");
+		options = PyList_AsStrList(ldb, py_options, "options");
 		if (options == NULL)
 			return -1;
 	}
@@ -1066,7 +1077,7 @@ static PyObject *py_ldb_connect(PyLdbObject *self, PyObject *args, PyObject *kwa
 	if (py_options == Py_None) {
 		options = NULL;
 	} else {
-		options = PyList_AsStringList(NULL, py_options, "options");
+		options = PyList_AsStrList(NULL, py_options, "options");
 		if (options == NULL)
 			return NULL;
 	}
@@ -1108,7 +1119,7 @@ static PyObject *py_ldb_modify(PyLdbObject *self, PyObject *args, PyObject *kwar
 	if (py_controls == Py_None) {
 		parsed_controls = NULL;
 	} else {
-		const char **controls = PyList_AsStringList(mem_ctx, py_controls, "controls");
+		const char **controls = PyList_AsStrList(mem_ctx, py_controls, "controls");
 		if (controls == NULL) {
 			talloc_free(mem_ctx);
 			return NULL;
@@ -1212,7 +1223,7 @@ static struct ldb_message *PyDict_AsMessage(TALLOC_CTX *mem_ctx,
 	}
 
 	while (PyDict_Next(py_obj, &dict_pos, &key, &value)) {
-		char *key_str = PyString_AsString(key);
+		char *key_str = PyStr_AsString(key);
 		if (ldb_attr_cmp(key_str, "dn") != 0) {
 			msg_el = PyObject_AsMessageElement(msg->elements, value,
 							   mod_flags, key_str);
@@ -1257,7 +1268,7 @@ static PyObject *py_ldb_add(PyLdbObject *self, PyObject *args, PyObject *kwargs)
 	if (py_controls == Py_None) {
 		parsed_controls = NULL;
 	} else {
-		const char **controls = PyList_AsStringList(mem_ctx, py_controls, "controls");
+		const char **controls = PyList_AsStrList(mem_ctx, py_controls, "controls");
 		if (controls == NULL) {
 			talloc_free(mem_ctx);
 			return NULL;
@@ -1350,7 +1361,7 @@ static PyObject *py_ldb_delete(PyLdbObject *self, PyObject *args, PyObject *kwar
 	if (py_controls == Py_None) {
 		parsed_controls = NULL;
 	} else {
-		const char **controls = PyList_AsStringList(mem_ctx, py_controls, "controls");
+		const char **controls = PyList_AsStrList(mem_ctx, py_controls, "controls");
 		if (controls == NULL) {
 			talloc_free(mem_ctx);
 			return NULL;
@@ -1428,7 +1439,7 @@ static PyObject *py_ldb_rename(PyLdbObject *self, PyObject *args, PyObject *kwar
 	if (py_controls == Py_None) {
 		parsed_controls = NULL;
 	} else {
-		const char **controls = PyList_AsStringList(mem_ctx, py_controls, "controls");
+		const char **controls = PyList_AsStrList(mem_ctx, py_controls, "controls");
 		if (controls == NULL) {
 			talloc_free(mem_ctx);
 			return NULL;
@@ -1553,7 +1564,7 @@ static PyObject *py_ldb_write_ldif(PyLdbObject *self, PyObject *args)
 		return NULL;
 	}
 
-	ret = PyString_FromString(string);
+	ret = PyStr_FromString(string);
 
 	talloc_free(mem_ctx);
 
@@ -1646,8 +1657,8 @@ static PyObject *py_ldb_schema_format_value(PyLdbObject *self, PyObject *args)
 	if (!PyArg_ParseTuple(args, "sO", &element_name, &val))
 		return NULL;
 
-	old_val.data = (uint8_t *)PyString_AsString(val);
-	old_val.length = PyString_Size(val);
+	old_val.data = (uint8_t *)PyBytes_AsString(val);
+	old_val.length = PyBytes_Size(val);
 
 	if (old_val.data == NULL) {
 		PyErr_SetString(PyExc_RuntimeError, "Failed to convert passed value to String");
@@ -1671,7 +1682,7 @@ static PyObject *py_ldb_schema_format_value(PyLdbObject *self, PyObject *args)
 		Py_RETURN_NONE;
 	}
 
-	ret = PyString_FromStringAndSize((const char *)new_val.data, new_val.length);
+	ret = PyBytes_FromStringAndSize((const char *)new_val.data, new_val.length);
 
 	talloc_free(mem_ctx);
 
@@ -1713,7 +1724,7 @@ static PyObject *py_ldb_search(PyLdbObject *self, PyObject *args, PyObject *kwar
 	if (py_attrs == Py_None) {
 		attrs = NULL;
 	} else {
-		attrs = PyList_AsStringList(mem_ctx, py_attrs, "attrs");
+		attrs = PyList_AsStrList(mem_ctx, py_attrs, "attrs");
 		if (attrs == NULL) {
 			talloc_free(mem_ctx);
 			return NULL;
@@ -1732,7 +1743,7 @@ static PyObject *py_ldb_search(PyLdbObject *self, PyObject *args, PyObject *kwar
 	if (py_controls == Py_None) {
 		parsed_controls = NULL;
 	} else {
-		const char **controls = PyList_AsStringList(mem_ctx, py_controls, "controls");
+		const char **controls = PyList_AsStrList(mem_ctx, py_controls, "controls");
 		if (controls == NULL) {
 			talloc_free(mem_ctx);
 			return NULL;
@@ -2105,7 +2116,7 @@ static PySequenceMethods py_ldb_result_seq = {
 
 static PyObject *py_ldb_result_repr(PyLdbObject *self)
 {
-	return PyString_FromFormat("<ldb result>");
+	return PyStr_FromFormat("<ldb result>");
 }
 
 
@@ -2124,13 +2135,13 @@ static PyTypeObject PyLdbResult = {
 
 static PyObject *py_ldb_module_repr(PyLdbModuleObject *self)
 {
-	return PyString_FromFormat("<ldb module '%s'>",
+	return PyStr_FromFormat("<ldb module '%s'>",
 		pyldb_Module_AsModule(self)->ops->name);
 }
 
 static PyObject *py_ldb_module_str(PyLdbModuleObject *self)
 {
-	return PyString_FromString(pyldb_Module_AsModule(self)->ops->name);
+	return PyStr_FromString(pyldb_Module_AsModule(self)->ops->name);
 }
 
 static PyObject *py_ldb_module_start_transaction(PyLdbModuleObject *self)
@@ -2171,7 +2182,7 @@ static PyObject *py_ldb_module_search(PyLdbModuleObject *self, PyObject *args, P
 	if (py_attrs == Py_None) {
 		attrs = NULL;
 	} else {
-		attrs = PyList_AsStringList(NULL, py_attrs, "attrs");
+		attrs = PyList_AsStrList(NULL, py_attrs, "attrs");
 		if (attrs == NULL)
 			return NULL;
 	}
@@ -2354,28 +2365,28 @@ static struct ldb_message_element *PyObject_AsMessageElement(
 
 	me->name = talloc_strdup(me, attr_name);
 	me->flags = flags;
-	if (PyString_Check(set_obj)) {
+	if (PyBytes_Check(set_obj)) {
 		me->num_values = 1;
 		me->values = talloc_array(me, struct ldb_val, me->num_values);
-		me->values[0].length = PyString_Size(set_obj);
+		me->values[0].length = PyBytes_Size(set_obj);
 		me->values[0].data = talloc_memdup(me, 
-			(uint8_t *)PyString_AsString(set_obj), me->values[0].length+1);
+			(uint8_t *)PyBytes_AsString(set_obj), me->values[0].length+1);
 	} else if (PySequence_Check(set_obj)) {
 		Py_ssize_t i;
 		me->num_values = PySequence_Size(set_obj);
 		me->values = talloc_array(me, struct ldb_val, me->num_values);
 		for (i = 0; i < me->num_values; i++) {
 			PyObject *obj = PySequence_GetItem(set_obj, i);
-			if (!PyString_Check(obj)) {
+			if (!PyBytes_Check(obj)) {
 				PyErr_Format(PyExc_TypeError,
-					     "Expected string as element %zd in list", i);
+					     "Expected bytestring as element %zd in list", i);
 				talloc_free(me);
 				return NULL;
 			}
 
-			me->values[i].length = PyString_Size(obj);
+			me->values[i].length = PyBytes_Size(obj);
 			me->values[i].data = talloc_memdup(me, 
-				(uint8_t *)PyString_AsString(obj), me->values[i].length+1);
+				(uint8_t *)PyBytes_AsString(obj), me->values[i].length+1);
 		}
 	} else {
 		PyErr_Format(PyExc_TypeError,
@@ -2453,7 +2464,7 @@ static PyObject *py_ldb_msg_element_find(PyLdbMessageElementObject *self, Py_ssi
 		PyErr_SetString(PyExc_IndexError, "Out of range");
 		return NULL;
 	}
-	return PyString_FromStringAndSize((char *)el->values[idx].data, el->values[idx].length);
+	return PyBytes_FromStringAndSize((char *)el->values[idx].data, el->values[idx].length);
 }
 
 static PySequenceMethods py_ldb_msg_element_seq = {
@@ -2461,11 +2472,15 @@ static PySequenceMethods py_ldb_msg_element_seq = {
 	.sq_item = (ssizeargfunc)py_ldb_msg_element_find,
 };
 
-static int py_ldb_msg_element_cmp(PyLdbMessageElementObject *self, PyLdbMessageElementObject *other)
+static PyObject *py_ldb_msg_element_richcmp(PyLdbMessageElementObject *self, PyLdbMessageElementObject *other, int op)
 {
-	int ret = ldb_msg_element_compare(pyldb_MessageElement_AsMessageElement(self),
-									  pyldb_MessageElement_AsMessageElement(other));
-	return SIGN(ret);
+	int ret;
+	if (pyldb_MessageElement_Check(self) && pyldb_MessageElement_Check(other)) {
+		ret = ldb_msg_element_compare(pyldb_MessageElement_AsMessageElement(self),
+					      pyldb_MessageElement_AsMessageElement(other));
+		return PY_RICHCMP(ret, 0, op);
+	}
+	Py_RETURN_NOTIMPLEMENTED;
 }
 
 static PyObject *py_ldb_msg_element_iter(PyLdbMessageElementObject *self)
@@ -2524,7 +2539,7 @@ static PyObject *py_ldb_msg_element_new(PyTypeObject *type, PyObject *args, PyOb
 
 	if (py_elements != NULL) {
 		Py_ssize_t i;
-		if (PyString_Check(py_elements)) {
+		if (PyBytes_Check(py_elements)) {
 			el->num_values = 1;
 			el->values = talloc_array(el, struct ldb_val, 1);
 			if (el->values == NULL) {
@@ -2532,9 +2547,9 @@ static PyObject *py_ldb_msg_element_new(PyTypeObject *type, PyObject *args, PyOb
 				PyErr_NoMemory();
 				return NULL;
 			}
-			el->values[0].length = PyString_Size(py_elements);
+			el->values[0].length = PyBytes_Size(py_elements);
 			el->values[0].data = talloc_memdup(el->values, 
-				(uint8_t *)PyString_AsString(py_elements), el->values[0].length+1);
+				(uint8_t *)PyBytes_AsString(py_elements), el->values[0].length+1);
 		} else if (PySequence_Check(py_elements)) {
 			el->num_values = PySequence_Size(py_elements);
 			el->values = talloc_array(el, struct ldb_val, el->num_values);
@@ -2549,19 +2564,19 @@ static PyObject *py_ldb_msg_element_new(PyTypeObject *type, PyObject *args, PyOb
 					talloc_free(mem_ctx);
 					return NULL;
 				}
-				if (!PyString_Check(item)) {
+				if (!PyBytes_Check(item)) {
 					PyErr_Format(PyExc_TypeError, 
-						     "Expected string as element %zd in list", i);
+						     "Expected bytestring as element %zd in list", i);
 					talloc_free(mem_ctx);
 					return NULL;
 				}
-				el->values[i].length = PyString_Size(item);
+				el->values[i].length = PyBytes_Size(item);
 				el->values[i].data = talloc_memdup(el,
-					(uint8_t *)PyString_AsString(item), el->values[i].length+1);
+					(uint8_t *)PyBytes_AsString(item), el->values[i].length+1);
 			}
 		} else {
 			PyErr_SetString(PyExc_TypeError, 
-					"Expected string or list");
+					"Expected bytestring or list");
 			talloc_free(mem_ctx);
 			return NULL;
 		}
@@ -2592,17 +2607,17 @@ static PyObject *py_ldb_msg_element_repr(PyLdbMessageElementObject *self)
 		PyObject *o = py_ldb_msg_element_find(self, i);
 		repr = PyObject_Repr(o);
 		if (element_str == NULL)
-			element_str = talloc_strdup(NULL, PyString_AsString(repr));
+			element_str = talloc_strdup(NULL, PyStr_AsString(repr));
 		else
-			element_str = talloc_asprintf_append(element_str, ",%s", PyString_AsString(repr));
+			element_str = talloc_asprintf_append(element_str, ",%s", PyStr_AsString(repr));
 		Py_DECREF(repr);
 	}
 
 	if (element_str != NULL) {
-		ret = PyString_FromFormat("MessageElement([%s])", element_str);
+		ret = PyStr_FromFormat("MessageElement([%s])", element_str);
 		talloc_free(element_str);
 	} else {
-		ret = PyString_FromString("MessageElement([])");
+		ret = PyStr_FromString("MessageElement([])");
 	}
 
 	return ret;
@@ -2613,7 +2628,7 @@ static PyObject *py_ldb_msg_element_str(PyLdbMessageElementObject *self)
 	struct ldb_message_element *el = pyldb_MessageElement_AsMessageElement(self);
 
 	if (el->num_values == 1)
-		return PyString_FromStringAndSize((char *)el->values[0].data, el->values[0].length);
+		return PyStr_FromStringAndSize((char *)el->values[0].data, el->values[0].length);
 	else
 		Py_RETURN_NONE;
 }
@@ -2631,7 +2646,7 @@ static PyTypeObject PyLdbMessageElement = {
 	.tp_repr = (reprfunc)py_ldb_msg_element_repr,
 	.tp_str = (reprfunc)py_ldb_msg_element_str,
 	.tp_methods = py_ldb_msg_element_methods,
-	.tp_compare = (cmpfunc)py_ldb_msg_element_cmp,
+	.tp_richcompare = (richcmpfunc)py_ldb_msg_element_richcmp,
 	.tp_iter = (getiterfunc)py_ldb_msg_element_iter,
 	.tp_as_sequence = &py_ldb_msg_element_seq,
 	.tp_new = py_ldb_msg_element_new,
@@ -2700,11 +2715,11 @@ static PyObject *py_ldb_msg_keys(PyLdbMessageObject *self)
 	Py_ssize_t i, j = 0;
 	PyObject *obj = PyList_New(msg->num_elements+(msg->dn != NULL?1:0));
 	if (msg->dn != NULL) {
-		PyList_SetItem(obj, j, PyString_FromString("dn"));
+		PyList_SetItem(obj, j, PyStr_FromString("dn"));
 		j++;
 	}
 	for (i = 0; i < msg->num_elements; i++) {
-		PyList_SetItem(obj, j, PyString_FromString(msg->elements[i].name));
+		PyList_SetItem(obj, j, PyStr_FromString(msg->elements[i].name));
 		j++;
 	}
 	return obj;
@@ -2715,11 +2730,11 @@ static PyObject *py_ldb_msg_getitem_helper(PyLdbMessageObject *self, PyObject *p
 	struct ldb_message_element *el;
 	char *name;
 	struct ldb_message *msg = pyldb_Message_AsMessage(self);
-	if (!PyString_Check(py_name)) {
-		PyErr_SetNone(PyExc_TypeError);
+	if (!PyStr_Check(py_name)) {
+		PyErr_SetString(PyExc_TypeError, "Expected string");
 		return NULL;
 	}
-	name = PyString_AsString(py_name);
+	name = PyStr_AsString(py_name);
 	if (!ldb_attr_cmp(name, "dn"))
 		return pyldb_Dn_FromDn(msg->dn);
 	el = ldb_msg_find_element(msg, name);
@@ -2880,12 +2895,12 @@ static int py_ldb_msg_setitem(PyLdbMessageObject *self, PyObject *name, PyObject
 {
 	char *attr_name;
 
-	if (!PyString_Check(name)) {
-		PyErr_SetNone(PyExc_TypeError);
+	if (!PyStr_Check(name)) {
+		PyErr_SetString(PyExc_TypeError, "Expected string");
 		return -1;
 	}
 
-	attr_name = PyString_AsString(name);
+	attr_name = PyStr_AsString(name);
 	if (value == NULL) {
 		/* delitem */
 		ldb_msg_remove_attr(self->msg, attr_name);
@@ -3003,11 +3018,19 @@ static PyGetSetDef py_ldb_msg_getset[] = {
 
 static PyObject *py_ldb_msg_repr(PyLdbMessageObject *self)
 {
-	PyObject *dict = PyDict_New(), *ret;
+	PyObject *dict = PyDict_New(), *ret, *repr=NULL;
+	if (dict == NULL)
+		return NULL;
 	if (PyDict_Update(dict, (PyObject *)self) != 0)
 		return NULL;
-	ret = PyString_FromFormat("Message(%s)", PyObject_REPR(dict));
+	repr = PyObject_Repr(dict);
+	if (repr == NULL) {
+		Py_DECREF(dict);
+		return NULL;
+	}
+	ret = PyStr_FromFormat("Message(%s)", PyStr_AsString(repr));
 	Py_DECREF(dict);
+	Py_DECREF(repr);
 	return ret;
 }
 
@@ -3017,41 +3040,47 @@ static void py_ldb_msg_dealloc(PyLdbMessageObject *self)
 	PyObject_Del(self);
 }
 
-static int py_ldb_msg_compare(PyLdbMessageObject *py_msg1,
-			      PyLdbMessageObject *py_msg2)
+static PyObject *py_ldb_msg_richcompare(PyObject *py_msg1,
+					 PyObject *py_msg2, int op)
 {
-	struct ldb_message *msg1 = pyldb_Message_AsMessage(py_msg1),
-			   *msg2 = pyldb_Message_AsMessage(py_msg2);
+	struct ldb_message *msg1, *msg2;
 	unsigned int i;
 	int ret;
 
+	if (!PyLdbMessage_Check(py_msg1) || !PyLdbMessage_Check(py_msg2)) {
+		Py_RETURN_NOTIMPLEMENTED;
+	}
+
+	msg1 = pyldb_Message_AsMessage(py_msg1),
+	msg2 = pyldb_Message_AsMessage(py_msg2);
+
 	if ((msg1->dn != NULL) || (msg2->dn != NULL)) {
 		ret = ldb_dn_compare(msg1->dn, msg2->dn);
 		if (ret != 0) {
-			return SIGN(ret);
+			return PY_RICHCMP(ret, 0, op);
 		}
 	}
 
 	ret = msg1->num_elements - msg2->num_elements;
 	if (ret != 0) {
-		return SIGN(ret);
+		return PY_RICHCMP(ret, 0, op);
 	}
 
 	for (i = 0; i < msg1->num_elements; i++) {
 		ret = ldb_msg_element_compare_name(&msg1->elements[i],
 						   &msg2->elements[i]);
 		if (ret != 0) {
-			return SIGN(ret);
+			return PY_RICHCMP(ret, 0, op);
 		}
 
 		ret = ldb_msg_element_compare(&msg1->elements[i],
 					      &msg2->elements[i]);
 		if (ret != 0) {
-			return SIGN(ret);
+			return PY_RICHCMP(ret, 0, op);
 		}
 	}
 
-	return 0;
+	return PY_RICHCMP(0, 0, op);
 }
 
 static PyTypeObject PyLdbMessage = {
@@ -3065,7 +3094,7 @@ static PyTypeObject PyLdbMessage = {
 	.tp_repr = (reprfunc)py_ldb_msg_repr,
 	.tp_flags = Py_TPFLAGS_DEFAULT,
 	.tp_iter = (getiterfunc)py_ldb_msg_iter,
-	.tp_compare = (cmpfunc)py_ldb_msg_compare,
+	.tp_richcompare = (richcmpfunc)py_ldb_msg_richcompare,
 	.tp_doc = "A LDB Message",
 };
 
@@ -3121,7 +3150,7 @@ static int py_module_search(struct ldb_module *mod, struct ldb_request *req)
 		for (len = 0; req->op.search.attrs[len]; len++);
 		py_attrs = PyList_New(len);
 		for (i = 0; i < len; i++)
-			PyList_SetItem(py_attrs, i, PyString_FromString(req->op.search.attrs[i]));
+			PyList_SetItem(py_attrs, i, PyStr_FromString(req->op.search.attrs[i]));
 	}
 
 	py_result = PyObject_CallMethod(py_ldb, discard_const_p(char, "search"),
@@ -3379,7 +3408,7 @@ static PyObject *py_register_module(PyObject *module, PyObject *args)
 		return NULL;
 	}
 
-	ops->name = talloc_strdup(ops, PyString_AsString(PyObject_GetAttrString(input, discard_const_p(char, "name"))));
+	ops->name = talloc_strdup(ops, PyStr_AsString(PyObject_GetAttrString(input, discard_const_p(char, "name"))));
 
 	Py_INCREF(input);
 	ops->private_data = input;
@@ -3412,7 +3441,7 @@ static PyObject *py_timestring(PyObject *module, PyObject *args)
 	if (!PyArg_ParseTuple(args, "l", &t_val))
 		return NULL;
 	tresult = ldb_timestring(NULL, (time_t) t_val);
-	ret = PyString_FromString(tresult);
+	ret = PyStr_FromString(tresult);
 	talloc_free(tresult);
 	return ret;
 }
@@ -3454,7 +3483,7 @@ static PyObject *py_binary_encode(PyObject *self, PyObject *args)
 		PyErr_SetString(PyExc_TypeError, "unable to encode binary string");
 		return NULL;
 	}
-	ret = PyString_FromString(encoded);
+	ret = PyStr_FromString(encoded);
 	talloc_free(encoded);
 	return ret;
 }
@@ -3506,102 +3535,111 @@ static PyMethodDef py_ldb_global_methods[] = {
 	{ NULL }
 };
 
-void initldb(void)
+static struct PyModuleDef moduledef = {
+    PyModuleDef_HEAD_INIT,
+    .m_name = "ldb",
+    .m_doc = PyDoc_STR("An interface to LDB, a LDAP-like API that can either to talk an embedded database (TDB-based) or a standards-compliant LDAP server."),
+    .m_size = -1,
+    .m_methods = py_ldb_global_methods,
+};
+
+MODULE_INIT_FUNC(ldb)
 {
 	PyObject *m;
 
 	if (PyType_Ready(&PyLdbDn) < 0)
-		return;
+		return NULL;
 
 	if (PyType_Ready(&PyLdbMessage) < 0)
-		return;
+		return NULL;
 
 	if (PyType_Ready(&PyLdbMessageElement) < 0)
-		return;
+		return NULL;
 
 	if (PyType_Ready(&PyLdb) < 0)
-		return;
+		return NULL;
 
 	if (PyType_Ready(&PyLdbModule) < 0)
-		return;
+		return NULL;
 
 	if (PyType_Ready(&PyLdbTree) < 0)
-		return;
+		return NULL;
 
 	if (PyType_Ready(&PyLdbResult) < 0)
-		return;
+		return NULL;
 
 	if (PyType_Ready(&PyLdbControl) < 0)
-		return;
+		return NULL;
 
-	m = Py_InitModule3("ldb", py_ldb_global_methods, 
-		"An interface to LDB, a LDAP-like API that can either to talk an embedded database (TDB-based) or a standards-compliant LDAP server.");
+	m = PyModule_Create(&moduledef);
 	if (m == NULL)
-		return;
-
-	PyModule_AddObject(m, "SEQ_HIGHEST_SEQ", PyInt_FromLong(LDB_SEQ_HIGHEST_SEQ));
-	PyModule_AddObject(m, "SEQ_HIGHEST_TIMESTAMP", PyInt_FromLong(LDB_SEQ_HIGHEST_TIMESTAMP));
-	PyModule_AddObject(m, "SEQ_NEXT", PyInt_FromLong(LDB_SEQ_NEXT));
-	PyModule_AddObject(m, "SCOPE_DEFAULT", PyInt_FromLong(LDB_SCOPE_DEFAULT));
-	PyModule_AddObject(m, "SCOPE_BASE", PyInt_FromLong(LDB_SCOPE_BASE));
-	PyModule_AddObject(m, "SCOPE_ONELEVEL", PyInt_FromLong(LDB_SCOPE_ONELEVEL));
-	PyModule_AddObject(m, "SCOPE_SUBTREE", PyInt_FromLong(LDB_SCOPE_SUBTREE));
-
-	PyModule_AddObject(m, "CHANGETYPE_NONE", PyInt_FromLong(LDB_CHANGETYPE_NONE));
-	PyModule_AddObject(m, "CHANGETYPE_ADD", PyInt_FromLong(LDB_CHANGETYPE_ADD));
-	PyModule_AddObject(m, "CHANGETYPE_DELETE", PyInt_FromLong(LDB_CHANGETYPE_DELETE));
-	PyModule_AddObject(m, "CHANGETYPE_MODIFY", PyInt_FromLong(LDB_CHANGETYPE_MODIFY));
-
-	PyModule_AddObject(m, "FLAG_MOD_ADD", PyInt_FromLong(LDB_FLAG_MOD_ADD));
-	PyModule_AddObject(m, "FLAG_MOD_REPLACE", PyInt_FromLong(LDB_FLAG_MOD_REPLACE));
-	PyModule_AddObject(m, "FLAG_MOD_DELETE", PyInt_FromLong(LDB_FLAG_MOD_DELETE));
-
-	PyModule_AddObject(m, "SUCCESS", PyInt_FromLong(LDB_SUCCESS));
-	PyModule_AddObject(m, "ERR_OPERATIONS_ERROR", PyInt_FromLong(LDB_ERR_OPERATIONS_ERROR));
-	PyModule_AddObject(m, "ERR_PROTOCOL_ERROR", PyInt_FromLong(LDB_ERR_PROTOCOL_ERROR));
-	PyModule_AddObject(m, "ERR_TIME_LIMIT_EXCEEDED", PyInt_FromLong(LDB_ERR_TIME_LIMIT_EXCEEDED));
-	PyModule_AddObject(m, "ERR_SIZE_LIMIT_EXCEEDED", PyInt_FromLong(LDB_ERR_SIZE_LIMIT_EXCEEDED));
-	PyModule_AddObject(m, "ERR_COMPARE_FALSE", PyInt_FromLong(LDB_ERR_COMPARE_FALSE));
-	PyModule_AddObject(m, "ERR_COMPARE_TRUE", PyInt_FromLong(LDB_ERR_COMPARE_TRUE));
-	PyModule_AddObject(m, "ERR_AUTH_METHOD_NOT_SUPPORTED", PyInt_FromLong(LDB_ERR_AUTH_METHOD_NOT_SUPPORTED));
-	PyModule_AddObject(m, "ERR_STRONG_AUTH_REQUIRED", PyInt_FromLong(LDB_ERR_STRONG_AUTH_REQUIRED));
-	PyModule_AddObject(m, "ERR_REFERRAL", PyInt_FromLong(LDB_ERR_REFERRAL));
-	PyModule_AddObject(m, "ERR_ADMIN_LIMIT_EXCEEDED", PyInt_FromLong(LDB_ERR_ADMIN_LIMIT_EXCEEDED));
-	PyModule_AddObject(m, "ERR_UNSUPPORTED_CRITICAL_EXTENSION", PyInt_FromLong(LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION));
-	PyModule_AddObject(m, "ERR_CONFIDENTIALITY_REQUIRED", PyInt_FromLong(LDB_ERR_CONFIDENTIALITY_REQUIRED));
-	PyModule_AddObject(m, "ERR_SASL_BIND_IN_PROGRESS", PyInt_FromLong(LDB_ERR_SASL_BIND_IN_PROGRESS));
-	PyModule_AddObject(m, "ERR_NO_SUCH_ATTRIBUTE", PyInt_FromLong(LDB_ERR_NO_SUCH_ATTRIBUTE));
-	PyModule_AddObject(m, "ERR_UNDEFINED_ATTRIBUTE_TYPE", PyInt_FromLong(LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE));
-	PyModule_AddObject(m, "ERR_INAPPROPRIATE_MATCHING", PyInt_FromLong(LDB_ERR_INAPPROPRIATE_MATCHING));
-	PyModule_AddObject(m, "ERR_CONSTRAINT_VIOLATION", PyInt_FromLong(LDB_ERR_CONSTRAINT_VIOLATION));
-	PyModule_AddObject(m, "ERR_ATTRIBUTE_OR_VALUE_EXISTS", PyInt_FromLong(LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS));
-	PyModule_AddObject(m, "ERR_INVALID_ATTRIBUTE_SYNTAX", PyInt_FromLong(LDB_ERR_INVALID_ATTRIBUTE_SYNTAX));
-	PyModule_AddObject(m, "ERR_NO_SUCH_OBJECT", PyInt_FromLong(LDB_ERR_NO_SUCH_OBJECT));
-	PyModule_AddObject(m, "ERR_ALIAS_PROBLEM", PyInt_FromLong(LDB_ERR_ALIAS_PROBLEM));
-	PyModule_AddObject(m, "ERR_INVALID_DN_SYNTAX", PyInt_FromLong(LDB_ERR_INVALID_DN_SYNTAX));
-	PyModule_AddObject(m, "ERR_ALIAS_DEREFERINCING_PROBLEM", PyInt_FromLong(LDB_ERR_ALIAS_DEREFERENCING_PROBLEM));
-	PyModule_AddObject(m, "ERR_INAPPROPRIATE_AUTHENTICATION", PyInt_FromLong(LDB_ERR_INAPPROPRIATE_AUTHENTICATION));
-	PyModule_AddObject(m, "ERR_INVALID_CREDENTIALS", PyInt_FromLong(LDB_ERR_INVALID_CREDENTIALS));
-	PyModule_AddObject(m, "ERR_INSUFFICIENT_ACCESS_RIGHTS", PyInt_FromLong(LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS));
-	PyModule_AddObject(m, "ERR_BUSY", PyInt_FromLong(LDB_ERR_BUSY));
-	PyModule_AddObject(m, "ERR_UNAVAILABLE", PyInt_FromLong(LDB_ERR_UNAVAILABLE));
-	PyModule_AddObject(m, "ERR_UNWILLING_TO_PERFORM", PyInt_FromLong(LDB_ERR_UNWILLING_TO_PERFORM));
-	PyModule_AddObject(m, "ERR_LOOP_DETECT", PyInt_FromLong(LDB_ERR_LOOP_DETECT));
-	PyModule_AddObject(m, "ERR_NAMING_VIOLATION", PyInt_FromLong(LDB_ERR_NAMING_VIOLATION));
-	PyModule_AddObject(m, "ERR_OBJECT_CLASS_VIOLATION", PyInt_FromLong(LDB_ERR_OBJECT_CLASS_VIOLATION));
-	PyModule_AddObject(m, "ERR_NOT_ALLOWED_ON_NON_LEAF", PyInt_FromLong(LDB_ERR_NOT_ALLOWED_ON_NON_LEAF));
-	PyModule_AddObject(m, "ERR_NOT_ALLOWED_ON_RDN", PyInt_FromLong(LDB_ERR_NOT_ALLOWED_ON_RDN));
-	PyModule_AddObject(m, "ERR_ENTRY_ALREADY_EXISTS", PyInt_FromLong(LDB_ERR_ENTRY_ALREADY_EXISTS));
-	PyModule_AddObject(m, "ERR_OBJECT_CLASS_MODS_PROHIBITED", PyInt_FromLong(LDB_ERR_OBJECT_CLASS_MODS_PROHIBITED));
-	PyModule_AddObject(m, "ERR_AFFECTS_MULTIPLE_DSAS", PyInt_FromLong(LDB_ERR_AFFECTS_MULTIPLE_DSAS));
-	PyModule_AddObject(m, "ERR_OTHER", PyInt_FromLong(LDB_ERR_OTHER));
-
-	PyModule_AddObject(m, "FLG_RDONLY", PyInt_FromLong(LDB_FLG_RDONLY));
-	PyModule_AddObject(m, "FLG_NOSYNC", PyInt_FromLong(LDB_FLG_NOSYNC));
-	PyModule_AddObject(m, "FLG_RECONNECT", PyInt_FromLong(LDB_FLG_RECONNECT));
-	PyModule_AddObject(m, "FLG_NOMMAP", PyInt_FromLong(LDB_FLG_NOMMAP));
-
-	PyModule_AddObject(m, "__docformat__", PyString_FromString("restructuredText"));
+		return NULL;
+
+#define ADD_LDB_INT(val)  PyModule_AddIntConstant(m, #val, LDB_## val)
+
+	ADD_LDB_INT(SEQ_HIGHEST_SEQ);
+	ADD_LDB_INT(SEQ_HIGHEST_TIMESTAMP);
+	ADD_LDB_INT(SEQ_NEXT);
+	ADD_LDB_INT(SCOPE_DEFAULT);
+	ADD_LDB_INT(SCOPE_BASE);
+	ADD_LDB_INT(SCOPE_ONELEVEL);
+	ADD_LDB_INT(SCOPE_SUBTREE);
+
+	ADD_LDB_INT(CHANGETYPE_NONE);
+	ADD_LDB_INT(CHANGETYPE_ADD);
+	ADD_LDB_INT(CHANGETYPE_DELETE);
+	ADD_LDB_INT(CHANGETYPE_MODIFY);
+
+	ADD_LDB_INT(FLAG_MOD_ADD);
+	ADD_LDB_INT(FLAG_MOD_REPLACE);
+	ADD_LDB_INT(FLAG_MOD_DELETE);
+
+	ADD_LDB_INT(SUCCESS);
+	ADD_LDB_INT(ERR_OPERATIONS_ERROR);
+	ADD_LDB_INT(ERR_PROTOCOL_ERROR);
+	ADD_LDB_INT(ERR_TIME_LIMIT_EXCEEDED);
+	ADD_LDB_INT(ERR_SIZE_LIMIT_EXCEEDED);
+	ADD_LDB_INT(ERR_COMPARE_FALSE);
+	ADD_LDB_INT(ERR_COMPARE_TRUE);
+	ADD_LDB_INT(ERR_AUTH_METHOD_NOT_SUPPORTED);
+	ADD_LDB_INT(ERR_STRONG_AUTH_REQUIRED);
+	ADD_LDB_INT(ERR_REFERRAL);
+	ADD_LDB_INT(ERR_ADMIN_LIMIT_EXCEEDED);
+	ADD_LDB_INT(ERR_UNSUPPORTED_CRITICAL_EXTENSION);
+	ADD_LDB_INT(ERR_CONFIDENTIALITY_REQUIRED);
+	ADD_LDB_INT(ERR_SASL_BIND_IN_PROGRESS);
+	ADD_LDB_INT(ERR_NO_SUCH_ATTRIBUTE);
+	ADD_LDB_INT(ERR_UNDEFINED_ATTRIBUTE_TYPE);
+	ADD_LDB_INT(ERR_INAPPROPRIATE_MATCHING);
+	ADD_LDB_INT(ERR_CONSTRAINT_VIOLATION);
+	ADD_LDB_INT(ERR_ATTRIBUTE_OR_VALUE_EXISTS);
+	ADD_LDB_INT(ERR_INVALID_ATTRIBUTE_SYNTAX);
+	ADD_LDB_INT(ERR_NO_SUCH_OBJECT);
+	ADD_LDB_INT(ERR_ALIAS_PROBLEM);
+	ADD_LDB_INT(ERR_INVALID_DN_SYNTAX);
+	PyModule_AddIntConstant(m, "ERR_ALIAS_DEREFERINCING_PROBLEM", LDB_ERR_ALIAS_DEREFERENCING_PROBLEM);
+	ADD_LDB_INT(ERR_INAPPROPRIATE_AUTHENTICATION);
+	ADD_LDB_INT(ERR_INVALID_CREDENTIALS);
+	ADD_LDB_INT(ERR_INSUFFICIENT_ACCESS_RIGHTS);
+	ADD_LDB_INT(ERR_BUSY);
+	ADD_LDB_INT(ERR_UNAVAILABLE);
+	ADD_LDB_INT(ERR_UNWILLING_TO_PERFORM);
+	ADD_LDB_INT(ERR_LOOP_DETECT);
+	ADD_LDB_INT(ERR_NAMING_VIOLATION);
+	ADD_LDB_INT(ERR_OBJECT_CLASS_VIOLATION);
+	ADD_LDB_INT(ERR_NOT_ALLOWED_ON_NON_LEAF);
+	ADD_LDB_INT(ERR_NOT_ALLOWED_ON_RDN);
+	ADD_LDB_INT(ERR_ENTRY_ALREADY_EXISTS);
+	ADD_LDB_INT(ERR_OBJECT_CLASS_MODS_PROHIBITED);
+	ADD_LDB_INT(ERR_AFFECTS_MULTIPLE_DSAS);
+	ADD_LDB_INT(ERR_OTHER);
+
+	ADD_LDB_INT(FLG_RDONLY);
+	ADD_LDB_INT(FLG_NOSYNC);
+	ADD_LDB_INT(FLG_RECONNECT);
+	ADD_LDB_INT(FLG_NOMMAP);
+
+	PyModule_AddStringConstant(m, "__docformat__", "restructuredText");
 
 	PyExc_LdbError = PyErr_NewException(discard_const_p(char, "_ldb.LdbError"), NULL, NULL);
 	PyModule_AddObject(m, "LdbError", PyExc_LdbError);
@@ -3623,9 +3661,9 @@ void initldb(void)
 	PyModule_AddObject(m, "Tree", (PyObject *)&PyLdbTree);
 	PyModule_AddObject(m, "Control", (PyObject *)&PyLdbControl);
 
-	PyModule_AddObject(m, "__version__", PyString_FromString(PACKAGE_VERSION));
+	PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION);
 
-#define ADD_LDB_STRING(val)  PyModule_AddObject(m, #val, PyString_FromString(LDB_## val))
+#define ADD_LDB_STRING(val)  PyModule_AddStringConstant(m, #val, LDB_## val)
 
 	ADD_LDB_STRING(SYNTAX_DN);
 	ADD_LDB_STRING(SYNTAX_DIRECTORY_STRING);
@@ -3635,4 +3673,6 @@ void initldb(void)
 	ADD_LDB_STRING(SYNTAX_UTC_TIME);
 	ADD_LDB_STRING(OID_COMPARATOR_AND);
 	ADD_LDB_STRING(OID_COMPARATOR_OR);
+
+	return m;
 }
diff --git a/lib/ldb/pyldb_util.c b/lib/ldb/pyldb_util.c
index 4be9126..70757a4 100644
--- a/lib/ldb/pyldb_util.c
+++ b/lib/ldb/pyldb_util.c
@@ -24,6 +24,7 @@
 */
 
 #include <Python.h>
+#include "py3compat.h"
 #include "ldb.h"
 #include "pyldb.h"
 
@@ -69,8 +70,8 @@ bool pyldb_Object_AsDn(TALLOC_CTX *mem_ctx, PyObject *object,
 	struct ldb_dn *odn;
 	PyTypeObject *PyLdb_Dn_Type;
 
-	if (ldb_ctx != NULL && PyString_Check(object)) {
-		odn = ldb_dn_new(mem_ctx, ldb_ctx, PyString_AsString(object));
+	if (ldb_ctx != NULL && PyStr_Check(object)) {
+		odn = ldb_dn_new(mem_ctx, ldb_ctx, PyStr_AsString(object));
 		*dn = odn;
 		return true;
 	}
diff --git a/lib/ldb/tests/python/api.py b/lib/ldb/tests/python/api.py
index d101de8..faed913 100755
--- a/lib/ldb/tests/python/api.py
+++ b/lib/ldb/tests/python/api.py
@@ -3,6 +3,7 @@
 # Copyright (C) 2007 Jelmer Vernooij <jelmer@samba.org>
 
 import os
+import sys
 from unittest import TestCase
 
 import ldb
@@ -54,7 +55,7 @@ class SimpleLdb(TestCase):
 
     def test_set_create_perms(self):
         x = ldb.Ldb()
-        x.set_create_perms(0600)
+        x.set_create_perms(0o600)
 
     def test_modules_none(self):
         x = ldb.Ldb()
@@ -103,7 +104,7 @@ class SimpleLdb(TestCase):
         l = ldb.Ldb(filename())
         m = ldb.Message()
         m.dn = ldb.Dn(l, "dc=foo1")
-        m["b"] = ["a"]
+        m["b"] = [b"a"]
         l.add(m)
         self.assertRaises(ldb.LdbError, lambda: l.delete(m.dn, ["search_options:1:2"]))
         l.delete(m.dn)
@@ -115,7 +116,7 @@ class SimpleLdb(TestCase):
         l = ldb.Ldb(name)
         m = ldb.Message()
         m.dn = ldb.Dn(l, "dc=foo3")
-        m["b"] = ["a"]
+        m["b"] = [b"a"]
         l.add(m)
         try:
             self.assertTrue(ldb.Dn(l, "dc=foo3") in l)
@@ -142,7 +143,7 @@ class SimpleLdb(TestCase):
         l = ldb.Ldb(filename())
         m = ldb.Message()
         m.dn = ldb.Dn(l, "dc=foo4")
-        m["bla"] = "bla"
+        m["bla"] = b"bla"
         self.assertEquals(len(l.search()), 0)
         l.add(m)
         try:
@@ -154,14 +155,14 @@ class SimpleLdb(TestCase):
         l = ldb.Ldb(filename())
         m = ldb.Message()
         m.dn = ldb.Dn(l, "dc=foo4")
-        m["bla"] = "bla"
+        m["bla"] = b"bla"
         self.assertEquals(len(l.search()), 0)
         self.assertRaises(ldb.LdbError, lambda: l.add(m,["search_options:1:2"]))
 
     def test_add_dict(self):
         l = ldb.Ldb(filename())
         m = {"dn": ldb.Dn(l, "dc=foo5"),
-             "bla": "bla"}
+             "bla": b"bla"}
         self.assertEquals(len(l.search()), 0)
         l.add(m)
         try:
@@ -171,7 +172,7 @@ class SimpleLdb(TestCase):
 
     def test_add_dict_string_dn(self):
         l = ldb.Ldb(filename())
-        m = {"dn": "dc=foo6", "bla": "bla"}
+        m = {"dn": "dc=foo6", "bla": b"bla"}
         self.assertEquals(len(l.search()), 0)
         l.add(m)
         try:
@@ -183,7 +184,7 @@ class SimpleLdb(TestCase):
         l = ldb.Ldb(filename())
         m = ldb.Message()
         m.dn = ldb.Dn(l, "dc=foo7")
-        m["bla"] = "bla"
+        m["bla"] = b"bla"
         self.assertEquals(len(l.search()), 0)
         l.add(m)
         try:
@@ -196,7 +197,7 @@ class SimpleLdb(TestCase):
         l = ldb.Ldb(filename())
         m = ldb.Message()
         m.dn = ldb.Dn(l, "dc=foo8")
-        m["bla"] = "bla"
+        m["bla"] = b"bla"
         self.assertEquals(len(l.search()), 0)
         l.add(m)
         self.assertEquals(len(l.search()), 1)
@@ -210,10 +211,10 @@ class SimpleLdb(TestCase):
         l = ldb.Ldb(filename())
         m = ldb.Message()
         m.dn = ldb.Dn(l, "dc=modifydelete")
-        m["bla"] = ["1234"]
+        m["bla"] = [b"1234"]
         l.add(m)
         rm = l.search(m.dn)[0]
-        self.assertEquals(["1234"], list(rm["bla"]))
+        self.assertEquals([b"1234"], list(rm["bla"]))
         try:
             m = ldb.Message()
             m.dn = ldb.Dn(l, "dc=modifydelete")
@@ -231,17 +232,17 @@ class SimpleLdb(TestCase):
         l = ldb.Ldb(filename())
         m = ldb.Message()
         m.dn = ldb.Dn(l, "dc=add")
-        m["bla"] = ["1234"]
+        m["bla"] = [b"1234"]
         l.add(m)
         try:
             m = ldb.Message()
             m.dn = ldb.Dn(l, "dc=add")
-            m["bla"] = ldb.MessageElement(["456"], ldb.FLAG_MOD_ADD, "bla")
+            m["bla"] = ldb.MessageElement([b"456"], ldb.FLAG_MOD_ADD, "bla")
             self.assertEquals(ldb.FLAG_MOD_ADD, m["bla"].flags())
             l.modify(m)
             rm = l.search(m.dn)[0]
             self.assertEquals(2, len(rm))
-            self.assertEquals(["1234", "456"], list(rm["bla"]))
+            self.assertEquals([b"1234", b"456"], list(rm["bla"]))
         finally:
             l.delete(ldb.Dn(l, "dc=add"))
 
@@ -249,17 +250,17 @@ class SimpleLdb(TestCase):
         l = ldb.Ldb(filename())
         m = ldb.Message()
         m.dn = ldb.Dn(l, "dc=modify2")
-        m["bla"] = ["1234", "456"]
+        m["bla"] = [b"1234", b"456"]
         l.add(m)
         try:
             m = ldb.Message()
             m.dn = ldb.Dn(l, "dc=modify2")
-            m["bla"] = ldb.MessageElement(["789"], ldb.FLAG_MOD_REPLACE, "bla")
+            m["bla"] = ldb.MessageElement([b"789"], ldb.FLAG_MOD_REPLACE, "bla")
             self.assertEquals(ldb.FLAG_MOD_REPLACE, m["bla"].flags())
             l.modify(m)
             rm = l.search(m.dn)[0]
             self.assertEquals(2, len(rm))
-            self.assertEquals(["789"], list(rm["bla"]))
+            self.assertEquals([b"789"], list(rm["bla"]))
             rm = l.search(m.dn, attrs=["bla"])[0]
             self.assertEquals(1, len(rm))
         finally:
@@ -269,25 +270,25 @@ class SimpleLdb(TestCase):
         l = ldb.Ldb(filename())
         m = ldb.Message()
         m.dn = ldb.Dn(l, "dc=add")
-        m["bla"] = ["1234"]
+        m["bla"] = [b"1234"]
         l.add(m)
         try:
             m = ldb.Message()
             m.dn = ldb.Dn(l, "dc=add")
-            m["bla"] = ldb.MessageElement(["456"], ldb.FLAG_MOD_ADD, "bla")
+            m["bla"] = ldb.MessageElement([b"456"], ldb.FLAG_MOD_ADD, "bla")
             self.assertEquals(ldb.FLAG_MOD_ADD, m["bla"].flags())
             l.modify(m)
             rm = l.search(m.dn)[0]
             self.assertEquals(2, len(rm))
-            self.assertEquals(["1234", "456"], list(rm["bla"]))
+            self.assertEquals([b"1234", b"456"], list(rm["bla"]))
 
             # Now create another modify, but switch the flags before we do it
-            m["bla"] = ldb.MessageElement(["456"], ldb.FLAG_MOD_ADD, "bla")
+            m["bla"] = ldb.MessageElement([b"456"], ldb.FLAG_MOD_ADD, "bla")
             m["bla"].set_flags(ldb.FLAG_MOD_DELETE)
             l.modify(m)
             rm = l.search(m.dn, attrs=["bla"])[0]
             self.assertEquals(1, len(rm))
-            self.assertEquals(["1234"], list(rm["bla"]))
+            self.assertEquals([b"1234"], list(rm["bla"]))
         finally:
             l.delete(ldb.Dn(l, "dc=add"))
 
@@ -295,7 +296,7 @@ class SimpleLdb(TestCase):
         l = ldb.Ldb(filename())
         l.transaction_start()
         m = ldb.Message(ldb.Dn(l, "dc=foo9"))
-        m["foo"] = ["bar"]
+        m["foo"] = [b"bar"]
         l.add(m)
         l.transaction_commit()
         l.delete(m.dn)
@@ -304,7 +305,7 @@ class SimpleLdb(TestCase):
         l = ldb.Ldb(filename())
         l.transaction_start()
         m = ldb.Message(ldb.Dn(l, "dc=foo10"))
-        m["foo"] = ["bar"]
+        m["foo"] = [b"bar"]
         l.add(m)
         l.transaction_cancel()
         self.assertEquals(0, len(l.search(ldb.Dn(l, "dc=foo10"))))
@@ -319,14 +320,14 @@ class SimpleLdb(TestCase):
         """Testing we do not get trapped in the \0 byte in a property string."""
         l = ldb.Ldb(filename())
         l.add({
-            "dn" : "dc=somedn",
-            "objectclass" : "user",
-            "cN" : "LDAPtestUSER",
-            "givenname" : "ldap",
-            "displayname" : "foo\0bar",
+            "dn": "dc=somedn",
+            "objectclass": b"user",
+            "cN": b"LDAPtestUSER",
+            "givenname": b"ldap",
+            "displayname": b"foo\0bar",
         })
         res = l.search(expression="(dn=dc=somedn)")
-        self.assertEquals("foo\0bar", res[0]["displayname"][0])
+        self.assertEquals(b"foo\0bar", res[0]["displayname"][0])
 
     def test_no_crash_broken_expr(self):
         l = ldb.Ldb(filename())
@@ -423,7 +424,7 @@ class DnTests(TestCase):
 
     def test_parse_ldif(self):
         msgs = self.ldb.parse_ldif("dn: foo=bar\n")
-        msg = msgs.next()
+        msg = next(msgs)
         self.assertEquals("foo=bar", str(msg[1].dn))
         self.assertTrue(isinstance(msg[1], ldb.Message))
         ldif = self.ldb.write_ldif(msg[1], ldb.CHANGETYPE_NONE)
@@ -431,9 +432,9 @@ class DnTests(TestCase):
 
     def test_parse_ldif_more(self):
         msgs = self.ldb.parse_ldif("dn: foo=bar\n\n\ndn: bar=bar")
-        msg = msgs.next()
+        msg = next(msgs)
         self.assertEquals("foo=bar", str(msg[1].dn))
-        msg = msgs.next()
+        msg = next(msgs)
         self.assertEquals("bar=bar", str(msg[1].dn))
 
     def test_canonical_string(self):
@@ -474,8 +475,14 @@ class LdbMsgTests(TestCase):
 
     def test_repr(self):
         self.msg.dn = ldb.Dn(ldb.Ldb(filename()), "dc=foo29")
-        self.msg["dc"] = "foo"
-        self.assertEquals("Message({'dn': Dn('dc=foo29'), 'dc': MessageElement(['foo'])})", repr(self.msg))
+        self.msg["dc"] = b"foo"
+        if sys.version_info > (3, 0):
+            self.assertIn(repr(self.msg), [
+                "Message({'dn': Dn('dc=foo29'), 'dc': MessageElement([b'foo'])})",
+                "Message({'dc': MessageElement([b'foo']), 'dn': Dn('dc=foo29')})",
+            ])
+        else:
+            self.assertEquals("Message({'dn': Dn('dc=foo29'), 'dc': MessageElement(['foo'])})", repr(self.msg))
 
     def test_len(self):
         self.assertEquals(0, len(self.msg))
@@ -487,37 +494,37 @@ class LdbMsgTests(TestCase):
         del self.msg["foo"]
 
     def test_add(self):
-        self.msg.add(ldb.MessageElement(["456"], ldb.FLAG_MOD_ADD, "bla"))
+        self.msg.add(ldb.MessageElement([b"456"], ldb.FLAG_MOD_ADD, "bla"))
 
     def test_elements_empty(self):
         self.assertEquals([], self.msg.elements())
 
     def test_elements(self):
-        el = ldb.MessageElement(["456"], ldb.FLAG_MOD_ADD, "bla")
+        el = ldb.MessageElement([b"456"], ldb.FLAG_MOD_ADD, "bla")
         self.msg.add(el)
         self.assertEquals([el], self.msg.elements())
 
     def test_add_value(self):
         self.assertEquals(0, len(self.msg))
-        self.msg["foo"] = ["foo"]
+        self.msg["foo"] = [b"foo"]
         self.assertEquals(1, len(self.msg))
 
     def test_add_value_multiple(self):
         self.assertEquals(0, len(self.msg))
-        self.msg["foo"] = ["foo", "bla"]
+        self.msg["foo"] = [b"foo", b"bla"]
         self.assertEquals(1, len(self.msg))
-        self.assertEquals(["foo", "bla"], list(self.msg["foo"]))
+        self.assertEquals([b"foo", b"bla"], list(self.msg["foo"]))
 
     def test_set_value(self):
-        self.msg["foo"] = ["fool"]
-        self.assertEquals(["fool"], list(self.msg["foo"]))
-        self.msg["foo"] = ["bar"]
-        self.assertEquals(["bar"], list(self.msg["foo"]))
+        self.msg["foo"] = [b"fool"]
+        self.assertEquals([b"fool"], list(self.msg["foo"]))
+        self.msg["foo"] = [b"bar"]
+        self.assertEquals([b"bar"], list(self.msg["foo"]))
 
     def test_keys(self):
         self.msg.dn = ldb.Dn(ldb.Ldb(filename()), "@BASEINFO")
-        self.msg["foo"] = ["bla"]
-        self.msg["bar"] = ["bla"]
+        self.msg["foo"] = [b"bla"]
+        self.msg["bar"] = [b"bla"]
         self.assertEquals(["dn", "foo", "bar"], self.msg.keys())
 
     def test_dn(self):
@@ -533,9 +540,9 @@ class LdbMsgTests(TestCase):
         self.assertRaises(TypeError, self.msg.get, 42)
 
     def test_get_other(self):
-        self.msg["foo"] = ["bar"]
-        self.assertEquals("bar", self.msg.get("foo")[0])
-        self.assertEquals("bar", self.msg.get("foo", idx=0))
+        self.msg["foo"] = [b"bar"]
+        self.assertEquals(b"bar", self.msg.get("foo")[0])
+        self.assertEquals(b"bar", self.msg.get("foo", idx=0))
         self.assertEquals(None, self.msg.get("foo", idx=1))
         self.assertEquals("", self.msg.get("foo", default='', idx=1))
 
@@ -549,8 +556,8 @@ class LdbMsgTests(TestCase):
     def test_msg_diff(self):
         l = ldb.Ldb()
         msgs = l.parse_ldif("dn: foo=bar\nfoo: bar\nbaz: do\n\ndn: foo=bar\nfoo: bar\nbaz: dont\n")
-        msg1 = msgs.next()[1]
-        msg2 = msgs.next()[1]
+        msg1 = next(msgs)[1]
+        msg2 = next(msgs)[1]
         msgdiff = l.msg_diff(msg1, msg2)
         self.assertEquals("foo=bar", msgdiff.get("dn").__str__())
         self.assertRaises(KeyError, lambda: msgdiff["foo"])
@@ -568,16 +575,16 @@ class LdbMsgTests(TestCase):
         msg2 = ldb.Message()
         msg2.dn = ldb.Dn(db, "foo=bar")
         self.assertEquals(msg1, msg2)
-        msg1['foo'] = 'bar'
-        msg2['foo'] = 'bar'
+        msg1['foo'] = b'bar'
+        msg2['foo'] = b'bar'
         self.assertEquals(msg1, msg2)
-        msg2['foo'] = 'blie'
+        msg2['foo'] = b'blie'
         self.assertNotEquals(msg1, msg2)
-        msg2['foo'] = 'blie'
+        msg2['foo'] = b'blie'
 
     def test_from_dict(self):
         rec = {"dn": "dc=fromdict",
-               "a1": ["a1-val1", "a1-val1"]}
+               "a1": [b"a1-val1", b"a1-val1"]}
         l = ldb.Ldb()
         # check different types of input Flags
         for flags in [ldb.FLAG_MOD_ADD, ldb.FLAG_MOD_REPLACE, ldb.FLAG_MOD_DELETE]:
@@ -594,63 +601,72 @@ class LdbMsgTests(TestCase):
 
     def test_copy_add_message_element(self):
         m = ldb.Message()
-        m["1"] = ldb.MessageElement(["val 111"], ldb.FLAG_MOD_ADD, "1")
-        m["2"] = ldb.MessageElement(["val 222"], ldb.FLAG_MOD_ADD, "2")
+        m["1"] = ldb.MessageElement([b"val 111"], ldb.FLAG_MOD_ADD, "1")
+        m["2"] = ldb.MessageElement([b"val 222"], ldb.FLAG_MOD_ADD, "2")
         mto = ldb.Message()
         mto["1"] = m["1"]
         mto["2"] = m["2"]
-        self.assertEqual(mto["1"], m["1"])
-        self.assertEqual(mto["2"], m["2"])
+        self.assertEquals(mto["1"], m["1"])
+        self.assertEquals(mto["2"], m["2"])
         mto = ldb.Message()
         mto.add(m["1"])
         mto.add(m["2"])
-        self.assertEqual(mto["1"], m["1"])
-        self.assertEqual(mto["2"], m["2"])
+        self.assertEquals(mto["1"], m["1"])
+        self.assertEquals(mto["2"], m["2"])
 
 
 class MessageElementTests(TestCase):
 
     def test_cmp_element(self):
-        x = ldb.MessageElement(["foo"])
-        y = ldb.MessageElement(["foo"])
-        z = ldb.MessageElement(["bzr"])
+        x = ldb.MessageElement([b"foo"])
+        y = ldb.MessageElement([b"foo"])
+        z = ldb.MessageElement([b"bzr"])
         self.assertEquals(x, y)
         self.assertNotEquals(x, z)
 
     def test_create_iterable(self):
-        x = ldb.MessageElement(["foo"])
-        self.assertEquals(["foo"], list(x))
+        x = ldb.MessageElement([b"foo"])
+        self.assertEquals([b"foo"], list(x))
 
     def test_repr(self):
-        x = ldb.MessageElement(["foo"])
-        self.assertEquals("MessageElement(['foo'])", repr(x))
-        x = ldb.MessageElement(["foo", "bla"])
+        x = ldb.MessageElement([b"foo"])
+        if sys.version_info > (3, 0):
+            self.assertEquals("MessageElement([b'foo'])", repr(x))
+        else:
+            self.assertEquals("MessageElement(['foo'])", repr(x))
+        x = ldb.MessageElement([b"foo", b"bla"])
         self.assertEquals(2, len(x))
-        self.assertEquals("MessageElement(['foo','bla'])", repr(x))
+        if sys.version_info > (3, 0):
+            self.assertEquals("MessageElement([b'foo',b'bla'])", repr(x))
+        else:
+            self.assertEquals("MessageElement(['foo','bla'])", repr(x))
 
     def test_get_item(self):
-        x = ldb.MessageElement(["foo", "bar"])
-        self.assertEquals("foo", x[0])
-        self.assertEquals("bar", x[1])
-        self.assertEquals("bar", x[-1])
+        x = ldb.MessageElement([b"foo", b"bar"])
+        self.assertEquals(b"foo", x[0])
+        self.assertEquals(b"bar", x[1])
+        self.assertEquals(b"bar", x[-1])
         self.assertRaises(IndexError, lambda: x[45])
 
     def test_len(self):
-        x = ldb.MessageElement(["foo", "bar"])
+        x = ldb.MessageElement([b"foo", b"bar"])
         self.assertEquals(2, len(x))
 
     def test_eq(self):
-        x = ldb.MessageElement(["foo", "bar"])
-        y = ldb.MessageElement(["foo", "bar"])
+        x = ldb.MessageElement([b"foo", b"bar"])
+        y = ldb.MessageElement([b"foo", b"bar"])
         self.assertEquals(y, x)
-        x = ldb.MessageElement(["foo"])
+        x = ldb.MessageElement([b"foo"])
         self.assertNotEquals(y, x)
-        y = ldb.MessageElement(["foo"])
+        y = ldb.MessageElement([b"foo"])
         self.assertEquals(y, x)
 
     def test_extended(self):
-        el = ldb.MessageElement(["456"], ldb.FLAG_MOD_ADD, "bla")
-        self.assertEquals("MessageElement(['456'])", repr(el))
+        el = ldb.MessageElement([b"456"], ldb.FLAG_MOD_ADD, "bla")
+        if sys.version_info > (3, 0):
+            self.assertEquals("MessageElement([b'456'])", repr(el))
+        else:
+            self.assertEquals("MessageElement(['456'])", repr(el))
 
 
 class ModuleTests(TestCase):
@@ -680,7 +696,7 @@ class ModuleTests(TestCase):
         if os.path.exists(name):
             os.unlink(name)
         l = ldb.Ldb(name)
-        l.add({"dn": "@MODULES", "@LIST": "bla"})
+        l.add({"dn": "@MODULES", "@LIST": b"bla"})
         self.assertEquals([], ops)
         l = ldb.Ldb(name)
         self.assertEquals(["init"], ops)
@@ -694,19 +710,19 @@ class LdbResultTests(TestCase):
         if os.path.exists(name):
             os.unlink(name)
         self.l = ldb.Ldb(name)
-        self.l.add({"dn": "DC=SAMBA,DC=ORG", "name": "samba.org"})
-        self.l.add({"dn": "OU=ADMIN,DC=SAMBA,DC=ORG", "name": "Admins"})
-        self.l.add({"dn": "OU=USERS,DC=SAMBA,DC=ORG", "name": "Users"})
-        self.l.add({"dn": "OU=OU1,DC=SAMBA,DC=ORG", "name": "OU #1"})
-        self.l.add({"dn": "OU=OU2,DC=SAMBA,DC=ORG", "name": "OU #2"})
-        self.l.add({"dn": "OU=OU3,DC=SAMBA,DC=ORG", "name": "OU #3"})
-        self.l.add({"dn": "OU=OU4,DC=SAMBA,DC=ORG", "name": "OU #4"})
-        self.l.add({"dn": "OU=OU5,DC=SAMBA,DC=ORG", "name": "OU #5"})
-        self.l.add({"dn": "OU=OU6,DC=SAMBA,DC=ORG", "name": "OU #6"})
-        self.l.add({"dn": "OU=OU7,DC=SAMBA,DC=ORG", "name": "OU #7"})
-        self.l.add({"dn": "OU=OU8,DC=SAMBA,DC=ORG", "name": "OU #8"})
-        self.l.add({"dn": "OU=OU9,DC=SAMBA,DC=ORG", "name": "OU #9"})
-        self.l.add({"dn": "OU=OU10,DC=SAMBA,DC=ORG", "name": "OU #10"})
+        self.l.add({"dn": "DC=SAMBA,DC=ORG", "name": b"samba.org"})
+        self.l.add({"dn": "OU=ADMIN,DC=SAMBA,DC=ORG", "name": b"Admins"})
+        self.l.add({"dn": "OU=USERS,DC=SAMBA,DC=ORG", "name": b"Users"})
+        self.l.add({"dn": "OU=OU1,DC=SAMBA,DC=ORG", "name": b"OU #1"})
+        self.l.add({"dn": "OU=OU2,DC=SAMBA,DC=ORG", "name": b"OU #2"})
+        self.l.add({"dn": "OU=OU3,DC=SAMBA,DC=ORG", "name": b"OU #3"})
+        self.l.add({"dn": "OU=OU4,DC=SAMBA,DC=ORG", "name": b"OU #4"})
+        self.l.add({"dn": "OU=OU5,DC=SAMBA,DC=ORG", "name": b"OU #5"})
+        self.l.add({"dn": "OU=OU6,DC=SAMBA,DC=ORG", "name": b"OU #6"})
+        self.l.add({"dn": "OU=OU7,DC=SAMBA,DC=ORG", "name": b"OU #7"})
+        self.l.add({"dn": "OU=OU8,DC=SAMBA,DC=ORG", "name": b"OU #8"})
+        self.l.add({"dn": "OU=OU9,DC=SAMBA,DC=ORG", "name": b"OU #9"})
+        self.l.add({"dn": "OU=OU10,DC=SAMBA,DC=ORG", "name": b"OU #10"})
 
     def tearDown(self):
         super(LdbResultTests, self).tearDown()
@@ -739,7 +755,7 @@ class LdbResultTests(TestCase):
     def test_iter_msgs_count(self):
         self.assertTrue(self.l.search().count > 0)
         # 13 objects has been added to the DC=SAMBA, DC=ORG
-        self.assertEqual(self.l.search(base="DC=SAMBA,DC=ORG").count, 13)
+        self.assertEquals(self.l.search(base="DC=SAMBA,DC=ORG").count, 13)
 
     def test_iter_controls(self):
         res = self.l.search().controls
diff --git a/lib/ldb/tests/test-tdb.sh b/lib/ldb/tests/test-tdb.sh
index 82eef69..70c0e0d 100755
--- a/lib/ldb/tests/test-tdb.sh
+++ b/lib/ldb/tests/test-tdb.sh
@@ -38,8 +38,3 @@ $VALGRIND ldbadd $LDBDIR/tests/init.ldif || exit 1
 . $LDBDIR/tests/test-tdb-features.sh
 
 . $LDBDIR/tests/test-controls.sh
-
-which python >/dev/null 2>&1
-if [ $? -eq 0 ]; then
-	SELFTEST_PREFIX=$PYDESTDIR PYTHONPATH=$BINDIR/python python $LDBDIR/tests/python/api.py
-fi
diff --git a/lib/ldb/wscript b/lib/ldb/wscript
index 47b8c46..aebd342 100755
--- a/lib/ldb/wscript
+++ b/lib/ldb/wscript
@@ -42,10 +42,8 @@ def configure(conf):
             conf.define('USING_SYSTEM_POPT', 1)
 
     conf.RECURSE('lib/replace')
-    conf.find_program('python', var='PYTHON')
     conf.find_program('xsltproc', var='XSLTPROC')
-    conf.check_tool('python')
-    conf.check_python_version((2,4,2))
+    conf.SAMBA_CHECK_PYTHON(version=(2, 4, 2), mandatory=False)
     conf.SAMBA_CHECK_PYTHON_HEADERS(mandatory=True)
 
     # where does the default LIBDIR end up? in conf.env somewhere?
@@ -120,8 +118,9 @@ def build(bld):
         bld.env.PACKAGE_VERSION = VERSION
         bld.env.PKGCONFIGDIR = '${LIBDIR}/pkgconfig'
 
+    pyldb_util_name = bld.pyembed_libname('pyldb-util')
     if not bld.CONFIG_SET('USING_SYSTEM_PYLDB_UTIL'):
-        bld.SAMBA_LIBRARY('pyldb-util',
+        bld.SAMBA_LIBRARY(pyldb_util_name,
                           deps='ldb',
                           source='pyldb_util.c',
                           public_headers='pyldb.h',
@@ -168,7 +167,7 @@ def build(bld):
 
 
         bld.SAMBA_PYTHON('pyldb', 'pyldb.c',
-                         deps='ldb pyldb-util',
+                         deps='ldb ' + pyldb_util_name,
                          realname='ldb.so',
                          cflags='-DPACKAGE_VERSION=\"%s\"' % VERSION)
 
@@ -274,10 +273,32 @@ def build(bld):
                           deps='ldb dl popt',
                           private_library=True)
 
+        if bld.env['EXTRA_PYTHON']:
+            pyldb_util_name = bld.pyembed_libname('pyldb-util', extrapython=True)
+            if not bld.CONFIG_SET('USING_SYSTEM_PYLDB_UTIL'):
+                bld.SAMBA_LIBRARY(pyldb_util_name,
+                                  deps='ldb',
+                                  source='pyldb_util.c',
+                                  public_headers='pyldb.h',
+                                  public_headers_install=not private_library,
+                                  vnum=VERSION,
+                                  private_library=private_library,
+                                  pc_files=None,
+                                  pyfeature='extrapyembed',
+                                  abi_directory='ABI',
+                                  abi_match='pyldb_*')
+
+            bld.SAMBA_PYTHON('extra-pyldb', 'pyldb.c',
+                             deps='ldb ' + pyldb_util_name,
+                             realname='ldb.so',
+                             cflags='-DPACKAGE_VERSION=\"%s\"' % VERSION,
+                             extra_python=True)
+
 
 def test(ctx):
     '''run ldb testsuite'''
     import Utils, samba_utils, shutil
+    env = samba_utils.LOAD_ENVIRONMENT()
     test_prefix = "%s/st" % (Utils.g_module.blddir)
     shutil.rmtree(test_prefix, ignore_errors=True)
     os.makedirs(test_prefix)
@@ -285,8 +306,11 @@ def test(ctx):
     cmd = 'tests/test-tdb.sh %s' % Utils.g_module.blddir
     ret = samba_utils.RUN_COMMAND(cmd)
     print("testsuite returned %d" % ret)
-    # FIXME: Run python testsuite
-    sys.exit(ret)
+    if not env.disable_python and not env['USING_SYSTEM_LDB']:
+        pyret = samba_utils.RUN_PYTHON_TESTS(
+            ['tests/python/api.py'],
+            setup_cmd='export TEST_DATA_PREFIX=%s' % test_prefix)
+    sys.exit(ret or pyret)
 
 def dist():
     '''makes a tarball for distribution'''
-- 
2.1.0

