Make LDAPEntry a wrapper around dict rather than a dict subclass.

https://fedorahosted.org/freeipa/ticket/3521
This commit is contained in:
Jan Cholasta 2013-02-26 11:27:55 +01:00 committed by Martin Kosek
parent b1bffb5eca
commit 4f0814d7c0

View File

@ -25,6 +25,7 @@ import shutil
from decimal import Decimal from decimal import Decimal
from copy import deepcopy from copy import deepcopy
import contextlib import contextlib
import collections
import ldap import ldap
import ldap.sasl import ldap.sasl
@ -613,8 +614,8 @@ class IPASimpleLDAPObject(object):
# r = result[0] # r = result[0]
# r[0] == r.dn # r[0] == r.dn
# r[1] == r.data # r[1] == r.data
class LDAPEntry(dict): class LDAPEntry(collections.MutableMapping):
__slots__ = ('_conn', '_dn', '_names', '_orig') __slots__ = ('_conn', '_dn', '_names', '_data', '_orig')
def __init__(self, _conn, _dn=None, _obj=None, **kwargs): def __init__(self, _conn, _dn=None, _obj=None, **kwargs):
""" """
@ -632,32 +633,34 @@ class LDAPEntry(dict):
Keyword arguments can be used to override values of specific attributes. Keyword arguments can be used to override values of specific attributes.
""" """
super(LDAPEntry, self).__init__()
if isinstance(_conn, LDAPEntry): if isinstance(_conn, LDAPEntry):
assert _dn is None assert _dn is None
_dn = _conn _dn = _conn
_conn = _conn._conn _conn = _conn._conn
assert isinstance(_conn, IPASimpleLDAPObject)
if isinstance(_dn, LDAPEntry): if isinstance(_dn, LDAPEntry):
assert _obj is None assert _obj is None
_obj = _dn _obj = _dn
_dn = DN(_dn._dn) _dn = _dn._dn
if isinstance(_obj, LDAPEntry):
orig = _obj._orig
else:
if _obj is None:
_obj = {}
orig = self
assert isinstance(_conn, IPASimpleLDAPObject)
assert isinstance(_dn, DN) assert isinstance(_dn, DN)
if _obj is None:
_obj = {}
self._conn = _conn self._conn = _conn
self._dn = _dn self._dn = _dn
self._orig = orig
self._names = CIDict() self._names = CIDict()
self._data = {}
self._orig = self
if isinstance(_obj, LDAPEntry):
#pylint: disable=E1103
self._names = CIDict(_obj._names)
self._data = dict(_obj._data)
self._orig = _obj._orig
_obj = {}
self.update(_obj, **kwargs) self.update(_obj, **kwargs)
@ -686,8 +689,7 @@ class LDAPEntry(dict):
return self._orig return self._orig
def __repr__(self): def __repr__(self):
return '%s(%r, %s)' % (type(self).__name__, self._dn, return '%s(%r, %r)' % (type(self).__name__, self._dn, self._data)
super(LDAPEntry, self).__repr__())
def copy(self): def copy(self):
return LDAPEntry(self) return LDAPEntry(self)
@ -695,11 +697,8 @@ class LDAPEntry(dict):
def clone(self): def clone(self):
result = LDAPEntry(self._conn, self._dn) result = LDAPEntry(self._conn, self._dn)
for name in self.iterkeys():
super(LDAPEntry, result).__setitem__(
name, deepcopy(super(LDAPEntry, self).__getitem__(name)))
result._names = deepcopy(self._names) result._names = deepcopy(self._names)
result._data = deepcopy(self._data)
if self._orig is not self: if self._orig is not self:
result._orig = self._orig.clone() result._orig = self._orig.clone()
@ -727,7 +726,7 @@ class LDAPEntry(dict):
def __setitem__(self, name, value): def __setitem__(self, name, value):
name = self._attr_name(name) name = self._attr_name(name)
if self._names.has_key(name): if name in self._names:
oldname = self._names[name] oldname = self._names[name]
if oldname != name: if oldname != name:
@ -735,7 +734,7 @@ class LDAPEntry(dict):
if keyname == oldname: if keyname == oldname:
self._names[altname] = name self._names[altname] = name
super(LDAPEntry, self).__delitem__(oldname) del self._data[oldname]
else: else:
self._names[name] = name self._names[name] = name
@ -747,17 +746,7 @@ class LDAPEntry(dict):
altname = altname.decode('utf-8') altname = altname.decode('utf-8')
self._names[altname] = name self._names[altname] = name
super(LDAPEntry, self).__setitem__(name, value) self._data[name] = value
def setdefault(self, name, default):
if name not in self:
self[name] = default
return self[name]
def update(self, _obj={}, **kwargs):
_obj = dict(_obj, **kwargs)
for (name, value) in _obj.iteritems():
self[name] = value
def _get_attr_name(self, name): def _get_attr_name(self, name):
name = self._attr_name(name) name = self._attr_name(name)
@ -765,21 +754,14 @@ class LDAPEntry(dict):
return name return name
def __getitem__(self, name): def __getitem__(self, name):
# for python-ldap tuple compatibility # FIXME: Remove when python-ldap tuple compatibility is dropped
if name == 0: if name == 0:
return self._dn return self._dn
elif name == 1: elif name == 1:
return self return self
return super(LDAPEntry, self).__getitem__(self._get_attr_name(name)) name = self._get_attr_name(name)
return self._data[name]
def get(self, name, default=None):
try:
name = self._get_attr_name(name)
except KeyError:
return default
return super(LDAPEntry, self).get(name, default)
def single_value(self, name, default=_missing): def single_value(self, name, default=_missing):
"""Return a single attribute value """Return a single attribute value
@ -790,8 +772,7 @@ class LDAPEntry(dict):
If the entry is missing and default is not given, raise KeyError. If the entry is missing and default is not given, raise KeyError.
""" """
try: try:
attr_name = self._get_attr_name(name) values = self[name]
values = super(LDAPEntry, self).__getitem__(attr_name)
except KeyError: except KeyError:
if default is _missing: if default is _missing:
raise raise
@ -803,47 +784,81 @@ class LDAPEntry(dict):
'%s has %s values, one expected' % (name, len(values))) '%s has %s values, one expected' % (name, len(values)))
return values[0] return values[0]
def _del_attr_name(self, name): def __delitem__(self, name):
name = self._get_attr_name(name) name = self._get_attr_name(name)
for (altname, keyname) in self._names.items(): for (altname, keyname) in self._names.items():
if keyname == name: if keyname == name:
del self._names[altname] del self._names[altname]
return name del self._data[name]
def __delitem__(self, name):
super(LDAPEntry, self).__delitem__(self._del_attr_name(name))
def pop(self, name, *default):
try:
name = self._del_attr_name(name)
except KeyError:
if not default:
raise
return super(LDAPEntry, self).pop(name, *default)
def popitem(self):
name, value = super(LDAPEntry, self).popitem()
self._del_attr_name(name)
return (name, value)
def clear(self): def clear(self):
super(LDAPEntry, self).clear()
self._names.clear() self._names.clear()
self._data.clear()
def __len__(self):
return len(self._data)
def __contains__(self, name): def __contains__(self, name):
return self._names.has_key(self._attr_name(name)) return name in self._names
def has_key(self, name): def has_key(self, name):
return name in self return name in self
# for python-ldap tuple compatibility def __eq__(self, other):
if not isinstance(other, LDAPEntry):
return NotImplemented
return other is self
def __ne__(self, other):
if not isinstance(other, LDAPEntry):
return NotImplemented
return other is not self
# FIXME: Remove when python-ldap tuple compatibility is dropped
def __iter__(self): def __iter__(self):
yield self._dn yield self._dn
yield self yield self
# FIXME: Remove when python-ldap tuple compatibility is dropped
def iterkeys(self):
return self._data.iterkeys()
# FIXME: Remove when python-ldap tuple compatibility is dropped
def itervalues(self):
return self._data.itervalues()
# FIXME: Remove when python-ldap tuple compatibility is dropped
def iteritems(self):
return self._data.iteritems()
# FIXME: Remove when python-ldap tuple compatibility is dropped
def keys(self):
return list(self.iterkeys())
# FIXME: Remove when python-ldap tuple compatibility is dropped
def values(self):
return list(self.itervalues())
# FIXME: Remove when python-ldap tuple compatibility is dropped
def items(self):
return list(self.iteritems())
# FIXME: Remove when python-ldap tuple compatibility is dropped
def update(self, _obj={}, **kwargs):
_obj = dict(_obj, **kwargs)
super(LDAPEntry, self).update(_obj)
# FIXME: Remove when python-ldap tuple compatibility is dropped
def popitem(self):
try:
name = self.iterkeys().next()
except StopIteration:
raise KeyError
return name, self.pop(name)
def toDict(self): def toDict(self):
# FIXME: for backwards compatibility only # FIXME: for backwards compatibility only
"""Convert the attrs and values to a dict. The dict is keyed on the """Convert the attrs and values to a dict. The dict is keyed on the