hammett icon indicating copy to clipboard operation
hammett copied to clipboard

pytest-django is no longer working with hammett

Open geoffbeier opened this issue 7 months ago • 1 comments

I have no idea how long this has been the case.

Here's how I discovered it:

git clone [email protected]:boxed/django-fastdev.git
python3 -mvenv venv
source venv/bin/activate
pip install -r requirements.txt
pip install -r test_requirements.txt
make test

While I expected all tests to pass on an unmodified repository, I got this exception/stack trace:

hammett
Traceback (most recent call last):
  File "/Users/geoff/bug_reproductions/django-fastdev/venv/bin/hammett", line 8, in <module>
    sys.exit(main_cli())
             ^^^^^^^^^^
  File "/Users/geoff/bug_reproductions/django-fastdev/venv/lib/python3.11/site-packages/hammett/__init__.py", line 779, in main_cli
    return m(
           ^^
  File "/Users/geoff/bug_reproductions/django-fastdev/venv/lib/python3.11/site-packages/hammett/__init__.py", line 454, in main
    return main_run_tests(match=match, module_unload=module_unload, **params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/geoff/bug_reproductions/django-fastdev/venv/lib/python3.11/site-packages/hammett/__init__.py", line 561, in main_run_tests
    load_plugins()
  File "/Users/geoff/bug_reproductions/django-fastdev/venv/lib/python3.11/site-packages/hammett/impl.py", line 782, in load_plugins
    load_plugin(plugin)
  File "/Users/geoff/bug_reproductions/django-fastdev/venv/lib/python3.11/site-packages/hammett/impl.py", line 721, in load_plugin
    plugin_module = importlib.import_module(module_name+'.plugin')
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/geoff/.pyenv/versions/3.11.8/lib/python3.11/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1204, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1176, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1126, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "<frozen importlib._bootstrap>", line 1204, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1176, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1147, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 940, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/Users/geoff/bug_reproductions/django-fastdev/venv/lib/python3.11/site-packages/pytest_django/__init__.py", line 9, in <module>
    from .plugin import DjangoDbBlocker
  File "/Users/geoff/bug_reproductions/django-fastdev/venv/lib/python3.11/site-packages/pytest_django/plugin.py", line 261, in <module>
    report_header_key = pytest.StashKey[List[str]]()
                        ^^^^^^^^^^^^^^^
AttributeError: module 'hammett' has no attribute 'StashKey'
make: *** [test] Error 1

As a first attempt, I copied stash.py into the hammett package and added StashKey and Stash to the interface for hammet. Then I added a stash member to EarlyConfig. That got me to a couple of places where pytest-django expected the config object to implement getoption(). I added a dumb implementation to get past that:

    def getoption(self, name, default):
        if name == "version":
            return default
        if name == "help":
            return default
        raise ValueError(f"EarlyConfig.getoption called with unexpected parameter: {name=}")

That got me to a point where pytest-django needed pytest's built-in MonkeyPatch fixtures.

I made a straightforward attempt add that to hammett, but quickly ran into circular imports (I suspect because so much code lives in __init__.py) and I decided I needed to stop shaving this yak and return to the reason I'd cloned django-fastdev in the first place.

I think in order to sanely add enough MonkeyPatch for pytest-django, much of what's in init.py would need to be split out into submodules, then just exposed on the package by adding, e.g.

from stash import Stash, StashKey

to __init__.py to avoid breaking the API.

If you still consider it desirable to support pytest-django, I might be happy to help do that. But I definitely didn't want to do a significant reorganization of the package without talking about it first :-)

I'm not sure what more would be required to support pytest-django once MonkeyPatch is there.

As I mentioned in the django-fastdev PR, running the tests via pytest:

PYTHONPATH=. pytest

works.

geoffbeier avatar Jul 08 '24 12:07 geoffbeier