Marco Ricci commited on 2024-12-31 22:30:36
Zeige 2 geänderte Dateien mit 137 Einfügungen und 8 Löschungen.
Reimplement the machinery for displaying help texts and version information, supporting translatable strings. Besides the one-line help texts for the `--help` and `--version` options, the version information text is a translatable string itself, in case the name and version number should be rearranged, or prefixed or suffixed, or whatever.
... | ... |
@@ -401,6 +401,11 @@ class Label(enum.Enum): |
401 | 401 |
""", |
402 | 402 |
context='help text (option one-line description)', |
403 | 403 |
) |
404 |
+ HELP_OPTION_HELP_TEXT = _prepare_translatable( |
|
405 |
+ 'show this help text, then exit', |
|
406 |
+ comments='', |
|
407 |
+ context='help text (option one-line description)', |
|
408 |
+ ) |
|
404 | 409 |
QUIET_OPTION_HELP_TEXT = _prepare_translatable( |
405 | 410 |
'suppress even warnings, emit only errors', |
406 | 411 |
comments='', |
... | ... |
@@ -411,6 +416,11 @@ class Label(enum.Enum): |
411 | 416 |
comments='', |
412 | 417 |
context='help text (option one-line description)', |
413 | 418 |
) |
419 |
+ VERSION_OPTION_HELP_TEXT = _prepare_translatable( |
|
420 |
+ 'show applicable version information, then exit', |
|
421 |
+ comments='', |
|
422 |
+ context='help text (option one-line description)', |
|
423 |
+ ) |
|
414 | 424 |
|
415 | 425 |
DERIVEPASSPHRASE_VAULT_PHRASE_HELP_TEXT = _prepare_translatable( |
416 | 426 |
msg='prompt for a master passphrase', |
... | ... |
@@ -675,6 +685,14 @@ class Label(enum.Enum): |
675 | 685 |
comments='', |
676 | 686 |
context='help text, option group name', |
677 | 687 |
) |
688 |
+ VERSION_INFO_TEXT = _prepare_translatable( |
|
689 |
+ msg=r""" |
|
690 |
+ {PROG_NAME!s} {__version__} |
|
691 |
+ """, # noqa: RUF027 |
|
692 |
+ comments='', |
|
693 |
+ context='help text, version info text', |
|
694 |
+ flags='python-brace-format', |
|
695 |
+ ) |
|
678 | 696 |
CONFIRM_THIS_CHOICE_PROMPT_TEXT = _prepare_translatable( |
679 | 697 |
comments=r""" |
680 | 698 |
TRANSLATORS: There is no support for "yes" or "no" in other |
... | ... |
@@ -423,6 +423,10 @@ class OptionGroupOption(click.Option): |
423 | 423 |
self.help = help |
424 | 424 |
|
425 | 425 |
|
426 |
+class StandardOption(OptionGroupOption): |
|
427 |
+ pass |
|
428 |
+ |
|
429 |
+ |
|
426 | 430 |
class CommandWithHelpGroups(click.Command): |
427 | 431 |
"""A [`click.Command`][] with support for some help text customizations. |
428 | 432 |
|
... | ... |
@@ -498,6 +502,83 @@ class CommandWithHelpGroups(click.Command): |
498 | 502 |
rv.extend(str(x) for x in param.get_usage_pieces(ctx)) |
499 | 503 |
return rv |
500 | 504 |
|
505 |
+ def get_help_option( |
|
506 |
+ self, |
|
507 |
+ ctx: click.Context, |
|
508 |
+ ) -> click.Option | None: |
|
509 |
+ """Return a standard help option object. |
|
510 |
+ |
|
511 |
+ Based on code from click 8.1. Subject to the following license |
|
512 |
+ (3-clause BSD license): |
|
513 |
+ |
|
514 |
+ Copyright 2024 Pallets |
|
515 |
+ |
|
516 |
+ Redistribution and use in source and binary forms, with or |
|
517 |
+ without modification, are permitted provided that the |
|
518 |
+ following conditions are met: |
|
519 |
+ |
|
520 |
+ 1. Redistributions of source code must retain the above |
|
521 |
+ copyright notice, this list of conditions and the |
|
522 |
+ following disclaimer. |
|
523 |
+ |
|
524 |
+ 2. Redistributions in binary form must reproduce the above |
|
525 |
+ copyright notice, this list of conditions and the |
|
526 |
+ following disclaimer in the documentation and/or other |
|
527 |
+ materials provided with the distribution. |
|
528 |
+ |
|
529 |
+ 3. Neither the name of the copyright holder nor the names |
|
530 |
+ of its contributors may be used to endorse or promote |
|
531 |
+ products derived from this software without specific |
|
532 |
+ prior written permission. |
|
533 |
+ |
|
534 |
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
|
535 |
+ CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, |
|
536 |
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
|
537 |
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
538 |
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR |
|
539 |
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
540 |
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
|
541 |
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|
542 |
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|
543 |
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
544 |
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
|
545 |
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|
546 |
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
547 |
+ |
|
548 |
+ Modifications are marked with respective comments. They too are |
|
549 |
+ released under the same license above. The original code did |
|
550 |
+ not contain any "noqa" or "pragma" comments. |
|
551 |
+ |
|
552 |
+ Args: |
|
553 |
+ ctx: |
|
554 |
+ The click context. |
|
555 |
+ |
|
556 |
+ """ |
|
557 |
+ help_options = self.get_help_option_names(ctx) |
|
558 |
+ |
|
559 |
+ if not help_options or not self.add_help_option: # pragma: no cover |
|
560 |
+ return None |
|
561 |
+ |
|
562 |
+ def show_help( |
|
563 |
+ ctx: click.Context, |
|
564 |
+ param: click.Parameter, # noqa: ARG001 |
|
565 |
+ value: str, |
|
566 |
+ ) -> None: |
|
567 |
+ if value and not ctx.resilient_parsing: |
|
568 |
+ click.echo(ctx.get_help(), color=ctx.color) |
|
569 |
+ ctx.exit() |
|
570 |
+ |
|
571 |
+ # Modified from click 8.1: We use StandardOption and a non-str |
|
572 |
+ # object as the help string. |
|
573 |
+ return StandardOption( |
|
574 |
+ help_options, |
|
575 |
+ is_flag=True, |
|
576 |
+ is_eager=True, |
|
577 |
+ expose_value=False, |
|
578 |
+ callback=show_help, |
|
579 |
+ help=_msg.TranslatedString(_msg.Label.HELP_OPTION_HELP_TEXT), |
|
580 |
+ ) |
|
581 |
+ |
|
501 | 582 |
def get_short_help_str( |
502 | 583 |
self, |
503 | 584 |
limit: int = 45, |
... | ... |
@@ -904,6 +985,37 @@ class CommandWithHelpGroups(click.Command): |
904 | 985 |
formatter.write_text(epilog) |
905 | 986 |
|
906 | 987 |
|
988 |
+def version_option_callback( |
|
989 |
+ ctx: click.Context, |
|
990 |
+ param: click.Parameter, |
|
991 |
+ value: bool, # noqa: FBT001 |
|
992 |
+) -> None: |
|
993 |
+ del param |
|
994 |
+ if value and not ctx.resilient_parsing: |
|
995 |
+ click.echo( |
|
996 |
+ str( |
|
997 |
+ _msg.TranslatedString( |
|
998 |
+ _msg.Label.VERSION_INFO_TEXT, |
|
999 |
+ PROG_NAME=PROG_NAME, |
|
1000 |
+ __version__=__version__, |
|
1001 |
+ ) |
|
1002 |
+ ), |
|
1003 |
+ ) |
|
1004 |
+ ctx.exit() |
|
1005 |
+ |
|
1006 |
+ |
|
1007 |
+def version_option(f: Callable[P, R]) -> Callable[P, R]: |
|
1008 |
+ return click.option( |
|
1009 |
+ '--version', |
|
1010 |
+ is_flag=True, |
|
1011 |
+ is_eager=True, |
|
1012 |
+ expose_value=False, |
|
1013 |
+ callback=version_option_callback, |
|
1014 |
+ cls=StandardOption, |
|
1015 |
+ help=_msg.TranslatedString(_msg.Label.VERSION_OPTION_HELP_TEXT), |
|
1016 |
+ )(f) |
|
1017 |
+ |
|
1018 |
+ |
|
907 | 1019 |
class LoggingOption(OptionGroupOption): |
908 | 1020 |
"""Logging options for the CLI.""" |
909 | 1021 |
|
... | ... |
@@ -1110,7 +1222,7 @@ class _TopLevelCLIEntryPoint(_DefaultToVaultGroup): |
1110 | 1222 |
_msg.TranslatedString(_msg.Label.DERIVEPASSPHRASE_03), |
1111 | 1223 |
), |
1112 | 1224 |
) |
1113 |
-@click.version_option(version=dpp.__version__, prog_name=PROG_NAME) |
|
1225 |
+@version_option |
|
1114 | 1226 |
@standard_logging_options |
1115 | 1227 |
@click.pass_context |
1116 | 1228 |
def derivepassphrase(ctx: click.Context, /) -> None: |
... | ... |
@@ -1159,7 +1271,7 @@ def derivepassphrase(ctx: click.Context, /) -> None: |
1159 | 1271 |
_msg.TranslatedString(_msg.Label.DERIVEPASSPHRASE_EXPORT_03), |
1160 | 1272 |
), |
1161 | 1273 |
) |
1162 |
-@click.version_option(version=dpp.__version__, prog_name=PROG_NAME) |
|
1274 |
+@version_option |
|
1163 | 1275 |
@standard_logging_options |
1164 | 1276 |
@click.pass_context |
1165 | 1277 |
def derivepassphrase_export(ctx: click.Context, /) -> None: |
... | ... |
@@ -1231,10 +1343,6 @@ def _load_data( |
1231 | 1343 |
assert_never(fmt) |
1232 | 1344 |
|
1233 | 1345 |
|
1234 |
-class StandardOption(OptionGroupOption): |
|
1235 |
- pass |
|
1236 |
- |
|
1237 |
- |
|
1238 | 1346 |
@derivepassphrase_export.command( |
1239 | 1347 |
'vault', |
1240 | 1348 |
context_settings={'help_option_names': ['-h', '--help']}, |
... | ... |
@@ -1255,7 +1363,6 @@ class StandardOption(OptionGroupOption): |
1255 | 1363 |
), |
1256 | 1364 |
), |
1257 | 1365 |
) |
1258 |
-@standard_logging_options |
|
1259 | 1366 |
@click.option( |
1260 | 1367 |
'-f', |
1261 | 1368 |
'--format', |
... | ... |
@@ -1288,6 +1395,8 @@ class StandardOption(OptionGroupOption): |
1288 | 1395 |
), |
1289 | 1396 |
cls=StandardOption, |
1290 | 1397 |
) |
1398 |
+@version_option |
|
1399 |
+@standard_logging_options |
|
1291 | 1400 |
@click.argument( |
1292 | 1401 |
'path', |
1293 | 1402 |
metavar=_msg.TranslatedString(_msg.Label.EXPORT_VAULT_METAVAR_PATH), |
... | ... |
@@ -2362,7 +2471,7 @@ DEFAULT_NOTES_MARKER = '# - - - - - >8 - - - - -' |
2362 | 2471 |
), |
2363 | 2472 |
cls=CompatibilityOption, |
2364 | 2473 |
) |
2365 |
-@click.version_option(version=dpp.__version__, prog_name=PROG_NAME) |
|
2474 |
+@version_option |
|
2366 | 2475 |
@standard_logging_options |
2367 | 2476 |
@click.argument( |
2368 | 2477 |
'service', |
... | ... |
@@ -2508,6 +2617,8 @@ def derivepassphrase_vault( # noqa: C901,PLR0912,PLR0913,PLR0914,PLR0915 |
2508 | 2617 |
group = LoggingOption |
2509 | 2618 |
elif isinstance(param, CompatibilityOption): |
2510 | 2619 |
group = CompatibilityOption |
2620 |
+ elif isinstance(param, StandardOption): |
|
2621 |
+ group = StandardOption |
|
2511 | 2622 |
elif isinstance(param, OptionGroupOption): |
2512 | 2623 |
raise AssertionError( # noqa: DOC501,TRY003,TRY004 |
2513 | 2624 |
f'Unknown option group for {param!r}' # noqa: EM102 |
2514 | 2625 |