ldap3: add types for Connection.unbind - #16243
Conversation
Co-Authored-By: Factory Droid (Opus 5)
This comment has been minimized.
This comment has been minimized.
|
According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉 |
| # Thread safe strategies return a (status, result, response, request) tuple instead of a bare status | ||
| def unbind( | ||
| self, controls: _ControlSequence | None = None | ||
| ) -> Literal[True] | tuple[Literal[True], _Result, _Response, _Request]: ... |
There was a problem hiding this comment.
I assume sub-classes override this incompatibly? Union return types can be a bit problematic (obligatory link to python/typing#566), but let's keep this for now. We can always change it if it turns out to be a problem in practice.
There was a problem hiding this comment.
I assume sub-classes override this incompatibly?
No, the behavior sync vs async of the entire class actually depends on the value (and type) of Connection.strategy.
I believe then the Connection would need to become generic in terms of strategy? And then we'd overload unbind with explicit self: Connection[SyncStrategies] vs self: Connection[AsyncStrategies]?
Since runtime Connection is not generic, what's the policy towards making the stubs generic? Users can hit TypeError: type 'Connection' is not subscriptable during runtime
There was a problem hiding this comment.
We sometimes make classes generic, even if they are not generic at runtime.
Users can hit TypeError: type 'Connection' is not subscriptable during runtime
Yes, that's unfortunate, but often that's a tradeoff we are willing to make.
This PR adds types for
Connection.unbind'scontrolsargument, and propagates the types that become known from there toprotocol.convert.build_controls_listand theBaseStrategy.sendset of subclasses thatunbindfeeds itscontrolsinto.Rationale for the types:
controlsis a_ControlSequence | None.build_controls_listrejects anything that is not inSEQUENCE_TYPES(set,list,tuple, generator,dict_keys), so the alias enumerates those rather than usingIterable, mirroring the existing_ServerSequencealias. Items are aControlobject or a(controlType, criticality, controlValue)triple, the two formsbuild_controls_listaccepts.protocol.convertnext tobuild_controls_list, the function that defines the contract, and is imported bycore.connectionand the strategies.build_controls_listreturnsControls | None; it returnsNonefor bothNoneand an empty sequence.unbindreturnsLiteral[True] | tuple[...]: it hands a hardcodedTrueto_prepare_return_value, which returns that status directly, except on the thread-safe strategies (SAFE_SYNC,SAFE_RESTARTABLE), where it returns a(status, result, response, request)tuple.result,responseandrequestmembers of that tuple use new_Result,_Responseand_Requestplaceholder aliases, also used for the matchingConnectionattributes. Their real types are per-operation dicts, which is left for a follow-up.Written with the help of Factory Droid (Opus 5), humanly curated.