Add extended version output information
Marco Ricci

Marco Ricci commited on 2025-02-13 21:07:55
Zeige 3 geänderte Dateien mit 152 Einfügungen und 12 Löschungen.

... ...
@@ -16,6 +16,7 @@ Warning:
16 16
 from __future__ import annotations
17 17
 
18 18
 import collections
19
+import importlib.metadata
19 20
 import inspect
20 21
 import logging
21 22
 import os
... ...
@@ -39,6 +40,7 @@ if TYPE_CHECKING:
39 40
 
40 41
 PROG_NAME = _internals.PROG_NAME
41 42
 VERSION = _internals.VERSION
43
+VERSION_OUTPUT_WRAPPING_WIDTH = 72
42 44
 
43 45
 # Error messages
44 46
 NOT_AN_INTEGER = 'not an integer'
... ...
@@ -957,14 +959,111 @@ def version_option_callback(
957 959
 ) -> None:
958 960
     del param
959 961
     if value and not ctx.resilient_parsing:
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
+        try:
990
+            cryptography_version = importlib.metadata.version('cryptography')
991
+        except ModuleNotFoundError:
992
+            pass
993
+        else:
994
+            major_dependencies.append(f'cryptography {cryptography_version}')
995
+        major_dependencies.append(f'click {click.__version__}')
996
+
997
+        click.echo(
998
+            ' '.join([
999
+                click.style(PROG_NAME, bold=True),
1000
+                VERSION,
1001
+            ]),
1002
+            color=ctx.color,
1003
+        )
1004
+        for dependency in major_dependencies:
960 1005
             click.echo(
961 1006
                 str(
962 1007
                     _msg.TranslatedString(
963
-                    _msg.Label.VERSION_INFO_TEXT,
964
-                    PROG_NAME=PROG_NAME,
965
-                    VERSION=VERSION,
1008
+                        _msg.Label.VERSION_INFO_MAJOR_LIBRARY_TEXT,
1009
+                        dependency_name_and_version=dependency,
1010
+                    )
1011
+                ),
1012
+                color=ctx.color,
966 1013
             )
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
+        }
1032
+        for message_label, item_list in version_info_types.items():
1033
+            if item_list:
1034
+                current_length = len(str(_msg.TranslatedString(message_label)))
1035
+                formatted_item_list_pieces: list[str] = []
1036
+                n = len(item_list)
1037
+                for i, item in enumerate(item_list, start=1):
1038
+                    space = ' '
1039
+                    punctuation = '.' if i == n else ','
1040
+                    if (
1041
+                        current_length
1042
+                        + len(space)
1043
+                        + len(item)
1044
+                        + len(punctuation)
1045
+                        <= VERSION_OUTPUT_WRAPPING_WIDTH
1046
+                    ):
1047
+                        current_length += (
1048
+                            len(space) + len(item) + len(punctuation)
1049
+                        )
1050
+                        piece = f'{space}{item}{punctuation}'
1051
+                    else:
1052
+                        space = '    '
1053
+                        current_length = (
1054
+                            len(space) + len(item) + len(punctuation)
1055
+                        )
1056
+                        piece = f'\n{space}{item}{punctuation}'
1057
+                    formatted_item_list_pieces.append(piece)
1058
+                click.echo(
1059
+                    ''.join([
1060
+                        click.style(
1061
+                            str(_msg.TranslatedString(message_label)),
1062
+                            bold=True,
967 1063
                         ),
1064
+                        ''.join(formatted_item_list_pieces),
1065
+                    ]),
1066
+                    color=ctx.color,
968 1067
                 )
969 1068
         ctx.exit()
970 1069
 
... ...
@@ -1214,7 +1214,7 @@ class Label(enum.Enum):
1214 1214
         '',
1215 1215
     )(
1216 1216
         'Label :: Help text :: One-line description',
1217
-        'Show applicable version information, then exit.',
1217
+        'Show version and feature information, then exit.',
1218 1218
     )
1219 1219
     """"""
1220 1220
     COMMANDS_LABEL = commented(
... ...
@@ -1273,14 +1273,60 @@ class Label(enum.Enum):
1273 1273
         'Storage management',
1274 1274
     )
1275 1275
     """"""
1276
-    VERSION_INFO_TEXT = commented(
1277
-        '',
1276
+    VERSION_INFO_MAJOR_LIBRARY_TEXT = commented(
1277
+        'This message reports on the version of a major library '
1278
+        'currently in use, such as "cryptography".',
1278 1279
     )(
1279 1280
         'Label :: Info Message',
1280
-        '{PROG_NAME} {VERSION}',  # noqa: RUF027
1281
+        'Using {dependency_name_and_version}',
1281 1282
         flags='python-brace-format',
1282 1283
     )
1283 1284
     """"""
1285
+    ENABLED_PEP508_EXTRAS = commented(
1286
+        'This is part of the version output, emitting lists of enabled '
1287
+        'PEP 508 extras.  A comma-separated English list of items follows, '
1288
+        'with standard English punctuation.',
1289
+    )(
1290
+        'Label :: Info Message:: Table row header',
1291
+        'PEP 508 extras:',
1292
+    )
1293
+    """"""
1294
+    KNOWN_DERIVATION_SCHEMES = commented(
1295
+        'This is part of the version output, emitting lists of known '
1296
+        'derivation schemes.  A comma-separated English list of items '
1297
+        'follows, with standard English punctuation.',
1298
+    )(
1299
+        'Label :: Info Message:: Table row header',
1300
+        'Known derivation schemes:',
1301
+    )
1302
+    """"""
1303
+    KNOWN_FOREIGN_CONFIGURATION_FORMATS = commented(
1304
+        'This is part of the version output, emitting lists of known '
1305
+        'foreign configuration formats.  A comma-separated English list '
1306
+        'of items follows, with standard English punctuation.',
1307
+    )(
1308
+        'Label :: Info Message:: Table row header',
1309
+        'Known foreign configuration formats:',
1310
+    )
1311
+    """"""
1312
+    SUPPORTED_DERIVATION_SCHEMES = commented(
1313
+        'This is part of the version output, emitting lists of supported '
1314
+        'derivation schemes.  A comma-separated English list of items '
1315
+        'follows, with standard English punctuation.',
1316
+    )(
1317
+        'Label :: Info Message:: Table row header',
1318
+        'Supported derivation schemes:',
1319
+    )
1320
+    """"""
1321
+    SUPPORTED_FOREIGN_CONFIGURATION_FORMATS = commented(
1322
+        'This is part of the version output, emitting lists of supported '
1323
+        'foreign configuration formats.  A comma-separated English list '
1324
+        'of items follows, with standard English punctuation.',
1325
+    )(
1326
+        'Label :: Info Message:: Table row header',
1327
+        'Supported foreign configuration formats:',
1328
+    )
1329
+    """"""
1284 1330
     CONFIRM_THIS_CHOICE_PROMPT_TEXT = commented(
1285 1331
         'There is no support for "yes" or "no" in other languages '
1286 1332
         'than English, so it is advised that your translation makes it '
... ...
@@ -1778,11 +1778,6 @@ class TestAllCLI:
1778 1778
             'Expected no color, but found an ANSI control sequence'
1779 1779
         )
1780 1780
 
1781
-    @pytest.mark.xfail(
1782
-        reason='extras output not implemented yet',
1783
-        raises=AssertionError,
1784
-        strict=True,
1785
-    )
1786 1781
     @Parametrize.COMMAND_NON_EAGER_ARGUMENTS
1787 1782
     def test_202_version_option_output(
1788 1783
         self,
1789 1784