odmantic
odmantic copied to clipboard
Use of Optional Fields in ODMantic Models can change global type annotations handling
Bug
If i use the type Optional[datetime.datetime]
in a class which extends odmantic.model
the type is mapped to typing.Optional[odmantic.bson._datetime]
. This is intended.
But after the fist us of this type, the type mapping is performed for all following Optional[datetime.datetime]
types used in different classes. Even if the class is a "plain" class, which does not extend odmantic.Model
.
Current Behavior
Test Script:
import datetime
from typing import Optional, get_type_hints
from odmantic import Model
class Model1:
a: datetime.datetime
b: Optional[datetime.datetime]
class MyDBModel(Model):
c: datetime.datetime
d: Optional[datetime.datetime]
class Model2:
e: datetime.datetime
f: Optional[datetime.datetime]
print("Model1", get_type_hints(Model1))
print("MyDBModel", get_type_hints(MyDBModel))
print("Model2", get_type_hints(Model2))
Output:
Model1 {'a': <class 'datetime.datetime'>, 'b': typing.Optional[datetime.datetime]}
MyDBModel {'c': <class 'odmantic.bson._datetime'>, 'd': typing.Optional[odmantic.bson._datetime], 'id': <class 'odmantic.bson.ObjectId'>}
Model2 {'e': <class 'datetime.datetime'>, 'f': typing.Optional[odmantic.bson._datetime]}
Expected behavior
The type of Field f in Model2 should be typing.Optional[datetime.datetime]
like in Model1 and not typing.Optional[odmantic.bson._datetime]
.
Environment
- ODMantic version: 0.3.5
- Pydantic infos (output of
python -c "import pydantic.utils; print(pydantic.utils.version_info())
): pydantic version: 1.8.2 pydantic compiled: True install path: /home/vscode/.local/lib/python3.9/site-packages/pydantic python version: 3.9.7 (default, Oct 13 2021, 09:00:49) [GCC 10.2.1 20210110] platform: Linux-5.4.72-microsoft-standard-WSL2-x86_64-with-glibc2.31 optional deps. installed: ['dotenv', 'typing-extensions']
I have this exact error too, also with odmantic 0.3.5 in the following enviroment
Current behaviour
>>> import typing
>>> import datetime
>>> import odmantic
>>> class A:
... a: typing.Optional[datetime.datetime]
...
>>> A.__annotations__
{'a': typing.Optional[datetime.datetime]}
>>> class B(odmantic.Model):
... b: typing.Optional[datetime.datetime]
...
>>> A.__annotations__
{'a': typing.Optional[odmantic.bson._datetime]}
I use annotations to generate function signatures for FastAPI.
Expected behaviour
A.__annotations__
should remain the same.
Environment:
- OS MacOS:
Darwin MacBookErny 19.6.0 Darwin Kernel Version 19.6.0: Tue Jun 22 19:49:55 PDT 2021; root:xnu-6153.141.35~1/RELEASE_X86_64 x86_64
- OS Linux:
Linux 5.4.0-107-generic #121~18.04.1-Ubuntu SMP Thu Mar 24 17:21:33 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
- Python 3.10.4
- pydantic: 1.8.2
- odmantic 0.3.5 / 0.4.0
I debugged this, and it happens exactly here: https://github.com/art049/odmantic/blob/e58b987c09898744a3bca66d389f284ab1d28e87/odmantic/model.py#L180
Instead of creating a new type, the args for composed types are change inline.
For python >= 3.9 I found that replacing the lines:
from types import FunctionType
with
from types import FunctionType, GenericAlias
and
setattr(type_, "__args__", new_arg_types)
with
type_ = GenericAlias(type_origin, new_arg_types)
would resolve this specific problem, But it seems not to be compatible with Python 3.8 and below.