Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ setup_requires =
pytest-runner
install_requires =
decopatch
makefun>=1.9.5
makefun>=1.15.1
packaging
# note: pytest, too :)
functools32;python_version<'3.2'
Expand Down
47 changes: 35 additions & 12 deletions src/pytest_cases/fixture_core1_unions.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,26 @@
from makefun import with_signature, add_signature_parameters, wraps

import pytest
import sys

try: # python 3.3+
from inspect import signature, Parameter
except ImportError:
from funcsigs import signature, Parameter # noqa

try: # native coroutines, python 3.5+
from inspect import iscoroutinefunction
except ImportError:
def iscoroutinefunction(obj):
return False

try: # native async generators, python 3.6+
from inspect import isasyncgenfunction
except ImportError:
def isasyncgenfunction(obj):
return False


try: # type hints, python 3+
from typing import Callable, Union, Optional, Any, List, Iterable, Sequence # noqa
from types import ModuleType # noqa
Expand Down Expand Up @@ -224,7 +238,27 @@ def ignore_unused(fixture_func):
else:
new_sig = old_sig

if not isgeneratorfunction(fixture_func):
if isasyncgenfunction(fixture_func) and sys.version_info >= (3, 6):
from .pep525 import _ignore_unused_asyncgen_pep525
wrapped_fixture_func = _ignore_unused_asyncgen_pep525(fixture_func, new_sig, func_needs_request)
elif iscoroutinefunction(fixture_func) and sys.version_info >= (3, 5):
from .pep492 import _ignore_unused_coroutine_pep492
wrapped_fixture_func = _ignore_unused_coroutine_pep492(fixture_func, new_sig, func_needs_request)
elif isgeneratorfunction(fixture_func):
if sys.version_info >= (3, 3):
from .pep380 import _ignore_unused_generator_pep380
wrapped_fixture_func = _ignore_unused_generator_pep380(fixture_func, new_sig, func_needs_request)
else:
# generator function (with a yield statement)
@wraps(fixture_func, new_sig=new_sig)
def wrapped_fixture_func(*args, **kwargs):
request = kwargs['request'] if func_needs_request else kwargs.pop('request')
if is_used_request(request):
for res in fixture_func(*args, **kwargs):
yield res
else:
yield NOT_USED
else:
# normal function with return statement
@wraps(fixture_func, new_sig=new_sig)
def wrapped_fixture_func(*args, **kwargs):
Expand All @@ -234,17 +268,6 @@ def wrapped_fixture_func(*args, **kwargs):
else:
return NOT_USED

else:
# generator function (with a yield statement)
@wraps(fixture_func, new_sig=new_sig)
def wrapped_fixture_func(*args, **kwargs):
request = kwargs['request'] if func_needs_request else kwargs.pop('request')
if is_used_request(request):
for res in fixture_func(*args, **kwargs):
yield res
else:
yield NOT_USED

return wrapped_fixture_func


Expand Down
46 changes: 34 additions & 12 deletions src/pytest_cases/fixture_core2.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,25 @@
from makefun import with_signature, add_signature_parameters, remove_signature_parameters, wraps

import pytest
import sys

try: # python 3.3+
from inspect import signature, Parameter
except ImportError:
from funcsigs import signature, Parameter # noqa

try: # native coroutines, python 3.5+
from inspect import iscoroutinefunction
except ImportError:
def iscoroutinefunction(obj):
return False

try: # native async generators, python 3.6+
from inspect import isasyncgenfunction
except ImportError:
def isasyncgenfunction(obj):
return False

try: # type hints, python 3+
from typing import Callable, Union, Any, List, Iterable, Sequence # noqa
from types import ModuleType # noqa
Expand Down Expand Up @@ -528,7 +541,27 @@ def _map_arguments(*_args, **_kwargs):
return _args, _kwargs

# --Finally create the fixture function, a wrapper of user-provided fixture with the new signature
if not isgeneratorfunction(fixture_func):
if isasyncgenfunction(fixture_func)and sys.version_info >= (3, 6):
from .pep525 import _decorate_fixture_plus_asyncgen_pep525
wrapped_fixture_func = _decorate_fixture_plus_asyncgen_pep525(fixture_func, new_sig, _map_arguments)
elif iscoroutinefunction(fixture_func) and sys.version_info >= (3, 5):
from .pep492 import _decorate_fixture_plus_coroutine_pep492
wrapped_fixture_func = _decorate_fixture_plus_coroutine_pep492(fixture_func, new_sig, _map_arguments)
elif isgeneratorfunction(fixture_func):
# generator function (with a yield statement)
if sys.version_info >= (3, 3):
from .pep380 import _decorate_fixture_plus_generator_pep380
wrapped_fixture_func = _decorate_fixture_plus_generator_pep380(fixture_func, new_sig, _map_arguments)
else:
@wraps(fixture_func, new_sig=new_sig)
def wrapped_fixture_func(*_args, **_kwargs):
if not is_used_request(_kwargs['request']):
yield NOT_USED
else:
_args, _kwargs = _map_arguments(*_args, **_kwargs)
for res in fixture_func(*_args, **_kwargs):
yield res
else:
# normal function with return statement
@wraps(fixture_func, new_sig=new_sig)
def wrapped_fixture_func(*_args, **_kwargs):
Expand All @@ -538,17 +571,6 @@ def wrapped_fixture_func(*_args, **_kwargs):
_args, _kwargs = _map_arguments(*_args, **_kwargs)
return fixture_func(*_args, **_kwargs)

else:
# generator function (with a yield statement)
@wraps(fixture_func, new_sig=new_sig)
def wrapped_fixture_func(*_args, **_kwargs):
if not is_used_request(_kwargs['request']):
yield NOT_USED
else:
_args, _kwargs = _map_arguments(*_args, **_kwargs)
for res in fixture_func(*_args, **_kwargs):
yield res

# transform the created wrapper into a fixture
_make_fix = pytest_fixture(scope=scope, params=final_values, autouse=autouse, hook=hook, ids=final_ids, **kwargs)
return _make_fix(wrapped_fixture_func)
57 changes: 42 additions & 15 deletions src/pytest_cases/fixture_parametrize_plus.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@
except ImportError:
from funcsigs import signature, Parameter # noqa

try: # native coroutines, python 3.5+
from inspect import iscoroutinefunction
except ImportError:
def iscoroutinefunction(obj):
return False

try: # native async generators, python 3.6+
from inspect import isasyncgenfunction
except ImportError:
def isasyncgenfunction(obj):
return False

try:
from collections.abc import Iterable
except ImportError: # noqa
Expand All @@ -25,6 +37,7 @@
pass

import pytest
import sys
from makefun import with_signature, remove_signature_parameters, add_signature_parameters, wraps

from .common_mini_six import string_types
Expand Down Expand Up @@ -1059,30 +1072,44 @@ def replace_paramfixture_with_values(kwargs): # noqa
# return
return kwargs

if not isgeneratorfunction(test_func):
# normal test or fixture function with return statement
@wraps(test_func, new_sig=new_sig)
def wrapped_test_func(*args, **kwargs): # noqa
if kwargs.get(fixture_union_name, None) is NOT_USED:
# TODO why this ? it is probably useless: this fixture
# is private and will never end up in another union
return NOT_USED
else:
replace_paramfixture_with_values(kwargs)
return test_func(*args, **kwargs)

if isasyncgenfunction(test_func)and sys.version_info >= (3, 6):
from .pep525 import _parametrize_plus_decorate_asyncgen_pep525
wrapped_test_func = _parametrize_plus_decorate_asyncgen_pep525(test_func, new_sig, fixture_union_name,
replace_paramfixture_with_values)
elif iscoroutinefunction(test_func) and sys.version_info >= (3, 5):
from .pep492 import _parametrize_plus_decorate_coroutine_pep492
wrapped_test_func = _parametrize_plus_decorate_coroutine_pep492(test_func, new_sig, fixture_union_name,
replace_paramfixture_with_values)
elif isgeneratorfunction(test_func):
# generator function (with a yield statement)
if sys.version_info >= (3, 3):
from .pep380 import _parametrize_plus_decorate_generator_pep380
wrapped_test_func = _parametrize_plus_decorate_generator_pep380(test_func, new_sig,
fixture_union_name,
replace_paramfixture_with_values)
else:
@wraps(test_func, new_sig=new_sig)
def wrapped_test_func(*args, **kwargs): # noqa
if kwargs.get(fixture_union_name, None) is NOT_USED:
# TODO why this ? it is probably useless: this fixture
# is private and will never end up in another union
yield NOT_USED
else:
replace_paramfixture_with_values(kwargs)
for res in test_func(*args, **kwargs):
yield res
else:
# generator test or fixture function (with one or several yield statements)
# normal function with return statement
@wraps(test_func, new_sig=new_sig)
def wrapped_test_func(*args, **kwargs): # noqa
if kwargs.get(fixture_union_name, None) is NOT_USED:
# TODO why this ? it is probably useless: this fixture
# is private and will never end up in another union
yield NOT_USED
return NOT_USED
else:
replace_paramfixture_with_values(kwargs)
for res in test_func(*args, **kwargs):
yield res
return test_func(*args, **kwargs)

# move all pytest marks from the test function to the wrapper
# not needed because the __dict__ is automatically copied when we use @wraps
Expand Down
50 changes: 50 additions & 0 deletions src/pytest_cases/pep380.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Authors: Sylvain MARIE <sylvain.marie@se.com>
# + All contributors to <https://github.com/smarie/python-pytest-cases>
#
# License: 3-clause BSD, <https://github.com/smarie/python-pytest-cases/blob/master/LICENSE>

# contains syntax illegal before PEP380 'Syntax for Delegating to a Subgenerator'

from makefun import wraps
from .fixture_core1_unions import is_used_request, NOT_USED


def _ignore_unused_generator_pep380(fixture_func, new_sig, func_needs_request):
@wraps(fixture_func, new_sig=new_sig)
def wrapped_fixture_func(*args, **kwargs):
request = kwargs['request'] if func_needs_request else kwargs.pop('request')
if is_used_request(request):
yield from fixture_func(*args, **kwargs)
else:
yield NOT_USED

return wrapped_fixture_func

def _decorate_fixture_plus_generator_pep380(fixture_func, new_sig, map_arguments):
@wraps(fixture_func, new_sig=new_sig)
def wrapped_fixture_func(*_args, **_kwargs):
if not is_used_request(_kwargs['request']):
yield NOT_USED
else:
_args, _kwargs = map_arguments(*_args, **_kwargs)
yield from fixture_func(*_args, **_kwargs)

return wrapped_fixture_func

def _parametrize_plus_decorate_generator_pep380(
test_func,
new_sig,
fixture_union_name,
replace_paramfixture_with_values
):
@wraps(test_func, new_sig=new_sig)
def wrapped_test_func(*args, **kwargs): # noqa
if kwargs.get(fixture_union_name, None) is NOT_USED:
# TODO why this ? it is probably useless: this fixture
# is private and will never end up in another union
yield NOT_USED
else:
replace_paramfixture_with_values(kwargs)
yield from test_func(*args, **kwargs)

return wrapped_test_func
50 changes: 50 additions & 0 deletions src/pytest_cases/pep492.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Authors: Sylvain MARIE <sylvain.marie@se.com>
# + All contributors to <https://github.com/smarie/python-pytest-cases>
#
# License: 3-clause BSD, <https://github.com/smarie/python-pytest-cases/blob/master/LICENSE>

# contains syntax illegal before PEP492 "Coroutines with async and await syntax"

from makefun import wraps
from .fixture_core1_unions import is_used_request, NOT_USED


def _ignore_unused_coroutine_pep492(fixture_func, new_sig, func_needs_request):
@wraps(fixture_func, new_sig=new_sig)
async def wrapped_fixture_func(*args, **kwargs):
request = kwargs['request'] if func_needs_request else kwargs.pop('request')
if is_used_request(request):
return await fixture_func(*args, **kwargs)
else:
return NOT_USED

return wrapped_fixture_func

def _decorate_fixture_plus_coroutine_pep492(fixture_func, new_sig, map_arguments):
@wraps(fixture_func, new_sig=new_sig)
async def wrapped_fixture_func(*_args, **_kwargs):
if not is_used_request(_kwargs['request']):
return NOT_USED
else:
_args, _kwargs = map_arguments(*_args, **_kwargs)
return await fixture_func(*_args, **_kwargs)

return wrapped_fixture_func

def _parametrize_plus_decorate_coroutine_pep492(
test_func,
new_sig,
fixture_union_name,
replace_paramfixture_with_values
):
@wraps(test_func, new_sig=new_sig)
async def wrapped_test_func(*args, **kwargs): # noqa
if kwargs.get(fixture_union_name, None) is NOT_USED:
# TODO why this ? it is probably useless: this fixture
# is private and will never end up in another union
return NOT_USED
else:
replace_paramfixture_with_values(kwargs)
return await test_func(*args, **kwargs)

return wrapped_test_func
53 changes: 53 additions & 0 deletions src/pytest_cases/pep525.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Authors: Sylvain MARIE <sylvain.marie@se.com>
# + All contributors to <https://github.com/smarie/python-pytest-cases>
#
# License: 3-clause BSD, <https://github.com/smarie/python-pytest-cases/blob/master/LICENSE>

# contains syntax illegal before PEP525 "Asynchronous Generators"

from makefun import wraps
from .fixture_core1_unions import is_used_request, NOT_USED


def _ignore_unused_asyncgen_pep525(fixture_func, new_sig, func_needs_request):
@wraps(fixture_func, new_sig=new_sig)
async def wrapped_fixture_func(*args, **kwargs):
request = kwargs['request'] if func_needs_request else kwargs.pop('request')
if is_used_request(request):
async for res in fixture_func(*args, **kwargs):
yield res
else:
yield NOT_USED

return wrapped_fixture_func

def _decorate_fixture_plus_asyncgen_pep525(fixture_func, new_sig, map_arguments):
@wraps(fixture_func, new_sig=new_sig)
async def wrapped_fixture_func(*_args, **_kwargs):
if not is_used_request(_kwargs['request']):
yield NOT_USED
else:
_args, _kwargs = map_arguments(*_args, **_kwargs)
async for res in fixture_func(*_args, **_kwargs):
yield res

return wrapped_fixture_func

def _parametrize_plus_decorate_asyncgen_pep525(
test_func,
new_sig,
fixture_union_name,
replace_paramfixture_with_values
):
@wraps(test_func, new_sig=new_sig)
async def wrapped_test_func(*args, **kwargs): # noqa
if kwargs.get(fixture_union_name, None) is NOT_USED:
# TODO why this ? it is probably useless: this fixture
# is private and will never end up in another union
yield NOT_USED
else:
replace_paramfixture_with_values(kwargs)
async for res in test_func(*args, **kwargs):
yield res

return wrapped_test_func
Loading