Use of ```not``` keyword does not always narrow out type ```None```
Dieses Issue hat noch niemand übernommen.
- Vorherrschende Sprache
- Python
- Sterne
- 20.6k
- Forks
- 3.3k
- PR-Merge-Kennzahlen
- PR-Kennzahlen ausstehend
Beschreibung
Bug Report
I wanted to make an item class with multiple descriptor-'variant' attributes (optional-strings). Any of these attr's could be null/empty, but AT LEAST ONE of them needs to be defined (i.e. a non-empty string). And I want a read-only property that should return the 'primary' descriptor-value (as string), or raise an err if no descriptors are defined.
In the simplified example below, I implemented this logic for two descriptor-variants, in two seemingly-equivalent ways, but for some reason MyPy yields an error in the first approach but not the second.
To Reproduce
from typing import Optional
class Item_V1:
descriptor_variant1: Optional[str]
descriptor_variant2: Optional[str]
# ... followed by any number of other arbitrary attr's that we don't care about.
@property
def primary_descriptor(self) -> str:
if not self.descriptor_variant1 and not self.descriptor_variant2:
raise AttributeError("At least one item-descriptor variant must be defined.")
if not self.descriptor_variant1:
return self.descriptor_variant2
return self.descriptor_variant1
class Item_V2:
descriptor_variant1: Optional[str]
descriptor_variant2: Optional[str]
@property
def primary_descriptor(self) -> str:
if not self.descriptor_variant1:
if not self.descriptor_variant2:
raise AttributeError("At least one item-descriptor variant must be defined.")
return self.descriptor_variant2
return self.descriptor_variant1
MyPy Output
I believe that the primary_descriptor property for both Item_V1 and Item_V2 should be evaluated without errors by MyPy; however, in reality, l.13 in the Item_V1 approach yields MyPy error "Incompatible return value type (got "Optional[str]", expected "str") [return-value]", whereas the equivalent return-statement for Item_V2 (l.26) is evaluated as correct.
Your Environment
- Python version used: 3.9 [but tested up to v3.12 using https://mypy-play.net/]
- Mypy version used: 1.6.1 [but tested up to v1.9.0 using https://mypy-play.net/]
- Mypy command-line flags: N/A
- Mypy configuration options from
mypy.ini(and other config files): nothing relevant
Caveats/Disclaimers
- Whilst in theory the
Item_V2method is fine for my current scenario, in an ideal world I would prefer to be able to the use theItem_V1-type sequence, as otherwise this would lead to an n-level nested if-statement, which seems like bad style, and would become unwieldy as num. descriptor variants increases. And mostly I just don't understand why there is any difference in MyPy's behaviour... - Before anyone asks something of the form: why not store the descriptor variants using an Iterable/dict? - the actual use-case here is in a Django model, where each descriptor variant needs to map to a nullable/blank-able CharField. But that's not really relevant to the error state, in any case.
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
Reproduziere die Item_V1- und Item_V2-Snippets auf mypy-play.net mit den gemeldeten Python- und mypy-Versionen und verfolge anschließend das Typverengungsverhalten bei verketteten not-Prüfungen. Als erledigt gilt die Aufgabe, wenn die Abweichung bestätigt und die erwartete Behandlung durch einen Regressionstest abgedeckt ist.
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