Adapt `derivepassphrase` to use `derivepassphrase-sshagentsocketprovider`
Marco Ricci

Marco Ricci commited on 2026-08-30 12:26:14
Zeige 14 geänderte Dateien mit 91 Einfügungen und 134 Löschungen.


Change all references to the SSH agent socket and socket provider types
to the `derivepassphrase-sshagentsocketprovider` package.  Include
`derivepassphrase-sshagentsocketprovider` in the dependencies.
... ...
@@ -30,6 +30,9 @@ classifiers = [
30 30
     "Typing :: Typed",
31 31
 ]
32 32
 dependencies = [
33
+    # Our API for SSH agent socket providers is declared in a separately
34
+    # published package.
35
+    "derivepassphrase-sshagentsocketprovider",
33 36
     # We use click for the command-line interface.  We require version
34 37
     # 8.1.0 or higher due to click issue #1985.
35 38
     "click >= 8.1",
... ...
@@ -632,7 +635,10 @@ parametrize-names-type = 'list'
632 635
 ban-relative-imports = "all"
633 636
 
634 637
 [tool.ruff.lint.isort]
635
-known-first-party = ["derivepassphrase"]
638
+known-first-party = [
639
+    "derivepassphrase",
640
+    "derivepassphrase_sshagentsocketprovider",
641
+]
636 642
 
637 643
 [tool.ruff.lint.pycodestyle]
638 644
 ignore-overlong-task-comments = true  # for E501
... ...
@@ -61,6 +61,8 @@ if TYPE_CHECKING:
61 61
 
62 62
     from typing_extensions import Buffer, Self
63 63
 
64
+    import derivepassphrase_sshagentsocketprovider as d_sasp
65
+
64 66
 PROG_NAME = _msg.PROG_NAME
65 67
 KEY_DISPLAY_LENGTH = 50
66 68
 
... ...
@@ -560,7 +562,7 @@ def load_user_config() -> dict[str, Any]:
560 562
 
561 563
 def get_suitable_ssh_keys(
562 564
     conn: ssh_agent.SSHAgentClient
563
-    | _types.SSHAgentSocket
565
+    | d_sasp.SSHAgentSocket
564 566
     | Sequence[str]
565 567
     | None = None,
566 568
     /,
... ...
@@ -854,7 +856,7 @@ def default_error_callback(
854 856
 
855 857
 def select_ssh_key(
856 858
     conn: ssh_agent.SSHAgentClient
857
-    | _types.SSHAgentSocket
859
+    | d_sasp.SSHAgentSocket
858 860
     | Sequence[str]
859 861
     | None = None,
860 862
     /,
... ...
@@ -1101,13 +1103,13 @@ def check_for_misleading_passphrase(
1101 1103
 
1102 1104
 def get_configured_connection_hint(
1103 1105
     conn: ssh_agent.SSHAgentClient
1104
-    | _types.SSHAgentSocket
1106
+    | d_sasp.SSHAgentSocket
1105 1107
     | Sequence[str]
1106 1108
     | None = None,
1107 1109
     /,
1108 1110
     *,
1109 1111
     main_config: dict[str, Any],
1110
-) -> ssh_agent.SSHAgentClient | _types.SSHAgentSocket | Sequence[str] | None:
1112
+) -> ssh_agent.SSHAgentClient | d_sasp.SSHAgentSocket | Sequence[str] | None:
1111 1113
     """Return a suitable connection hint for the SSH agent client.
1112 1114
 
1113 1115
     If a connection hint is already known, return that connection hint.
... ...
@@ -1129,12 +1131,12 @@ def get_configured_connection_hint(
1129 1131
 
1130 1132
     def handle_return_value(
1131 1133
         val: ssh_agent.SSHAgentClient
1132
-        | _types.SSHAgentSocket
1134
+        | d_sasp.SSHAgentSocket
1133 1135
         | str
1134 1136
         | Sequence[str]
1135 1137
         | None,
1136 1138
     ) -> (
1137
-        ssh_agent.SSHAgentClient | _types.SSHAgentSocket | Sequence[str] | None
1139
+        ssh_agent.SSHAgentClient | d_sasp.SSHAgentSocket | Sequence[str] | None
1138 1140
     ):  # pragma: no cover [external]
1139 1141
         # A separate function, to easily exclude it from coverage,
1140 1142
         # because it's just type handling, which the type checker
... ...
@@ -1168,7 +1170,7 @@ def key_to_phrase(
1168 1170
     error_callback: Callable[..., NoReturn],
1169 1171
     warning_callback: Callable[..., None],
1170 1172
     conn: ssh_agent.SSHAgentClient
1171
-    | _types.SSHAgentSocket
1173
+    | d_sasp.SSHAgentSocket
1172 1174
     | Sequence[str]
1173 1175
     | None = None,
1174 1176
 ) -> bytes:
... ...
@@ -17,12 +17,10 @@ from typing_extensions import (
17 17
     Buffer,
18 18
     NamedTuple,
19 19
     NotRequired,
20
-    TypeAlias,
21 20
     TypedDict,
22 21
     deprecated,
23 22
     get_overloads,
24 23
     overload,
25
-    runtime_checkable,
26 24
 )
27 25
 
28 26
 if TYPE_CHECKING:
... ...
@@ -1140,63 +1138,3 @@ class BuiltinSSHAgentSocketProvider(str, enum.Enum):
1140 1138
 
1141 1139
     __str__ = str.__str__
1142 1140
     __format__ = str.__format__  # type: ignore[assignment]
1143
-
1144
-
1145
-@runtime_checkable
1146
-class SSHAgentSocket(Protocol):
1147
-    """An abstract networking socket connected to an SSH agent.
1148
-
1149
-    The abstract socket supports the [`sendall`][socket.socket.sendall]
1150
-    and a [`recv`][socket.socket.recv] operation, with the same
1151
-    signatures and semantics as for "real" sockets.  The abstract socket
1152
-    also supports use as a context manager, for automatically closing
1153
-    the socket upon exiting the context.
1154
-
1155
-    """
1156
-
1157
-    def __enter__(self) -> Any: ...  # noqa: ANN401
1158
-
1159
-    # mypy/typeshed has a *very* lax annotation of
1160
-    # socket.socket.__exit__, which we need to be compatible with.
1161
-    # *sigh*
1162
-    def __exit__(
1163
-        self,
1164
-        *args: object,
1165
-    ) -> bool | None: ...
1166
-
1167
-    def sendall(self, data: Buffer, flags: int = 0, /) -> None: ...
1168
-
1169
-    def recv(self, bufsize: int, flags: int = 0, /) -> bytes: ...
1170
-
1171
-
1172
-SSHAgentSocketProvider: TypeAlias = "Callable[[], SSHAgentSocket]"
1173
-"""A callable that provides an SSH agent socket."""
1174
-
1175
-
1176
-class SSHAgentSocketProviderEntry(NamedTuple):
1177
-    """Registry information for the table of SSH agent socket providers.
1178
-
1179
-    Third-party developers can register new socket providers for
1180
-    auto-discovery by setting up an entry point named
1181
-    [`derivepassphrase.ssh_agent_socket_providers`][derivepassphrase.ssh_agent.socketprovider.SocketProvider.ENTRY_POINT_GROUP_NAME],
1182
-    referencing an instance of this class.  Upon startup of the `vault`
1183
-    subsystem, `derivepassphrase` will then add appropriate entries to
1184
-    the registry.
1185
-
1186
-    Attributes:
1187
-        provider: The callable that provides the socket.
1188
-        key: The table key which this entry is registered as.
1189
-        aliases: Other keys that shall point to this entry's key.
1190
-
1191
-    Note:
1192
-        The socket provider registry table uses the key as the key, and
1193
-        the provider as the value. It does not store this info object
1194
-        directly.
1195
-
1196
-    """
1197
-
1198
-    provider: SSHAgentSocketProvider
1199
-    """"""
1200
-    key: str
1201
-    """"""
1202
-    aliases: tuple[str, ...]
... ...
@@ -14,6 +14,7 @@ from typing import TYPE_CHECKING, ClassVar, overload
14 14
 
15 15
 from typing_extensions import Never, Self, assert_type
16 16
 
17
+import derivepassphrase_sshagentsocketprovider as d_sasp
17 18
 from derivepassphrase import _types
18 19
 
19 20
 if sys.version_info < (3, 11):
... ...
@@ -171,7 +172,7 @@ class SSHAgentClient:
171 172
 
172 173
     """
173 174
 
174
-    _connection: _types.SSHAgentSocket
175
+    _connection: d_sasp.SSHAgentSocket
175 176
     SOCKET_PROVIDERS: ClassVar = ("native",)
176 177
     """
177 178
     The default list of SSH agent socket providers.
... ...
@@ -181,7 +182,7 @@ class SSHAgentClient:
181 182
         self,
182 183
         /,
183 184
         *,
184
-        socket: _types.SSHAgentSocket | Sequence[str] | None = None,
185
+        socket: d_sasp.SSHAgentSocket | Sequence[str] | None = None,
185 186
     ) -> None:
186 187
         """Initialize the client.
187 188
 
... ...
@@ -251,7 +252,7 @@ class SSHAgentClient:
251 252
                 socket = self.SOCKET_PROVIDERS
252 253
             assert isinstance(socket, Sequence)  # for the type checker
253 254
             excs: list[NotImplementedError] = []
254
-            providers: list[_types.SSHAgentSocketProvider] = []
255
+            providers: list[d_sasp.SSHAgentSocketProvider] = []
255 256
             for candidate in socket:
256 257
                 try:
257 258
                     provider = socketprovider.SocketProvider.resolve(candidate)
... ...
@@ -275,7 +276,7 @@ class SSHAgentClient:
275 276
                     if excs
276 277
                     else NotImplementedError(msg)
277 278
                 )
278
-        elif isinstance(socket, _types.SSHAgentSocket):
279
+        elif isinstance(socket, d_sasp.SSHAgentSocket):
279 280
             self._connection = socket
280 281
         else:  # pragma: no cover [failsafe]
281 282
             assert_type(socket, Never)
... ...
@@ -439,7 +440,7 @@ class SSHAgentClient:
439 440
     def ensure_agent_subcontext(
440 441
         cls,
441 442
         conn: SSHAgentClient
442
-        | _types.SSHAgentSocket
443
+        | d_sasp.SSHAgentSocket
443 444
         | Sequence[str]
444 445
         | None = None,
445 446
     ) -> Generator[SSHAgentClient, None, None]:
... ...
@@ -486,7 +487,7 @@ class SSHAgentClient:
486 487
             with contextlib.nullcontext():
487 488
                 yield conn
488 489
         elif (
489
-            isinstance(conn, (_types.SSHAgentSocket, str, Sequence))
490
+            isinstance(conn, (d_sasp.SSHAgentSocket, str, Sequence))
490 491
             or conn is None
491 492
         ):
492 493
             with SSHAgentClient(socket=conn) as client:
... ...
@@ -27,6 +27,7 @@ from ctypes.wintypes import (  # type: ignore[attr-defined]
27 27
 )
28 28
 from typing import TYPE_CHECKING, cast
29 29
 
30
+import derivepassphrase_sshagentsocketprovider as d_sasp
30 31
 from derivepassphrase import _types
31 32
 
32 33
 if TYPE_CHECKING:
... ...
@@ -45,7 +46,7 @@ if TYPE_CHECKING:
45 46
     )
46 47
 
47 48
     SSHAgentSocketProviderT = TypeVar(
48
-        "SSHAgentSocketProviderT", bound=_types.SSHAgentSocketProvider
49
+        "SSHAgentSocketProviderT", bound=d_sasp.SSHAgentSocketProvider
49 50
     )
50 51
     """A parametrized SSH agent socket provider."""
51 52
 
... ...
@@ -759,7 +760,7 @@ class SocketProvider:
759 760
         return cls._windows_named_pipe(None)
760 761
 
761 762
     registry: ClassVar[
762
-        dict[str, _types.SSHAgentSocketProvider | str | None]
763
+        dict[str, d_sasp.SSHAgentSocketProvider | str | None]
763 764
     ] = {}
764 765
     """A dictionary of callables that provide SSH agent sockets.
765 766
 
... ...
@@ -850,8 +851,8 @@ class SocketProvider:
850 851
 
851 852
     @classmethod
852 853
     def lookup(
853
-        cls, provider: _types.SSHAgentSocketProvider | str | None, /
854
-    ) -> _types.SSHAgentSocketProvider | None:
854
+        cls, provider: d_sasp.SSHAgentSocketProvider | str | None, /
855
+    ) -> d_sasp.SSHAgentSocketProvider | None:
855 856
         """Look up a socket provider entry.
856 857
 
857 858
         Args:
... ...
@@ -876,8 +877,8 @@ class SocketProvider:
876 877
 
877 878
     @classmethod
878 879
     def resolve(
879
-        cls, provider: _types.SSHAgentSocketProvider | str | None, /
880
-    ) -> _types.SSHAgentSocketProvider:
880
+        cls, provider: d_sasp.SSHAgentSocketProvider | str | None, /
881
+    ) -> d_sasp.SSHAgentSocketProvider:
881 882
         """Resolve a socket provider to a proper callable.
882 883
 
883 884
         Args:
... ...
@@ -925,10 +926,10 @@ class SocketProvider:
925 926
         })
926 927
         canonical: dict[str, str] = {}
927 928
         sorter: graphlib.TopologicalSorter[
928
-            _types.SSHAgentSocketProvider | str | None
929
+            d_sasp.SSHAgentSocketProvider | str | None
929 930
         ] = graphlib.TopologicalSorter()
930
-        k: _types.SSHAgentSocketProvider | str | None
931
-        v: _types.SSHAgentSocketProvider | str | None
931
+        k: d_sasp.SSHAgentSocketProvider | str | None
932
+        v: d_sasp.SSHAgentSocketProvider | str | None
932 933
         for k, v in cls.registry.items():
933 934
             sorter.add(k, v)
934 935
         sorter.prepare()
... ...
@@ -956,14 +957,6 @@ class SocketProvider:
956 957
             for k, v in itertools.groupby(sorted_entries, key=by_value)
957 958
         }
958 959
 
959
-    ENTRY_POINT_GROUP_NAME = "derivepassphrase.ssh_agent_socket_providers"
960
-    """
961
-    The group name under which [entry
962
-    points][importlib.metadata.entry_points] for the SSH agent socket
963
-    provider registry should be recorded.  Each target of such an entry
964
-    point should be a [`_types.SSHAgentSocketProviderEntry`][] object.
965
-    """
966
-
967 960
     @classmethod
968 961
     def _find_all_ssh_agent_socket_providers(cls) -> None:
969 962
         """Find and load all declared SSH agent socket providers.
... ...
@@ -988,10 +981,10 @@ class SocketProvider:
988 981
         origins: dict[str, str | None] = {}
989 982
         entries = collections.ChainMap({}, cls.registry)
990 983
         for entry_point in importlib.metadata.entry_points(
991
-            group=cls.ENTRY_POINT_GROUP_NAME
984
+            group=d_sasp.ENTRY_POINT_GROUP_NAME
992 985
         ):
993 986
             provider_entry = cast(
994
-                "_types.SSHAgentSocketProviderEntry", entry_point.load()
987
+                "d_sasp.SSHAgentSocketProviderEntry", entry_point.load()
995 988
             )
996 989
             key = provider_entry.key
997 990
             value = entry_point.value
... ...
@@ -1004,7 +997,7 @@ class SocketProvider:
1004 997
             if not callable(provider_entry.provider):
1005 998
                 msg = (
1006 999
                     f"Not an SSHAgentSocketProvider: "
1007
-                    f"{dist = }, {cls.ENTRY_POINT_GROUP_NAME = }, "
1000
+                    f"{dist = }, {d_sasp.ENTRY_POINT_GROUP_NAME = }, "
1008 1001
                     f"{value = }, {provider_entry = }"
1009 1002
                 )
1010 1003
                 raise AssertionError(msg)  # noqa: TRY004
... ...
@@ -16,13 +16,15 @@ from typing import TYPE_CHECKING, Final
16 16
 
17 17
 from typing_extensions import TypeAlias, assert_type
18 18
 
19
-from derivepassphrase import _types, sequin, ssh_agent
19
+from derivepassphrase import sequin, ssh_agent
20 20
 
21 21
 if TYPE_CHECKING:
22 22
     from collections.abc import Callable, Sequence
23 23
 
24 24
     from typing_extensions import Buffer
25 25
 
26
+    import derivepassphrase_sshagentsocketprovider as d_sasp
27
+
26 28
 
27 29
 class Vault:
28 30
     """A work-alike of James Coglan's vault.
... ...
@@ -517,7 +519,7 @@ class Vault:
517 519
         /,
518 520
         *,
519 521
         conn: ssh_agent.SSHAgentClient
520
-        | _types.SSHAgentSocket
522
+        | d_sasp.SSHAgentSocket
521 523
         | Sequence[str]
522 524
         | None = None,
523 525
     ) -> bytes:
... ...
@@ -27,6 +27,8 @@ from tests.machinery import pytest as pytest_machinery
27 27
 if TYPE_CHECKING:
28 28
     from collections.abc import Generator, Sequence
29 29
 
30
+    import derivepassphrase_sshagentsocketprovider as d_sasp
31
+
30 32
 startup_ssh_auth_sock = os.environ.get("SSH_AUTH_SOCK", None)
31 33
 
32 34
 
... ...
@@ -171,7 +173,7 @@ class SSHAgentInterfaceFunc(Protocol):
171 173
         self,
172 174
         executable: str | None,
173 175
         env: dict[str, Any],
174
-    ) -> tuple[_types.SSHAgentSocket, str] | None:
176
+    ) -> tuple[d_sasp.SSHAgentSocket, str] | None:
175 177
         """Interface the SSH agent.
176 178
 
177 179
         Args:
... ...
@@ -319,7 +321,7 @@ def spawn_noop(  # pragma: no cover [unused]
319 321
 def interface_pageant_on_the_annoying_os(
320 322
     executable: str | None,
321 323
     env: dict[str, Any],
322
-) -> tuple[_types.SSHAgentSocket, str] | None:  # pragma: no cover [external]
324
+) -> tuple[d_sasp.SSHAgentSocket, str] | None:  # pragma: no cover [external]
323 325
     """Interface a Pageant instance on The Annoying OS, if possible.
324 326
 
325 327
     Args:
... ...
@@ -346,7 +348,7 @@ def interface_pageant_on_the_annoying_os(
346 348
 def interface_openssh_agent_on_the_annoying_os(
347 349
     executable: str | None,
348 350
     env: dict[str, Any],
349
-) -> tuple[_types.SSHAgentSocket, str] | None:  # pragma: no cover [external]
351
+) -> tuple[d_sasp.SSHAgentSocket, str] | None:  # pragma: no cover [external]
350 352
     """Interface an OpenSSH agent instance on The Annoying OS, if possible.
351 353
 
352 354
     Args:
... ...
@@ -778,7 +780,7 @@ def spawn_named_agent(  # noqa: C901
778 780
             not isinstance(proc_or_socket_data, subprocess.Popen)
779 781
             and proc_or_socket_data is not None
780 782
         ):
781
-            socket: _types.SSHAgentSocket
783
+            socket: d_sasp.SSHAgentSocket
782 784
             socket, ssh_auth_sock = proc_or_socket_data
783 785
             monkeypatch.setenv("SSH_AUTH_SOCK", ssh_auth_sock)
784 786
             client = exit_stack.enter_context(
... ...
@@ -30,6 +30,7 @@ from typing import TYPE_CHECKING, cast
30 30
 
31 31
 from typing_extensions import NamedTuple, assert_never
32 32
 
33
+import derivepassphrase_sshagentsocketprovider as d_sasp
33 34
 from derivepassphrase import _types, ssh_agent, vault
34 35
 from derivepassphrase.ssh_agent import socketprovider
35 36
 
... ...
@@ -533,7 +534,7 @@ class RunningSSHAgentInfo(NamedTuple):
533 534
 
534 535
     """
535 536
 
536
-    socket: str | type[_types.SSHAgentSocket]
537
+    socket: str | type[d_sasp.SSHAgentSocket]
537 538
     """"""
538 539
     agent_type: KnownSSHAgentType
539 540
     """"""
... ...
@@ -661,7 +662,7 @@ class VaultTestConfig(NamedTuple):
661 662
 # ------------------------------
662 663
 
663 664
 
664
-ssh_auth_sock_on_posix_entry = _types.SSHAgentSocketProviderEntry(
665
+ssh_auth_sock_on_posix_entry = d_sasp.SSHAgentSocketProviderEntry(
665 666
     socketprovider.SocketProvider.resolve(
666 667
         _types.BuiltinSSHAgentSocketProvider.SSH_AUTH_SOCK_ON_POSIX
667 668
     ),
... ...
@@ -673,7 +674,7 @@ The standard [`_types.SSHAgentSocketProviderEntry`][] for the UNIX
673 674
 domain socket handler on POSIX systems.
674 675
 """
675 676
 
676
-ssh_auth_sock_on_windows_entry = _types.SSHAgentSocketProviderEntry(
677
+ssh_auth_sock_on_windows_entry = d_sasp.SSHAgentSocketProviderEntry(
677 678
     socketprovider.SocketProvider.resolve(
678 679
         _types.BuiltinSSHAgentSocketProvider.SSH_AUTH_SOCK_ON_WINDOWS
679 680
     ),
... ...
@@ -685,7 +686,7 @@ The standard [`_types.SSHAgentSocketProviderEntry`][] for the Windows
685 686
 named pipe handler on The Annoying Operating System.
686 687
 """
687 688
 
688
-faulty_entry_callable = _types.SSHAgentSocketProviderEntry(
689
+faulty_entry_callable = d_sasp.SSHAgentSocketProviderEntry(
689 690
     (),  # type: ignore[arg-type]
690 691
     "tuple",
691 692
     (),
... ...
@@ -695,7 +696,7 @@ A faulty [`_types.SSHAgentSocketProviderEntry`][]: the indicated handler
695 696
 is not a callable.
696 697
 """
697 698
 
698
-faulty_entry_name_exists = _types.SSHAgentSocketProviderEntry(
699
+faulty_entry_name_exists = d_sasp.SSHAgentSocketProviderEntry(
699 700
     socketprovider.SocketProvider.resolve(
700 701
         _types.BuiltinSSHAgentSocketProvider.WINDOWS
701 702
     ),
... ...
@@ -707,7 +708,7 @@ A faulty [`_types.SSHAgentSocketProviderEntry`][]: the indicated handler
707 708
 is already registered with a different callable.
708 709
 """
709 710
 
710
-faulty_entry_alias_exists = _types.SSHAgentSocketProviderEntry(
711
+faulty_entry_alias_exists = d_sasp.SSHAgentSocketProviderEntry(
711 712
     socketprovider.SocketProvider.resolve(
712 713
         _types.BuiltinSSHAgentSocketProvider.SSH_AUTH_SOCK_ON_POSIX
713 714
     ),
... ...
@@ -20,6 +20,7 @@ import shlex
20 20
 import stat
21 21
 from typing import TYPE_CHECKING
22 22
 
23
+import derivepassphrase_sshagentsocketprovider as d_sasp
23 24
 import tests.data
24 25
 from derivepassphrase import _types, ssh_agent, vault
25 26
 
... ...
@@ -159,18 +160,18 @@ def phrase_from_key(
159 160
 # ===============================================
160 161
 
161 162
 
162
-def provider_entry_provider() -> _types.SSHAgentSocket:  # pragma: no cover
163
+def provider_entry_provider() -> d_sasp.SSHAgentSocket:  # pragma: no cover
163 164
     """A pseudo provider for a [`_types.SSHAgentSocketProviderEntry`][]."""
164 165
     msg = "We are not supposed to be called!"
165 166
     raise AssertionError(msg)
166 167
 
167 168
 
168
-provider_entry1 = _types.SSHAgentSocketProviderEntry(
169
+provider_entry1 = d_sasp.SSHAgentSocketProviderEntry(
169 170
     provider_entry_provider, "entry1", ("entry1a", "entry1b", "entry1c")
170 171
 )
171 172
 """A sample [`_types.SSHAgentSocketProviderEntry`][]."""
172 173
 
173
-provider_entry2 = _types.SSHAgentSocketProviderEntry(
174
+provider_entry2 = d_sasp.SSHAgentSocketProviderEntry(
174 175
     provider_entry_provider, "entry2", ("entry2d", "entry2e")
175 176
 )
176 177
 
... ...
@@ -51,6 +51,8 @@ if TYPE_CHECKING:
51 51
 
52 52
     from typing_extensions import Any
53 53
 
54
+    import derivepassphrase_sshagentsocketprovider as d_sasp
55
+
54 56
 
55 57
 # Marks
56 58
 # =====
... ...
@@ -623,7 +625,7 @@ def ensure_singleton_client_if_non_reentrant_agent(
623 625
 
624 626
         def ensure_agent_subcontext(
625 627
             conn: ssh_agent.SSHAgentClient
626
-            | _types.SSHAgentSocket
628
+            | d_sasp.SSHAgentSocket
627 629
             | Sequence[str]
628 630
             | None = None,
629 631
         ) -> contextlib.AbstractContextManager[ssh_agent.SSHAgentClient]:
... ...
@@ -44,6 +44,8 @@ if TYPE_CHECKING:
44 44
 
45 45
     from typing_extensions import Any
46 46
 
47
+    import derivepassphrase_sshagentsocketprovider as d_sasp
48
+
47 49
 
48 50
 DUMMY_SERVICE = data.DUMMY_SERVICE
49 51
 DUMMY_PASSPHRASE = data.DUMMY_PASSPHRASE
... ...
@@ -1154,7 +1156,7 @@ class TestMisc:
1154 1156
                 "list_keys",
1155 1157
                 callables.list_keys,
1156 1158
             )
1157
-            hint: ssh_agent.SSHAgentClient | _types.SSHAgentSocket | None
1159
+            hint: ssh_agent.SSHAgentClient | d_sasp.SSHAgentSocket | None
1158 1160
             # TODO(the-13th-letter): Rewrite using structural pattern
1159 1161
             # matching.
1160 1162
             # https://the13thletter.info/derivepassphrase/latest/pycompatibility/#after-eol-py3.9
... ...
@@ -28,6 +28,8 @@ if TYPE_CHECKING:
28 28
     from collections.abc import Generator
29 29
     from typing import NoReturn
30 30
 
31
+    import derivepassphrase_sshagentsocketprovider as d_sasp
32
+
31 33
 DUMMY_SERVICE = data.DUMMY_SERVICE
32 34
 DUMMY_PASSPHRASE = data.DUMMY_PASSPHRASE
33 35
 DUMMY_CONFIG_SETTINGS = data.DUMMY_CONFIG_SETTINGS
... ...
@@ -578,7 +580,7 @@ class TestKeyBasic:
578 580
             },
579 581
         },
580 582
         main_config_str: str | None = None,
581
-        registry: dict[str, _types.SSHAgentSocketProvider | str | None]
583
+        registry: dict[str, d_sasp.SSHAgentSocketProvider | str | None]
582 584
         | None = None,
583 585
     ) -> Generator[machinery.CliRunner, None, None]:
584 586
         runner = machinery.CliRunner(mix_stderr=False)
... ...
@@ -647,7 +649,7 @@ class TestKeyBasic:
647 649
             "services": {DUMMY_SERVICE: DUMMY_CONFIG_SETTINGS}
648 650
         },
649 651
         main_config_str: str | None = None,
650
-        registry: dict[str, _types.SSHAgentSocketProvider | str | None]
652
+        registry: dict[str, d_sasp.SSHAgentSocketProvider | str | None]
651 653
         | None = None,
652 654
         multiline: bool = False,
653 655
         input: str | bytes | None = None,
... ...
@@ -684,11 +686,11 @@ class TestKeyBasic:
684 686
 
685 687
 @pytest.fixture
686 688
 def provider_registry() -> dict[
687
-    str, _types.SSHAgentSocketProvider | str | None
689
+    str, d_sasp.SSHAgentSocketProvider | str | None
688 690
 ]:
689 691
     """Set up a controlled SSH agent socket provider registry."""
690 692
 
691
-    def err() -> _types.SSHAgentSocket:
693
+    def err() -> d_sasp.SSHAgentSocket:
692 694
         pytest.fail("Attempting to use the wrong SSH agent socket provider!")
693 695
 
694 696
     return {
... ...
@@ -722,7 +724,7 @@ class TestKeyExplicitSSHAgentSocketProvider(TestKeyBasic):
722 724
     def test_explicit_ssh_agent_socket_provider(
723 725
         self,
724 726
         provider_registry: dict[
725
-            str, _types.SSHAgentSocketProvider | str | None
727
+            str, d_sasp.SSHAgentSocketProvider | str | None
726 728
         ],
727 729
         config: _types.VaultConfig,
728 730
         on_command_line: bool,
... ...
@@ -748,7 +750,7 @@ class TestKeyExplicitSSHAgentSocketProvider(TestKeyBasic):
748 750
     def test_explicit_ssh_agent_socket_provider_not_found(
749 751
         self,
750 752
         provider_registry: dict[
751
-            str, _types.SSHAgentSocketProvider | str | None
753
+            str, d_sasp.SSHAgentSocketProvider | str | None
752 754
         ],
753 755
         config: _types.VaultConfig,
754 756
         on_command_line: bool,
... ...
@@ -19,6 +19,7 @@ import pytest
19 19
 from hypothesis import strategies
20 20
 from typing_extensions import TypeAlias
21 21
 
22
+import derivepassphrase_sshagentsocketprovider as d_sasp
22 23
 from derivepassphrase import _types, ssh_agent, vault
23 24
 from derivepassphrase._internals import cli_helpers
24 25
 from derivepassphrase.ssh_agent import socketprovider
... ...
@@ -49,7 +50,7 @@ class Parametrize(pytest_machinery.Parametrize):
49 50
                 [
50 51
                     importlib.metadata.EntryPoint(
51 52
                         name=data.faulty_entry_callable.key,
52
-                        group=socketprovider.SocketProvider.ENTRY_POINT_GROUP_NAME,
53
+                        group=d_sasp.ENTRY_POINT_GROUP_NAME,
53 54
                         value="tests.data: faulty_entry_callable",
54 55
                     ),
55 56
                 ],
... ...
@@ -59,7 +60,7 @@ class Parametrize(pytest_machinery.Parametrize):
59 60
                 [
60 61
                     importlib.metadata.EntryPoint(
61 62
                         name=data.faulty_entry_name_exists.key,
62
-                        group=socketprovider.SocketProvider.ENTRY_POINT_GROUP_NAME,
63
+                        group=d_sasp.ENTRY_POINT_GROUP_NAME,
63 64
                         value="tests.data: faulty_entry_name_exists",
64 65
                     ),
65 66
                 ],
... ...
@@ -69,7 +70,7 @@ class Parametrize(pytest_machinery.Parametrize):
69 70
                 [
70 71
                     importlib.metadata.EntryPoint(
71 72
                         name=data.faulty_entry_alias_exists.key,
72
-                        group=socketprovider.SocketProvider.ENTRY_POINT_GROUP_NAME,
73
+                        group=d_sasp.ENTRY_POINT_GROUP_NAME,
73 74
                         value="tests.data: faulty_entry_alias_exists",
74 75
                     ),
75 76
                 ],
... ...
@@ -84,12 +85,12 @@ class Parametrize(pytest_machinery.Parametrize):
84 85
                 [
85 86
                     importlib.metadata.EntryPoint(
86 87
                         name=data.ssh_auth_sock_on_posix_entry.key,
87
-                        group=socketprovider.SocketProvider.ENTRY_POINT_GROUP_NAME,
88
+                        group=d_sasp.ENTRY_POINT_GROUP_NAME,
88 89
                         value="tests.data: ssh_auth_sock_on_posix_entry",
89 90
                     ),
90 91
                     importlib.metadata.EntryPoint(
91 92
                         name=data.ssh_auth_sock_on_windows_entry.key,
92
-                        group=socketprovider.SocketProvider.ENTRY_POINT_GROUP_NAME,
93
+                        group=d_sasp.ENTRY_POINT_GROUP_NAME,
93 94
                         value="tests.data: ssh_auth_sock_on_windows_entry",
94 95
                     ),
95 96
                 ],
... ...
@@ -99,12 +100,12 @@ class Parametrize(pytest_machinery.Parametrize):
99 100
                 [
100 101
                     importlib.metadata.EntryPoint(
101 102
                         name=callables.provider_entry1.key,
102
-                        group=socketprovider.SocketProvider.ENTRY_POINT_GROUP_NAME,
103
+                        group=d_sasp.ENTRY_POINT_GROUP_NAME,
103 104
                         value="tests.data.callables: provider_entry1",
104 105
                     ),
105 106
                     importlib.metadata.EntryPoint(
106 107
                         name=callables.provider_entry2.key,
107
-                        group=socketprovider.SocketProvider.ENTRY_POINT_GROUP_NAME,
108
+                        group=d_sasp.ENTRY_POINT_GROUP_NAME,
108 109
                         value="tests.data.callables: provider_entry2",
109 110
                     ),
110 111
                 ],
... ...
@@ -439,7 +440,7 @@ class Strategies:
439 440
 
440 441
     @staticmethod
441 442
     def io_operations_on_handles() -> strategies.SearchStrategy[
442
-        Callable[[_types.SSHAgentSocket], Any]
443
+        Callable[[d_sasp.SSHAgentSocket], Any]
443 444
     ]:
444 445
         """Return an I/O operation on an SSH agent socket."""
445 446
         return strategies.one_of(
... ...
@@ -753,7 +754,7 @@ class TestSSHAgentSocketProviderRegistry:
753 754
             pytest.fail("Native SSH agent socket provider is unavailable?!")
754 755
         # TODO(the-13th-letter): Rewrite using structural pattern matching.
755 756
         # https://the13thletter.info/derivepassphrase/latest/pycompatibility/#after-eol-py3.9
756
-        target: _types.SSHAgentSocketProvider | str | None = (
757
+        target: d_sasp.SSHAgentSocketProvider | str | None = (
757 758
             None
758 759
             if terminal == "unimplemented"
759 760
             else _types.BuiltinSSHAgentSocketProvider.NATIVE
... ...
@@ -823,8 +824,8 @@ class TestSSHAgentSocketProviderRegistry:
823 824
         register = socketprovider.SocketProvider.register
824 825
 
825 826
         def ancestry_chain(name: str) -> Generator[str, None, None]:
826
-            current: _types.SSHAgentSocketProvider | str | None = name
827
-            seen: set[_types.SSHAgentSocketProvider | str | None] = set()
827
+            current: d_sasp.SSHAgentSocketProvider | str | None = name
828
+            seen: set[d_sasp.SSHAgentSocketProvider | str | None] = set()
828 829
             while isinstance(current, str) and current not in seen:
829 830
                 yield current
830 831
                 seen.add(current)
... ...
@@ -889,7 +890,7 @@ class TestSSHAgentSocketProviderRegistry:
889 890
     ) -> None:
890 891
         """Registering new entries works."""
891 892
 
892
-        def socket_provider() -> _types.SSHAgentSocket:
893
+        def socket_provider() -> d_sasp.SSHAgentSocket:
893 894
             raise AssertionError
894 895
 
895 896
         names = ["spam", "ham", "eggs", "parrot"]
... ...
@@ -7,6 +7,7 @@
7 7
 from __future__ import annotations
8 8
 
9 9
 import contextlib
10
+from typing import TYPE_CHECKING
10 11
 
11 12
 import pytest
12 13
 from hypothesis import stateful, strategies
... ...
@@ -15,6 +16,9 @@ from derivepassphrase import _types
15 16
 from derivepassphrase.ssh_agent import socketprovider
16 17
 from tests.machinery import pytest as pytest_machinery
17 18
 
19
+if TYPE_CHECKING:
20
+    import derivepassphrase_sshagentsocketprovider as d_sasp
21
+
18 22
 # All tests in this module are heavy-duty tests.
19 23
 pytestmark = [pytest_machinery.heavy_duty]
20 24
 
... ...
@@ -146,7 +150,7 @@ class SSHAgentSocketProviderRegistryStateMachine(
146 150
         )
147 151
         self.orig_registry = socketprovider.SocketProvider.registry
148 152
         self.registry: dict[
149
-            str, _types.SSHAgentSocketProvider | str | None
153
+            str, d_sasp.SSHAgentSocketProvider | str | None
150 154
         ] = {
151 155
             _types.BuiltinSSHAgentSocketProvider.SSH_AUTH_SOCK_ON_POSIX: (
152 156
                 self.orig_registry[
... ...
@@ -176,14 +180,14 @@ class SSHAgentSocketProviderRegistryStateMachine(
176 180
         self.monkeypatch.setattr(
177 181
             socketprovider.SocketProvider, "registry", self.registry
178 182
         )
179
-        self.model: dict[str, _types.SSHAgentSocketProvider | None] = {}
183
+        self.model: dict[str, d_sasp.SSHAgentSocketProvider | None] = {}
180 184
 
181 185
     known_keys: stateful.Bundle[str] = stateful.Bundle("known_keys")
182 186
     """"""
183 187
     new_keys: stateful.Bundle[str] = stateful.Bundle("new_keys")
184 188
     """"""
185 189
 
186
-    def sample_provider(self) -> _types.SSHAgentSocket:
190
+    def sample_provider(self) -> d_sasp.SSHAgentSocket:
187 191
         raise AssertionError
188 192
 
189 193
     @stateful.initialize(
190 194