DSML & async.py
Cristian Sebastian Rocha
crocha at dc.uba.ar
Thu Nov 13 21:05:16 CET 2003
Hi,
Traying to use the DSML writer as the LDIF writer with the async module, with
the following code, I changed some lines in the dsml.py and async.py files.
s = ldap.async.DSMLWriter(
ldap.initialize('ldap://%s' % host),
sys.stdout
)
s.startSearch(
base,
ldap.SCOPE_SUBTREE,
'(objectClass=*)',
)
I checked out the files from the CVS and I changed them. It's working fine. For
commit them in the CVS, I attached them in the mail.
Bye,
Cristian.
PD: Sorry by my bad english. I hope you understand it ;)
-------------- next part --------------
"""
ldap.async - handle async LDAP operations
written by Michael Stroeder <michael at stroeder.com>
See http://python-ldap.sourceforge.net for details.
\$Id: async.py,v 1.18 2003/06/01 15:38:46 stroeder Exp $
Python compability note:
Tested on Python 2.0+ but should run on Python 1.5.x.
"""
__version__ = '0.0.2'
_searchResultTypes={
'RES_SEARCH_ENTRY':None,
'RES_SEARCH_RESULT':None,
'RES_SEARCH_REFERENCE':None
}
_entryResultTypes={
'RES_SEARCH_ENTRY':None,
'RES_SEARCH_RESULT':None,
}
class WrongResultType(Exception):
def __init__(self,receivedResultType,expectedResultTypes):
self.receivedResultType = receivedResultType
self.expectedResultTypes = expectedResultTypes
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
"""
def __init__(self,l):
"""
Initialize a StreamResultHandler
Parameters:
l
LDAPObject instance
"""
self._l = l
self._msgId = None
def startSearch(
self,
searchRoot,
searchScope,
filterStr,
attrList=None,
attrsOnly=0,
timeout=-1,
sizelimit=0
):
"""
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)
"""
self._msgId = self._l.search_ext(
searchRoot,searchScope,filterStr,
attrList,attrsOnly,None,None,timeout,sizelimit
)
return # startSearch()
def preProcessing(self):
"""
Do anything you want after starting search but
before receiving and processing results
"""
def postProcessing(self):
"""
Do anything you want after receiving and processing 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 = self._l.result(self._msgId,0,timeout)
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 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 FileWriter(AsyncSearchHandler):
"""
Class for writing a stream of LDAP search results to a file object
"""
def __init__(self,l,f,headerStr='',footerStr=''):
"""
Initialize a StreamResultHandler
Parameters:
l
LDAPObject instance
f
File object instance where the LDIF data is written to
"""
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
"""
def __init__(self,l,writer_obj,headerStr='',footerStr=''):
"""
Initialize a StreamResultHandler
Parameters:
l
LDAPObject instance
writer_obj
Either a file-like object or a ldif.LDIFWriter instance
used for output
"""
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
"""
def __init__(self,l,writer_obj,headerStr='',footerStr=''):
"""
Initialize a StreamResultHandler
Parameters:
l
LDAPObject instance
writer_obj
Either a file-like object or a ldif.DSMLWriter instance
used for output
"""
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)
-------------- next part --------------
"""
dsml - generate and parse DSMLv1 data
(see http://www.oasis-open.org/committees/dsml/)
written by Michael Stroeder <michael at stroeder.com>
See http://python-ldap.sourceforge.net for details.
$Id: dsml.py,v 1.5 2003/08/13 22:02:48 stroeder Exp $
Python compability note:
Tested with Python 2.0+.
"""
__version__ = '0.5.1'
import string,base64
def list_dict(l):
"""
return a dictionary with all items of l being the keys of the dictionary
"""
d = {}
for i in l:
d[i]=None
return d
special_entities = (
('&','&'),
('<','<'),
('"','"'),
("'",'''),
)
def replace_char(s):
for char,entity in special_entities:
s = string.replace(s,char,entity)
return s
class DSMLWriter:
def __init__(
self,f,base64_attrs=[],dsml_comment='',indent=' '
):
"""
Parameters:
f
File object for output.
base64_attrs
Attribute types to be base64-encoded.
dsml_comment
Text placed in comment lines behind <dsml:dsml>.
indent
String used for indentiation of next nested level.
"""
self._output_file = f
self._base64_attrs = list_dict(map(string.lower,base64_attrs))
self._dsml_comment = dsml_comment
self._indent = indent
def _needs_base64_encoding(self,attr_type,attr_value):
if self._base64_attrs:
return self._base64_attrs.has_key(string.lower(attr_type))
else:
try:
unicode(attr_value,'utf-8')
except UnicodeError:
return 1
else:
return 0
def writeHeader(self):
"""
Write the header
"""
self._output_file.write('\n'.join([
'<?xml version="1.0" encoding="UTF-8"?>',
'<!DOCTYPE root PUBLIC "dsml.dtd" "http://www.dsml.org/1.0/dsml.dtd">',
'<dsml:dsml xmlns:dsml="http://www.dsml.org/DSML">',
'%s<dsml:directory-entries>\n' % (self._indent),
])
)
if self._dsml_comment:
self._output_file.write('%s<!--\n' % (self._indent))
self._output_file.write('%s%s\n' % (self._indent,self._dsml_comment))
self._output_file.write('%s-->\n' % (self._indent))
def writeFooter(self):
"""
Write the footer
"""
self._output_file.write('%s</dsml:directory-entries>\n' % (self._indent))
self._output_file.write('</dsml:dsml>\n')
def unparse(self,dn,entry):
"""
dn
string-representation of distinguished name
entry
dictionary holding the LDAP entry {attr:data}
"""
# Write line dn: first
self._output_file.write(
'%s<dsml:entry dn="%s">\n' % (
self._indent*2,replace_char(dn)
)
)
objectclasses = entry.get('objectclass',entry.get('objectClass',[]))
self._output_file.write('%s<dsml:objectclass>\n' % (self._indent*3))
for oc in objectclasses:
self._output_file.write('%s<dsml:oc-value>%s</dsml:oc-value>\n' % (self._indent*4,oc))
self._output_file.write('%s</dsml:objectclass>\n' % (self._indent*3))
attr_types = entry.keys()[:]
try:
attr_types.remove('objectclass')
attr_types.remove('objectClass')
except ValueError:
pass
attr_types.sort()
for attr_type in attr_types:
self._output_file.write('%s<dsml:attr name="%s">\n' % (self._indent*3,attr_type))
for attr_value_item in entry[attr_type]:
needs_base64_encoding = self._needs_base64_encoding(
attr_type,attr_value_item
)
if needs_base64_encoding:
attr_value_item = base64.encodestring(attr_value_item)
else:
attr_value_item = replace_char(attr_value_item)
self._output_file.write('%s<dsml:value%s>\n' % (
self._indent*4,
' encoding="base64"'*needs_base64_encoding
)
)
self._output_file.write('%s%s\n' % (
self._indent*5,
attr_value_item
)
)
self._output_file.write('%s</dsml:value>\n' % (
self._indent*4,
)
)
self._output_file.write('%s</dsml:attr>\n' % (self._indent*3))
self._output_file.write('%s</dsml:entry>\n' % (self._indent*2))
return
try:
import xml.sax,xml.sax.handler
except ImportError:
pass
else:
class DSMLv1Handler(xml.sax.handler.ContentHandler):
"""
Content handler class for DSMLv1
"""
def __init__(self,parser_instance):
self._parser_instance = parser_instance
def startDocument(self):
pass
def endDocument(self):
pass
def startElement(self,raw_name,attrs):
assert raw_name.startswith(''),'Illegal name'
name = raw_name[5:]
if name=='dsml':
pass
elif name=='directory-entries':
self._parsing_entries = 1
elif name=='entry':
self._dn = attrs['dn']
self._entry = {}
elif name=='attr':
self._attr_type = attrs['name'].encode('utf-8')
self._attr_values = []
elif name=='value':
self._attr_value = ''
self._base64_encoding = attrs.get('encoding','').lower()=='base64'
# Handle object class tags
elif name=='objectclass':
self._object_classes = []
elif name=='oc-value':
self._oc_value = ''
# Unhandled tags
else:
raise ValueError,'Unknown tag' % (raw_name)
def endElement(self,raw_name):
assert raw_name.startswith('dsml:'),'Illegal name'
name = raw_name[5:]
if name=='dsml':
pass
elif name=='directory-entries':
self._parsing_entries = 0
elif name=='entry':
self._parser_instance.handle(self._dn,self._entry)
del self._dn
del self._entry
elif name=='attr':
self._entry[self._attr_type] = self._attr_values
del self._attr_type
del self._attr_values
elif name=='value':
if self._base64_encoding:
attr_value = base64.decodestring(self._attr_value.strip())
else:
attr_value = self._attr_value.strip().encode('utf-8')
self._attr_values.append(attr_value)
del attr_value
del self._attr_value
del self._base64_encoding
# Handle object class tags
elif name=='objectclass':
self._entry['objectClass'] = self._object_classes
del self._object_classes
elif name=='oc-value':
self._object_classes.append(self._oc_value.strip().encode('utf-8'))
del self._oc_value
# Unhandled tags
else:
raise ValueError,'Unknown tag' % (raw_name)
def characters(self,ch):
if self.__dict__.has_key('_oc_value'):
self._oc_value = self._oc_value + ch
elif self.__dict__.has_key('_attr_value'):
self._attr_value = self._attr_value + ch
else:
pass
class DSMLParser:
"""
Base class for a DSMLv1 parser. Applications should sub-class this
class and override method handle() to implement something meaningful.
Public class attributes:
records_read
Counter for records processed so far
"""
def __init__(
self,
input_file,
ContentHandlerClass,
ignored_attr_types=None,
max_entries=0,
line_sep='\n'
):
"""
Parameters:
input_file
File-object to read the LDIF input from
ignored_attr_types
Attributes with these attribute type names will be ignored.
max_entries
If non-zero specifies the maximum number of entries to be
read from f.
line_sep
String used as line separator
"""
self._input_file = input_file
self._max_entries = max_entries
self._ignored_attr_types = list_dict(map(string.lower,(ignored_attr_types or [])))
self._current_record = None,None
self.records_read = 0
self._parser = xml.sax.make_parser()
self._parser.setFeature(xml.sax.handler.feature_namespaces,0)
content_handler = ContentHandlerClass(self)
self._parser.setContentHandler(content_handler)
def handle(self,*args,**kwargs):
"""
Process a single content LDIF record. This method should be
implemented by applications using LDIFParser.
"""
import pprint
pprint.pprint(args)
pprint.pprint(kwargs)
def parse(self):
"""
Continously read and parse DSML records
"""
self._parser.parse(self._input_file)
More information about the python-ldap
mailing list