Skip to content

Commit 723b509

Browse files
authored
Rewrite cidict and LDAPUrlExtensions in terms of MutableMapping
python-ldap#186
2 parents cb4eb78 + b1b3b5a commit 723b509

File tree

4 files changed

+134
-90
lines changed

4 files changed

+134
-90
lines changed

Lib/ldap/cidict.py

Lines changed: 48 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,55 +7,68 @@
77
"""
88
import warnings
99

10+
from ldap.compat import MutableMapping
1011
from ldap import __version__
1112

12-
from ldap.compat import IterableUserDict
1313

14+
class cidict(MutableMapping):
15+
"""
16+
Case-insensitive but case-respecting dictionary.
17+
"""
18+
__slots__ = ('_keys', '_data')
1419

15-
class cidict(IterableUserDict):
16-
"""
17-
Case-insensitive but case-respecting dictionary.
18-
"""
20+
def __init__(self, default=None):
21+
self._keys = {}
22+
self._data = {}
23+
if default:
24+
self.update(default)
25+
26+
# MutableMapping abstract methods
27+
28+
def __getitem__(self, key):
29+
return self._data[key.lower()]
1930

20-
def __init__(self,default=None):
21-
self._keys = {}
22-
IterableUserDict.__init__(self,{})
23-
self.update(default or {})
31+
def __setitem__(self, key, value):
32+
lower_key = key.lower()
33+
self._keys[lower_key] = key
34+
self._data[lower_key] = value
2435

25-
def __getitem__(self,key):
26-
return self.data[key.lower()]
36+
def __delitem__(self, key):
37+
lower_key = key.lower()
38+
del self._keys[lower_key]
39+
del self._data[lower_key]
2740

28-
def __setitem__(self,key,value):
29-
lower_key = key.lower()
30-
self._keys[lower_key] = key
31-
self.data[lower_key] = value
41+
def __iter__(self):
42+
return iter(self._keys.values())
3243

33-
def __delitem__(self,key):
34-
lower_key = key.lower()
35-
del self._keys[lower_key]
36-
del self.data[lower_key]
44+
def __len__(self):
45+
return len(self._keys)
3746

38-
def update(self,dict):
39-
for key, value in dict.items():
40-
self[key] = value
47+
# Specializations for performance
4148

42-
def has_key(self,key):
43-
return key in self
49+
def __contains__(self, key):
50+
return key.lower() in self._keys
4451

45-
def __contains__(self,key):
46-
return IterableUserDict.__contains__(self, key.lower())
52+
def clear(self):
53+
self._keys.clear()
54+
self._data.clear()
4755

48-
def __iter__(self):
49-
return iter(self.keys())
56+
# Backwards compatibility
5057

51-
def keys(self):
52-
return self._keys.values()
58+
def has_key(self, key):
59+
"""Compatibility with python-ldap 2.x"""
60+
return key in self
5361

54-
def items(self):
55-
result = []
56-
for k in self._keys.values():
57-
result.append((k,self[k]))
58-
return result
62+
@property
63+
def data(self):
64+
"""Compatibility with older IterableUserDict-based implementation"""
65+
warnings.warn(
66+
'ldap.cidict.cidict.data is an internal attribute; it may be ' +
67+
'removed at any time',
68+
category=DeprecationWarning,
69+
stacklevel=2,
70+
)
71+
return self._data
5972

6073

6174
def strlist_minus(a,b):

Lib/ldap/compat.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from urllib import unquote as urllib_unquote
1111
from urllib import urlopen
1212
from urlparse import urlparse
13+
from collections import MutableMapping
1314

1415
def unquote(uri):
1516
"""Specialized unquote that uses UTF-8 for parsing."""
@@ -33,6 +34,7 @@ def unquote(uri):
3334
IterableUserDict = UserDict
3435
from urllib.parse import quote, quote_plus, unquote, urlparse
3536
from urllib.request import urlopen
37+
from collections.abc import MutableMapping
3638

3739
def reraise(exc_type, exc_value, exc_traceback):
3840
"""Re-raise an exception given information from sys.exc_info()

Lib/ldapurl.py

Lines changed: 74 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
'LDAPUrlExtension','LDAPUrlExtensions','LDAPUrl'
1717
]
1818

19-
from ldap.compat import UserDict, quote, unquote
19+
from ldap.compat import quote, unquote, MutableMapping
2020

2121
LDAP_SCOPE_BASE = 0
2222
LDAP_SCOPE_ONELEVEL = 1
@@ -130,58 +130,71 @@ def __ne__(self,other):
130130
return not self.__eq__(other)
131131

132132

133-
class LDAPUrlExtensions(UserDict):
134-
"""
135-
Models a collection of LDAP URL extensions as
136-
dictionary type
137-
"""
138-
139-
def __init__(self,default=None):
140-
UserDict.__init__(self)
141-
for k,v in (default or {}).items():
142-
self[k]=v
143-
144-
def __setitem__(self,name,value):
133+
class LDAPUrlExtensions(MutableMapping):
145134
"""
146-
value
147-
Either LDAPUrlExtension instance, (critical,exvalue)
148-
or string'ed exvalue
135+
Models a collection of LDAP URL extensions as
136+
a mapping type
149137
"""
150-
assert isinstance(value,LDAPUrlExtension)
151-
assert name==value.extype
152-
self.data[name] = value
153-
154-
def values(self):
155-
return [
156-
self[k]
157-
for k in self.keys()
158-
]
159-
160-
def __str__(self):
161-
return ','.join(str(v) for v in self.values())
162-
163-
def __repr__(self):
164-
return '<%s.%s instance at %s: %s>' % (
165-
self.__class__.__module__,
166-
self.__class__.__name__,
167-
hex(id(self)),
168-
self.data
169-
)
138+
__slots__ = ('_data', )
139+
140+
def __init__(self, default=None):
141+
self._data = {}
142+
if default is not None:
143+
self.update(default)
144+
145+
def __setitem__(self, name, value):
146+
"""Store an extension
147+
148+
name
149+
string
150+
value
151+
LDAPUrlExtension instance, whose extype nust match `name`
152+
"""
153+
if not isinstance(value, LDAPUrlExtension):
154+
raise TypeError("value must be LDAPUrlExtension, not "
155+
+ type(value).__name__)
156+
if name != value.extype:
157+
raise ValueError(
158+
"key {!r} does not match extension type {!r}".format(
159+
name, value.extype))
160+
self._data[name] = value
161+
162+
def __getitem__(self, name):
163+
return self._data[name]
164+
165+
def __delitem__(self, name):
166+
del self._data[name]
167+
168+
def __iter__(self):
169+
return iter(self._data)
170+
171+
def __len__(self):
172+
return len(self._data)
173+
174+
def __str__(self):
175+
return ','.join(str(v) for v in self.values())
176+
177+
def __repr__(self):
178+
return '<%s.%s instance at %s: %s>' % (
179+
self.__class__.__module__,
180+
self.__class__.__name__,
181+
hex(id(self)),
182+
self._data
183+
)
170184

171-
def __eq__(self,other):
172-
assert isinstance(other,self.__class__),TypeError(
173-
"other has to be instance of %s" % (self.__class__)
174-
)
175-
return self.data==other.data
185+
def __eq__(self,other):
186+
if not isinstance(other, self.__class__):
187+
return NotImplemented
188+
return self._data == other._data
176189

177-
def parse(self,extListStr):
178-
for extension_str in extListStr.strip().split(','):
179-
if extension_str:
180-
e = LDAPUrlExtension(extension_str)
181-
self[e.extype] = e
190+
def parse(self,extListStr):
191+
for extension_str in extListStr.strip().split(','):
192+
if extension_str:
193+
e = LDAPUrlExtension(extension_str)
194+
self[e.extype] = e
182195

183-
def unparse(self):
184-
return ','.join([ v.unparse() for v in self.values() ])
196+
def unparse(self):
197+
return ','.join(v.unparse() for v in self.values())
185198

186199

187200
class LDAPUrl(object):
@@ -366,17 +379,23 @@ def htmlHREF(self,urlPrefix='',hrefText=None,hrefTarget=None):
366379
hrefTarget
367380
string added as link target attribute
368381
"""
369-
assert type(urlPrefix)==StringType, "urlPrefix must be StringType"
382+
if not isinstance(urlPrefix, str):
383+
raise TypeError("urlPrefix must be str, not "
384+
+ type(urlPrefix).__name__)
370385
if hrefText is None:
371-
hrefText = self.unparse()
372-
assert type(hrefText)==StringType, "hrefText must be StringType"
386+
hrefText = self.unparse()
387+
if not isinstance(hrefText, str):
388+
raise TypeError("hrefText must be str, not "
389+
+ type(hrefText).__name__)
373390
if hrefTarget is None:
374-
target = ''
391+
target = ''
375392
else:
376-
assert type(hrefTarget)==StringType, "hrefTarget must be StringType"
377-
target = ' target="%s"' % hrefTarget
393+
if not isinstance(hrefTarget, str):
394+
raise TypeError("hrefTarget must be str, not "
395+
+ type(hrefTarget).__name__)
396+
target = ' target="%s"' % hrefTarget
378397
return '<a%s href="%s%s">%s</a>' % (
379-
target,urlPrefix,self.unparse(),hrefText
398+
target, urlPrefix, self.unparse(), hrefText
380399
)
381400

382401
def __str__(self):

Tests/t_cidict.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,16 @@ def test_strlist_deprecated(self):
6262
strlist_func(["a"], ["b"])
6363
self.assertEqual(len(w), 1)
6464

65+
def test_cidict_data(self):
66+
"""test the deprecated data atrtribute"""
67+
d = ldap.cidict.cidict({'A': 1, 'B': 2})
68+
with warnings.catch_warnings(record=True) as w:
69+
warnings.resetwarnings()
70+
warnings.simplefilter('always', DeprecationWarning)
71+
data = d.data
72+
assert data == {'a': 1, 'b': 2}
73+
self.assertEqual(len(w), 1)
74+
6575

6676
if __name__ == '__main__':
6777
unittest.main()

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy