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 |