[PATCHES] Port ldb to Python 3

Petr Viktorin pviktori at redhat.com
Fri Aug 14 11:07:22 UTC 2015


On 08/13/2015 03:12 PM, Alexander Bokovoy wrote:
> On Thu, 13 Aug 2015, Petr Viktorin wrote:
>> On 08/12/2015 10:53 PM, Stefan Metzmacher wrote:
>>> Hi Petr,
[...]
>>> I've reviewed most of the patches see
>>> https://git.samba.org/?p=metze/samba/wip.git;a=shortlog;h=refs/heads/master3-python
>>>
>>> I have just a couple of questions.
>>>
>>> This one needs to be squashed
>>> https://git.samba.org/?p=metze/samba/wip.git;a=commitdiff;h=985f384e1fe3d998c032eb17f01d991ea33fdbfb
>>> into
>>> https://git.samba.org/?p=metze/samba/wip.git;a=commitdiff;h=b8aaa4a020d6d36972fdf6fb33d762b9321699a7
>>> otherwise the build with --picky-developer fails.
>>
>> Thanks; I'll need to enable that option for my builds.
>>
>>> I've added some TODO's here:
>>> https://git.samba.org/?p=metze/samba/wip.git;a=commitdiff;h=5e00a20e55baa3f83d69bf0dc9280527a3af7ae1
>>>
>>> As I'm trying to use --extra-python=/usr/bin/python3 on ubuntu 14.04
>>>
>>> I've got the following when trying to bump the ldb version.
>>>
>>> https://git.samba.org/?p=metze/samba/wip.git;a=commitdiff;h=0604992f1a185557102881e4837bef291696053e
>>>
>>> A pyldb-util.cpython-34m-1.1.22.sigs is generated, the question would
>>> how can we have just
>>> one pyldb-util-1.1.22.sigs that is checked for both versions or we need
>>> a python2 and a python3 file. But that should not depend an the exact
>>> python3 version.
>>
>> I can see if I can convince the buildsystem to replace the
>> ".cpython-34m" with something like ".py3".
>> That would affect pytalloc, pytdb, pytevent as well, so I see it as
>> orthogonal to these particular patches.
>>
>> In talloc, pytalloc_CObject_FromTallocPtr is not available for Python 3
>> (and if a replacement is re-added, it would need a different name). So,
>> one file for both Pythons won't work.
>>
>> As for the ABI itself, it *is* tied to the exact Python version, even
>> for py2 (unless we'd switch to only use Python's stable ABI subset, but
>> that's not available for py2). I guess that doesn't matter for the
>> signatures file, though.
> It doesn't -- signature files are to track our ABI, not what we use from
> Python side.
> 

Thanks.

Here is a new version of the patchset. It includes a buildtools change
to replace the ABI tag by just ".py3". See the first patch.

I've also moved the wscript patch to the end as discussed earlier in
this thread, and I've squashed in Stefan's const-correctness fixes.

-- 
Petr Viktorin
-------------- next part --------------
From e7bf57b2c505472f77cedccde76fa5c2153835de Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori at redhat.com>
Date: Fri, 14 Aug 2015 12:17:48 +0200
Subject: [PATCH 01/10] buildtools: Ignore exact Python version for ABI
 checking

Utilities for Python libraries are built for a specific Python
version. Starting with Python 3, the Python version is recorded
in the shared library filename as an ABI tag, e.g. "pytalloc.cpython-34m.so.
The exact version doesn't matter for Samba's ABI check.
Replace the ABI tag with a simpler one that just records the major
version of Python.

Signed-off-by: Petr Viktorin <pviktori at redhat.com>
---
 buildtools/wafsamba/samba_abi.py |  3 ++-
 buildtools/wafsamba/wafsamba.py  | 12 +++++++++++-
 2 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/buildtools/wafsamba/samba_abi.py b/buildtools/wafsamba/samba_abi.py
index 76acd00..3be782a 100644
--- a/buildtools/wafsamba/samba_abi.py
+++ b/buildtools/wafsamba/samba_abi.py
@@ -137,7 +137,8 @@ def abi_check(self):
     topsrc = self.bld.srcnode.abspath()
     abi_gen = os.path.join(topsrc, 'buildtools/scripts/abi_gen.sh')
 
-    abi_file = "%s/%s-%s.sigs" % (self.abi_directory, self.name, self.vnum)
+    abi_file = "%s/%s-%s.sigs" % (self.abi_directory, self.version_libname,
+                                  self.vnum)
 
     tsk = self.create_task('abi_check', self.link_task.outputs[0])
     tsk.ABI_FILE = abi_file
diff --git a/buildtools/wafsamba/wafsamba.py b/buildtools/wafsamba/wafsamba.py
index c27241e..f902851 100644
--- a/buildtools/wafsamba/wafsamba.py
+++ b/buildtools/wafsamba/wafsamba.py
@@ -254,6 +254,15 @@ def SAMBA_LIBRARY(bld, libname, source,
     if abi_directory:
         features += ' abi_check'
 
+    if pyembed and bld.env['PYTHON_SO_ABI_FLAG']:
+        # For ABI checking, we don't care about the exact Python version.
+        # Replace the Python ABI tag (e.g. ".cpython-35m") by a generic ".py3"
+        abi_flag = bld.env['PYTHON_SO_ABI_FLAG']
+        replacement = '.py%s' % bld.env['PYTHON_VERSION'].split('.')[0]
+        version_libname = libname.replace(abi_flag, replacement)
+    else:
+        version_libname = libname
+
     vscript = None
     if bld.env.HAVE_LD_VERSION_SCRIPT:
         if private_library:
@@ -264,7 +273,7 @@ def SAMBA_LIBRARY(bld, libname, source,
             version = None
         if version:
             vscript = "%s.vscript" % libname
-            bld.ABI_VSCRIPT(libname, abi_directory, version, vscript,
+            bld.ABI_VSCRIPT(version_libname, abi_directory, version, vscript,
                             abi_match)
             fullname = apply_pattern(bundled_name, bld.env.shlib_PATTERN)
             fullpath = bld.path.find_or_declare(fullname)
@@ -290,6 +299,7 @@ def SAMBA_LIBRARY(bld, libname, source,
         samba_deps      = deps,
         samba_includes  = includes,
         version_script  = vscript,
+        version_libname = version_libname,
         local_include   = local_include,
         global_include  = global_include,
         vnum            = vnum,
-- 
2.1.0


From 62a8c60c048bd26ddcd28ecff0577c278fca9153 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori at redhat.com>
Date: Mon, 8 Jun 2015 14:17:12 +0200
Subject: [PATCH 02/10] ldb: Run the Python testsuite

Signed-off-by: Petr Viktorin <pviktori at redhat.com>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 buildtools/wafsamba/samba_utils.py |  5 ++++-
 lib/ldb/tests/test-tdb.sh          |  7 -------
 lib/ldb/wscript                    | 11 +++++++++--
 3 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/buildtools/wafsamba/samba_utils.py b/buildtools/wafsamba/samba_utils.py
index 540fe44..0ae58b3 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, extra_env=None):
     env = LOAD_ENVIRONMENT()
     if pythonpath is None:
         pythonpath = os.path.join(Utils.g_module.blddir, 'python')
@@ -394,6 +394,9 @@ 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 extra_env:
+                for key, value in extra_env.items():
+                    cmd = "%s=%s %s" % (key, value, cmd)
             print('Running Python test with %s: %s' % (interp, testfile))
             ret = RUN_COMMAND(cmd)
             if ret:
diff --git a/lib/ldb/tests/test-tdb.sh b/lib/ldb/tests/test-tdb.sh
index 82eef69..91c48ab 100755
--- a/lib/ldb/tests/test-tdb.sh
+++ b/lib/ldb/tests/test-tdb.sh
@@ -7,9 +7,7 @@ if [ -n "$TEST_DATA_PREFIX" ]; then
 	PYDESTDIR="$TEST_DATA_PREFIX"
 else
 	LDB_URL="tdbtest.ldb"
-	PYDESTDIR="/tmp"
 fi
-mkdir $PYDESTDIR/tmp
 export LDB_URL
 
 PATH=$BINDIR:$PATH
@@ -38,8 +36,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 0e81932..ba1fa17 100755
--- a/lib/ldb/wscript
+++ b/lib/ldb/wscript
@@ -284,8 +284,15 @@ 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)
+
+    tmp_dir = os.path.join(test_prefix, 'tmp')
+    if not os.path.exists(tmp_dir):
+        os.mkdir(tmp_dir)
+    pyret = samba_utils.RUN_PYTHON_TESTS(
+        ['tests/python/api.py'],
+        extra_env={'SELFTEST_PREFIX': test_prefix})
+    print("Python testsuite returned %d" % pyret)
+    sys.exit(ret or pyret)
 
 def dist():
     '''makes a tarball for distribution'''
-- 
2.1.0


From 9dc48d12821e1b5eb06bb873ee5835f3881943d1 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori at redhat.com>
Date: Thu, 11 Jun 2015 10:16:48 +0200
Subject: [PATCH 03/10] pyldb: Properly increase refcount of returned values

Signed-off-by: Petr Viktorin <pviktori at redhat.com>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 lib/ldb/pyldb.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lib/ldb/pyldb.c b/lib/ldb/pyldb.c
index f18e06e..5567501 100644
--- a/lib/ldb/pyldb.c
+++ b/lib/ldb/pyldb.c
@@ -1800,7 +1800,7 @@ static PyObject *py_ldb_get_opaque(PyLdbObject *self, PyObject *args)
 
 	/* FIXME: More interpretation */
 
-	return Py_True;
+	Py_RETURN_TRUE;
 }
 
 static PyObject *py_ldb_set_opaque(PyLdbObject *self, PyObject *args)
@@ -2761,6 +2761,7 @@ static PyObject *py_ldb_msg_get(PyLdbMessageObject *self, PyObject *args, PyObje
 
 	if (el == NULL || (idx != -1 && el->num_values <= idx)) {
 		if (def != NULL) {
+			Py_INCREF(def);
 			return def;
 		}
 		Py_RETURN_NONE;
-- 
2.1.0


From 545b5cbe7680f263a68a8447ef0b1edbc6da6ea3 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori at redhat.com>
Date: Wed, 10 Jun 2015 15:41:57 +0200
Subject: [PATCH 04/10] pyldb: Don't use the internal macro PyObject_REPR

Signed-off-by: Petr Viktorin <pviktori at redhat.com>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 lib/ldb/pyldb.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/lib/ldb/pyldb.c b/lib/ldb/pyldb.c
index 5567501..50a3990 100644
--- a/lib/ldb/pyldb.c
+++ b/lib/ldb/pyldb.c
@@ -3004,10 +3004,16 @@ 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;
 	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 = PyString_FromFormat("Message(%s)", PyString_AsString(repr));
+	Py_DECREF(repr);
 	Py_DECREF(dict);
 	return ret;
 }
-- 
2.1.0


From a5f0cb6d8a023c726907a5c2bcda9673343b08da Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori at redhat.com>
Date: Wed, 10 Jun 2015 15:40:34 +0200
Subject: [PATCH 05/10] pyldb: DECREF old debug function when resetting it

Signed-off-by: Petr Viktorin <pviktori at redhat.com>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 lib/ldb/pyldb.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/lib/ldb/pyldb.c b/lib/ldb/pyldb.c
index 50a3990..f079d7d 100644
--- a/lib/ldb/pyldb.c
+++ b/lib/ldb/pyldb.c
@@ -839,6 +839,8 @@ static void py_ldb_debug(void *context, enum ldb_debug_level level, const char *
 	PyObject_CallFunction(fn, discard_const_p(char, "(i,O)"), level, PyString_FromFormatV(fmt, ap));
 }
 
+static PyObject *py_ldb_debug_func;
+
 static PyObject *py_ldb_set_debug(PyObject *self, PyObject *args)
 {
 	PyObject *cb;
@@ -847,8 +849,13 @@ static PyObject *py_ldb_set_debug(PyObject *self, PyObject *args)
 	if (!PyArg_ParseTuple(args, "O", &cb))
 		return NULL;
 
+	if (py_ldb_debug_func != NULL) {
+		Py_DECREF(py_ldb_debug_func);
+	}
+
 	Py_INCREF(cb);
-	/* FIXME: Where do we DECREF cb ? */
+	/* FIXME: DECREF cb when exiting program */
+	py_ldb_debug_func = cb;
 	ldb_ctx = pyldb_Ldb_AsLdbContext(self);
 	PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError,
 		ldb_set_debug(ldb_ctx, py_ldb_debug, cb),
-- 
2.1.0


From 49596c1df90fe1a6ebf76f006a88e73683ea02f4 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori at redhat.com>
Date: Tue, 9 Jun 2015 10:36:26 +0200
Subject: [PATCH 06/10] pyldb: Add Python 3 compatibility (except strings)

- Use a macro for adding constants to module
(This also ensures that the Python constants have the same
name as the C ones. One existing misspelling is retained.)

- Use new module initialization for Python 3

- Use rich comparison for ldb objects

- Prepare tests for Python 3

Signed-off-by: Petr Viktorin <pviktori at redhat.com>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 lib/ldb/pyldb.c             | 265 +++++++++++++++++++++++++++-----------------
 lib/ldb/tests/python/api.py |  17 +--
 2 files changed, 176 insertions(+), 106 deletions(-)

diff --git a/lib/ldb/pyldb.c b/lib/ldb/pyldb.c
index f079d7d..e279f97 100644
--- a/lib/ldb/pyldb.c
+++ b/lib/ldb/pyldb.c
@@ -64,10 +64,27 @@ typedef inquiry lenfunc;
 typedef intargfunc ssizeargfunc;
 #endif
 
-#define SIGN(a) (((a) == 0)?0:((a) < 0?-1:1))
 
 
 
+static PyObject *richcmp(int cmp_val, int op)
+{
+	int ret;
+	switch (op) {
+		case Py_LT: ret = cmp_val < 0;  break;
+		case Py_LE: ret = cmp_val <= 0; break;
+		case Py_EQ: ret = cmp_val == 0; break;
+		case Py_NE: ret = cmp_val != 0; break;
+		case Py_GT: ret = cmp_val > 0;  break;
+		case Py_GE: ret = cmp_val >= 0; break;
+		default:
+			Py_INCREF(Py_NotImplemented);
+			return Py_NotImplemented;
+	}
+	return PyBool_FromLong(ret);
+}
+
+
 static PyObject *py_ldb_control_str(PyLdbControlObject *self)
 {
 	if (self->data != NULL) {
@@ -464,13 +481,15 @@ 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_richcmp(PyObject *dn1, PyObject *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(dn2)) {
+		Py_INCREF(Py_NotImplemented);
+		return Py_NotImplemented;
+	}
+	ret = ldb_dn_compare(pyldb_Dn_AsDn(dn1), pyldb_Dn_AsDn(dn2));
+	return richcmp(ret, op);
 }
 
 static PyObject *py_ldb_dn_get_parent(PyLdbDnObject *self)
@@ -822,7 +841,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_richcmp,
 	.tp_as_sequence = &py_ldb_dn_seq,
 	.tp_doc = "A LDB distinguished name.",
 	.tp_new = py_ldb_dn_new,
@@ -2468,11 +2487,16 @@ 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(PyObject *self, PyObject *other, int op)
 {
-	int ret = ldb_msg_element_compare(pyldb_MessageElement_AsMessageElement(self),
+	int ret;
+	if (!pyldb_MessageElement_Check(other)) {
+		Py_INCREF(Py_NotImplemented);
+		return Py_NotImplemented;
+	}
+	ret = ldb_msg_element_compare(pyldb_MessageElement_AsMessageElement(self),
 									  pyldb_MessageElement_AsMessageElement(other));
-	return SIGN(ret);
+	return richcmp(ret, op);
 }
 
 static PyObject *py_ldb_msg_element_iter(PyLdbMessageElementObject *self)
@@ -2638,7 +2662,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,
@@ -3031,41 +3055,48 @@ 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_richcmp(PyLdbMessageObject *py_msg1,
+			      PyLdbMessageObject *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)) {
+		Py_INCREF(Py_NotImplemented);
+		return Py_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 richcmp(ret, op);
 		}
 	}
 
 	ret = msg1->num_elements - msg2->num_elements;
 	if (ret != 0) {
-		return SIGN(ret);
+		return richcmp(ret, 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 richcmp(ret, op);
 		}
 
 		ret = ldb_msg_element_compare(&msg1->elements[i],
 					      &msg2->elements[i]);
 		if (ret != 0) {
-			return SIGN(ret);
+			return richcmp(ret, op);
 		}
 	}
 
-	return 0;
+	return richcmp(0, op);
 }
 
 static PyTypeObject PyLdbMessage = {
@@ -3079,7 +3110,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_richcmp,
 	.tp_doc = "A LDB Message",
 };
 
@@ -3520,102 +3551,122 @@ static PyMethodDef py_ldb_global_methods[] = {
 	{ NULL }
 };
 
-void initldb(void)
+#define MODULE_DOC "An interface to LDB, a LDAP-like API that can either to talk an embedded database (TDB-based) or a standards-compliant LDAP server."
+
+#if PY_MAJOR_VERSION >= 3
+static struct PyModuleDef moduledef = {
+	PyModuleDef_HEAD_INIT,
+	.m_name = "ldb",
+	.m_doc = MODULE_DOC,
+	.m_size = -1,
+	.m_methods = py_ldb_global_methods,
+};
+#endif
+
+static PyObject* module_init(void)
 {
 	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.");
+#if PY_MAJOR_VERSION >= 3
+	m = PyModule_Create(&moduledef);
+#else
+	m = Py_InitModule3("ldb", py_ldb_global_methods, MODULE_DOC);
+#endif
 	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);
+	ADD_LDB_INT(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);
+
+	/* Historical misspelling */
+	PyModule_AddIntConstant(m, "ERR_ALIAS_DEREFERINCING_PROBLEM", LDB_ERR_ALIAS_DEREFERENCING_PROBLEM);
+
+	PyModule_AddStringConstant(m, "__docformat__", "restructuredText");
 
 	PyExc_LdbError = PyErr_NewException(discard_const_p(char, "_ldb.LdbError"), NULL, NULL);
 	PyModule_AddObject(m, "LdbError", PyExc_LdbError);
@@ -3637,9 +3688,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);
@@ -3649,4 +3700,20 @@ void initldb(void)
 	ADD_LDB_STRING(SYNTAX_UTC_TIME);
 	ADD_LDB_STRING(OID_COMPARATOR_AND);
 	ADD_LDB_STRING(OID_COMPARATOR_OR);
+
+	return m;
 }
+
+#if PY_MAJOR_VERSION >= 3
+PyMODINIT_FUNC PyInit_ldb(void);
+PyMODINIT_FUNC PyInit_ldb(void)
+{
+	return module_init();
+}
+#else
+void initldb(void);
+void initldb(void)
+{
+	module_init();
+}
+#endif
diff --git a/lib/ldb/tests/python/api.py b/lib/ldb/tests/python/api.py
index d101de8..408c364 100755
--- a/lib/ldb/tests/python/api.py
+++ b/lib/ldb/tests/python/api.py
@@ -54,7 +54,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()
@@ -423,7 +423,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 +431,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):
@@ -475,7 +475,10 @@ 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.assertIn(repr(self.msg), [
+            "Message({'dn': Dn('dc=foo29'), 'dc': MessageElement(['foo'])})",
+            "Message({'dc': MessageElement(['foo']), 'dn': Dn('dc=foo29')})",
+        ])
 
     def test_len(self):
         self.assertEquals(0, len(self.msg))
@@ -549,8 +552,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"])
-- 
2.1.0


From e4b4a392fc87a654429ec3256e813eeb526cd991 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori at redhat.com>
Date: Wed, 10 Jun 2015 10:21:24 +0200
Subject: [PATCH 07/10] pyldb: Modernize test suite

This gets rid of deprecation warnings for the old method names.

Signed-off-by: Petr Viktorin <pviktori at redhat.com>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 lib/ldb/tests/python/api.py | 232 ++++++++++++++++++++++----------------------
 1 file changed, 116 insertions(+), 116 deletions(-)

diff --git a/lib/ldb/tests/python/api.py b/lib/ldb/tests/python/api.py
index 408c364..ac9dd61 100755
--- a/lib/ldb/tests/python/api.py
+++ b/lib/ldb/tests/python/api.py
@@ -24,17 +24,17 @@ class NoContextTests(TestCase):
         self.assertFalse(ldb.valid_attr_name("24foo"))
 
     def test_timestring(self):
-        self.assertEquals("19700101000000.0Z", ldb.timestring(0))
-        self.assertEquals("20071119191012.0Z", ldb.timestring(1195499412))
+        self.assertEqual("19700101000000.0Z", ldb.timestring(0))
+        self.assertEqual("20071119191012.0Z", ldb.timestring(1195499412))
 
     def test_string_to_time(self):
-        self.assertEquals(0, ldb.string_to_time("19700101000000.0Z"))
-        self.assertEquals(1195499412, ldb.string_to_time("20071119191012.0Z"))
+        self.assertEqual(0, ldb.string_to_time("19700101000000.0Z"))
+        self.assertEqual(1195499412, ldb.string_to_time("20071119191012.0Z"))
 
     def test_binary_encode(self):
         encoded = ldb.binary_encode('test\\x')
         decoded = ldb.binary_decode(encoded)
-        self.assertEquals(decoded, 'test\\x')
+        self.assertEqual(decoded, 'test\\x')
 
 class SimpleLdb(TestCase):
 
@@ -58,27 +58,27 @@ class SimpleLdb(TestCase):
 
     def test_modules_none(self):
         x = ldb.Ldb()
-        self.assertEquals([], x.modules())
+        self.assertEqual([], x.modules())
 
     def test_modules_tdb(self):
         x = ldb.Ldb(filename())
-        self.assertEquals("[<ldb module 'tdb'>]", repr(x.modules()))
+        self.assertEqual("[<ldb module 'tdb'>]", repr(x.modules()))
 
     def test_search(self):
         l = ldb.Ldb(filename())
-        self.assertEquals(len(l.search()), 0)
+        self.assertEqual(len(l.search()), 0)
 
     def test_search_controls(self):
         l = ldb.Ldb(filename())
-        self.assertEquals(len(l.search(controls=["paged_results:0:5"])), 0)
+        self.assertEqual(len(l.search(controls=["paged_results:0:5"])), 0)
 
     def test_search_attrs(self):
         l = ldb.Ldb(filename())
-        self.assertEquals(len(l.search(ldb.Dn(l, ""), ldb.SCOPE_SUBTREE, "(dc=*)", ["dc"])), 0)
+        self.assertEqual(len(l.search(ldb.Dn(l, ""), ldb.SCOPE_SUBTREE, "(dc=*)", ["dc"])), 0)
 
     def test_search_string_dn(self):
         l = ldb.Ldb(filename())
-        self.assertEquals(len(l.search("", ldb.SCOPE_SUBTREE, "(dc=*)", ["dc"])), 0)
+        self.assertEqual(len(l.search("", ldb.SCOPE_SUBTREE, "(dc=*)", ["dc"])), 0)
 
     def test_search_attr_string(self):
         l = ldb.Ldb(filename())
@@ -88,11 +88,11 @@ class SimpleLdb(TestCase):
         l = ldb.Ldb(filename())
         l.set_opaque("my_opaque", l)
         self.assertTrue(l.get_opaque("my_opaque") is not None)
-        self.assertEquals(None, l.get_opaque("unknown"))
+        self.assertEqual(None, l.get_opaque("unknown"))
 
     def test_search_scope_base(self):
         l = ldb.Ldb(filename())
-        self.assertEquals(len(l.search(ldb.Dn(l, "dc=foo1"), 
+        self.assertEqual(len(l.search(ldb.Dn(l, "dc=foo1"),
                           ldb.SCOPE_ONELEVEL)), 0)
 
     def test_delete(self):
@@ -124,29 +124,29 @@ class SimpleLdb(TestCase):
 
     def test_get_config_basedn(self):
         l = ldb.Ldb(filename())
-        self.assertEquals(None, l.get_config_basedn())
+        self.assertEqual(None, l.get_config_basedn())
 
     def test_get_root_basedn(self):
         l = ldb.Ldb(filename())
-        self.assertEquals(None, l.get_root_basedn())
+        self.assertEqual(None, l.get_root_basedn())
 
     def test_get_schema_basedn(self):
         l = ldb.Ldb(filename())
-        self.assertEquals(None, l.get_schema_basedn())
+        self.assertEqual(None, l.get_schema_basedn())
 
     def test_get_default_basedn(self):
         l = ldb.Ldb(filename())
-        self.assertEquals(None, l.get_default_basedn())
+        self.assertEqual(None, l.get_default_basedn())
 
     def test_add(self):
         l = ldb.Ldb(filename())
         m = ldb.Message()
         m.dn = ldb.Dn(l, "dc=foo4")
         m["bla"] = "bla"
-        self.assertEquals(len(l.search()), 0)
+        self.assertEqual(len(l.search()), 0)
         l.add(m)
         try:
-            self.assertEquals(len(l.search()), 1)
+            self.assertEqual(len(l.search()), 1)
         finally:
             l.delete(ldb.Dn(l, "dc=foo4"))
 
@@ -155,27 +155,27 @@ class SimpleLdb(TestCase):
         m = ldb.Message()
         m.dn = ldb.Dn(l, "dc=foo4")
         m["bla"] = "bla"
-        self.assertEquals(len(l.search()), 0)
+        self.assertEqual(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"}
-        self.assertEquals(len(l.search()), 0)
+        self.assertEqual(len(l.search()), 0)
         l.add(m)
         try:
-            self.assertEquals(len(l.search()), 1)
+            self.assertEqual(len(l.search()), 1)
         finally:
             l.delete(ldb.Dn(l, "dc=foo5"))
 
     def test_add_dict_string_dn(self):
         l = ldb.Ldb(filename())
         m = {"dn": "dc=foo6", "bla": "bla"}
-        self.assertEquals(len(l.search()), 0)
+        self.assertEqual(len(l.search()), 0)
         l.add(m)
         try:
-            self.assertEquals(len(l.search()), 1)
+            self.assertEqual(len(l.search()), 1)
         finally:
             l.delete(ldb.Dn(l, "dc=foo6"))
 
@@ -184,11 +184,11 @@ class SimpleLdb(TestCase):
         m = ldb.Message()
         m.dn = ldb.Dn(l, "dc=foo7")
         m["bla"] = "bla"
-        self.assertEquals(len(l.search()), 0)
+        self.assertEqual(len(l.search()), 0)
         l.add(m)
         try:
             l.rename(ldb.Dn(l, "dc=foo7"), ldb.Dn(l, "dc=bar"))
-            self.assertEquals(len(l.search()), 1)
+            self.assertEqual(len(l.search()), 1)
         finally:
             l.delete(ldb.Dn(l, "dc=bar"))
 
@@ -197,12 +197,12 @@ class SimpleLdb(TestCase):
         m = ldb.Message()
         m.dn = ldb.Dn(l, "dc=foo8")
         m["bla"] = "bla"
-        self.assertEquals(len(l.search()), 0)
+        self.assertEqual(len(l.search()), 0)
         l.add(m)
-        self.assertEquals(len(l.search()), 1)
+        self.assertEqual(len(l.search()), 1)
         try:
             l.rename("dc=foo8", "dc=bar")
-            self.assertEquals(len(l.search()), 1)
+            self.assertEqual(len(l.search()), 1)
         finally:
             l.delete(ldb.Dn(l, "dc=bar"))
 
@@ -213,17 +213,17 @@ class SimpleLdb(TestCase):
         m["bla"] = ["1234"]
         l.add(m)
         rm = l.search(m.dn)[0]
-        self.assertEquals(["1234"], list(rm["bla"]))
+        self.assertEqual(["1234"], list(rm["bla"]))
         try:
             m = ldb.Message()
             m.dn = ldb.Dn(l, "dc=modifydelete")
             m["bla"] = ldb.MessageElement([], ldb.FLAG_MOD_DELETE, "bla")
-            self.assertEquals(ldb.FLAG_MOD_DELETE, m["bla"].flags())
+            self.assertEqual(ldb.FLAG_MOD_DELETE, m["bla"].flags())
             l.modify(m)
             rm = l.search(m.dn)[0]
-            self.assertEquals(1, len(rm))
+            self.assertEqual(1, len(rm))
             rm = l.search(m.dn, attrs=["bla"])
-            self.assertEquals(0, len(rm))
+            self.assertEqual(0, len(rm))
         finally:
             l.delete(ldb.Dn(l, "dc=modifydelete"))
 
@@ -237,11 +237,11 @@ class SimpleLdb(TestCase):
             m = ldb.Message()
             m.dn = ldb.Dn(l, "dc=add")
             m["bla"] = ldb.MessageElement(["456"], ldb.FLAG_MOD_ADD, "bla")
-            self.assertEquals(ldb.FLAG_MOD_ADD, m["bla"].flags())
+            self.assertEqual(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.assertEqual(2, len(rm))
+            self.assertEqual(["1234", "456"], list(rm["bla"]))
         finally:
             l.delete(ldb.Dn(l, "dc=add"))
 
@@ -255,13 +255,13 @@ class SimpleLdb(TestCase):
             m = ldb.Message()
             m.dn = ldb.Dn(l, "dc=modify2")
             m["bla"] = ldb.MessageElement(["789"], ldb.FLAG_MOD_REPLACE, "bla")
-            self.assertEquals(ldb.FLAG_MOD_REPLACE, m["bla"].flags())
+            self.assertEqual(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.assertEqual(2, len(rm))
+            self.assertEqual(["789"], list(rm["bla"]))
             rm = l.search(m.dn, attrs=["bla"])[0]
-            self.assertEquals(1, len(rm))
+            self.assertEqual(1, len(rm))
         finally:
             l.delete(ldb.Dn(l, "dc=modify2"))
 
@@ -275,19 +275,19 @@ class SimpleLdb(TestCase):
             m = ldb.Message()
             m.dn = ldb.Dn(l, "dc=add")
             m["bla"] = ldb.MessageElement(["456"], ldb.FLAG_MOD_ADD, "bla")
-            self.assertEquals(ldb.FLAG_MOD_ADD, m["bla"].flags())
+            self.assertEqual(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.assertEqual(2, len(rm))
+            self.assertEqual(["1234", "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"].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.assertEqual(1, len(rm))
+            self.assertEqual(["1234"], list(rm["bla"]))
         finally:
             l.delete(ldb.Dn(l, "dc=add"))
 
@@ -307,7 +307,7 @@ class SimpleLdb(TestCase):
         m["foo"] = ["bar"]
         l.add(m)
         l.transaction_cancel()
-        self.assertEquals(0, len(l.search(ldb.Dn(l, "dc=foo10"))))
+        self.assertEqual(0, len(l.search(ldb.Dn(l, "dc=foo10"))))
 
     def test_set_debug(self):
         def my_report_fn(level, text):
@@ -326,7 +326,7 @@ class SimpleLdb(TestCase):
             "displayname" : "foo\0bar",
         })
         res = l.search(expression="(dn=dc=somedn)")
-        self.assertEquals("foo\0bar", res[0]["displayname"][0])
+        self.assertEqual("foo\0bar", res[0]["displayname"][0])
 
     def test_no_crash_broken_expr(self):
         l = ldb.Ldb(filename())
@@ -348,21 +348,21 @@ class DnTests(TestCase):
     def test_eq(self):
         x = ldb.Dn(self.ldb, "dc=foo11,bar=bloe")
         y = ldb.Dn(self.ldb, "dc=foo11,bar=bloe")
-        self.assertEquals(x, y)
+        self.assertEqual(x, y)
         y = ldb.Dn(self.ldb, "dc=foo11,bar=blie")
-        self.assertNotEquals(x, y)
+        self.assertNotEqual(x, y)
 
     def test_str(self):
         x = ldb.Dn(self.ldb, "dc=foo12,bar=bloe")
-        self.assertEquals(x.__str__(), "dc=foo12,bar=bloe")
+        self.assertEqual(x.__str__(), "dc=foo12,bar=bloe")
 
     def test_repr(self):
         x = ldb.Dn(self.ldb, "dc=foo13,bla=blie")
-        self.assertEquals(x.__repr__(), "Dn('dc=foo13,bla=blie')")
+        self.assertEqual(x.__repr__(), "Dn('dc=foo13,bla=blie')")
 
     def test_get_casefold(self):
         x = ldb.Dn(self.ldb, "dc=foo14,bar=bloe")
-        self.assertEquals(x.get_casefold(), "DC=FOO14,BAR=bloe")
+        self.assertEqual(x.get_casefold(), "DC=FOO14,BAR=bloe")
 
     def test_validate(self):
         x = ldb.Dn(self.ldb, "dc=foo15,bar=bloe")
@@ -370,11 +370,11 @@ class DnTests(TestCase):
 
     def test_parent(self):
         x = ldb.Dn(self.ldb, "dc=foo16,bar=bloe")
-        self.assertEquals("bar=bloe", x.parent().__str__())
+        self.assertEqual("bar=bloe", x.parent().__str__())
 
     def test_parent_nonexistent(self):
         x = ldb.Dn(self.ldb, "@BLA")
-        self.assertEquals(None, x.parent())
+        self.assertEqual(None, x.parent())
 
     def test_is_valid(self):
         x = ldb.Dn(self.ldb, "dc=foo18,dc=bloe")
@@ -396,53 +396,53 @@ class DnTests(TestCase):
 
     def test_len(self):
         x = ldb.Dn(self.ldb, "dc=foo21,bar=bloe")
-        self.assertEquals(2, len(x))
+        self.assertEqual(2, len(x))
         x = ldb.Dn(self.ldb, "dc=foo21")
-        self.assertEquals(1, len(x))
+        self.assertEqual(1, len(x))
 
     def test_add_child(self):
         x = ldb.Dn(self.ldb, "dc=foo22,bar=bloe")
         self.assertTrue(x.add_child(ldb.Dn(self.ldb, "bla=bloe")))
-        self.assertEquals("bla=bloe,dc=foo22,bar=bloe", x.__str__())
+        self.assertEqual("bla=bloe,dc=foo22,bar=bloe", x.__str__())
 
     def test_add_base(self):
         x = ldb.Dn(self.ldb, "dc=foo23,bar=bloe")
         base = ldb.Dn(self.ldb, "bla=bloe")
         self.assertTrue(x.add_base(base))
-        self.assertEquals("dc=foo23,bar=bloe,bla=bloe", x.__str__())
+        self.assertEqual("dc=foo23,bar=bloe,bla=bloe", x.__str__())
 
     def test_add(self):
         x = ldb.Dn(self.ldb, "dc=foo24")
         y = ldb.Dn(self.ldb, "bar=bla")
-        self.assertEquals("dc=foo24,bar=bla", str(x + y))
+        self.assertEqual("dc=foo24,bar=bla", str(x + y))
 
     def test_remove_base_components(self):
         x = ldb.Dn(self.ldb, "dc=foo24,dc=samba,dc=org")
         x.remove_base_components(len(x)-1)
-        self.assertEquals("dc=foo24", str(x))
+        self.assertEqual("dc=foo24", str(x))
 
     def test_parse_ldif(self):
         msgs = self.ldb.parse_ldif("dn: foo=bar\n")
         msg = next(msgs)
-        self.assertEquals("foo=bar", str(msg[1].dn))
+        self.assertEqual("foo=bar", str(msg[1].dn))
         self.assertTrue(isinstance(msg[1], ldb.Message))
         ldif = self.ldb.write_ldif(msg[1], ldb.CHANGETYPE_NONE)
-        self.assertEquals("dn: foo=bar\n\n", ldif)
+        self.assertEqual("dn: foo=bar\n\n", ldif)
 
     def test_parse_ldif_more(self):
         msgs = self.ldb.parse_ldif("dn: foo=bar\n\n\ndn: bar=bar")
         msg = next(msgs)
-        self.assertEquals("foo=bar", str(msg[1].dn))
+        self.assertEqual("foo=bar", str(msg[1].dn))
         msg = next(msgs)
-        self.assertEquals("bar=bar", str(msg[1].dn))
+        self.assertEqual("bar=bar", str(msg[1].dn))
 
     def test_canonical_string(self):
         x = ldb.Dn(self.ldb, "dc=foo25,bar=bloe")
-        self.assertEquals("/bloe/foo25", x.canonical_str())
+        self.assertEqual("/bloe/foo25", x.canonical_str())
 
     def test_canonical_ex_string(self):
         x = ldb.Dn(self.ldb, "dc=foo26,bar=bloe")
-        self.assertEquals("/bloe\nfoo26", x.canonical_ex_str())
+        self.assertEqual("/bloe\nfoo26", x.canonical_ex_str())
 
     def test_ldb_is_child_of(self):
         """Testing ldb_dn_compare_dn"""
@@ -465,12 +465,12 @@ class LdbMsgTests(TestCase):
 
     def test_init_dn(self):
         self.msg = ldb.Message(ldb.Dn(ldb.Ldb(), "dc=foo27"))
-        self.assertEquals("dc=foo27", str(self.msg.dn))
+        self.assertEqual("dc=foo27", str(self.msg.dn))
 
     def test_iter_items(self):
-        self.assertEquals(0, len(self.msg.items()))
+        self.assertEqual(0, len(self.msg.items()))
         self.msg.dn = ldb.Dn(ldb.Ldb(filename()), "dc=foo28")
-        self.assertEquals(1, len(self.msg.items()))
+        self.assertEqual(1, len(self.msg.items()))
 
     def test_repr(self):
         self.msg.dn = ldb.Dn(ldb.Ldb(filename()), "dc=foo29")
@@ -481,7 +481,7 @@ class LdbMsgTests(TestCase):
         ])
 
     def test_len(self):
-        self.assertEquals(0, len(self.msg))
+        self.assertEqual(0, len(self.msg))
 
     def test_notpresent(self):
         self.assertRaises(KeyError, lambda: self.msg["foo"])
@@ -493,43 +493,43 @@ class LdbMsgTests(TestCase):
         self.msg.add(ldb.MessageElement(["456"], ldb.FLAG_MOD_ADD, "bla"))
 
     def test_elements_empty(self):
-        self.assertEquals([], self.msg.elements())
+        self.assertEqual([], self.msg.elements())
 
     def test_elements(self):
         el = ldb.MessageElement(["456"], ldb.FLAG_MOD_ADD, "bla")
         self.msg.add(el)
-        self.assertEquals([el], self.msg.elements())
+        self.assertEqual([el], self.msg.elements())
 
     def test_add_value(self):
-        self.assertEquals(0, len(self.msg))
+        self.assertEqual(0, len(self.msg))
         self.msg["foo"] = ["foo"]
-        self.assertEquals(1, len(self.msg))
+        self.assertEqual(1, len(self.msg))
 
     def test_add_value_multiple(self):
-        self.assertEquals(0, len(self.msg))
+        self.assertEqual(0, len(self.msg))
         self.msg["foo"] = ["foo", "bla"]
-        self.assertEquals(1, len(self.msg))
-        self.assertEquals(["foo", "bla"], list(self.msg["foo"]))
+        self.assertEqual(1, len(self.msg))
+        self.assertEqual(["foo", "bla"], list(self.msg["foo"]))
 
     def test_set_value(self):
         self.msg["foo"] = ["fool"]
-        self.assertEquals(["fool"], list(self.msg["foo"]))
+        self.assertEqual(["fool"], list(self.msg["foo"]))
         self.msg["foo"] = ["bar"]
-        self.assertEquals(["bar"], list(self.msg["foo"]))
+        self.assertEqual(["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.assertEquals(["dn", "foo", "bar"], self.msg.keys())
+        self.assertEqual(["dn", "foo", "bar"], self.msg.keys())
 
     def test_dn(self):
         self.msg.dn = ldb.Dn(ldb.Ldb(filename()), "@BASEINFO")
-        self.assertEquals("@BASEINFO", self.msg.dn.__str__())
+        self.assertEqual("@BASEINFO", self.msg.dn.__str__())
 
     def test_get_dn(self):
         self.msg.dn = ldb.Dn(ldb.Ldb(filename()), "@BASEINFO")
-        self.assertEquals("@BASEINFO", self.msg.get("dn").__str__())
+        self.assertEqual("@BASEINFO", self.msg.get("dn").__str__())
 
     def test_get_invalid(self):
         self.msg.dn = ldb.Dn(ldb.Ldb(filename()), "@BASEINFO")
@@ -537,17 +537,17 @@ class LdbMsgTests(TestCase):
 
     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.assertEquals(None, self.msg.get("foo", idx=1))
-        self.assertEquals("", self.msg.get("foo", default='', idx=1))
+        self.assertEqual("bar", self.msg.get("foo")[0])
+        self.assertEqual("bar", self.msg.get("foo", idx=0))
+        self.assertEqual(None, self.msg.get("foo", idx=1))
+        self.assertEqual("", self.msg.get("foo", default='', idx=1))
 
     def test_get_default(self):
-        self.assertEquals(None, self.msg.get("tatayoyo", idx=0))
-        self.assertEquals("anniecordie", self.msg.get("tatayoyo", "anniecordie"))
+        self.assertEqual(None, self.msg.get("tatayoyo", idx=0))
+        self.assertEqual("anniecordie", self.msg.get("tatayoyo", "anniecordie"))
 
     def test_get_unknown(self):
-        self.assertEquals(None, self.msg.get("lalalala"))
+        self.assertEqual(None, self.msg.get("lalalala"))
 
     def test_msg_diff(self):
         l = ldb.Ldb()
@@ -555,14 +555,14 @@ class LdbMsgTests(TestCase):
         msg1 = next(msgs)[1]
         msg2 = next(msgs)[1]
         msgdiff = l.msg_diff(msg1, msg2)
-        self.assertEquals("foo=bar", msgdiff.get("dn").__str__())
+        self.assertEqual("foo=bar", msgdiff.get("dn").__str__())
         self.assertRaises(KeyError, lambda: msgdiff["foo"])
-        self.assertEquals(1, len(msgdiff))
+        self.assertEqual(1, len(msgdiff))
 
     def test_equal_empty(self):
         msg1 = ldb.Message()
         msg2 = ldb.Message()
-        self.assertEquals(msg1, msg2)
+        self.assertEqual(msg1, msg2)
 
     def test_equal_simplel(self):
         db = ldb.Ldb(filename())
@@ -570,12 +570,12 @@ class LdbMsgTests(TestCase):
         msg1.dn = ldb.Dn(db, "foo=bar")
         msg2 = ldb.Message()
         msg2.dn = ldb.Dn(db, "foo=bar")
-        self.assertEquals(msg1, msg2)
+        self.assertEqual(msg1, msg2)
         msg1['foo'] = 'bar'
         msg2['foo'] = 'bar'
-        self.assertEquals(msg1, msg2)
+        self.assertEqual(msg1, msg2)
         msg2['foo'] = 'blie'
-        self.assertNotEquals(msg1, msg2)
+        self.assertNotEqual(msg1, msg2)
         msg2['foo'] = 'blie'
 
     def test_from_dict(self):
@@ -585,8 +585,8 @@ class LdbMsgTests(TestCase):
         # check different types of input Flags
         for flags in [ldb.FLAG_MOD_ADD, ldb.FLAG_MOD_REPLACE, ldb.FLAG_MOD_DELETE]:
             m = ldb.Message.from_dict(l, rec, flags)
-            self.assertEquals(rec["a1"], list(m["a1"]))
-            self.assertEquals(flags, m["a1"].flags())
+            self.assertEqual(rec["a1"], list(m["a1"]))
+            self.assertEqual(flags, m["a1"].flags())
         # check input params
         self.assertRaises(TypeError, ldb.Message.from_dict, dict(), rec, ldb.FLAG_MOD_REPLACE)
         self.assertRaises(TypeError, ldb.Message.from_dict, l, list(), ldb.FLAG_MOD_REPLACE)
@@ -617,43 +617,43 @@ class MessageElementTests(TestCase):
         x = ldb.MessageElement(["foo"])
         y = ldb.MessageElement(["foo"])
         z = ldb.MessageElement(["bzr"])
-        self.assertEquals(x, y)
-        self.assertNotEquals(x, z)
+        self.assertEqual(x, y)
+        self.assertNotEqual(x, z)
 
     def test_create_iterable(self):
         x = ldb.MessageElement(["foo"])
-        self.assertEquals(["foo"], list(x))
+        self.assertEqual(["foo"], list(x))
 
     def test_repr(self):
         x = ldb.MessageElement(["foo"])
-        self.assertEquals("MessageElement(['foo'])", repr(x))
+        self.assertEqual("MessageElement(['foo'])", repr(x))
         x = ldb.MessageElement(["foo", "bla"])
-        self.assertEquals(2, len(x))
-        self.assertEquals("MessageElement(['foo','bla'])", repr(x))
+        self.assertEqual(2, len(x))
+        self.assertEqual("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])
+        self.assertEqual("foo", x[0])
+        self.assertEqual("bar", x[1])
+        self.assertEqual("bar", x[-1])
         self.assertRaises(IndexError, lambda: x[45])
 
     def test_len(self):
         x = ldb.MessageElement(["foo", "bar"])
-        self.assertEquals(2, len(x))
+        self.assertEqual(2, len(x))
 
     def test_eq(self):
         x = ldb.MessageElement(["foo", "bar"])
         y = ldb.MessageElement(["foo", "bar"])
-        self.assertEquals(y, x)
+        self.assertEqual(y, x)
         x = ldb.MessageElement(["foo"])
-        self.assertNotEquals(y, x)
+        self.assertNotEqual(y, x)
         y = ldb.MessageElement(["foo"])
-        self.assertEquals(y, x)
+        self.assertEqual(y, x)
 
     def test_extended(self):
         el = ldb.MessageElement(["456"], ldb.FLAG_MOD_ADD, "bla")
-        self.assertEquals("MessageElement(['456'])", repr(el))
+        self.assertEqual("MessageElement(['456'])", repr(el))
 
 
 class ModuleTests(TestCase):
@@ -684,9 +684,9 @@ class ModuleTests(TestCase):
             os.unlink(name)
         l = ldb.Ldb(name)
         l.add({"dn": "@MODULES", "@LIST": "bla"})
-        self.assertEquals([], ops)
+        self.assertEqual([], ops)
         l = ldb.Ldb(name)
-        self.assertEquals(["init"], ops)
+        self.assertEqual(["init"], ops)
 
 class LdbResultTests(TestCase):
 
@@ -718,7 +718,7 @@ class LdbResultTests(TestCase):
 
     def test_return_type(self):
         res = self.l.search()
-        self.assertEquals(str(res), "<ldb result>")
+        self.assertEqual(str(res), "<ldb result>")
 
     def test_get_msgs(self):
         res = self.l.search()
@@ -751,8 +751,8 @@ class LdbResultTests(TestCase):
     def test_create_control(self):
         self.assertRaises(ValueError, ldb.Control, self.l, "tatayoyo:0")
         c = ldb.Control(self.l, "relax:1")
-        self.assertEquals(c.critical, True)
-        self.assertEquals(c.oid, "1.3.6.1.4.1.4203.666.5.12")
+        self.assertEqual(c.critical, True)
+        self.assertEqual(c.oid, "1.3.6.1.4.1.4203.666.5.12")
 
     def test_iter_refs(self):
         res = self.l.search().referals
-- 
2.1.0


From cdabc16daab56231b79ee822e571fc4bb11f9a1c Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori at redhat.com>
Date: Tue, 9 Jun 2015 17:44:40 +0200
Subject: [PATCH 08/10] pyldb: Split text/byte strings for compatibility with
 Python 3

Compatibility with Python 2, and backwards compatibility on Python 2,
is kept.

Under Python 3, DNs, attribute names, filters, controls are always text
(unicode) strings, encoded to/from UTF-8 for storage.
Attribute values are byte strings.

When creating DNs and attribute values, both text and bytes are accepted.
This allows creating messages from homogeneous dicts.

LDB Messages and MessageElements have a .text attribute, which offers
a text view on the contents: any value retrieved from it will be a text
string. The wrapper is implemented in a new Python module.

Thanks to Stefan Metzmacher for const warning fixes

Signed-off-by: Petr Viktorin <pviktori at redhat.com>
---
 lib/ldb/_ldb_text.py | 148 ++++++++++++++++++++++++
 lib/ldb/pyldb.c      | 317 ++++++++++++++++++++++++++++++++++++---------------
 lib/ldb/pyldb_util.c |  21 ++--
 lib/ldb/wscript      |   8 ++
 4 files changed, 396 insertions(+), 98 deletions(-)
 create mode 100644 lib/ldb/_ldb_text.py

diff --git a/lib/ldb/_ldb_text.py b/lib/ldb/_ldb_text.py
new file mode 100644
index 0000000..f6f1ac0
--- /dev/null
+++ b/lib/ldb/_ldb_text.py
@@ -0,0 +1,148 @@
+# Text wrapper for ldb bindings
+#
+# Copyright (C) 2015 Petr Viktorin <pviktori at redhat.com>
+# Published under the GNU LGPLv3 or later
+
+import sys
+import functools
+
+import ldb
+
+
+def _recursive_encode(obj):
+    if isinstance(obj, bytes):
+        return obj
+    elif isinstance(obj, str):
+        return obj.encode('utf-8')
+    else:
+        return [_recursive_encode(o) for o in obj]
+
+
+class _WrapBase(object):
+
+    @classmethod
+    def _wrap(cls, wrapped):
+        self = cls.__new__(cls)
+        self._wrapped = wrapped
+        return self
+
+    def __len__(self):
+        return len(self._wrapped)
+
+    def __eq__(self, other):
+        if hasattr(other, '_wrapped'):
+            return self._wrapped == other._wrapped
+        else:
+            return self._wrapped == other
+
+    def __ne__(self, other):
+        if hasattr(other, '_wrapped'):
+            return self._wrapped != other._wrapped
+        else:
+            return self._wrapped != other
+
+    def __lt__(self, other):
+        if hasattr(other, '_wrapped'):
+            return self._wrapped < other._wrapped
+        else:
+            return self._wrapped < other
+
+    def __le__(self, other):
+        if hasattr(other, '_wrapped'):
+            return self._wrapped >= other._wrapped
+        else:
+            return self._wrapped >= other
+
+    def __gt__(self, other):
+        if hasattr(other, '_wrapped'):
+            return self._wrapped > other._wrapped
+        else:
+            return self._wrapped > other
+
+    def __ge__(self, other):
+        if hasattr(other, '_wrapped'):
+            return self._wrapped >= other._wrapped
+        else:
+            return self._wrapped >= other
+
+    def __repr__(self):
+        return '%s.text' % repr(self._wrapped)
+
+
+class MessageElementTextWrapper(_WrapBase):
+
+    """Text interface for a LDB message element"""
+
+    def __iter__(self):
+        for item in self._wrapped:
+            yield item.decode('utf-8')
+
+    def __getitem__(self, key):
+        result = self._wrapped[key]
+        if result is None:
+            return None
+        else:
+            return result.decode('utf-8')
+
+    @property
+    def flags(self):
+        return self._wrapped.flags
+
+    @property
+    def set_flags(self):
+        return self._wrapped.set_flags
+
+_wrap_element = MessageElementTextWrapper._wrap
+
+
+class MessageTextWrapper(_WrapBase):
+
+    """Text interface for a LDB message"""
+
+    def __getitem__(self, key):
+        result = self._wrapped[key]
+        if result is None:
+            return None
+        else:
+            return _wrap_element(result)
+
+    def get(self, *args, **kwargs):
+        result = self._wrapped.get(*args, **kwargs)
+        if isinstance(result, ldb.MessageElement):
+            return _wrap_element(result)
+        elif isinstance(result, bytes):
+            return result.decode('utf-8')
+        else:
+            return result
+
+    def __setitem__(self, key, item):
+        self._wrapped[key] = _recursive_encode(item)
+
+    def __delitem__(self, key):
+        del self._wrapped[key]
+
+    def elements(self):
+        return [_wrap_element(el) for el in self._wrapped.elements()]
+
+    def items(self):
+        return [(attr, _wrap_element(el)) for attr, el in self._wrapped.items()]
+
+    @property
+    def keys(self):
+        return self._wrapped.keys
+
+    @property
+    def remove(self):
+        return self._wrapped.remove
+
+    @property
+    def add(self):
+        return self._wrapped.add
+
+    @property
+    def dn(self):
+        return self._wrapped.dn
+
+    @dn.setter
+    def dn(self, new_value):
+        self._wrapped.dn = new_value
diff --git a/lib/ldb/pyldb.c b/lib/ldb/pyldb.c
index e279f97..308ecb7 100644
--- a/lib/ldb/pyldb.c
+++ b/lib/ldb/pyldb.c
@@ -57,15 +57,34 @@ static struct ldb_message_element *PyObject_AsMessageElement(
 						      unsigned int flags,
 						      const char *attr_name);
 
-/* There's no Py_ssize_t in 2.4, apparently */
-#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 5
-typedef int Py_ssize_t;
-typedef inquiry lenfunc;
-typedef intargfunc ssizeargfunc;
-#endif
-
-
+#if PY_MAJOR_VERSION >= 3
+#define PyStr_Check PyUnicode_Check
+#define PyStr_FromString PyUnicode_FromString
+#define PyStr_FromStringAndSize PyUnicode_FromStringAndSize
+#define PyStr_FromFormat PyUnicode_FromFormat
+#define PyStr_FromFormatV PyUnicode_FromFormatV
+#define PyStr_AsUTF8 PyUnicode_AsUTF8
+#define PyStr_AsUTF8AndSize PyUnicode_AsUTF8AndSize
+#define PyInt_FromLong PyLong_FromLong
+#else
+#define PyStr_Check PyString_Check
+#define PyStr_FromString PyString_FromString
+#define PyStr_FromStringAndSize PyString_FromStringAndSize
+#define PyStr_FromFormat PyString_FromFormat
+#define PyStr_FromFormatV PyString_FromFormatV
+#define PyStr_AsUTF8 PyString_AsString
 
+const char *PyStr_AsUTF8AndSize(PyObject *pystr, Py_ssize_t *sizeptr);
+const char *
+PyStr_AsUTF8AndSize(PyObject *pystr, Py_ssize_t *sizeptr)
+{
+	const char * ret = PyString_AsString(pystr);
+	if (ret == NULL)
+		return NULL;
+	*sizeptr = PyString_Size(pystr);
+	return ret;
+}
+#endif
 
 static PyObject *richcmp(int cmp_val, int op)
 {
@@ -93,9 +112,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_FromString("ldb control");
 	}
 }
 
@@ -108,9 +127,32 @@ static void py_ldb_control_dealloc(PyLdbControlObject *self)
 	Py_TYPE(self)->tp_free(self);
 }
 
+/* Create a text (rather than bytes) interface for a LDB result object */
+static PyObject *wrap_text(const char *type, PyObject *wrapped)
+{
+	PyObject *mod, *cls, *constructor, *inst;
+	mod = PyImport_ImportModule("_ldb_text");
+	if (mod == NULL)
+		return NULL;
+	cls = PyObject_GetAttrString(mod, type);
+	Py_DECREF(mod);
+	if (cls == NULL) {
+		Py_DECREF(mod);
+		return NULL;
+	}
+	constructor = PyObject_GetAttrString(cls, "_wrap");
+	Py_DECREF(cls);
+	if (constructor == NULL) {
+		return NULL;
+	}
+	inst = PyObject_CallFunction(constructor, discard_const_p(char, "O"), wrapped);
+	Py_DECREF(constructor);
+	return inst;
+}
+
 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)
@@ -208,7 +250,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);
 }
 
 /**
@@ -334,7 +376,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;
@@ -392,22 +434,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)
@@ -418,7 +460,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)
@@ -433,14 +475,15 @@ 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)
 {
 	char *name;
 	PyObject *value;
-	int err;
+	int err, result;
+	Py_ssize_t size;
 
 	if (!PyArg_ParseTuple(args, "sO", &name, &value))
 		return NULL;
@@ -449,12 +492,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)) {
-			PyErr_SetString(PyExc_TypeError, "Expected a string argument");
+		result = PyBytes_AsStringAndSize(value, (char **) &val.data, &size);
+		val.length = size;
+		if (result != 0) {
+			PyErr_SetString(PyExc_TypeError, "Expected a bytestring argument");
 			return NULL;
 		}
-		val.data = (uint8_t *)PyString_AsString(value);
-		val.length = PyString_Size(value);
 		err = ldb_dn_set_extended_component(self->dn, name, &val);
 	}
 
@@ -468,7 +511,19 @@ 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, *result;
+	if (str == NULL)
+		return NULL;
+	repr = PyObject_Repr(str);
+	if (repr == NULL) {
+		Py_DECREF(str);
+		return NULL;
+	}
+	result = PyStr_FromFormat("Dn(%s)", PyStr_AsUTF8(repr));
+	Py_DECREF(str);
+	Py_DECREF(repr);
+	return result;
 }
 
 static PyObject *py_ldb_dn_check_special(PyLdbDnObject *self, PyObject *args)
@@ -591,7 +646,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)
@@ -619,18 +674,19 @@ static PyObject *py_ldb_dn_set_component(PyLdbDnObject *self, PyObject *args)
 	char *name = NULL;
 	PyObject *value = Py_None;
 	struct ldb_val val = { NULL, };
-	int err;
+	int err, ret;
+	Py_ssize_t size;
 
 	if (!PyArg_ParseTuple(args, "IsO", &num, &name, &value))
 		return NULL;
 
 	if (value != Py_None) {
-		if (!PyString_Check(value)) {
-			PyErr_SetString(PyExc_TypeError, "Expected a string argument");
+		ret = PyBytes_AsStringAndSize(value, (char **) &val.data, &size);
+		if (ret != 0) {
+			PyErr_SetString(PyExc_TypeError, "Expected a bytestring argument");
 			return NULL;
 		}
-		val.data = (uint8_t *)PyString_AsString(value);
-		val.length = PyString_Size(value);
+		val.length = size;
 	}
 
 	err = ldb_dn_set_component(self->dn, num, name, val);
@@ -654,7 +710,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)
@@ -855,7 +911,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_debug_func;
@@ -952,7 +1008,7 @@ static PyObject *py_ldb_setup_wellknown_attributes(PyLdbObject *self)
 
 static PyObject *py_ldb_repr(PyLdbObject *self)
 {
-	return PyString_FromFormat("<ldb connection>");
+	return PyStr_FromString("<ldb connection>");
 }
 
 static PyObject *py_ldb_get_root_basedn(PyLdbObject *self)
@@ -988,8 +1044,8 @@ 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, 
-					const char *paramname)
+static const char **PyList_AsStrList(TALLOC_CTX *mem_ctx, PyObject *list,
+                    const char *paramname)
 {
 	const char **ret;
 	Py_ssize_t i;
@@ -1004,13 +1060,20 @@ static const char **PyList_AsStringList(TALLOC_CTX *mem_ctx, PyObject *list,
 	}
 
 	for (i = 0; i < PyList_Size(list); i++) {
+		const char *str = NULL;
+		Py_ssize_t size;
 		PyObject *item = PyList_GetItem(list, i);
-		if (!PyString_Check(item)) {
+		if (!PyStr_Check(item)) {
 			PyErr_Format(PyExc_TypeError, "%s should be strings", paramname);
+			talloc_free(ret);
 			return NULL;
 		}
-		ret[i] = talloc_strndup(ret, PyString_AsString(item),
-					PyString_Size(item));
+		str = PyStr_AsUTF8AndSize(item, &size);
+		if (str == NULL) {
+			talloc_free(ret);
+			return NULL;
+		}
+		ret[i] = talloc_strndup(ret, str, size);
 	}
 	ret[i] = NULL;
 	return ret;
@@ -1036,7 +1099,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;
 	}
@@ -1092,7 +1155,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;
 	}
@@ -1134,7 +1197,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;
@@ -1161,7 +1224,7 @@ static PyObject *py_ldb_modify(PyLdbObject *self, PyObject *args, PyObject *kwar
 
 	ret = ldb_build_mod_req(&req, ldb_ctx, mem_ctx, msg, parsed_controls,
 				NULL, ldb_op_default_callback, NULL);
-        if (ret != LDB_SUCCESS) {
+	if (ret != LDB_SUCCESS) {
 		PyErr_SetString(PyExc_TypeError, "failed to build request");
 		talloc_free(mem_ctx);
 		return NULL;
@@ -1238,7 +1301,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_AsUTF8(key);
 		if (ldb_attr_cmp(key_str, "dn") != 0) {
 			msg_el = PyObject_AsMessageElement(msg->elements, value,
 							   mod_flags, key_str);
@@ -1283,7 +1346,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;
@@ -1376,7 +1439,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;
@@ -1454,7 +1517,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;
@@ -1579,7 +1642,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);
 
@@ -1668,14 +1731,16 @@ static PyObject *py_ldb_schema_format_value(PyLdbObject *self, PyObject *args)
 	PyObject *ret;
 	char *element_name;
 	PyObject *val;
+	Py_ssize_t size;
+	int result;
 
 	if (!PyArg_ParseTuple(args, "sO", &element_name, &val))
 		return NULL;
 
-	old_val.data = (uint8_t *)PyString_AsString(val);
-	old_val.length = PyString_Size(val);
+	result = PyBytes_AsStringAndSize(val, (char **)&old_val.data, &size);
+	old_val.length = size;
 
-	if (old_val.data == NULL) {
+	if (result != 0) {
 		PyErr_SetString(PyExc_RuntimeError, "Failed to convert passed value to String");
 		return NULL;
 	}
@@ -1697,7 +1762,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);
 
@@ -1739,7 +1804,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;
@@ -1758,7 +1823,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;
@@ -2131,7 +2196,7 @@ static PySequenceMethods py_ldb_result_seq = {
 
 static PyObject *py_ldb_result_repr(PyLdbObject *self)
 {
-	return PyString_FromFormat("<ldb result>");
+	return PyStr_FromString("<ldb result>");
 }
 
 
@@ -2150,13 +2215,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)
@@ -2197,7 +2262,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;
 	}
@@ -2361,6 +2426,9 @@ static struct ldb_message_element *PyObject_AsMessageElement(
 						      const char *attr_name)
 {
 	struct ldb_message_element *me;
+	const char *msg = NULL;
+	Py_ssize_t size;
+	int result;
 
 	if (pyldb_MessageElement_Check(set_obj)) {
 		PyLdbMessageElementObject *set_obj_as_me = (PyLdbMessageElementObject *)set_obj;
@@ -2380,28 +2448,58 @@ 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) || PyStr_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].data = talloc_memdup(me, 
-			(uint8_t *)PyString_AsString(set_obj), me->values[0].length+1);
+		if (PyBytes_Check(set_obj)) {
+			char *_msg = NULL;
+			result = PyBytes_AsStringAndSize(set_obj, &_msg, &size);
+			if (result != 0) {
+				talloc_free(me);
+				return NULL;
+			}
+			msg = _msg;
+		} else {
+			msg = PyStr_AsUTF8AndSize(set_obj, &size);
+			if (msg == NULL) {
+				talloc_free(me);
+				return NULL;
+			}
+		}
+		me->values[0].data = talloc_memdup(me,
+						   (const uint8_t *)msg,
+						   size+1);
+		me->values[0].length = size;
 	} 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)) {
+				char *_msg = NULL;
+				result = PyBytes_AsStringAndSize(obj, &_msg, &size);
+				if (result != 0) {
+					talloc_free(me);
+					return NULL;
+				}
+				msg = _msg;
+			} else if (PyStr_Check(obj)) {
+				msg = PyStr_AsUTF8AndSize(obj, &size);
+				if (msg == NULL) {
+					talloc_free(me);
+					return NULL;
+				}
+			} else {
 				PyErr_Format(PyExc_TypeError,
 					     "Expected string as element %zd in list", i);
 				talloc_free(me);
 				return NULL;
 			}
-
-			me->values[i].length = PyString_Size(obj);
-			me->values[i].data = talloc_memdup(me, 
-				(uint8_t *)PyString_AsString(obj), me->values[i].length+1);
+			me->values[i].data = talloc_memdup(me,
+							   (const uint8_t *)msg,
+							   size+1);
+			me->values[i].length = size;
 		}
 	} else {
 		PyErr_Format(PyExc_TypeError,
@@ -2479,7 +2577,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 = {
@@ -2534,6 +2632,9 @@ static PyObject *py_ldb_msg_element_new(PyTypeObject *type, PyObject *args, PyOb
 	const char * const kwnames[] = { "elements", "flags", "name", NULL };
 	PyLdbMessageElementObject *ret;
 	TALLOC_CTX *mem_ctx;
+	const char *msg = NULL;
+	Py_ssize_t size;
+	int result;
 
 	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OIs",
 					 discard_const_p(char *, kwnames),
@@ -2555,7 +2656,8 @@ 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)) {
+			char *_msg = NULL;
 			el->num_values = 1;
 			el->values = talloc_array(el, struct ldb_val, 1);
 			if (el->values == NULL) {
@@ -2563,9 +2665,15 @@ static PyObject *py_ldb_msg_element_new(PyTypeObject *type, PyObject *args, PyOb
 				PyErr_NoMemory();
 				return NULL;
 			}
-			el->values[0].length = PyString_Size(py_elements);
+			result = PyBytes_AsStringAndSize(py_elements, &_msg, &size);
+			if (result != 0) {
+				talloc_free(mem_ctx);
+				return NULL;
+			}
+			msg = _msg;
 			el->values[0].data = talloc_memdup(el->values, 
-				(uint8_t *)PyString_AsString(py_elements), el->values[0].length+1);
+				(const uint8_t *)msg, size);
+			el->values[0].length = size;
 		} else if (PySequence_Check(py_elements)) {
 			el->num_values = PySequence_Size(py_elements);
 			el->values = talloc_array(el, struct ldb_val, el->num_values);
@@ -2580,15 +2688,25 @@ 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)) {
+					char *_msg = NULL;
+					result = PyBytes_AsStringAndSize(item, &_msg, &size);
+					msg = _msg;
+				} else if (PyStr_Check(item)) {
+					msg = PyStr_AsUTF8AndSize(item, &size);
+					result = (msg == NULL) ? -1 : 0;
+				} else {
 					PyErr_Format(PyExc_TypeError, 
 						     "Expected string as element %zd in list", i);
+					result = -1;
+				}
+				if (result != 0) {
 					talloc_free(mem_ctx);
 					return NULL;
 				}
-				el->values[i].length = PyString_Size(item);
 				el->values[i].data = talloc_memdup(el,
-					(uint8_t *)PyString_AsString(item), el->values[i].length+1);
+					(const uint8_t *)msg, size+1);
+				el->values[i].length = size;
 			}
 		} else {
 			PyErr_SetString(PyExc_TypeError, 
@@ -2623,17 +2741,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_AsUTF8(repr));
 		else
-			element_str = talloc_asprintf_append(element_str, ",%s", PyString_AsString(repr));
+			element_str = talloc_asprintf_append(element_str, ",%s", PyStr_AsUTF8(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;
@@ -2644,7 +2762,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;
 }
@@ -2655,6 +2773,16 @@ static void py_ldb_msg_element_dealloc(PyLdbMessageElementObject *self)
 	PyObject_Del(self);
 }
 
+static PyObject *py_ldb_msg_element_get_text(PyObject *self, void *closure)
+{
+	return wrap_text("MessageElementTextWrapper", self);
+}
+
+static PyGetSetDef py_ldb_msg_element_getset[] = {
+	{ discard_const_p(char, "text"), (getter)py_ldb_msg_element_get_text, NULL, NULL },
+	{ NULL }
+};
+
 static PyTypeObject PyLdbMessageElement = {
 	.tp_name = "ldb.MessageElement",
 	.tp_basicsize = sizeof(PyLdbMessageElementObject),
@@ -2662,6 +2790,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_getset = py_ldb_msg_element_getset,
 	.tp_richcompare = (richcmpfunc)py_ldb_msg_element_richcmp,
 	.tp_iter = (getiterfunc)py_ldb_msg_element_iter,
 	.tp_as_sequence = &py_ldb_msg_element_seq,
@@ -2731,11 +2860,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;
@@ -2746,11 +2875,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)) {
+	name = PyStr_AsUTF8(py_name);
+	if (name == NULL) {
 		PyErr_SetNone(PyExc_TypeError);
 		return NULL;
 	}
-	name = PyString_AsString(py_name);
 	if (!ldb_attr_cmp(name, "dn"))
 		return pyldb_Dn_FromDn(msg->dn);
 	el = ldb_msg_find_element(msg, name);
@@ -2912,12 +3041,12 @@ static int py_ldb_msg_setitem(PyLdbMessageObject *self, PyObject *name, PyObject
 {
 	char *attr_name;
 
-	if (!PyString_Check(name)) {
+	attr_name = PyStr_AsUTF8(name);
+	if (attr_name == NULL) {
 		PyErr_SetNone(PyExc_TypeError);
 		return -1;
 	}
 
-	attr_name = PyString_AsString(name);
 	if (value == NULL) {
 		/* delitem */
 		ldb_msg_remove_attr(self->msg, attr_name);
@@ -3028,8 +3157,14 @@ static int py_ldb_msg_set_dn(PyLdbMessageObject *self, PyObject *value, void *cl
 	return 0;
 }
 
+static PyObject *py_ldb_msg_get_text(PyObject *self, void *closure)
+{
+	return wrap_text("MessageTextWrapper", self);
+}
+
 static PyGetSetDef py_ldb_msg_getset[] = {
 	{ discard_const_p(char, "dn"), (getter)py_ldb_msg_get_dn, (setter)py_ldb_msg_set_dn, NULL },
+	{ discard_const_p(char, "text"), (getter)py_ldb_msg_get_text, NULL, NULL },
 	{ NULL }
 };
 
@@ -3043,7 +3178,7 @@ static PyObject *py_ldb_msg_repr(PyLdbMessageObject *self)
 		Py_DECREF(dict);
 		return NULL;
 	}
-	ret = PyString_FromFormat("Message(%s)", PyString_AsString(repr));
+	ret = PyStr_FromFormat("Message(%s)", PyStr_AsUTF8(repr));
 	Py_DECREF(repr);
 	Py_DECREF(dict);
 	return ret;
@@ -3166,7 +3301,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"),
@@ -3424,7 +3559,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_AsUTF8(PyObject_GetAttrString(input, discard_const_p(char, "name"))));
 
 	Py_INCREF(input);
 	ops->private_data = input;
@@ -3457,7 +3592,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;
 }
@@ -3499,7 +3634,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;
 }
@@ -3521,7 +3656,7 @@ static PyObject *py_binary_decode(PyObject *self, PyObject *args)
 		PyErr_SetString(PyExc_TypeError, "unable to decode binary string");
 		return NULL;
 	}
-	ret = Py_BuildValue("s#", val.data, val.length);
+	ret = PyBytes_FromStringAndSize((const char*)val.data, val.length);
 	talloc_free(val.data);
 	return ret;
 }
diff --git a/lib/ldb/pyldb_util.c b/lib/ldb/pyldb_util.c
index 4be9126..3bda1db 100644
--- a/lib/ldb/pyldb_util.c
+++ b/lib/ldb/pyldb_util.c
@@ -29,11 +29,12 @@
 
 static PyObject *ldb_module = NULL;
 
-/* There's no Py_ssize_t in 2.4, apparently */
-#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 5
-typedef int Py_ssize_t;
-typedef inquiry lenfunc;
-typedef intargfunc ssizeargfunc;
+#if PY_MAJOR_VERSION >= 3
+#define PyStr_Check PyUnicode_Check
+#define PyStr_AsUTF8 PyUnicode_AsUTF8
+#else
+#define PyStr_Check PyString_Check
+#define PyStr_AsUTF8 PyString_AsString
 #endif
 
 /**
@@ -69,8 +70,14 @@ 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_AsUTF8(object));
+		*dn = odn;
+		return true;
+	}
+
+	if (ldb_ctx != NULL && PyBytes_Check(object)) {
+		odn = ldb_dn_new(mem_ctx, ldb_ctx, PyBytes_AsString(object));
 		*dn = odn;
 		return true;
 	}
diff --git a/lib/ldb/wscript b/lib/ldb/wscript
index ba1fa17..9b3429c 100755
--- a/lib/ldb/wscript
+++ b/lib/ldb/wscript
@@ -133,6 +133,14 @@ def build(bld):
                           abi_directory='ABI',
                           abi_match='pyldb_*')
 
+    if not bld.env.disable_python:
+        for env in bld.gen_python_environments(['PKGCONFIGDIR']):
+            bld.SAMBA_SCRIPT('_ldb_text.py',
+                             pattern='_ldb_text.py',
+                             installdir='python')
+
+            bld.INSTALL_FILES('${PYTHONARCHDIR}', '_ldb_text.py')
+
     if not bld.CONFIG_SET('USING_SYSTEM_LDB'):
         if Options.is_install:
             modules_dir = bld.EXPAND_VARIABLES('${LDB_MODULESDIR}')
-- 
2.1.0


From 18d1b49458a5a18bc1803a7674ae7d80d4e49f5f Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori at redhat.com>
Date: Wed, 29 Jul 2015 12:45:43 +0200
Subject: [PATCH 09/10] pyldb: Adapt tests to Python 3

Signed-off-by: Petr Viktorin <pviktori at redhat.com>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 lib/ldb/tests/python/api.py | 405 ++++++++++++++++++++++++++++++++++++--------
 1 file changed, 330 insertions(+), 75 deletions(-)

diff --git a/lib/ldb/tests/python/api.py b/lib/ldb/tests/python/api.py
index ac9dd61..2a85c08 100755
--- a/lib/ldb/tests/python/api.py
+++ b/lib/ldb/tests/python/api.py
@@ -4,9 +4,12 @@
 
 import os
 from unittest import TestCase
+import sys
 
 import ldb
 
+PY3 = sys.version_info > (3, 0)
+
 
 def filename():
     import tempfile
@@ -32,9 +35,12 @@ class NoContextTests(TestCase):
         self.assertEqual(1195499412, ldb.string_to_time("20071119191012.0Z"))
 
     def test_binary_encode(self):
-        encoded = ldb.binary_encode('test\\x')
+        encoded = ldb.binary_encode(b'test\\x')
         decoded = ldb.binary_decode(encoded)
-        self.assertEqual(decoded, 'test\\x')
+        self.assertEqual(decoded, b'test\\x')
+
+        encoded2 = ldb.binary_encode('test\\x')
+        self.assertEqual(encoded2, encoded)
 
 class SimpleLdb(TestCase):
 
@@ -83,6 +89,7 @@ class SimpleLdb(TestCase):
     def test_search_attr_string(self):
         l = ldb.Ldb(filename())
         self.assertRaises(TypeError, l.search, attrs="dc")
+        self.assertRaises(TypeError, l.search, attrs=b"dc")
 
     def test_opaque(self):
         l = ldb.Ldb(filename())
@@ -103,7 +110,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)
@@ -142,6 +149,18 @@ class SimpleLdb(TestCase):
         l = ldb.Ldb(filename())
         m = ldb.Message()
         m.dn = ldb.Dn(l, "dc=foo4")
+        m["bla"] = b"bla"
+        self.assertEqual(len(l.search()), 0)
+        l.add(m)
+        try:
+            self.assertEqual(len(l.search()), 1)
+        finally:
+            l.delete(ldb.Dn(l, "dc=foo4"))
+
+    def test_add_text(self):
+        l = ldb.Ldb(filename())
+        m = ldb.Message()
+        m.dn = ldb.Dn(l, "dc=foo4")
         m["bla"] = "bla"
         self.assertEqual(len(l.search()), 0)
         l.add(m)
@@ -154,13 +173,24 @@ 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.assertEqual(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": b"bla"}
+        self.assertEqual(len(l.search()), 0)
+        l.add(m)
+        try:
+            self.assertEqual(len(l.search()), 1)
+        finally:
+            l.delete(ldb.Dn(l, "dc=foo5"))
+
+    def test_add_dict_text(self):
+        l = ldb.Ldb(filename())
+        m = {"dn": ldb.Dn(l, "dc=foo5"),
              "bla": "bla"}
         self.assertEqual(len(l.search()), 0)
         l.add(m)
@@ -171,7 +201,17 @@ 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.assertEqual(len(l.search()), 0)
+        l.add(m)
+        try:
+            self.assertEqual(len(l.search()), 1)
+        finally:
+            l.delete(ldb.Dn(l, "dc=foo6"))
+
+    def test_add_dict_bytes_dn(self):
+        l = ldb.Ldb(filename())
+        m = {"dn": b"dc=foo6", "bla": b"bla"}
         self.assertEqual(len(l.search()), 0)
         l.add(m)
         try:
@@ -183,7 +223,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.assertEqual(len(l.search()), 0)
         l.add(m)
         try:
@@ -196,7 +236,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.assertEqual(len(l.search()), 0)
         l.add(m)
         self.assertEqual(len(l.search()), 1)
@@ -210,10 +250,31 @@ 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.assertEqual([b"1234"], list(rm["bla"]))
+        try:
+            m = ldb.Message()
+            m.dn = ldb.Dn(l, "dc=modifydelete")
+            m["bla"] = ldb.MessageElement([], ldb.FLAG_MOD_DELETE, "bla")
+            self.assertEqual(ldb.FLAG_MOD_DELETE, m["bla"].flags())
+            l.modify(m)
+            rm = l.search(m.dn)[0]
+            self.assertEqual(1, len(rm))
+            rm = l.search(m.dn, attrs=["bla"])
+            self.assertEqual(0, len(rm))
+        finally:
+            l.delete(ldb.Dn(l, "dc=modifydelete"))
+
+    def test_modify_delete_text(self):
+        l = ldb.Ldb(filename())
+        m = ldb.Message()
+        m.dn = ldb.Dn(l, "dc=modifydelete")
+        m.text["bla"] = ["1234"]
         l.add(m)
         rm = l.search(m.dn)[0]
-        self.assertEqual(["1234"], list(rm["bla"]))
+        self.assertEqual(["1234"], list(rm.text["bla"]))
         try:
             m = ldb.Message()
             m.dn = ldb.Dn(l, "dc=modifydelete")
@@ -231,7 +292,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([b"456"], ldb.FLAG_MOD_ADD, "bla")
+            self.assertEqual(ldb.FLAG_MOD_ADD, m["bla"].flags())
+            l.modify(m)
+            rm = l.search(m.dn)[0]
+            self.assertEqual(2, len(rm))
+            self.assertEqual([b"1234", b"456"], list(rm["bla"]))
+        finally:
+            l.delete(ldb.Dn(l, "dc=add"))
+
+    def test_modify_add_text(self):
+        l = ldb.Ldb(filename())
+        m = ldb.Message()
+        m.dn = ldb.Dn(l, "dc=add")
+        m.text["bla"] = ["1234"]
         l.add(m)
         try:
             m = ldb.Message()
@@ -241,7 +320,7 @@ class SimpleLdb(TestCase):
             l.modify(m)
             rm = l.search(m.dn)[0]
             self.assertEqual(2, len(rm))
-            self.assertEqual(["1234", "456"], list(rm["bla"]))
+            self.assertEqual(["1234", "456"], list(rm.text["bla"]))
         finally:
             l.delete(ldb.Dn(l, "dc=add"))
 
@@ -249,7 +328,27 @@ 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([b"789"], ldb.FLAG_MOD_REPLACE, "bla")
+            self.assertEqual(ldb.FLAG_MOD_REPLACE, m["bla"].flags())
+            l.modify(m)
+            rm = l.search(m.dn)[0]
+            self.assertEqual(2, len(rm))
+            self.assertEqual([b"789"], list(rm["bla"]))
+            rm = l.search(m.dn, attrs=["bla"])[0]
+            self.assertEqual(1, len(rm))
+        finally:
+            l.delete(ldb.Dn(l, "dc=modify2"))
+
+    def test_modify_replace_text(self):
+        l = ldb.Ldb(filename())
+        m = ldb.Message()
+        m.dn = ldb.Dn(l, "dc=modify2")
+        m.text["bla"] = ["1234", "456"]
         l.add(m)
         try:
             m = ldb.Message()
@@ -259,7 +358,7 @@ class SimpleLdb(TestCase):
             l.modify(m)
             rm = l.search(m.dn)[0]
             self.assertEqual(2, len(rm))
-            self.assertEqual(["789"], list(rm["bla"]))
+            self.assertEqual(["789"], list(rm.text["bla"]))
             rm = l.search(m.dn, attrs=["bla"])[0]
             self.assertEqual(1, len(rm))
         finally:
@@ -269,7 +368,33 @@ 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([b"456"], ldb.FLAG_MOD_ADD, "bla")
+            self.assertEqual(ldb.FLAG_MOD_ADD, m["bla"].flags())
+            l.modify(m)
+            rm = l.search(m.dn)[0]
+            self.assertEqual(2, len(rm))
+            self.assertEqual([b"1234", b"456"], list(rm["bla"]))
+
+            # Now create another modify, but switch the flags before we do it
+            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.assertEqual(1, len(rm))
+            self.assertEqual([b"1234"], list(rm["bla"]))
+        finally:
+            l.delete(ldb.Dn(l, "dc=add"))
+
+    def test_modify_flags_change_text(self):
+        l = ldb.Ldb(filename())
+        m = ldb.Message()
+        m.dn = ldb.Dn(l, "dc=add")
+        m.text["bla"] = ["1234"]
         l.add(m)
         try:
             m = ldb.Message()
@@ -279,7 +404,7 @@ class SimpleLdb(TestCase):
             l.modify(m)
             rm = l.search(m.dn)[0]
             self.assertEqual(2, len(rm))
-            self.assertEqual(["1234", "456"], list(rm["bla"]))
+            self.assertEqual(["1234", "456"], list(rm.text["bla"]))
 
             # Now create another modify, but switch the flags before we do it
             m["bla"] = ldb.MessageElement(["456"], ldb.FLAG_MOD_ADD, "bla")
@@ -287,7 +412,7 @@ class SimpleLdb(TestCase):
             l.modify(m)
             rm = l.search(m.dn, attrs=["bla"])[0]
             self.assertEqual(1, len(rm))
-            self.assertEqual(["1234"], list(rm["bla"]))
+            self.assertEqual(["1234"], list(rm.text["bla"]))
         finally:
             l.delete(ldb.Dn(l, "dc=add"))
 
@@ -295,7 +420,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 +429,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.assertEqual(0, len(l.search(ldb.Dn(l, "dc=foo10"))))
@@ -319,14 +444,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" : b"dc=somedn",
+            "objectclass" : b"user",
+            "cN" : b"LDAPtestUSER",
+            "givenname" : b"ldap",
+            "displayname" : b"foo\0bar",
         })
         res = l.search(expression="(dn=dc=somedn)")
-        self.assertEqual("foo\0bar", res[0]["displayname"][0])
+        self.assertEqual(b"foo\0bar", res[0]["displayname"][0])
 
     def test_no_crash_broken_expr(self):
         l = ldb.Ldb(filename())
@@ -474,11 +599,23 @@ class LdbMsgTests(TestCase):
 
     def test_repr(self):
         self.msg.dn = ldb.Dn(ldb.Ldb(filename()), "dc=foo29")
-        self.msg["dc"] = "foo"
-        self.assertIn(repr(self.msg), [
-            "Message({'dn': Dn('dc=foo29'), 'dc': MessageElement(['foo'])})",
-            "Message({'dc': MessageElement(['foo']), 'dn': Dn('dc=foo29')})",
-        ])
+        self.msg["dc"] = b"foo"
+        if PY3:
+            self.assertIn(repr(self.msg), [
+                "Message({'dn': Dn('dc=foo29'), 'dc': MessageElement([b'foo'])})",
+                "Message({'dc': MessageElement([b'foo']), 'dn': Dn('dc=foo29')})",
+            ])
+            self.assertIn(repr(self.msg.text), [
+                "Message({'dn': Dn('dc=foo29'), 'dc': MessageElement([b'foo'])}).text",
+                "Message({'dc': MessageElement([b'foo']), 'dn': Dn('dc=foo29')}).text",
+            ])
+        else:
+            self.assertEquals(
+                repr(self.msg),
+                "Message({'dn': Dn('dc=foo29'), 'dc': MessageElement(['foo'])})")
+            self.assertEquals(
+                repr(self.msg.text),
+                "Message({'dn': Dn('dc=foo29'), 'dc': MessageElement(['foo'])}).text")
 
     def test_len(self):
         self.assertEqual(0, len(self.msg))
@@ -490,38 +627,65 @@ class LdbMsgTests(TestCase):
         del self.msg["foo"]
 
     def test_add(self):
+        self.msg.add(ldb.MessageElement([b"456"], ldb.FLAG_MOD_ADD, "bla"))
+
+    def test_add_text(self):
         self.msg.add(ldb.MessageElement(["456"], ldb.FLAG_MOD_ADD, "bla"))
 
     def test_elements_empty(self):
         self.assertEqual([], 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.assertEqual([el], self.msg.elements())
+        self.assertEqual([el.text], self.msg.text.elements())
 
     def test_add_value(self):
         self.assertEqual(0, len(self.msg))
+        self.msg["foo"] = [b"foo"]
+        self.assertEqual(1, len(self.msg))
+
+    def test_add_value_text(self):
+        self.assertEqual(0, len(self.msg))
         self.msg["foo"] = ["foo"]
         self.assertEqual(1, len(self.msg))
 
     def test_add_value_multiple(self):
         self.assertEqual(0, len(self.msg))
+        self.msg["foo"] = [b"foo", b"bla"]
+        self.assertEqual(1, len(self.msg))
+        self.assertEqual([b"foo", b"bla"], list(self.msg["foo"]))
+
+    def test_add_value_multiple_text(self):
+        self.assertEqual(0, len(self.msg))
         self.msg["foo"] = ["foo", "bla"]
         self.assertEqual(1, len(self.msg))
-        self.assertEqual(["foo", "bla"], list(self.msg["foo"]))
+        self.assertEqual(["foo", "bla"], list(self.msg.text["foo"]))
 
     def test_set_value(self):
+        self.msg["foo"] = [b"fool"]
+        self.assertEqual([b"fool"], list(self.msg["foo"]))
+        self.msg["foo"] = [b"bar"]
+        self.assertEqual([b"bar"], list(self.msg["foo"]))
+
+    def test_set_value_text(self):
         self.msg["foo"] = ["fool"]
-        self.assertEqual(["fool"], list(self.msg["foo"]))
+        self.assertEqual(["fool"], list(self.msg.text["foo"]))
         self.msg["foo"] = ["bar"]
-        self.assertEqual(["bar"], list(self.msg["foo"]))
+        self.assertEqual(["bar"], list(self.msg.text["foo"]))
 
     def test_keys(self):
         self.msg.dn = ldb.Dn(ldb.Ldb(filename()), "@BASEINFO")
+        self.msg["foo"] = [b"bla"]
+        self.msg["bar"] = [b"bla"]
+        self.assertEqual(["dn", "foo", "bar"], self.msg.keys())
+
+    def test_keys_text(self):
+        self.msg.dn = ldb.Dn(ldb.Ldb(filename()), "@BASEINFO")
         self.msg["foo"] = ["bla"]
         self.msg["bar"] = ["bla"]
-        self.assertEqual(["dn", "foo", "bar"], self.msg.keys())
+        self.assertEqual(["dn", "foo", "bar"], self.msg.text.keys())
 
     def test_dn(self):
         self.msg.dn = ldb.Dn(ldb.Ldb(filename()), "@BASEINFO")
@@ -531,14 +695,32 @@ class LdbMsgTests(TestCase):
         self.msg.dn = ldb.Dn(ldb.Ldb(filename()), "@BASEINFO")
         self.assertEqual("@BASEINFO", self.msg.get("dn").__str__())
 
+    def test_dn_text(self):
+        self.msg.text.dn = ldb.Dn(ldb.Ldb(filename()), "@BASEINFO")
+        self.assertEqual("@BASEINFO", str(self.msg.dn))
+        self.assertEqual("@BASEINFO", str(self.msg.text.dn))
+
+    def test_get_dn_text(self):
+        self.msg.dn = ldb.Dn(ldb.Ldb(filename()), "@BASEINFO")
+        self.assertEqual("@BASEINFO", str(self.msg.get("dn")))
+        self.assertEqual("@BASEINFO", str(self.msg.text.get("dn")))
+
     def test_get_invalid(self):
         self.msg.dn = ldb.Dn(ldb.Ldb(filename()), "@BASEINFO")
         self.assertRaises(TypeError, self.msg.get, 42)
 
     def test_get_other(self):
+        self.msg["foo"] = [b"bar"]
+        self.assertEqual(b"bar", self.msg.get("foo")[0])
+        self.assertEqual(b"bar", self.msg.get("foo", idx=0))
+        self.assertEqual(None, self.msg.get("foo", idx=1))
+        self.assertEqual("", self.msg.get("foo", default='', idx=1))
+
+    def test_get_other_text(self):
         self.msg["foo"] = ["bar"]
-        self.assertEqual("bar", self.msg.get("foo")[0])
-        self.assertEqual("bar", self.msg.get("foo", idx=0))
+        self.assertEqual(["bar"], list(self.msg.text.get("foo")))
+        self.assertEqual("bar", self.msg.text.get("foo")[0])
+        self.assertEqual("bar", self.msg.text.get("foo", idx=0))
         self.assertEqual(None, self.msg.get("foo", idx=1))
         self.assertEqual("", self.msg.get("foo", default='', idx=1))
 
@@ -546,9 +728,16 @@ class LdbMsgTests(TestCase):
         self.assertEqual(None, self.msg.get("tatayoyo", idx=0))
         self.assertEqual("anniecordie", self.msg.get("tatayoyo", "anniecordie"))
 
+    def test_get_default_text(self):
+        self.assertEqual(None, self.msg.text.get("tatayoyo", idx=0))
+        self.assertEqual("anniecordie", self.msg.text.get("tatayoyo", "anniecordie"))
+
     def test_get_unknown(self):
         self.assertEqual(None, self.msg.get("lalalala"))
 
+    def test_get_unknown_text(self):
+        self.assertEqual(None, self.msg.text.get("lalalala"))
+
     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")
@@ -571,16 +760,16 @@ class LdbMsgTests(TestCase):
         msg2 = ldb.Message()
         msg2.dn = ldb.Dn(db, "foo=bar")
         self.assertEqual(msg1, msg2)
-        msg1['foo'] = 'bar'
-        msg2['foo'] = 'bar'
+        msg1['foo'] = b'bar'
+        msg2['foo'] = b'bar'
         self.assertEqual(msg1, msg2)
-        msg2['foo'] = 'blie'
+        msg2['foo'] = b'blie'
         self.assertNotEqual(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]:
@@ -592,13 +781,30 @@ class LdbMsgTests(TestCase):
         self.assertRaises(TypeError, ldb.Message.from_dict, l, list(), ldb.FLAG_MOD_REPLACE)
         self.assertRaises(ValueError, ldb.Message.from_dict, l, rec, 0)
         # Message.from_dict expects dictionary with 'dn'
+        err_rec = {"a1": [b"a1-val1", b"a1-val1"]}
+        self.assertRaises(TypeError, ldb.Message.from_dict, l, err_rec, ldb.FLAG_MOD_REPLACE)
+
+    def test_from_dict_text(self):
+        rec = {"dn": "dc=fromdict",
+               "a1": ["a1-val1", "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]:
+            m = ldb.Message.from_dict(l, rec, flags)
+            self.assertEqual(rec["a1"], list(m.text["a1"]))
+            self.assertEqual(flags, m.text["a1"].flags())
+        # check input params
+        self.assertRaises(TypeError, ldb.Message.from_dict, dict(), rec, ldb.FLAG_MOD_REPLACE)
+        self.assertRaises(TypeError, ldb.Message.from_dict, l, list(), ldb.FLAG_MOD_REPLACE)
+        self.assertRaises(ValueError, ldb.Message.from_dict, l, rec, 0)
+        # Message.from_dict expects dictionary with 'dn'
         err_rec = {"a1": ["a1-val1", "a1-val1"]}
         self.assertRaises(TypeError, ldb.Message.from_dict, l, err_rec, ldb.FLAG_MOD_REPLACE)
 
     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"]
@@ -610,50 +816,99 @@ class LdbMsgTests(TestCase):
         self.assertEqual(mto["1"], m["1"])
         self.assertEqual(mto["2"], m["2"])
 
+    def test_copy_add_message_element_text(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")
+        mto = ldb.Message()
+        mto["1"] = m["1"]
+        mto["2"] = m["2"]
+        self.assertEqual(mto["1"], m.text["1"])
+        self.assertEqual(mto["2"], m.text["2"])
+        mto = ldb.Message()
+        mto.add(m["1"])
+        mto.add(m["2"])
+        self.assertEqual(mto.text["1"], m.text["1"])
+        self.assertEqual(mto.text["2"], m.text["2"])
+        self.assertEqual(mto["1"], m["1"])
+        self.assertEqual(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.assertEqual(x, y)
         self.assertNotEqual(x, z)
 
+    def test_cmp_element_text(self):
+        x = ldb.MessageElement([b"foo"])
+        y = ldb.MessageElement(["foo"])
+        self.assertEqual(x, y)
+
     def test_create_iterable(self):
-        x = ldb.MessageElement(["foo"])
-        self.assertEqual(["foo"], list(x))
+        x = ldb.MessageElement([b"foo"])
+        self.assertEqual([b"foo"], list(x))
+        self.assertEqual(["foo"], list(x.text))
 
     def test_repr(self):
-        x = ldb.MessageElement(["foo"])
-        self.assertEqual("MessageElement(['foo'])", repr(x))
-        x = ldb.MessageElement(["foo", "bla"])
+        x = ldb.MessageElement([b"foo"])
+        if PY3:
+            self.assertEqual("MessageElement([b'foo'])", repr(x))
+            self.assertEqual("MessageElement([b'foo']).text", repr(x.text))
+        else:
+            self.assertEqual("MessageElement(['foo'])", repr(x))
+            self.assertEqual("MessageElement(['foo']).text", repr(x.text))
+        x = ldb.MessageElement([b"foo", b"bla"])
         self.assertEqual(2, len(x))
-        self.assertEqual("MessageElement(['foo','bla'])", repr(x))
+        if PY3:
+            self.assertEqual("MessageElement([b'foo',b'bla'])", repr(x))
+            self.assertEqual("MessageElement([b'foo',b'bla']).text", repr(x.text))
+        else:
+            self.assertEqual("MessageElement(['foo','bla'])", repr(x))
+            self.assertEqual("MessageElement(['foo','bla']).text", repr(x.text))
 
     def test_get_item(self):
+        x = ldb.MessageElement([b"foo", b"bar"])
+        self.assertEqual(b"foo", x[0])
+        self.assertEqual(b"bar", x[1])
+        self.assertEqual(b"bar", x[-1])
+        self.assertRaises(IndexError, lambda: x[45])
+
+    def test_get_item_text(self):
         x = ldb.MessageElement(["foo", "bar"])
-        self.assertEqual("foo", x[0])
-        self.assertEqual("bar", x[1])
-        self.assertEqual("bar", x[-1])
+        self.assertEqual("foo", x.text[0])
+        self.assertEqual("bar", x.text[1])
+        self.assertEqual("bar", x.text[-1])
         self.assertRaises(IndexError, lambda: x[45])
 
     def test_len(self):
-        x = ldb.MessageElement(["foo", "bar"])
+        x = ldb.MessageElement([b"foo", b"bar"])
         self.assertEqual(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.assertEqual(y, x)
-        x = ldb.MessageElement(["foo"])
+        x = ldb.MessageElement([b"foo"])
         self.assertNotEqual(y, x)
-        y = ldb.MessageElement(["foo"])
+        y = ldb.MessageElement([b"foo"])
         self.assertEqual(y, x)
 
     def test_extended(self):
-        el = ldb.MessageElement(["456"], ldb.FLAG_MOD_ADD, "bla")
-        self.assertEqual("MessageElement(['456'])", repr(el))
+        el = ldb.MessageElement([b"456"], ldb.FLAG_MOD_ADD, "bla")
+        if PY3:
+            self.assertEqual("MessageElement([b'456'])", repr(el))
+            self.assertEqual("MessageElement([b'456']).text", repr(el.text))
+        else:
+            self.assertEqual("MessageElement(['456'])", repr(el))
+            self.assertEqual("MessageElement(['456']).text", repr(el.text))
+
+    def test_bad_text(self):
+        el = ldb.MessageElement(b'\xba\xdd')
+        self.assertRaises(UnicodeDecodeError, el.text.__getitem__, 0)
 
 
 class ModuleTests(TestCase):
@@ -697,19 +952,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()
-- 
2.1.0


From a3a8d8e495007011a42b8f96ae3b5a0b05cddd59 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori at redhat.com>
Date: Fri, 14 Aug 2015 12:43:41 +0200
Subject: [PATCH 10/10] ldb: Build for two Python versions at once

Signed-off-by: Petr Viktorin <pviktori at redhat.com>
---
 lib/ldb/wscript | 39 +++++++++++++++++++++------------------
 1 file changed, 21 insertions(+), 18 deletions(-)

diff --git a/lib/ldb/wscript b/lib/ldb/wscript
index 9b3429c..55e5d91 100755
--- a/lib/ldb/wscript
+++ b/lib/ldb/wscript
@@ -120,20 +120,28 @@ def build(bld):
         bld.env.PACKAGE_VERSION = VERSION
         bld.env.PKGCONFIGDIR = '${LIBDIR}/pkgconfig'
 
-    if not bld.CONFIG_SET('USING_SYSTEM_PYLDB_UTIL'):
-        bld.SAMBA_LIBRARY('pyldb-util',
-                          deps='ldb',
-                          source='pyldb_util.c',
-                          public_headers='pyldb.h',
-                          public_headers_install=not private_library,
-                          vnum=VERSION,
-                          private_library=private_library,
-                          pc_files='pyldb-util.pc',
-                          pyembed=True,
-                          abi_directory='ABI',
-                          abi_match='pyldb_*')
-
     if not bld.env.disable_python:
+        if not bld.CONFIG_SET('USING_SYSTEM_PYLDB_UTIL'):
+            for env in bld.gen_python_environments(['PKGCONFIGDIR']):
+                name = bld.pyembed_libname('pyldb-util')
+                bld.SAMBA_LIBRARY(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='pyldb-util.pc',
+                                  pyembed=True,
+                                  abi_directory='ABI',
+                                  abi_match='pyldb_*')
+
+                if not bld.CONFIG_SET('USING_SYSTEM_LDB'):
+                    bld.SAMBA_PYTHON('pyldb', 'pyldb.c',
+                                     deps='ldb ' + name,
+                                     realname='ldb.so',
+                                     cflags='-DPACKAGE_VERSION=\"%s\"' % VERSION)
+
         for env in bld.gen_python_environments(['PKGCONFIGDIR']):
             bld.SAMBA_SCRIPT('_ldb_text.py',
                              pattern='_ldb_text.py',
@@ -175,11 +183,6 @@ def build(bld):
         t.env.LDB_VERSION = VERSION
 
 
-        bld.SAMBA_PYTHON('pyldb', 'pyldb.c',
-                         deps='ldb pyldb-util',
-                         realname='ldb.so',
-                         cflags='-DPACKAGE_VERSION=\"%s\"' % VERSION)
-
         bld.SAMBA_MODULE('ldb_paged_results',
                          'modules/paged_results.c',
                          init_function='ldb_paged_results_init',
-- 
2.1.0



More information about the samba-technical mailing list