Skip to content

Commit a833aa7

Browse files
committed
Move class-based routing into a separate classmethod
1 parent f8a12fe commit a833aa7

File tree

6 files changed

+96
-126
lines changed

6 files changed

+96
-126
lines changed

Lib/ldap/connection.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ def result(self, msgid: int = ldap.RES_ANY, *, all: int = 1,
107107
data['defaultClass'] = defaultIntermediateClass
108108
if msgtype == ldap.RES_EXTENDED:
109109
data['defaultClass'] = defaultExtendedClass
110-
m = Response(msgid, msgtype, controls, **data)
110+
m = Response.from_message(msgid, msgtype, controls, **data)
111111
results.append(m)
112112

113113
return results
@@ -170,7 +170,8 @@ def modify_s(self, dn: str,
170170
return result
171171

172172
def passwd_s(self, user: Optional[str] = None,
173-
oldpw: Optional[bytes] = None, newpw: Optional[bytes] = None,
173+
oldpw: Optional[bytes] = None,
174+
newpw: Optional[bytes] = None, *,
174175
ctrls: RequestControls = None) -> PasswordModifyResponse:
175176
msgid = self.passwd(user, oldpw, newpw, serverctrls=ctrls)
176177
res, = self.result(msgid, defaultExtendedClass=PasswordModifyResponse)

Lib/ldap/extop/__init__.py

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -68,17 +68,15 @@ def __convert_old_api(cls, responseName_or_msgid=_NOTSET,
6868
controls=None, *,
6969
result=_NOTSET, matcheddn=_NOTSET, message=_NOTSET,
7070
referrals=_NOTSET, name=None, value=None,
71-
defaultClass: Optional[type['ExtendedResult']] = None,
7271
msgid=_NOTSET, msgtype=_NOTSET,
7372
responseName=_NOTSET, encodedResponseValue=_NOTSET,
7473
**kwargs):
7574
"""
7675
Implements both old and new API:
7776
__init__(self, responseName, encodedResponseValue)
7877
and
79-
__init__/__new__(self, msgid, msgtype, controls=None, *,
80-
result, matcheddn, message, referrals,
81-
defaultClass=None, **kwargs)
78+
__init__(self, msgid, msgtype, controls=None, *,
79+
result, matcheddn, message, referrals, **kwargs)
8280
"""
8381
if responseName is not _NOTSET:
8482
name = responseName
@@ -127,29 +125,10 @@ def __convert_old_api(cls, responseName_or_msgid=_NOTSET,
127125
'referrals': referrals,
128126
'name': name,
129127
'value': value,
130-
'defaultClass': defaultClass,
131128
**kwargs
132129
}
133130
)
134131

135-
def __new__(cls, *args, **kwargs):
136-
"""
137-
Has to support both old and new API:
138-
__new__(cls, responseName: Optional[str],
139-
encodedResponseValue: Optional[bytes])
140-
and
141-
__new__(cls, msgid: int, msgtype: int, controls: Controls = None, *,
142-
result: int, matcheddn: str, message: str, referrals: List[str],
143-
defaultClass: Optional[type[ExtendedResponse]] = None,
144-
**kwargs)
145-
146-
The old API is deprecated and will be removed in 4.0.
147-
"""
148-
# TODO: retire polymorhpism when old API is removed (4.0?)
149-
_, _, args, kwargs = __class__.__convert_old_api(*args, **kwargs)
150-
151-
return super().__new__(cls, *args, **kwargs)
152-
153132
def __init__(self, *args, **kwargs):
154133
"""
155134
Supports both old and new API:
@@ -158,14 +137,14 @@ def __init__(self, *args, **kwargs):
158137
and
159138
__init__(self, msgid: int, msgtype: int, controls: Controls = None, *,
160139
result: int, matcheddn: str, message: str, referrals: List[str],
161-
defaultClass: Optional[type[ExtendedResponse]] = None,
162140
**kwargs)
163141
164142
The old API is deprecated and will be removed in 5.0.
165143
"""
166144
# TODO: retire polymorhpism when old API is removed (5.0?)
167-
responseName, encodedResponseValue, _, _ = \
145+
responseName, encodedResponseValue, args, kwargs = \
168146
__class__.__convert_old_api(*args, **kwargs)
147+
super().__init__(*args, **kwargs)
169148

170149
self.responseName = responseName
171150
if encodedResponseValue is not None:

Lib/ldap/extop/syncrepl.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,9 @@ class SyncInfoMessage(IntermediateResponse):
121121
"""
122122
responseName = '1.3.6.1.4.1.4203.1.9.1.4'
123123

124-
def __new__(cls, msgid, msgtype, controls=None, *,
125-
name=None, value=None,
126-
**kwargs):
127-
if cls is not __class__:
128-
return super().__new__(cls, msgid, msgtype, controls,
129-
name=name, value=value)
124+
@classmethod
125+
def from_message(cls, msgid, msgtype, controls=None, *,
126+
name=None, value=None, **kwargs):
130127
syncinfo, _ = decoder.decode(value, asn1Spec=SyncInfoValue())
131128
choice = syncinfo.getName()
132129
if choice == 'newcookie':
@@ -139,8 +136,7 @@ def __new__(cls, msgid, msgtype, controls=None, *,
139136
klass = SyncInfoIDSet
140137
else:
141138
raise ValueError
142-
return klass.__new__(klass, msgid, msgtype, controls,
143-
name=name, value=value)
139+
return klass(msgid, msgtype, controls, name=name, value=value)
144140

145141
def decode(self, value: bytes):
146142
self.syncinfo, _ = decoder.decode(

Lib/ldap/response.py

Lines changed: 81 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -56,23 +56,18 @@ def __init_subclass__(cls):
5656
f"that's incompatible with the existing default: "
5757
f"{c.__module__}.{c.__qualname__}")
5858

59-
def __new__(cls, msgid, msgtype, controls=None, **kwargs):
60-
if cls is not __class__:
61-
instance = super().__new__(cls)
62-
instance.msgid = msgid
63-
instance.msgtype = msgtype
64-
instance.controls = controls
65-
return instance
66-
67-
c = __class__.__subclasses.get(msgtype)
68-
if c:
69-
return c.__new__(c, msgid, msgtype, controls, **kwargs)
70-
71-
instance = super().__new__(cls, **kwargs)
72-
instance.msgid = msgid
73-
instance.msgtype = msgtype
74-
instance.controls = controls
75-
return instance
59+
def __init__(self, msgid, msgtype, controls=None):
60+
self.msgid = msgid
61+
self.msgtype = msgtype
62+
self.controls = controls
63+
64+
@classmethod
65+
def from_message(cls, msgid, msgtype, controls=None, **kwargs):
66+
c = cls.__subclasses.get(msgtype)
67+
if c and c is not cls:
68+
return c.from_message(msgid, msgtype, controls, **kwargs)
69+
70+
return cls(msgid, msgtype, controls, **kwargs)
7671

7772
def __repr__(self):
7873
optional = ""
@@ -92,16 +87,15 @@ class Result(Response):
9287
message: str
9388
referrals: Optional[list[str]]
9489

95-
def __new__(cls, msgid, msgtype, controls=None, *,
96-
result, matcheddn, message, referrals, **kwargs):
97-
instance = super().__new__(cls, msgid, msgtype, controls, **kwargs)
98-
99-
instance.result = result
100-
instance.matcheddn = matcheddn
101-
instance.message = message
102-
instance.referrals = referrals
90+
def __init__(self, msgid, msgtype, controls=None, *,
91+
result: int, matcheddn: str, message: str,
92+
referrals: Optional[list[str]]):
93+
super().__init__(msgid, msgtype, controls)
10394

104-
return instance
95+
self.result = result
96+
self.matcheddn = matcheddn
97+
self.message = message
98+
self.referrals = referrals
10599

106100
def raise_for_result(self) -> 'Result':
107101
if self.result in _SUCCESS_CODES:
@@ -131,14 +125,12 @@ class SearchEntry(Response):
131125
dn: str
132126
attrs: dict[str, Optional[list[bytes]]]
133127

134-
def __new__(cls, msgid, msgtype, controls=None, *,
135-
dn: str, attrs: dict[str, Optional[list[bytes]]], **kwargs):
136-
instance = super().__new__(cls, msgid, msgtype, controls, **kwargs)
128+
def __init__(self, msgid, msgtype, controls=None, *,
129+
dn: str, attrs: dict[str, Optional[list[bytes]]]):
130+
super().__init__(msgid, msgtype, controls)
137131

138-
instance.dn = dn
139-
instance.attrs = attrs
140-
141-
return instance
132+
self.dn = dn
133+
self.attrs = attrs
142134

143135
def __rich_repr__(self):
144136
yield from super().__rich_repr__()
@@ -151,13 +143,10 @@ class SearchReference(Response):
151143

152144
referrals: list[str]
153145

154-
def __new__(cls, msgid, msgtype, controls=None, *,
155-
referrals, **kwargs):
156-
instance = super().__new__(cls, msgid, msgtype, controls, **kwargs)
157-
158-
instance.referrals = referrals
146+
def __init__(self, msgid, msgtype, controls=None, *, referrals):
147+
super().__init__(msgid, msgtype, controls)
159148

160-
return instance
149+
self.referrals = referrals
161150

162151
def __rich_repr__(self):
163152
yield from super().__rich_repr__()
@@ -174,28 +163,27 @@ class IntermediateResponse(Response):
174163
name: Optional[str]
175164
value: Optional[bytes]
176165

177-
def __new__(cls, msgid, msgtype, controls=None, *,
178-
name=None, value=None,
179-
defaultClass: Optional[type['IntermediateResponse']] = None,
180-
**kwargs):
181-
if cls is not __class__:
182-
instance = super().__new__(cls, msgid, msgtype, controls, **kwargs)
183-
instance.name = name
184-
instance.value = value
185-
return instance
186-
166+
def __init__(self, msgid: int, msgtype: int,
167+
controls: ResponseControl = None, *,
168+
name: Optional[str] = None, value: Optional[bytes] = None):
169+
super().__init__(msgid, msgtype, controls)
170+
self.name = name
171+
self.value = value
172+
173+
if hasattr(self, 'decode'):
174+
self.decode(value)
175+
176+
@classmethod
177+
def from_message(cls, msgid, msgtype, controls=None, *,
178+
name=None, value=None, defaultClass:
179+
Optional[type['IntermediateResponse']] = None,
180+
**kwargs):
187181
c = ldap.KNOWN_INTERMEDIATE_RESPONSES.get(name, defaultClass)
188-
if c:
189-
instance = c.__new__(c, msgid, msgtype, controls,
190-
name=name, value=value, **kwargs)
191-
if hasattr(instance, 'decode'):
192-
instance.decode(value)
193-
return instance
194-
195-
instance = super().__new__(cls, msgid, msgtype, controls, **kwargs)
196-
instance.name = name
197-
instance.value = value
198-
return instance
182+
if c and c is not cls:
183+
return c.from_message(msgid, msgtype, controls,
184+
name=name, value=value, **kwargs)
185+
186+
return cls(msgid, msgtype, controls, name=name, value=value, **kwargs)
199187

200188
def __repr__(self):
201189
optional = ""
@@ -219,6 +207,15 @@ class BindResult(Result):
219207

220208
credentials: Optional[bytes]
221209

210+
def __init__(self, msgid: int, msgtype: int,
211+
controls: ResponseControl = None, *,
212+
result, matcheddn, message, referrals,
213+
credentials: Optional[bytes] = None):
214+
super().__init__(msgid, msgtype, controls, result=result,
215+
matcheddn=matcheddn, message=message,
216+
referrals=referrals)
217+
self.credentials = credentials
218+
222219
def __rich_repr__(self):
223220
yield from super().__rich_repr__()
224221
yield "credentials", self.credentials, None
@@ -257,35 +254,31 @@ class ExtendedResult(Result):
257254
name: Optional[str]
258255
value: Optional[bytes]
259256

260-
def __new__(cls, msgid, msgtype, controls=None, *,
261-
result, matcheddn, message, referrals,
262-
name=None, value=None,
263-
defaultClass: Optional[type['ExtendedResult']] = None,
264-
**kwargs):
265-
if cls is not __class__:
266-
instance = super().__new__(cls, msgid, msgtype, controls,
267-
result=result, matcheddn=matcheddn,
268-
message=message, referrals=referrals)
269-
instance.name = name
270-
instance.value = value
271-
return instance
272-
257+
def __init__(self, msgid: int, msgtype: int,
258+
controls: ResponseControl = None, *,
259+
result: int, matcheddn: str, message: str,
260+
referrals: Optional[list[str]],
261+
name: Optional[str] = None, value: Optional[bytes] = None):
262+
super().__init__(msgid, msgtype, controls, result=result,
263+
matcheddn=matcheddn, message=message,
264+
referrals=referrals)
265+
self.name = name
266+
self.value = value
267+
268+
if hasattr(self, 'decode'):
269+
self.decode(value)
270+
271+
@classmethod
272+
def from_message(cls, msgid, msgtype, controls=None, *,
273+
name=None, value=None, defaultClass:
274+
Optional[type['ExtendedResult']] = None,
275+
**kwargs):
273276
c = ldap.KNOWN_EXTENDED_RESPONSES.get(name, defaultClass)
274-
if not c and msgid == ldap.RES_UNSOLICITED:
275-
c = UnsolicitedNotification
276-
277-
if c:
278-
return c.__new__(c, msgid, msgtype, controls,
279-
result=result, matcheddn=matcheddn,
280-
message=message, referrals=referrals,
281-
name=name, value=value, **kwargs)
282-
283-
instance = super().__new__(cls, msgid, msgtype, controls,
284-
result=result, matcheddn=matcheddn,
285-
message=message, referrals=referrals)
286-
instance.name = name
287-
instance.value = value
288-
return instance
277+
if c and c is not cls:
278+
return c.from_message(msgid, msgtype, controls,
279+
name=name, value=value, **kwargs)
280+
281+
return cls(msgid, msgtype, controls, name=name, value=value, **kwargs)
289282

290283
def __repr__(self):
291284
optional = ""

Lib/ldap/syncrepl.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,9 @@ def syncrepl_poll(self, msgid=-1, timeout=None, all=0):
122122
# Intermediate message, process any that are SyncInfoMessage
123123
for m in msg:
124124
name, value, controls = m
125-
m = Response(mid, type, name=name, value=value,
126-
controls=controls)
125+
m = Response.from_message(mid, type,
126+
name=name, value=value,
127+
controls=controls)
127128
if isinstance(m, SyncInfoNewCookie):
128129
self.syncrepl_set_cookie(m.cookie)
129130
elif isinstance(m, (SyncInfoRefreshPresent, SyncInfoRefreshDelete)):

Tests/t_ldap_syncrepl.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ def test_syncidset_message(self):
473473
""".replace(' ', '').replace('\n', '')
474474

475475
msgraw = binascii.unhexlify(msg)
476-
sim = IntermediateResponse(1, ldap.RES_INTERMEDIATE,
476+
sim = IntermediateResponse.from_message(1, ldap.RES_INTERMEDIATE,
477477
name=SyncInfoMessage.responseName, value=msgraw)
478478
self.assertIsInstance(sim, SyncInfoIDSet)
479479
self.assertEqual(sim.cookie,

0 commit comments

Comments
 (0)