Skip to content

Commit

Permalink
fix(matchers): enable explicit regex based matching. fix URL matcher.…
Browse files Browse the repository at this point in the history
… several refactors
  • Loading branch information
h2non committed Dec 12, 2016
1 parent b0bfbdf commit e171318
Show file tree
Hide file tree
Showing 8 changed files with 240 additions and 138 deletions.
16 changes: 11 additions & 5 deletions pook/assertion.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,38 +19,44 @@ def equal(x, y):
return TestCase().assertEqual(x, y) or True


def matches(x, y):
def matches(x, y, regex_expr=False):
"""
Tries to match a regular expression value ``x`` against ``y``.
Aliast``unittest.TestCase.assertEqual()``
Arguments:
x (regex|str): regular expression to test.
y (str): value to match.
regex_expr (bool): enables regex string based expression matching.
Raises:
AssertionError: in case of mismatching.
Returns:
bool
"""
x = strip_regex(x) if isregex_expr(x) else x
# Parse regex expression, if needed
x = strip_regex(x) if regex_expr and isregex_expr(x) else x
# Retrieve original regex pattern
x = x.pattern if isregex(x) else x
# Run regex assertion
return TestCase().assertRegexpMatches(x, y) or True


def test(x, y):
def test(x, y, regex_expr=False):
"""
Compares to values based on regular expression matching or
strict equality comparison.
Arguments:
x (regex|str): string or regular expression to test.
y (str): value to match.
regex_expr (bool): enables regex string based expression matching.
Raises:
AssertionError: in case of mismatching.
AssertionError: in case of matching error.
Returns:
bool
"""
return matches(x, y) if isregex(x) else equal(x, y)
return matches(x, y, regex_expr=regex_expr) if isregex(x) else equal(x, y)
14 changes: 9 additions & 5 deletions pook/compare.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,17 @@ def strip_negate(value):
return value[len(NEGATE):].lstrip()


def compare(expr, value):
def compare(expr, value, regex_expr=False):
"""
Compares an string or regular expression againast a given value.
Arguments:
expr (str|regex): string or regular expression value to compare.
value (str): value to compare against to.
regex_expr (bool, optional): enables string based regex matching.
Raises:
AssertionError: in case of assertion error.
Returns:
bool
Expand All @@ -42,15 +46,15 @@ def compare(expr, value):
negate = False
if isinstance(expr, str):
negate = expr.startswith(NEGATE)
if negate:
expr = strip_negate(expr)
expr = strip_negate(expr) if negate else expr

try:
# RegExp or strict equality comparison
test(expr, value)
return True
test(expr, value, regex_expr=regex_expr)
except Exception as err:
if negate:
return True
else:
raise err

return True
87 changes: 1 addition & 86 deletions pook/matchers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,86 +1 @@
from .base import BaseMatcher
from .url import URLMatcher
from .body import BodyMatcher
from .query import QueryMatcher
from .method import MethodMatcher
from .headers import HeadersMatcher
from .path import PathMatcher
from .xml import XMLMatcher
from .json import JSONMatcher
from .json_schema import JSONSchemaMatcher

# Explicit symbols to export
__all__ = (
'matchers', 'init',
'add', 'get',
'BaseMatcher',
'MethodMatcher',
'URLMatcher',
'HeadersMatcher',
'QueryMatcher',
'PathMatcher',
'BodyMatcher',
'XMLMatcher',
'JSONMatcher',
'JSONSchemaMatcher',
'QueryMatcher',
)

# List of built-in matchers
# This is intended to be mutable.
matchers = [
MethodMatcher,
URLMatcher,
HeadersMatcher,
QueryMatcher,
PathMatcher,
BodyMatcher,
XMLMatcher,
JSONMatcher,
JSONSchemaMatcher,
QueryMatcher,
]


def add(*matchers):
"""
Registers one or multiple matchers to be used by default from
mocking engine.
"""
matchers.append(*matchers)


def get(name):
"""
Returns a matcher instance by class or alias name.
Arguments:
name (str): matcher class name or alias.
Returns:
matcher: found matcher instance, otherwise ``None``.
"""
for matcher in matchers:
if matcher.__name__ == name or getattr(matcher, 'name', None) == name:
return matcher


def init(name, *args):
"""
Initializes a matcher instance passing variadic arguments to
its constructor. Acts as a delegator proxy.
Arguments:
name (str): matcher class name or alias to execute.
*args (mixed): variadic argument
Returns:
matcher: matcher instance.
Raises:
ValueError: if matcher was not found.
"""
matcher = get(name)
if not matcher:
raise ValueError('Cannot find matcher: {}'.format(name))
return matcher(*args)
from .api import * # noqa
91 changes: 91 additions & 0 deletions pook/matchers/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
from .base import BaseMatcher
from .url import URLMatcher
from .body import BodyMatcher
from .query import QueryMatcher
from .method import MethodMatcher
from .headers import HeadersMatcher
from .path import PathMatcher
from .xml import XMLMatcher
from .json import JSONMatcher
from .json_schema import JSONSchemaMatcher

# Explicit symbols to export
__all__ = (
'init',
'add',
'get',
'matchers',
'BaseMatcher',
'MethodMatcher',
'URLMatcher',
'HeadersMatcher',
'QueryMatcher',
'PathMatcher',
'BodyMatcher',
'XMLMatcher',
'JSONMatcher',
'JSONSchemaMatcher',
'QueryMatcher',
)

# List of built-in matchers
# This is intended to be mutable.
matchers = [
MethodMatcher,
URLMatcher,
HeadersMatcher,
QueryMatcher,
PathMatcher,
BodyMatcher,
XMLMatcher,
JSONMatcher,
JSONSchemaMatcher,
QueryMatcher,
]


def add(*matcher):
"""
Registers one or multiple matchers to be used by default from
mocking engine.
Arguments:
*matcher (list[pook.BaseMatcher]): variadic matchers to add.
"""
[matchers.append(m) for m in matcher]


def get(name):
"""
Returns a matcher instance by class or alias name.
Arguments:
name (str): matcher class name or alias.
Returns:
matcher: found matcher instance, otherwise ``None``.
"""
for matcher in matchers:
if matcher.__name__ == name or getattr(matcher, 'name', None) == name:
return matcher


def init(name, *args):
"""
Initializes a matcher instance passing variadic arguments to
its constructor. Acts as a delegator proxy.
Arguments:
name (str): matcher class name or alias to execute.
*args (mixed): variadic argument
Returns:
matcher: matcher instance.
Raises:
ValueError: if matcher was not found.
"""
matcher = get(name)
if not matcher:
raise ValueError('Cannot find matcher: {}'.format(name))
return matcher(*args)
43 changes: 8 additions & 35 deletions pook/matchers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from copy import deepcopy
from abc import abstractmethod, ABCMeta
from ..compare import compare
from ..regex import isregex, isregex_expr, strip_regex


class BaseMatcher(object):
Expand All @@ -14,8 +13,6 @@ class BaseMatcher(object):

# Negate matching if necessary
negate = False
# Defines if the matching supports regular expression matching
regexp = False

def __init__(self, expectation, negate=False):
if not expectation:
Expand All @@ -30,7 +27,11 @@ def name(self):

@property
def expectation(self):
return self._expectation if hasattr(self, '_expectation') else None
return self._expectation

@expectation.setter
def expectation(self, value):
self._expectation = value

@abstractmethod
def match(self, request):
Expand All @@ -43,47 +44,19 @@ def match(self, request):
"""
pass

@expectation.setter
def expectation(self, value):
self._expectation = value

def match_regexp(self, re, value):
"""
Matches a regular expression value.
Arguments:
re (str|regex): regular expression value to use.
value (str): value to match.
Returns:
bool
"""
if not isregex(re):
return False

if isregex_expr(re):
re = strip_regex(re)

try:
return bool(re.compile(re).match(value))
except:
return False

def compare(self, value, expectation):
def compare(self, value, expectation, regex_expr=False):
"""
Compares two values with regular expression matching support.
Arguments:
value (mixed): value to compare.
expectation (mixed): value to match.
regex_expr (bool, optional): enables string based regex matching.
Returns:
bool
"""
if not value:
return False

return compare(value, expectation)
return compare(value, expectation, regex_expr=regex_expr)

def to_dict(self):
"""
Expand Down

0 comments on commit e171318

Please sign in to comment.