Skip to content

Commit d3d0dc3

Browse files
ldapobject: store message id for exceptions raised from result4()
Otherwise calling result4() can return exceptions like NO_SUCH_OBJECT or COMPARE_TRUE without the caller being able to find the matching asynchronous operation that failed. This change adds the message id as an argument to the exception.
1 parent 860ea96 commit d3d0dc3

File tree

5 files changed

+39
-2
lines changed

5 files changed

+39
-2
lines changed

Doc/reference/ldap.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,14 @@ The module defines the following exceptions:
321321
is set to a truncated form of the name provided or alias dereferenced
322322
for the lowest entry (object or alias) that was matched.
323323

324+
For use in asynchronous operations an optional field :py:const:`msg_id` is
325+
also set in the dictionary in cases where the exception can be associated
326+
with a request. This can be used in asynchronous code where
327+
:py:meth:`result()` returns an exception that is effectively the result of a
328+
previously started asynchronous operation. For example, this is the case for
329+
asynchronous (:py:meth:`compare()`), where the boolean result is always
330+
raised as an exception (:py:exc:`COMPARE_TRUE` or :py:exc:`COMPARE_FALSE`).
331+
324332

325333
.. py:exception:: ADMINLIMIT_EXCEEDED
326334

Modules/LDAPObject.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,7 +1174,7 @@ l_ldap_result4(LDAPObject *self, PyObject *args)
11741174
ldap_controls_free(serverctrls);
11751175
ldap_msgfree(msg);
11761176
Py_XDECREF(valuestr);
1177-
return LDAPerror(self->ldap, e);
1177+
return LDAPerror_with_message_id(self->ldap, e, res_msgid);
11781178
}
11791179

11801180
if (!(pyctrls = LDAPControls_to_List(serverctrls))) {
@@ -1186,7 +1186,7 @@ l_ldap_result4(LDAPObject *self, PyObject *args)
11861186
ldap_controls_free(serverctrls);
11871187
ldap_msgfree(msg);
11881188
Py_XDECREF(valuestr);
1189-
return LDAPerror(self->ldap, "LDAPControls_to_List");
1189+
return LDAPerror_with_message_id(self->ldap, "LDAPControls_to_List", res_msgid);
11901190
}
11911191
ldap_controls_free(serverctrls);
11921192

Modules/constants.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ LDAPerr(int errnum)
4949
/* Convert an LDAP error into an informative python exception */
5050
PyObject *
5151
LDAPerror(LDAP *l, char *msg)
52+
{
53+
return LDAPerror_with_message_id(l, msg, -1);
54+
}
55+
56+
/* Convert an LDAP error into an informative python exception containing the message id */
57+
PyObject *
58+
LDAPerror_with_message_id(LDAP *l, char *msg, int msg_id)
5259
{
5360
if (l == NULL) {
5461
PyErr_SetFromErrno(LDAPexception_class);
@@ -93,6 +100,13 @@ LDAPerror(LDAP *l, char *msg)
93100
Py_XDECREF(pyerrno);
94101
}
95102

103+
if (msg_id > 0) {
104+
PyObject *pymsg_id = PyInt_FromLong(msg_id);
105+
if (pymsg_id)
106+
PyDict_SetItemString(info, "msg_id", pymsg_id);
107+
Py_XDECREF(pymsg_id);
108+
}
109+
96110
if (ldap_get_option(l, LDAP_OPT_MATCHED_DN, &matched) >= 0
97111
&& matched != NULL) {
98112
if (*matched != '\0') {

Modules/constants.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ extern PyObject *LDAPconstant(int);
1212

1313
extern PyObject *LDAPexception_class;
1414
extern PyObject *LDAPerror(LDAP *, char *msg);
15+
extern PyObject *LDAPerror_with_message_id(LDAP *, char *msg, int msg_id);
1516
PyObject *LDAPerr(int errnum);
1617

1718
#ifndef LDAP_CONTROL_PAGE_OID

Tests/t_ldapobject.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,20 @@ def test_compare_s_invalidattr(self):
673673
with self.assertRaises(ldap.UNDEFINED_TYPE):
674674
result = l.compare_s('cn=Foo1,%s' % base, 'invalidattr', b'invalid')
675675

676+
def test_compare_true_exception_contains_message_id(self):
677+
base = self.server.suffix
678+
l = self._ldap_conn
679+
msg_id = l.compare('cn=Foo1,%s' % base, 'cn', b'Foo1')
680+
with self.assertRaises(ldap.COMPARE_TRUE) as cm:
681+
l.result()
682+
self.assertEqual(cm.exception.args[0]["msg_id"], msg_id)
683+
684+
def test_async_search_no_such_object_exception_contains_message_id(self):
685+
msg_id = self._ldap_conn.search("CN=XXX", ldap.SCOPE_SUBTREE)
686+
with self.assertRaises(ldap.NO_SUCH_OBJECT) as cm:
687+
self._ldap_conn.result()
688+
self.assertEqual(cm.exception.args[0]["msg_id"], msg_id)
689+
676690

677691
class Test01_ReconnectLDAPObject(Test00_SimpleLDAPObject):
678692
"""

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy