Marco Ricci commited on 2024-12-19 15:04:11
              Zeige 4 geänderte Dateien mit 92 Einfügungen und 31 Löschungen.
            
Introduce a new file for centralized user configuration of `derivepassphrase`, which we expect the user to write and maintain, and which we will never attempt to write to. Treat the existing "vault" configuration file as a "data" file, which is `derivepassphrase`'s responsibility to write and to maintain, and which the user shouldn't edit directly. In general, we now divide the configuration file set semantically into "user" files and "data" files with the aforementioned responsibilities: "user" files are managed by the user, "data" files are managed by the application. (This mirrors the distinction in the XDG Base Directory Specification, although we do not use their paths or their environment variables.) We choose TOML as the file format for "user" files, because it is the simplest configuration file format with both a robust parser in the Python standard library and support for comments. The "data" files currently use JSON, but may use other serialization formats in the future as well. We envision some use cases for the user configuration file such as disabling certain expected warnings, or declaring the desired Unicode normalization form of a text string, but as of now, no such configuration is defined or supported.
| ... | ... | 
                      @@ -33,6 +33,9 @@ dependencies = [  | 
                  
| 33 | 33 | 
                        # available in older Pythons (such as typing.Self). These are loaded from  | 
                    
| 34 | 34 | 
                        # typing_extensions, instead of using explicit version guards.  | 
                    
| 35 | 35 | 
                        "typing_extensions",  | 
                    
| 36 | 
                        + # We read configuration files in JSON and TOML format. The latter is  | 
                    |
| 37 | 
                        + # unavailable in the Python standard library until Python 3.11.  | 
                    |
| 38 | 
                        + 'tomli; python_version < "3.11"'  | 
                    |
| 36 | 39 | 
                        ]  | 
                    
| 37 | 40 | 
                        dynamic = ['version']  | 
                    
| 38 | 41 | 
                         | 
                    
| ... | ... | 
                      @@ -17,6 +17,7 @@ import inspect  | 
                  
| 17 | 17 | 
                        import json  | 
                    
| 18 | 18 | 
                        import logging  | 
                    
| 19 | 19 | 
                        import os  | 
                    
| 20 | 
                        +import sys  | 
                    |
| 20 | 21 | 
                        import unicodedata  | 
                    
| 21 | 22 | 
                        import warnings  | 
                    
| 22 | 23 | 
                        from typing import (  | 
                    
| ... | ... | 
                      @@ -40,6 +41,11 @@ from typing_extensions import (  | 
                  
| 40 | 41 | 
                        import derivepassphrase as dpp  | 
                    
| 41 | 42 | 
                        from derivepassphrase import _types, exporter, ssh_agent, vault  | 
                    
| 42 | 43 | 
                         | 
                    
| 44 | 
                        +if sys.version_info >= (3, 11):  | 
                    |
| 45 | 
                        + import tomllib  | 
                    |
| 46 | 
                        +else:  | 
                    |
| 47 | 
                        + import tomli as tomllib  | 
                    |
| 48 | 
                        +  | 
                    |
| 43 | 49 | 
                        if TYPE_CHECKING:  | 
                    
| 44 | 50 | 
                        import pathlib  | 
                    
| 45 | 51 | 
                        import socket  | 
                    
| ... | ... | 
                      @@ -914,6 +920,8 @@ def _config_filename(  | 
                  
| 914 | 920 | 
                        return path  | 
                    
| 915 | 921 | 
                        elif subsystem == 'vault': # noqa: RET505  | 
                    
| 916 | 922 | 
                                 filename = f'{subsystem}.json'
                       | 
                    
| 923 | 
                        + elif subsystem == 'user configuration':  | 
                    |
| 924 | 
                        + filename = 'config.toml'  | 
                    |
| 917 | 925 | 
                        elif subsystem == 'old settings.json':  | 
                    
| 918 | 926 | 
                        filename = 'settings.json'  | 
                    
| 919 | 927 | 
                        else: # pragma: no cover  | 
                    
| ... | ... | 
                      @@ -1013,6 +1021,27 @@ def _save_config(config: _types.VaultConfig, /) -> None:  | 
                  
| 1013 | 1021 | 
                        json.dump(config, fileobj)  | 
                    
| 1014 | 1022 | 
                         | 
                    
| 1015 | 1023 | 
                         | 
                    
| 1024 | 
                        +def _load_user_config() -> dict[str, Any]:  | 
                    |
| 1025 | 
                        + """Load the user config from the application directory.  | 
                    |
| 1026 | 
                        +  | 
                    |
| 1027 | 
                        + The filename is obtained via [`_config_filename`][].  | 
                    |
| 1028 | 
                        +  | 
                    |
| 1029 | 
                        + Returns:  | 
                    |
| 1030 | 
                        + The user configuration, as a nested `dict`.  | 
                    |
| 1031 | 
                        +  | 
                    |
| 1032 | 
                        + Raises:  | 
                    |
| 1033 | 
                        + OSError:  | 
                    |
| 1034 | 
                        + There was an OS error accessing the file.  | 
                    |
| 1035 | 
                        + ValueError:  | 
                    |
| 1036 | 
                        + The data loaded from the file is not a valid configuration  | 
                    |
| 1037 | 
                        + file.  | 
                    |
| 1038 | 
                        +  | 
                    |
| 1039 | 
                        + """  | 
                    |
| 1040 | 
                        + filename = _config_filename(subsystem='user configuration')  | 
                    |
| 1041 | 
                        + with open(filename, 'rb') as fileobj:  | 
                    |
| 1042 | 
                        + return tomllib.load(fileobj)  | 
                    |
| 1043 | 
                        +  | 
                    |
| 1044 | 
                        +  | 
                    |
| 1016 | 1045 | 
                        def _get_suitable_ssh_keys(  | 
                    
| 1017 | 1046 | 
                        conn: ssh_agent.SSHAgentClient | socket.socket | None = None, /  | 
                    
| 1018 | 1047 | 
                        ) -> Iterator[_types.KeyCommentPair]:  | 
                    
| ... | ... | 
                      @@ -1780,6 +1809,16 @@ def derivepassphrase_vault( # noqa: C901,PLR0912,PLR0913,PLR0914,PLR0915  | 
                  
| 1780 | 1809 | 
                        except Exception as exc: # noqa: BLE001  | 
                    
| 1781 | 1810 | 
                                     err('Cannot store config: %s', str(exc), exc_info=exc)
                       | 
                    
| 1782 | 1811 | 
                         | 
                    
| 1812 | 
                        + def get_user_config() -> dict[str, Any]:  | 
                    |
| 1813 | 
                        + try:  | 
                    |
| 1814 | 
                        + return _load_user_config()  | 
                    |
| 1815 | 
                        + except FileNotFoundError:  | 
                    |
| 1816 | 
                        +            return {}
                       | 
                    |
| 1817 | 
                        + except OSError as e:  | 
                    |
| 1818 | 
                        +            err('Cannot load user config: %s: %r', e.strerror, e.filename)
                       | 
                    |
| 1819 | 
                        + except Exception as e: # noqa: BLE001  | 
                    |
| 1820 | 
                        +            err('Cannot load user config: %s', str(e), exc_info=e)
                       | 
                    |
| 1821 | 
                        +  | 
                    |
| 1783 | 1822 | 
                        configuration: _types.VaultConfig  | 
                    
| 1784 | 1823 | 
                         | 
                    
| 1785 | 1824 | 
                             check_incompatible_options('--phrase', '--key')
                       | 
                    
| ... | ... | 
                      @@ -1822,6 +1861,8 @@ def derivepassphrase_vault( # noqa: C901,PLR0912,PLR0913,PLR0914,PLR0915  | 
                  
| 1822 | 1861 | 
                                     msg = f'{opt_str} does not take a SERVICE argument'
                       | 
                    
| 1823 | 1862 | 
                        raise click.UsageError(msg)  | 
                    
| 1824 | 1863 | 
                         | 
                    
| 1864 | 
                        + user_config = get_user_config()  | 
                    |
| 1865 | 
                        +  | 
                    |
| 1825 | 1866 | 
                        if service == '': # noqa: PLC1901  | 
                    
| 1826 | 1867 | 
                        logger.warning(  | 
                    
| 1827 | 1868 | 
                        'An empty SERVICE is not supported by vault(1). '  | 
                    
| ... | ... | 
                      @@ -1429,6 +1429,7 @@ def phrase_from_key(  | 
                  
| 1429 | 1429 | 
                        def isolated_config(  | 
                    
| 1430 | 1430 | 
                        monkeypatch: pytest.MonkeyPatch,  | 
                    
| 1431 | 1431 | 
                        runner: click.testing.CliRunner,  | 
                    
| 1432 | 
                        + main_config_str: str | None = None,  | 
                    |
| 1432 | 1433 | 
                        ) -> Iterator[None]:  | 
                    
| 1433 | 1434 | 
                        prog_name = cli.PROG_NAME  | 
                    
| 1434 | 1435 | 
                             env_name = prog_name.replace(' ', '_').upper() + '_PATH'
                       | 
                    
| ... | ... | 
                      @@ -1445,6 +1446,13 @@ def isolated_config(  | 
                  
| 1445 | 1446 | 
                        monkeypatch.delenv(env_name, raising=False)  | 
                    
| 1446 | 1447 | 
                        config_dir = cli._config_filename(subsystem=None)  | 
                    
| 1447 | 1448 | 
                        os.makedirs(config_dir, exist_ok=True)  | 
                    
| 1449 | 
                        + if isinstance(main_config_str, str):  | 
                    |
| 1450 | 
                        + with open(  | 
                    |
| 1451 | 
                        +                cli._config_filename('user configuration'),
                       | 
                    |
| 1452 | 
                        + 'w',  | 
                    |
| 1453 | 
                        + encoding='UTF-8',  | 
                    |
| 1454 | 
                        + ) as outfile:  | 
                    |
| 1455 | 
                        + outfile.write(main_config_str)  | 
                    |
| 1448 | 1456 | 
                        yield  | 
                    
| 1449 | 1457 | 
                         | 
                    
| 1450 | 1458 | 
                         | 
                    
| ... | ... | 
                      @@ -1452,12 +1460,15 @@ def isolated_config(  | 
                  
| 1452 | 1460 | 
                        def isolated_vault_config(  | 
                    
| 1453 | 1461 | 
                        monkeypatch: pytest.MonkeyPatch,  | 
                    
| 1454 | 1462 | 
                        runner: click.testing.CliRunner,  | 
                    
| 1455 | 
                        - config: Any,  | 
                    |
| 1463 | 
                        + vault_config: Any,  | 
                    |
| 1464 | 
                        + main_config_str: str | None = None,  | 
                    |
| 1456 | 1465 | 
                        ) -> Iterator[None]:  | 
                    
| 1457 | 
                        - with isolated_config(monkeypatch=monkeypatch, runner=runner):  | 
                    |
| 1466 | 
                        + with isolated_config(  | 
                    |
| 1467 | 
                        + monkeypatch=monkeypatch, runner=runner, main_config_str=main_config_str  | 
                    |
| 1468 | 
                        + ):  | 
                    |
| 1458 | 1469 | 
                        config_filename = cli._config_filename(subsystem='vault')  | 
                    
| 1459 | 1470 | 
                        with open(config_filename, 'w', encoding='UTF-8') as outfile:  | 
                    
| 1460 | 
                        - json.dump(config, outfile)  | 
                    |
| 1471 | 
                        + json.dump(vault_config, outfile)  | 
                    |
| 1461 | 1472 | 
                        yield  | 
                    
| 1462 | 1473 | 
                         | 
                    
| 1463 | 1474 | 
                         | 
                    
| ... | ... | 
                      @@ -327,7 +327,7 @@ class TestCLI:  | 
                  
| 327 | 327 | 
                        ) -> None:  | 
                    
| 328 | 328 | 
                        runner = click.testing.CliRunner(mix_stderr=False)  | 
                    
| 329 | 329 | 
                        with tests.isolated_vault_config(  | 
                    
| 330 | 
                        - monkeypatch=monkeypatch, runner=runner, config=config  | 
                    |
| 330 | 
                        + monkeypatch=monkeypatch, runner=runner, vault_config=config  | 
                    |
| 331 | 331 | 
                        ):  | 
                    
| 332 | 332 | 
                        monkeypatch.setattr(  | 
                    
| 333 | 333 | 
                        vault.Vault, 'phrase_from_key', tests.phrase_from_key  | 
                    
| ... | ... | 
                      @@ -356,7 +356,7 @@ class TestCLI:  | 
                  
| 356 | 356 | 
                        with tests.isolated_vault_config(  | 
                    
| 357 | 357 | 
                        monkeypatch=monkeypatch,  | 
                    
| 358 | 358 | 
                        runner=runner,  | 
                    
| 359 | 
                        -            config={'services': {DUMMY_SERVICE: DUMMY_CONFIG_SETTINGS}},
                       | 
                    |
| 359 | 
                        +            vault_config={'services': {DUMMY_SERVICE: DUMMY_CONFIG_SETTINGS}},
                       | 
                    |
| 360 | 360 | 
                        ):  | 
                    
| 361 | 361 | 
                        monkeypatch.setattr(  | 
                    
| 362 | 362 | 
                        cli, '_get_suitable_ssh_keys', tests.suitable_ssh_keys  | 
                    
| ... | ... | 
                      @@ -420,7 +420,7 @@ class TestCLI:  | 
                  
| 420 | 420 | 
                        monkeypatch.setattr(ssh_agent.SSHAgentClient, 'sign', tests.sign)  | 
                    
| 421 | 421 | 
                        runner = click.testing.CliRunner(mix_stderr=False)  | 
                    
| 422 | 422 | 
                        with tests.isolated_vault_config(  | 
                    
| 423 | 
                        - monkeypatch=monkeypatch, runner=runner, config=config  | 
                    |
| 423 | 
                        + monkeypatch=monkeypatch, runner=runner, vault_config=config  | 
                    |
| 424 | 424 | 
                        ):  | 
                    
| 425 | 425 | 
                        _result = runner.invoke(  | 
                    
| 426 | 426 | 
                        cli.derivepassphrase_vault,  | 
                    
| ... | ... | 
                      @@ -450,7 +450,7 @@ class TestCLI:  | 
                  
| 450 | 450 | 
                        with tests.isolated_vault_config(  | 
                    
| 451 | 451 | 
                        monkeypatch=monkeypatch,  | 
                    
| 452 | 452 | 
                        runner=runner,  | 
                    
| 453 | 
                        -                config={
                       | 
                    |
| 453 | 
                        +                vault_config={
                       | 
                    |
| 454 | 454 | 
                                             'global': {'key': DUMMY_KEY1_B64},
                       | 
                    
| 455 | 455 | 
                                             'services': {
                       | 
                    
| 456 | 456 | 
                                                 DUMMY_SERVICE: {
                       | 
                    
| ... | ... | 
                      @@ -510,7 +510,7 @@ class TestCLI:  | 
                  
| 510 | 510 | 
                        with tests.isolated_vault_config(  | 
                    
| 511 | 511 | 
                        monkeypatch=monkeypatch,  | 
                    
| 512 | 512 | 
                        runner=runner,  | 
                    
| 513 | 
                        - config=config,  | 
                    |
| 513 | 
                        + vault_config=config,  | 
                    |
| 514 | 514 | 
                        ):  | 
                    
| 515 | 515 | 
                        _result = runner.invoke(  | 
                    
| 516 | 516 | 
                        cli.derivepassphrase_vault,  | 
                    
| ... | ... | 
                      @@ -587,7 +587,7 @@ class TestCLI:  | 
                  
| 587 | 587 | 
                        with tests.isolated_vault_config(  | 
                    
| 588 | 588 | 
                        monkeypatch=monkeypatch,  | 
                    
| 589 | 589 | 
                        runner=runner,  | 
                    
| 590 | 
                        -            config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 590 | 
                        +            vault_config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 591 | 591 | 
                        ):  | 
                    
| 592 | 592 | 
                        _result = runner.invoke(  | 
                    
| 593 | 593 | 
                        cli.derivepassphrase_vault,  | 
                    
| ... | ... | 
                      @@ -613,7 +613,7 @@ class TestCLI:  | 
                  
| 613 | 613 | 
                        with tests.isolated_vault_config(  | 
                    
| 614 | 614 | 
                        monkeypatch=monkeypatch,  | 
                    
| 615 | 615 | 
                        runner=runner,  | 
                    
| 616 | 
                        -                config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 616 | 
                        +                vault_config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 617 | 617 | 
                        ):  | 
                    
| 618 | 618 | 
                        monkeypatch.setattr(  | 
                    
| 619 | 619 | 
                        cli, '_prompt_for_passphrase', tests.auto_prompt  | 
                    
| ... | ... | 
                      @@ -644,7 +644,7 @@ class TestCLI:  | 
                  
| 644 | 644 | 
                        with tests.isolated_vault_config(  | 
                    
| 645 | 645 | 
                        monkeypatch=monkeypatch,  | 
                    
| 646 | 646 | 
                        runner=runner,  | 
                    
| 647 | 
                        -            config={'services': {}},
                       | 
                    |
| 647 | 
                        +            vault_config={'services': {}},
                       | 
                    |
| 648 | 648 | 
                        ):  | 
                    
| 649 | 649 | 
                        _result = runner.invoke(  | 
                    
| 650 | 650 | 
                        cli.derivepassphrase_vault,  | 
                    
| ... | ... | 
                      @@ -727,7 +727,7 @@ class TestCLI:  | 
                  
| 727 | 727 | 
                        with tests.isolated_vault_config(  | 
                    
| 728 | 728 | 
                        monkeypatch=monkeypatch,  | 
                    
| 729 | 729 | 
                        runner=runner,  | 
                    
| 730 | 
                        -            config={'services': {}},
                       | 
                    |
| 730 | 
                        +            vault_config={'services': {}},
                       | 
                    |
| 731 | 731 | 
                        ):  | 
                    
| 732 | 732 | 
                        _result = runner.invoke(  | 
                    
| 733 | 733 | 
                        cli.derivepassphrase_vault,  | 
                    
| ... | ... | 
                      @@ -766,7 +766,7 @@ class TestCLI:  | 
                  
| 766 | 766 | 
                        with tests.isolated_vault_config(  | 
                    
| 767 | 767 | 
                        monkeypatch=pytest.MonkeyPatch(),  | 
                    
| 768 | 768 | 
                        runner=runner,  | 
                    
| 769 | 
                        -            config={'services': {}},
                       | 
                    |
| 769 | 
                        +            vault_config={'services': {}},
                       | 
                    |
| 770 | 770 | 
                        ):  | 
                    
| 771 | 771 | 
                        _result = runner.invoke(  | 
                    
| 772 | 772 | 
                        cli.derivepassphrase_vault,  | 
                    
| ... | ... | 
                      @@ -866,7 +866,7 @@ class TestCLI:  | 
                  
| 866 | 866 | 
                        ) -> None:  | 
                    
| 867 | 867 | 
                        runner = click.testing.CliRunner(mix_stderr=False)  | 
                    
| 868 | 868 | 
                        with tests.isolated_vault_config(  | 
                    
| 869 | 
                        -            monkeypatch=monkeypatch, runner=runner, config={}
                       | 
                    |
| 869 | 
                        +            monkeypatch=monkeypatch, runner=runner, vault_config={}
                       | 
                    |
| 870 | 870 | 
                        ):  | 
                    
| 871 | 871 | 
                        _result = runner.invoke(  | 
                    
| 872 | 872 | 
                        cli.derivepassphrase_vault,  | 
                    
| ... | ... | 
                      @@ -936,6 +936,8 @@ class TestCLI:  | 
                  
| 936 | 936 | 
                        result = tests.ReadableResult.parse(_result)  | 
                    
| 937 | 937 | 
                        assert result.error_exit(  | 
                    
| 938 | 938 | 
                        error='Cannot load config'  | 
                    
| 939 | 
                        + ) or result.error_exit(  | 
                    |
| 940 | 
                        + error='Cannot load user config'  | 
                    |
| 939 | 941 | 
                        ), 'expected error exit and known error message'  | 
                    
| 940 | 942 | 
                         | 
                    
| 941 | 943 | 
                        def test_220_edit_notes_successfully(  | 
                    
| ... | ... | 
                      @@ -950,7 +952,7 @@ contents go here  | 
                  
| 950 | 952 | 
                        with tests.isolated_vault_config(  | 
                    
| 951 | 953 | 
                        monkeypatch=monkeypatch,  | 
                    
| 952 | 954 | 
                        runner=runner,  | 
                    
| 953 | 
                        -            config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 955 | 
                        +            vault_config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 954 | 956 | 
                        ):  | 
                    
| 955 | 957 | 
                        monkeypatch.setattr(click, 'edit', lambda *a, **kw: edit_result) # noqa: ARG005  | 
                    
| 956 | 958 | 
                        _result = runner.invoke(  | 
                    
| ... | ... | 
                      @@ -976,7 +978,7 @@ contents go here  | 
                  
| 976 | 978 | 
                        with tests.isolated_vault_config(  | 
                    
| 977 | 979 | 
                        monkeypatch=monkeypatch,  | 
                    
| 978 | 980 | 
                        runner=runner,  | 
                    
| 979 | 
                        -            config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 981 | 
                        +            vault_config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 980 | 982 | 
                        ):  | 
                    
| 981 | 983 | 
                        monkeypatch.setattr(click, 'edit', lambda *a, **kw: None) # noqa: ARG005  | 
                    
| 982 | 984 | 
                        _result = runner.invoke(  | 
                    
| ... | ... | 
                      @@ -999,7 +1001,7 @@ contents go here  | 
                  
| 999 | 1001 | 
                        with tests.isolated_vault_config(  | 
                    
| 1000 | 1002 | 
                        monkeypatch=monkeypatch,  | 
                    
| 1001 | 1003 | 
                        runner=runner,  | 
                    
| 1002 | 
                        -            config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 1004 | 
                        +            vault_config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 1003 | 1005 | 
                        ):  | 
                    
| 1004 | 1006 | 
                        monkeypatch.setattr(click, 'edit', lambda *a, **kw: 'long\ntext') # noqa: ARG005  | 
                    
| 1005 | 1007 | 
                        _result = runner.invoke(  | 
                    
| ... | ... | 
                      @@ -1025,7 +1027,7 @@ contents go here  | 
                  
| 1025 | 1027 | 
                        with tests.isolated_vault_config(  | 
                    
| 1026 | 1028 | 
                        monkeypatch=monkeypatch,  | 
                    
| 1027 | 1029 | 
                        runner=runner,  | 
                    
| 1028 | 
                        -            config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 1030 | 
                        +            vault_config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 1029 | 1031 | 
                        ):  | 
                    
| 1030 | 1032 | 
                        monkeypatch.setattr(click, 'edit', lambda *a, **kw: '\n\n') # noqa: ARG005  | 
                    
| 1031 | 1033 | 
                        _result = runner.invoke(  | 
                    
| ... | ... | 
                      @@ -1096,7 +1098,7 @@ contents go here  | 
                  
| 1096 | 1098 | 
                        with tests.isolated_vault_config(  | 
                    
| 1097 | 1099 | 
                        monkeypatch=monkeypatch,  | 
                    
| 1098 | 1100 | 
                        runner=runner,  | 
                    
| 1099 | 
                        -            config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 1101 | 
                        +            vault_config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 1100 | 1102 | 
                        ):  | 
                    
| 1101 | 1103 | 
                        monkeypatch.setattr(  | 
                    
| 1102 | 1104 | 
                        cli, '_get_suitable_ssh_keys', tests.suitable_ssh_keys  | 
                    
| ... | ... | 
                      @@ -1141,7 +1143,7 @@ contents go here  | 
                  
| 1141 | 1143 | 
                        with tests.isolated_vault_config(  | 
                    
| 1142 | 1144 | 
                        monkeypatch=monkeypatch,  | 
                    
| 1143 | 1145 | 
                        runner=runner,  | 
                    
| 1144 | 
                        -            config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 1146 | 
                        +            vault_config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 1145 | 1147 | 
                        ):  | 
                    
| 1146 | 1148 | 
                        monkeypatch.setattr(  | 
                    
| 1147 | 1149 | 
                        cli, '_get_suitable_ssh_keys', tests.suitable_ssh_keys  | 
                    
| ... | ... | 
                      @@ -1165,7 +1167,7 @@ contents go here  | 
                  
| 1165 | 1167 | 
                        with tests.isolated_vault_config(  | 
                    
| 1166 | 1168 | 
                        monkeypatch=monkeypatch,  | 
                    
| 1167 | 1169 | 
                        runner=runner,  | 
                    
| 1168 | 
                        -            config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 1170 | 
                        +            vault_config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 1169 | 1171 | 
                        ):  | 
                    
| 1170 | 1172 | 
                        custom_error = 'custom error message'  | 
                    
| 1171 | 1173 | 
                         | 
                    
| ... | ... | 
                      @@ -1193,7 +1195,7 @@ contents go here  | 
                  
| 1193 | 1195 | 
                        with tests.isolated_vault_config(  | 
                    
| 1194 | 1196 | 
                        monkeypatch=monkeypatch,  | 
                    
| 1195 | 1197 | 
                        runner=runner,  | 
                    
| 1196 | 
                        -            config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 1198 | 
                        +            vault_config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 1197 | 1199 | 
                        ):  | 
                    
| 1198 | 1200 | 
                                     monkeypatch.delenv('SSH_AUTH_SOCK', raising=False)
                       | 
                    
| 1199 | 1201 | 
                        _result = runner.invoke(  | 
                    
| ... | ... | 
                      @@ -1214,7 +1216,7 @@ contents go here  | 
                  
| 1214 | 1216 | 
                        with tests.isolated_vault_config(  | 
                    
| 1215 | 1217 | 
                        monkeypatch=monkeypatch,  | 
                    
| 1216 | 1218 | 
                        runner=runner,  | 
                    
| 1217 | 
                        -            config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 1219 | 
                        +            vault_config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 1218 | 1220 | 
                        ):  | 
                    
| 1219 | 1221 | 
                                     monkeypatch.setenv('SSH_AUTH_SOCK', os.getcwd())
                       | 
                    
| 1220 | 1222 | 
                        _result = runner.invoke(  | 
                    
| ... | ... | 
                      @@ -1237,7 +1239,7 @@ contents go here  | 
                  
| 1237 | 1239 | 
                        with tests.isolated_vault_config(  | 
                    
| 1238 | 1240 | 
                        monkeypatch=monkeypatch,  | 
                    
| 1239 | 1241 | 
                        runner=runner,  | 
                    
| 1240 | 
                        -            config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 1242 | 
                        +            vault_config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 1241 | 1243 | 
                        ):  | 
                    
| 1242 | 1244 | 
                        tests.make_file_readonly(  | 
                    
| 1243 | 1245 | 
                        cli._config_filename(subsystem='vault'),  | 
                    
| ... | ... | 
                      @@ -1261,7 +1263,7 @@ contents go here  | 
                  
| 1261 | 1263 | 
                        with tests.isolated_vault_config(  | 
                    
| 1262 | 1264 | 
                        monkeypatch=monkeypatch,  | 
                    
| 1263 | 1265 | 
                        runner=runner,  | 
                    
| 1264 | 
                        -            config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 1266 | 
                        +            vault_config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 1265 | 1267 | 
                        ):  | 
                    
| 1266 | 1268 | 
                        custom_error = 'custom error message'  | 
                    
| 1267 | 1269 | 
                         | 
                    
| ... | ... | 
                      @@ -1495,7 +1497,9 @@ contents go here  | 
                  
| 1495 | 1497 | 
                        with tests.isolated_vault_config(  | 
                    
| 1496 | 1498 | 
                        monkeypatch=monkeypatch,  | 
                    
| 1497 | 1499 | 
                        runner=runner,  | 
                    
| 1498 | 
                        -            config={'services': {DUMMY_SERVICE: DUMMY_CONFIG_SETTINGS.copy()}},
                       | 
                    |
| 1500 | 
                        +            vault_config={
                       | 
                    |
| 1501 | 
                        +                'services': {DUMMY_SERVICE: DUMMY_CONFIG_SETTINGS.copy()}
                       | 
                    |
| 1502 | 
                        + },  | 
                    |
| 1499 | 1503 | 
                        ):  | 
                    
| 1500 | 1504 | 
                        _result = runner.invoke(  | 
                    
| 1501 | 1505 | 
                        cli.derivepassphrase_vault,  | 
                    
| ... | ... | 
                      @@ -1517,7 +1521,7 @@ contents go here  | 
                  
| 1517 | 1521 | 
                        with tests.isolated_vault_config(  | 
                    
| 1518 | 1522 | 
                        monkeypatch=monkeypatch,  | 
                    
| 1519 | 1523 | 
                        runner=runner,  | 
                    
| 1520 | 
                        -            config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 1524 | 
                        +            vault_config={'global': {'phrase': 'abc'}, 'services': {}},
                       | 
                    |
| 1521 | 1525 | 
                        ):  | 
                    
| 1522 | 1526 | 
                        monkeypatch.setenv(  | 
                    
| 1523 | 1527 | 
                        'SSH_AUTH_SOCK', "the value doesn't even matter"  | 
                    
| ... | ... | 
                      @@ -1559,7 +1563,7 @@ class TestCLIUtils:  | 
                  
| 1559 | 1563 | 
                        ) -> None:  | 
                    
| 1560 | 1564 | 
                        runner = click.testing.CliRunner()  | 
                    
| 1561 | 1565 | 
                        with tests.isolated_vault_config(  | 
                    
| 1562 | 
                        - monkeypatch=monkeypatch, runner=runner, config=config  | 
                    |
| 1566 | 
                        + monkeypatch=monkeypatch, runner=runner, vault_config=config  | 
                    |
| 1563 | 1567 | 
                        ):  | 
                    
| 1564 | 1568 | 
                        config_filename = cli._config_filename(subsystem='vault')  | 
                    
| 1565 | 1569 | 
                        with open(config_filename, encoding='UTF-8') as fileobj:  | 
                    
| ... | ... | 
                      @@ -1575,7 +1579,7 @@ class TestCLIUtils:  | 
                  
| 1575 | 1579 | 
                        with contextlib.ExitStack() as stack:  | 
                    
| 1576 | 1580 | 
                        stack.enter_context(  | 
                    
| 1577 | 1581 | 
                        tests.isolated_vault_config(  | 
                    
| 1578 | 
                        -                    monkeypatch=monkeypatch, runner=runner, config={}
                       | 
                    |
| 1582 | 
                        +                    monkeypatch=monkeypatch, runner=runner, vault_config={}
                       | 
                    |
| 1579 | 1583 | 
                        )  | 
                    
| 1580 | 1584 | 
                        )  | 
                    
| 1581 | 1585 | 
                        stack.enter_context(  | 
                    
| ... | ... | 
                      @@ -1862,7 +1866,9 @@ Boo.  | 
                  
| 1862 | 1866 | 
                        runner = click.testing.CliRunner(mix_stderr=False)  | 
                    
| 1863 | 1867 | 
                        for start_config in [config, result_config]:  | 
                    
| 1864 | 1868 | 
                        with tests.isolated_vault_config(  | 
                    
| 1865 | 
                        - monkeypatch=monkeypatch, runner=runner, config=start_config  | 
                    |
| 1869 | 
                        + monkeypatch=monkeypatch,  | 
                    |
| 1870 | 
                        + runner=runner,  | 
                    |
| 1871 | 
                        + vault_config=start_config,  | 
                    |
| 1866 | 1872 | 
                        ):  | 
                    
| 1867 | 1873 | 
                        _result = runner.invoke(  | 
                    
| 1868 | 1874 | 
                        cli.derivepassphrase_vault,  | 
                    
| ... | ... | 
                      @@ -2424,7 +2430,7 @@ class ConfigManagementStateMachine(stateful.RuleBasedStateMachine):  | 
                  
| 2424 | 2430 | 
                        tests.isolated_vault_config(  | 
                    
| 2425 | 2431 | 
                        monkeypatch=self.monkeypatch,  | 
                    
| 2426 | 2432 | 
                        runner=self.runner,  | 
                    
| 2427 | 
                        -                config={'services': {}},
                       | 
                    |
| 2433 | 
                        +                vault_config={'services': {}},
                       | 
                    |
| 2428 | 2434 | 
                        )  | 
                    
| 2429 | 2435 | 
                        )  | 
                    
| 2430 | 2436 | 
                         | 
                    
| 2431 | 2437 |