You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I have the following problem, to which I did not find an answer to in the Descriptor HowTo Guide you've linked to. So, running mypy 0.700 on the following code...
desc.py:8: error: Incompatible types in assignment (expression has type "StringField", variable has type "str")
desc.py:10: error: Revealed type is 'builtins.str'
desc.py:13: error: Revealed type is 'builtins.str'
So mypy correctly reveals the type of the x field (at least in case of dealing with an instance of MyClass), but annotation still gives the "Incompatible types in assignment" error.
When I remove the annotation relveal_type mypy gives the same outputs and the error disappears.
That is why I thought I need the documentation, because I must be doing something wrong. Or the feature is not yet implemented? Or is it a bug? Let me know if it makes sense to create a separate issue.
@pawelswiecki mypy is perfectly right here and your annotation is wrong. Descriptor protocol is invoked only when accessing an attribute, not inside the class body. To understand it, try this:
classMyClass:
x=StringField()
print(x) # <__main__.StringField object at ...>print(MyClass.x) # "string"
When I remove the annotation reveal_type mypy gives the same outputs and the error disappears.
classStringField:
def__get__(self, obj, objtype) ->str:
return"string"classMyClass:
x=StringField()
y=MyClass()
y.x="test"# OKy.x=42# Error: Incompatible types in assignment (expression has type "int", variable has type "str")
What else do you need?
Also please stop spamming in this issue. If you have questions about mypy, there is a Gitter channel for this.
What you wrote looks like the solution. I just wanted to use the same typing pattern as dataclasses (it seemed natural at the time), which is just not adequate in the descriptor context, as you pointed out. Thank you very much for your responses. And sorry for the overabundance of comments, I'm stopping here.
I'm trying to define a simple descriptor base class, but after 3 hours of trying, still can't make all the annotations work.....It's just so hard for a normal user. This is the best I came up with:
from __future__ importannotationsfromtypingimportTypeVar, Generic, Type, AnyfromweakrefimportWeakKeyDictionaryS=TypeVar('S', bound='Descriptorable')
classDescriptorable:
# This is the most precise one, but I can't make it to work: # _descriptor_instances: WeakKeyDictionary[Any, S] # Also not working, although it looks innocent to me:# _descriptor_instances: WeakKeyDictionary[Any, Descriptorable]_descriptor_instances: WeakKeyDictionarydef__init__(self):
self._descriptor_instances=WeakKeyDictionary()
# This is not precise enough, see reveal_type()s below# def __get__(self, instance, owner) -> Descriptorable: def__get__(self: S, instance, owner) ->S:
ifinstanceisNone:
returnselfifinstanceinself._descriptor_instances:
descriptor_instance=self._descriptor_instances[instance]
else:
descriptor_instance=self._create_descriptor_instance(instance)
self._descriptor_instances[instance] =descriptor_instancereturndescriptor_instancedef_create_descriptor_instance(self, instance):
raiseNotImplementedErrorT=TypeVar('T')
# This class subclasses Descriptorable, so it can be used as descriptor. classDataContainer(Descriptorable, Generic[T]):
data_type: Type[T]
data: Tdef__init__(self, data: T, data_type: Type[T]):
super().__init__()
self.data_type=data_typeself.data=datadef_create_descriptor_instance(self, instance) ->DataContainer[T]:
returnDataContainer(self.data, self.data_type)
defget_data(self) ->T:
returnself.datadefset_data(self, data: T) ->None:
self.data=dataclassMyClass:
data=DataContainer(2.0, float)
m=MyClass()
reveal_type(m.data) # Should be: DataContainer[float]reveal_type(m.data._descriptor_instances) # Should be WeakKeyDictionary[Any, DataContainer[float]]
The hardest part is how to annotate _descriptor_instances: WeakKeyDictionary[Any, S] (see comments in the above code). I've also tried making the Descriptorable class generic, but with no success.
Any help would be much appreciated. And I really think that descriptors deserve multiple paragraphs, if not a whole page, in the documentation.
Was the verdict here that code using the dataclass-style pattern is wrong by design?
class_StringField:
def__get__(self, obj, objtype) ->str:
return"string"classMyClass:
x: str=StringField() # what is the ruling on this annotation
That is, is the marked annotation quite deliberately rejected by mypy now and for all time, or is there a possibility that a patch to make this legal would be considered?
@starhel I'm encountering this now too. I have a generic descriptor class ProfileValue[T] with appropriate type hints on __get__ and __set__, but mypy reports errors with either of these dataclass field definitions:
@dataclass
class Profile:
name: ProfileValue[str] = ProfileValue(default="TODO")
test.py: Argument "name" to "Profile" has incompatible type "str"; expected "ProfileValue[str]"
or
@dataclass
class Profile:
name: str = ProfileValue(default="TODO")
profile.py: Incompatible types in assignment (expression has type "ProfileValue[str]", variable has type "str")
The first one seems to be the preferred declaration, based on the example in the dataclasses docs, but mypy isn't inferring type correctly from the descriptor's __set__ method (#13856). The second one works at point of instantiation but produces an error on the field definition. For what it's worth, pyright accepts the first one but not the second.
Activity
pawelswiecki commentedon Apr 15, 2019
Is the documentation available somewhere? I cannot find it.
ilevkivskyi commentedon Apr 15, 2019
@pawelswiecki No, the issue is still open. The general Python docs are here, from this you can guess how it translates to static typing.
pawelswiecki commentedon Apr 17, 2019
Thanks for the answer.
I have the following problem, to which I did not find an answer to in the Descriptor HowTo Guide you've linked to. So, running mypy 0.700 on the following code...
... gives me the following output:
So mypy correctly reveals the type of the
xfield (at least in case of dealing with an instance ofMyClass), but annotation still gives the "Incompatible types in assignment" error.When I remove the annotation
relveal_typemypy gives the same outputs and the error disappears.That is why I thought I need the documentation, because I must be doing something wrong. Or the feature is not yet implemented? Or is it a bug? Let me know if it makes sense to create a separate issue.
ilevkivskyi commentedon Apr 17, 2019
@pawelswiecki mypy is perfectly right here and your annotation is wrong. Descriptor protocol is invoked only when accessing an attribute, not inside the class body. To understand it, try this:
pawelswiecki commentedon Apr 17, 2019
Right, so how do I tell mypy that
xas an accessed attribute isstr? Just like in dataclasses.pawelswiecki commentedon Apr 17, 2019
I'd like to avoid using
cast.Like something like this:
This is a partial solucion, since return type of
__get__is ignored (no surprise here).ilevkivskyi commentedon Apr 17, 2019
Just an hour ago you wrote
What else do you need?
Also please stop spamming in this issue. If you have questions about mypy, there is a Gitter channel for this.
pawelswiecki commentedon Apr 17, 2019
What you wrote looks like the solution. I just wanted to use the same typing pattern as dataclasses (it seemed natural at the time), which is just not adequate in the descriptor context, as you pointed out. Thank you very much for your responses. And sorry for the overabundance of comments, I'm stopping here.
JukkaL commentedon Jun 6, 2019
Increased priority to high since this is a pretty fundamental Python feature and annotating descriptors is non-obvious.
ilevkivskyi commentedon Aug 12, 2019
We should also add an example with an overloaded
__get__(for access on class vs instance), because it is a very common pattern.kawing-chiu commentedon Feb 16, 2020
I'm trying to define a simple descriptor base class, but after 3 hours of trying, still can't make all the annotations work.....It's just so hard for a normal user. This is the best I came up with:
The hardest part is how to annotate
_descriptor_instances: WeakKeyDictionary[Any, S](see comments in the above code). I've also tried making theDescriptorableclass generic, but with no success.Any help would be much appreciated. And I really think that descriptors deserve multiple paragraphs, if not a whole page, in the documentation.
eric-wieser commentedon May 12, 2020
Was the verdict here that code using the dataclass-style pattern is wrong by design?
That is, is the marked annotation quite deliberately rejected by mypy now and for all time, or is there a possibility that a patch to make this legal would be considered?
msullivan commentedon May 20, 2020
I think we'd probably consider it?
jmehnle commentedon Oct 6, 2020
Because it took me about 90 minutes trying to figure out how to do this today, I'm leaving this here for posterity:
starhel commentedon Oct 7, 2022
Dataclasses and descriptors are described in https://docs.python.org/3/library/dataclasses.html#descriptor-typed-fields
Unfortunately mypy reports an error:
I've tried many suggestions from this issue and SO threads, but nothing is working correctly. How dataclasses with descriptors should be annotated?
bszonye commentedon Dec 6, 2022
@starhel I'm encountering this now too. I have a generic descriptor class
ProfileValue[T]with appropriate type hints on__get__and__set__, but mypy reports errors with either of these dataclass field definitions:or
The first one seems to be the preferred declaration, based on the example in the dataclasses docs, but mypy isn't inferring type correctly from the descriptor's
__set__method (#13856). The second one works at point of instantiation but produces an error on the field definition. For what it's worth, pyright accepts the first one but not the second.