Skip to content

Commit 4c68d98

Browse files
authored
fix: handle session fixtures with exceptions (#81)
* fix: handle session fixtures with exceptions * test: update tests
1 parent 8ebba2f commit 4c68d98

File tree

4 files changed

+114
-0
lines changed

4 files changed

+114
-0
lines changed

pytest_asyncio_cooperative/fixtures.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import asyncio
22
import inspect
3+
import types
34
from typing import List
45
from typing import Union
56

@@ -114,6 +115,11 @@ def __init__(self, wrapped_func):
114115
self.lock = asyncio.Lock()
115116
self.wrapped_func = wrapped_func
116117

118+
# Trying to fool pytest's use of inspect.isgeneratorfunction
119+
self.__defaults__ = getattr(wrapped_func, "__defaults__", None)
120+
self.__kwdefaults__ = getattr(wrapped_func, "__kwdefaults__", None)
121+
self.__annotations__ = getattr(wrapped_func, "__annotations__", {})
122+
117123
@property
118124
def __code__(self):
119125
return self.wrapped_func.__code__

tests/test_fixture.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import sys
2+
13
import pytest
24

35

@@ -463,6 +465,7 @@ async def test_a(aaa, bbb):
463465
result.assert_outcomes(passed=1)
464466

465467

468+
@pytest.mark.skipif(sys.version_info < (3, 10), reason="Requires Python 3.10+")
466469
@pytest.mark.parametrize("scope", ["module", "session"])
467470
@pytest.mark.parametrize("def_", ["def", "async def"])
468471
@pytest.mark.parametrize("ret", ["return", "yield"])
@@ -496,6 +499,51 @@ async def test_b(shared_fixture):
496499

497500
result = testdir.runpytest()
498501

502+
if fail:
503+
if ret == "yield" and def_ == "def":
504+
result.assert_outcomes(errors=2)
505+
else:
506+
result.assert_outcomes(failed=2)
507+
# Should be errors instead of failures
508+
# https://github.com/willemt/pytest-asyncio-cooperative/issues/42
509+
else:
510+
result.assert_outcomes(passed=2)
511+
512+
513+
@pytest.mark.skipif(sys.version_info > (3, 9), reason="Legacy behaviour")
514+
@pytest.mark.parametrize("scope", ["module", "session"])
515+
@pytest.mark.parametrize("def_", ["def", "async def"])
516+
@pytest.mark.parametrize("ret", ["return", "yield"])
517+
@pytest.mark.parametrize("fail", [False, True])
518+
def test_shared_fixture_caching_pre_py3_10(testdir, scope, def_, ret, fail):
519+
testdir.makepyfile(
520+
f"""
521+
import pytest
522+
import time
523+
524+
called = False
525+
@pytest.fixture(scope="{scope}")
526+
{def_} shared_fixture():
527+
global called
528+
if called:
529+
assert {fail}
530+
else:
531+
called = True
532+
assert not {fail}
533+
{ret}
534+
535+
@pytest.mark.asyncio_cooperative
536+
async def test_a(shared_fixture):
537+
assert True
538+
539+
@pytest.mark.asyncio_cooperative
540+
async def test_b(shared_fixture):
541+
assert True
542+
"""
543+
)
544+
545+
result = testdir.runpytest()
546+
499547
if fail:
500548
result.assert_outcomes(failed=2)
501549
# Should be errors instead of failures

tests/test_fixture_ducktyping.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import inspect
2+
import sys
3+
4+
import pytest
5+
6+
from pytest_asyncio_cooperative.fixtures import CachedGen
7+
8+
9+
def my_function():
10+
yield "Hello, World!"
11+
12+
13+
@pytest.mark.skipif(sys.version_info < (3, 10), reason="Requires Python 3.10+")
14+
def test_isgeneratorfunction_duck_typing():
15+
wrapped = CachedGen(my_function)
16+
assert inspect.isgeneratorfunction(wrapped)

tests/test_fixture_session.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import sys
2+
3+
import pytest
4+
5+
6+
@pytest.mark.skipif(sys.version_info < (3, 10), reason="Requires Python 3.10+")
7+
def test_session_fixture_with_exception(testdir):
8+
testdir.makepyfile(
9+
"""
10+
import asyncio
11+
12+
import pytest
13+
14+
15+
class Foo:
16+
def reset(self):
17+
print("Foo reset")
18+
19+
20+
@pytest.fixture(scope="session")
21+
def session():
22+
raise NotImplementedError("xxx")
23+
foo = Foo()
24+
yield foo
25+
26+
27+
@pytest.fixture
28+
def regularfixture(session):
29+
session.reset()
30+
yield session
31+
32+
33+
@pytest.mark.asyncio_cooperative
34+
async def test_1(regularfixture):
35+
await asyncio.sleep(1)
36+
37+
"""
38+
)
39+
40+
result = testdir.runpytest("-q")
41+
42+
assert "object has no attribute 'reset'" not in "".join(result.outlines)
43+
44+
result.assert_outcomes(passed=0, failed=0, errors=1)

0 commit comments

Comments
 (0)