From 922482adc66cb07bf0e3e0e3880fa5ecebd6f5b0 Mon Sep 17 00:00:00 2001 From: Kadir Can Ozden <101993364+bysiber@users.noreply.github.com> Date: Sun, 22 Feb 2026 00:51:30 +0300 Subject: [PATCH] Suppress PasswordDeleteError in ChainerBackend.delete_password When deleting a password through ChainerBackend, if the highest-priority backend raises PasswordDeleteError (because it doesn't hold that particular password), the chainer now catches the error and continues to the next backend instead of propagating immediately. This matches the existing pattern for NotImplementedError and fixes the case where a password exists in a lower-priority backend but cannot be deleted because a higher-priority backend fails first. Fixes #697 --- commit_msg.txt | 14 ++++++++++++++ keyring/backends/chainer.py | 3 ++- tests/backends/test_chainer.py | 17 +++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 commit_msg.txt diff --git a/commit_msg.txt b/commit_msg.txt new file mode 100644 index 00000000..3bf66cc1 --- /dev/null +++ b/commit_msg.txt @@ -0,0 +1,14 @@ +Suppress PasswordDeleteError in ChainerBackend.delete_password + +When deleting a password through ChainerBackend, if the +highest-priority backend raises PasswordDeleteError (because it +doesn't hold that particular password), the chainer now catches +the error and continues to the next backend instead of +propagating immediately. + +This matches the existing pattern for NotImplementedError and +fixes the case where a password exists in a lower-priority +backend but cannot be deleted because a higher-priority backend +fails first. + +Fixes #697 diff --git a/keyring/backends/chainer.py b/keyring/backends/chainer.py index 6bc711f1..38563d7a 100644 --- a/keyring/backends/chainer.py +++ b/keyring/backends/chainer.py @@ -5,6 +5,7 @@ from .. import backend from ..compat import properties +from ..errors import PasswordDeleteError from . import fail @@ -61,7 +62,7 @@ def delete_password(self, service, username): for keyring in self.backends: try: return keyring.delete_password(service, username) - except NotImplementedError: + except (NotImplementedError, PasswordDeleteError): pass def get_credential(self, service, username): diff --git a/tests/backends/test_chainer.py b/tests/backends/test_chainer.py index 11dc6865..9b746b4b 100644 --- a/tests/backends/test_chainer.py +++ b/tests/backends/test_chainer.py @@ -2,6 +2,7 @@ import keyring.backends.chainer from keyring import backend +from keyring.errors import PasswordDeleteError @pytest.fixture @@ -16,6 +17,9 @@ def get_password(self, system, user): def set_password(self, system, user, password): pass + def delete_password(self, system, user): + pass + class Keyring2(backend.KeyringBackend): priority = 2 @@ -25,6 +29,9 @@ def get_password(self, system, user): def set_password(self, system, user, password): raise NotImplementedError() + def delete_password(self, system, user): + raise PasswordDeleteError("Password not found") + return Keyring1(), Keyring2() monkeypatch.setattr('keyring.backend.get_all_keyring', get_two) @@ -45,3 +52,13 @@ def test_chainer_defers_to_fail(self, monkeypatch): assert keyring.backend.by_priority( keyring.backends.chainer.ChainerBackend ) < keyring.backend.by_priority(keyring.backends.fail.Keyring) + + def test_chainer_delete_skips_missing(self, two_keyrings): + """ + When a higher-priority backend raises PasswordDeleteError, + the chainer should fall through to the next backend. + """ + chainer = keyring.backends.chainer.ChainerBackend() + # Should not raise even though Keyring2 (higher priority) + # raises PasswordDeleteError + chainer.delete_password('alpha', 'bravo')