Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

freeze_time doesn't work with pydantic #480

Open
Tehtehteh opened this issue Nov 14, 2022 · 7 comments
Open

freeze_time doesn't work with pydantic #480

Tehtehteh opened this issue Nov 14, 2022 · 7 comments

Comments

@Tehtehteh
Copy link

Tehtehteh commented Nov 14, 2022

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
@bradydean
Copy link

I found that pydantic==1.9.2 still works, but it would be nice to see this resovled.

@RemiDesgrange
Copy link

I re-tested with @Tehtehteh example and it worked with pydantic v2.

@samueldg
Copy link

samueldg commented Jul 29, 2023

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",
]

@chrisk314
Copy link

chrisk314 commented May 7, 2024

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.

@samueldg
Copy link

samueldg commented May 7, 2024

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

@chrisk314
Copy link

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

@chrisk314
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants