New method ldap.async.AsyncSearchHandler.afterFirstResult()
This commit is contained in:
		
							
								
								
									
										317
									
								
								Lib/ldap/async.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										317
									
								
								Lib/ldap/async.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,317 @@ | ||||
| """ | ||||
| ldap.async - handle async LDAP operations | ||||
|  | ||||
| See http://www.python-ldap.org/ for details. | ||||
|  | ||||
| \$Id: async.py,v 1.33 2013/09/21 03:55:38 stroeder Exp $ | ||||
|  | ||||
| Python compability note: | ||||
| Tested on Python 2.0+ but should run on Python 1.5.x. | ||||
| """ | ||||
|  | ||||
| import ldap | ||||
|  | ||||
| from ldap import __version__ | ||||
|  | ||||
|  | ||||
| _searchResultTypes={ | ||||
|   ldap.RES_SEARCH_ENTRY:None, | ||||
|   ldap.RES_SEARCH_RESULT:None, | ||||
|   ldap.RES_SEARCH_REFERENCE:None, | ||||
| } | ||||
|  | ||||
| _entryResultTypes={ | ||||
|   ldap.RES_SEARCH_ENTRY:None, | ||||
|   ldap.RES_SEARCH_RESULT:None, | ||||
| } | ||||
|  | ||||
|  | ||||
| class WrongResultType(Exception): | ||||
|  | ||||
|   def __init__(self,receivedResultType,expectedResultTypes): | ||||
|     self.receivedResultType = receivedResultType | ||||
|     self.expectedResultTypes = expectedResultTypes | ||||
|     Exception.__init__(self) | ||||
|  | ||||
|   def __str__(self): | ||||
|     return 'Received wrong result type %s (expected one of %s).' % ( | ||||
|       self.receivedResultType, | ||||
|       ', '.join(self.expectedResultTypes), | ||||
|     ) | ||||
|  | ||||
|  | ||||
| class AsyncSearchHandler: | ||||
|   """ | ||||
|   Class for stream-processsing LDAP search results | ||||
|  | ||||
|   Arguments: | ||||
|  | ||||
|   l | ||||
|     LDAPObject instance | ||||
|   """ | ||||
|  | ||||
|   def __init__(self,l): | ||||
|     self._l = l | ||||
|     self._msgId = None | ||||
|     self._afterFirstResult = 1 | ||||
|  | ||||
|   def startSearch( | ||||
|     self, | ||||
|     searchRoot, | ||||
|     searchScope, | ||||
|     filterStr, | ||||
|     attrList=None, | ||||
|     attrsOnly=0, | ||||
|     timeout=-1, | ||||
|     sizelimit=0, | ||||
|     serverctrls=None, | ||||
|     clientctrls=None | ||||
|   ): | ||||
|     """ | ||||
|     searchRoot | ||||
|         See parameter base of method LDAPObject.search() | ||||
|     searchScope | ||||
|         See parameter scope of method LDAPObject.search() | ||||
|     filterStr | ||||
|         See parameter filter of method LDAPObject.search() | ||||
|     attrList=None | ||||
|         See parameter attrlist of method LDAPObject.search() | ||||
|     attrsOnly | ||||
|         See parameter attrsonly of method LDAPObject.search() | ||||
|     timeout | ||||
|         Maximum time the server shall use for search operation | ||||
|     sizelimit | ||||
|         Maximum number of entries a server should return | ||||
|         (request client-side limit) | ||||
|     serverctrls | ||||
|         list of server-side LDAP controls | ||||
|     clientctrls | ||||
|         list of client-side LDAP controls | ||||
|     """ | ||||
|     self._msgId = self._l.search_ext( | ||||
|       searchRoot,searchScope,filterStr, | ||||
|       attrList,attrsOnly,serverctrls,clientctrls,timeout,sizelimit | ||||
|     ) | ||||
|     self._afterFirstResult = 1 | ||||
|     return # startSearch() | ||||
|  | ||||
|   def preProcessing(self): | ||||
|     """ | ||||
|     Do anything you want after starting search but | ||||
|     before receiving and processing results | ||||
|     """ | ||||
|  | ||||
|   def afterFirstResult(self): | ||||
|     """ | ||||
|     Do anything you want right after successfully receiving but before  | ||||
|     processing first result | ||||
|     """ | ||||
|  | ||||
|   def postProcessing(self): | ||||
|     """ | ||||
|     Do anything you want after receiving and processing all results | ||||
|     """ | ||||
|  | ||||
|   def processResults(self,ignoreResultsNumber=0,processResultsCount=0,timeout=-1): | ||||
|     """ | ||||
|     ignoreResultsNumber | ||||
|         Don't process the first ignoreResultsNumber results. | ||||
|     processResultsCount | ||||
|         If non-zero this parameters indicates the number of results | ||||
|         processed is limited to processResultsCount. | ||||
|     timeout | ||||
|         See parameter timeout of ldap.LDAPObject.result() | ||||
|     """ | ||||
|     self.preProcessing() | ||||
|     result_counter = 0 | ||||
|     end_result_counter = ignoreResultsNumber+processResultsCount | ||||
|     go_ahead = 1 | ||||
|     partial = 0 | ||||
|     self.beginResultsDropped = 0 | ||||
|     self.endResultBreak = result_counter | ||||
|     try: | ||||
|       result_type,result_list = None,None | ||||
|       while go_ahead: | ||||
|         while result_type is None and not result_list: | ||||
|           result_type,result_list,result_msgid,result_serverctrls = self._l.result3(self._msgId,0,timeout) | ||||
|           if self._afterFirstResult: | ||||
|             self.afterFirstResult() | ||||
|             self._afterFirstResult = 0 | ||||
|         if not result_list: | ||||
|           break | ||||
|         if not _searchResultTypes.has_key(result_type): | ||||
|           raise WrongResultType(result_type,_searchResultTypes.keys()) | ||||
|         # Loop over list of search results | ||||
|         for result_item in result_list: | ||||
|           if result_counter<ignoreResultsNumber: | ||||
|             self.beginResultsDropped = self.beginResultsDropped+1 | ||||
|           elif processResultsCount==0 or result_counter<end_result_counter: | ||||
|             self._processSingleResult(result_type,result_item) | ||||
|           else: | ||||
|             go_ahead = 0 # break-out from while go_ahead | ||||
|             partial = 1 | ||||
|             break # break-out from this for-loop | ||||
|           result_counter = result_counter+1 | ||||
|         result_type,result_list = None,None | ||||
|         self.endResultBreak = result_counter | ||||
|     finally: | ||||
|       if partial and self._msgId!=None: | ||||
|         self._l.abandon(self._msgId) | ||||
|     self.postProcessing() | ||||
|     return partial # processResults() | ||||
|  | ||||
|   def _processSingleResult(self,resultType,resultItem): | ||||
|     """ | ||||
|     Process single entry | ||||
|  | ||||
|     resultType | ||||
|         result type | ||||
|     resultItem | ||||
|         Single item of a result list | ||||
|     """ | ||||
|     pass | ||||
|  | ||||
|  | ||||
| class List(AsyncSearchHandler): | ||||
|   """ | ||||
|   Class for collecting all search results. | ||||
|  | ||||
|   This does not seem to make sense in the first place but think | ||||
|   of retrieving exactly a certain portion of the available search | ||||
|   results. | ||||
|   """ | ||||
|  | ||||
|   def __init__(self,l): | ||||
|     AsyncSearchHandler.__init__(self,l) | ||||
|     self.allResults = [] | ||||
|  | ||||
|   def _processSingleResult(self,resultType,resultItem): | ||||
|     self.allResults.append((resultType,resultItem)) | ||||
|  | ||||
|  | ||||
| class Dict(AsyncSearchHandler): | ||||
|   """ | ||||
|   Class for collecting all search results into a dictionary {dn:entry} | ||||
|   """ | ||||
|  | ||||
|   def __init__(self,l): | ||||
|     AsyncSearchHandler.__init__(self,l) | ||||
|     self.allEntries = {} | ||||
|  | ||||
|   def _processSingleResult(self,resultType,resultItem): | ||||
|     if _entryResultTypes.has_key(resultType): | ||||
|       # Search continuations are ignored | ||||
|       dn,entry = resultItem | ||||
|       self.allEntries[dn] = entry | ||||
|  | ||||
|  | ||||
| class IndexedDict(Dict): | ||||
|   """ | ||||
|   Class for collecting all search results into a dictionary {dn:entry} | ||||
|   and maintain case-sensitive equality indexes to entries | ||||
|   """ | ||||
|  | ||||
|   def __init__(self,l,indexed_attrs=None): | ||||
|     Dict.__init__(self,l) | ||||
|     self.indexed_attrs = indexed_attrs or tuple() | ||||
|     self.index = {}.fromkeys(self.indexed_attrs,{}) | ||||
|  | ||||
|   def _processSingleResult(self,resultType,resultItem): | ||||
|     if _entryResultTypes.has_key(resultType): | ||||
|       # Search continuations are ignored | ||||
|       dn,entry = resultItem | ||||
|       self.allEntries[dn] = entry | ||||
|       for a in self.indexed_attrs: | ||||
|         if entry.has_key(a): | ||||
|           for v in entry[a]: | ||||
|             try: | ||||
|               self.index[a][v].append(dn) | ||||
|             except KeyError: | ||||
|               self.index[a][v] = [ dn ] | ||||
|  | ||||
|  | ||||
| class FileWriter(AsyncSearchHandler): | ||||
|   """ | ||||
|   Class for writing a stream of LDAP search results to a file object | ||||
|  | ||||
|   Arguments: | ||||
|   l | ||||
|     LDAPObject instance | ||||
|   f | ||||
|     File object instance where the LDIF data is written to | ||||
|   """ | ||||
|  | ||||
|   def __init__(self,l,f,headerStr='',footerStr=''): | ||||
|     AsyncSearchHandler.__init__(self,l) | ||||
|     self._f = f | ||||
|     self.headerStr = headerStr | ||||
|     self.footerStr = footerStr | ||||
|  | ||||
|   def preProcessing(self): | ||||
|     """ | ||||
|     The headerStr is written to output after starting search but | ||||
|     before receiving and processing results. | ||||
|     """ | ||||
|     self._f.write(self.headerStr) | ||||
|  | ||||
|   def postProcessing(self): | ||||
|     """ | ||||
|     The footerStr is written to output after receiving and | ||||
|     processing results. | ||||
|     """ | ||||
|     self._f.write(self.footerStr) | ||||
|  | ||||
|  | ||||
| class LDIFWriter(FileWriter): | ||||
|   """ | ||||
|   Class for writing a stream LDAP search results to a LDIF file | ||||
|  | ||||
|   Arguments: | ||||
|  | ||||
|   l | ||||
|     LDAPObject instance | ||||
|   writer_obj | ||||
|     Either a file-like object or a ldif.LDIFWriter instance used for output | ||||
|   """ | ||||
|  | ||||
|   def __init__(self,l,writer_obj,headerStr='',footerStr=''): | ||||
|     import ldif | ||||
|     if isinstance(writer_obj,ldif.LDIFWriter): | ||||
|       self._ldif_writer = writer_obj | ||||
|     else: | ||||
|       self._ldif_writer = ldif.LDIFWriter(writer_obj) | ||||
|     FileWriter.__init__(self,l,self._ldif_writer._output_file,headerStr,footerStr) | ||||
|  | ||||
|   def _processSingleResult(self,resultType,resultItem): | ||||
|     if _entryResultTypes.has_key(resultType): | ||||
|       # Search continuations are ignored | ||||
|       dn,entry = resultItem | ||||
|       self._ldif_writer.unparse(dn,entry) | ||||
|  | ||||
|  | ||||
| class DSMLWriter(FileWriter): | ||||
|   """ | ||||
|   Class for writing a stream LDAP search results to a DSML file | ||||
|  | ||||
|   Arguments: | ||||
|  | ||||
|   l | ||||
|     LDAPObject instance | ||||
|   writer_obj | ||||
|     Either a file-like object or a dsml.DSMLWriter instance used for output | ||||
|   """ | ||||
|  | ||||
|   def __init__(self,l,writer_obj,headerStr='',footerStr=''): | ||||
|     import dsml | ||||
|     if isinstance(writer_obj,dsml.DSMLWriter): | ||||
|       self._dsml_writer = writer_obj | ||||
|     else: | ||||
|       self._dsml_writer = dsml.DSMLWriter(writer_obj) | ||||
|     FileWriter.__init__(self,l,self._dsml_writer._output_file,headerStr,footerStr) | ||||
|  | ||||
|   def _processSingleResult(self,resultType,resultItem): | ||||
|     if _entryResultTypes.has_key(resultType): | ||||
|       # Search continuations are ignored | ||||
|       dn,entry = resultItem | ||||
|       self._dsml_writer.unparse(dn,entry) | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 stroeder
					stroeder