Convert Parametrizations enums to Parametrize namespace
Marco Ricci

Marco Ricci commited on 2025-02-01 23:25:35
Zeige 9 geänderte Dateien mit 137 Einfügungen und 161 Löschungen.


Switch out `enum.Enum` for `types.SimpleNamespace` to collect the
various `pytest.mark.parametrize` decorators: it works equally well in
practice when coupled with a type checker, and it avoids having to
unpack the enum value when actually calling the decorator.  Opt for the
shorter name `Parametrize` instead of `Parametrizations`.

Also consolidate the two versions of the `VAULT_CONFIG_FORMATS_DATA`
parametrization set by moving it into a dedicated namespace in the
top-level `tests` package.
... ...
@@ -17,6 +17,7 @@ import re
17 17
 import shlex
18 18
 import stat
19 19
 import tempfile
20
+import types
20 21
 import zipfile
21 22
 from typing import TYPE_CHECKING
22 23
 
... ...
@@ -2117,3 +2118,29 @@ deprecation_info_emitted = message_emitted_factory(
2117 2118
     logging.INFO, logger_name=f'{cli.PROG_NAME}.deprecation'
2118 2119
 )
2119 2120
 error_emitted = message_emitted_factory(logging.ERROR)
2121
+
2122
+
2123
+class Parametrize(types.SimpleNamespace):
2124
+    VAULT_CONFIG_FORMATS_DATA = pytest.mark.parametrize(
2125
+        ['config', 'format', 'config_data'],
2126
+        [
2127
+            pytest.param(
2128
+                VAULT_V02_CONFIG,
2129
+                'v0.2',
2130
+                VAULT_V02_CONFIG_DATA,
2131
+                id='0.2',
2132
+            ),
2133
+            pytest.param(
2134
+                VAULT_V03_CONFIG,
2135
+                'v0.3',
2136
+                VAULT_V03_CONFIG_DATA,
2137
+                id='0.3',
2138
+            ),
2139
+            pytest.param(
2140
+                VAULT_STOREROOM_CONFIG_ZIPPED,
2141
+                'storeroom',
2142
+                VAULT_STOREROOM_CONFIG_DATA,
2143
+                id='storeroom',
2144
+            ),
2145
+        ],
2146
+    )
... ...
@@ -7,7 +7,6 @@ from __future__ import annotations
7 7
 import base64
8 8
 import contextlib
9 9
 import copy
10
-import enum
11 10
 import errno
12 11
 import io
13 12
 import json
... ...
@@ -18,6 +17,7 @@ import shlex
18 17
 import shutil
19 18
 import socket
20 19
 import textwrap
20
+import types
21 21
 import warnings
22 22
 from typing import TYPE_CHECKING
23 23
 
... ...
@@ -346,7 +346,7 @@ def zsh_format(item: click.shell_completion.CompletionItem) -> str:
346 346
     return f'{item.type}\n{value}\n{help_}'
347 347
 
348 348
 
349
-class Parametrizations(enum.Enum):
349
+class Parametrize(types.SimpleNamespace):
350 350
     EAGER_ARGUMENTS = pytest.mark.parametrize(
351 351
         'arguments',
352 352
         [['--help'], ['--version']],
... ...
@@ -1438,8 +1438,8 @@ class TestAllCLI:
1438 1438
             empty_stderr=True, output='Use $VISUAL or $EDITOR to configure'
1439 1439
         ), 'expected clean exit, and option group epilog in help text'
1440 1440
 
1441
-    @Parametrizations.COMMAND_NON_EAGER_ARGUMENTS.value
1442
-    @Parametrizations.EAGER_ARGUMENTS.value
1441
+    @Parametrize.COMMAND_NON_EAGER_ARGUMENTS
1442
+    @Parametrize.EAGER_ARGUMENTS
1443 1443
     def test_200_eager_options(
1444 1444
         self,
1445 1445
         command: list[str],
... ...
@@ -1467,10 +1467,10 @@ class TestAllCLI:
1467 1467
             result = tests.ReadableResult.parse(result_)
1468 1468
         assert result.clean_exit(empty_stderr=True), 'expected clean exit'
1469 1469
 
1470
-    @Parametrizations.NO_COLOR.value
1471
-    @Parametrizations.FORCE_COLOR.value
1472
-    @Parametrizations.ISATTY.value
1473
-    @Parametrizations.COLORFUL_COMMAND_INPUT.value
1470
+    @Parametrize.NO_COLOR
1471
+    @Parametrize.FORCE_COLOR
1472
+    @Parametrize.ISATTY
1473
+    @Parametrize.COLORFUL_COMMAND_INPUT
1474 1474
     def test_201_no_color_force_color(
1475 1475
         self,
1476 1476
         no_color: bool,
... ...
@@ -1578,7 +1578,7 @@ class TestCLI:
1578 1578
             'expected clean exit, and version in help text'
1579 1579
         )
1580 1580
 
1581
-    @Parametrizations.CHARSET_NAME.value
1581
+    @Parametrize.CHARSET_NAME
1582 1582
     def test_201_disable_character_set(
1583 1583
         self,
1584 1584
         charset_name: str,
... ...
@@ -1650,7 +1650,7 @@ class TestCLI:
1650 1650
                 f'at position {i}: {result.output!r}'
1651 1651
             )
1652 1652
 
1653
-    @Parametrizations.CONFIG_WITH_KEY.value
1653
+    @Parametrize.CONFIG_WITH_KEY
1654 1654
     def test_204a_key_from_config(
1655 1655
         self,
1656 1656
         config: _types.VaultConfig,
... ...
@@ -1731,8 +1731,8 @@ class TestCLI:
1731 1731
             'expected known output'
1732 1732
         )
1733 1733
 
1734
-    @Parametrizations.BASE_CONFIG_WITH_KEY_VARIATIONS.value
1735
-    @Parametrizations.KEY_INDEX.value
1734
+    @Parametrize.BASE_CONFIG_WITH_KEY_VARIATIONS
1735
+    @Parametrize.KEY_INDEX
1736 1736
     def test_204c_key_override_on_command_line(
1737 1737
         self,
1738 1738
         running_ssh_agent: tests.RunningSSHAgentInfo,
... ...
@@ -1818,7 +1818,7 @@ class TestCLI:
1818 1818
             'expected known output'
1819 1819
         )
1820 1820
 
1821
-    @Parametrizations.KEY_OVERRIDING_IN_CONFIG.value
1821
+    @Parametrize.KEY_OVERRIDING_IN_CONFIG
1822 1822
     def test_206_setting_phrase_thus_overriding_key_in_config(
1823 1823
         self,
1824 1824
         running_ssh_agent: tests.RunningSSHAgentInfo,
... ...
@@ -1869,7 +1869,7 @@ class TestCLI:
1869 1869
             map(is_harmless_config_import_warning, caplog.record_tuples)
1870 1870
         ), 'unexpected error output'
1871 1871
 
1872
-    @Parametrizations.VAULT_CHARSET_OPTION.value
1872
+    @Parametrize.VAULT_CHARSET_OPTION
1873 1873
     def test_210_invalid_argument_range(
1874 1874
         self,
1875 1875
         option: str,
... ...
@@ -1899,7 +1899,7 @@ class TestCLI:
1899 1899
                     'expected error exit and known error message'
1900 1900
                 )
1901 1901
 
1902
-    @Parametrizations.OPTION_COMBINATIONS_SERVICE_NEEDED.value
1902
+    @Parametrize.OPTION_COMBINATIONS_SERVICE_NEEDED
1903 1903
     def test_211_service_needed(
1904 1904
         self,
1905 1905
         options: list[str],
... ...
@@ -2038,7 +2038,7 @@ class TestCLI:
2038 2038
                 'services': {'': {'length': 40}},
2039 2039
             }, 'requested configuration change was not applied'
2040 2040
 
2041
-    @Parametrizations.OPTION_COMBINATIONS_INCOMPATIBLE.value
2041
+    @Parametrize.OPTION_COMBINATIONS_INCOMPATIBLE
2042 2042
     def test_212_incompatible_options(
2043 2043
         self,
2044 2044
         options: list[str],
... ...
@@ -2068,7 +2068,7 @@ class TestCLI:
2068 2068
             'expected error exit and known error message'
2069 2069
         )
2070 2070
 
2071
-    @Parametrizations.VALID_TEST_CONFIGS.value
2071
+    @Parametrize.VALID_TEST_CONFIGS
2072 2072
     def test_213_import_config_success(
2073 2073
         self,
2074 2074
         caplog: pytest.LogCaptureFixture,
... ...
@@ -2253,7 +2253,7 @@ class TestCLI:
2253 2253
             'expected error exit and known error message'
2254 2254
         )
2255 2255
 
2256
-    @Parametrizations.EXPORT_FORMAT_OPTIONS.value
2256
+    @Parametrize.EXPORT_FORMAT_OPTIONS
2257 2257
     def test_214_export_settings_no_stored_settings(
2258 2258
         self,
2259 2259
         export_options: list[str],
... ...
@@ -2286,7 +2286,7 @@ class TestCLI:
2286 2286
         result = tests.ReadableResult.parse(result_)
2287 2287
         assert result.clean_exit(empty_stderr=True), 'expected clean exit'
2288 2288
 
2289
-    @Parametrizations.EXPORT_FORMAT_OPTIONS.value
2289
+    @Parametrize.EXPORT_FORMAT_OPTIONS
2290 2290
     def test_214a_export_settings_bad_stored_config(
2291 2291
         self,
2292 2292
         export_options: list[str],
... ...
@@ -2316,7 +2316,7 @@ class TestCLI:
2316 2316
             'expected error exit and known error message'
2317 2317
         )
2318 2318
 
2319
-    @Parametrizations.EXPORT_FORMAT_OPTIONS.value
2319
+    @Parametrize.EXPORT_FORMAT_OPTIONS
2320 2320
     def test_214b_export_settings_not_a_file(
2321 2321
         self,
2322 2322
         export_options: list[str],
... ...
@@ -2348,7 +2348,7 @@ class TestCLI:
2348 2348
             'expected error exit and known error message'
2349 2349
         )
2350 2350
 
2351
-    @Parametrizations.EXPORT_FORMAT_OPTIONS.value
2351
+    @Parametrize.EXPORT_FORMAT_OPTIONS
2352 2352
     def test_214c_export_settings_target_not_a_file(
2353 2353
         self,
2354 2354
         export_options: list[str],
... ...
@@ -2378,7 +2378,7 @@ class TestCLI:
2378 2378
             'expected error exit and known error message'
2379 2379
         )
2380 2380
 
2381
-    @Parametrizations.EXPORT_FORMAT_OPTIONS.value
2381
+    @Parametrize.EXPORT_FORMAT_OPTIONS
2382 2382
     def test_214d_export_settings_settings_directory_not_a_directory(
2383 2383
         self,
2384 2384
         export_options: list[str],
... ...
@@ -2556,7 +2556,7 @@ contents go here
2556 2556
                 config = json.load(infile)
2557 2557
             assert config == {'global': {'phrase': 'abc'}, 'services': {}}
2558 2558
 
2559
-    @Parametrizations.CONFIG_EDITING_VIA_CONFIG_FLAG.value
2559
+    @Parametrize.CONFIG_EDITING_VIA_CONFIG_FLAG
2560 2560
     def test_224_store_config_good(
2561 2561
         self,
2562 2562
         command_line: list[str],
... ...
@@ -2596,7 +2596,7 @@ contents go here
2596 2596
                 'stored config does not match expectation'
2597 2597
             )
2598 2598
 
2599
-    @Parametrizations.CONFIG_EDITING_VIA_CONFIG_FLAG_FAILURES.value
2599
+    @Parametrize.CONFIG_EDITING_VIA_CONFIG_FLAG_FAILURES
2600 2600
     def test_225_store_config_fail(
2601 2601
         self,
2602 2602
         command_line: list[str],
... ...
@@ -2723,7 +2723,7 @@ contents go here
2723 2723
             'expected error exit and known error message'
2724 2724
         )
2725 2725
 
2726
-    @Parametrizations.TRY_RACE_FREE_IMPLEMENTATION.value
2726
+    @Parametrize.TRY_RACE_FREE_IMPLEMENTATION
2727 2727
     def test_225d_store_config_fail_manual_read_only_file(
2728 2728
         self,
2729 2729
         try_race_free_implementation: bool,
... ...
@@ -3105,7 +3105,7 @@ contents go here
3105 3105
                 'expected error exit and known error message'
3106 3106
             )
3107 3107
 
3108
-    @Parametrizations.UNICODE_NORMALIZATION_WARNING_INPUTS.value
3108
+    @Parametrize.UNICODE_NORMALIZATION_WARNING_INPUTS
3109 3109
     def test_300_unicode_normalization_form_warning(
3110 3110
         self,
3111 3111
         caplog: pytest.LogCaptureFixture,
... ...
@@ -3145,7 +3145,7 @@ contents go here
3145 3145
             'expected known warning message in stderr'
3146 3146
         )
3147 3147
 
3148
-    @Parametrizations.UNICODE_NORMALIZATION_ERROR_INPUTS.value
3148
+    @Parametrize.UNICODE_NORMALIZATION_ERROR_INPUTS
3149 3149
     def test_301_unicode_normalization_form_error(
3150 3150
         self,
3151 3151
         main_config: str,
... ...
@@ -3186,7 +3186,7 @@ contents go here
3186 3186
             'expected error exit and known error message'
3187 3187
         )
3188 3188
 
3189
-    @Parametrizations.UNICODE_NORMALIZATION_COMMAND_LINES.value
3189
+    @Parametrize.UNICODE_NORMALIZATION_COMMAND_LINES
3190 3190
     def test_301a_unicode_normalization_form_error_from_stored_config(
3191 3191
         self,
3192 3192
         command_line: list[str],
... ...
@@ -3293,7 +3293,7 @@ contents go here
3293 3293
 class TestCLIUtils:
3294 3294
     """Tests for command-line utility functions."""
3295 3295
 
3296
-    @Parametrizations.BASE_CONFIG_VARIATIONS.value
3296
+    @Parametrize.BASE_CONFIG_VARIATIONS
3297 3297
     def test_100_load_config(
3298 3298
         self,
3299 3299
         config: Any,
... ...
@@ -3855,7 +3855,7 @@ Boo.
3855 3855
         assert _types.is_vault_config(config)
3856 3856
         return self.export_as_sh_helper(config)
3857 3857
 
3858
-    @Parametrizations.DELETE_CONFIG_INPUT.value
3858
+    @Parametrize.DELETE_CONFIG_INPUT
3859 3859
     def test_203_repeated_config_deletion(
3860 3860
         self,
3861 3861
         command_line: list[str],
... ...
@@ -3901,7 +3901,7 @@ Boo.
3901 3901
             == DUMMY_RESULT_KEY1
3902 3902
         )
3903 3903
 
3904
-    @Parametrizations.VALIDATION_FUNCTION_INPUT.value
3904
+    @Parametrize.VALIDATION_FUNCTION_INPUT
3905 3905
     def test_210a_validate_constraints_manually(
3906 3906
         self,
3907 3907
         vfunc: Callable[[click.Context, click.Parameter, Any], int | None],
... ...
@@ -3912,7 +3912,7 @@ Boo.
3912 3912
         param = cli.derivepassphrase_vault.params[0]
3913 3913
         assert vfunc(ctx, param, input) == input
3914 3914
 
3915
-    @Parametrizations.CONNECTION_HINTS.value
3915
+    @Parametrize.CONNECTION_HINTS
3916 3916
     def test_227_get_suitable_ssh_keys(
3917 3917
         self,
3918 3918
         running_ssh_agent: tests.RunningSSHAgentInfo,
... ...
@@ -4043,7 +4043,7 @@ Boo.
4043 4043
 class TestCLITransition:
4044 4044
     """Transition tests for the command-line interface up to v1.0."""
4045 4045
 
4046
-    @Parametrizations.BASE_CONFIG_VARIATIONS.value
4046
+    @Parametrize.BASE_CONFIG_VARIATIONS
4047 4047
     def test_110_load_config_backup(
4048 4048
         self,
4049 4049
         config: Any,
... ...
@@ -4066,7 +4066,7 @@ class TestCLITransition:
4066 4066
             ).write_text(json.dumps(config, indent=2) + '\n', encoding='UTF-8')
4067 4067
             assert cli_helpers.migrate_and_load_old_config()[0] == config
4068 4068
 
4069
-    @Parametrizations.BASE_CONFIG_VARIATIONS.value
4069
+    @Parametrize.BASE_CONFIG_VARIATIONS
4070 4070
     def test_111_migrate_config(
4071 4071
         self,
4072 4072
         config: Any,
... ...
@@ -4089,7 +4089,7 @@ class TestCLITransition:
4089 4089
             ).write_text(json.dumps(config, indent=2) + '\n', encoding='UTF-8')
4090 4090
             assert cli_helpers.migrate_and_load_old_config() == (config, None)
4091 4091
 
4092
-    @Parametrizations.BASE_CONFIG_VARIATIONS.value
4092
+    @Parametrize.BASE_CONFIG_VARIATIONS
4093 4093
     def test_112_migrate_config_error(
4094 4094
         self,
4095 4095
         config: Any,
... ...
@@ -4118,7 +4118,7 @@ class TestCLITransition:
4118 4118
             assert isinstance(err, OSError)
4119 4119
             assert err.errno == errno.EISDIR
4120 4120
 
4121
-    @Parametrizations.BAD_CONFIGS.value
4121
+    @Parametrize.BAD_CONFIGS
4122 4122
     def test_113_migrate_config_error_bad_config_value(
4123 4123
         self,
4124 4124
         config: Any,
... ...
@@ -4212,7 +4212,7 @@ class TestCLITransition:
4212 4212
             'expected error exit and known error type'
4213 4213
         )
4214 4214
 
4215
-    @Parametrizations.CHARSET_NAME.value
4215
+    @Parametrize.CHARSET_NAME
4216 4216
     def test_210_forward_vault_disable_character_set(
4217 4217
         self,
4218 4218
         caplog: pytest.LogCaptureFixture,
... ...
@@ -4977,7 +4977,7 @@ class TestShellCompletion:
4977 4977
             """Return the completion items' values, as a sequence."""
4978 4978
             return tuple(c.value for c in self())
4979 4979
 
4980
-    @Parametrizations.COMPLETABLE_ITEMS.value
4980
+    @Parametrize.COMPLETABLE_ITEMS
4981 4981
     def test_100_is_completable_item(
4982 4982
         self,
4983 4983
         partial: str,
... ...
@@ -4986,7 +4986,7 @@ class TestShellCompletion:
4986 4986
         """Our `_is_completable_item` predicate for service names works."""
4987 4987
         assert cli_helpers.is_completable_item(partial) == is_completable
4988 4988
 
4989
-    @Parametrizations.COMPLETABLE_OPTIONS.value
4989
+    @Parametrize.COMPLETABLE_OPTIONS
4990 4990
     def test_200_options(
4991 4991
         self,
4992 4992
         command_prefix: Sequence[str],
... ...
@@ -4997,7 +4997,7 @@ class TestShellCompletion:
4997 4997
         comp = self.Completions(command_prefix, incomplete)
4998 4998
         assert frozenset(comp.get_words()) == completions
4999 4999
 
5000
-    @Parametrizations.COMPLETABLE_SUBCOMMANDS.value
5000
+    @Parametrize.COMPLETABLE_SUBCOMMANDS
5001 5001
     def test_201_subcommands(
5002 5002
         self,
5003 5003
         command_prefix: Sequence[str],
... ...
@@ -5008,8 +5008,8 @@ class TestShellCompletion:
5008 5008
         comp = self.Completions(command_prefix, incomplete)
5009 5009
         assert frozenset(comp.get_words()) == completions
5010 5010
 
5011
-    @Parametrizations.COMPLETABLE_PATH_ARGUMENT.value
5012
-    @Parametrizations.INCOMPLETE.value
5011
+    @Parametrize.COMPLETABLE_PATH_ARGUMENT
5012
+    @Parametrize.INCOMPLETE
5013 5013
     def test_202_paths(
5014 5014
         self,
5015 5015
         command_prefix: Sequence[str],
... ...
@@ -5023,7 +5023,7 @@ class TestShellCompletion:
5023 5023
             frozenset((x.type, x.value, x.help) for x in comp()) == completions
5024 5024
         )
5025 5025
 
5026
-    @Parametrizations.COMPLETABLE_SERVICE_NAMES.value
5026
+    @Parametrize.COMPLETABLE_SERVICE_NAMES
5027 5027
     def test_203_service_names(
5028 5028
         self,
5029 5029
         config: _types.VaultConfig,
... ...
@@ -5047,8 +5047,8 @@ class TestShellCompletion:
5047 5047
             comp = self.Completions(['vault'], incomplete)
5048 5048
             assert frozenset(comp.get_words()) == completions
5049 5049
 
5050
-    @Parametrizations.SHELL_FORMATTER.value
5051
-    @Parametrizations.COMPLETION_FUNCTION_INPUTS.value
5050
+    @Parametrize.SHELL_FORMATTER
5051
+    @Parametrize.COMPLETION_FUNCTION_INPUTS
5052 5052
     def test_300_shell_completion_formatting(
5053 5053
         self,
5054 5054
         shell: str,
... ...
@@ -5112,8 +5112,8 @@ class TestShellCompletion:
5112 5112
             assert actual_items == expected_items
5113 5113
             assert actual_string == expected_string
5114 5114
 
5115
-    @Parametrizations.CONFIG_SETTING_MODE.value
5116
-    @Parametrizations.SERVICE_NAME_COMPLETION_INPUTS.value
5115
+    @Parametrize.CONFIG_SETTING_MODE
5116
+    @Parametrize.SERVICE_NAME_COMPLETION_INPUTS
5117 5117
     def test_400_incompletable_service_names(
5118 5118
         self,
5119 5119
         caplog: pytest.LogCaptureFixture,
... ...
@@ -5191,7 +5191,7 @@ class TestShellCompletion:
5191 5191
                 '',
5192 5192
             )
5193 5193
 
5194
-    @Parametrizations.SERVICE_NAME_EXCEPTIONS.value
5194
+    @Parametrize.SERVICE_NAME_EXCEPTIONS
5195 5195
     def test_410b_service_name_exceptions_custom_error(
5196 5196
         self,
5197 5197
         exc_type: type[Exception],
... ...
@@ -6,9 +6,9 @@ from __future__ import annotations
6 6
 
7 7
 import base64
8 8
 import contextlib
9
-import enum
10 9
 import json
11 10
 import pathlib
11
+import types
12 12
 from typing import TYPE_CHECKING
13 13
 
14 14
 import click.testing
... ...
@@ -40,7 +40,7 @@ if TYPE_CHECKING:
40 40
     from typing_extensions import Buffer, Literal
41 41
 
42 42
 
43
-class Parametrizations(enum.Enum):
43
+class Parametrize(types.SimpleNamespace):
44 44
     BAD_CONFIG = pytest.mark.parametrize(
45 45
         'config', ['xxx', 'null', '{"version": 255}']
46 46
     )
... ...
@@ -94,32 +94,6 @@ class Parametrizations(enum.Enum):
94 94
             ),
95 95
         ],
96 96
     )
97
-    # TODO(the-13th-letter): Consolidate with
98
-    # test_derivepassphrase_exporter.Parametrizations.VAULT_CONFIG_FORMATS_DATA.
99
-    # TODO(the-13th-letter): Reorder as "config", "format", "config_data".
100
-    VAULT_CONFIG_FORMATS_DATA = pytest.mark.parametrize(
101
-        ['format', 'config', 'config_data'],
102
-        [
103
-            pytest.param(
104
-                'v0.2',
105
-                tests.VAULT_V02_CONFIG,
106
-                tests.VAULT_V02_CONFIG_DATA,
107
-                id='0.2',
108
-            ),
109
-            pytest.param(
110
-                'v0.3',
111
-                tests.VAULT_V03_CONFIG,
112
-                tests.VAULT_V03_CONFIG_DATA,
113
-                id='0.3',
114
-            ),
115
-            pytest.param(
116
-                'storeroom',
117
-                tests.VAULT_STOREROOM_CONFIG_ZIPPED,
118
-                tests.VAULT_STOREROOM_CONFIG_DATA,
119
-                id='storeroom',
120
-            ),
121
-        ],
122
-    )
123 97
     STOREROOM_HANDLER = pytest.mark.parametrize(
124 98
         'handler',
125 99
         [
... ...
@@ -261,7 +235,7 @@ class TestCLI:
261 235
         assert result.clean_exit(empty_stderr=True), 'expected clean exit'
262 236
         assert json.loads(result.output) == tests.VAULT_V03_CONFIG_DATA
263 237
 
264
-    @Parametrizations.VAULT_CONFIG_FORMATS_DATA.value
238
+    @tests.Parametrize.VAULT_CONFIG_FORMATS_DATA
265 239
     def test_210_load_vault_v02_v03_storeroom(
266 240
         self,
267 241
         format: str,
... ...
@@ -467,9 +441,9 @@ class TestCLI:
467 441
 class TestStoreroom:
468 442
     """Test the "storeroom" handler and handler machinery."""
469 443
 
470
-    @Parametrizations.PATH.value
471
-    @Parametrizations.KEY_FORMATS.value
472
-    @Parametrizations.STOREROOM_HANDLER.value
444
+    @Parametrize.PATH
445
+    @Parametrize.KEY_FORMATS
446
+    @Parametrize.STOREROOM_HANDLER
473 447
     def test_200_export_data_path_and_keys_type(
474 448
         self,
475 449
         path: str | None,
... ...
@@ -514,7 +488,7 @@ class TestStoreroom:
514 488
         with pytest.raises(ValueError, match='Cannot handle version 255'):
515 489
             storeroom._decrypt_bucket_item(bucket_item, master_keys)
516 490
 
517
-    @Parametrizations.BAD_CONFIG.value
491
+    @Parametrize.BAD_CONFIG
518 492
     def test_401_decrypt_bucket_file_bad_json_or_version(
519 493
         self,
520 494
         config: str,
... ...
@@ -549,8 +523,8 @@ class TestStoreroom:
549 523
             with pytest.raises(ValueError, match='Invalid bucket file: '):
550 524
                 list(storeroom._decrypt_bucket_file(p, master_keys))
551 525
 
552
-    @Parametrizations.BAD_MASTER_KEYS_DATA.value
553
-    @Parametrizations.STOREROOM_HANDLER.value
526
+    @Parametrize.BAD_MASTER_KEYS_DATA
527
+    @Parametrize.STOREROOM_HANDLER
554 528
     def test_402_export_storeroom_data_bad_master_keys_file(
555 529
         self,
556 530
         data: str,
... ...
@@ -582,8 +556,8 @@ class TestStoreroom:
582 556
             with pytest.raises(RuntimeError, match=err_msg):
583 557
                 handler(format='storeroom')
584 558
 
585
-    @Parametrizations.BAD_STOREROOM_CONFIG_DATA.value
586
-    @Parametrizations.STOREROOM_HANDLER.value
559
+    @Parametrize.BAD_STOREROOM_CONFIG_DATA
560
+    @Parametrize.STOREROOM_HANDLER
587 561
     def test_403_export_storeroom_data_bad_directory_listing(
588 562
         self,
589 563
         zipped_config: bytes,
... ...
@@ -701,7 +675,7 @@ class TestStoreroom:
701 675
 class TestVaultNativeConfig:
702 676
     """Test the vault-native handler and handler machinery."""
703 677
 
704
-    @Parametrizations.VAULT_NATIVE_PBKDF2_RESULT.value
678
+    @Parametrize.VAULT_NATIVE_PBKDF2_RESULT
705 679
     def test_200_pbkdf2_manually(self, iterations: int, result: bytes) -> None:
706 680
         """The PBKDF2 helper function works."""
707 681
         assert (
... ...
@@ -711,8 +685,8 @@ class TestVaultNativeConfig:
711 685
             == result
712 686
         )
713 687
 
714
-    @Parametrizations.VAULT_NATIVE_CONFIG_DATA.value
715
-    @Parametrizations.VAULT_NATIVE_HANDLER.value
688
+    @Parametrize.VAULT_NATIVE_CONFIG_DATA
689
+    @Parametrize.VAULT_NATIVE_HANDLER
716 690
     def test_201_export_vault_native_data_explicit_version(
717 691
         self,
718 692
         config: str,
... ...
@@ -751,9 +725,9 @@ class TestVaultNativeConfig:
751 725
                 parsed_config = handler(None, format=format)
752 726
                 assert parsed_config == result
753 727
 
754
-    @Parametrizations.PATH.value
755
-    @Parametrizations.KEY_FORMATS.value
756
-    @Parametrizations.VAULT_NATIVE_HANDLER.value
728
+    @Parametrize.PATH
729
+    @Parametrize.KEY_FORMATS
730
+    @Parametrize.VAULT_NATIVE_HANDLER
757 731
     def test_202_export_data_path_and_keys_type(
758 732
         self,
759 733
         path: str | None,
... ...
@@ -785,7 +759,7 @@ class TestVaultNativeConfig:
785 759
                 == tests.VAULT_V03_CONFIG_DATA
786 760
             )
787 761
 
788
-    @Parametrizations.VAULT_NATIVE_PARSER_CLASS_DATA.value
762
+    @Parametrize.VAULT_NATIVE_PARSER_CLASS_DATA
789 763
     def test_300_result_caching(
790 764
         self,
791 765
         parser_class: type[vault_native.VaultNativeConfigParser],
... ...
@@ -5,11 +5,11 @@
5 5
 from __future__ import annotations
6 6
 
7 7
 import contextlib
8
-import enum
9 8
 import operator
10 9
 import os
11 10
 import pathlib
12 11
 import string
12
+import types
13 13
 from typing import TYPE_CHECKING, Any, NamedTuple
14 14
 
15 15
 import click.testing
... ...
@@ -24,7 +24,7 @@ if TYPE_CHECKING:
24 24
     from typing_extensions import Buffer
25 25
 
26 26
 
27
-class Parametrizations(enum.Enum):
27
+class Parametrize(types.SimpleNamespace):
28 28
     EXPECTED_VAULT_PATH = pytest.mark.parametrize(
29 29
         ['expected', 'path'],
30 30
         [
... ...
@@ -33,32 +33,6 @@ class Parametrizations(enum.Enum):
33 33
             (pathlib.Path('~/.vault'), None),
34 34
         ],
35 35
     )
36
-    # TODO(the-13th-letter): Consolidate with
37
-    # test_derivepassphrase_cli_export_vault.Parametrizations.VAULT_CONFIG_FORMATS_DATA.
38
-    # TODO(the-13th-letter): Reorder as "config", "format", "config_data".
39
-    VAULT_CONFIG_FORMATS_DATA = pytest.mark.parametrize(
40
-        ['format', 'config', 'config_data'],
41
-        [
42
-            pytest.param(
43
-                'v0.2',
44
-                tests.VAULT_V02_CONFIG,
45
-                tests.VAULT_V02_CONFIG_DATA,
46
-                id='0.2',
47
-            ),
48
-            pytest.param(
49
-                'v0.3',
50
-                tests.VAULT_V03_CONFIG,
51
-                tests.VAULT_V03_CONFIG_DATA,
52
-                id='0.3',
53
-            ),
54
-            pytest.param(
55
-                'storeroom',
56
-                tests.VAULT_STOREROOM_CONFIG_ZIPPED,
57
-                tests.VAULT_STOREROOM_CONFIG_DATA,
58
-                id='storeroom',
59
-            ),
60
-        ],
61
-    )
62 36
     EXPORT_VAULT_CONFIG_DATA_HANDLER_NAMELISTS = pytest.mark.parametrize(
63 37
         ['namelist', 'err_pat'],
64 38
         [
... ...
@@ -248,7 +222,7 @@ class Test001ExporterUtils:
248 222
                     monkeypatch.setenv(key, value)
249 223
             assert os.fsdecode(exporter.get_vault_key()) == expected
250 224
 
251
-    @Parametrizations.EXPECTED_VAULT_PATH.value
225
+    @Parametrize.EXPECTED_VAULT_PATH
252 226
     def test_210_get_vault_path(
253 227
         self,
254 228
         expected: pathlib.Path,
... ...
@@ -351,7 +325,7 @@ class Test001ExporterUtils:
351 325
             ):
352 326
                 exporter.get_vault_path()
353 327
 
354
-    @Parametrizations.EXPORT_VAULT_CONFIG_DATA_HANDLER_NAMELISTS.value
328
+    @Parametrize.EXPORT_VAULT_CONFIG_DATA_HANDLER_NAMELISTS
355 329
     def test_320_register_export_vault_config_data_handler_errors(
356 330
         self,
357 331
         namelist: tuple[str, ...],
... ...
@@ -430,15 +404,16 @@ class Test002CLI:
430 404
             )
431 405
 
432 406
     @tests.skip_if_cryptography_support
433
-    @Parametrizations.VAULT_CONFIG_FORMATS_DATA.value
407
+    @tests.Parametrize.VAULT_CONFIG_FORMATS_DATA
434 408
     def test_999_no_cryptography_error_message(
435 409
         self,
436 410
         caplog: pytest.LogCaptureFixture,
437
-        format: str,
438 411
         config: str | bytes,
439
-        key: str,
412
+        format: str,
413
+        config_data: str,
440 414
     ) -> None:
441 415
         """Abort export call if no cryptography is available."""
416
+        del config_data
442 417
         runner = click.testing.CliRunner(mix_stderr=False)
443 418
         # TODO(the-13th-letter): Rewrite using parenthesized
444 419
         # with-statements.
... ...
@@ -450,7 +425,7 @@ class Test002CLI:
450 425
                     monkeypatch=monkeypatch,
451 426
                     runner=runner,
452 427
                     vault_config=config,
453
-                    vault_key=key,
428
+                    vault_key=tests.VAULT_MASTER_KEY,
454 429
                 )
455 430
             )
456 431
             result_ = runner.invoke(
... ...
@@ -8,10 +8,10 @@ from __future__ import annotations
8 8
 
9 9
 import collections
10 10
 import contextlib
11
-import enum
12 11
 import functools
13 12
 import math
14 13
 import operator
14
+import types
15 15
 from typing import TYPE_CHECKING, NamedTuple
16 16
 
17 17
 import hypothesis
... ...
@@ -55,7 +55,7 @@ def bitseq(string: str) -> list[int]:
55 55
     return [int(char, 2) for char in string]
56 56
 
57 57
 
58
-class Parametrizations(enum.Enum):
58
+class Parametrize(types.SimpleNamespace):
59 59
     BIG_ENDIAN_NUMBER_EXCEPTIONS = pytest.mark.parametrize(
60 60
         ['exc_type', 'exc_pattern', 'sequence', 'base'],
61 61
         [
... ...
@@ -199,7 +199,7 @@ class TestStaticFunctionality:
199 199
             sequin.Sequin._big_endian_number(sequence, base=base)
200 200
         ) == expected
201 201
 
202
-    @Parametrizations.BIG_ENDIAN_NUMBER_EXCEPTIONS.value
202
+    @Parametrize.BIG_ENDIAN_NUMBER_EXCEPTIONS
203 203
     def test_300_big_endian_number_exceptions(
204 204
         self,
205 205
         exc_type: type[Exception],
... ...
@@ -541,7 +541,7 @@ class TestSequin:
541 541
                     f'After step {i}, the bit sequence is not exhausted yet'
542 542
                 )
543 543
 
544
-    @Parametrizations.INVALID_SEQUIN_INPUTS.value
544
+    @Parametrize.INVALID_SEQUIN_INPUTS
545 545
     def test_300_constructor_exceptions(
546 546
         self,
547 547
         sequence: list[int] | str,
... ...
@@ -8,10 +8,10 @@ from __future__ import annotations
8 8
 
9 9
 import base64
10 10
 import contextlib
11
-import enum
12 11
 import io
13 12
 import re
14 13
 import socket
14
+import types
15 15
 from typing import TYPE_CHECKING
16 16
 
17 17
 import click
... ...
@@ -30,7 +30,7 @@ if TYPE_CHECKING:
30 30
     from typing_extensions import Any, Buffer
31 31
 
32 32
 
33
-class Parametrizations(enum.Enum):
33
+class Parametrize(types.SimpleNamespace):
34 34
     SSH_STRING_EXCEPTIONS = pytest.mark.parametrize(
35 35
         ['input', 'exc_type', 'exc_pattern'],
36 36
         [
... ...
@@ -377,7 +377,7 @@ class TestStaticFunctionality:
377 377
     # It cannot provide true tamper-resistence, but probably appears to.
378 378
     # TODO(the-13th-letter): Modify parametrization to work directly on the
379 379
     # struct.
380
-    @Parametrizations.PUBLIC_KEY_DATA.value
380
+    @Parametrize.PUBLIC_KEY_DATA
381 381
     def test_100_key_decoding(
382 382
         self, public_key: bytes, public_key_data: bytes
383 383
     ) -> None:
... ...
@@ -387,7 +387,7 @@ class TestStaticFunctionality:
387 387
             "recorded public key data doesn't match"
388 388
         )
389 389
 
390
-    @Parametrizations.SH_EXPORT_LINES.value
390
+    @Parametrize.SH_EXPORT_LINES
391 391
     def test_190_sh_export_line_parsing(
392 392
         self, line: str, env_name: str, value: str | None
393 393
     ) -> None:
... ...
@@ -411,7 +411,7 @@ class TestStaticFunctionality:
411 411
             ):
412 412
                 ssh_agent.SSHAgentClient()
413 413
 
414
-    @Parametrizations.UINT32_INPUT.value
414
+    @Parametrize.UINT32_INPUT
415 415
     def test_210_uint32(self, input: int, expected: bytes | bytearray) -> None:
416 416
         """`uint32` encoding works."""
417 417
         uint32 = ssh_agent.SSHAgentClient.uint32
... ...
@@ -436,7 +436,7 @@ class TestStaticFunctionality:
436 436
             == bytestring
437 437
         )
438 438
 
439
-    @Parametrizations.SSH_STRING_INPUT.value
439
+    @Parametrize.SSH_STRING_INPUT
440 440
     def test_211_string(
441 441
         self, input: bytes | bytearray, expected: bytes | bytearray
442 442
     ) -> None:
... ...
@@ -455,7 +455,7 @@ class TestStaticFunctionality:
455 455
         assert int.from_bytes(res[:4], 'big', signed=False) == len(bytestring)
456 456
         assert res[4:] == bytestring
457 457
 
458
-    @Parametrizations.SSH_UNSTRING_INPUT.value
458
+    @Parametrize.SSH_UNSTRING_INPUT
459 459
     def test_212_unstring(
460 460
         self, input: bytes | bytearray, expected: bytes | bytearray
461 461
     ) -> None:
... ...
@@ -521,7 +521,7 @@ class TestStaticFunctionality:
521 521
                 assert canon1(canon2(encoded)) == canon1(encoded)
522 522
 
523 523
     # TODO(the-13th-letter): Rename "value" to "input".
524
-    @Parametrizations.UINT32_EXCEPTIONS.value
524
+    @Parametrize.UINT32_EXCEPTIONS
525 525
     def test_310_uint32_exceptions(
526 526
         self, value: int, exc_type: type[Exception], exc_pattern: str
527 527
     ) -> None:
... ...
@@ -530,7 +530,7 @@ class TestStaticFunctionality:
530 530
         with pytest.raises(exc_type, match=exc_pattern):
531 531
             uint32(value)
532 532
 
533
-    @Parametrizations.SSH_STRING_EXCEPTIONS.value
533
+    @Parametrize.SSH_STRING_EXCEPTIONS
534 534
     def test_311_string_exceptions(
535 535
         self, input: Any, exc_type: type[Exception], exc_pattern: str
536 536
     ) -> None:
... ...
@@ -539,7 +539,7 @@ class TestStaticFunctionality:
539 539
         with pytest.raises(exc_type, match=exc_pattern):
540 540
             string(input)
541 541
 
542
-    @Parametrizations.SSH_UNSTRING_EXCEPTIONS.value
542
+    @Parametrize.SSH_UNSTRING_EXCEPTIONS
543 543
     def test_312_unstring_exceptions(
544 544
         self,
545 545
         input: bytes | bytearray,
... ...
@@ -566,7 +566,7 @@ class TestAgentInteraction:
566 566
     # TODO(the-13th-letter): Convert skip into xfail, and include the
567 567
     # key type in the skip/xfail message.  This means the key type needs
568 568
     # to be passed to the test function as well.
569
-    @Parametrizations.SUPPORTED_SSH_TEST_KEYS.value
569
+    @Parametrize.SUPPORTED_SSH_TEST_KEYS
570 570
     def test_200_sign_data_via_agent(
571 571
         self,
572 572
         ssh_agent_client_with_test_keys_loaded: ssh_agent.SSHAgentClient,
... ...
@@ -604,7 +604,7 @@ class TestAgentInteraction:
604 604
     # TODO(the-13th-letter): Include the key type in the skip message.
605 605
     # This means the key type needs to be passed to the test function as
606 606
     # well.
607
-    @Parametrizations.UNSUITABLE_SSH_TEST_KEYS.value
607
+    @Parametrize.UNSUITABLE_SSH_TEST_KEYS
608 608
     def test_201_sign_data_via_agent_unsupported(
609 609
         self,
610 610
         ssh_agent_client_with_test_keys_loaded: ssh_agent.SSHAgentClient,
... ...
@@ -632,7 +632,7 @@ class TestAgentInteraction:
632 632
         with pytest.raises(ValueError, match='unsuitable SSH key'):
633 633
             vault.Vault.phrase_from_key(public_key_data, conn=client)
634 634
 
635
-    @Parametrizations.SSH_KEY_SELECTION.value
635
+    @Parametrize.SSH_KEY_SELECTION
636 636
     def test_210_ssh_key_selector(
637 637
         self,
638 638
         monkeypatch: pytest.MonkeyPatch,
... ...
@@ -733,7 +733,7 @@ class TestAgentInteraction:
733 733
             ):
734 734
                 ssh_agent.SSHAgentClient()
735 735
 
736
-    @Parametrizations.TRUNCATED_AGENT_RESPONSES.value
736
+    @Parametrize.TRUNCATED_AGENT_RESPONSES
737 737
     def test_310_truncated_server_response(
738 738
         self,
739 739
         running_ssh_agent: tests.RunningSSHAgentInfo,
... ...
@@ -757,7 +757,7 @@ class TestAgentInteraction:
757 757
             with pytest.raises(EOFError):
758 758
                 client.request(255, b'')
759 759
 
760
-    @Parametrizations.LIST_KEYS_ERROR_RESPONSES.value
760
+    @Parametrize.LIST_KEYS_ERROR_RESPONSES
761 761
     def test_320_list_keys_error_responses(
762 762
         self,
763 763
         running_ssh_agent: tests.RunningSSHAgentInfo,
... ...
@@ -817,7 +817,7 @@ class TestAgentInteraction:
817 817
             with pytest.raises(exc_type, match=exc_pattern):
818 818
                 client.list_keys()
819 819
 
820
-    @Parametrizations.SIGN_ERROR_RESPONSES.value
820
+    @Parametrize.SIGN_ERROR_RESPONSES
821 821
     def test_330_sign_error_responses(
822 822
         self,
823 823
         running_ssh_agent: tests.RunningSSHAgentInfo,
... ...
@@ -886,7 +886,7 @@ class TestAgentInteraction:
886 886
             with pytest.raises(exc_type, match=exc_pattern):
887 887
                 client.sign(key, b'abc', check_if_key_loaded=check)
888 888
 
889
-    @Parametrizations.REQUEST_ERROR_RESPONSES.value
889
+    @Parametrize.REQUEST_ERROR_RESPONSES
890 890
     def test_340_request_error_responses(
891 891
         self,
892 892
         running_ssh_agent: tests.RunningSSHAgentInfo,
... ...
@@ -914,7 +914,7 @@ class TestAgentInteraction:
914 914
             client = stack.enter_context(ssh_agent.SSHAgentClient())
915 915
             client.request(request_code, b'', response_code=response_code)
916 916
 
917
-    @Parametrizations.QUERY_EXTENSIONS_MALFORMED_RESPONSES.value
917
+    @Parametrize.QUERY_EXTENSIONS_MALFORMED_RESPONSES
918 918
     def test_350_query_extensions_malformed_responses(
919 919
         self,
920 920
         monkeypatch: pytest.MonkeyPatch,
... ...
@@ -5,8 +5,8 @@
5 5
 from __future__ import annotations
6 6
 
7 7
 import copy
8
-import enum
9 8
 import math
9
+import types
10 10
 
11 11
 import hypothesis
12 12
 import pytest
... ...
@@ -67,7 +67,7 @@ def js_nested_strategy(draw: strategies.DrawFn) -> Any:
67 67
     )
68 68
 
69 69
 
70
-class Parametrizations(enum.Enum):
70
+class Parametrize(types.SimpleNamespace):
71 71
     VALID_VAULT_TEST_CONFIGS = pytest.mark.parametrize(
72 72
         'test_config',
73 73
         [
... ...
@@ -101,7 +101,7 @@ def test_100_js_truthiness(value: Any) -> None:
101 101
     assert _types.js_truthiness(value) == expected
102 102
 
103 103
 
104
-@Parametrizations.VALID_VAULT_TEST_CONFIGS.value
104
+@Parametrize.VALID_VAULT_TEST_CONFIGS
105 105
 def test_200_is_vault_config(test_config: tests.VaultTestConfig) -> None:
106 106
     """Is this vault configuration recognized as valid/invalid?
107 107
 
... ...
@@ -156,7 +156,7 @@ def test_200a_is_vault_config_smudged(
156 156
     )
157 157
 
158 158
 
159
-@Parametrizations.VAULT_TEST_CONFIGS.value
159
+@Parametrize.VAULT_TEST_CONFIGS
160 160
 def test_400_validate_vault_config(test_config: tests.VaultTestConfig) -> None:
161 161
     """Validate this vault configuration.
162 162
 
... ...
@@ -7,9 +7,9 @@
7 7
 from __future__ import annotations
8 8
 
9 9
 import array
10
-import enum
11 10
 import hashlib
12 11
 import math
12
+import types
13 13
 from typing import TYPE_CHECKING
14 14
 
15 15
 import hypothesis
... ...
@@ -42,7 +42,7 @@ The standard derived passphrase for the "twitter" service, from
42 42
 """
43 43
 
44 44
 
45
-class Parametrizations(enum.Enum):
45
+class Parametrize(types.SimpleNamespace):
46 46
     ENTROPY_RESULTS = pytest.mark.parametrize(
47 47
         ['length', 'settings', 'entropy'],
48 48
         [
... ...
@@ -318,7 +318,7 @@ class TestVault:
318 318
             phrase=phrases[0], service=service
319 319
         ) == vault.Vault.create_hash(phrase=phrases[1], service=service)
320 320
 
321
-    @Parametrizations.SAMPLE_SERVICES_AND_PHRASES.value
321
+    @Parametrize.SAMPLE_SERVICES_AND_PHRASES
322 322
     def test_200_basic_configuration(
323 323
         self, service: bytes | str, expected: bytes
324 324
     ) -> None:
... ...
@@ -798,7 +798,7 @@ class TestVault:
798 798
         """Removing allowed characters internally works."""
799 799
         assert vault.Vault._subtract(b'be', b'abcdef') == bytearray(b'acdf')
800 800
 
801
-    @Parametrizations.ENTROPY_RESULTS.value
801
+    @Parametrize.ENTROPY_RESULTS
802 802
     def test_221_entropy(
803 803
         self, length: int, settings: dict[str, int], entropy: int
804 804
     ) -> None:
... ...
@@ -828,7 +828,7 @@ class TestVault:
828 828
         assert v._entropy() == 0.0
829 829
         assert v._estimate_sufficient_hash_length() > 0
830 830
 
831
-    @Parametrizations.SAMPLE_SERVICES_AND_PHRASES.value
831
+    @Parametrize.SAMPLE_SERVICES_AND_PHRASES
832 832
     def test_223_hash_length_expansion(
833 833
         self,
834 834
         monkeypatch: pytest.MonkeyPatch,
... ...
@@ -847,7 +847,7 @@ class TestVault:
847 847
         assert v._estimate_sufficient_hash_length() < len(self.phrase)
848 848
         assert v.generate(service) == expected
849 849
 
850
-    @Parametrizations.BINARY_STRINGS.value
850
+    @Parametrize.BINARY_STRINGS
851 851
     def test_224_binary_strings(self, s: str | bytes | bytearray) -> None:
852 852
         """Byte string conversion is idempotent."""
853 853
         binstr = vault.Vault._get_binary_string
... ...
@@ -7,12 +7,12 @@
7 7
 from __future__ import annotations
8 8
 
9 9
 import contextlib
10
-import enum
11 10
 import errno
12 11
 import gettext
13 12
 import os
14 13
 import re
15 14
 import string
15
+import types
16 16
 from typing import TYPE_CHECKING, cast
17 17
 
18 18
 import hypothesis
... ...
@@ -25,7 +25,7 @@ if TYPE_CHECKING:
25 25
     from collections.abc import Iterator
26 26
 
27 27
 
28
-class Parametrizations(enum.Enum):
28
+class Parametrize(types.SimpleNamespace):
29 29
     MAYBE_FORMAT_STRINGS = pytest.mark.parametrize(
30 30
         's', ['{spam}', '{spam}abc', '{', '}', '{{{']
31 31
     )
... ...
@@ -211,7 +211,7 @@ class TestL10nMachineryWithDebugTranslations:
211 211
             assert ts0 != ts1
212 212
             assert len({ts0, ts1}) == 2
213 213
 
214
-    @Parametrizations.MAYBE_FORMAT_STRINGS.value
214
+    @Parametrize.MAYBE_FORMAT_STRINGS
215 215
     def test_102_translated_strings_suppressed_interpolation_fail(
216 216
         self,
217 217
         s: str,
218 218