Skip to main content

Handy wrappers for advanced object comparing and matching.

Project description

compr - flexible comparing and matching

Rationale

compr is a collection of wrappers (comparators) that enable flexible and complex comparing scenarios via simple code.

Comparators bind value(s) AND compare logic into single object. Quite similar to what pytest.approx(...) does.

Comparators redefine __eq__. The goal: exploit == to simplify underlying code (mainly by getting rid of extra if-else's later). Works well with assertions, filters, dispatchers etc.

Human readable __repr__ is also available!

Installation

pip install compr

List available comparator functions

>>> import compr
>>> list(compr._COMPARATORS)

Usage

We create comparators via functions in compr module:

>>> import copmr
>>> contains_5 = compr.contains(5)  # matches iterable with 5 as one of elements
>>> [3, 4, 6,] == contains_5
False
>>> range(10) == contains_5
True

The real profit comes by using comparators as arguments. So underlying handlers can stay simple (check only ==). While flexibility is achieved by providing different comparators when needed.

Please see examples (also the one with kwargs2compr demonstrates in-depth usage scenario).

NOTE: These examples are intentionally synthetic for better illustration.

Example 0. Basic assert (note the assert message):

# Assert actual value is greater than 5
>>> import compr
>>> expected = compr.gt(5)  # gt stands for greater than as in __gt__
>>> actual = 3
>>> assert expected == actual, f'{actual=} does not match {expected=}'
...
AssertionError: actual=3 does not match expected=gt(5)

Example 1. Searhing list by condition:

# Find index of the first word starting with 't'
>>> from compr import startswith
>>> words = 'one', 'two', 'three', 'four'
>>> words.index(startswith('t'))
1

Example 2: Validate HTTP response object attributes

Imagine requests-like response object:

>>> dir(response)
['status', 'headers', 'body', 'body_len'] # int, dict, str, int respectively

Without compr validator with parateters may look like:

>>> def validate(response, **kwargs):
>>>     """
>>>     Check if all given kwargs equal to response attributes
>>>     """
>>>     for k, v in kwargs.items():
>>>         if getattr(response, k) == v:
>>>             continue
>>>         else:
>>>             raise AssertionError(k, v)

Usage (check if status is 200 and body is 'asdf'):

>>> validate(response, status=200, body='asdf')

Now let's expand our requirements. We need to check if:

  • body_len is between 300 and 400 bytes (inclusive)
  • status code is < 206.

Pushing compare logic into validator above will eventually overwhelm it with complex code, extra arguments or even magic.

Instead we may keep method above unchanged and use comparators:

>>> from compr import lt, within  # less-than, fits-range
>>> validate(response, status=lt(206), body_len=within(300, 400))

Now when validate tries to == status, it will actually check if its less-than 206. Similarly with == body_len, it will check if it fits into range [300..400].

Profit!

Few more steps can make things prettier (introducing kwargs2compr):

>>> from compr import kwargs2compr
>>>
>>> def validate(response, **kwargs):
>>>     for k, v in kwargs2compr(kwargs):  # arg name parsing kicks in here
>>>     # allow kwargs to be interpreted as
>>>     # <attribute-name>_<comparator-func-name>=<comparator-func-argument>
>>>         if getattr(response, k) == v:
>>>             continue
>>>         else:
>>>             raise AssertionError(k, v)

And usage becomes:

>>> validate(response, status_lt=206, body_len_within=[300, 400])

After more refactoring (see all_attrs docstring for help):

>>> from compr import all_attrs
>>>
>>> def validate(response, **kwargs):
>>>         if not all_attrs(response, kwargs2compr(kwargs)):
>>>             raise AssertionError(response, kwargs)

Example 3: Combining multiple comparators

2 special functions: match_all and match_any allow including other comparators to check against complex conditions

# Check if list shorter than 3 elements OR contains 1
>>> from compr import match_any, shorter_than, contains
>>> [2, 3] == match_any(shorter_than(3), contains(1))
True
>>> [1, 2, 3, 5] == match_any(shorter_than(3), contains(1))
True
>>> [2, 3, 5, 6, 7] == match_any(shorter_than(3), contains(1))
False

Creating Your own comparator object:

@compr.comparator
def somewhat_equal(actual, expected):
    """Compare with 10% precision"""
    return 0.9 * expected < actual < 1.1 * expected

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

compr-0.1.4.tar.gz (5.1 kB view details)

Uploaded Source

Built Distribution

compr-0.1.4-py3-none-any.whl (5.2 kB view details)

Uploaded Python 3

File details

Details for the file compr-0.1.4.tar.gz.

File metadata

  • Download URL: compr-0.1.4.tar.gz
  • Upload date:
  • Size: 5.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.9.13

File hashes

Hashes for compr-0.1.4.tar.gz
Algorithm Hash digest
SHA256 f455476a22cec17eb9c2f356f7a86e3089de257d0b096e5879e4aeaa35355803
MD5 4efaf0ae2487a83af198ca704ad77409
BLAKE2b-256 415a73321bb94e958c9304e7fafaff404c17e1eb15adfe0262e7406a049371a2

See more details on using hashes here.

File details

Details for the file compr-0.1.4-py3-none-any.whl.

File metadata

  • Download URL: compr-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 5.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.9.13

File hashes

Hashes for compr-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 de4261e31b981bd868602b01a132f0c223d404ac1dd1362615e0808b2d63087a
MD5 c14efacf61a4301a624cb52ffcbb486e
BLAKE2b-256 4bf907eb8c00900e3f8f69e491662caed42f913d8bc7663a348969500019e789

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page