Pattern matching data structures for Python
Python
Switch branches/tags
Nothing to show
Pull request Compare This branch is 2 commits ahead of eallik:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
patternmatching
.gitignore
LICENSE
README
setup.cfg
setup.py

README

Usage
=====

Call the `patternmatching.match()` function with two arguments:

* Argument 1: the pattern to match against
* Argument 2: the data structure to match

* Optional argument `flatten`: `True` to have the matched values
  flattened, i.e. returned as a flat tuple regardless of their position
  in the structure of the matched data.

Return value:
    if flatten is true:
        match_ok, [matched_value, matched_value, matched_value, ...]
    if flatten is not true:
        match_ok, matched_data_structure

Caveat:
    if all matched values in the pattern are ignored, `match()` does not return a `tuple` but a boolean.
    YES:
        `match_ok = match(PATTERN, DATA)`
    NO:
        `match_ok, = match(PATTERN, DATA)`


Usage in (unit) tests (but also anywhere else)
==============================================

Comparing the return values of functions is tedious if you can't
simply use the `==` operator. For example when a function returns a
tuple and you only care about some parts of the tuple and not the
entire tuple:

    retval = some_function_under_test()
    assert retval and isinstance(retval, tuple) \
        and len(retval) == 3 and retval[0] == 'foo' \
        and isinstance(retval[1], tuple) and len(retval[1]) == 2 \
        and isinstance(retval[1][1], SomeException)

With pattern matching:

    retval = some_function_under_test()
    assert retval == ('foo', (ANY, IS_INSTANCE(SomeException)), ANY)


Examples
========

MATCH OK
--------

match_ok, value = match(ANY, 'foobar')
assert match_ok and value == 'foobar'

match_ok = match(IGNORE(ANY), 'foobar')
assert match_ok

match_ok, value1, value2 match((ANY, ANY), ('foo', 'bar'))
assert match_ok and value1 == 'foo' and value2 == 'bar'

match_ok, (value1, value2) = match((ANY, ANY), ('foo', 'bar'), flatten=False)
assert match_ok and value1 == 'foo' and value2 == 'bar'

match_ok, (value1, ) = match((ANY, IGNORE(ANY)), ('foo', 'whatev'))
assert match_ok and value1 == 'foo'

match_ok, value1, value2 = match(('foo', ANY, ANY), ('foo', 1, 2))
assert match_ok and value1 == 1 and value2 == 2

match_ok, value1, value2, value3 = match(('foo', ANY, (ANY, ANY)), ('foo', 1, (2, 3)))
assert match_ok and value1 == 1 and value2 == 2 and value3 = 3

match_ok, (value1, (value2, value3)) = match(('foo', ANY, (ANY, ANY)), ('foo', 1, (2, 3)), flatten=False)
assert match_ok and value1 == 1 and value2 == 2 and value3 = 3

NO MATCH
--------

match_ok = match(3, 4)
assert not match_ok

# notice how you can still successfully do unpacking of return values and just ignore `value` if the match failed
match_ok, value = match(IS_INSTANCE(unicode), '123132')
assert not match_ok

# notice how you can still successfully do unpacking of return values and just ignore `value` if the match failed
match_ok, (value1, (value2, value3)) = match(('foo', (ANY, (ANY, ANY))), ('not-foo', (1, (2, 3))))
assert not match_ok

# ...even when the structure of the data completely mismatches
match_ok, (value1, (value2, value3)) = match(('foo', (ANY, (ANY, ANY))), ('foo', 'blabla'))
assert not match_ok

# ...don't rely on `value1`, `value2` and `value3` being `None` though--the matcher can still return whatever it wants there; you have to check `match_ok` yourself.