Irregularities in populating new dict from a source dict with a narrower key type
Dieses Issue hat noch niemand übernommen.
- Vorherrschende Sprache
- Python
- Sterne
- 20.6k
- Forks
- 3.3k
- PR-Merge-Kennzahlen
- PR-Kennzahlen ausstehend
Beschreibung
Bug Report
When creating or updating a new dict with items from another dict with a narrower key type, Mypy complains about some methods which are actually perfectly valid.
To Reproduce
from typing import TypeVar
_KT = TypeVar("_KT")
_VT = TypeVar("_VT")
def clone_dict(source_dict: dict[_KT, _VT]) -> dict[_KT, _VT]:
return dict(source_dict)
def test(source_dict: dict[str, None]) -> list[dict[str | int, None]]:
a: dict[str | int, None] = {"a": None, "b": None}
b: dict[str | int, None] = {k: v for k, v in source_dict.items()}
c: dict[str | int, None] = {k: None for k in source_dict.keys()}
d: dict[str | int, None] = {**source_dict}
e: dict[str | int, None] = dict(source_dict.items())
f: dict[str | int, None] = dict(source_dict)
g: dict[str | int, None] = source_dict.copy()
h: dict[str | int, None] = clone_dict(source_dict)
i: dict[str | int, None] = {}
i.update(source_dict.items())
j: dict[str | int, None] = {}
j.update(source_dict)
return [a, b, c, d, e, f, g, h, i, j]
Expected Behavior
The above code shows 10 ways of creating the new dictionary:
adoesn't actually clone the source dict; it just creates a new dict from a dict literal that just happens to only contain string keysbcopies the source dict using a dict comprehension and.items()cuses a dict comprehension and.keys()(possible in this case because the dict value type isNone, which is a singleton)duses a dict comprehension and star-star-expansioneuses thedict()constructor and.items()fuses thedict()constructor and the source dict directlyguses the built-in.copy()methodhuses a user-created dict copying methodicreates an empty dict and then.update()s it using.items()jcreates an empty dict and then.update()s it using the source dict directly
Of these:
- Mypy should complain about
h; Mypy has no way of knowing that the function in question is returning a fresh dict which is used nowhere else (and thus can safely be implicitly cast to a wider type) - Mypy might complain about
g; since that is a built-in method, it could be special-cased into Mypy that this is safe, but absent such a special case it would have the same semantics asclone_dict() - All the other option are safe and should not raise any error
Actual Behavior
Mypy correctly warns about g and h, but also issues spurious warnings about d, f, and j:
$ mypy test.py
test.py:15: error: Unpacked dict entry 0 has incompatible type "dict[str, None]"; expected "SupportsKeysAndGetItem[str | int, None]" [dict-item]
test.py:17: error: Incompatible types in assignment (expression has type "dict[str, None]", variable has type "dict[str | int, None]") [assignment]
test.py:18: error: Incompatible types in assignment (expression has type "dict[str, None]", variable has type "dict[str | int, None]") [assignment]
test.py:19: error: Argument 1 to "clone_dict" has incompatible type "dict[str, None]"; expected "dict[str | int, None]" [arg-type]
test.py:23: error: Argument 1 to "update" of "MutableMapping" has incompatible type "dict[str, None]"; expected "SupportsKeysAndGetItem[str | int, None]" [arg-type]
Found 5 errors in 1 file (checked 1 source file)
Your Environment
- Mypy version used:
mypy 1.5.1 (compiled: yes) - Mypy command-line flags: None
- Mypy configuration options from
mypy.ini(and other config files): None - Python version used:
Python 3.11.2
Beitragsleitfaden
Erste Schritte
- Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
- Forke das Repository und arbeite in einem Branch.
- Öffne einen Pull Request, der die Issue-Nummer nennt.
Rechercherichtung
Beginne damit, den bereitgestellten Reproducer mit mypy auszuführen, und vergleiche die Diagnosen für das Entpacken von dict, den dict-Konstruktor und update(). Verfolge anschließend die Pfade der Typprüfung für diese Operationen und bestätige dann, dass die sicheren Fälle keine Fehler mehr melden, während die Fälle mit clone_dict() und copy() ihr erwartetes Verhalten beibehalten.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- python
- Bereich
- devtools
- Issue-Typ
- Bug
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Aktivitätsstatus
- Veraltet
- Klarheit
- Größtenteils klar
- Anfängerfreundlichkeit
- 45/100