Add known and supported features in the version output
Marco Ricci

Marco Ricci commited on 2025-02-15 00:01:29
Zeige 3 geänderte Dateien mit 57 Einfügungen und 1 Löschungen.


Emit known and supported features in the version output, such as master
SSH key support for the `vault` subcommand.
... ...
@@ -20,6 +20,7 @@ import importlib.metadata
20 20
 import inspect
21 21
 import logging
22 22
 import os
23
+import socket
23 24
 import warnings
24 25
 from typing import TYPE_CHECKING, Callable, Literal, TextIO, TypeVar
25 26
 
... ...
@@ -1126,6 +1127,19 @@ def vault_version_option_callback(
1126 1127
 ) -> None:
1127 1128
     if value and not ctx.resilient_parsing:
1128 1129
         common_version_output(ctx, param, value)
1130
+        features = {
1131
+            'master SSH key': hasattr(socket, 'AF_UNIX'),
1132
+        }
1133
+        click.echo()
1134
+        version_info_types = {
1135
+            _msg.Label.SUPPORTED_FEATURES: [
1136
+                k for k, v in features.items() if v
1137
+            ],
1138
+            _msg.Label.KNOWN_FEATURES: [
1139
+                k for k, v in features.items() if not v
1140
+            ],
1141
+        }
1142
+        print_version_info_types(version_info_types, ctx=ctx)
1129 1143
         ctx.exit()
1130 1144
 
1131 1145
 
... ...
@@ -1300,6 +1300,16 @@ class Label(enum.Enum):
1300 1300
         'Known derivation schemes:',
1301 1301
     )
1302 1302
     """"""
1303
+    KNOWN_FEATURES = commented(
1304
+        'This is part of the version output, emitting lists of known, '
1305
+        'unavailable features for this subcommand.  '
1306
+        'A comma-separated English list of items follows, '
1307
+        'with standard English punctuation.',
1308
+    )(
1309
+        'Label :: Info Message:: Table row header',
1310
+        'Known features:',
1311
+    )
1312
+    """"""
1303 1313
     KNOWN_FOREIGN_CONFIGURATION_FORMATS = commented(
1304 1314
         'This is part of the version output, emitting lists of known '
1305 1315
         'foreign configuration formats.  A comma-separated English list '
... ...
@@ -1318,6 +1328,15 @@ class Label(enum.Enum):
1318 1328
         'Supported derivation schemes:',
1319 1329
     )
1320 1330
     """"""
1331
+    SUPPORTED_FEATURES = commented(
1332
+        'This is part of the version output, emitting lists of supported '
1333
+        'features for this subcommand.  A comma-separated '
1334
+        'English list of items follows, with standard English punctuation.',
1335
+    )(
1336
+        'Label :: Info Message:: Table row header',
1337
+        'Supported features:',
1338
+    )
1339
+    """"""
1321 1340
     SUPPORTED_FOREIGN_CONFIGURATION_FORMATS = commented(
1322 1341
         'This is part of the version output, emitting lists of supported '
1323 1342
         'foreign configuration formats.  A comma-separated English list '
... ...
@@ -86,6 +86,7 @@ class VersionOutputData(NamedTuple):
86 86
     foreign_configuration_formats: dict[str, bool]
87 87
     extras: frozenset[str]
88 88
     subcommands: frozenset[str]
89
+    features: dict[str, bool]
89 90
 
90 91
 
91 92
 PASSPHRASE_GENERATION_OPTIONS: list[tuple[str, ...]] = [
... ...
@@ -405,12 +406,14 @@ def parse_version_output(  # noqa: C901
405 406
     formats: dict[str, bool] = {}
406 407
     subcommands: set[str] = set()
407 408
     extras: set[str] = set()
408
-    if len(paragraphs) < 2:
409
+    features: dict[str, bool] = {}
410
+    if len(paragraphs) < 2:  # pragma: no cover
409 411
         return VersionOutputData(
410 412
             derivation_schemes=schemes,
411 413
             foreign_configuration_formats=formats,
412 414
             subcommands=frozenset(subcommands),
413 415
             extras=frozenset(extras),
416
+            features=features,
414 417
         )
415 418
     for line in paragraphs[1]:
416 419
         line_type, _, value = line.partition(':')
... ...
@@ -432,6 +435,10 @@ def parse_version_output(  # noqa: C901
432 435
                 subcommands.add(item)
433 436
             elif line_type == 'PEP 508 extras':
434 437
                 extras.add(item)
438
+            elif line_type == 'Supported features':
439
+                features[item] = True
440
+            elif line_type == 'Known features':
441
+                features[item] = False
435 442
             else:
436 443
                 raise AssertionError(  # noqa: TRY003
437 444
                     f'Unknown version info line type: {line_type!r}'  # noqa: EM102
... ...
@@ -441,6 +448,7 @@ def parse_version_output(  # noqa: C901
441 448
         foreign_configuration_formats=formats,
442 449
         subcommands=frozenset(subcommands),
443 450
         extras=frozenset(extras),
451
+        features=features,
444 452
     )
445 453
 
446 454
 
... ...
@@ -1481,6 +1489,7 @@ PEP 508 extras: export.
1481 1489
                         'vault v0.3': True,
1482 1490
                     },
1483 1491
                     subcommands=frozenset(),
1492
+                    features={},
1484 1493
                     extras=frozenset({'export'}),
1485 1494
                 ),
1486 1495
                 id='derivepassphrase-0.4.0-export',
... ...
@@ -1504,6 +1513,7 @@ No PEP 508 extras are active.
1504 1513
                         'vault v0.3': False,
1505 1514
                     },
1506 1515
                     subcommands=frozenset({'export', 'vault'}),
1516
+                    features={},
1507 1517
                     extras=frozenset({}),
1508 1518
                 ),
1509 1519
                 id='derivepassphrase-0.5-plain',
... ...
@@ -1525,6 +1535,8 @@ Supported foreign configuration formats: derivepassphrase, nonsense.
1525 1535
 Known foreign configuration formats: divination v3.141592,
1526 1536
     /dev/random.
1527 1537
 Supported subcommands: delete-all-files, dump-core.
1538
+Supported features: delete-while-open.
1539
+Known features: backups-are-nice-to-have.
1528 1540
 PEP 508 extras: annoying-popups, delete-all-files,
1529 1541
     dump-core-depending-on-the-phase-of-the-moon.
1530 1542
 
... ...
@@ -1548,6 +1560,10 @@ PEP 508 extras: annoying-popups, delete-all-files,
1548 1560
                         '/dev/random': False,
1549 1561
                     },
1550 1562
                     subcommands=frozenset({'delete-all-files', 'dump-core'}),
1563
+                    features={
1564
+                        'delete-while-open': True,
1565
+                        'backups-are-nice-to-have': False,
1566
+                    },
1551 1567
                     extras=frozenset({
1552 1568
                         'annoying-popups',
1553 1569
                         'delete-all-files',
... ...
@@ -1837,6 +1853,7 @@ class TestAllCLI:
1837 1853
         assert version_data.derivation_schemes == actually_known_schemes
1838 1854
         assert not version_data.foreign_configuration_formats
1839 1855
         assert version_data.subcommands == subcommands
1856
+        assert not version_data.features
1840 1857
         assert not version_data.extras
1841 1858
 
1842 1859
     def test_202b_export_version_option_output(
... ...
@@ -1885,6 +1902,7 @@ class TestAllCLI:
1885 1902
             == actually_known_formats
1886 1903
         )
1887 1904
         assert version_data.subcommands == subcommands
1905
+        assert not version_data.features
1888 1906
         assert not version_data.extras
1889 1907
 
1890 1908
     def test_202c_export_vault_version_option_output(
... ...
@@ -1939,6 +1957,7 @@ class TestAllCLI:
1939 1957
             == actually_known_formats
1940 1958
         )
1941 1959
         assert not version_data.subcommands
1960
+        assert not version_data.features
1942 1961
         assert version_data.extras == actually_enabled_extras
1943 1962
 
1944 1963
     def test_202d_vault_version_option_output(
... ...
@@ -1974,9 +1993,13 @@ class TestAllCLI:
1974 1993
         assert result.clean_exit(empty_stderr=True), 'expected clean exit'
1975 1994
         assert result.output.strip(), 'expected version output'
1976 1995
         version_data = parse_version_output(result.output)
1996
+        features: dict[str, bool] = {
1997
+            'master SSH key': hasattr(socket, 'AF_UNIX'),
1998
+        }
1977 1999
         assert not version_data.derivation_schemes
1978 2000
         assert not version_data.foreign_configuration_formats
1979 2001
         assert not version_data.subcommands
2002
+        assert version_data.features == features
1980 2003
         assert not version_data.extras
1981 2004
 
1982 2005
 
1983 2006