Marco Ricci commited on 2024-07-19 18:58:12
              Zeige 8 geänderte Dateien mit 44 Einfügungen und 29 Löschungen.
            
(One of them actually was a bug: an f-string missing the leading `f`.)
| ... | ... | 
                      @@ -81,7 +81,6 @@ detached = false  | 
                  
| 81 | 81 | 
                        [tool.hatch.envs.types]  | 
                    
| 82 | 82 | 
                        extra-dependencies = [  | 
                    
| 83 | 83 | 
                        "mypy>=1.0.0",  | 
                    
| 84 | 
                        - "pytest",  | 
                    |
| 85 | 84 | 
                        ]  | 
                    
| 86 | 85 | 
                        [tool.hatch.envs.types.scripts]  | 
                    
| 87 | 86 | 
                         check = "mypy --install-types --non-interactive {args:src/derivepassphrase tests}"
                       | 
                    
| ... | ... | 
                      @@ -115,3 +114,13 @@ exclude_also = [  | 
                  
| 115 | 114 | 
                        "raise NotImplementedError",  | 
                    
| 116 | 115 | 
                        'assert False',  | 
                    
| 117 | 116 | 
                        ]  | 
                    
| 117 | 
                        +  | 
                    |
| 118 | 
                        +[tool.ruff]  | 
                    |
| 119 | 
                        +line-length = 79  | 
                    |
| 120 | 
                        +src = ["src"]  | 
                    |
| 121 | 
                        +  | 
                    |
| 122 | 
                        +[tool.ruff.format]  | 
                    |
| 123 | 
                        +quote-style = 'single'  | 
                    |
| 124 | 
                        +  | 
                    |
| 125 | 
                        +[tool.ruff.lint.pydocstyle]  | 
                    |
| 126 | 
                        +convention = 'google'  | 
                    
| ... | ... | 
                      @@ -810,7 +810,7 @@ def derivepassphrase(  | 
                  
| 810 | 810 | 
                        with outfile:  | 
                    
| 811 | 811 | 
                        json.dump(configuration, outfile)  | 
                    
| 812 | 812 | 
                        except OSError as e:  | 
                    
| 813 | 
                        -            ctx.fail('cannot write config: {e.strerror}')
                       | 
                    |
| 813 | 
                        +            ctx.fail(f'cannot write config: {e.strerror}')
                       | 
                    |
| 814 | 814 | 
                        else:  | 
                    
| 815 | 815 | 
                        configuration = get_config()  | 
                    
| 816 | 816 | 
                        # This block could be type checked more stringently, but this  | 
                    
| ... | ... | 
                      @@ -874,7 +874,7 @@ def derivepassphrase(  | 
                  
| 874 | 874 | 
                        _save_config(configuration)  | 
                    
| 875 | 875 | 
                        else:  | 
                    
| 876 | 876 | 
                        if not service:  | 
                    
| 877 | 
                        - raise click.UsageError(f'SERVICE is required')  | 
                    |
| 877 | 
                        +                raise click.UsageError('SERVICE is required')
                       | 
                    |
| 878 | 878 | 
                                     kwargs: dict[str, Any] = {k: v for k, v in settings.items()
                       | 
                    
| 879 | 879 | 
                        if k in service_keys and v is not None}  | 
                    
| 880 | 880 | 
                        # If either --key or --phrase are given, use that setting.  | 
                    
| ... | ... | 
                      @@ -885,7 +885,10 @@ def derivepassphrase(  | 
                  
| 885 | 885 | 
                        # these above cases, set the phrase via  | 
                    
| 886 | 886 | 
                        # derivepassphrase.Vault.phrase_from_key if a key is  | 
                    
| 887 | 887 | 
                        # given. Finally, if nothing is set, error out.  | 
                    
| 888 | 
                        - key_to_phrase = lambda key: dpp.Vault.phrase_from_key(  | 
                    |
| 888 | 
                        + def key_to_phrase(  | 
                    |
| 889 | 
                        + key: str | bytes | bytearray  | 
                    |
| 890 | 
                        + ) -> bytes | bytearray:  | 
                    |
| 891 | 
                        + return dpp.Vault.phrase_from_key(  | 
                    |
| 889 | 892 | 
                        base64.standard_b64decode(key))  | 
                    
| 890 | 893 | 
                        if use_key or use_phrase:  | 
                    
| 891 | 894 | 
                        if use_key:  | 
                    
| ... | ... | 
                      @@ -157,7 +157,8 @@ class SSHAgentClient:  | 
                  
| 157 | 157 | 
                        Examples:  | 
                    
| 158 | 158 | 
                        >>> bytes(SSHAgentClient.unstring(b'\x00\x00\x00\x07ssh-rsa'))  | 
                    
| 159 | 159 | 
                        b'ssh-rsa'  | 
                    
| 160 | 
                        - >>> bytes(SSHAgentClient.unstring(SSHAgentClient.string(b'ssh-ed25519')))  | 
                    |
| 160 | 
                        + >>> bytes(SSHAgentClient.unstring(  | 
                    |
| 161 | 
                        + ... SSHAgentClient.string(b'ssh-ed25519')))  | 
                    |
| 161 | 162 | 
                        b'ssh-ed25519'  | 
                    
| 162 | 163 | 
                         | 
                    
| 163 | 164 | 
                        """  | 
                    
| ... | ... | 
                      @@ -41,7 +41,7 @@ idwcakUGCekJD/vCEml2AAAAG3Rlc3Qga2V5IHdpdGhvdXQgcGFzc3BocmFzZQEC  | 
                  
| 41 | 41 | 
                        -----END OPENSSH PRIVATE KEY-----  | 
                    
| 42 | 42 | 
                        ''',  | 
                    
| 43 | 43 | 
                        'public_key': rb'''ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIF4gWgm1gJIXw//Mkhv5MEwidwcakUGCekJD/vCEml2 test key without passphrase  | 
                    
| 44 | 
                        -''',  | 
                    |
| 44 | 
                        +''', # noqa: E501  | 
                    |
| 45 | 45 | 
                                 'public_key_data': bytes.fromhex('''
                       | 
                    
| 46 | 46 | 
                        00 00 00 0b 73 73 68 2d 65 64 32 35 35 31 39  | 
                    
| 47 | 47 | 
                        00 00 00 20  | 
                    
| ... | ... | 
                      @@ -56,7 +56,7 @@ idwcakUGCekJD/vCEml2AAAAG3Rlc3Qga2V5IHdpdGhvdXQgcGFzc3BocmFzZQEC  | 
                  
| 56 | 56 | 
                        0d 08 1f ec f8 73 9b 8c 5f 55 39 16 7c 53 54 2c  | 
                    
| 57 | 57 | 
                        1e 52 bb 30 ed 7f 89 e2 2f 69 51 55 d8 9e a6 02  | 
                    
| 58 | 58 | 
                        '''),  | 
                    
| 59 | 
                        - 'derived_passphrase': rb'8JgZgGwal9UmA27M42WPhmYHExkTCSEzM/nkNlMdr/0NCB/s+HObjF9VORZ8U1QsHlK7MO1/ieIvaVFV2J6mAg==',  | 
                    |
| 59 | 
                        + 'derived_passphrase': rb'8JgZgGwal9UmA27M42WPhmYHExkTCSEzM/nkNlMdr/0NCB/s+HObjF9VORZ8U1QsHlK7MO1/ieIvaVFV2J6mAg==', # noqa: E501  | 
                    |
| 60 | 60 | 
                        },  | 
                    
| 61 | 61 | 
                        # Currently only supported by PuTTY (which is deficient in other  | 
                    
| 62 | 62 | 
                        # niceties of the SSH agent and the agent's client).  | 
                    
| ... | ... | 
                      @@ -73,7 +73,7 @@ dGhvdXQgcGFzc3BocmFzZQECAwQFBgcICQ==  | 
                  
| 73 | 73 | 
                        -----END OPENSSH PRIVATE KEY-----  | 
                    
| 74 | 74 | 
                        ''',  | 
                    
| 75 | 75 | 
                        'public_key': rb'''ssh-ed448 AAAACXNzaC1lZDQ0OAAAADni9nLTT1a7zATGO8RveGq0vPUY7/53m+YZRsStZDgBQ72ZgtPMckdzabiz7JbM/b0JzcRzGLMsbwA= test key without passphrase  | 
                    
| 76 | 
                        -''',  | 
                    |
| 76 | 
                        +''', # noqa: E501  | 
                    |
| 77 | 77 | 
                                 'public_key_data': bytes.fromhex('''
                       | 
                    
| 78 | 78 | 
                        00 00 00 09 73 73 68 2d 65 64 34 34 38  | 
                    
| 79 | 79 | 
                        00 00 00 39  | 
                    
| ... | ... | 
                      @@ -94,7 +94,7 @@ dGhvdXQgcGFzc3BocmFzZQECAwQFBgcICQ==  | 
                  
| 94 | 94 | 
                        db bd 77 7c 80 20 7f 3a 48 61 f6 1f ae a9 5e 53  | 
                    
| 95 | 95 | 
                        7b e0 9d 93 1e ea dc eb b5 cd 56 4c ea 8f 08 00  | 
                    
| 96 | 96 | 
                        '''),  | 
                    
| 97 | 
                        - 'derived_passphrase': rb'Bob0ZKSmutnDIsSTSZn8Ed5nlwjy2Lc8LBPnxRwekqYO2C9tgQOCAONy5DJtctJtMoQ/zKkeVywAmrOZ3kXazi7R2+WJ8zW+JFiQxsoE8NuIgNu9d3yAIH86SGH2H66pXlN74J2THurc67XNVkzqjwgA',  | 
                    |
| 97 | 
                        + 'derived_passphrase': rb'Bob0ZKSmutnDIsSTSZn8Ed5nlwjy2Lc8LBPnxRwekqYO2C9tgQOCAONy5DJtctJtMoQ/zKkeVywAmrOZ3kXazi7R2+WJ8zW+JFiQxsoE8NuIgNu9d3yAIH86SGH2H66pXlN74J2THurc67XNVkzqjwgA', # noqa: E501  | 
                    |
| 98 | 98 | 
                        },  | 
                    
| 99 | 99 | 
                             'rsa': {
                       | 
                    
| 100 | 100 | 
                        'private_key': rb'''-----BEGIN OPENSSH PRIVATE KEY-----  | 
                    
| ... | ... | 
                      @@ -137,7 +137,7 @@ Bgp6142WnSCQAAABt0ZXN0IGtleSB3aXRob3V0IHBhc3NwaHJhc2UB  | 
                  
| 137 | 137 | 
                        -----END OPENSSH PRIVATE KEY-----  | 
                    
| 138 | 138 | 
                        ''',  | 
                    
| 139 | 139 | 
                        'public_key': rb'''ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCxoe7pezhxWy4NI0mUwKqg9WCYOAS+IjxN9eYcqpfcmQiojcuy9XsiN/xYJ1O94SrsKS5mEia2xHnYA4RUChTyYNcM2v6cnnBQ/N/VQhpGMN7SVxdbhKUXTWFCwbjBgO6rGyHB6WtoH8vd7TOEPt+NgcXwhsWyoaUUdYTA62V+GF9vEmxMaC4ubgDz+B0QkPnauSoNxmkhcIe0lsLNb1pClZyz88PDnKXCX/d0HuN/HJ+sbPg7dCvOyqFYSyKn3uY6bCXqoIdurxXzH3O7z0P8f5sbmKOrGGKNuNxVRbeVl/D/3uDL0nqsbfUc1qvkfwbJwtMXC4IV6kOZMSk2BAsqh7x48gQ+rhYeEVSi8F3CWs4HJQoqrGt7K9a3mCSlMBHP70u3w6ME7eumoryxlUofewTd17ZEkzdX08l2ZlKzZvwQUrc+xQZ2Uw8z2mfW6Ti4gi0pYGaig7Ke4PwuXpo/C5YAWfeXycsvJZ2uaYRjMdZeJGNAnHLUGLkBscw5aI8= test key without passphrase  | 
                    
| 140 | 
                        -''',  | 
                    |
| 140 | 
                        +''', # noqa: E501  | 
                    |
| 141 | 141 | 
                                 'public_key_data': bytes.fromhex('''
                       | 
                    
| 142 | 142 | 
                        00 00 00 07 73 73 68 2d 72 73 61  | 
                    
| 143 | 143 | 
                        00 00 00 03 01 00 01  | 
                    
| ... | ... | 
                      @@ -195,7 +195,7 @@ Bgp6142WnSCQAAABt0ZXN0IGtleSB3aXRob3V0IHBhc3NwaHJhc2UB  | 
                  
| 195 | 195 | 
                        de 69 2c 48 62 d9 fd d1 9b 6b b0 49 db d3 ff 38  | 
                    
| 196 | 196 | 
                        e7 10 d9 2d ce 9f 0d 5e 09 7b 37 d2 7b c3 bf ce  | 
                    
| 197 | 197 | 
                        '''),  | 
                    
| 198 | 
                        - 'derived_passphrase': rb'ohB8Lva7U6h0KqEZma2Bvnmc7dadCU5uxRhIM5B3mWj3ngNazU4Y64l9haLurkqS9m/Ouf6GfyprMdpuGv6ipYi4RH+hdnOz7HW10Ka5FZdlCRN9lCHR+10PiyMEd8LDVSKxoAmK9Tgq1n8bhymgJdMlb8tkYQeY3BTFhPiSJF5QEWtJ5fDMKcspqRnYp3EfkQsFsQFLwl8ApbYhv/gsnWebRzsKSWt5Lfwd7Ayw5Sci1an408P530ho6fvvPNwmv8/qKUMBpuPFUZX0Zm2KVeJH7OgwRUyuR+fJpCGLZLq2iPYh+HO5yxGheHWSxlrlZP7tQtmVmeYrbzwWPCh0pHIvDT8sM2eqNRmO57URL7P3asUC4m+jQuNiGZkD6qUg56HjvMgGo7V81nZd329gRoMqCADW09mkwUGM+GBWRYHaO6IWH55OdYMX2sNTwz4ZpBu80im4eGEreOaxUrDV7N5pLEhi2f3Rm2uwSdvT/zjnENktzp8NXgl7N9J7w7/O',  | 
                    |
| 198 | 
                        + 'derived_passphrase': rb'ohB8Lva7U6h0KqEZma2Bvnmc7dadCU5uxRhIM5B3mWj3ngNazU4Y64l9haLurkqS9m/Ouf6GfyprMdpuGv6ipYi4RH+hdnOz7HW10Ka5FZdlCRN9lCHR+10PiyMEd8LDVSKxoAmK9Tgq1n8bhymgJdMlb8tkYQeY3BTFhPiSJF5QEWtJ5fDMKcspqRnYp3EfkQsFsQFLwl8ApbYhv/gsnWebRzsKSWt5Lfwd7Ayw5Sci1an408P530ho6fvvPNwmv8/qKUMBpuPFUZX0Zm2KVeJH7OgwRUyuR+fJpCGLZLq2iPYh+HO5yxGheHWSxlrlZP7tQtmVmeYrbzwWPCh0pHIvDT8sM2eqNRmO57URL7P3asUC4m+jQuNiGZkD6qUg56HjvMgGo7V81nZd329gRoMqCADW09mkwUGM+GBWRYHaO6IWH55OdYMX2sNTwz4ZpBu80im4eGEreOaxUrDV7N5pLEhi2f3Rm2uwSdvT/zjnENktzp8NXgl7N9J7w7/O', # noqa: E501  | 
                    |
| 199 | 199 | 
                        },  | 
                    
| 200 | 200 | 
                        }  | 
                    
| 201 | 201 | 
                         | 
                    
| ... | ... | 
                      @@ -224,7 +224,7 @@ u7HfrQhdOiKSa+ZO9AAojbURqrLDRfBJa5dXn2AAAAFQDJHfenj4EJ9WkehpdJatPBlqCW  | 
                  
| 224 | 224 | 
                        -----END OPENSSH PRIVATE KEY-----  | 
                    
| 225 | 225 | 
                        ''',  | 
                    
| 226 | 226 | 
                        'public_key': rb'''ssh-dss AAAAB3NzaC1kc3MAAACBALsoBleoEY1UsFA+twxgCg1bngGEPxoiF7sENZjMlyxoy3vZUpKSC5nz5dHudFrQL9mwGL64mnR2nHL1kxM5Zfi7lg8x5BxcR0YTFkh+KYapI4CzLp8KV3Yh8lklkTFwKaF71KyOx3dhIA8lGW45cVBz3kxmhHmEzCUgMPxDOsTtAAAAFQD32c5k6B3tocxUahelQQFyfseiywAAAIAuvYCDeHEzesp3HNVTDx9fRVU9c77f4qvyEZ7Qpz/s3BVoFUvUZDx96cG5bKekBRsfTCjeHXCQH/yFfqn5Lxye7msgGVS5U3AvD9shiiEr3wt+pNgr9X6DooP7ybfjC8SJdmarLBjnifZuSxyHU2q+P+02kvMTFLH9dLSRIzVqKAAAAIBtA1E9xUS4YOsRx/7GDm2AB6M9cE9ev8myz4KGTriSbeaKsxiMBbJZi1VyBP7uE5jG1hGKfwvIwuopGaprRDlSu8N8KGAuG+wb1hJv8ynDmqbw+IdJp/CGRrP+17f7yEqiCqh7ux360IXToikmvmTvQAKI21Eaqyw0XwSWuXV59g== test key without passphrase  | 
                    
| 227 | 
                        -''',  | 
                    |
| 227 | 
                        +''', # noqa: E501  | 
                    |
| 228 | 228 | 
                                 'public_key_data': bytes.fromhex('''
                       | 
                    
| 229 | 229 | 
                        00 00 00 07 73 73 68 2d 64 73 73  | 
                    
| 230 | 230 | 
                        00 00 00 81 00  | 
                    
| ... | ... | 
                      @@ -272,7 +272,7 @@ dGhvdXQgcGFzc3BocmFzZQECAwQ=  | 
                  
| 272 | 272 | 
                        -----END OPENSSH PRIVATE KEY-----  | 
                    
| 273 | 273 | 
                        ''',  | 
                    
| 274 | 274 | 
                        'public_key': rb'''ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMttTTMPCyTYO+n5Vgiuw1V/mBbDPZLdJnxNvGJBGSmcZJWrIigck4lz41Ai0BrvGUn/xnqB/PntndqlSRowmbo= test key without passphrase  | 
                    
| 275 | 
                        -''',  | 
                    |
| 275 | 
                        +''', # noqa: E501  | 
                    |
| 276 | 276 | 
                                 'public_key_data': bytes.fromhex('''
                       | 
                    
| 277 | 277 | 
                        00 00 00 13 65 63 64 73 61 2d 73 68 61 32 2d 6e  | 
                    
| 278 | 278 | 
                        69 73 74 70 32 35 36  | 
                    
| ... | ... | 
                      @@ -299,7 +299,7 @@ JAu0J3Q+cypZuKQVAAAAMQD5sTy8p+B1cn/DhOmXquui1BcxvASqzzevkBlbQoBa73y04B  | 
                  
| 299 | 299 | 
                        -----END OPENSSH PRIVATE KEY-----  | 
                    
| 300 | 300 | 
                        ''',  | 
                    
| 301 | 301 | 
                        'public_key': rb'''ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBKCQ6OQC+ru/m8e6PcoEvj8QBZyfmFkPIpxvJXR4EwYWruEpdCVmohqEtWp4xHRCqaTE0nauXLZUdxed6re9n718ixYI51iTlY/c1k/O/3XVefvBsSQLtCd0PnMqWbikFQ== test key without passphrase  | 
                    
| 302 | 
                        -''',  | 
                    |
| 302 | 
                        +''', # noqa: E501  | 
                    |
| 303 | 303 | 
                                 'public_key_data': bytes.fromhex('''
                       | 
                    
| 304 | 304 | 
                        00 00 00 13  | 
                    
| 305 | 305 | 
                        65 63 64 73 61 2d 73 68 61 32 2d 6e 69 73 74 70  | 
                    
| ... | ... | 
                      @@ -335,7 +335,7 @@ DUMMY_PHRASE_FROM_KEY1_RAW = (  | 
                  
| 335 | 335 | 
                        b'\x1d\xaf\xfd\r\x08\x1f\xec\xf8s\x9b\x8c_U9\x16|ST,'  | 
                    
| 336 | 336 | 
                        b'\x1eR\xbb0\xed\x7f\x89\xe2/iQU\xd8\x9e\xa6\x02'  | 
                    
| 337 | 337 | 
                        )  | 
                    
| 338 | 
                        -DUMMY_PHRASE_FROM_KEY1 = b'8JgZgGwal9UmA27M42WPhmYHExkTCSEzM/nkNlMdr/0NCB/s+HObjF9VORZ8U1QsHlK7MO1/ieIvaVFV2J6mAg=='  | 
                    |
| 338 | 
                        +DUMMY_PHRASE_FROM_KEY1 = b'8JgZgGwal9UmA27M42WPhmYHExkTCSEzM/nkNlMdr/0NCB/s+HObjF9VORZ8U1QsHlK7MO1/ieIvaVFV2J6mAg==' # noqa: E501  | 
                    |
| 339 | 339 | 
                         | 
                    
| 340 | 340 | 
                        skip_if_no_agent = pytest.mark.skipif(  | 
                    
| 341 | 341 | 
                             not os.environ.get('SSH_AUTH_SOCK'), reason='running SSH agent required')
                       | 
                    
| ... | ... | 
                      @@ -137,11 +137,10 @@ class TestVault:  | 
                  
| 137 | 137 | 
                        assert v._estimate_sufficient_hash_length(8.0) >= entropy  | 
                    
| 138 | 138 | 
                         | 
                    
| 139 | 139 | 
                        def test_222_hash_length_estimation(self) -> None:  | 
                    
| 140 | 
                        - v = Vault(phrase=self.phrase)  | 
                    |
| 141 | 
                        - v2 = Vault(phrase=self.phrase, lower=0, upper=0, number=0,  | 
                    |
| 140 | 
                        + v = Vault(phrase=self.phrase, lower=0, upper=0, number=0,  | 
                    |
| 142 | 141 | 
                        symbol=0, space=1, length=1)  | 
                    
| 143 | 
                        - assert v2._entropy() == 0.0  | 
                    |
| 144 | 
                        - assert v2._estimate_sufficient_hash_length() > 0  | 
                    |
| 142 | 
                        + assert v._entropy() == 0.0  | 
                    |
| 143 | 
                        + assert v._estimate_sufficient_hash_length() > 0  | 
                    |
| 145 | 144 | 
                         | 
                    
| 146 | 145 | 
                        @pytest.mark.parametrize(['service', 'expected'], [  | 
                    
| 147 | 146 | 
                        (b'google', google_phrase),  | 
                    
| ... | ... | 
                      @@ -763,7 +763,7 @@ Our menu:  | 
                  
| 763 | 763 | 
                        Your selection? (1-10, leave empty to abort): 9  | 
                    
| 764 | 764 | 
                        A fine choice: Spam, spam, spam, spam, spam, spam, baked beans, spam, spam, spam and spam  | 
                    
| 765 | 765 | 
                        (Note: Vikings strictly optional.)  | 
                    
| 766 | 
                        -''', 'driver program produced unexpected output'  | 
                    |
| 766 | 
                        +''', 'driver program produced unexpected output' # noqa: E501  | 
                    |
| 767 | 767 | 
                        result = runner.invoke(driver, ['--heading='], input='',  | 
                    
| 768 | 768 | 
                        catch_exceptions=True)  | 
                    
| 769 | 769 | 
                        assert result.exit_code > 0, 'driver program succeeded?!'  | 
                    
| ... | ... | 
                      @@ -778,7 +778,7 @@ A fine choice: Spam, spam, spam, spam, spam, spam, baked beans, spam, spam, spam  | 
                  
| 778 | 778 | 
                        [8] Spam, spam, spam, egg and spam  | 
                    
| 779 | 779 | 
                        [9] Spam, spam, spam, spam, spam, spam, baked beans, spam, spam, spam and spam  | 
                    
| 780 | 780 | 
                        [10] Lobster thermidor aux crevettes with a mornay sauce garnished with truffle paté, brandy and a fried egg on top and spam  | 
                    
| 781 | 
                        -Your selection? (1-10, leave empty to abort): \n''', (  | 
                    |
| 781 | 
                        +Your selection? (1-10, leave empty to abort): \n''', ( # noqa: E501  | 
                    |
| 782 | 782 | 
                        'driver program produced unexpected output'  | 
                    
| 783 | 783 | 
                        )  | 
                    
| 784 | 784 | 
                        assert isinstance(result.exception, IndexError), (  | 
                    
| ... | ... | 
                      @@ -800,7 +800,8 @@ Your selection? (1-10, leave empty to abort): \n''', (  | 
                  
| 800 | 800 | 
                        else:  | 
                    
| 801 | 801 | 
                                         click.echo('Great!')
                       | 
                    
| 802 | 802 | 
                        runner = click.testing.CliRunner(mix_stderr=True)  | 
                    
| 803 | 
                        - result = runner.invoke(driver, ['Will replace with spam. Confirm, y/n?'],  | 
                    |
| 803 | 
                        + result = runner.invoke(driver,  | 
                    |
| 804 | 
                        + ['Will replace with spam. Confirm, y/n?'],  | 
                    |
| 804 | 805 | 
                        input='y')  | 
                    
| 805 | 806 | 
                        assert result.exit_code == 0, 'driver program failed'  | 
                    
| 806 | 807 | 
                        assert result.stdout == '''\  | 
                    
| ... | ... | 
                      @@ -825,7 +826,8 @@ Boo.  | 
                  
| 825 | 826 | 
                         | 
                    
| 826 | 827 | 
                        def test_103_prompt_for_passphrase(self, monkeypatch: Any) -> None:  | 
                    
| 827 | 828 | 
                        monkeypatch.setattr(click, 'prompt',  | 
                    
| 828 | 
                        -                            lambda *a, **kw: json.dumps({'args': a, 'kwargs': kw}))
                       | 
                    |
| 829 | 
                        + lambda *a, **kw:  | 
                    |
| 830 | 
                        +                                json.dumps({'args': a, 'kwargs': kw}))
                       | 
                    |
| 829 | 831 | 
                        res = json.loads(cli._prompt_for_passphrase())  | 
                    
| 830 | 832 | 
                        assert 'args' in res and 'kwargs' in res, (  | 
                    
| 831 | 833 | 
                        'missing arguments to passphrase prompt'  | 
                    
| ... | ... | 
                      @@ -919,4 +921,4 @@ Boo.  | 
                  
| 919 | 921 | 
                        except Exception as e: # pragma: no cover  | 
                    
| 920 | 922 | 
                        exception = e  | 
                    
| 921 | 923 | 
                        finally:  | 
                    
| 922 | 
                        - assert exception == None, 'exception querying suitable SSH keys'  | 
                    |
| 924 | 
                        + assert exception is None, 'exception querying suitable SSH keys'  | 
                    
| ... | ... | 
                      @@ -88,7 +88,8 @@ class TestSequin:  | 
                  
| 88 | 88 | 
                         | 
                    
| 89 | 89 | 
                        def test_211_shifting(self):  | 
                    
| 90 | 90 | 
                        seq = sequin.Sequin([1, 0, 1, 0, 0, 1, 0, 0, 0, 1], is_bitstring=True)  | 
                    
| 91 | 
                        -        assert seq.bases == {2: collections.deque([1, 0, 1, 0, 0, 1, 0, 0, 0, 1])}
                       | 
                    |
| 91 | 
                        +        assert seq.bases == {2: collections.deque([
                       | 
                    |
| 92 | 
                        + 1, 0, 1, 0, 0, 1, 0, 0, 0, 1])}  | 
                    |
| 92 | 93 | 
                        #  | 
                    
| 93 | 94 | 
                        assert seq._all_or_nothing_shift(3) == (1, 0, 1)  | 
                    
| 94 | 95 | 
                        assert seq._all_or_nothing_shift(3) == (0, 0, 1)  | 
                    
| ... | ... | 
                      @@ -124,7 +124,7 @@ class TestAgentInteraction:  | 
                  
| 124 | 124 | 
                        def test_200_sign_data_via_agent(self, keytype, data_dict):  | 
                    
| 125 | 125 | 
                        private_key = data_dict['private_key']  | 
                    
| 126 | 126 | 
                        try:  | 
                    
| 127 | 
                        - result = subprocess.run(['ssh-add', '-t', '30', '-q', '-'],  | 
                    |
| 127 | 
                        + _ = subprocess.run(['ssh-add', '-t', '30', '-q', '-'],  | 
                    |
| 128 | 128 | 
                        input=private_key, check=True,  | 
                    
| 129 | 129 | 
                        capture_output=True)  | 
                    
| 130 | 130 | 
                        except subprocess.CalledProcessError as e:  | 
                    
| ... | ... | 
                      @@ -161,7 +161,7 @@ class TestAgentInteraction:  | 
                  
| 161 | 161 | 
                        def test_201_sign_data_via_agent_unsupported(self, keytype, data_dict):  | 
                    
| 162 | 162 | 
                        private_key = data_dict['private_key']  | 
                    
| 163 | 163 | 
                        try:  | 
                    
| 164 | 
                        - result = subprocess.run(['ssh-add', '-t', '30', '-q', '-'],  | 
                    |
| 164 | 
                        + _ = subprocess.run(['ssh-add', '-t', '30', '-q', '-'],  | 
                    |
| 165 | 165 | 
                        input=private_key, check=True,  | 
                    
| 166 | 166 | 
                        capture_output=True)  | 
                    
| 167 | 167 | 
                        except subprocess.CalledProcessError as e: # pragma: no cover  | 
                    
| ... | ... | 
                      @@ -178,7 +178,7 @@ class TestAgentInteraction:  | 
                  
| 178 | 178 | 
                                     key_comment_pairs = {bytes(k): bytes(c)
                       | 
                    
| 179 | 179 | 
                        for k, c in client.list_keys()}  | 
                    
| 180 | 180 | 
                        public_key_data = data_dict['public_key_data']  | 
                    
| 181 | 
                        - expected_signature = data_dict['expected_signature']  | 
                    |
| 181 | 
                        + _ = data_dict['expected_signature']  | 
                    |
| 182 | 182 | 
                        if public_key_data not in key_comment_pairs: # pragma: no cover  | 
                    
| 183 | 183 | 
                                         pytest.skip('prerequisite SSH key not loaded')
                       | 
                    
| 184 | 184 | 
                        signature = bytes(client.sign(  | 
                    
| ... | ... | 
                      @@ -211,7 +211,7 @@ class TestAgentInteraction:  | 
                  
| 211 | 211 | 
                        keys = [pair.key for pair in tests.list_keys_singleton()  | 
                    
| 212 | 212 | 
                        if key_is_suitable(pair.key)]  | 
                    
| 213 | 213 | 
                        index = '1'  | 
                    
| 214 | 
                        - text = f'Use this key? yes\n'  | 
                    |
| 214 | 
                        + text = 'Use this key? yes\n'  | 
                    |
| 215 | 215 | 
                        else:  | 
                    
| 216 | 216 | 
                        monkeypatch.setattr(ssh_agent_client.SSHAgentClient,  | 
                    
| 217 | 217 | 
                        'list_keys', tests.list_keys)  | 
                    
| 218 | 218 |