From 60d8a8bc64d682994588f37a796f10f0ad74c1c8 Mon Sep 17 00:00:00 2001 From: andraxin Date: Sat, 21 Feb 2026 17:46:12 +0100 Subject: [PATCH 1/9] Use logged in user's username by default, when none specified --- keyring/cli.py | 1 + 1 file changed, 1 insertion(+) diff --git a/keyring/cli.py b/keyring/cli.py index 2c0ba4d3..baebda2c 100644 --- a/keyring/cli.py +++ b/keyring/cli.py @@ -94,6 +94,7 @@ def __init__(self): self.parser.add_argument( 'username', nargs="?", + default=getpass.getuser(), ) completion.install(self.parser) From 20a6810553f4c39d1630cfeb4f8a0dc3e9016d00 Mon Sep 17 00:00:00 2001 From: andraxin Date: Sat, 21 Feb 2026 18:51:38 +0100 Subject: [PATCH 2/9] Handle case when no user can be identified --- keyring/cli.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/keyring/cli.py b/keyring/cli.py index baebda2c..acee9308 100644 --- a/keyring/cli.py +++ b/keyring/cli.py @@ -91,10 +91,14 @@ def __init__(self): 'service', nargs="?", ) + try: + default_user = getpass.getuser() + except OSError: + default_user = None self.parser.add_argument( 'username', nargs="?", - default=getpass.getuser(), + default=default_user, ) completion.install(self.parser) From 388c6f19a4ea6d5785714bb22bc5ba87aafaa1d6 Mon Sep 17 00:00:00 2001 From: andraxin Date: Sat, 21 Feb 2026 19:17:21 +0100 Subject: [PATCH 3/9] Cater for undocumented exception from `getpass.getuser` --- keyring/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keyring/cli.py b/keyring/cli.py index acee9308..1503f85a 100644 --- a/keyring/cli.py +++ b/keyring/cli.py @@ -93,7 +93,7 @@ def __init__(self): ) try: default_user = getpass.getuser() - except OSError: + except OSError, ModuleNotFoundError: default_user = None self.parser.add_argument( 'username', From aba71e2f156cf0f500894cd1f1ebef0d333379b4 Mon Sep 17 00:00:00 2001 From: andraxin Date: Sat, 21 Feb 2026 20:31:38 +0100 Subject: [PATCH 4/9] Fix multi-exception oversight --- keyring/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keyring/cli.py b/keyring/cli.py index 1503f85a..1867537f 100644 --- a/keyring/cli.py +++ b/keyring/cli.py @@ -93,7 +93,7 @@ def __init__(self): ) try: default_user = getpass.getuser() - except OSError, ModuleNotFoundError: + except (OSError, ModuleNotFoundError): default_user = None self.parser.add_argument( 'username', From 0c57062b148645ecf46bfd42a82d19d87bdab914 Mon Sep 17 00:00:00 2001 From: andraxin Date: Sat, 21 Feb 2026 19:08:50 +0100 Subject: [PATCH 5/9] Reuse `librt` "fix" from `jaraco/skeleton@8f3d95e --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 18bb6eb0..f1e431fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -79,6 +79,9 @@ type = [ # upstream "pytest-mypy >= 1.0.1", + ## workaround for python/mypy#20454 + "mypy < 1.19; python_implementation == 'PyPy'", + # local "pygobject-stubs", "shtab", # Optional install for completion From 29f1f72f73b2343a1b044083c9a8013cd66564f8 Mon Sep 17 00:00:00 2001 From: andraxin Date: Sat, 21 Feb 2026 21:24:23 +0100 Subject: [PATCH 6/9] Replace deprecated `diffcov` flag --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 14243051..e05a3d4a 100644 --- a/tox.ini +++ b/tox.ini @@ -20,7 +20,7 @@ deps = diff-cover commands = pytest {posargs} --cov-report xml - diff-cover coverage.xml --compare-branch=origin/main --html-report diffcov.html + diff-cover coverage.xml --compare-branch=origin/main --format html:diffcov.html diff-cover coverage.xml --compare-branch=origin/main --fail-under=100 [testenv:docs] From f3c7bb7630079d74d31ebf4fc2124fdb968364a5 Mon Sep 17 00:00:00 2001 From: andraxin Date: Sat, 21 Feb 2026 22:12:54 +0100 Subject: [PATCH 7/9] Add test for `No username set` --- tests/test_cli.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/test_cli.py b/tests/test_cli.py index a4bc49c7..c92def07 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,5 +1,6 @@ import getpass import itertools +import os import sys from unittest import mock @@ -42,6 +43,19 @@ def mocked_get_credential(): yield get_credential +def test_set_no_user(monkeypatch, mocked_set): + for name in ('LOGNAME', 'USER', 'LNAME', 'USERNAME'): + monkeypatch.delitem(os.environ, name, raising=False) + monkeypatch.setattr(os, 'getuid', lambda: -1) + tool = cli.CommandLineTool() + tool.service = 'svc' + tool.username = 'usr' + monkeypatch.setattr(sys.stdin, 'isatty', lambda: True) + monkeypatch.setattr(getpass, 'getpass', PasswordEmitter('foo123')) + tool.do_set() + mocked_set.assert_called_once_with('svc', 'usr', 'foo123') + + def test_set_interactive(monkeypatch, mocked_set): tool = cli.CommandLineTool() tool.service = 'svc' From 217725de86976a207b8a03b33940f1f407ccf70c Mon Sep 17 00:00:00 2001 From: andraxin Date: Sun, 22 Feb 2026 09:19:59 +0100 Subject: [PATCH 8/9] Catch `KeyError` on user lookup --- keyring/cli.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/keyring/cli.py b/keyring/cli.py index 1867537f..4ea346de 100644 --- a/keyring/cli.py +++ b/keyring/cli.py @@ -93,7 +93,8 @@ def __init__(self): ) try: default_user = getpass.getuser() - except (OSError, ModuleNotFoundError): + except (OSError, KeyError, ModuleNotFoundError): + # on recent Python versions (>=3.13), `OSError` should suffice default_user = None self.parser.add_argument( 'username', From 87b7e39f7d60a3bc8a158a02b8d94bd5ce0ec971 Mon Sep 17 00:00:00 2001 From: andraxin Date: Sun, 1 Mar 2026 16:27:02 +0100 Subject: [PATCH 9/9] Avoid raising exception when `os.getuid` does not exist (e.g. on Windows) --- tests/test_cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index c92def07..15287c49 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -46,7 +46,7 @@ def mocked_get_credential(): def test_set_no_user(monkeypatch, mocked_set): for name in ('LOGNAME', 'USER', 'LNAME', 'USERNAME'): monkeypatch.delitem(os.environ, name, raising=False) - monkeypatch.setattr(os, 'getuid', lambda: -1) + monkeypatch.setattr(os, 'getuid', lambda: -1, raising=False) tool = cli.CommandLineTool() tool.service = 'svc' tool.username = 'usr'