Marco Ricci commited on 2024-10-01 11:51:05
Zeige 12 geänderte Dateien mit 102 Einfügungen und 103 Löschungen.
Support Python 3.9 by not using the following features: - `match`/`case` statements; replace them with `if`-statements - annotation-like class unions in `isinstance`; use a tuple instead - `TypeAlias` objects; write these as strings - the `root_dir` parameter of `glob.glob`; build the file list via `fnmatch.fnmatch` ourselves
... | ... |
@@ -6,7 +6,7 @@ build-backend = "hatchling.build" |
6 | 6 |
name = "derivepassphrase" |
7 | 7 |
description = "An almost faithful Python reimplementation of James Coglan's vault." |
8 | 8 |
readme = "README.md" |
9 |
-requires-python = ">= 3.10" |
|
9 |
+requires-python = ">= 3.9" |
|
10 | 10 |
license = "MIT" |
11 | 11 |
keywords = [] |
12 | 12 |
authors = [ |
... | ... |
@@ -133,7 +133,7 @@ extra-dependencies = [ |
133 | 133 |
matrix-name-format = '{variable}_{value}' |
134 | 134 |
|
135 | 135 |
[[tool.hatch.envs.hatch-test.matrix]] |
136 |
-python = ["3.12", "3.11", "3.10", "pypy3.10"] |
|
136 |
+python = ["3.12", "3.11", "3.10", "3.9", "pypy3.10", "pypy3.9"] |
|
137 | 137 |
cryptography = ["no", "yes"] |
138 | 138 |
hypothesis-profile = ["user-default"] |
139 | 139 |
|
... | ... |
@@ -221,18 +221,18 @@ def validate_vault_config( # noqa: C901,PLR0912,PLR0915 |
221 | 221 |
if not isinstance(o_global, dict): |
222 | 222 |
raise TypeError(err_not_a_dict(['global'])) |
223 | 223 |
for key, value in o_global.items(): |
224 |
- match key: |
|
225 |
- case 'key' | 'phrase': |
|
224 |
+ # Use match/case here once Python 3.9 becomes unsupported. |
|
225 |
+ if key in {'key', 'phrase'}: |
|
226 | 226 |
if not isinstance(value, str): |
227 | 227 |
raise TypeError(err_not_a_dict(['global', key])) |
228 |
- case 'unicode_normalization_form': |
|
228 |
+ elif key == 'unicode_normalization_form': |
|
229 | 229 |
if not isinstance(value, str): |
230 | 230 |
raise TypeError(err_not_a_dict(['global', key])) |
231 | 231 |
if not allow_derivepassphrase_extensions: |
232 | 232 |
raise ValueError( |
233 | 233 |
err_derivepassphrase_extension(key, ('global',)) |
234 | 234 |
) |
235 |
- case _ if not allow_unknown_settings: |
|
235 |
+ elif not allow_unknown_settings: |
|
236 | 236 |
raise ValueError(err_unknown_setting(key, ('global',))) |
237 | 237 |
if 'key' in o_global and 'phrase' in o_global: |
238 | 238 |
raise ValueError(err_key_and_phrase) |
... | ... |
@@ -244,17 +244,15 @@ def validate_vault_config( # noqa: C901,PLR0912,PLR0915 |
244 | 244 |
if not isinstance(service, dict): |
245 | 245 |
raise TypeError(err_not_a_dict(['services', sv_name])) |
246 | 246 |
for key, value in service.items(): |
247 |
- match key: |
|
248 |
- case 'notes' | 'phrase' | 'key': |
|
247 |
+ # Use match/case here once Python 3.9 becomes unsupported. |
|
248 |
+ if key in {'notes', 'phrase', 'key'}: |
|
249 | 249 |
if not isinstance(value, str): |
250 | 250 |
raise TypeError( |
251 | 251 |
err_not_a_string(['services', sv_name, key]) |
252 | 252 |
) |
253 |
- case 'length': |
|
253 |
+ elif key == 'length': |
|
254 | 254 |
if not isinstance(value, int): |
255 |
- raise TypeError( |
|
256 |
- err_not_an_int(['services', sv_name, key]) |
|
257 |
- ) |
|
255 |
+ raise TypeError(err_not_an_int(['services', sv_name, key])) |
|
258 | 256 |
if value < 1: |
259 | 257 |
raise ValueError( |
260 | 258 |
err_bad_number( |
... | ... |
@@ -263,19 +261,17 @@ def validate_vault_config( # noqa: C901,PLR0912,PLR0915 |
263 | 261 |
strictly_positive=True, |
264 | 262 |
) |
265 | 263 |
) |
266 |
- case ( |
|
267 |
- 'repeat' |
|
268 |
- | 'lower' |
|
269 |
- | 'upper' |
|
270 |
- | 'number' |
|
271 |
- | 'space' |
|
272 |
- | 'dash' |
|
273 |
- | 'symbol' |
|
274 |
- ): |
|
264 |
+ elif key in { |
|
265 |
+ 'repeat', |
|
266 |
+ 'lower', |
|
267 |
+ 'upper', |
|
268 |
+ 'number', |
|
269 |
+ 'space', |
|
270 |
+ 'dash', |
|
271 |
+ 'symbol', |
|
272 |
+ }: |
|
275 | 273 |
if not isinstance(value, int): |
276 |
- raise TypeError( |
|
277 |
- err_not_an_int(['services', sv_name, key]) |
|
278 |
- ) |
|
274 |
+ raise TypeError(err_not_an_int(['services', sv_name, key])) |
|
279 | 275 |
if value < 0: |
280 | 276 |
raise ValueError( |
281 | 277 |
err_bad_number( |
... | ... |
@@ -284,7 +280,7 @@ def validate_vault_config( # noqa: C901,PLR0912,PLR0915 |
284 | 280 |
strictly_positive=False, |
285 | 281 |
) |
286 | 282 |
) |
287 |
- case _ if not allow_unknown_settings: |
|
283 |
+ elif not allow_unknown_settings: |
|
288 | 284 |
raise ValueError( |
289 | 285 |
err_unknown_setting(key, ['services', sv_name]) |
290 | 286 |
) |
... | ... |
@@ -202,8 +202,8 @@ def _load_data( |
202 | 202 |
) -> Any: # noqa: ANN401 |
203 | 203 |
contents: bytes |
204 | 204 |
module: types.ModuleType |
205 |
- match fmt: |
|
206 |
- case 'v0.2': |
|
205 |
+ # Use match/case here once Python 3.9 becomes unsupported. |
|
206 |
+ if fmt == 'v0.2': |
|
207 | 207 |
module = importlib.import_module( |
208 | 208 |
'derivepassphrase.exporter.vault_native' |
209 | 209 |
) |
... | ... |
@@ -214,7 +214,7 @@ def _load_data( |
214 | 214 |
return module.export_vault_native_data( |
215 | 215 |
contents, key, try_formats=['v0.2'] |
216 | 216 |
) |
217 |
- case 'v0.3': |
|
217 |
+ elif fmt == 'v0.3': # noqa: RET505 |
|
218 | 218 |
module = importlib.import_module( |
219 | 219 |
'derivepassphrase.exporter.vault_native' |
220 | 220 |
) |
... | ... |
@@ -225,14 +225,12 @@ def _load_data( |
225 | 225 |
return module.export_vault_native_data( |
226 | 226 |
contents, key, try_formats=['v0.3'] |
227 | 227 |
) |
228 |
- case 'storeroom': |
|
229 |
- module = importlib.import_module( |
|
230 |
- 'derivepassphrase.exporter.storeroom' |
|
231 |
- ) |
|
228 |
+ elif fmt == 'storeroom': |
|
229 |
+ module = importlib.import_module('derivepassphrase.exporter.storeroom') |
|
232 | 230 |
if module.STUBBED: |
233 | 231 |
raise ModuleNotFoundError |
234 | 232 |
return module.export_storeroom_data(path, key) |
235 |
- case _: # pragma: no cover |
|
233 |
+ else: # pragma: no cover |
|
236 | 234 |
assert_never(fmt) |
237 | 235 |
|
238 | 236 |
|
... | ... |
@@ -373,12 +371,12 @@ def _config_filename( |
373 | 371 |
path = os.getenv(PROG_NAME.upper() + '_PATH') or click.get_app_dir( |
374 | 372 |
PROG_NAME, force_posix=True |
375 | 373 |
) |
376 |
- match subsystem: |
|
377 |
- case None: |
|
374 |
+ # Use match/case here once Python 3.9 becomes unsupported. |
|
375 |
+ if subsystem is None: |
|
378 | 376 |
return path |
379 |
- case 'vault' | 'settings': |
|
377 |
+ elif subsystem in {'vault', 'settings'}: # noqa: RET505 |
|
380 | 378 |
filename = f'{subsystem}.json' |
381 |
- case _: # pragma: no cover |
|
379 |
+ else: # pragma: no cover |
|
382 | 380 |
msg = f'Unknown configuration subsystem: {subsystem!r}' |
383 | 381 |
raise AssertionError(msg) |
384 | 382 |
return os.path.join(path, filename) |
... | ... |
@@ -522,14 +520,14 @@ def _get_suitable_ssh_keys( |
522 | 520 |
""" |
523 | 521 |
client: ssh_agent.SSHAgentClient |
524 | 522 |
client_context: contextlib.AbstractContextManager[Any] |
525 |
- match conn: |
|
526 |
- case ssh_agent.SSHAgentClient(): |
|
523 |
+ # Use match/case here once Python 3.9 becomes unsupported. |
|
524 |
+ if isinstance(conn, ssh_agent.SSHAgentClient): |
|
527 | 525 |
client = conn |
528 | 526 |
client_context = contextlib.nullcontext() |
529 |
- case socket.socket() | None: |
|
527 |
+ elif isinstance(conn, socket.socket) or conn is None: |
|
530 | 528 |
client = ssh_agent.SSHAgentClient(socket=conn) |
531 | 529 |
client_context = client |
532 |
- case _: # pragma: no cover |
|
530 |
+ else: # pragma: no cover |
|
533 | 531 |
assert_never(conn) |
534 | 532 |
msg = f'invalid connection hint: {conn!r}' |
535 | 533 |
raise TypeError(msg) # noqa: DOC501 |
... | ... |
@@ -1216,18 +1214,18 @@ def derivepassphrase_vault( # noqa: C901,PLR0912,PLR0913,PLR0914,PLR0915 |
1216 | 1214 |
for param in ctx.command.params: |
1217 | 1215 |
if isinstance(param, click.Option): |
1218 | 1216 |
group: type[click.Option] |
1219 |
- match param: |
|
1220 |
- case PasswordGenerationOption(): |
|
1217 |
+ # Use match/case here once Python 3.9 becomes unsupported. |
|
1218 |
+ if isinstance(param, PasswordGenerationOption): |
|
1221 | 1219 |
group = PasswordGenerationOption |
1222 |
- case ConfigurationOption(): |
|
1220 |
+ elif isinstance(param, ConfigurationOption): |
|
1223 | 1221 |
group = ConfigurationOption |
1224 |
- case StorageManagementOption(): |
|
1222 |
+ elif isinstance(param, StorageManagementOption): |
|
1225 | 1223 |
group = StorageManagementOption |
1226 |
- case OptionGroupOption(): |
|
1227 |
- raise AssertionError( # noqa: DOC501,TRY003 |
|
1224 |
+ elif isinstance(param, OptionGroupOption): |
|
1225 |
+ raise AssertionError( # noqa: DOC501,TRY003,TRY004 |
|
1228 | 1226 |
f'Unknown option group for {param!r}' # noqa: EM102 |
1229 | 1227 |
) |
1230 |
- case _: |
|
1228 |
+ else: |
|
1231 | 1229 |
group = click.Option |
1232 | 1230 |
options_in_group.setdefault(group, []).append(param) |
1233 | 1231 |
params_by_str[param.human_readable_name] = param |
... | ... |
@@ -23,7 +23,7 @@ should *not* be used or relied on. |
23 | 23 |
from __future__ import annotations |
24 | 24 |
|
25 | 25 |
import base64 |
26 |
-import glob |
|
26 |
+import fnmatch |
|
27 | 27 |
import json |
28 | 28 |
import logging |
29 | 29 |
import os |
... | ... |
@@ -678,9 +678,15 @@ def export_storeroom_data( # noqa: C901,PLR0912,PLR0914,PLR0915 |
678 | 678 |
|
679 | 679 |
config_structure: dict[str, Any] = {} |
680 | 680 |
json_contents: dict[str, bytes] = {} |
681 |
- for file in glob.glob( |
|
682 |
- '[01][0-9a-f]', root_dir=os.fsdecode(storeroom_path) |
|
683 |
- ): |
|
681 |
+ # Use glob.glob(..., root_dir=...) here once Python 3.9 becomes |
|
682 |
+ # unsupported. |
|
683 |
+ storeroom_path_str = os.fsdecode(storeroom_path) |
|
684 |
+ valid_hashdirs = [ |
|
685 |
+ hashdir_name |
|
686 |
+ for hashdir_name in os.listdir(storeroom_path_str) |
|
687 |
+ if fnmatch.fnmatch(hashdir_name, '[01][0-9a-f]') |
|
688 |
+ ] |
|
689 |
+ for file in valid_hashdirs: |
|
684 | 690 |
bucket_contents = list( |
685 | 691 |
decrypt_bucket_file(file, master_keys, root_dir=storeroom_path) |
686 | 692 |
) |
... | ... |
@@ -441,20 +441,20 @@ def export_vault_native_data( |
441 | 441 |
key = exporter.get_vault_key() |
442 | 442 |
stored_exception: Exception | None = None |
443 | 443 |
for config_format in try_formats: |
444 |
- match config_format: |
|
445 |
- case 'v0.2': |
|
444 |
+ # Use match/case here once Python 3.9 becomes unsupported. |
|
445 |
+ if config_format == 'v0.2': |
|
446 | 446 |
try: |
447 | 447 |
return VaultNativeV02ConfigParser(contents, key)() |
448 | 448 |
except ValueError as exc: |
449 | 449 |
exc.__context__ = stored_exception |
450 | 450 |
stored_exception = exc |
451 |
- case 'v0.3': |
|
451 |
+ elif config_format == 'v0.3': |
|
452 | 452 |
try: |
453 | 453 |
return VaultNativeV03ConfigParser(contents, key)() |
454 | 454 |
except ValueError as exc: |
455 | 455 |
exc.__context__ = stored_exception |
456 | 456 |
stored_exception = exc |
457 |
- case _: # pragma: no cover |
|
457 |
+ else: # pragma: no cover |
|
458 | 458 |
msg = ( |
459 | 459 |
f'Invalid vault native configuration format: ' |
460 | 460 |
f'{config_format!r}' |
... | ... |
@@ -43,14 +43,17 @@ class SSHAgentFailedError(RuntimeError): |
43 | 43 |
"""The SSH agent failed to complete the requested operation.""" |
44 | 44 |
|
45 | 45 |
def __str__(self) -> str: |
46 |
- match self.args: |
|
47 |
- case (_types.SSH_AGENT.FAILURE.value, b''): # pragma: no branch |
|
46 |
+ # Use match/case here once Python 3.9 becomes unsupported. |
|
47 |
+ if self.args == ( # pragma: no branch |
|
48 |
+ _types.SSH_AGENT.FAILURE.value, |
|
49 |
+ b'', |
|
50 |
+ ): |
|
48 | 51 |
return 'The SSH agent failed to complete the request' |
49 |
- case (_, _msg) if _msg: # pragma: no cover |
|
52 |
+ elif self.args[1]: # noqa: RET505 # pragma: no cover |
|
50 | 53 |
code = self.args[0] |
51 | 54 |
msg = self.args[1].decode('utf-8', 'surrogateescape') |
52 | 55 |
return f'[Code {code:d}] {msg:s}' |
53 |
- case _: # pragma: no cover |
|
56 |
+ else: # pragma: no cover |
|
54 | 57 |
return repr(self) |
55 | 58 |
|
56 | 59 |
def __repr__(self) -> str: # pragma: no cover |
... | ... |
@@ -348,7 +351,7 @@ class SSHAgentClient: |
348 | 351 |
|
349 | 352 |
""" |
350 | 353 |
if isinstance( # pragma: no branch |
351 |
- response_code, int | _types.SSH_AGENT |
|
354 |
+ response_code, (int, _types.SSH_AGENT) |
|
352 | 355 |
): |
353 | 356 |
response_code = frozenset({response_code}) |
354 | 357 |
if response_code is not None: # pragma: no branch |
... | ... |
@@ -11,13 +11,15 @@ import collections |
11 | 11 |
import hashlib |
12 | 12 |
import math |
13 | 13 |
import types |
14 |
-from collections.abc import Callable |
|
15 |
-from typing import TypeAlias |
|
14 |
+from typing import TYPE_CHECKING |
|
16 | 15 |
|
17 |
-from typing_extensions import assert_type |
|
16 |
+from typing_extensions import TypeAlias, assert_type |
|
18 | 17 |
|
19 | 18 |
from derivepassphrase import sequin, ssh_agent |
20 | 19 |
|
20 |
+if TYPE_CHECKING: |
|
21 |
+ from collections.abc import Callable |
|
22 |
+ |
|
21 | 23 |
__author__ = 'Marco Ricci <software@the13thletter.info>' |
22 | 24 |
|
23 | 25 |
|
... | ... |
@@ -458,7 +460,7 @@ class Vault: |
458 | 460 |
a passphrase deterministically. |
459 | 461 |
|
460 | 462 |
""" |
461 |
- TestFunc: TypeAlias = Callable[[bytes | bytearray], bool] |
|
463 |
+ TestFunc: TypeAlias = 'Callable[[bytes | bytearray], bool]' |
|
462 | 464 |
deterministic_signature_types: dict[str, TestFunc] |
463 | 465 |
deterministic_signature_types = { |
464 | 466 |
'ssh-ed25519': lambda k: k.startswith( |
... | ... |
@@ -806,11 +806,11 @@ def isolated_vault_exporter_config( |
806 | 806 |
monkeypatch.delenv('USERNAME', raising=False) |
807 | 807 |
if vault_key is not None: |
808 | 808 |
monkeypatch.setenv('VAULT_KEY', vault_key) |
809 |
- match vault_config: |
|
810 |
- case str(): |
|
809 |
+ # Use match/case here once Python 3.9 becomes unsupported. |
|
810 |
+ if isinstance(vault_config, str): |
|
811 | 811 |
with open('.vault', 'w', encoding='UTF-8') as outfile: |
812 | 812 |
print(vault_config, file=outfile) |
813 |
- case bytes(): |
|
813 |
+ elif isinstance(vault_config, bytes): |
|
814 | 814 |
os.makedirs('.vault', mode=0o700, exist_ok=True) |
815 | 815 |
with ( |
816 | 816 |
chdir('.vault'), |
... | ... |
@@ -822,9 +822,9 @@ def isolated_vault_exporter_config( |
822 | 822 |
tmpzipfile.seek(0, 0) |
823 | 823 |
with zipfile.ZipFile(tmpzipfile.file) as zipfileobj: |
824 | 824 |
zipfileobj.extractall() |
825 |
- case None: |
|
825 |
+ elif vault_config is None: |
|
826 | 826 |
pass |
827 |
- case _: # pragma: no cover |
|
827 |
+ else: # pragma: no cover |
|
828 | 828 |
assert_never(vault_config) |
829 | 829 |
yield |
830 | 830 |
|
... | ... |
@@ -935,14 +935,14 @@ class ReadableResult(NamedTuple): |
935 | 935 |
code, or an expected exception type. |
936 | 936 |
|
937 | 937 |
""" |
938 |
- match error: |
|
939 |
- case str(): |
|
938 |
+ # Use match/case here once Python 3.9 becomes unsupported. |
|
939 |
+ if isinstance(error, str): |
|
940 | 940 |
return ( |
941 | 941 |
isinstance(self.exception, SystemExit) |
942 | 942 |
and self.exit_code > 0 |
943 | 943 |
and (not error or error in self.stderr) |
944 | 944 |
) |
945 |
- case _: |
|
945 |
+ else: # noqa: RET505 |
|
946 | 946 |
return isinstance(self.exception, error) |
947 | 947 |
|
948 | 948 |
|
... | ... |
@@ -262,8 +262,8 @@ def running_ssh_agent() -> Iterator[str]: # pragma: no cover |
262 | 262 |
else: # pragma: no cover |
263 | 263 |
monkeypatch.delenv('SSH_AUTH_SOCK', raising=False) |
264 | 264 |
for exec_name, spawn_func, _ in _spawn_handlers: |
265 |
- match exec_name: |
|
266 |
- case '(system)': |
|
265 |
+ # Use match/case here once Python 3.9 becomes unsupported. |
|
266 |
+ if exec_name == '(system)': |
|
267 | 267 |
assert ( |
268 | 268 |
os.environ.get('SSH_AUTH_SOCK', None) |
269 | 269 |
== startup_ssh_auth_sock |
... | ... |
@@ -277,8 +277,8 @@ def running_ssh_agent() -> Iterator[str]: # pragma: no cover |
277 | 277 |
assert ( |
278 | 278 |
os.environ.get('SSH_AUTH_SOCK', None) |
279 | 279 |
== startup_ssh_auth_sock |
280 |
- ), 'SSH_AUTH_SOCK mismatch after returning from running agent' # noqa: E501 |
|
281 |
- case _: |
|
280 |
+ ), 'SSH_AUTH_SOCK mismatch after returning from running agent' |
|
281 |
+ else: |
|
282 | 282 |
assert ( |
283 | 283 |
os.environ.get('SSH_AUTH_SOCK', None) |
284 | 284 |
== startup_ssh_auth_sock |
... | ... |
@@ -310,9 +310,7 @@ def running_ssh_agent() -> Iterator[str]: # pragma: no cover |
310 | 310 |
'pid' not in pid_line.lower() |
311 | 311 |
and '_pid' not in pid_line.lower() |
312 | 312 |
): # pragma: no cover |
313 |
- pytest.skip( |
|
314 |
- f'Cannot parse agent output: {pid_line!r}' |
|
315 |
- ) |
|
313 |
+ pytest.skip(f'Cannot parse agent output: {pid_line!r}') |
|
316 | 314 |
proc2 = _spawn_data_sink( |
317 | 315 |
emits_debug_output=emits_debug_output, proc=proc |
318 | 316 |
) |
... | ... |
@@ -419,11 +417,10 @@ def spawn_ssh_agent( # noqa: C901 |
419 | 417 |
else: # pragma: no cover |
420 | 418 |
monkeypatch.delenv('SSH_AUTH_SOCK', raising=False) |
421 | 419 |
exec_name, spawn_func, agent_type = request.param |
422 |
- match exec_name: |
|
423 |
- case '(system)': |
|
420 |
+ # Use match/case here once Python 3.9 becomes unsupported. |
|
421 |
+ if exec_name == '(system)': |
|
424 | 422 |
assert ( |
425 |
- os.environ.get('SSH_AUTH_SOCK', None) |
|
426 |
- == startup_ssh_auth_sock |
|
423 |
+ os.environ.get('SSH_AUTH_SOCK', None) == startup_ssh_auth_sock |
|
427 | 424 |
), 'SSH_AUTH_SOCK mismatch when checking for running agent' |
428 | 425 |
try: |
429 | 426 |
client = ssh_agent.SSHAgentClient() |
... | ... |
@@ -439,25 +436,22 @@ def spawn_ssh_agent( # noqa: C901 |
439 | 436 |
assert ( |
440 | 437 |
os.environ.get('SSH_AUTH_SOCK', None) |
441 | 438 |
== startup_ssh_auth_sock |
442 |
- ), 'SSH_AUTH_SOCK mismatch before setting up for running agent' # noqa: E501 |
|
439 |
+ ), 'SSH_AUTH_SOCK mismatch before setting up for running agent' |
|
443 | 440 |
yield tests.SpawnedSSHAgentInfo(agent_type, client, False) |
444 | 441 |
assert ( |
445 |
- os.environ.get('SSH_AUTH_SOCK', None) |
|
446 |
- == startup_ssh_auth_sock |
|
442 |
+ os.environ.get('SSH_AUTH_SOCK', None) == startup_ssh_auth_sock |
|
447 | 443 |
), 'SSH_AUTH_SOCK mismatch after returning from running agent' |
448 | 444 |
return |
449 | 445 |
|
450 |
- case _: |
|
446 |
+ else: |
|
451 | 447 |
assert ( |
452 |
- os.environ.get('SSH_AUTH_SOCK', None) |
|
453 |
- == startup_ssh_auth_sock |
|
448 |
+ os.environ.get('SSH_AUTH_SOCK', None) == startup_ssh_auth_sock |
|
454 | 449 |
), f'SSH_AUTH_SOCK mismatch when checking for spawnable {exec_name}' # noqa: E501 |
455 | 450 |
spawn_data = spawn_func( |
456 | 451 |
executable=shutil.which(exec_name), env=agent_env |
457 | 452 |
) |
458 | 453 |
assert ( |
459 |
- os.environ.get('SSH_AUTH_SOCK', None) |
|
460 |
- == startup_ssh_auth_sock |
|
454 |
+ os.environ.get('SSH_AUTH_SOCK', None) == startup_ssh_auth_sock |
|
461 | 455 |
), f'SSH_AUTH_SOCK mismatch after spawning {exec_name}' |
462 | 456 |
if spawn_data is None: # pragma: no cover |
463 | 457 |
pytest.skip(f'Cannot spawn usable {exec_name}') |
... | ... |
@@ -483,7 +477,7 @@ def spawn_ssh_agent( # noqa: C901 |
483 | 477 |
assert ( |
484 | 478 |
os.environ.get('SSH_AUTH_SOCK', None) |
485 | 479 |
== startup_ssh_auth_sock |
486 |
- ), f'SSH_AUTH_SOCK mismatch before spawning {exec_name} helper' # noqa: E501 |
|
480 |
+ ), f'SSH_AUTH_SOCK mismatch before spawning {exec_name} helper' |
|
487 | 481 |
proc2 = _spawn_data_sink( |
488 | 482 |
emits_debug_output=emits_debug_output, proc=proc |
489 | 483 |
) |
... | ... |
@@ -492,7 +486,7 @@ def spawn_ssh_agent( # noqa: C901 |
492 | 486 |
assert ( |
493 | 487 |
os.environ.get('SSH_AUTH_SOCK', None) |
494 | 488 |
== startup_ssh_auth_sock |
495 |
- ), f'SSH_AUTH_SOCK mismatch after spawning {exec_name} helper' # noqa: E501 |
|
489 |
+ ), f'SSH_AUTH_SOCK mismatch after spawning {exec_name} helper' |
|
496 | 490 |
monkeypatch2 = exit_stack.enter_context( |
497 | 491 |
pytest.MonkeyPatch.context() |
498 | 492 |
) |
... | ... |
@@ -507,8 +501,7 @@ def spawn_ssh_agent( # noqa: C901 |
507 | 501 |
exit_stack.enter_context(client) |
508 | 502 |
yield tests.SpawnedSSHAgentInfo(agent_type, client, True) |
509 | 503 |
assert ( |
510 |
- os.environ.get('SSH_AUTH_SOCK', None) |
|
511 |
- == startup_ssh_auth_sock |
|
504 |
+ os.environ.get('SSH_AUTH_SOCK', None) == startup_ssh_auth_sock |
|
512 | 505 |
), f'SSH_AUTH_SOCK mismatch after tearing down {exec_name}' |
513 | 506 |
return |
514 | 507 |
|
... | ... |
@@ -1568,13 +1568,13 @@ Boo. |
1568 | 1568 |
ssh_agent.SSHAgentClient, 'list_keys', tests.list_keys |
1569 | 1569 |
) |
1570 | 1570 |
hint: ssh_agent.SSHAgentClient | socket.socket | None |
1571 |
- match conn_hint: |
|
1572 |
- case 'client': |
|
1571 |
+ # Use match/case here once Python 3.9 becomes unsupported. |
|
1572 |
+ if conn_hint == 'client': |
|
1573 | 1573 |
hint = ssh_agent.SSHAgentClient() |
1574 |
- case 'socket': |
|
1574 |
+ elif conn_hint == 'socket': |
|
1575 | 1575 |
hint = socket.socket(family=socket.AF_UNIX) |
1576 | 1576 |
hint.connect(running_ssh_agent) |
1577 |
- case _: |
|
1577 |
+ else: |
|
1578 | 1578 |
assert conn_hint == 'none' |
1579 | 1579 |
hint = None |
1580 | 1580 |
exception: Exception | None = None |
... | ... |
@@ -430,7 +430,7 @@ class TestAgentInteraction: |
430 | 430 |
del request_code |
431 | 431 |
del payload |
432 | 432 |
if isinstance( # pragma: no branch |
433 |
- response_code, int | _types.SSH_AGENT |
|
433 |
+ response_code, (int, _types.SSH_AGENT) |
|
434 | 434 |
): |
435 | 435 |
response_code = frozenset({response_code}) |
436 | 436 |
if response_code is not None: # pragma: no branch |
... | ... |
@@ -507,7 +507,7 @@ class TestAgentInteraction: |
507 | 507 |
del request_code |
508 | 508 |
del payload |
509 | 509 |
if isinstance( # pragma: no branch |
510 |
- response_code, int | _types.SSH_AGENT |
|
510 |
+ response_code, (int, _types.SSH_AGENT) |
|
511 | 511 |
): |
512 | 512 |
response_code = frozenset({response_code}) |
513 | 513 |
if response_code is not None: # pragma: no branch |
... | ... |
@@ -7,11 +7,12 @@ |
7 | 7 |
from __future__ import annotations |
8 | 8 |
|
9 | 9 |
import math |
10 |
-from typing import TYPE_CHECKING, TypeAlias, TypeVar |
|
10 |
+from typing import TYPE_CHECKING |
|
11 | 11 |
|
12 | 12 |
import hypothesis |
13 | 13 |
import pytest |
14 | 14 |
from hypothesis import strategies |
15 |
+from typing_extensions import TypeAlias, TypeVar |
|
15 | 16 |
|
16 | 17 |
import derivepassphrase |
17 | 18 |
|
18 | 19 |