spulec / freezegun

Let your Python tests travel through time

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

`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 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.

@samschiff19 You are right, that worked, thanks!