From 4b570397263c6adceabccdb017e8726a8f03fe25 Mon Sep 17 00:00:00 2001 From: Maico Timmerman <904824+MaicoTimmerman@users.noreply.github.com> Date: Tue, 18 Aug 2026 13:26:32 +0200 Subject: [PATCH] ldap3: add types for Connection.unbind Co-Authored-By: Factory Droid (Opus 5) --- stubs/ldap3/ldap3/core/connection.pyi | 17 +++++++++++++---- stubs/ldap3/ldap3/protocol/convert.pyi | 14 +++++++++++++- stubs/ldap3/ldap3/strategy/base.pyi | 4 +++- stubs/ldap3/ldap3/strategy/ldifProducer.pyi | 3 ++- stubs/ldap3/ldap3/strategy/mockBase.pyi | 4 +++- stubs/ldap3/ldap3/strategy/restartable.pyi | 3 ++- stubs/ldap3/ldap3/strategy/reusable.pyi | 3 ++- 7 files changed, 38 insertions(+), 10 deletions(-) diff --git a/stubs/ldap3/ldap3/core/connection.pyi b/stubs/ldap3/ldap3/core/connection.pyi index 10cb20008e94..d348f2fbe1d4 100644 --- a/stubs/ldap3/ldap3/core/connection.pyi +++ b/stubs/ldap3/ldap3/core/connection.pyi @@ -6,6 +6,7 @@ from typing_extensions import Self from pyasn1.type.base import Asn1Item +from ..protocol.convert import _ControlSequence from .pooling import ServerPool from .server import Server @@ -15,6 +16,11 @@ CLIENT_STRATEGIES: Incomplete _ServerSequence: TypeAlias = ( set[Server] | list[Server] | tuple[Server, ...] | Generator[Server, None, None] | dict_keys[Server, Incomplete] ) +# TODO: Construct full request and response types. This is non-trivial because there are many different request/response types and +# the libary was written async (not thread-safe) design. +_Request: TypeAlias = Incomplete +_Response: TypeAlias = Incomplete +_Result: TypeAlias = Incomplete class Connection: connection_lock: Incomplete @@ -25,9 +31,9 @@ class Connection: authentication: Incomplete version: Incomplete auto_referrals: Incomplete - request: Incomplete - response: Incomplete | None - result: Incomplete + request: _Request + response: _Response | None + result: _Result bound: bool listening: bool closed: bool @@ -133,7 +139,10 @@ class Connection: read_server_info: bool = True, controls=None, ): ... - def unbind(self, controls=None): ... + # 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]: ... def search( self, search_base: str, diff --git a/stubs/ldap3/ldap3/protocol/convert.pyi b/stubs/ldap3/ldap3/protocol/convert.pyi index 10b06b4f6165..8909df7c028c 100644 --- a/stubs/ldap3/ldap3/protocol/convert.pyi +++ b/stubs/ldap3/ldap3/protocol/convert.pyi @@ -1,3 +1,15 @@ +from _collections_abc import Generator, dict_keys +from _typeshed import Incomplete +from typing import TypeAlias + +from .rfc4511 import Control, Controls + +# A control is either a Control object or a (controlType, criticality, controlValue) triple +_Control: TypeAlias = Control | tuple[str, bool, str | bytes | None] +_ControlSequence: TypeAlias = ( + set[_Control] | list[_Control] | tuple[_Control, ...] | Generator[_Control, None, None] | dict_keys[_Control, Incomplete] +) + def to_str_or_normalized_unicode(val): ... def attribute_to_dict(attribute): ... def attributes_to_dict(attributes): ... @@ -13,7 +25,7 @@ def attributes_to_list(attributes): ... def ava_to_dict(ava): ... def substring_to_dict(substring): ... def prepare_changes_for_request(changes): ... -def build_controls_list(controls): ... +def build_controls_list(controls: _ControlSequence | None) -> Controls | None: ... def validate_assertion_value(schema, name, value, auto_escape, auto_encode, validator, check_names): ... def validate_attribute_value(schema, name, value, auto_encode, validator=None, check_names: bool = False): ... def prepare_filter_for_sending(raw_string): ... diff --git a/stubs/ldap3/ldap3/strategy/base.pyi b/stubs/ldap3/ldap3/strategy/base.pyi index 1adc87f03185..bf28ee9f9548 100644 --- a/stubs/ldap3/ldap3/strategy/base.pyi +++ b/stubs/ldap3/ldap3/strategy/base.pyi @@ -1,5 +1,7 @@ from _typeshed import Incomplete +from ..protocol.convert import _ControlSequence + unix_socket_available: bool SESSION_TERMINATED_BY_SERVER: str TRANSACTION_ERROR: str @@ -16,7 +18,7 @@ class BaseStrategy: def __init__(self, ldap_connection) -> None: ... def open(self, reset_usage: bool = True, read_server_info: bool = True) -> None: ... def close(self) -> None: ... - def send(self, message_type, request, controls=None): ... + def send(self, message_type, request, controls: _ControlSequence | None = None): ... def get_response(self, message_id, timeout=None, get_request: bool = False): ... @staticmethod def compute_ldap_message_size(data): ... diff --git a/stubs/ldap3/ldap3/strategy/ldifProducer.pyi b/stubs/ldap3/ldap3/strategy/ldifProducer.pyi index 92524d3ad59c..36268051577c 100644 --- a/stubs/ldap3/ldap3/strategy/ldifProducer.pyi +++ b/stubs/ldap3/ldap3/strategy/ldifProducer.pyi @@ -1,5 +1,6 @@ from _typeshed import Incomplete +from ..protocol.convert import _ControlSequence from .base import BaseStrategy class LdifProducerStrategy(BaseStrategy): @@ -13,7 +14,7 @@ class LdifProducerStrategy(BaseStrategy): order: Incomplete def __init__(self, ldap_connection) -> None: ... def receiving(self) -> None: ... - def send(self, message_type, request, controls=None): ... + def send(self, message_type, request, controls: _ControlSequence | None = None): ... def post_send_single_response(self, message_id): ... def post_send_search(self, message_id) -> None: ... def accumulate_stream(self, fragment) -> None: ... diff --git a/stubs/ldap3/ldap3/strategy/mockBase.pyi b/stubs/ldap3/ldap3/strategy/mockBase.pyi index 879cc5efecc8..36042b449036 100644 --- a/stubs/ldap3/ldap3/strategy/mockBase.pyi +++ b/stubs/ldap3/ldap3/strategy/mockBase.pyi @@ -1,5 +1,7 @@ from _typeshed import Incomplete +from ..protocol.convert import _ControlSequence + SEARCH_CONTROLS: Incomplete SERVER_ENCODING: str @@ -34,4 +36,4 @@ class MockBaseStrategy: def mock_extended(self, request_message, controls): ... def evaluate_filter_node(self, node, candidates): ... def equal(self, dn, attribute_type, value_to_check): ... - def send(self, message_type, request, controls=None): ... + def send(self, message_type, request, controls: _ControlSequence | None = None): ... diff --git a/stubs/ldap3/ldap3/strategy/restartable.pyi b/stubs/ldap3/ldap3/strategy/restartable.pyi index bec37ed375ac..4ac56e980728 100644 --- a/stubs/ldap3/ldap3/strategy/restartable.pyi +++ b/stubs/ldap3/ldap3/strategy/restartable.pyi @@ -1,5 +1,6 @@ from _typeshed import Incomplete +from ..protocol.convert import _ControlSequence from .sync import SyncStrategy class RestartableStrategy(SyncStrategy): @@ -12,7 +13,7 @@ class RestartableStrategy(SyncStrategy): exception_history: Incomplete def __init__(self, ldap_connection) -> None: ... def open(self, reset_usage: bool = False, read_server_info: bool = True) -> None: ... - def send(self, message_type, request, controls=None): ... + def send(self, message_type, request, controls: _ControlSequence | None = None): ... def post_send_single_response(self, message_id): ... def post_send_search(self, message_id): ... def get_stream(self) -> None: ... diff --git a/stubs/ldap3/ldap3/strategy/reusable.pyi b/stubs/ldap3/ldap3/strategy/reusable.pyi index 92b2f3ad7b8f..7b5159b57224 100644 --- a/stubs/ldap3/ldap3/strategy/reusable.pyi +++ b/stubs/ldap3/ldap3/strategy/reusable.pyi @@ -1,6 +1,7 @@ from _typeshed import Incomplete from threading import Thread +from ..protocol.convert import _ControlSequence from .base import BaseStrategy TERMINATE_REUSABLE: str @@ -68,7 +69,7 @@ class ReusableStrategy(BaseStrategy): def __init__(self, ldap_connection) -> None: ... def open(self, reset_usage: bool = True, read_server_info: bool = True) -> None: ... def terminate(self) -> None: ... - def send(self, message_type, request, controls=None): ... + def send(self, message_type, request, controls: _ControlSequence | None = None): ... def validate_bind(self, controls): ... def get_response(self, counter, timeout=None, get_request: bool = False): ... def post_send_single_response(self, counter): ...