matchlib

A tool for partial comparison of (nested) data structures


Keywords
fuzzy-data-matching, partial-comparison, python-tests
License
MIT
Install
pip install matchlib==0.2.3

Documentation

matchlib

PyPI version

This package provides a handy way to partially compare python data structures (typically nested lists/dictionaries).

Installation

pip install matchlib

Usage

from matchlib import matches

user = {
    'id': 42,
    'name': 'John Doe',
    'email': 'johndoe@gmail.com',
    'posts': [
        {
            'id': 1,
            'text': 'some text'
        },
        {
            'id': 2,
            'text': 'lorem ipsum',
            'comments': [42, 142, 242]
        }
    ]
}

assert matches(
    user,
    {
        'id': ...,
        'name': 'John Doe',
        'email': 'johndoe@gmail.com',
        ...: ...
    }
)

Same can be achieved using standard == operator with matchlib.Partial object:

from matchlib import Partial

assert user == Partial({
    'id': 42,
    'email': 'johndoe@gmail.com',
    ...: ...
})

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}

Some more hacks

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': 'user42@domain.com',
    }
}

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.