Marco Ricci commited on 2025-02-14 15:45:36
Zeige 4 geänderte Dateien mit 311 Einfügungen und 87 Löschungen.
This allows the version output to only calculate the status information relevant to this subcommand. As a consequence, the tests now are subcommand-specific, and much less amenable to deduplication/parametrization.
| ... | ... |
@@ -952,7 +952,7 @@ def validate_length( |
| 952 | 952 |
return int_value |
| 953 | 953 |
|
| 954 | 954 |
|
| 955 |
-def version_option_callback( |
|
| 955 |
+def common_version_output( |
|
| 956 | 956 |
ctx: click.Context, |
| 957 | 957 |
param: click.Parameter, |
| 958 | 958 |
value: bool, # noqa: FBT001 |
| ... | ... |
@@ -960,32 +960,6 @@ def version_option_callback( |
| 960 | 960 |
del param |
| 961 | 961 |
if value and not ctx.resilient_parsing: |
| 962 | 962 |
major_dependencies: list[str] = [] |
| 963 |
- derivation_schemes = {'vault': True}
|
|
| 964 |
- foreign_configuration_formats = {
|
|
| 965 |
- 'vault storeroom': False, |
|
| 966 |
- 'vault v0.2': False, |
|
| 967 |
- 'vault v0.3': False, |
|
| 968 |
- } |
|
| 969 |
- known_extras = {
|
|
| 970 |
- 'export': False, |
|
| 971 |
- } |
|
| 972 |
- try: |
|
| 973 |
- from derivepassphrase.exporter import storeroom, vault_native # noqa: I001,PLC0415 |
|
| 974 |
- |
|
| 975 |
- foreign_configuration_formats[ |
|
| 976 |
- 'vault storeroom' |
|
| 977 |
- ] = not storeroom.STUBBED |
|
| 978 |
- foreign_configuration_formats[ |
|
| 979 |
- 'vault v0.2' |
|
| 980 |
- ] = not vault_native.STUBBED |
|
| 981 |
- foreign_configuration_formats[ |
|
| 982 |
- 'vault v0.3' |
|
| 983 |
- ] = not vault_native.STUBBED |
|
| 984 |
- known_extras['export'] = ( |
|
| 985 |
- not storeroom.STUBBED and not vault_native.STUBBED |
|
| 986 |
- ) |
|
| 987 |
- except ModuleNotFoundError: # pragma: no cover |
|
| 988 |
- pass |
|
| 989 | 963 |
try: |
| 990 | 964 |
cryptography_version = importlib.metadata.version('cryptography')
|
| 991 | 965 |
except ModuleNotFoundError: |
| ... | ... |
@@ -1011,24 +985,14 @@ def version_option_callback( |
| 1011 | 985 |
), |
| 1012 | 986 |
color=ctx.color, |
| 1013 | 987 |
) |
| 1014 |
- click.echo() |
|
| 1015 |
- version_info_types = {
|
|
| 1016 |
- _msg.Label.SUPPORTED_DERIVATION_SCHEMES: [ |
|
| 1017 |
- k for k, v in derivation_schemes.items() if v |
|
| 1018 |
- ], |
|
| 1019 |
- _msg.Label.KNOWN_DERIVATION_SCHEMES: [ |
|
| 1020 |
- k for k, v in derivation_schemes.items() if not v |
|
| 1021 |
- ], |
|
| 1022 |
- _msg.Label.SUPPORTED_FOREIGN_CONFIGURATION_FORMATS: [ |
|
| 1023 |
- k for k, v in foreign_configuration_formats.items() if v |
|
| 1024 |
- ], |
|
| 1025 |
- _msg.Label.KNOWN_FOREIGN_CONFIGURATION_FORMATS: [ |
|
| 1026 |
- k for k, v in foreign_configuration_formats.items() if not v |
|
| 1027 |
- ], |
|
| 1028 |
- _msg.Label.ENABLED_PEP508_EXTRAS: [ |
|
| 1029 |
- k for k, v in known_extras.items() if v |
|
| 1030 |
- ], |
|
| 1031 |
- } |
|
| 988 |
+ |
|
| 989 |
+ |
|
| 990 |
+def print_version_info_types( |
|
| 991 |
+ version_info_types: dict[_msg.Label, list[str]], |
|
| 992 |
+ /, |
|
| 993 |
+ *, |
|
| 994 |
+ ctx: click.Context, |
|
| 995 |
+) -> None: |
|
| 1032 | 996 |
for message_label, item_list in version_info_types.items(): |
| 1033 | 997 |
if item_list: |
| 1034 | 998 |
current_length = len(str(_msg.TranslatedString(message_label))) |
| ... | ... |
@@ -1038,21 +1002,14 @@ def version_option_callback( |
| 1038 | 1002 |
space = ' ' |
| 1039 | 1003 |
punctuation = '.' if i == n else ',' |
| 1040 | 1004 |
if ( |
| 1041 |
- current_length |
|
| 1042 |
- + len(space) |
|
| 1043 |
- + len(item) |
|
| 1044 |
- + len(punctuation) |
|
| 1005 |
+ current_length + len(space) + len(item) + len(punctuation) |
|
| 1045 | 1006 |
<= VERSION_OUTPUT_WRAPPING_WIDTH |
| 1046 | 1007 |
): |
| 1047 |
- current_length += ( |
|
| 1048 |
- len(space) + len(item) + len(punctuation) |
|
| 1049 |
- ) |
|
| 1008 |
+ current_length += len(space) + len(item) + len(punctuation) |
|
| 1050 | 1009 |
piece = f'{space}{item}{punctuation}'
|
| 1051 | 1010 |
else: |
| 1052 | 1011 |
space = ' ' |
| 1053 |
- current_length = ( |
|
| 1054 |
- len(space) + len(item) + len(punctuation) |
|
| 1055 |
- ) |
|
| 1012 |
+ current_length = len(space) + len(item) + len(punctuation) |
|
| 1056 | 1013 |
piece = f'\n{space}{item}{punctuation}'
|
| 1057 | 1014 |
formatted_item_list_pieces.append(piece) |
| 1058 | 1015 |
click.echo( |
| ... | ... |
@@ -1065,10 +1022,118 @@ def version_option_callback( |
| 1065 | 1022 |
]), |
| 1066 | 1023 |
color=ctx.color, |
| 1067 | 1024 |
) |
| 1025 |
+ |
|
| 1026 |
+ |
|
| 1027 |
+def derivepassphrase_version_option_callback( |
|
| 1028 |
+ ctx: click.Context, |
|
| 1029 |
+ param: click.Parameter, |
|
| 1030 |
+ value: bool, # noqa: FBT001 |
|
| 1031 |
+) -> None: |
|
| 1032 |
+ if value and not ctx.resilient_parsing: |
|
| 1033 |
+ common_version_output(ctx, param, value) |
|
| 1034 |
+ derivation_schemes = {'vault': True}
|
|
| 1035 |
+ supported_subcommands = {'export', 'vault'}
|
|
| 1036 |
+ click.echo() |
|
| 1037 |
+ version_info_types = {
|
|
| 1038 |
+ _msg.Label.SUPPORTED_DERIVATION_SCHEMES: [ |
|
| 1039 |
+ k for k, v in derivation_schemes.items() if v |
|
| 1040 |
+ ], |
|
| 1041 |
+ _msg.Label.KNOWN_DERIVATION_SCHEMES: [ |
|
| 1042 |
+ k for k, v in derivation_schemes.items() if not v |
|
| 1043 |
+ ], |
|
| 1044 |
+ _msg.Label.SUPPORTED_SUBCOMMANDS: sorted(supported_subcommands), |
|
| 1045 |
+ } |
|
| 1046 |
+ print_version_info_types(version_info_types, ctx=ctx) |
|
| 1068 | 1047 |
ctx.exit() |
| 1069 | 1048 |
|
| 1070 | 1049 |
|
| 1071 |
-def version_option(f: Callable[P, R]) -> Callable[P, R]: |
|
| 1050 |
+def export_version_option_callback( |
|
| 1051 |
+ ctx: click.Context, |
|
| 1052 |
+ param: click.Parameter, |
|
| 1053 |
+ value: bool, # noqa: FBT001 |
|
| 1054 |
+) -> None: |
|
| 1055 |
+ if value and not ctx.resilient_parsing: |
|
| 1056 |
+ common_version_output(ctx, param, value) |
|
| 1057 |
+ supported_subcommands = {'vault'}
|
|
| 1058 |
+ foreign_configuration_formats = {
|
|
| 1059 |
+ 'vault storeroom': False, |
|
| 1060 |
+ 'vault v0.2': False, |
|
| 1061 |
+ 'vault v0.3': False, |
|
| 1062 |
+ } |
|
| 1063 |
+ click.echo() |
|
| 1064 |
+ version_info_types = {
|
|
| 1065 |
+ _msg.Label.KNOWN_FOREIGN_CONFIGURATION_FORMATS: [ |
|
| 1066 |
+ k for k, v in foreign_configuration_formats.items() if not v |
|
| 1067 |
+ ], |
|
| 1068 |
+ _msg.Label.SUPPORTED_SUBCOMMANDS: sorted(supported_subcommands), |
|
| 1069 |
+ } |
|
| 1070 |
+ print_version_info_types(version_info_types, ctx=ctx) |
|
| 1071 |
+ ctx.exit() |
|
| 1072 |
+ |
|
| 1073 |
+ |
|
| 1074 |
+def export_vault_version_option_callback( |
|
| 1075 |
+ ctx: click.Context, |
|
| 1076 |
+ param: click.Parameter, |
|
| 1077 |
+ value: bool, # noqa: FBT001 |
|
| 1078 |
+) -> None: |
|
| 1079 |
+ if value and not ctx.resilient_parsing: |
|
| 1080 |
+ common_version_output(ctx, param, value) |
|
| 1081 |
+ foreign_configuration_formats = {
|
|
| 1082 |
+ 'vault storeroom': False, |
|
| 1083 |
+ 'vault v0.2': False, |
|
| 1084 |
+ 'vault v0.3': False, |
|
| 1085 |
+ } |
|
| 1086 |
+ known_extras = {
|
|
| 1087 |
+ 'export': False, |
|
| 1088 |
+ } |
|
| 1089 |
+ try: |
|
| 1090 |
+ from derivepassphrase.exporter import storeroom, vault_native # noqa: I001,PLC0415 |
|
| 1091 |
+ |
|
| 1092 |
+ foreign_configuration_formats[ |
|
| 1093 |
+ 'vault storeroom' |
|
| 1094 |
+ ] = not storeroom.STUBBED |
|
| 1095 |
+ foreign_configuration_formats[ |
|
| 1096 |
+ 'vault v0.2' |
|
| 1097 |
+ ] = not vault_native.STUBBED |
|
| 1098 |
+ foreign_configuration_formats[ |
|
| 1099 |
+ 'vault v0.3' |
|
| 1100 |
+ ] = not vault_native.STUBBED |
|
| 1101 |
+ known_extras['export'] = ( |
|
| 1102 |
+ not storeroom.STUBBED and not vault_native.STUBBED |
|
| 1103 |
+ ) |
|
| 1104 |
+ except ModuleNotFoundError: # pragma: no cover |
|
| 1105 |
+ pass |
|
| 1106 |
+ click.echo() |
|
| 1107 |
+ version_info_types = {
|
|
| 1108 |
+ _msg.Label.SUPPORTED_FOREIGN_CONFIGURATION_FORMATS: [ |
|
| 1109 |
+ k for k, v in foreign_configuration_formats.items() if v |
|
| 1110 |
+ ], |
|
| 1111 |
+ _msg.Label.KNOWN_FOREIGN_CONFIGURATION_FORMATS: [ |
|
| 1112 |
+ k for k, v in foreign_configuration_formats.items() if not v |
|
| 1113 |
+ ], |
|
| 1114 |
+ _msg.Label.ENABLED_PEP508_EXTRAS: [ |
|
| 1115 |
+ k for k, v in known_extras.items() if v |
|
| 1116 |
+ ], |
|
| 1117 |
+ } |
|
| 1118 |
+ print_version_info_types(version_info_types, ctx=ctx) |
|
| 1119 |
+ ctx.exit() |
|
| 1120 |
+ |
|
| 1121 |
+ |
|
| 1122 |
+def vault_version_option_callback( |
|
| 1123 |
+ ctx: click.Context, |
|
| 1124 |
+ param: click.Parameter, |
|
| 1125 |
+ value: bool, # noqa: FBT001 |
|
| 1126 |
+) -> None: |
|
| 1127 |
+ if value and not ctx.resilient_parsing: |
|
| 1128 |
+ common_version_output(ctx, param, value) |
|
| 1129 |
+ ctx.exit() |
|
| 1130 |
+ |
|
| 1131 |
+ |
|
| 1132 |
+def version_option( |
|
| 1133 |
+ version_option_callback: Callable[ |
|
| 1134 |
+ [click.Context, click.Parameter, Any], Any |
|
| 1135 |
+ ], |
|
| 1136 |
+) -> Callable[[Callable[P, R]], Callable[P, R]]: |
|
| 1072 | 1137 |
return click.option( |
| 1073 | 1138 |
'--version', |
| 1074 | 1139 |
is_flag=True, |
| ... | ... |
@@ -1077,7 +1142,7 @@ def version_option(f: Callable[P, R]) -> Callable[P, R]: |
| 1077 | 1142 |
callback=version_option_callback, |
| 1078 | 1143 |
cls=StandardOption, |
| 1079 | 1144 |
help=_msg.TranslatedString(_msg.Label.VERSION_OPTION_HELP_TEXT), |
| 1080 |
- )(f) |
|
| 1145 |
+ ) |
|
| 1081 | 1146 |
|
| 1082 | 1147 |
|
| 1083 | 1148 |
color_forcing_pseudo_option = click.option( |
| ... | ... |
@@ -1327,6 +1327,15 @@ class Label(enum.Enum): |
| 1327 | 1327 |
'Supported foreign configuration formats:', |
| 1328 | 1328 |
) |
| 1329 | 1329 |
"""""" |
| 1330 |
+ SUPPORTED_SUBCOMMANDS = commented( |
|
| 1331 |
+ 'This is part of the version output, emitting lists of supported ' |
|
| 1332 |
+ 'subcommands. A comma-separated English list ' |
|
| 1333 |
+ 'of items follows, with standard English punctuation.', |
|
| 1334 |
+ )( |
|
| 1335 |
+ 'Label :: Info Message:: Table row header', |
|
| 1336 |
+ 'Supported subcommands:', |
|
| 1337 |
+ ) |
|
| 1338 |
+ """""" |
|
| 1330 | 1339 |
CONFIRM_THIS_CHOICE_PROMPT_TEXT = commented( |
| 1331 | 1340 |
'There is no support for "yes" or "no" in other languages ' |
| 1332 | 1341 |
'than English, so it is advised that your translation makes it ' |
| ... | ... |
@@ -58,7 +58,9 @@ VERSION = _internals.VERSION |
| 58 | 58 |
_msg.TranslatedString(_msg.Label.DERIVEPASSPHRASE_03), |
| 59 | 59 |
), |
| 60 | 60 |
) |
| 61 |
-@cli_machinery.version_option |
|
| 61 |
+@cli_machinery.version_option( |
|
| 62 |
+ cli_machinery.derivepassphrase_version_option_callback |
|
| 63 |
+) |
|
| 62 | 64 |
@cli_machinery.color_forcing_pseudo_option |
| 63 | 65 |
@cli_machinery.standard_logging_options |
| 64 | 66 |
@click.pass_context |
| ... | ... |
@@ -113,7 +115,7 @@ def derivepassphrase(ctx: click.Context, /) -> None: |
| 113 | 115 |
_msg.TranslatedString(_msg.Label.DERIVEPASSPHRASE_EXPORT_03), |
| 114 | 116 |
), |
| 115 | 117 |
) |
| 116 |
-@cli_machinery.version_option |
|
| 118 |
+@cli_machinery.version_option(cli_machinery.export_version_option_callback) |
|
| 117 | 119 |
@cli_machinery.color_forcing_pseudo_option |
| 118 | 120 |
@cli_machinery.standard_logging_options |
| 119 | 121 |
@click.pass_context |
| ... | ... |
@@ -204,7 +206,9 @@ def derivepassphrase_export(ctx: click.Context, /) -> None: |
| 204 | 206 |
), |
| 205 | 207 |
cls=cli_machinery.StandardOption, |
| 206 | 208 |
) |
| 207 |
-@cli_machinery.version_option |
|
| 209 |
+@cli_machinery.version_option( |
|
| 210 |
+ cli_machinery.export_vault_version_option_callback |
|
| 211 |
+) |
|
| 208 | 212 |
@cli_machinery.color_forcing_pseudo_option |
| 209 | 213 |
@cli_machinery.standard_logging_options |
| 210 | 214 |
@click.argument( |
| ... | ... |
@@ -610,7 +614,7 @@ def derivepassphrase_export_vault( |
| 610 | 614 |
), |
| 611 | 615 |
cls=cli_machinery.CompatibilityOption, |
| 612 | 616 |
) |
| 613 |
-@cli_machinery.version_option |
|
| 617 |
+@cli_machinery.version_option(cli_machinery.vault_version_option_callback) |
|
| 614 | 618 |
@cli_machinery.color_forcing_pseudo_option |
| 615 | 619 |
@cli_machinery.standard_logging_options |
| 616 | 620 |
@click.argument( |
| ... | ... |
@@ -85,6 +85,7 @@ class VersionOutputData(NamedTuple): |
| 85 | 85 |
derivation_schemes: dict[str, bool] |
| 86 | 86 |
foreign_configuration_formats: dict[str, bool] |
| 87 | 87 |
extras: frozenset[str] |
| 88 |
+ subcommands: frozenset[str] |
|
| 88 | 89 |
|
| 89 | 90 |
|
| 90 | 91 |
PASSPHRASE_GENERATION_OPTIONS: list[tuple[str, ...]] = [ |
| ... | ... |
@@ -349,15 +350,15 @@ def parse_version_output( # noqa: C901 |
| 349 | 350 |
The version output contains two paragraphs. The first paragraph |
| 350 | 351 |
details the version number, and the version number of any major |
| 351 | 352 |
libraries in use. The second paragraph details known and supported |
| 352 |
- passphrase derivation schemes, foreign configuration formats, and |
|
| 353 |
- PEP 508 package extras. For the schemes and formats, there is |
|
| 354 |
- a "supported" line for supported items, and a "known" line for known |
|
| 355 |
- but currently unsupported items (usually because of missing |
|
| 356 |
- dependencies), either of which may be empty and thus omitted. For |
|
| 357 |
- extras, only active items are shown, and there is a separate message |
|
| 358 |
- for the "no extras active" case. Item lists may be spilled across |
|
| 359 |
- multiple lines, but only at item boundaries, and the continuation |
|
| 360 |
- lines are then indented. |
|
| 353 |
+ passphrase derivation schemes, foreign configuration formats, |
|
| 354 |
+ subcommands and PEP 508 package extras. For the schemes and |
|
| 355 |
+ formats, there is a "supported" line for supported items, and |
|
| 356 |
+ a "known" line for known but currently unsupported items (usually |
|
| 357 |
+ because of missing dependencies), either of which may be empty and |
|
| 358 |
+ thus omitted. For extras, only active items are shown, and there is |
|
| 359 |
+ a separate message for the "no extras active" case. Item lists may |
|
| 360 |
+ be spilled across multiple lines, but only at item boundaries, and |
|
| 361 |
+ the continuation lines are then indented. |
|
| 361 | 362 |
|
| 362 | 363 |
Args: |
| 363 | 364 |
text: |
| ... | ... |
@@ -389,8 +390,8 @@ def parse_version_output( # noqa: C901 |
| 389 | 390 |
if paragraph: # pragma: no branch |
| 390 | 391 |
paragraphs.append(paragraph.copy()) |
| 391 | 392 |
paragraph.clear() |
| 392 |
- assert len(paragraphs) == 2, ( |
|
| 393 |
- f'expected exactly two lines of version output: {paragraphs!r}'
|
|
| 393 |
+ assert paragraphs, ( |
|
| 394 |
+ f'expected at least one paragraph of version output: {paragraphs!r}'
|
|
| 394 | 395 |
) |
| 395 | 396 |
assert prog_name is None or prog_name in paragraphs[0][0], ( |
| 396 | 397 |
f'first version output line should mention ' |
| ... | ... |
@@ -400,9 +401,17 @@ def parse_version_output( # noqa: C901 |
| 400 | 401 |
f'first version output line should mention the version number ' |
| 401 | 402 |
f'{version}: {paragraphs[0][0]!r}'
|
| 402 | 403 |
) |
| 403 |
- schemas: dict[str, bool] = {}
|
|
| 404 |
+ schemes: dict[str, bool] = {}
|
|
| 404 | 405 |
formats: dict[str, bool] = {}
|
| 406 |
+ subcommands: set[str] = set() |
|
| 405 | 407 |
extras: set[str] = set() |
| 408 |
+ if len(paragraphs) < 2: |
|
| 409 |
+ return VersionOutputData( |
|
| 410 |
+ derivation_schemes=schemes, |
|
| 411 |
+ foreign_configuration_formats=formats, |
|
| 412 |
+ subcommands=frozenset(subcommands), |
|
| 413 |
+ extras=frozenset(extras), |
|
| 414 |
+ ) |
|
| 406 | 415 |
for line in paragraphs[1]: |
| 407 | 416 |
line_type, _, value = line.partition(':')
|
| 408 | 417 |
if line_type == line: |
| ... | ... |
@@ -416,16 +425,23 @@ def parse_version_output( # noqa: C901 |
| 416 | 425 |
elif line_type == 'Known foreign configuration formats': |
| 417 | 426 |
formats[item] = False |
| 418 | 427 |
elif line_type == 'Supported derivation schemes': |
| 419 |
- schemas[item] = True |
|
| 428 |
+ schemes[item] = True |
|
| 420 | 429 |
elif line_type == 'Known derivation schemes': |
| 421 |
- schemas[item] = False |
|
| 430 |
+ schemes[item] = False |
|
| 431 |
+ elif line_type == 'Supported subcommands': |
|
| 432 |
+ subcommands.add(item) |
|
| 422 | 433 |
elif line_type == 'PEP 508 extras': |
| 423 | 434 |
extras.add(item) |
| 424 | 435 |
else: |
| 425 | 436 |
raise AssertionError( # noqa: TRY003 |
| 426 | 437 |
f'Unknown version info line type: {line_type!r}' # noqa: EM102
|
| 427 | 438 |
) |
| 428 |
- return VersionOutputData(schemas, formats, frozenset(extras)) |
|
| 439 |
+ return VersionOutputData( |
|
| 440 |
+ derivation_schemes=schemes, |
|
| 441 |
+ foreign_configuration_formats=formats, |
|
| 442 |
+ subcommands=frozenset(subcommands), |
|
| 443 |
+ extras=frozenset(extras), |
|
| 444 |
+ ) |
|
| 429 | 445 |
|
| 430 | 446 |
|
| 431 | 447 |
def bash_format(item: click.shell_completion.CompletionItem) -> str: |
| ... | ... |
@@ -1451,7 +1467,6 @@ class Parametrize(types.SimpleNamespace): |
| 1451 | 1467 |
derivepassphrase 0.4.0 |
| 1452 | 1468 |
Using cryptography 44.0.0 |
| 1453 | 1469 |
|
| 1454 |
-Supported derivation schemes: vault. |
|
| 1455 | 1470 |
Supported foreign configuration formats: vault storeroom, vault v0.2, |
| 1456 | 1471 |
vault v0.3. |
| 1457 | 1472 |
PEP 508 extras: export. |
| ... | ... |
@@ -1459,12 +1474,13 @@ PEP 508 extras: export. |
| 1459 | 1474 |
'derivepassphrase', |
| 1460 | 1475 |
'0.4.0', |
| 1461 | 1476 |
VersionOutputData( |
| 1462 |
- derivation_schemes={'vault': True},
|
|
| 1477 |
+ derivation_schemes={},
|
|
| 1463 | 1478 |
foreign_configuration_formats={
|
| 1464 | 1479 |
'vault storeroom': True, |
| 1465 | 1480 |
'vault v0.2': True, |
| 1466 | 1481 |
'vault v0.3': True, |
| 1467 | 1482 |
}, |
| 1483 |
+ subcommands=frozenset(), |
|
| 1468 | 1484 |
extras=frozenset({'export'}),
|
| 1469 | 1485 |
), |
| 1470 | 1486 |
id='derivepassphrase-0.4.0-export', |
| ... | ... |
@@ -1475,6 +1491,7 @@ derivepassphrase 0.5 |
| 1475 | 1491 |
|
| 1476 | 1492 |
Supported derivation schemes: vault. |
| 1477 | 1493 |
Known foreign configuration formats: vault storeroom, vault v0.2, vault v0.3. |
| 1494 |
+Supported subcommands: export, vault. |
|
| 1478 | 1495 |
No PEP 508 extras are active. |
| 1479 | 1496 |
""", |
| 1480 | 1497 |
'derivepassphrase', |
| ... | ... |
@@ -1486,6 +1503,7 @@ No PEP 508 extras are active. |
| 1486 | 1503 |
'vault v0.2': False, |
| 1487 | 1504 |
'vault v0.3': False, |
| 1488 | 1505 |
}, |
| 1506 |
+ subcommands=frozenset({'export', 'vault'}),
|
|
| 1489 | 1507 |
extras=frozenset({}),
|
| 1490 | 1508 |
), |
| 1491 | 1509 |
id='derivepassphrase-0.5-plain', |
| ... | ... |
@@ -1506,6 +1524,7 @@ Known derivation schemes: divination, /dev/random, |
| 1506 | 1524 |
Supported foreign configuration formats: derivepassphrase, nonsense. |
| 1507 | 1525 |
Known foreign configuration formats: divination v3.141592, |
| 1508 | 1526 |
/dev/random. |
| 1527 |
+Supported subcommands: delete-all-files, dump-core. |
|
| 1509 | 1528 |
PEP 508 extras: annoying-popups, delete-all-files, |
| 1510 | 1529 |
dump-core-depending-on-the-phase-of-the-moon. |
| 1511 | 1530 |
|
| ... | ... |
@@ -1528,6 +1547,7 @@ PEP 508 extras: annoying-popups, delete-all-files, |
| 1528 | 1547 |
'divination v3.141592': False, |
| 1529 | 1548 |
'/dev/random': False, |
| 1530 | 1549 |
}, |
| 1550 |
+ subcommands=frozenset({'delete-all-files', 'dump-core'}),
|
|
| 1531 | 1551 |
extras=frozenset({
|
| 1532 | 1552 |
'annoying-popups', |
| 1533 | 1553 |
'delete-all-files', |
| ... | ... |
@@ -1778,20 +1798,19 @@ class TestAllCLI: |
| 1778 | 1798 |
'Expected no color, but found an ANSI control sequence' |
| 1779 | 1799 |
) |
| 1780 | 1800 |
|
| 1781 |
- @Parametrize.COMMAND_NON_EAGER_ARGUMENTS |
|
| 1782 |
- def test_202_version_option_output( |
|
| 1801 |
+ def test_202a_derivepassphrase_version_option_output( |
|
| 1783 | 1802 |
self, |
| 1784 |
- command: list[str], |
|
| 1785 |
- non_eager_arguments: list[str], |
|
| 1786 | 1803 |
) -> None: |
| 1787 | 1804 |
"""The version output states supported features. |
| 1788 | 1805 |
|
| 1789 | 1806 |
The version output is parsed using [`parse_version_output`][]. |
| 1790 | 1807 |
Format examples can be found in |
| 1791 |
- [`Parametrize.VERSION_OUTPUT_DATA`][]. |
|
| 1808 |
+ [`Parametrize.VERSION_OUTPUT_DATA`][]. Specifically, for the |
|
| 1809 |
+ top-level `derivepassphrase` command, the output should contain |
|
| 1810 |
+ the known and supported derivation schemes, and a list of |
|
| 1811 |
+ subcommands. |
|
| 1792 | 1812 |
|
| 1793 | 1813 |
""" |
| 1794 |
- del non_eager_arguments |
|
| 1795 | 1814 |
runner = click.testing.CliRunner(mix_stderr=False) |
| 1796 | 1815 |
# TODO(the-13th-letter): Rewrite using parenthesized |
| 1797 | 1816 |
# with-statements. |
| ... | ... |
@@ -1806,15 +1825,103 @@ class TestAllCLI: |
| 1806 | 1825 |
) |
| 1807 | 1826 |
result_ = runner.invoke( |
| 1808 | 1827 |
cli.derivepassphrase, |
| 1809 |
- [*command, '--version'], |
|
| 1828 |
+ ['--version'], |
|
| 1810 | 1829 |
catch_exceptions=False, |
| 1811 | 1830 |
) |
| 1812 | 1831 |
result = tests.ReadableResult.parse(result_) |
| 1813 | 1832 |
assert result.clean_exit(empty_stderr=True), 'expected clean exit' |
| 1814 | 1833 |
assert result.output.strip(), 'expected version output' |
| 1815 | 1834 |
version_data = parse_version_output(result.output) |
| 1816 |
- actually_known_formats: dict[str, bool] = {}
|
|
| 1817 | 1835 |
actually_known_schemes = {'vault': True}
|
| 1836 |
+ subcommands = {'export', 'vault'}
|
|
| 1837 |
+ assert version_data.derivation_schemes == actually_known_schemes |
|
| 1838 |
+ assert not version_data.foreign_configuration_formats |
|
| 1839 |
+ assert version_data.subcommands == subcommands |
|
| 1840 |
+ assert not version_data.extras |
|
| 1841 |
+ |
|
| 1842 |
+ def test_202b_export_version_option_output( |
|
| 1843 |
+ self, |
|
| 1844 |
+ ) -> None: |
|
| 1845 |
+ """The version output states supported features. |
|
| 1846 |
+ |
|
| 1847 |
+ The version output is parsed using [`parse_version_output`][]. |
|
| 1848 |
+ Format examples can be found in |
|
| 1849 |
+ [`Parametrize.VERSION_OUTPUT_DATA`][]. Specifically, for the |
|
| 1850 |
+ `export` command, the output should contain the known foreign |
|
| 1851 |
+ configuration formats (but not marked as supported), and a list |
|
| 1852 |
+ of subcommands. |
|
| 1853 |
+ |
|
| 1854 |
+ """ |
|
| 1855 |
+ runner = click.testing.CliRunner(mix_stderr=False) |
|
| 1856 |
+ # TODO(the-13th-letter): Rewrite using parenthesized |
|
| 1857 |
+ # with-statements. |
|
| 1858 |
+ # https://the13thletter.info/derivepassphrase/latest/pycompatibility/#after-eol-py3.9 |
|
| 1859 |
+ with contextlib.ExitStack() as stack: |
|
| 1860 |
+ monkeypatch = stack.enter_context(pytest.MonkeyPatch.context()) |
|
| 1861 |
+ stack.enter_context( |
|
| 1862 |
+ tests.isolated_config( |
|
| 1863 |
+ monkeypatch=monkeypatch, |
|
| 1864 |
+ runner=runner, |
|
| 1865 |
+ ) |
|
| 1866 |
+ ) |
|
| 1867 |
+ result_ = runner.invoke( |
|
| 1868 |
+ cli.derivepassphrase, |
|
| 1869 |
+ ['export', '--version'], |
|
| 1870 |
+ catch_exceptions=False, |
|
| 1871 |
+ ) |
|
| 1872 |
+ result = tests.ReadableResult.parse(result_) |
|
| 1873 |
+ assert result.clean_exit(empty_stderr=True), 'expected clean exit' |
|
| 1874 |
+ assert result.output.strip(), 'expected version output' |
|
| 1875 |
+ version_data = parse_version_output(result.output) |
|
| 1876 |
+ actually_known_formats: dict[str, bool] = {
|
|
| 1877 |
+ 'vault storeroom': False, |
|
| 1878 |
+ 'vault v0.2': False, |
|
| 1879 |
+ 'vault v0.3': False, |
|
| 1880 |
+ } |
|
| 1881 |
+ subcommands = {'vault'}
|
|
| 1882 |
+ assert not version_data.derivation_schemes |
|
| 1883 |
+ assert ( |
|
| 1884 |
+ version_data.foreign_configuration_formats |
|
| 1885 |
+ == actually_known_formats |
|
| 1886 |
+ ) |
|
| 1887 |
+ assert version_data.subcommands == subcommands |
|
| 1888 |
+ assert not version_data.extras |
|
| 1889 |
+ |
|
| 1890 |
+ def test_202c_export_vault_version_option_output( |
|
| 1891 |
+ self, |
|
| 1892 |
+ ) -> None: |
|
| 1893 |
+ """The version output states supported features. |
|
| 1894 |
+ |
|
| 1895 |
+ The version output is parsed using [`parse_version_output`][]. |
|
| 1896 |
+ Format examples can be found in |
|
| 1897 |
+ [`Parametrize.VERSION_OUTPUT_DATA`][]. Specifically, for the |
|
| 1898 |
+ `export vault` subcommand, the output should contain the |
|
| 1899 |
+ vault-specific subset of the known or supported foreign |
|
| 1900 |
+ configuration formats, and a list of available PEP 508 extras. |
|
| 1901 |
+ |
|
| 1902 |
+ """ |
|
| 1903 |
+ runner = click.testing.CliRunner(mix_stderr=False) |
|
| 1904 |
+ # TODO(the-13th-letter): Rewrite using parenthesized |
|
| 1905 |
+ # with-statements. |
|
| 1906 |
+ # https://the13thletter.info/derivepassphrase/latest/pycompatibility/#after-eol-py3.9 |
|
| 1907 |
+ with contextlib.ExitStack() as stack: |
|
| 1908 |
+ monkeypatch = stack.enter_context(pytest.MonkeyPatch.context()) |
|
| 1909 |
+ stack.enter_context( |
|
| 1910 |
+ tests.isolated_config( |
|
| 1911 |
+ monkeypatch=monkeypatch, |
|
| 1912 |
+ runner=runner, |
|
| 1913 |
+ ) |
|
| 1914 |
+ ) |
|
| 1915 |
+ result_ = runner.invoke( |
|
| 1916 |
+ cli.derivepassphrase, |
|
| 1917 |
+ ['export', 'vault', '--version'], |
|
| 1918 |
+ catch_exceptions=False, |
|
| 1919 |
+ ) |
|
| 1920 |
+ result = tests.ReadableResult.parse(result_) |
|
| 1921 |
+ assert result.clean_exit(empty_stderr=True), 'expected clean exit' |
|
| 1922 |
+ assert result.output.strip(), 'expected version output' |
|
| 1923 |
+ version_data = parse_version_output(result.output) |
|
| 1924 |
+ actually_known_formats: dict[str, bool] = {}
|
|
| 1818 | 1925 |
actually_enabled_extras: set[str] = set() |
| 1819 | 1926 |
with contextlib.suppress(ModuleNotFoundError): |
| 1820 | 1927 |
from derivepassphrase.exporter import storeroom, vault_native # noqa: I001,PLC0415 |
| ... | ... |
@@ -1826,13 +1933,52 @@ class TestAllCLI: |
| 1826 | 1933 |
}) |
| 1827 | 1934 |
if not storeroom.STUBBED and not vault_native.STUBBED: |
| 1828 | 1935 |
actually_enabled_extras.add('export')
|
| 1936 |
+ assert not version_data.derivation_schemes |
|
| 1829 | 1937 |
assert ( |
| 1830 | 1938 |
version_data.foreign_configuration_formats |
| 1831 | 1939 |
== actually_known_formats |
| 1832 | 1940 |
) |
| 1833 |
- assert version_data.derivation_schemes == actually_known_schemes |
|
| 1941 |
+ assert not version_data.subcommands |
|
| 1834 | 1942 |
assert version_data.extras == actually_enabled_extras |
| 1835 | 1943 |
|
| 1944 |
+ def test_202d_vault_version_option_output( |
|
| 1945 |
+ self, |
|
| 1946 |
+ ) -> None: |
|
| 1947 |
+ """The version output states supported features. |
|
| 1948 |
+ |
|
| 1949 |
+ The version output is parsed using [`parse_version_output`][]. |
|
| 1950 |
+ Format examples can be found in |
|
| 1951 |
+ [`Parametrize.VERSION_OUTPUT_DATA`][]. Specifically, for the |
|
| 1952 |
+ vault command, the output should not contain anything beyond the |
|
| 1953 |
+ first paragraph. |
|
| 1954 |
+ |
|
| 1955 |
+ """ |
|
| 1956 |
+ runner = click.testing.CliRunner(mix_stderr=False) |
|
| 1957 |
+ # TODO(the-13th-letter): Rewrite using parenthesized |
|
| 1958 |
+ # with-statements. |
|
| 1959 |
+ # https://the13thletter.info/derivepassphrase/latest/pycompatibility/#after-eol-py3.9 |
|
| 1960 |
+ with contextlib.ExitStack() as stack: |
|
| 1961 |
+ monkeypatch = stack.enter_context(pytest.MonkeyPatch.context()) |
|
| 1962 |
+ stack.enter_context( |
|
| 1963 |
+ tests.isolated_config( |
|
| 1964 |
+ monkeypatch=monkeypatch, |
|
| 1965 |
+ runner=runner, |
|
| 1966 |
+ ) |
|
| 1967 |
+ ) |
|
| 1968 |
+ result_ = runner.invoke( |
|
| 1969 |
+ cli.derivepassphrase, |
|
| 1970 |
+ ['vault', '--version'], |
|
| 1971 |
+ catch_exceptions=False, |
|
| 1972 |
+ ) |
|
| 1973 |
+ result = tests.ReadableResult.parse(result_) |
|
| 1974 |
+ assert result.clean_exit(empty_stderr=True), 'expected clean exit' |
|
| 1975 |
+ assert result.output.strip(), 'expected version output' |
|
| 1976 |
+ version_data = parse_version_output(result.output) |
|
| 1977 |
+ assert not version_data.derivation_schemes |
|
| 1978 |
+ assert not version_data.foreign_configuration_formats |
|
| 1979 |
+ assert not version_data.subcommands |
|
| 1980 |
+ assert not version_data.extras |
|
| 1981 |
+ |
|
| 1836 | 1982 |
|
| 1837 | 1983 |
class TestCLI: |
| 1838 | 1984 |
"""Tests for the `derivepassphrase vault` command-line interface.""" |
| 1839 | 1985 |