This package provides a handy way to partially compare python data structures (typically nested lists/dictionaries).
pip install matchlib
from matchlib import matches
user = {
'id': 42,
'name': 'John Doe',
'email': '[email protected]',
'posts': [
{
'id': 1,
'text': 'some text'
},
{
'id': 2,
'text': 'lorem ipsum',
'comments': [42, 142, 242]
}
]
}
assert matches(
user,
{
'id': ...,
'name': 'John Doe',
'email': '[email protected]',
...: ...
}
)
Same can be achieved using standard ==
operator with matchlib.Partial
object:
from matchlib import Partial
assert user == Partial({
'id': 42,
'email': '[email protected]',
...: ...
})
The ...
"wildcard" could be placed at any nested level.
Let's say we only need to check that comment 142
is present in specific post:
assert user == Partial({
'posts': [
...,
{
'id': 2,
'comments': [..., 142, ...],
...: ...
}
],
...: ...
})
Matching rules are simple:
- In lists and tuples
...
matches zero or more elements and order is preserved:Partial([1, 2, ...]) == [1, 2, 3, 4] Partial([1, 2, ...]) == [1, 2] Partial([1, 2, ...]) != [0, 1, 2] Partial([1, 2, ...]) != [2, 1]
- Same for the sets except they are unordered:
Partial({1, 2, ...}) == {1, 2} Partial({1, 2, ...}) == {0, 1, 2, 3} Partial({1, 2, ...}) != {0, 1, 3}
- As dict value
...
matches any object:Partial({'a': 1, 'b': ...}) == {'a': 1, 'b': 2}
- As dict key
...
matches any key if assosiated values match:Partial({'a': 1, ...: 2}) == {'a': 2, 'b': 2}
- When passed as both key and value matches zero or more arbitrary key-value pairs:
Partial({'a': 1, ...: ...}) == {'a': 1, 'b': 2, 'c': 3}
mathchlib
provides a Regex
object that allows to match an arbitrary string element
(except if it is a dict key) against a regular expression.
Also pytest.approx
is supported for floating-point numbers comparison:
from pytest import approx
from matchlib import Regex, Partial
account = {
'id': 1,
'balance': 1007.62,
'owner': {
'email': '[email protected]',
}
}
assert account == Partial({
'id': ...,
'balance': approx(1000, 0.1),
'owner': {
'email': Regex(r'\w+@domain\.com')
}
})
If for any reason you dislike Ellipsis literal (...
)
a matchlib.Any
object can be used interchangeably.