-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
mypy doesn't understand set operations on optional #13192
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
The typeshed stub for the from typing import MutableSet, Generic, AbstractSet, TypeVar
_T = TypeVar("_T")
class set(MutableSet[_T], Generic[_T]):
def __sub__(self, __s: AbstractSet[_T | None]) -> set[_T]: ... I guess we could special-case from typing import MutableSet, Generic, AbstractSet, TypeVar, overload
_S = TypeVar("_S")
_T = TypeVar("_T")
class set(MutableSet[_T], Generic[_T]):
@overload
def __sub__(self: set[_S | None], __s: AbstractSet[None]) -> set[_S]: ...
@overload
def __sub__(self, __s: AbstractSet[_T | None]) -> set[_T]: ... But I'm not sure this kind of thing is common enough to be worth special-casing here. Such a change would have to happen over at the typeshed repo rather than the mypy repo, since mypy vendors its stdlib stubs from typeshed. |
Shouldn't the special casing should really be for singleton types in general, similar to the changes made here? #13117 |
Well that one is different since it's about Implementing the other singleton objects too would be tricky, since you'd need to account for all possible combinations of |
I agree with @TeamSpen210. There's no generalised way in the type system at the moment of indicating that a type is a singleton, and I don't think there would be enough use cases to make it worth the added complexity of creating a generalised way of doing that. The changes made in #13117 are highly specific to I'm closing this as I don't think there's anything actionable for mypy here. If we did want to do something in this area, the change would have to be made over at typeshed. |
Is there a suggested workaround, short of resorting to a I've tried the kitchen sink without success (mypy-play.net): from __future__ import annotations
from typing import *
base_chars: Set[Optional[str]]
chars = frozenset(base_chars - {None})
assert None not in chars
assert all(chars)
assert all(c is not None for c in chars)
assert all(isinstance(c, str) for c in chars)
reveal_type(chars) # FrozenSet[Union[str, None]] [edited to use |
Here's a straightforward workaround: def func(base_chars: set[str | None]):
chars = frozenset(x for x in base_chars if x is not None)
reveal_type(chars) # frozenset[str] |
Thanks, @erictraut! (I hadn't considered a comprehension because it wasn't verifying the type of the set, but that works 99% of the time. Where zero-copy/performance really matters, there's always If this issue is ever reconsidered, I'd like to offer a thought: perhaps an Te = TypeVar('Te', bound=bool|Enum|Ellipsis|None)
def every(type_: Type[Te], /) -> CompleteFrozenSet[Te]:
'''returns every member of an enumerable type
Args:
type_: 'None', 'bool', 'Ellipsis', or an 'Enum' type
Returns:
a frozen set containing every value of the given type
'''
# magic happens
return ...
class CompleteFrozenSet(FrozenSet[T]):
'''(empty body)'''
# (I'm not sure if this would work as written:)
class Set(...):
...
@overload
def __isub__(self: Set[A|B], other: CompleteFrozenSet[B]) -> Set[A]: ...
.... I suppose an I do realize this probably isn't a priority. |
I have tried to search for similar issues but failed because I can't find the keywords to limit the open issues to a manageable list. Sorry if it's indeed an old bug. Hope someone here can help
Bug Report
mypy doesn't seem to understand that
Set[Optional[T]]
minusNone
is justSet[T]
To Reproduce
Expected Behavior
both line 3 and line 5 should be correct
Actual Behavior
Your Environment
The text was updated successfully, but these errors were encountered: