'Returns a 2-tuple of all must and may attributes including
all inherited attributes of superior object classes
by walking up classes along the SUP attribute.
The attributes are stored in a ldap.cidict.cidict dictionary.
object_class_list
list of strings specifying object class names or OIDs
attr_type_filter
list of 2-tuples containing lists of class attributes
which has to be matched
raise_keyerror
All KeyError exceptions for non-existent schema elements
are ignored
ignore_dit_content_rule
A DIT content rule governing the structural object class
is ignored'

def attribute_types(self, object_class_list, attr_type_filter=None, raise_keyerror=1, ignore_dit_content_rule=0):

    AttributeType = ldap.schema.AttributeType
    ObjectClass = ldap.schema.ObjectClass
    object_class_oids = [self.name2oid[ObjectClass].get(o, o) for o in object_class_list]
    oid_cache = {}
    (r_must, r_may) = (ldap.cidict.cidict(), ldap.cidict.cidict())
    if ('1.3.6.1.4.1.1466.101.120.111' in object_class_oids):
        for at_obj in self.sed[AttributeType].values():
            r_may[at_obj.oid] = at_obj
    while object_class_oids:
        object_class_oid = object_class_oids.pop(0)
        if oid_cache.has_key(object_class_oid):
            continue
        oid_cache[object_class_oid] = None
        try:
            object_class = self.sed[ObjectClass][object_class_oid]
        except KeyError:
            if raise_keyerror:
                raise
            continue
        assert isinstance(object_class, ObjectClass)
        assert hasattr(object_class, 'must'), ValueError(object_class_oid)
        assert hasattr(object_class, 'may'), ValueError(object_class_oid)
        for a in object_class.must:
            try:
                at_obj = self.sed[AttributeType][self.name2oid[AttributeType].get(a, a)]
            except KeyError:
                if raise_keyerror:
                    raise
                else:
                    r_must[a] = None
            else:
                r_must[at_obj.oid] = at_obj
        for a in object_class.may:
            try:
                at_obj = self.sed[AttributeType][self.name2oid[AttributeType].get(a, a)]
            except KeyError:
                if raise_keyerror:
                    raise
                else:
                    r_may[a] = None
            else:
                r_may[at_obj.oid] = at_obj
        object_class_oids.extend([self.name2oid[ObjectClass].get(o, o) for o in object_class.sup])
    for a in r_may.keys():
        if r_must.has_key(a):
            del r_may[a]
    if (not ignore_dit_content_rule):
        structural_oc = self.get_structural_oc(object_class_list)
        if structural_oc:
            dit_content_rule = self.get_obj(DITContentRule, structural_oc)
            if dit_content_rule:
                for a in dit_content_rule.must:
                    try:
                        at_obj = self.sed[AttributeType][self.name2oid[AttributeType].get(a, a)]
                    except KeyError:
                        if raise_keyerror:
                            raise
                        else:
                            r_must[a] = None
                    else:
                        r_must[at_obj.oid] = at_obj
                for a in dit_content_rule.may:
                    try:
                        at_obj = self.sed[AttributeType][self.name2oid[AttributeType].get(a, a)]
                    except KeyError:
                        if raise_keyerror:
                            raise
                        else:
                            r_may[a] = None
                    else:
                        r_may[at_obj.oid] = at_obj
                for a in dit_content_rule.nots:
                    a_oid = self.name2oid[AttributeType].get(a, a)
                    if (not r_must.has_key(a_oid)):
                        try:
                            at_obj = self.sed[AttributeType][a_oid]
                        except KeyError:
                            if raise_keyerror:
                                raise
                        else:
                            try:
                                del r_must[at_obj.oid]
                            except KeyError:
                                pass
                            try:
                                del r_may[at_obj.oid]
                            except KeyError:
                                pass
    if attr_type_filter:
        for l in [r_must, r_may]:
            for a in l.keys():
                for (afk, afv) in attr_type_filter:
                    try:
                        schema_attr_type = self.sed[AttributeType][a]
                    except KeyError:
                        if raise_keyerror:
                            raise KeyError, ('No    attribute    type    found    in    sub    schema    by    name    %s' % a)
                        del l[a]
                        break
                    else:
                        if (not (getattr(schema_attr_type, afk) in afv)):
                            del l[a]
                            break
    return (r_must, r_may)
