-
As in subject I'm having an issue that Container objects are not correctly verified just like in below example: from typing import Any, List, Container
from multimethod import multimethod
@multimethod
def validate_enum_list(key: str, value: Any, allowed_values: Any):
return NotImplemented
@validate_enum_list.register
def _(key: str, value: str, allowed_values: Container[str]) -> None:
print("key: str, value: str, allowed_values: Container[str]")
print(type(value))
print(type(allowed_values))
@validate_enum_list.register
def _(key: str, value: Container[str], allowed_values: Container[Container[str]]) -> None:
print("key: str, value: Container[str], allowed_values: Container[Container[str]]")
print(type(value))
print(type(allowed_values))
validate_enum_list("test1", "test1", ["test1"])
validate_enum_list("test2", ["test2"], [["test2"]])
validate_enum_list("test2", "test2", "test2") According to this the third call of key: str, value: str, allowed_values: Container[str]
<class 'str'>
<class 'list'>
key: str, value: Container[str], allowed_values: Container[Container[str]]
<class 'list'>
<class 'list'>
key: str, value: str, allowed_values: Container[str]
<class 'str'>
<class 'str'> I have verified that if I will add additional method to serve such case than it's all OK but I wanted to have it served with the |
Beta Was this translation helpful? Give feedback.
Replies: 8 comments 1 reply
-
It's actually working as intended; the catch is that strings are containers of strings. >>> issubclass(str, Container)
True
>>> 't' in 'test'
True You can try It's also worth mentioning that |
Beta Was this translation helpful? Give feedback.
-
Hi @coady, thx for your quick response, yes I know that i can use |
Beta Was this translation helpful? Give feedback.
-
In that case, you want an abstract base class that is container-like but excludes strings. Something like class MyIterable(abc.ABC):
@classmethod
def __subclasshook__(cls, subclass):
return issubclass(subclass, Iterable) and not issubclass(subclass, str)
def __class_getitem__(cls, key):
return GenericAlias(cls, key) |
Beta Was this translation helpful? Give feedback.
-
Ech this is really ugly.... Btw. I have verified one more thing that if I have changed the code to use from typing import Any, List, Container
from multimethod import multimethod
@multimethod
def validate_enum_list(key: str, value: str, allowed_values: List[str]) -> None:
print("key: str, value: str, allowed_values: List[str]")
print(type(value))
print(type(allowed_values))
@validate_enum_list.register
def _(key: str, value: List[str], allowed_values: List[List[str]]) -> None:
print("key: str, value: List[str], allowed_values: List[List[str]]")
print(type(value))
print(type(allowed_values))
validate_enum_list("test1", "test1", ["test1"])
validate_enum_list("test2", ["test2"], [["test2"]])
validate_enum_list("test2", "test2", "test2") and I run mypy on it and it does not write result that the line |
Beta Was this translation helpful? Give feedback.
-
Another thing is that if I use this class than mypy argues that |
Beta Was this translation helpful? Give feedback.
-
But still I have a few more questions. First this example:
Unfortunately it happens, that I would like to have in the body of the second method a call of sorted and it would look like that: @validate_enum_list.register
def _(key: str, value: MyIterable[str], allowed_values: MyIterable[MyIterable[str]]) -> None:
print("key: str, value: List[str], allowed_values: MyIterable[MyIterable[str]]")
print(type(value))
print(type(allowed_values))
value = sorted(value)
allowed_values = sorted([sorted(item) for item in allowed_values]) and on runtime it works, but mypy says an error like below: multi2.py:29: error: No overload variant of "sorted" matches argument type "MyIterable[str]" [call-overload]
multi2.py:29: note: Possible overload variants:
multi2.py:29: note: def [SupportsRichComparisonT] sorted(Iterable[SupportsRichComparisonT], /, *, key: None = ..., reverse: bool = ...) -> List[SupportsRichComparisonT]
multi2.py:29: note: def [_T] sorted(Iterable[_T], /, *, key: Callable[[_T], Union[SupportsDunderLT[Any], SupportsDunderGT[Any]]], reverse: bool = ...) -> List[_T]
multi2.py:30: error: Incompatible types in assignment (expression has type "List[List[Any]]", variable has type "MyIterable[MyIterable[str]]") [assignment]
multi2.py:30: error: "MyIterable[MyIterable[str]]" has no attribute "__iter__" (not iterable) [attr-defined] and I have a question to you, because currently we have MyIterable class Container-like without str, shall i implement T = TypeVar("T")
class MyIterable(Iterable[T]):
@classmethod
def __subclasshook__(cls, subclass: type) -> bool:
return not issubclass(subclass, str)
def __class_getitem__(cls, key: Any) -> GenericAlias:
return GenericAlias(cls, key) Btw. the T = TypeVar("T") is due to mypy |
Beta Was this translation helpful? Give feedback.
-
@coady correct my if my understand is wrong, but is
|
Beta Was this translation helpful? Give feedback.
-
Yes, |
Beta Was this translation helpful? Give feedback.
It's actually working as intended; the catch is that strings are containers of strings.
You can try
list[str]
instead.It's also worth mentioning that
Container[]
can't be fully supported, because there's no way to detect what it contains.Iterable[]
orCollection[]
checks the first element.