Usage examples

Here we provide some brief usage examples of the code.

Importing, compiling, and validating a kmatch pattern

The K object can be imported directly from kmatch

from kmatch import K

Once imported, simply instantiating the K object performs validation on the pattern. It also compiles any regular expressions that are in the pattern for faster match function calls.

k = K(['>', 'k1', 2])

Performing simple value filters

Simple value filters can be used directly when a logical operator is not needed. For example, performing a regex match on a key that has the ‘@’ symbol:

print K(['=~', 'k', '.*@.*']).match({'k': 'person@mail.com'})
True

This also applies to filtering numerical values:

print K(['>', 'num', 15]).match({'num': 16})
True

print K(['<=', 'num', 7]).match({'num': 8})
False

Performing simple key filters

Simple key filters can also be done to match dictionaries that have a key named ‘k1’:

print K(['?', 'k1']).match({'k1': True})
True

The inverse can also be checked:

print K(['!?', 'k1']).match({'k2': 'value'})
True

In the above example, ‘k1’ does not exist in the matched dictionary, so True is returned.

Performing logical operations across filters

Filters can be combined with the & (AND) logical operator for more complex filtering:

print K(['&', [
    ['>', 'k1', 0],
    ['<=', 'k2', 5],
]]).match({
    'k1': 50,
    'k2': 1,
})
True

The same can be done with the | (OR) logical operator:

print K(['|', [
    ['==', 'k1', 'Hello'],
    ['==', 'k1', 'World'],
    ['==', 'k1', '!'],
]]).match({'k1': 'Hi'})
False

The ! (NOT) logical operator operates on a single filter (or logical combination of filters):

print K(['!', ['==', 'k', 0]]).match({'k', 1})
True

Operators can be combined in various ways to form more complex patterns like so:

print K(['|', [
    ['&', [
        ['==', 'k1', 3],
        ['==', 'k2', 4],
    ]],
    ['!', ['=~', 'val', '.*Hello.*']],
]]).match({
    'k1': 4,
    'k2': 5,
    'val', 'Hi',
})
True

Filtering with non-extant keys and suppressing KeyErrors

If keys from a kmatch pattern do not exist in the matched dictionary, the default behavior is to throw a KeyError exception:

K(['==', 'k1', 5]).match({'k2': 1})
Traceback (most recent call last):
    # Traceback message here ...
KeyError: 'k1'

This behavior, however, is not always desirable when matching dictionaries that have many keys that may or may not exist. It can be cumbersome to always have to check for existence along with doing filtering. To avoid this scenario, the K object comes with an optional suppress_key_errors flag that defaults to False. If set to True, the value False will be returned any time a key does not exist for an associated filter instead of a KeyError being raised.

Take our previous example, except with suppress_key_errors set to True.

print K(['==', 'k1', 5], suppress_key_errors=True).match({'k2': 1})
False

Using the test mixin

The KmatchTestMixin can be used for test classes when you want to verify a dictionary matches a particular pattern.

from unittest import TestCase
from kmatch import KmatchTestMixin


class MyTestClass(KmatchTestMixin, TestCase):
    def my_test(self):
        self.assertKmatches(['<=', 'f', 0], {'f': -1})

    def my_opposite_test(self):
        with self.assertRaises(AssertionError):
            self.assertNotKmatches(['<=', 'f', 0], {'f': -1})

        self.assertNotKmatches(['<=', 'f', 0], {'g': 1})

Note

The suppress_key_errors parameter is set to False by default for .assertKmatches(), and True for .assertNotKmatches().