Skip to content

Commit 3c8b16a

Browse files
smarieSylvain MARIE
andauthored
Feature/issues 243 plus (#247)
* Completed fix for #243: now also with cases having parameters or requiring fixtures. Made the test only valid in python 3. * Fixed version flag in develop mode - there was a bug due to the folder refactoring. * Added support for the new Scopes enum in pytest 7: `fixdef.scopenum` and `callspec._arg2scopenum` are now `fixdef._scope` and `callspec._arg2scope` and contain a `Scope`. Sorting of fixture defs is now done according to the reversed order of Scope enum instances. Not yet tested with pytest-7.0.0rc1. (hopefully) Fixed #241 * Changelog 3.6.7 Co-authored-by: Sylvain MARIE <[email protected]>
1 parent 971e86f commit 3c8b16a

File tree

8 files changed

+91
-44
lines changed

8 files changed

+91
-44
lines changed

docs/changelog.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
# Changelog
22

3-
### 3.6.7 In Progress - Minor improvements
3+
### 3.6.7 - Minor improvements and preparing for pytest 7
44

55
- Improved error message when a case function nested in a class has no `self` argument and is not static. Fixes [#243](https://github.com/smarie/python-pytest-cases/issues/243)
6+
- Added support for the new Scopes enum in pytest 7. Fixed [#241](https://github.com/smarie/python-pytest-cases/issues/241)
7+
- Fixed `__version__` in development mode.
68

79
### 3.6.6 - Layout change
810

src/pytest_cases/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
# use setuptools_scm to get the current version from src using git
2424
from setuptools_scm import get_version as _gv
2525
from os import path as _path
26-
__version__ = _gv(_path.join(_path.dirname(__file__), _path.pardir))
26+
__version__ = _gv(_path.join(_path.dirname(__file__), _path.pardir, _path.pardir))
2727

2828

2929
AUTO2 = AUTO

src/pytest_cases/case_parametrizer_new.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -824,7 +824,7 @@ def _of_interest(x): # noqa
824824
# ignore any error here, this is optional.
825825
pass
826826
else:
827-
if len(s.parameters) < 1:
827+
if len(s.parameters) < 1 or (tuple(s.parameters.keys())[0] != "self"):
828828
raise TypeError("case method is missing 'self' argument but is not static: %s" % m)
829829
# partialize the function to get one without the 'self' argument
830830
new_m = functools.partial(m, cls())

src/pytest_cases/common_pytest.py

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -521,18 +521,37 @@ def get_pytest_nodeid(metafunc):
521521

522522

523523
try:
524-
from _pytest.fixtures import scopes as pt_scopes
524+
# pytest 7+ : scopes is an enum
525+
from _pytest.scope import Scope
526+
527+
def get_pytest_function_scopeval():
528+
return Scope.Function
529+
530+
def has_function_scope(fixdef):
531+
return fixdef._scope is Scope.Function
532+
533+
def set_callspec_arg_scope_to_function(callspec, arg_name):
534+
callspec._arg2scope[arg_name] = Scope.Function
535+
525536
except ImportError:
526-
# pytest 2
527-
from _pytest.python import scopes as pt_scopes, Metafunc # noqa
537+
try:
538+
# pytest 3+
539+
from _pytest.fixtures import scopes as pt_scopes
540+
except ImportError:
541+
# pytest 2
542+
from _pytest.python import scopes as pt_scopes
528543

544+
# def get_pytest_scopenum(scope_str):
545+
# return pt_scopes.index(scope_str)
529546

530-
def get_pytest_scopenum(scope_str):
531-
return pt_scopes.index(scope_str)
547+
def get_pytest_function_scopeval():
548+
return pt_scopes.index("function")
532549

550+
def has_function_scope(fixdef):
551+
return fixdef.scopenum == get_pytest_function_scopeval()
533552

534-
def get_pytest_function_scopenum():
535-
return pt_scopes.index("function")
553+
def set_callspec_arg_scope_to_function(callspec, arg_name):
554+
callspec._arg2scopenum[arg_name] = get_pytest_function_scopeval() # noqa
536555

537556

538557
from _pytest.python import _idval # noqa

src/pytest_cases/common_pytest_marks.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
PYTEST54_OR_GREATER = PYTEST_VERSION >= LooseVersion('5.4.0')
4242
PYTEST421_OR_GREATER = PYTEST_VERSION >= LooseVersion('4.2.1')
4343
PYTEST6_OR_GREATER = PYTEST_VERSION >= LooseVersion('6.0.0')
44+
PYTEST7_OR_GREATER = PYTEST_VERSION >= LooseVersion('7.0.0')
4445

4546

4647
def get_param_argnames_as_list(argnames):

src/pytest_cases/plugin.py

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@
2828

2929
from .common_mini_six import string_types
3030
from .common_pytest_lazy_values import get_lazy_args
31-
from .common_pytest_marks import PYTEST35_OR_GREATER, PYTEST46_OR_GREATER, PYTEST37_OR_GREATER
32-
from .common_pytest import get_pytest_nodeid, get_pytest_function_scopenum, is_function_node, get_param_names, \
33-
get_param_argnames_as_list
31+
from .common_pytest_marks import PYTEST35_OR_GREATER, PYTEST46_OR_GREATER, PYTEST37_OR_GREATER, PYTEST7_OR_GREATER
32+
from .common_pytest import get_pytest_nodeid, get_pytest_function_scopeval, is_function_node, get_param_names, \
33+
get_param_argnames_as_list, has_function_scope, set_callspec_arg_scope_to_function
3434

3535
from .fixture_core1_unions import NOT_USED, USED, is_fixture_union_params, UnionFixtureAlternative
3636

@@ -187,12 +187,22 @@ def get_all_fixture_defs(self, drop_fake_fixtures=True, try_to_sort=True):
187187
items = self.gen_all_fixture_defs(drop_fake_fixtures=drop_fake_fixtures)
188188

189189
# sort by scope as in pytest fixture closure creator (pytest did not do it in early versions, align with this)
190-
if try_to_sort and PYTEST35_OR_GREATER:
191-
f_scope = get_pytest_function_scopenum()
192-
def sort_by_scope(kv_pair): # noqa
193-
fixture_name, fixture_defs = kv_pair
194-
return fixture_defs[-1].scopenum if fixture_defs is not None else f_scope
195-
items = sorted(list(items), key=sort_by_scope)
190+
if try_to_sort:
191+
if PYTEST7_OR_GREATER:
192+
# Scope is an enum, values are in reversed order, and the field is _scope
193+
f_scope = get_pytest_function_scopeval()
194+
def sort_by_scope(kv_pair):
195+
fixture_name, fixture_defs = kv_pair
196+
return fixture_defs[-1]._scope if fixture_defs is not None else f_scope
197+
items = sorted(list(items), key=sort_by_scope, reverse=True)
198+
199+
elif PYTEST35_OR_GREATER:
200+
# scopes is a list, values are indices in the list, and the field is scopenum
201+
f_scope = get_pytest_function_scopeval()
202+
def sort_by_scope(kv_pair): # noqa
203+
fixture_name, fixture_defs = kv_pair
204+
return fixture_defs[-1].scopenum if fixture_defs is not None else f_scope
205+
items = sorted(list(items), key=sort_by_scope)
196206

197207
return OrderedDict(items)
198208

@@ -562,7 +572,7 @@ def _update_fixture_defs(self):
562572

563573
# # also sort all partitions (note that we cannot rely on the order in all_fixture_defs when scopes are same!)
564574
# if LooseVersion(pytest.__version__) >= LooseVersion('3.5.0'):
565-
# f_scope = get_pytest_function_scopenum()
575+
# f_scope = get_pytest_function_scopeval()
566576
# for p in self.partitions:
567577
# def sort_by_scope2(fixture_name): # noqa
568578
# fixture_defs = all_fixture_defs[fixture_name]
@@ -1031,14 +1041,13 @@ def _cleanup_calls_list(metafunc,
10311041
# create ref lists of fixtures per scope
10321042
_not_always_used_func_scoped = []
10331043
# _not_always_used_other_scoped = []
1034-
_function_scope_num = get_pytest_function_scopenum()
10351044
for fixture_name in fix_closure_tree.get_not_always_used():
10361045
try:
10371046
fixdef = metafunc._arg2fixturedefs[fixture_name] # noqa
10381047
except KeyError:
10391048
continue # dont raise any error here and let pytest say "not found" later
10401049
else:
1041-
if fixdef[-1].scopenum == _function_scope_num:
1050+
if has_function_scope(fixdef[-1]):
10421051
_not_always_used_func_scoped.append(fixture_name)
10431052
# else:
10441053
# _not_always_used_other_scoped.append(fixture_name)
@@ -1078,12 +1087,12 @@ def _cleanup_calls_list(metafunc,
10781087
# explicitly add it as discarded by creating a parameter value for it.
10791088
c.params[fixture_name] = NOT_USED
10801089
c.indices[fixture_name] = 1
1081-
c._arg2scopenum[fixture_name] = _function_scope_num # get_pytest_scopenum(fixdef[-1].scope) # noqa
1090+
set_callspec_arg_scope_to_function(c, fixture_name)
10821091
else:
10831092
# explicitly add it as active
10841093
c.params[fixture_name] = USED
10851094
c.indices[fixture_name] = 0
1086-
c._arg2scopenum[fixture_name] = _function_scope_num # get_pytest_scopenum(fixdef[-1].scope) # noqa
1095+
set_callspec_arg_scope_to_function(c, fixture_name)
10871096

10881097
# finally, if there are some session or module-scoped fixtures that
10891098
# are used in *none* of the calls, they could be deactivated too

tests/cases/issues/test_issue_243.py

Lines changed: 0 additions & 20 deletions
This file was deleted.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import pytest
2+
3+
from pytest_cases import parametrize_with_cases, fixture
4+
5+
6+
def test_missing_self():
7+
class MyCases:
8+
def case_forgot_self() -> int:
9+
return 456
10+
11+
with pytest.raises(TypeError) as exc_info:
12+
@parametrize_with_cases(argnames="expected", cases=MyCases)
13+
def test_foo(expected):
14+
pass
15+
16+
assert str(exc_info.value) == ("case method is missing 'self' argument but is not static: %s"
17+
% MyCases.case_forgot_self)
18+
19+
20+
@fixture
21+
def a():
22+
return
23+
24+
25+
def test_missing_self_params():
26+
class MyCases:
27+
def case_fix_forgot_self(a) -> int:
28+
return a
29+
30+
with pytest.raises(TypeError) as exc_info:
31+
@parametrize_with_cases(argnames="expected", cases=MyCases)
32+
def test_foo(expected):
33+
pass
34+
35+
assert str(exc_info.value) == ("case method is missing 'self' argument but is not static: %s"
36+
% MyCases.case_fix_forgot_self)

0 commit comments

Comments
 (0)