Type checking error due to casting a subclass as `backend.KeyringBackend` base class
- Dominant language
- Python
- Stars
- 1.5k
- Forks
- 196
- PR merge metrics
- No merged PRs in 30d
Description
**Describe the bug**
I am attempting to contribute an implementation using this library to a type checked project and mypy is failing the PR.
I think the cause of the failure is that the backend returned from `keyring.get_keyring()` is typed as the wrong class. It's being cast to `keyring.backend.Backend` but it is in fact a subclass.
```py
>>> import keyring
>>> kr = keyring.get_keyring()
>>> type(kr)
>>> kr = keyring.get_keyring()
>>> type(kr)
>>> type(kr).mro()
[, , , ]
```
https://github.com/jaraco/keyring/blob/af7233958ece36da8d493509a6388484f5534dfc/keyring/core.py#L30-L34
Type checkers reject this incorrect casting whenever a method is used that's unique to a subclass, such as `get_preferred_collection()` which exists on `keyring.backends.SecretService.Keyring` but not `keyring.backend.KeyringBackend`.
```
pydantic_settings/sources.py:666: error: Incompatible types in assignment (expression has type "KeyringBackend", variable has type "Keyring") [assignment]
pydantic_settings/sources.py:676: error: Call to untyped function "get_preferred_collection" in typed context [no-untyped-call]
Found 2 errors in 1 file (checked 5 source files)
```
due to this code at line 666 :imp: :
```py
663 def _read_keyring(self, case_sensitive: bool) -> Mapping[str, str | None]:
664 keyring_backend = self.keyring_backend
665 if keyring_backend is None:
666 kr: SecretServiceKeyring = keyring.core.get_keyring()
667 else:
668 all_keyrings: Iterator[SecretServiceKeyring] = keyring.backend.get_all_keyring()
```
and further on at 676:
```py
675 keyring_vars: dict[str, str | None] = {}
676 kr_collection: SecretServiceCollection = kr.get_preferred_collection()
677 kr_items = kr_collection.get_all_items()
678 keyring_vars.update({item.get_attributes()['service']: item.get_secret().decode() for item in kr_items})
679 return keyring_vars
```
It looks tricky but maybe I can help make some progress on these type annotations, maybe the cast helps identify a place to start?
I can't figure out if there is already a mypy workflow in use in this project (is `pytest-mypy` called somewhere?)
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with keyring/core.py lines 30-34 and the backend definitions to compare the get_keyring() annotation with the concrete SecretService.Keyring usage shown in pydantic_settings/sources.py. Reproduce the reported mypy errors and determine an annotation approach that preserves valid subclass-specific use; done means the affected type checks pass without incorrect casts.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100