Issue36363
Created on 2019-03-19 13:29 by bux, last changed 2022-04-11 14:59 by admin. This issue is now closed.
| Messages (4) | |||
|---|---|---|---|
| msg338354 - (view) | Author: Bastien Sevajol (bux) | Date: 2019-03-19 13:29 | |
Hello,
For following code:
```
import dataclasses
import typing
from datetime import datetime
@dataclasses.dataclass
class Foo:
datetime: typing.Optional[datetime] = None
print(dataclasses.fields(Foo)[0].type)
```
`datetime` `Foo` attribute have `NoneType` type. Problem come from `datetime` attribute name is already used by the `from datetime import datetime` import. I'm not sure if it is a bug or a strange behavior but it seems not regular. Tested on python 3.8.0a2 with same result.
|
|||
| msg338367 - (view) | Author: Karthikeyan Singaravelan (xtreak) * ![]() |
Date: 2019-03-19 15:41 | |
I am not sure this is a problem with dataclasses. dataclasses acts upon information from cls.__dict__.get('__annotations__', {}) at [0] and sets the type attribute for the field. Seems like using a valid identifier (class or function) used as class attribute along with defining it as optional type of the same name sets None type in annotation. Also happens when a class attribute is used with optional type for another attribute as in Spam class This more feels like a behavior with typing module. I am adding typing module maintainers for clarification.
# bpo36363.py
import typing
class Baz:
pass
class Bar:
pass
class Foo:
baz: typing.Optional[Bar] = None
Bar: typing.Optional[Bar] = None
class Spam:
bar: typing.Optional[Bar] = None
baz: typing.Optional[bar] = None
print(Foo.__dict__.get('__annotations__', {}))
print(Spam.__dict__.get('__annotations__', {}))
$ ./python.exe ../backups/bpo36363.py
{'baz': typing.Union[__main__.Bar, NoneType], 'Bar': <class 'NoneType'>}
{'bar': typing.Union[__main__.Bar, NoneType], 'baz': <class 'NoneType'>}
[0] https://github.com/python/cpython/blob/dcf617152e1d4c4a5e7965733928858a9c0936ca/Lib/dataclasses.py#L828
|
|||
| msg338387 - (view) | Author: Karthikeyan Singaravelan (xtreak) * ![]() |
Date: 2019-03-19 16:53 | |
class Spam:
bar: typing.Optional[bar] = str
class Spaz:
bar: typing.Optional[bar] = None
print(Spam.__annotations__)
print(Spaz.__annotations__)
{'bar': typing.Union[str, NoneType]}
{'bar': <class 'NoneType'>}
In Spam bar has str assigned to it and seems like in Spaz bar is assigned None and hence during annotation creation this evaluates to bar: typing.Optional[None] where None is the value of bar and in Spam.bar it's typing.Union[str, NoneType] instead.
|
|||
| msg338389 - (view) | Author: Guido van Rossum (gvanrossum) * ![]() |
Date: 2019-03-19 17:15 | |
A simpler example shows it has nothing to do with annotations -- it is simply behavior of the typing module. >>> import typing >>> typing.Optional[str] typing.Union[str, NoneType] >>> typing.Optional[None] <class 'NoneType'> >>> I don't think there's a bug here, and I am closing this as "not a bug". The problem in the original code is that the annotation references a global name that is shadowed by a local (to the class body) name, and because of the initialization, the latter takes precedence. (To see for yourself, use the dis module to disassemble the code for Spam and Spaz.) |
|||
| History | |||
|---|---|---|---|
| Date | User | Action | Args |
| 2022-04-11 14:59:12 | admin | set | github: 80544 |
| 2022-02-20 15:19:08 | serhiy.storchaka | link | issue46807 superseder |
| 2019-03-19 17:15:25 | gvanrossum | set | status: open -> closed resolution: not a bug messages: + msg338389 stage: resolved |
| 2019-03-19 16:53:21 | xtreak | set | messages: + msg338387 |
| 2019-03-19 15:41:34 | xtreak | set | nosy:
+ xtreak, gvanrossum, levkivskyi messages:
+ msg338367 |
| 2019-03-19 15:05:01 | rhettinger | set | assignee: eric.smith |
| 2019-03-19 13:40:40 | xtreak | set | nosy:
+ eric.smith |
| 2019-03-19 13:29:50 | bux | create | |
