`freeze_time` doesn't work with pydantic
Tehtehteh opened this issue · comments
Overview
It seems that freezegun.freeze_time
patch is incompatible with pydantic
imports, because of some datetime classes re-definition. Minimal working example:
import freezegun
def main():
with freezegun.freeze_time('2022-10-10') as frozen_time:
import pydantic
print('Hello, world')
if __name__ == '__main__':
main()
This code throws following error:
Traceback (most recent call last):
File "main.py", line 11, in <module>
main()
File "main.py", line 6, in main
import pydantic
File "pydantic/__init__.py", line 2, in init pydantic.__init__
File "pydantic/dataclasses.py", line 56, in init pydantic.dataclasses
# +=======+=======+=======+
File "pydantic/error_wrappers.py", line 4, in init pydantic.error_wrappers
File "pydantic/json.py", line 14, in init pydantic.json
File "pydantic/types.py", line 1162, in init pydantic.types
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
Requirements.txt
freezegun==1.2.2
pydantic==1.10.2
python-dateutil==2.8.2
six==1.16.0
typing_extensions==4.4.0
I found that pydantic==1.9.2 still works, but it would be nice to see this resovled.
I re-tested with @Tehtehteh example and it worked with pydantic v2.
If you're stuck with pydantic 1.10 and need a workaround for this, you can replace your freeze_time
import with an intermediate module which already import pydantic.types
:
freezegun_shim.py
:
"""
Hack to ensure that we never import pydantic from inside a `freeze_time`.
The freezegun library monkey patches or replaces certain types such as `date`
or `datetime` in order to make them return whatever frozen time we specified.
That doesn't play nicely with Pydantic, which uses such types as metaclasses.
When you use a fake class as a metaclass, it may start breaking at import time,
with an error like this:
TypeError: metaclass conflict: the metaclass of a derived class must be a
(non-strict) subclass of the metaclasses of all its bases
The incompatibility with Pydantic is documented in an issue:
https://github.com/spulec/freezegun/issues/480
The fix here is to make sure that whenever you import freeze_time,
pydantic.types (the module that fails) is already imported and cached
in `sys.modules`, so it's fine if any downstream code attempts to import it.
"""
import pydantic.types
from freezegun import freeze_time
__all__ = [
"freeze_time",
]
Any update on this? I'm still hitting this bug with pydantic v2. For others facing this issue, there is an alternative lib very similar to freezegun which is working without issues called time-machine.
@chrisk314 you might be able to work around the issue with an import pydantic.types
in your conftest -- basically ensure you import those types before you import freezegun or timemachine.
@samueldg thanks for the suggestion. I did try out the work around you shared above. unfortunately it didn't work in my case. My issue was actually being caused by an import from the Huggingface transformers
library, though the error message was identical. Your import trick did circumvent this issue, but then another similar issue from another import popped up.
For me a better solution was to switch to time-machine library: that library is providing the same functionality; it's working correctly; and I don't need any work arounds introduced into the tests. Would be nice if there was a fix for freezegun as it's a great library.
Relevant part of the stack trace for the similar problem coming from the Huggingface transformers library in case it's useful for debugging this issue.
.venv/lib/python3.12/site-packages/freezegun/api.py:686: in __enter__
return self.start()
.venv/lib/python3.12/site-packages/freezegun/api.py:775: in start
module_attrs = _get_cached_module_attributes(module)
.venv/lib/python3.12/site-packages/freezegun/api.py:144: in _get_cached_module_attributes
_setup_module_cache(module)
.venv/lib/python3.12/site-packages/freezegun/api.py:123: in _setup_module_cache
all_module_attributes = _get_module_attributes(module)
.venv/lib/python3.12/site-packages/freezegun/api.py:112: in _get_module_attributes
attribute_value = getattr(module, attribute_name)
.venv/lib/python3.12/site-packages/transformers/utils/import_utils.py:1501: in __getattr__
value = getattr(module, name)
.venv/lib/python3.12/site-packages/transformers/utils/import_utils.py:1500: in __getattr__
module = self._get_module(self._class_to_module[name])
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <module 'transformers.tools' from '/redacted/.venv/lib/python3.12/site-packages/transformers/tools/__init__.py'>
module_name = 'agents'
def _get_module(self, module_name: str):
try:
return importlib.import_module("." + module_name, self.__name__)
except Exception as e:
> raise RuntimeError(
f"Failed to import {self.__name__}.{module_name} because of the following error (look up to see its"
f" traceback):\n{e}"
) from e
E RuntimeError: Failed to import transformers.tools.agents because of the following error (look up to see its traceback):
E metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
I have been hit by the same issue described here working with openai
library. Tried the workaround but unfortunately, it didn't work
@josenava I had the same issue. The solution is import pydantic.v1.types
instead of import pydantic.types
. The class that's causing the error was in Pydantic V1 which is no longer the latest version but is included as a legacy in the package.
Relevant part of the stack trace for the similar problem coming from the Huggingface transformers library in case it's useful for debugging this issue.
I'm running into exactly this issue with Huggingface and trying to use freezegun. Is there a solution?
@josenava I had the same issue. The solution is
import pydantic.v1.types
instead ofimport pydantic.types
. The class that's causing the error was in Pydantic V1 which is no longer the latest version but is included as a legacy in the package.
@samschiff19 You are right, that worked, thanks!