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

Raise error with pytest==8.0.0 #65

Open
OttoAndrey opened this issue Jan 29, 2024 · 20 comments · Fixed by EmuKit/emukit#460
Open

Raise error with pytest==8.0.0 #65

OttoAndrey opened this issue Jan 29, 2024 · 20 comments · Fixed by EmuKit/emukit#460

Comments

@OttoAndrey
Copy link

Hi! pytest-lazy-fixture doesn't work with pytest==8.0.0

 AttributeError: 'CallSpec2' object has no attribute 'funcargs'
@OttoAndrey OttoAndrey changed the title Raise error with pytest 8.0.0 Raise error with pytest==8.0.0 Jan 29, 2024
@glatterf42
Copy link

Since the main branch of this repo hasn't seen an update in two years, I've been looking for an alternative. In my case, I was using something like this:

# In conftest.py:
@pytest.fixture
def test_api_sqlite_mp(test_sqlite_mp):
    return Platform(_backend=RestTestBackend(test_sqlite_mp.backend))


@pytest.fixture
def test_api_pgsql_mp(test_pgsql_mp):
    return Platform(_backend=RestTestBackend(test_pgsql_mp.backend))

# In another file:
api_platforms = pytest.mark.parametrize(
    "test_mp",
    [
        pytest.lazy_fixture("test_api_sqlite_mp"),
        pytest.lazy_fixture("test_api_pgsql_mp"),
    ],
)

# And finally, for the function:
@api_platforms
def test_index_model(test_mp):
    ...

So following suggestions on StackOverflow and here, I changed that to

# In conftest.py (everything stays the same):
@pytest.fixture
def test_api_sqlite_mp(test_sqlite_mp):
    return Platform(_backend=RestTestBackend(test_sqlite_mp.backend))


@pytest.fixture
def test_api_pgsql_mp(test_pgsql_mp):
    return Platform(_backend=RestTestBackend(test_pgsql_mp.backend))

# In another file:
api_platforms = pytest.mark.parametrize(
    "test_mp",
    [
        "test_api_sqlite_mp",
        "test_api_pgsql_mp",
    ],
)

# And finally, for the function:
@api_platforms
def test_index_model(test_mp, request):
    test_mp = request.getfixturevalue(test_mp)
    ...

After uninstalling pytest-lazy-fixture, my tests are running just fine again. Hope this helps :)

@PrieJos
Copy link

PrieJos commented Jan 29, 2024

Hi All,

As mentioned by @glatterf42, it looks like this issue might never get fixed. So what I did was to come out with my own local pytest-lazy-fixture plugin like this:

import dataclasses
import typing

import pytest


@dataclasses.dataclass
class LazyFixture:
    """Lazy fixture dataclass."""

    name: str


def lazy_fixture(name: str) -> LazyFixture:
    """Mark a fixture as lazy."""
    return LazyFixture(name)


def is_lazy_fixture(value: object) -> bool:
    """Check whether a value is a lazy fixture."""
    return isinstance(value, LazyFixture)


def pytest_make_parametrize_id(
    config: pytest.Config,
    val: object,
    argname: str,
) -> str | None:
    """Inject lazy fixture parametrized id.

    Reference:
    - https://bit.ly/48Off6r

    Args:
        config (pytest.Config): pytest configuration.
        value (object): fixture value.
        argname (str): automatic parameter name.

    Returns:
        str: new parameter id.
    """
    if is_lazy_fixture(val):
        return typing.cast(LazyFixture, val).name
    return None


@pytest.hookimpl(tryfirst=True)
def pytest_fixture_setup(
    fixturedef: pytest.FixtureDef,
    request: pytest.FixtureRequest,
) -> object | None:
    """Lazy fixture setup hook.

    This hook will never take over a fixture setup but just simply will
    try to resolve recursively any lazy fixture found in request.param.

    Reference:
    - https://bit.ly/3SyvsXJ

    Args:
        fixturedef (pytest.FixtureDef): fixture definition object.
        request (pytest.FixtureRequest): fixture request object.

    Returns:
        object | None: fixture value or None otherwise.
    """
    if hasattr(request, "param") and request.param:
        request.param = _resolve_lazy_fixture(request.param, request)
    return None


def _resolve_lazy_fixture(__val: object, request: pytest.FixtureRequest) -> object:
    """Lazy fixture resolver.

    Args:
        __val (object): fixture value object.
        request (pytest.FixtureRequest): pytest fixture request object.

    Returns:
        object: resolved fixture value.
    """
    if isinstance(__val, list | tuple):
        return tuple(_resolve_lazy_fixture(v, request) for v in __val)
    if isinstance(__val, typing.Mapping):
        return {k: _resolve_lazy_fixture(v, request) for k, v in __val.items()}
    if not is_lazy_fixture(__val):
        return __val
    lazy_obj = typing.cast(LazyFixture, __val)
    return request.getfixturevalue(lazy_obj.name)

By now I am simply including this into my root conftest.py.

gentoo-bot pushed a commit to gentoo/gentoo that referenced this issue Jan 29, 2024
pitrou added a commit to pitrou/arrow that referenced this issue Jan 29, 2024
The PyArrow test suite relies on the pytest-lazy-fixture plugin, which breaks on pytest 8.0.0:
TvoroG/pytest-lazy-fixture#65
pitrou added a commit to apache/arrow that referenced this issue Jan 29, 2024
### Rationale for this change

The PyArrow test suite relies on the pytest-lazy-fixture plugin, which breaks on pytest 8.0.0: TvoroG/pytest-lazy-fixture#65

### What changes are included in this PR?

Avoid installing pytest 8 on CI builds, by putting an upper bound on the pytest version.

### Are these changes tested?

Yes, by construction.

### Are there any user-facing changes?

No.

Authored-by: Antoine Pitrou <antoine@python.org>
Signed-off-by: Antoine Pitrou <antoine@python.org>
@YannickJadoul
Copy link
Contributor

YannickJadoul commented Jan 30, 2024

For the record, the minimal set of changes to make things work again doesn't seem to be that massive. This works for me, passing pytest-lazy-fixture's test set (not running tox, just running Python 3.10 with pytest 8.0.0):

diff --git a/pytest_lazyfixture.py b/pytest_lazyfixture.py
index abf5db5..df83ce7 100644
--- a/pytest_lazyfixture.py
+++ b/pytest_lazyfixture.py
@@ -71,14 +71,13 @@ def pytest_make_parametrize_id(config, val, argname):
 def pytest_generate_tests(metafunc):
     yield
 
-    normalize_metafunc_calls(metafunc, 'funcargs')
-    normalize_metafunc_calls(metafunc, 'params')
+    normalize_metafunc_calls(metafunc)
 
 
-def normalize_metafunc_calls(metafunc, valtype, used_keys=None):
+def normalize_metafunc_calls(metafunc, used_keys=None):
     newcalls = []
     for callspec in metafunc._calls:
-        calls = normalize_call(callspec, metafunc, valtype, used_keys)
+        calls = normalize_call(callspec, metafunc, used_keys)
         newcalls.extend(calls)
     metafunc._calls = newcalls
 
@@ -98,17 +97,21 @@ def copy_metafunc(metafunc):
     return copied
 
 
-def normalize_call(callspec, metafunc, valtype, used_keys):
+def normalize_call(callspec, metafunc, used_keys):
     fm = metafunc.config.pluginmanager.get_plugin('funcmanage')
 
     used_keys = used_keys or set()
-    valtype_keys = set(getattr(callspec, valtype).keys()) - used_keys
+    keys = set(callspec.params.keys()) - used_keys
+    print(used_keys, keys)
 
-    for arg in valtype_keys:
-        val = getattr(callspec, valtype)[arg]
+    for arg in keys:
+        val = callspec.params[arg]
         if is_lazy_fixture(val):
             try:
-                _, fixturenames_closure, arg2fixturedefs = fm.getfixtureclosure([val.name], metafunc.definition.parent)
+                if pytest.version_tuple >= (8, 0, 0):
+                    fixturenames_closure, arg2fixturedefs = fm.getfixtureclosure(metafunc.definition.parent, [val.name], {})
+                else:
+                    _, fixturenames_closure, arg2fixturedefs = fm.getfixtureclosure([val.name], metafunc.definition.parent)
             except ValueError:
                 # 3.6.0 <= pytest < 3.7.0; `FixtureManager.getfixtureclosure` returns 2 values
                 fixturenames_closure, arg2fixturedefs = fm.getfixtureclosure([val.name], metafunc.definition.parent)
@@ -117,14 +120,14 @@ def normalize_call(callspec, metafunc, valtype, used_keys):
                 fixturenames_closure, arg2fixturedefs = fm.getfixtureclosure([val.name], current_node)
 
             extra_fixturenames = [fname for fname in fixturenames_closure
-                                  if fname not in callspec.params and fname not in callspec.funcargs]
+                                  if fname not in callspec.params]# and fname not in callspec.funcargs]
 
             newmetafunc = copy_metafunc(metafunc)
             newmetafunc.fixturenames = extra_fixturenames
             newmetafunc._arg2fixturedefs.update(arg2fixturedefs)
             newmetafunc._calls = [callspec]
             fm.pytest_generate_tests(newmetafunc)
-            normalize_metafunc_calls(newmetafunc, valtype, used_keys | set([arg]))
+            normalize_metafunc_calls(newmetafunc, used_keys | set([arg]))
             return newmetafunc._calls
 
         used_keys.add(arg)

But the bigger question is of course how much more overdue maintenance is waiting to happen, and whether whether @TvoroG would be interested to keep maintaining or to transfer maintenance to someone else (cfr #63).

@nicoddemus
Copy link
Contributor

I didn't look at the code in detail, but if the only problem is the call to getfixtureclosure, pytest-dev/pytest#11888 will fix the problem.

@hugovk
Copy link

hugovk commented Jan 30, 2024

I still get an error with pytest-dev/pytest#11888:

______________________ ERROR collecting tests/test_prettytable.py _______________________
/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/pluggy/_hooks.py:501: in __call__
    return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/pluggy/_manager.py:119: in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
../pytest/src/_pytest/python.py:274: in pytest_pycollect_makeitem
    return list(collector._genfunctions(name, obj))
../pytest/src/_pytest/python.py:489: in _genfunctions
    self.ihook.pytest_generate_tests.call_extra(methods, dict(metafunc=metafunc))
/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/pluggy/_hooks.py:562: in call_extra
    return self._hookexec(self.name, hookimpls, kwargs, firstresult)
/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/pluggy/_manager.py:119: in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/pytest_lazyfixture.py:74: in pytest_generate_tests
    normalize_metafunc_calls(metafunc, 'funcargs')
/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/pytest_lazyfixture.py:81: in normalize_metafunc_calls
    calls = normalize_call(callspec, metafunc, valtype, used_keys)
/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/pytest_lazyfixture.py:105: in normalize_call
    valtype_keys = set(getattr(callspec, valtype).keys()) - used_keys
E   AttributeError: 'CallSpec2' object has no attribute 'funcargs'

@YannickJadoul
Copy link
Contributor

I didn't look at the code in detail, but if the only problem is the call to getfixtureclosure, pytest-dev/pytest#11888 will fix the problem.

I believe the order of arguments also changed, or am I misreading the 3 different versions in pytest-lazy-fixutre?

I still get an error with pytest-dev/pytest#11888:

And yes, the other fix is that CallSpec2.funcargs was merged into CallSpec2.params? At least, stopping to access funcargs and only accessing params did the trick.
(So yes, actually my patch up there is not backwards compatible, right now.)

elisno added a commit to elisno/cleanlab that referenced this issue Jan 30, 2024
Related to error discussed in TvoroG/pytest-lazy-fixture#65

Update pytest version in requirements-dev.txt

Temporarily ignore recent release of 8.x

Update pytest version in requirements-dev.txt
manopapad added a commit to manopapad/legate.core that referenced this issue Jan 30, 2024
manopapad added a commit to nv-legate/legate.core that referenced this issue Jan 30, 2024
fhanzer added a commit to openamundsen/openamundsen that referenced this issue Apr 3, 2024
fhanzer added a commit to openamundsen/openamundsen that referenced this issue Apr 3, 2024
ArtyomYaprintsev added a commit to ArtyomYaprintsev/leetcode that referenced this issue Apr 8, 2024
With pytest==8.0.0 can not to use pytest-lazy-fixture

Related issue: TvoroG/pytest-lazy-fixture#65
BradyJ27 pushed a commit to BradyJ27/dvc that referenced this issue Apr 22, 2024
john0isaac added a commit to render-engine/render-engine that referenced this issue May 3, 2024
bob-beck pushed a commit to openbsd/ports that referenced this issue May 26, 2024
ANogin added a commit to ANogin/ofrak that referenced this issue May 30, 2024
* Use python3.8 in docker images

* Require pytest<8.0

This is needed becase of pytest-dev/pytest#11890
TvoroG/pytest-lazy-fixture#65

* Update changelog

* Revert "Update changelog"

This reverts commit 500ee9b.

Making changes before having coffee :(

* Add a note on recommending Python 3.8

* `ofrak_core` also needs `pytest<8.0`
ANogin added a commit to ANogin/ofrak that referenced this issue May 30, 2024
* Use python3.8 in docker images

* Require pytest<8.0

This is needed becase of pytest-dev/pytest#11890
TvoroG/pytest-lazy-fixture#65

* Update changelog

* Revert "Update changelog"

This reverts commit 500ee9b.

Making changes before having coffee :(

* Update to latest angr==9.2.89, which also necessitates Python >= 3.8 and capstone==5.0.0.post1

* Apply Edward's attempted fix to angr test failure

* Add a note on recommending Python 3.8

* Add a note on recommending Python 3.8

* Document the requirement of Python 3.8+

* Switch to angr 9.2.77

* `ofrak_core` also needs `pytest<8.0`

* ignore DataWord in test due to angr bug

* add another now missing block

* black linting

* Attempt to fix a capstone error

* Dropping the .altinstr_replacement section from the toolchain (redballoonsecurity#414)

* Dropping the .altinstr_replacement section from the toolchain

* Updated CHANGELOG

* Set the fallback font to monospace (redballoonsecurity#422)

* Set the fallback font to monospace

* Update CHANGELOG

* Display strings with numbers primarily as strings (redballoonsecurity#423)

* Display strings with numbers primarily as strings

* Update CHANGELOG

* Add typing support to ofrak_ghidra package (redballoonsecurity#421)

* Add typing to ofrak_ghidra package

* Add changelog

---------

Co-authored-by: Paul Noalhyt <paul@redballoonsecurity.com>

* Increase time limit on `test_comment_content`

* Fix a spurious "no current event loop" test error

* Explain 3.7 vs 3.8 better in the docs

* Cite specific versions of angr in comment

* Update docs/environment-setup.md

* Update docs/getting-started.md

---------

Co-authored-by: Edward Larson <edward@redballoonsecurity.com>
Co-authored-by: rbs-alexr <122491504+rbs-alexr@users.noreply.github.com>
Co-authored-by: Jacob Strieb <99368685+rbs-jacob@users.noreply.github.com>
Co-authored-by: Paul Noalhyt <paul.noalhyt@gmail.com>
Co-authored-by: Paul Noalhyt <paul@redballoonsecurity.com>
Co-authored-by: Wyatt <53830972+whyitfor@users.noreply.github.com>
mgorny added a commit to mgorny/django-timezone-field that referenced this issue Jul 8, 2024
`pytest-lazy-fixture` has not been maintained since 2022, and it does
not work properly with pytest-8 anymore [1].  Switch to the maintained
`pytest-lazy-fixtures` replacement.

[1] TvoroG/pytest-lazy-fixture#65
mgorny added a commit to mgorny/django-timezone-field that referenced this issue Jul 8, 2024
`pytest-lazy-fixture` has not been maintained since 2022, and it does
not work properly with pytest-8 anymore [1].  Switch to the maintained
`pytest-lazy-fixtures` replacement.

[1] TvoroG/pytest-lazy-fixture#65
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

Successfully merging a pull request may close this issue.

8 participants