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

Add default filters #184

Merged
merged 9 commits into from
Feb 26, 2021
Merged
Show file tree
Hide file tree
Changes from 6 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
11 changes: 11 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,17 @@ def test_good_datasets2(data):
assert sqrt(data) > 0
```

- pytest-cases offers you an array of default filter for you to choose from in the `filters` module such as: `has_tag`, `has_prefix`, and etc. You can use logical operations on them like "and" (&) "or" (|) and "invert" (~) in order to create you own custom filters.

```python
from pytest_cases import filters

@parametrize_with_cases("data", cases='.',
filter=filters.has_tag("success") & filters.has_prefix("case_b")
def test_good_datasets3(data):
assert sqrt(data) > 0
```


### b- Case functions

Expand Down
57 changes: 57 additions & 0 deletions pytest_cases/filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import re

from .case_info import CaseInfo


class CaseFilter(object):
"""
This class represents a case filter. You can use it in order to filter cases
to be used by `parametrize_by_cases`.

You can join filters with the "and" relation by using & operator and join them
by "or" relation using | operator.
Moreover, you can negate a filter by using ~.
"""

def __init__(self, filter_function):
self.filter_function = filter_function

def __call__(self, case):
return self.filter_function(case)

def __and__(self, other):
return CaseFilter(
lambda case: self(case) and other(case)
)

def __rand__(self, other):
return self & other

def __or__(self, other):
return CaseFilter(
lambda case: self(case) or other(case)
)

def __ror__(self, other):
return self | other

def __invert__(self):
return CaseFilter(
lambda case: not self(case)
)


def has_tag(tag_name):
smarie marked this conversation as resolved.
Show resolved Hide resolved
return CaseFilter(lambda case: tag_name in CaseInfo.get_from(case).tags)


def has_prefix(prefix):
return CaseFilter(lambda case: case.__name__.startswith(prefix))
Copy link
Owner

@smarie smarie Feb 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue here is that parametrize_with_cases already has a prefix argument, and this is always used. Besides, it is used before the filters are called. Finally it does not take into account overridden ids.

So I suggest that we modify this so that it acts on the case id - that will complement nicely the prefix argument in order to work on the case id, not on the case function name.

Suggested change
def has_prefix(prefix):
return CaseFilter(lambda case: case.__name__.startswith(prefix))
def id_has_prefix(prefix):
"""Select cases that have a case id prefix `prefix`.
Note that this is not the prefix of the whole case function name, but the case id, possibly overridden with `@case(id=)`"""
return CaseFilter(lambda case: CaseInfo.get_from(case).id.startswith(prefix))

Note that tests should be updated accordingly, and the "suffix" filter too.



def has_suffix(suffix):
return CaseFilter(lambda case: case.__name__.endswith(suffix))
Copy link
Owner

@smarie smarie Feb 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def has_suffix(suffix):
return CaseFilter(lambda case: case.__name__.endswith(suffix))
def id_has_suffix(suffix):
"""Select cases that have a case id suffix `suffix`.
Note that this is not the suffix of the whole case function name, but the case id, possibly overridden with `@case(id=)`
"""
return CaseFilter(lambda case: CaseInfo.get_from(case).id.endswith(prefix))



def match_regex(regex):
return CaseFilter(lambda case: re.match(regex, case.__name__))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def match_regex(regex):
return CaseFilter(lambda case: re.match(regex, case.__name__))
def id_match_regex(regex):
"""Select cases that have a case id matching `regex`.
Note that this is not a match of the whole case function name, but the case id, possibly overridden with `@case(id=)`
"""
return CaseFilter(lambda case: re.match(regex, CaseInfo.get_from(case).id))

Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from pytest_cases import filters, case, parametrize_with_cases

A = "a"
B = "b"
C = "c"


@case(tags=[A])
def case_one():
return 1


@case(tags=[A, B])
def case_two():
return 2


@case(tags=[B, C])
def case_three():
return 3


@case(tags=[A, C])
def case_four():
return 4


@parametrize_with_cases(
argnames="value", cases=".", filter=filters.has_tag(B)
)
def test_filter_with_tag(value):
print(value)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
print(value)
pass



@parametrize_with_cases(
argnames="value", cases=".", filter=~filters.has_tag(B)
)
def test_filter_without_tag(value):
print(value)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
print(value)
pass



@parametrize_with_cases(
argnames="value", cases=".", filter=filters.has_tag(B) & filters.has_tag(C)
)
def test_filter_with_and_relation(value):
print(value)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
print(value)
pass



@parametrize_with_cases(
argnames="value", cases=".", filter=filters.has_tag(B) | filters.has_tag(C)
)
def test_filter_with_or_relation(value):
print(value)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
print(value)
pass



@parametrize_with_cases(
argnames="value", cases=".", filter=filters.has_prefix("case_t")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above: if you had written "data_t" here you would not be able to retrieve anythin, because prefix="case_" and is applied before filters are executed. We should change this to has_id_prefix as suggested above.

)
def test_filter_with_prefix(value):
print(value)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
print(value)
pass



@parametrize_with_cases(
argnames="value", cases=".", filter=filters.has_suffix("e")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as for prefix: rename id suffix

)
def test_filter_with_suffix(value):
print(value)
Copy link
Owner

@smarie smarie Feb 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
print(value)
pass
def test_synthesis(module_results_dct):
assert list(modeul_results_dct) == [
"test_filter_with_tag[tim]",
"test_filter_with_tag[toni]",
"test_filter_without_tag[tom]",
"test_filter_without_tag[dom]",
"test_filter_with_and_relation[toni]",
"test_filter_with_or_relation[tim]",
"test_filter_with_or_relation[toni]",
"test_filter_with_or_relation[dom]",
"test_filter_with_prefix[tom]",
"test_filter_with_prefix[tim]",
"test_filter_with_prefix[toni]",
"test_filter_with_suffix[tom]",
"test_filter_with_suffix[tim]",
"test_filter_with_suffix[dom]",
]