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 = (
  ('&','&amp;'),
  ('<','&lt;'),
  ('"','&quot;'),
  ("'",'&apos;'),
)

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