
Turns out the LDAP module for Python breaks the API while developing version 2.4 -with no backwards compability, but a workaround is relatively easy. If, like me, you need to be able to run code on platforms traditionally "slow" in adopting the latest and greatest, as well as those that are traditionally, relatively "fast", here's an idea:
# Catch python-ldap-2.4 changes
from distutils import version
if version.StrictVersion('2.4.0') <= version.StrictVersion(ldap.__version__):
LDAP_CONTROL_PAGED_RESULTS = ldap.CONTROL_PAGEDRESULTS
else:
LDAP_CONTROL_PAGED_RESULTS = ldap.LDAP_CONTROL_PAGE_OID
class SimplePagedResultsControl(ldap.controls.SimplePagedResultsControl):
"""
Python LDAP 2.4 and later breaks the API. This is an abstraction class
so that we can handle either.
"""
def __init__(self, page_size=0, cookie=''):
if version.StrictVersion('2.4.0') <= version.StrictVersion(ldap.__version__):
ldap.controls.SimplePagedResultsControl.__init__(
self,
size=page_size,
cookie=cookie
)
else:
ldap.controls.SimplePagedResultsControl.__init__(
self,
LDAP_CONTROL_PAGED_RESULTS,
critical,
(page_size, '')
)
def cookie(self):
if version.StrictVersion('2.4.0') <= version.StrictVersion(ldap.__version__):
return self.cookie
else:
return self.controlValue[1]
def size(self):
if version.StrictVersion('2.4.0') <= version.StrictVersion(ldap.__version__):
return self.size
else:
return self.controlValue[0]
Where 'self.ldap' is the LDAP object;
def _search(self,
base_dn,
scope=ldap.SCOPE_SUBTREE,
filterstr="(objectClass=*)",
attrlist=None,
attrsonly=0,
timeout=-1
):
_results = []
page_size = 500
critical = True
server_page_control = SimplePagedResultsControl(page_size=page_size)
_search = self.ldap.search_ext(
base_dn,
scope=scope,
filterstr=filterstr,
attrlist=attrlist,
attrsonly=attrsonly,
serverctrls=[server_page_control]
)
pages = 0
while True:
pages += 1
try:
(
_result_type,
_result_data,
_result_msgid,
_result_controls
) = self.ldap.result3(_search)
except ldap.NO_SUCH_OBJECT, e:
log.warning(_("Object %s searched no longer exists") %(base_dn))
break
_results.extend(_result_data)
if (pages % 2) == 0:
log.debug(_("%d results...") %(len(_results)))
pctrls = [
c for c in _result_controls
if c.controlType == LDAP_CONTROL_PAGED_RESULTS
]
if pctrls:
size = pctrls[0].size()
cookie = pctrls[0].cookie()
if cookie:
server_page_control.cookie = cookie
_search = self.ldap.search_ext(
base_dn,
scope=scope,
filterstr=filterstr,
attrlist=attrlist,
attrsonly=attrsonly,
serverctrls=[server_page_control]
)
else:
# TODO: Error out more verbose
break
else:
# TODO: Error out more verbose
print "Warning: Server ignores RFC 2696 control."
break
return _results
Or something like that ;-)
Comments
Trying to get this to work
Hi,
I came across this post when I tried to implement some LDAP search using Paging, using Python 2.6 and python-ldap 2.4.8 on Windows64. It does seem like it could be the solution to by troubles, but I am having trouble getting it to work completely.
I've adapted the code for my own script, but am getting this error message down the line:
Traceback (most recent call last):
File "ldap_paged_results.py", line 100, in <module>
size = pctrls[0].size()
TypeError: 'int' object is not callable
Now, I believe this may be stupid me not knowing where exactly to put the top portion of the code you posted - which is meant to handle the discrepancies between the versions. What I did was just putting it into my script at the beginning - which doesn't seem to do the trick really. How do I actually make sure it is in effect?
Any comments would be greatly appreciated,
Bastian