Skip to content
51 changes: 51 additions & 0 deletions docs/api_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,57 @@ def case_hi():
- `marks`: optional pytest marks to add on the case. Note that decorating the function directly with the mark also works, and if marks are provided in both places they are merged.


### `@with_case_tags`

```python
@with_case_tags(*tags, # type: Any
):
```

This decorator can be applied to a class defining cases to apply multiple
`*tags` to all case methods defined thereby.

```python
@with_case_tags('tag_1', 'tag_2')
class CasesContainerClass:

def case_one(self, ...):
...

@case(tags='another_tag')
def case_two(self, ...):
...

@case(tags='tag_1')
def case_three(self, ...):
...
```

This is equivalent to:


```python
class CasesContainerClass:

@case(tags=('tag_1', 'tag_2'))
def case_one(self, ...):
...

@case(tags=('another_tag', 'tag_1', 'tag_2'))
def case_two(self, ...):
...

@case(tags=('tag_1', 'tag_2'))
def case_three(self, ...):
...
```

**Parameters:**

- `tags`: custom tags to be added to all case methods. See also [`@case(tags=...)`](#case).



### `copy_case_info`

```python
Expand Down
11 changes: 11 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

### 3.10.0 - (in progress) New `with_case_tags` decorator

- Added the `with_case_tags` decorator for applying common tags to all cases
defined in a case class. Fixes [#351](https://github.com/smarie/python-pytest-cases/issues/351).
PR [#361](https://github.com/smarie/python-pytest-cases/pull/361)
by [@michele-riva](https://github.com/michele-riva).

### 3.9.1 - support for python 3.14 and pytest 8.4

- Fixed `AttributeError: 'MiniMetafunc' object has no attribute '_params_directness'` when a case function is
Expand All @@ -12,6 +19,10 @@
[#186](https://github.com/smarie/python-pytest-cases/issues/186)
- Fixed test suite for python 3.14, officializing the support for this version.

### 3.9.0 - yanked version

This version was yanked. See 3.9.1.

### 3.8.6 - compatibility fix

- Fixed issue with legacy python 2.7 and 3.5. Fixes [#352](https://github.com/smarie/python-pytest-cases/issues/352).
Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ def test_bad_datasets(data, err_type, err_msg):
```


- the `has_tag` argument allows you to filter cases based on tags set on case functions using the `@case` decorator. See API reference of [`@case`](./api_reference.md#case) and [`@parametrize_with_cases`](./api_reference.md#parametrize_with_cases).
- the `has_tag` argument allows you to filter cases based on tags set on case functions using the `@case` decorator. See API reference of [`@case`](./api_reference.md#case) and [`@parametrize_with_cases`](./api_reference.md#parametrize_with_cases). Tags shared by multiple cases grouped inside a class may be added automatically to all cases using the [`@with_case_tags`](./api_reference.md#with_case_tags) decorator.


```python
Expand Down
3 changes: 2 additions & 1 deletion src/pytest_cases/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from .fixture_parametrize_plus import pytest_parametrize_plus, parametrize_plus, parametrize, fixture_ref

from .case_funcs import case, copy_case_info, set_case_id, get_case_id, get_case_marks, \
get_case_tags, matches_tag_query, is_case_class, is_case_function
get_case_tags, matches_tag_query, is_case_class, is_case_function, with_case_tags
from .case_parametrizer_new import parametrize_with_cases, THIS_MODULE, get_all_cases, get_parametrize_args, \
get_current_case_id, get_current_cases, get_current_params, CasesCollectionWarning

Expand Down Expand Up @@ -53,6 +53,7 @@
# case functions
'case', 'copy_case_info', 'set_case_id', 'get_case_id', 'get_case_marks',
'get_case_tags', 'matches_tag_query', 'is_case_class', 'is_case_function',
'with_case_tags',
# test functions
'get_all_cases', 'parametrize_with_cases', 'THIS_MODULE', 'get_parametrize_args', 'get_current_case_id',
'get_current_cases', 'get_current_params', 'CasesCollectionWarning'
Expand Down
32 changes: 32 additions & 0 deletions src/pytest_cases/case_funcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,3 +366,35 @@ def is_case_function(f, # type: Any
except:
# GH#287: safe fallback
return False


def with_case_tags(*tags):
"""Attach `tags` to all cases defined in the decorated class."""
def _decorator(cls):
if is_case_function(cls):
raise ValueError(
'Cannot use `with_case_tags` on a case '
'function. Use the `@case` decorator instead.'
)
if not is_case_class(cls):
raise ValueError('`with_case_tags` can only be applied to classes '
'defining a collection of cases.')
for case_name in dir(cls):
case_ = getattr(cls, case_name)
if not is_case_function(case_): # Not a case
continue
try:
case_info = getattr(case_, CASE_FIELD)
except AttributeError:
# Not explicitly decorated with @case. Do so now.
# NB: `case(obj) is obj`, i.e., the `@case` decorator
# only adds some attributes to `obj`. In the future, if
# `@case` will return a different object, we will have
# to `setattr(cls, case_name, case_mod)`
_ = case(case_)
case_info = getattr(case_, CASE_FIELD)
tags_to_add = tuple(t for t in tags if t not in case_info.tags)
case_info.add_tags(tags_to_add)
return cls
return _decorator

Loading