Marco Ricci commited on 2025-01-01 00:47:57
              Zeige 2 geänderte Dateien mit 98 Einfügungen und 19 Löschungen.
            
We implement this with an eager, hidden "pseudo" option that checks for the `NO_COLOR` and `FORCE_COLOR` options and sets the context's color setting appropriately. To actually make use of this color setting, all output must query the context's color setting, as must all log output. For the log output specifically, we arrange to pass the `click` context in the extra dict of each logging call, so that the handler has access to the color setting too.
| ... | ... | 
                      @@ -95,7 +95,11 @@ class ClickEchoStderrHandler(logging.Handler):  | 
                  
| 95 | 95 | 
                        [`sys.stderr`][].  | 
                    
| 96 | 96 | 
                         | 
                    
| 97 | 97 | 
                        """  | 
                    
| 98 | 
                        - click.echo(self.format(record), err=True)  | 
                    |
| 98 | 
                        + click.echo(  | 
                    |
| 99 | 
                        + self.format(record),  | 
                    |
| 100 | 
                        + err=True,  | 
                    |
| 101 | 
                        + color=getattr(record, 'color', None),  | 
                    |
| 102 | 
                        + )  | 
                    |
| 99 | 103 | 
                         | 
                    
| 100 | 104 | 
                         | 
                    
| 101 | 105 | 
                        class CLIofPackageFormatter(logging.Formatter):  | 
                    
| ... | ... | 
                      @@ -1016,6 +1020,31 @@ def version_option(f: Callable[P, R]) -> Callable[P, R]:  | 
                  
| 1016 | 1020 | 
                        )(f)  | 
                    
| 1017 | 1021 | 
                         | 
                    
| 1018 | 1022 | 
                         | 
                    
| 1023 | 
                        +def color_forcing_callback(  | 
                    |
| 1024 | 
                        + ctx: click.Context,  | 
                    |
| 1025 | 
                        + param: click.Parameter,  | 
                    |
| 1026 | 
                        + value: Any, # noqa: ANN401  | 
                    |
| 1027 | 
                        +) -> None:  | 
                    |
| 1028 | 
                        + """Force the `click` context to honor `NO_COLOR` and `FORCE_COLOR`."""  | 
                    |
| 1029 | 
                        + del param, value  | 
                    |
| 1030 | 
                        +    if os.environ.get('NO_COLOR'):  # pragma: no cover
                       | 
                    |
| 1031 | 
                        + ctx.color = False  | 
                    |
| 1032 | 
                        +    if os.environ.get('FORCE_COLOR'):  # pragma: no cover
                       | 
                    |
| 1033 | 
                        + ctx.color = True  | 
                    |
| 1034 | 
                        +  | 
                    |
| 1035 | 
                        +  | 
                    |
| 1036 | 
                        +color_forcing_pseudo_option = click.option(  | 
                    |
| 1037 | 
                        + '--_pseudo-option-color-forcing',  | 
                    |
| 1038 | 
                        + '_color_forcing',  | 
                    |
| 1039 | 
                        + is_flag=True,  | 
                    |
| 1040 | 
                        + is_eager=True,  | 
                    |
| 1041 | 
                        + expose_value=False,  | 
                    |
| 1042 | 
                        + hidden=True,  | 
                    |
| 1043 | 
                        + callback=color_forcing_callback,  | 
                    |
| 1044 | 
                        + help='(pseudo-option)',  | 
                    |
| 1045 | 
                        +)  | 
                    |
| 1046 | 
                        +  | 
                    |
| 1047 | 
                        +  | 
                    |
| 1019 | 1048 | 
                        class LoggingOption(OptionGroupOption):  | 
                    
| 1020 | 1049 | 
                        """Logging options for the CLI."""  | 
                    
| 1021 | 1050 | 
                         | 
                    
| ... | ... | 
                      @@ -1223,6 +1252,7 @@ class _TopLevelCLIEntryPoint(_DefaultToVaultGroup):  | 
                  
| 1223 | 1252 | 
                        ),  | 
                    
| 1224 | 1253 | 
                        )  | 
                    
| 1225 | 1254 | 
                        @version_option  | 
                    
| 1255 | 
                        +@color_forcing_pseudo_option  | 
                    |
| 1226 | 1256 | 
                        @standard_logging_options  | 
                    
| 1227 | 1257 | 
                        @click.pass_context  | 
                    
| 1228 | 1258 | 
                        def derivepassphrase(ctx: click.Context, /) -> None:  | 
                    
| ... | ... | 
                      @@ -1240,7 +1270,10 @@ def derivepassphrase(ctx: click.Context, /) -> None:  | 
                  
| 1240 | 1270 | 
                             deprecation = logging.getLogger(f'{PROG_NAME}.deprecation')
                       | 
                    
| 1241 | 1271 | 
                        if ctx.invoked_subcommand is None:  | 
                    
| 1242 | 1272 | 
                        deprecation.warning(  | 
                    
| 1243 | 
                        - _msg.TranslatedString(_msg.WarnMsgTemplate.V10_SUBCOMMAND_REQUIRED)  | 
                    |
| 1273 | 
                        + _msg.TranslatedString(  | 
                    |
| 1274 | 
                        + _msg.WarnMsgTemplate.V10_SUBCOMMAND_REQUIRED  | 
                    |
| 1275 | 
                        + ),  | 
                    |
| 1276 | 
                        +            extra={'color': ctx.color},
                       | 
                    |
| 1244 | 1277 | 
                        )  | 
                    
| 1245 | 1278 | 
                        # See definition of click.Group.invoke, non-chained case.  | 
                    
| 1246 | 1279 | 
                        with ctx:  | 
                    
| ... | ... | 
                      @@ -1272,6 +1305,7 @@ def derivepassphrase(ctx: click.Context, /) -> None:  | 
                  
| 1272 | 1305 | 
                        ),  | 
                    
| 1273 | 1306 | 
                        )  | 
                    
| 1274 | 1307 | 
                        @version_option  | 
                    
| 1308 | 
                        +@color_forcing_pseudo_option  | 
                    |
| 1275 | 1309 | 
                        @standard_logging_options  | 
                    
| 1276 | 1310 | 
                        @click.pass_context  | 
                    
| 1277 | 1311 | 
                        def derivepassphrase_export(ctx: click.Context, /) -> None:  | 
                    
| ... | ... | 
                      @@ -1289,7 +1323,10 @@ def derivepassphrase_export(ctx: click.Context, /) -> None:  | 
                  
| 1289 | 1323 | 
                             deprecation = logging.getLogger(f'{PROG_NAME}.deprecation')
                       | 
                    
| 1290 | 1324 | 
                        if ctx.invoked_subcommand is None:  | 
                    
| 1291 | 1325 | 
                        deprecation.warning(  | 
                    
| 1292 | 
                        - _msg.TranslatedString(_msg.WarnMsgTemplate.V10_SUBCOMMAND_REQUIRED)  | 
                    |
| 1326 | 
                        + _msg.TranslatedString(  | 
                    |
| 1327 | 
                        + _msg.WarnMsgTemplate.V10_SUBCOMMAND_REQUIRED  | 
                    |
| 1328 | 
                        + ),  | 
                    |
| 1329 | 
                        +            extra={'color': ctx.color},
                       | 
                    |
| 1293 | 1330 | 
                        )  | 
                    
| 1294 | 1331 | 
                        # See definition of click.Group.invoke, non-chained case.  | 
                    
| 1295 | 1332 | 
                        with ctx:  | 
                    
| ... | ... | 
                      @@ -1413,6 +1450,7 @@ def _shell_complete_vault_path( # pragma: no cover  | 
                  
| 1413 | 1450 | 
                        cls=StandardOption,  | 
                    
| 1414 | 1451 | 
                        )  | 
                    
| 1415 | 1452 | 
                        @version_option  | 
                    
| 1453 | 
                        +@color_forcing_pseudo_option  | 
                    |
| 1416 | 1454 | 
                        @standard_logging_options  | 
                    
| 1417 | 1455 | 
                        @click.argument(  | 
                    
| 1418 | 1456 | 
                        'path',  | 
                    
| ... | ... | 
                      @@ -1462,6 +1500,7 @@ def derivepassphrase_export_vault(  | 
                  
| 1462 | 1500 | 
                        path=path,  | 
                    
| 1463 | 1501 | 
                        fmt=fmt,  | 
                    
| 1464 | 1502 | 
                        ),  | 
                    
| 1503 | 
                        +                extra={'color': ctx.color},
                       | 
                    |
| 1465 | 1504 | 
                        )  | 
                    
| 1466 | 1505 | 
                        continue  | 
                    
| 1467 | 1506 | 
                        except OSError as exc:  | 
                    
| ... | ... | 
                      @@ -1472,6 +1511,7 @@ def derivepassphrase_export_vault(  | 
                  
| 1472 | 1511 | 
                        error=exc.strerror,  | 
                    
| 1473 | 1512 | 
                        filename=exc.filename,  | 
                    
| 1474 | 1513 | 
                        ).maybe_without_filename(),  | 
                    
| 1514 | 
                        +                extra={'color': ctx.color},
                       | 
                    |
| 1475 | 1515 | 
                        )  | 
                    
| 1476 | 1516 | 
                        ctx.exit(1)  | 
                    
| 1477 | 1517 | 
                        except ModuleNotFoundError:  | 
                    
| ... | ... | 
                      @@ -1480,12 +1520,14 @@ def derivepassphrase_export_vault(  | 
                  
| 1480 | 1520 | 
                        _msg.ErrMsgTemplate.MISSING_MODULE,  | 
                    
| 1481 | 1521 | 
                        module='cryptography',  | 
                    
| 1482 | 1522 | 
                        ),  | 
                    
| 1523 | 
                        +                extra={'color': ctx.color},
                       | 
                    |
| 1483 | 1524 | 
                        )  | 
                    
| 1484 | 1525 | 
                        logger.info(  | 
                    
| 1485 | 1526 | 
                        _msg.TranslatedString(  | 
                    
| 1486 | 1527 | 
                        _msg.InfoMsgTemplate.PIP_INSTALL_EXTRA,  | 
                    
| 1487 | 1528 | 
                        extra_name='export',  | 
                    
| 1488 | 1529 | 
                        ),  | 
                    
| 1530 | 
                        +                extra={'color': ctx.color},
                       | 
                    |
| 1489 | 1531 | 
                        )  | 
                    
| 1490 | 1532 | 
                        ctx.exit(1)  | 
                    
| 1491 | 1533 | 
                        else:  | 
                    
| ... | ... | 
                      @@ -1495,9 +1537,13 @@ def derivepassphrase_export_vault(  | 
                  
| 1495 | 1537 | 
                        _msg.ErrMsgTemplate.INVALID_VAULT_CONFIG,  | 
                    
| 1496 | 1538 | 
                        config=config,  | 
                    
| 1497 | 1539 | 
                        ),  | 
                    
| 1540 | 
                        +                    extra={'color': ctx.color},
                       | 
                    |
| 1498 | 1541 | 
                        )  | 
                    
| 1499 | 1542 | 
                        ctx.exit(1)  | 
                    
| 1500 | 
                        - click.echo(json.dumps(config, indent=2, sort_keys=True))  | 
                    |
| 1543 | 
                        + click.echo(  | 
                    |
| 1544 | 
                        + json.dumps(config, indent=2, sort_keys=True),  | 
                    |
| 1545 | 
                        + color=ctx.color,  | 
                    |
| 1546 | 
                        + )  | 
                    |
| 1501 | 1547 | 
                        break  | 
                    
| 1502 | 1548 | 
                        else:  | 
                    
| 1503 | 1549 | 
                        logger.error(  | 
                    
| ... | ... | 
                      @@ -1505,6 +1551,7 @@ def derivepassphrase_export_vault(  | 
                  
| 1505 | 1551 | 
                        _msg.ErrMsgTemplate.CANNOT_PARSE_AS_VAULT_CONFIG,  | 
                    
| 1506 | 1552 | 
                        path=path,  | 
                    
| 1507 | 1553 | 
                        ).maybe_without_filename(),  | 
                    
| 1554 | 
                        +            extra={'color': ctx.color},
                       | 
                    |
| 1508 | 1555 | 
                        )  | 
                    
| 1509 | 1556 | 
                        ctx.exit(1)  | 
                    
| 1510 | 1557 | 
                         | 
                    
| ... | ... | 
                      @@ -1728,6 +1775,7 @@ def _prompt_for_selection(  | 
                  
| 1728 | 1775 | 
                        items: Sequence[str | bytes],  | 
                    
| 1729 | 1776 | 
                        heading: str = 'Possible choices:',  | 
                    
| 1730 | 1777 | 
                        single_choice_prompt: str = 'Confirm this choice?',  | 
                    
| 1778 | 
                        + ctx: click.Context | None = None,  | 
                    |
| 1731 | 1779 | 
                        ) -> int:  | 
                    
| 1732 | 1780 | 
                        """Prompt user for a choice among the given items.  | 
                    
| 1733 | 1781 | 
                         | 
                    
| ... | ... | 
                      @@ -1747,6 +1795,9 @@ def _prompt_for_selection(  | 
                  
| 1747 | 1795 | 
                        single_choice_prompt:  | 
                    
| 1748 | 1796 | 
                        The confirmation prompt if there is only a single possible  | 
                    
| 1749 | 1797 | 
                        choice. Defaults to a reasonable standard prompt.  | 
                    
| 1798 | 
                        + ctx:  | 
                    |
| 1799 | 
                        + An optional `click` context, from which output device  | 
                    |
| 1800 | 
                        + properties and color preferences will be queried.  | 
                    |
| 1750 | 1801 | 
                         | 
                    
| 1751 | 1802 | 
                        Returns:  | 
                    
| 1752 | 1803 | 
                        An index into the items sequence, indicating the user's  | 
                    
| ... | ... | 
                      @@ -1759,12 +1810,13 @@ def _prompt_for_selection(  | 
                  
| 1759 | 1810 | 
                         | 
                    
| 1760 | 1811 | 
                        """  | 
                    
| 1761 | 1812 | 
                        n = len(items)  | 
                    
| 1813 | 
                        + color = ctx.color if ctx is not None else None  | 
                    |
| 1762 | 1814 | 
                        if heading:  | 
                    
| 1763 | 
                        - click.echo(click.style(heading, bold=True))  | 
                    |
| 1815 | 
                        + click.echo(click.style(heading, bold=True), color=color)  | 
                    |
| 1764 | 1816 | 
                        for i, x in enumerate(items, start=1):  | 
                    
| 1765 | 
                        -        click.echo(click.style(f'[{i}]', bold=True), nl=False)
                       | 
                    |
| 1766 | 
                        -        click.echo(' ', nl=False)
                       | 
                    |
| 1767 | 
                        - click.echo(x)  | 
                    |
| 1817 | 
                        +        click.echo(click.style(f'[{i}]', bold=True), nl=False, color=color)
                       | 
                    |
| 1818 | 
                        +        click.echo(' ', nl=False, color=color)
                       | 
                    |
| 1819 | 
                        + click.echo(x, color=color)  | 
                    |
| 1768 | 1820 | 
                        if n > 1:  | 
                    
| 1769 | 1821 | 
                        choices = click.Choice([''] + [str(i) for i in range(1, n + 1)])  | 
                    
| 1770 | 1822 | 
                        choice = click.prompt(  | 
                    
| ... | ... | 
                      @@ -1796,7 +1848,10 @@ def _prompt_for_selection(  | 
                  
| 1796 | 1848 | 
                         | 
                    
| 1797 | 1849 | 
                         | 
                    
| 1798 | 1850 | 
                        def _select_ssh_key(  | 
                    
| 1799 | 
                        - conn: ssh_agent.SSHAgentClient | socket.socket | None = None, /  | 
                    |
| 1851 | 
                        + conn: ssh_agent.SSHAgentClient | socket.socket | None = None,  | 
                    |
| 1852 | 
                        + /,  | 
                    |
| 1853 | 
                        + *,  | 
                    |
| 1854 | 
                        + ctx: click.Context | None = None,  | 
                    |
| 1800 | 1855 | 
                        ) -> bytes | bytearray:  | 
                    
| 1801 | 1856 | 
                        """Interactively select an SSH key for passphrase derivation.  | 
                    
| 1802 | 1857 | 
                         | 
                    
| ... | ... | 
                      @@ -1808,6 +1863,9 @@ def _select_ssh_key(  | 
                  
| 1808 | 1863 | 
                        conn:  | 
                    
| 1809 | 1864 | 
                        An optional connection hint to the SSH agent. See  | 
                    
| 1810 | 1865 | 
                        [`ssh_agent.SSHAgentClient.ensure_agent_subcontext`][].  | 
                    
| 1866 | 
                        + ctx:  | 
                    |
| 1867 | 
                        + An `click` context, queried for output device properties and  | 
                    |
| 1868 | 
                        + color preferences when issuing the prompt.  | 
                    |
| 1811 | 1869 | 
                         | 
                    
| 1812 | 1870 | 
                        Returns:  | 
                    
| 1813 | 1871 | 
                        The selected SSH key.  | 
                    
| ... | ... | 
                      @@ -1852,6 +1910,7 @@ def _select_ssh_key(  | 
                  
| 1852 | 1910 | 
                        key_listing,  | 
                    
| 1853 | 1911 | 
                        heading='Suitable SSH keys:',  | 
                    
| 1854 | 1912 | 
                        single_choice_prompt='Use this key?',  | 
                    
| 1913 | 
                        + ctx=ctx,  | 
                    |
| 1855 | 1914 | 
                        )  | 
                    
| 1856 | 1915 | 
                        return suitable_keys[choice].key  | 
                    
| 1857 | 1916 | 
                         | 
                    
| ... | ... | 
                      @@ -1917,6 +1976,7 @@ def _check_for_misleading_passphrase(  | 
                  
| 1917 | 1976 | 
                        value: dict[str, Any],  | 
                    
| 1918 | 1977 | 
                        *,  | 
                    
| 1919 | 1978 | 
                        main_config: dict[str, Any],  | 
                    
| 1979 | 
                        + ctx: click.Context | None = None,  | 
                    |
| 1920 | 1980 | 
                        ) -> None:  | 
                    
| 1921 | 1981 | 
                        form_key = 'unicode-normalization-form'  | 
                    
| 1922 | 1982 | 
                             default_form: str = main_config.get('vault', {}).get(
                       | 
                    
| ... | ... | 
                      @@ -1954,6 +2014,7 @@ def _check_for_misleading_passphrase(  | 
                  
| 1954 | 2014 | 
                        formatted_key,  | 
                    
| 1955 | 2015 | 
                        form,  | 
                    
| 1956 | 2016 | 
                        stacklevel=2,  | 
                    
| 2017 | 
                        +                extra={'color': ctx.color if ctx is not None else None},
                       | 
                    |
| 1957 | 2018 | 
                        )  | 
                    
| 1958 | 2019 | 
                         | 
                    
| 1959 | 2020 | 
                         | 
                    
| ... | ... | 
                      @@ -2522,6 +2583,7 @@ DEFAULT_NOTES_MARKER = '# - - - - - >8 - - - - -'  | 
                  
| 2522 | 2583 | 
                        cls=CompatibilityOption,  | 
                    
| 2523 | 2584 | 
                        )  | 
                    
| 2524 | 2585 | 
                        @version_option  | 
                    
| 2586 | 
                        +@color_forcing_pseudo_option  | 
                    |
| 2525 | 2587 | 
                        @standard_logging_options  | 
                    
| 2526 | 2588 | 
                        @click.argument(  | 
                    
| 2527 | 2589 | 
                        'service',  | 
                    
| ... | ... | 
                      @@ -2725,7 +2787,9 @@ def derivepassphrase_vault( # noqa: C901,PLR0912,PLR0913,PLR0914,PLR0915  | 
                  
| 2725 | 2787 | 
                        def err(msg: Any, /, **kwargs: Any) -> NoReturn: # noqa: ANN401  | 
                    
| 2726 | 2788 | 
                                 stacklevel = kwargs.pop('stacklevel', 1)
                       | 
                    
| 2727 | 2789 | 
                        stacklevel += 1  | 
                    
| 2728 | 
                        - logger.error(msg, stacklevel=stacklevel, **kwargs)  | 
                    |
| 2790 | 
                        +        extra = kwargs.pop('extra', {})
                       | 
                    |
| 2791 | 
                        +        extra.setdefault('color', ctx.color)
                       | 
                    |
| 2792 | 
                        + logger.error(msg, stacklevel=stacklevel, extra=extra, **kwargs)  | 
                    |
| 2729 | 2793 | 
                        ctx.exit(1)  | 
                    
| 2730 | 2794 | 
                         | 
                    
| 2731 | 2795 | 
                        def get_config() -> _types.VaultConfig:  | 
                    
| ... | ... | 
                      @@ -2746,6 +2810,7 @@ def derivepassphrase_vault( # noqa: C901,PLR0912,PLR0913,PLR0914,PLR0915  | 
                  
| 2746 | 2810 | 
                        old=old_name,  | 
                    
| 2747 | 2811 | 
                        new=new_name,  | 
                    
| 2748 | 2812 | 
                        ),  | 
                    
| 2813 | 
                        +                extra={'color': ctx.color},
                       | 
                    |
| 2749 | 2814 | 
                        )  | 
                    
| 2750 | 2815 | 
                        if isinstance(exc, OSError):  | 
                    
| 2751 | 2816 | 
                        logger.warning(  | 
                    
| ... | ... | 
                      @@ -2755,6 +2820,7 @@ def derivepassphrase_vault( # noqa: C901,PLR0912,PLR0913,PLR0914,PLR0915  | 
                  
| 2755 | 2820 | 
                        error=exc.strerror,  | 
                    
| 2756 | 2821 | 
                        filename=exc.filename,  | 
                    
| 2757 | 2822 | 
                        ).maybe_without_filename(),  | 
                    
| 2823 | 
                        +                    extra={'color': ctx.color},
                       | 
                    |
| 2758 | 2824 | 
                        )  | 
                    
| 2759 | 2825 | 
                        else:  | 
                    
| 2760 | 2826 | 
                        deprecation.info(  | 
                    
| ... | ... | 
                      @@ -2762,6 +2828,7 @@ def derivepassphrase_vault( # noqa: C901,PLR0912,PLR0913,PLR0914,PLR0915  | 
                  
| 2762 | 2828 | 
                        _msg.InfoMsgTemplate.SUCCESSFULLY_MIGRATED,  | 
                    
| 2763 | 2829 | 
                        path=new_name,  | 
                    
| 2764 | 2830 | 
                        ),  | 
                    
| 2831 | 
                        +                    extra={'color': ctx.color},
                       | 
                    |
| 2765 | 2832 | 
                        )  | 
                    
| 2766 | 2833 | 
                        return backup_config  | 
                    
| 2767 | 2834 | 
                        except OSError as exc:  | 
                    
| ... | ... | 
                      @@ -2882,7 +2949,8 @@ def derivepassphrase_vault( # noqa: C901,PLR0912,PLR0913,PLR0914,PLR0915  | 
                  
| 2882 | 2949 | 
                        _msg.TranslatedString(  | 
                    
| 2883 | 2950 | 
                        _msg.WarnMsgTemplate.EMPTY_SERVICE_NOT_SUPPORTED,  | 
                    
| 2884 | 2951 | 
                        service_metavar=service_metavar,  | 
                    
| 2885 | 
                        - )  | 
                    |
| 2952 | 
                        + ),  | 
                    |
| 2953 | 
                        +            extra={'color': ctx.color},
                       | 
                    |
| 2886 | 2954 | 
                        )  | 
                    
| 2887 | 2955 | 
                         | 
                    
| 2888 | 2956 | 
                        if edit_notes:  | 
                    
| ... | ... | 
                      @@ -2982,6 +3050,7 @@ def derivepassphrase_vault( # noqa: C901,PLR0912,PLR0913,PLR0914,PLR0915  | 
                  
| 2982 | 3050 | 
                        path=_types.json_path(step.path),  | 
                    
| 2983 | 3051 | 
                        new=json.dumps(step.new_value),  | 
                    
| 2984 | 3052 | 
                        ),  | 
                    
| 3053 | 
                        +                    extra={'color': ctx.color},
                       | 
                    |
| 2985 | 3054 | 
                        )  | 
                    
| 2986 | 3055 | 
                        else:  | 
                    
| 2987 | 3056 | 
                        logger.warning(  | 
                    
| ... | ... | 
                      @@ -2990,6 +3059,7 @@ def derivepassphrase_vault( # noqa: C901,PLR0912,PLR0913,PLR0914,PLR0915  | 
                  
| 2990 | 3059 | 
                        path=_types.json_path(step.path),  | 
                    
| 2991 | 3060 | 
                        old=json.dumps(step.old_value),  | 
                    
| 2992 | 3061 | 
                        ),  | 
                    
| 3062 | 
                        +                    extra={'color': ctx.color},
                       | 
                    |
| 2993 | 3063 | 
                        )  | 
                    
| 2994 | 3064 | 
                        if '' in maybe_config['services']:  | 
                    
| 2995 | 3065 | 
                        logger.warning(  | 
                    
| ... | ... | 
                      @@ -2998,18 +3068,21 @@ def derivepassphrase_vault( # noqa: C901,PLR0912,PLR0913,PLR0914,PLR0915  | 
                  
| 2998 | 3068 | 
                        service_metavar=service_metavar,  | 
                    
| 2999 | 3069 | 
                        PROG_NAME=PROG_NAME,  | 
                    
| 3000 | 3070 | 
                        ),  | 
                    
| 3071 | 
                        +                extra={'color': ctx.color},
                       | 
                    |
| 3001 | 3072 | 
                        )  | 
                    
| 3002 | 3073 | 
                        try:  | 
                    
| 3003 | 3074 | 
                        _check_for_misleading_passphrase(  | 
                    
| 3004 | 3075 | 
                                         ('global',),
                       | 
                    
| 3005 | 3076 | 
                                         cast(dict[str, Any], maybe_config.get('global', {})),
                       | 
                    
| 3006 | 3077 | 
                        main_config=user_config,  | 
                    
| 3078 | 
                        + ctx=ctx,  | 
                    |
| 3007 | 3079 | 
                        )  | 
                    
| 3008 | 3080 | 
                        for key, value in maybe_config['services'].items():  | 
                    
| 3009 | 3081 | 
                        _check_for_misleading_passphrase(  | 
                    
| 3010 | 3082 | 
                                             ('services', key),
                       | 
                    
| 3011 | 3083 | 
                        cast(dict[str, Any], value),  | 
                    
| 3012 | 3084 | 
                        main_config=user_config,  | 
                    
| 3085 | 
                        + ctx=ctx,  | 
                    |
| 3013 | 3086 | 
                        )  | 
                    
| 3014 | 3087 | 
                        except AssertionError as exc:  | 
                    
| 3015 | 3088 | 
                        err(  | 
                    
| ... | ... | 
                      @@ -3026,7 +3099,8 @@ def derivepassphrase_vault( # noqa: C901,PLR0912,PLR0913,PLR0914,PLR0915  | 
                  
| 3026 | 3099 | 
                        logger.warning(  | 
                    
| 3027 | 3100 | 
                        _msg.TranslatedString(  | 
                    
| 3028 | 3101 | 
                        _msg.WarnMsgTemplate.GLOBAL_PASSPHRASE_INEFFECTIVE,  | 
                    
| 3029 | 
                        - )  | 
                    |
| 3102 | 
                        + ),  | 
                    |
| 3103 | 
                        +                extra={'color': ctx.color},
                       | 
                    |
| 3030 | 3104 | 
                        )  | 
                    
| 3031 | 3105 | 
                        for service_name, service_obj in maybe_config['services'].items():  | 
                    
| 3032 | 3106 | 
                        has_key = _types.js_truthiness(  | 
                    
| ... | ... | 
                      @@ -3041,6 +3115,7 @@ def derivepassphrase_vault( # noqa: C901,PLR0912,PLR0913,PLR0914,PLR0915  | 
                  
| 3041 | 3115 | 
                        _msg.WarnMsgTemplate.SERVICE_PASSPHRASE_INEFFECTIVE,  | 
                    
| 3042 | 3116 | 
                        service=json.dumps(service_name),  | 
                    
| 3043 | 3117 | 
                        ),  | 
                    
| 3118 | 
                        +                    extra={'color': ctx.color},
                       | 
                    |
| 3044 | 3119 | 
                        )  | 
                    
| 3045 | 3120 | 
                        if overwrite_config:  | 
                    
| 3046 | 3121 | 
                        put_config(maybe_config)  | 
                    
| ... | ... | 
                      @@ -3146,9 +3221,9 @@ def derivepassphrase_vault( # noqa: C901,PLR0912,PLR0913,PLR0914,PLR0915  | 
                  
| 3146 | 3221 | 
                        )  | 
                    
| 3147 | 3222 | 
                        if use_key:  | 
                    
| 3148 | 3223 | 
                        try:  | 
                    
| 3149 | 
                        - key = base64.standard_b64encode(_select_ssh_key()).decode(  | 
                    |
| 3150 | 
                        - 'ASCII'  | 
                    |
| 3151 | 
                        - )  | 
                    |
| 3224 | 
                        + key = base64.standard_b64encode(  | 
                    |
| 3225 | 
                        + _select_ssh_key(ctx=ctx)  | 
                    |
| 3226 | 
                        +                ).decode('ASCII')
                       | 
                    |
| 3152 | 3227 | 
                        except IndexError:  | 
                    
| 3153 | 3228 | 
                        err(  | 
                    
| 3154 | 3229 | 
                        _msg.TranslatedString(  | 
                    
| ... | ... | 
                      @@ -3219,6 +3294,7 @@ def derivepassphrase_vault( # noqa: C901,PLR0912,PLR0913,PLR0914,PLR0915  | 
                  
| 3219 | 3294 | 
                                                 ('services', service) if service else ('global',),
                       | 
                    
| 3220 | 3295 | 
                                                 {'phrase': phrase},
                       | 
                    
| 3221 | 3296 | 
                        main_config=user_config,  | 
                    
| 3297 | 
                        + ctx=ctx,  | 
                    |
| 3222 | 3298 | 
                        )  | 
                    
| 3223 | 3299 | 
                        except AssertionError as exc:  | 
                    
| 3224 | 3300 | 
                        err(  | 
                    
| ... | ... | 
                      @@ -3234,13 +3310,15 @@ def derivepassphrase_vault( # noqa: C901,PLR0912,PLR0913,PLR0914,PLR0915  | 
                  
| 3234 | 3310 | 
                        _msg.TranslatedString(  | 
                    
| 3235 | 3311 | 
                        _msg.WarnMsgTemplate.SERVICE_PASSPHRASE_INEFFECTIVE,  | 
                    
| 3236 | 3312 | 
                        service=json.dumps(service),  | 
                    
| 3237 | 
                        - )  | 
                    |
| 3313 | 
                        + ),  | 
                    |
| 3314 | 
                        +                            extra={'color': ctx.color},
                       | 
                    |
| 3238 | 3315 | 
                        )  | 
                    
| 3239 | 3316 | 
                        else:  | 
                    
| 3240 | 3317 | 
                        logger.warning(  | 
                    
| 3241 | 3318 | 
                        _msg.TranslatedString(  | 
                    
| 3242 | 3319 | 
                        _msg.WarnMsgTemplate.GLOBAL_PASSPHRASE_INEFFECTIVE  | 
                    
| 3243 | 
                        - )  | 
                    |
| 3320 | 
                        + ),  | 
                    |
| 3321 | 
                        +                            extra={'color': ctx.color},
                       | 
                    |
| 3244 | 3322 | 
                        )  | 
                    
| 3245 | 3323 | 
                        if not view.maps[0] and not unset_settings:  | 
                    
| 3246 | 3324 | 
                        settings_type = 'service' if service else 'global'  | 
                    
| ... | ... | 
                      @@ -3292,6 +3370,7 @@ def derivepassphrase_vault( # noqa: C901,PLR0912,PLR0913,PLR0914,PLR0915  | 
                  
| 3292 | 3370 | 
                        _ORIGIN.INTERACTIVE,  | 
                    
| 3293 | 3371 | 
                                                 {'phrase': phrase},
                       | 
                    
| 3294 | 3372 | 
                        main_config=user_config,  | 
                    
| 3373 | 
                        + ctx=ctx,  | 
                    |
| 3295 | 3374 | 
                        )  | 
                    
| 3296 | 3375 | 
                        except AssertionError as exc:  | 
                    
| 3297 | 3376 | 
                        err(  | 
                    
| ... | ... | 
                      @@ -3327,7 +3406,7 @@ def derivepassphrase_vault( # noqa: C901,PLR0912,PLR0913,PLR0914,PLR0915  | 
                  
| 3327 | 3406 | 
                        raise click.UsageError(str(err_msg))  | 
                    
| 3328 | 3407 | 
                                     kwargs.pop('key', '')
                       | 
                    
| 3329 | 3408 | 
                        result = vault.Vault(**kwargs).generate(service)  | 
                    
| 3330 | 
                        -            click.echo(result.decode('ASCII'))
                       | 
                    |
| 3409 | 
                        +            click.echo(result.decode('ASCII'), color=ctx.color)
                       | 
                    |
| 3331 | 3410 | 
                         | 
                    
| 3332 | 3411 | 
                         | 
                    
| 3333 | 3412 | 
                        if __name__ == '__main__':  | 
                    
| ... | ... | 
                      @@ -1294,7 +1294,7 @@ contents go here  | 
                  
| 1294 | 1294 | 
                        ):  | 
                    
| 1295 | 1295 | 
                        custom_error = 'custom error message'  | 
                    
| 1296 | 1296 | 
                         | 
                    
| 1297 | 
                        - def raiser() -> None:  | 
                    |
| 1297 | 
                        + def raiser(*_args: Any, **_kwargs: Any) -> None:  | 
                    |
| 1298 | 1298 | 
                        raise RuntimeError(custom_error)  | 
                    
| 1299 | 1299 | 
                         | 
                    
| 1300 | 1300 | 
                        monkeypatch.setattr(cli, '_select_ssh_key', raiser)  | 
                    
| 1301 | 1301 |