Ausgabe der neuen DB Einträge
This commit is contained in:
parent
bad48e1627
commit
cfbbb9ee3d
2399 changed files with 843193 additions and 43 deletions
7
venv/lib/python3.9/site-packages/hamcrest/__init__.py
Normal file
7
venv/lib/python3.9/site-packages/hamcrest/__init__.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
from hamcrest.core import *
|
||||
from hamcrest.library import *
|
||||
|
||||
__version__ = "2.0.2"
|
||||
__author__ = "Chris Rose"
|
||||
__copyright__ = "Copyright 2020 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
from hamcrest.core.assert_that import assert_that
|
||||
from hamcrest.core.core import *
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
import warnings
|
||||
from typing import Optional, TypeVar, cast, overload
|
||||
|
||||
from hamcrest.core.matcher import Matcher
|
||||
from hamcrest.core.string_description import StringDescription
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
# unittest integration; hide these frames from tracebacks
|
||||
__unittest = True
|
||||
# py.test integration; hide these frames from tracebacks
|
||||
__tracebackhide__ = True
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
@overload
|
||||
def assert_that(actual: T, matcher: Matcher[T], reason: str = "") -> None:
|
||||
...
|
||||
|
||||
|
||||
@overload
|
||||
def assert_that(assertion: bool, reason: str = "") -> None:
|
||||
...
|
||||
|
||||
|
||||
def assert_that(actual, matcher=None, reason=""):
|
||||
"""Asserts that actual value satisfies matcher. (Can also assert plain
|
||||
boolean condition.)
|
||||
|
||||
:param actual: The object to evaluate as the actual value.
|
||||
:param matcher: The matcher to satisfy as the expected condition.
|
||||
:param reason: Optional explanation to include in failure description.
|
||||
|
||||
``assert_that`` passes the actual value to the matcher for evaluation. If
|
||||
the matcher is not satisfied, an exception is thrown describing the
|
||||
mismatch.
|
||||
|
||||
``assert_that`` is designed to integrate well with PyUnit and other unit
|
||||
testing frameworks. The exception raised for an unmet assertion is an
|
||||
:py:exc:`AssertionError`, which PyUnit reports as a test failure.
|
||||
|
||||
With a different set of parameters, ``assert_that`` can also verify a
|
||||
boolean condition:
|
||||
|
||||
.. function:: assert_that(assertion[, reason])
|
||||
|
||||
:param assertion: Boolean condition to verify.
|
||||
:param reason: Optional explanation to include in failure description.
|
||||
|
||||
This is equivalent to the :py:meth:`~unittest.TestCase.assertTrue` method
|
||||
of :py:class:`unittest.TestCase`, but offers greater flexibility in test
|
||||
writing by being a standalone function.
|
||||
|
||||
"""
|
||||
if isinstance(matcher, Matcher):
|
||||
_assert_match(actual=actual, matcher=matcher, reason=reason)
|
||||
else:
|
||||
if isinstance(actual, Matcher):
|
||||
warnings.warn("arg1 should be boolean, but was {}".format(type(actual)))
|
||||
_assert_bool(assertion=cast(bool, actual), reason=cast(str, matcher))
|
||||
|
||||
|
||||
def _assert_match(actual: T, matcher: Matcher[T], reason: str) -> None:
|
||||
if not matcher.matches(actual):
|
||||
description = StringDescription()
|
||||
description.append_text(reason).append_text("\nExpected: ").append_description_of(
|
||||
matcher
|
||||
).append_text("\n but: ")
|
||||
matcher.describe_mismatch(actual, description)
|
||||
description.append_text("\n")
|
||||
raise AssertionError(description)
|
||||
|
||||
|
||||
def _assert_bool(assertion: bool, reason: Optional[str] = None) -> None:
|
||||
if not assertion:
|
||||
if not reason:
|
||||
reason = "Assertion failed"
|
||||
raise AssertionError(reason)
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
from typing import Any, Iterable
|
||||
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.helpers.hasmethod import hasmethod
|
||||
from hamcrest.core.helpers.ismock import ismock
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
|
||||
class BaseDescription(Description):
|
||||
"""Base class for all :py:class:`~hamcrest.core.description.Description`
|
||||
implementations.
|
||||
|
||||
"""
|
||||
|
||||
def append_text(self, text: str) -> Description:
|
||||
self.append(text)
|
||||
return self
|
||||
|
||||
def append_description_of(self, value: Any) -> Description:
|
||||
if not ismock(value) and hasmethod(value, "describe_to"):
|
||||
value.describe_to(self)
|
||||
elif isinstance(value, str):
|
||||
self.append(repr(value))
|
||||
else:
|
||||
description = str(value)
|
||||
if description[:1] == "<" and description[-1:] == ">":
|
||||
self.append(description)
|
||||
else:
|
||||
self.append("<")
|
||||
self.append(description)
|
||||
self.append(">")
|
||||
return self
|
||||
|
||||
def append_list(self, start: str, separator: str, end: str, list: Iterable[Any]) -> Description:
|
||||
separate = False
|
||||
|
||||
self.append(start)
|
||||
for item in list:
|
||||
if separate:
|
||||
self.append(separator)
|
||||
self.append_description_of(item)
|
||||
separate = True
|
||||
self.append(end)
|
||||
return self
|
||||
|
||||
def append(self, string: str) -> None:
|
||||
"""Append the string to the description."""
|
||||
raise NotImplementedError("append")
|
||||
|
||||
def append_string_in_python_syntax(self, string: str) -> None:
|
||||
self.append("'")
|
||||
for ch in string:
|
||||
self.append(character_in_python_syntax(ch))
|
||||
self.append("'")
|
||||
|
||||
|
||||
def character_in_python_syntax(ch: str) -> str:
|
||||
if ch == "'":
|
||||
return "'"
|
||||
elif ch == "\n":
|
||||
return "\\n"
|
||||
elif ch == "\r":
|
||||
return "\\r"
|
||||
elif ch == "\t":
|
||||
return "\\t"
|
||||
else:
|
||||
return ch
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
from typing import Optional, TypeVar
|
||||
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.matcher import Matcher
|
||||
from hamcrest.core.string_description import tostring
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class BaseMatcher(Matcher[T]):
|
||||
"""Base class for all :py:class:`~hamcrest.core.matcher.Matcher`
|
||||
implementations.
|
||||
|
||||
Most implementations can just implement :py:obj:`_matches`, leaving the
|
||||
handling of any mismatch description to the ``matches`` method. But if it
|
||||
makes more sense to generate the mismatch description during the matching,
|
||||
override :py:meth:`~hamcrest.core.matcher.Matcher.matches` instead.
|
||||
|
||||
"""
|
||||
|
||||
def __str__(self) -> str:
|
||||
return tostring(self)
|
||||
|
||||
def _matches(self, item: T) -> bool:
|
||||
raise NotImplementedError("_matches")
|
||||
|
||||
def matches(self, item: T, mismatch_description: Optional[Description] = None) -> bool:
|
||||
match_result = self._matches(item)
|
||||
if not match_result and mismatch_description:
|
||||
self.describe_mismatch(item, mismatch_description)
|
||||
return match_result
|
||||
|
||||
def describe_mismatch(self, item: T, mismatch_description: Description) -> None:
|
||||
mismatch_description.append_text("was ").append_description_of(item)
|
||||
|
||||
def describe_match(self, item: T, match_description: Description) -> None:
|
||||
match_description.append_text("was ").append_description_of(item)
|
||||
19
venv/lib/python3.9/site-packages/hamcrest/core/compat.py
Normal file
19
venv/lib/python3.9/site-packages/hamcrest/core/compat.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
__author__ = "Per Fagrell"
|
||||
__copyright__ = "Copyright 2013 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
__all__ = ['is_callable']
|
||||
|
||||
import sys
|
||||
|
||||
# callable was not part of py3k until 3.2, so we create this
|
||||
# generic is_callable to use callable if possible, otherwise
|
||||
# we use generic homebrew.
|
||||
if sys.version_info[0] == 3 and sys.version_info[1] < 2:
|
||||
def is_callable(function):
|
||||
"""Return whether the object is callable (i.e., some kind of function)."""
|
||||
if function is None:
|
||||
return False
|
||||
return any("__call__" in klass.__dict__ for klass in type(function).__mro__)
|
||||
else:
|
||||
is_callable = callable
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
"""Fundamental matchers of objects and values, and composite matchers."""
|
||||
|
||||
from hamcrest.core.core.allof import all_of
|
||||
from hamcrest.core.core.anyof import any_of
|
||||
from hamcrest.core.core.described_as import described_as
|
||||
from hamcrest.core.core.is_ import is_
|
||||
from hamcrest.core.core.isanything import anything
|
||||
from hamcrest.core.core.isequal import equal_to
|
||||
from hamcrest.core.core.isinstanceof import instance_of
|
||||
from hamcrest.core.core.isnone import none, not_none
|
||||
from hamcrest.core.core.isnot import is_not, not_
|
||||
from hamcrest.core.core.issame import same_instance
|
||||
from hamcrest.core.core.raises import calling, raises
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
58
venv/lib/python3.9/site-packages/hamcrest/core/core/allof.py
Normal file
58
venv/lib/python3.9/site-packages/hamcrest/core/core/allof.py
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
from typing import Optional, TypeVar, Union
|
||||
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.helpers.wrap_matcher import wrap_matcher
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class AllOf(BaseMatcher[T]):
|
||||
def __init__(self, *matchers: Matcher[T], **kwargs):
|
||||
self.matchers = matchers
|
||||
self.describe_matcher_in_mismatch = kwargs.pop(
|
||||
"describe_matcher_in_mismatch", True
|
||||
) # No keyword-only args in 2.7 :-(
|
||||
self.describe_all_mismatches = kwargs.pop("describe_all_mismatches", False)
|
||||
|
||||
def matches(self, item: T, mismatch_description: Optional[Description] = None) -> bool:
|
||||
found_mismatch = False
|
||||
for i, matcher in enumerate(self.matchers):
|
||||
if not matcher.matches(item):
|
||||
if mismatch_description:
|
||||
if self.describe_matcher_in_mismatch:
|
||||
mismatch_description.append_description_of(matcher).append_text(" ")
|
||||
matcher.describe_mismatch(item, mismatch_description)
|
||||
found_mismatch = True
|
||||
if not self.describe_all_mismatches:
|
||||
break
|
||||
elif i < len(self.matchers) - 1 and mismatch_description:
|
||||
mismatch_description.append_text(" and ")
|
||||
return not found_mismatch
|
||||
|
||||
def describe_mismatch(self, item: T, mismatch_description: Description) -> None:
|
||||
self.matches(item, mismatch_description)
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_list("(", " and ", ")", self.matchers)
|
||||
|
||||
|
||||
def all_of(*items: Union[Matcher[T], T]) -> Matcher[T]:
|
||||
"""Matches if all of the given matchers evaluate to ``True``.
|
||||
|
||||
:param matcher1,...: A comma-separated list of matchers.
|
||||
|
||||
The matchers are evaluated from left to right using short-circuit
|
||||
evaluation, so evaluation stops as soon as a matcher returns ``False``.
|
||||
|
||||
Any argument that is not a matcher is implicitly wrapped in an
|
||||
:py:func:`~hamcrest.core.core.isequal.equal_to` matcher to check for
|
||||
equality.
|
||||
|
||||
"""
|
||||
return AllOf(*[wrap_matcher(item) for item in items])
|
||||
42
venv/lib/python3.9/site-packages/hamcrest/core/core/anyof.py
Normal file
42
venv/lib/python3.9/site-packages/hamcrest/core/core/anyof.py
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
from typing import TypeVar, Union
|
||||
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.helpers.wrap_matcher import wrap_matcher
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class AnyOf(BaseMatcher[T]):
|
||||
def __init__(self, *matchers: Matcher[T]) -> None:
|
||||
self.matchers = matchers
|
||||
|
||||
def _matches(self, item: T) -> bool:
|
||||
for matcher in self.matchers:
|
||||
if matcher.matches(item):
|
||||
return True
|
||||
return False
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_list("(", " or ", ")", self.matchers)
|
||||
|
||||
|
||||
def any_of(*items: Union[Matcher[T], T]) -> Matcher[T]:
|
||||
"""Matches if any of the given matchers evaluate to ``True``.
|
||||
|
||||
:param matcher1,...: A comma-separated list of matchers.
|
||||
|
||||
The matchers are evaluated from left to right using short-circuit
|
||||
evaluation, so evaluation stops as soon as a matcher returns ``True``.
|
||||
|
||||
Any argument that is not a matcher is implicitly wrapped in an
|
||||
:py:func:`~hamcrest.core.core.isequal.equal_to` matcher to check for
|
||||
equality.
|
||||
|
||||
"""
|
||||
return AnyOf(*[wrap_matcher(item) for item in items])
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
import re
|
||||
from typing import Any, Optional, Tuple
|
||||
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
ARG_PATTERN = re.compile("%([0-9]+)")
|
||||
|
||||
|
||||
class DescribedAs(BaseMatcher[Any]):
|
||||
def __init__(
|
||||
self, description_template: str, matcher: Matcher[Any], *values: Tuple[Any, ...]
|
||||
) -> None:
|
||||
self.template = description_template
|
||||
self.matcher = matcher
|
||||
self.values = values
|
||||
|
||||
def matches(self, item: Any, mismatch_description: Optional[Description] = None) -> bool:
|
||||
return self.matcher.matches(item, mismatch_description)
|
||||
|
||||
def describe_mismatch(self, item: Any, mismatch_description: Description) -> None:
|
||||
self.matcher.describe_mismatch(item, mismatch_description)
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
text_start = 0
|
||||
for match in re.finditer(ARG_PATTERN, self.template):
|
||||
description.append_text(self.template[text_start : match.start()])
|
||||
arg_index = int(match.group()[1:])
|
||||
description.append_description_of(self.values[arg_index])
|
||||
text_start = match.end()
|
||||
|
||||
if text_start < len(self.template):
|
||||
description.append_text(self.template[text_start:])
|
||||
|
||||
|
||||
def described_as(description: str, matcher: Matcher[Any], *values) -> Matcher[Any]:
|
||||
"""Adds custom failure description to a given matcher.
|
||||
|
||||
:param description: Overrides the matcher's description.
|
||||
:param matcher: The matcher to satisfy.
|
||||
:param value1,...: Optional comma-separated list of substitution values.
|
||||
|
||||
The description may contain substitution placeholders %0, %1, etc. These
|
||||
will be replaced by any values that follow the matcher.
|
||||
|
||||
"""
|
||||
return DescribedAs(description, matcher, *values)
|
||||
101
venv/lib/python3.9/site-packages/hamcrest/core/core/is_.py
Normal file
101
venv/lib/python3.9/site-packages/hamcrest/core/core/is_.py
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
from typing import Optional, Type, TypeVar, Union, overload
|
||||
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.helpers.wrap_matcher import is_matchable_type, wrap_matcher
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
from .isinstanceof import instance_of
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class Is(BaseMatcher[T]):
|
||||
def __init__(self, matcher: Matcher[T]) -> None:
|
||||
self.matcher = matcher
|
||||
|
||||
def matches(self, item: T, mismatch_description: Optional[Description] = None) -> bool:
|
||||
return self.matcher.matches(item, mismatch_description)
|
||||
|
||||
def describe_mismatch(self, item: T, mismatch_description: Description) -> None:
|
||||
return self.matcher.describe_mismatch(item, mismatch_description)
|
||||
|
||||
def describe_to(self, description: Description):
|
||||
description.append_description_of(self.matcher)
|
||||
|
||||
|
||||
@overload
|
||||
def _wrap_value_or_type(x: Type) -> Matcher[object]:
|
||||
...
|
||||
|
||||
|
||||
@overload
|
||||
def _wrap_value_or_type(x: T) -> Matcher[T]:
|
||||
...
|
||||
|
||||
|
||||
def _wrap_value_or_type(x):
|
||||
if is_matchable_type(x):
|
||||
return instance_of(x)
|
||||
else:
|
||||
return wrap_matcher(x)
|
||||
|
||||
|
||||
@overload
|
||||
def is_(x: Type) -> Matcher[object]:
|
||||
...
|
||||
|
||||
|
||||
@overload
|
||||
def is_(x: Union[Matcher[T], T]) -> Matcher[T]:
|
||||
...
|
||||
|
||||
|
||||
def is_(x):
|
||||
"""Decorates another matcher, or provides shortcuts to the frequently used
|
||||
``is(equal_to(x))`` and ``is(instance_of(x))``.
|
||||
|
||||
:param x: The matcher to satisfy, or a type for
|
||||
:py:func:`~hamcrest.core.core.isinstanceof.instance_of` matching, or an
|
||||
expected value for :py:func:`~hamcrest.core.core.isequal.equal_to`
|
||||
matching.
|
||||
|
||||
This matcher compares the evaluated object to the given matcher.
|
||||
|
||||
.. note::
|
||||
|
||||
PyHamcrest's ``is_`` matcher is unrelated to Python's ``is`` operator.
|
||||
The matcher for object identity is
|
||||
:py:func:`~hamcrest.core.core.issame.same_instance`.
|
||||
|
||||
If the ``x`` argument is a matcher, its behavior is retained, but the test
|
||||
may be more expressive. For example::
|
||||
|
||||
assert_that(value, less_than(5))
|
||||
assert_that(value, is_(less_than(5)))
|
||||
|
||||
If the ``x`` argument is a type, it is wrapped in an
|
||||
:py:func:`~hamcrest.core.core.isinstanceof.instance_of` matcher. This makes
|
||||
the following statements equivalent::
|
||||
|
||||
assert_that(cheese, instance_of(Cheddar))
|
||||
assert_that(cheese, is_(instance_of(Cheddar)))
|
||||
assert_that(cheese, is_(Cheddar))
|
||||
|
||||
Otherwise, if the ``x`` argument is not a matcher, it is wrapped in an
|
||||
:py:func:`~hamcrest.core.core.isequal.equal_to` matcher. This makes the
|
||||
following statements equivalent::
|
||||
|
||||
assert_that(cheese, equal_to(smelly))
|
||||
assert_that(cheese, is_(equal_to(smelly)))
|
||||
assert_that(cheese, is_(smelly))
|
||||
|
||||
Choose the style that makes your expression most readable. This will vary
|
||||
depending on context.
|
||||
|
||||
"""
|
||||
return Is(_wrap_value_or_type(x))
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
from typing import Any, Optional
|
||||
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
|
||||
class IsAnything(BaseMatcher[Any]):
|
||||
def __init__(self, description: Optional[str]) -> None:
|
||||
self.description = description or "ANYTHING" # type: str
|
||||
|
||||
def _matches(self, item: Any) -> bool:
|
||||
return True
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_text(self.description)
|
||||
|
||||
|
||||
def anything(description: Optional[str] = None) -> Matcher[Any]:
|
||||
"""Matches anything.
|
||||
|
||||
:param description: Optional string used to describe this matcher.
|
||||
|
||||
This matcher always evaluates to ``True``. Specify this in composite
|
||||
matchers when the value of a particular element is unimportant.
|
||||
|
||||
"""
|
||||
return IsAnything(description)
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
from typing import Any
|
||||
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
|
||||
class IsEqual(BaseMatcher[Any]):
|
||||
def __init__(self, equals: Any) -> None:
|
||||
self.object = equals
|
||||
|
||||
def _matches(self, item: Any) -> bool:
|
||||
return item == self.object
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
nested_matcher = isinstance(self.object, Matcher)
|
||||
if nested_matcher:
|
||||
description.append_text("<")
|
||||
description.append_description_of(self.object)
|
||||
if nested_matcher:
|
||||
description.append_text(">")
|
||||
|
||||
|
||||
def equal_to(obj: Any) -> Matcher[Any]:
|
||||
"""Matches if object is equal to a given object.
|
||||
|
||||
:param obj: The object to compare against as the expected value.
|
||||
|
||||
This matcher compares the evaluated object to ``obj`` for equality."""
|
||||
return IsEqual(obj)
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
from typing import Type
|
||||
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.helpers.wrap_matcher import is_matchable_type
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
|
||||
class IsInstanceOf(BaseMatcher[object]):
|
||||
def __init__(self, expected_type: Type) -> None:
|
||||
if not is_matchable_type(expected_type):
|
||||
raise TypeError("IsInstanceOf requires type")
|
||||
self.expected_type = expected_type
|
||||
|
||||
def _matches(self, item: object) -> bool:
|
||||
return isinstance(item, self.expected_type)
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_text("an instance of ").append_text(self.expected_type.__name__)
|
||||
|
||||
|
||||
def instance_of(atype: Type) -> Matcher[object]:
|
||||
"""Matches if object is an instance of, or inherits from, a given type.
|
||||
|
||||
:param atype: The type to compare against as the expected type.
|
||||
|
||||
This matcher checks whether the evaluated object is an instance of
|
||||
``atype`` or an instance of any class that inherits from ``atype``.
|
||||
|
||||
Example::
|
||||
|
||||
instance_of(str)
|
||||
|
||||
"""
|
||||
return IsInstanceOf(atype)
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
from typing import Any, Optional
|
||||
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
from .isnot import is_not
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
|
||||
class IsNone(BaseMatcher[Optional[Any]]):
|
||||
def _matches(self, item: Any) -> bool:
|
||||
return item is None
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_text("None")
|
||||
|
||||
|
||||
def none() -> Matcher[Optional[Any]]:
|
||||
"""Matches if object is ``None``."""
|
||||
return IsNone()
|
||||
|
||||
|
||||
def not_none() -> Matcher[Optional[Any]]:
|
||||
"""Matches if object is not ``None``."""
|
||||
return is_not(none())
|
||||
86
venv/lib/python3.9/site-packages/hamcrest/core/core/isnot.py
Normal file
86
venv/lib/python3.9/site-packages/hamcrest/core/core/isnot.py
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
from typing import Type, TypeVar, Union, overload
|
||||
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.helpers.wrap_matcher import is_matchable_type, wrap_matcher
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
from .isinstanceof import instance_of
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class IsNot(BaseMatcher[T]):
|
||||
def __init__(self, matcher: Matcher[T]) -> None:
|
||||
self.matcher = matcher
|
||||
|
||||
def _matches(self, item: T) -> bool:
|
||||
return not self.matcher.matches(item)
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_text("not ").append_description_of(self.matcher)
|
||||
|
||||
def describe_mismatch(self, item: T, mismatch_description: Description) -> None:
|
||||
mismatch_description.append_text("but ")
|
||||
self.matcher.describe_match(item, mismatch_description)
|
||||
|
||||
|
||||
@overload
|
||||
def _wrap_value_or_type(x: Type) -> Matcher[object]:
|
||||
...
|
||||
|
||||
|
||||
@overload
|
||||
def _wrap_value_or_type(x: T) -> Matcher[T]:
|
||||
...
|
||||
|
||||
|
||||
def _wrap_value_or_type(x):
|
||||
if is_matchable_type(x):
|
||||
return instance_of(x)
|
||||
else:
|
||||
return wrap_matcher(x)
|
||||
|
||||
|
||||
@overload
|
||||
def is_not(match: Type) -> Matcher[object]:
|
||||
...
|
||||
|
||||
|
||||
@overload
|
||||
def is_not(match: Union[Matcher[T], T]) -> Matcher[T]:
|
||||
...
|
||||
|
||||
|
||||
def is_not(match):
|
||||
"""Inverts the given matcher to its logical negation.
|
||||
|
||||
:param match: The matcher to negate.
|
||||
|
||||
This matcher compares the evaluated object to the negation of the given
|
||||
matcher. If the ``match`` argument is not a matcher, it is implicitly
|
||||
wrapped in an :py:func:`~hamcrest.core.core.isequal.equal_to` matcher to
|
||||
check for equality, and thus matches for inequality.
|
||||
|
||||
Examples::
|
||||
|
||||
assert_that(cheese, is_not(equal_to(smelly)))
|
||||
assert_that(cheese, is_not(smelly))
|
||||
|
||||
"""
|
||||
return IsNot(_wrap_value_or_type(match))
|
||||
|
||||
|
||||
def not_(match: Union[Matcher[T], T]) -> Matcher[T]:
|
||||
"""Alias of :py:func:`is_not` for better readability of negations.
|
||||
|
||||
Examples::
|
||||
|
||||
assert_that(alist, not_(has_item(item)))
|
||||
|
||||
"""
|
||||
return is_not(match)
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
from typing import TypeVar
|
||||
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class IsSame(BaseMatcher[T]):
|
||||
def __init__(self, obj: T) -> None:
|
||||
self.object = obj
|
||||
|
||||
def _matches(self, item: T) -> bool:
|
||||
return item is self.object
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_text("same instance as ").append_text(hex(id(self.object))).append_text(
|
||||
" "
|
||||
).append_description_of(self.object)
|
||||
|
||||
def describe_mismatch(self, item: T, mismatch_description: Description) -> None:
|
||||
mismatch_description.append_text("was ")
|
||||
if item is not None:
|
||||
mismatch_description.append_text(hex(id(item))).append_text(" ")
|
||||
mismatch_description.append_description_of(item)
|
||||
|
||||
|
||||
def same_instance(obj: T) -> Matcher[T]:
|
||||
"""Matches if evaluated object is the same instance as a given object.
|
||||
|
||||
:param obj: The object to compare against as the expected value.
|
||||
|
||||
This matcher invokes the ``is`` identity operator to determine if the
|
||||
evaluated object is the the same object as ``obj``.
|
||||
|
||||
"""
|
||||
return IsSame(obj)
|
||||
141
venv/lib/python3.9/site-packages/hamcrest/core/core/raises.py
Normal file
141
venv/lib/python3.9/site-packages/hamcrest/core/core/raises.py
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
import re
|
||||
import sys
|
||||
from typing import Any, Callable, Mapping, Optional, Tuple, Type, cast
|
||||
from weakref import ref
|
||||
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
__author__ = "Per Fagrell"
|
||||
__copyright__ = "Copyright 2013 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
|
||||
class Raises(BaseMatcher[Callable[..., Any]]):
|
||||
def __init__(
|
||||
self,
|
||||
expected: Type[Exception],
|
||||
pattern: Optional[str] = None,
|
||||
matching: Optional[Matcher] = None,
|
||||
) -> None:
|
||||
self.pattern = pattern
|
||||
self.matcher = matching
|
||||
self.expected = expected
|
||||
self.actual = None # type: Optional[BaseException]
|
||||
self.function = None # type: Optional[Callable[..., Any]]
|
||||
|
||||
def _matches(self, function: Callable[..., Any]) -> bool:
|
||||
if not callable(function):
|
||||
return False
|
||||
|
||||
self.function = cast(Callable[..., Any], ref(function))
|
||||
return self._call_function(function)
|
||||
|
||||
def _call_function(self, function: Callable[..., Any]) -> bool:
|
||||
self.actual = None
|
||||
try:
|
||||
function()
|
||||
except BaseException:
|
||||
self.actual = sys.exc_info()[1]
|
||||
|
||||
if isinstance(self.actual, self.expected):
|
||||
if self.pattern is not None:
|
||||
if re.search(self.pattern, str(self.actual)) is None:
|
||||
return False
|
||||
if self.matcher is not None:
|
||||
if not self.matcher.matches(self.actual):
|
||||
return False
|
||||
return True
|
||||
return False
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_text("Expected a callable raising %s" % self.expected)
|
||||
|
||||
def describe_mismatch(self, item, description: Description) -> None:
|
||||
if not callable(item):
|
||||
description.append_text("%s is not callable" % item)
|
||||
return
|
||||
|
||||
function = None if self.function is None else self.function()
|
||||
if function is None or function is not item:
|
||||
self.function = ref(item)
|
||||
if not self._call_function(item):
|
||||
return
|
||||
|
||||
if self.actual is None:
|
||||
description.append_text("No exception raised.")
|
||||
elif isinstance(self.actual, self.expected):
|
||||
if self.pattern is not None or self.matcher is not None:
|
||||
description.append_text("Correct assertion type raised, but ")
|
||||
if self.pattern is not None:
|
||||
description.append_text('the expected pattern ("%s") ' % self.pattern)
|
||||
if self.pattern is not None and self.matcher is not None:
|
||||
description.append_text("and ")
|
||||
if self.matcher is not None:
|
||||
description.append_description_of(self.matcher)
|
||||
description.append_text(" ")
|
||||
description.append_text('not found. Exception message was: "%s"' % str(self.actual))
|
||||
else:
|
||||
description.append_text(
|
||||
"%r of type %s was raised instead" % (self.actual, type(self.actual))
|
||||
)
|
||||
|
||||
def describe_match(self, item, match_description: Description) -> None:
|
||||
self._call_function(item)
|
||||
match_description.append_text(
|
||||
"%r of type %s was raised." % (self.actual, type(self.actual))
|
||||
)
|
||||
|
||||
|
||||
def raises(exception: Type[Exception], pattern=None, matching=None) -> Matcher[Callable[..., Any]]:
|
||||
"""Matches if the called function raised the expected exception.
|
||||
|
||||
:param exception: The class of the expected exception
|
||||
:param pattern: Optional regular expression to match exception message.
|
||||
:param matching: Optional Hamcrest matchers to apply to the exception.
|
||||
|
||||
Expects the actual to be wrapped by using :py:func:`~hamcrest.core.core.raises.calling`,
|
||||
or a callable taking no arguments.
|
||||
Optional argument pattern should be a string containing a regular expression. If provided,
|
||||
the string representation of the actual exception - e.g. `str(actual)` - must match pattern.
|
||||
|
||||
Examples::
|
||||
|
||||
assert_that(calling(int).with_args('q'), raises(TypeError))
|
||||
assert_that(calling(parse, broken_input), raises(ValueError))
|
||||
assert_that(
|
||||
calling(valid_user, bad_json),
|
||||
raises(HTTPError, matching=has_properties(status_code=500)
|
||||
)
|
||||
"""
|
||||
return Raises(exception, pattern, matching)
|
||||
|
||||
|
||||
class DeferredCallable(object):
|
||||
def __init__(self, func: Callable[..., Any]):
|
||||
self.func = func
|
||||
self.args = tuple() # type: Tuple[Any, ...]
|
||||
self.kwargs = {} # type: Mapping[str, Any]
|
||||
|
||||
def __call__(self):
|
||||
self.func(*self.args, **self.kwargs)
|
||||
|
||||
def with_args(self, *args, **kwargs):
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
return self
|
||||
|
||||
|
||||
def calling(func: Callable[..., Any]) -> DeferredCallable:
|
||||
"""Wrapper for function call that delays the actual execution so that
|
||||
:py:func:`~hamcrest.core.core.raises.raises` matcher can catch any thrown exception.
|
||||
|
||||
:param func: The function or method to be called
|
||||
|
||||
The arguments can be provided with a call to the `with_args` function on the returned
|
||||
object::
|
||||
|
||||
calling(my_method).with_args(arguments, and_='keywords')
|
||||
"""
|
||||
return DeferredCallable(func)
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
from typing import Any, Iterable, Sequence
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
|
||||
class Description:
|
||||
"""A description of a :py:class:`~hamcrest.core.matcher.Matcher`.
|
||||
|
||||
A :py:class:`~hamcrest.core.matcher.Matcher` will describe itself to a
|
||||
description which can later be used for reporting.
|
||||
|
||||
"""
|
||||
|
||||
def append_text(self, text: str) -> "Description":
|
||||
"""Appends some plain text to the description.
|
||||
|
||||
:returns: ``self``, for chaining
|
||||
|
||||
"""
|
||||
raise NotImplementedError("append_text")
|
||||
|
||||
def append_description_of(self, value: Any) -> "Description":
|
||||
"""Appends description of given value to this description.
|
||||
|
||||
If the value implements
|
||||
:py:meth:`~hamcrest.core.selfdescribing.SelfDescribing.describe_to`,
|
||||
then it will be used.
|
||||
|
||||
:returns: ``self``, for chaining
|
||||
|
||||
"""
|
||||
raise NotImplementedError("append_description_of")
|
||||
|
||||
def append_list(
|
||||
self, start: str, separator: str, end: str, list: Iterable[Any]
|
||||
) -> "Description":
|
||||
"""Appends a list of objects to the description.
|
||||
|
||||
:param start: String that will begin the list description.
|
||||
:param separator: String that will separate each object in the
|
||||
description.
|
||||
:param end: String that will end the list description.
|
||||
:param list: List of objects to be described.
|
||||
|
||||
:returns: ``self``, for chaining
|
||||
|
||||
"""
|
||||
raise NotImplementedError("append_list")
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
"""Utilities for writing Matchers."""
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
|
||||
def hasmethod(obj: object, methodname: str) -> bool:
|
||||
"""Does ``obj`` have a method named ``methodname``?"""
|
||||
|
||||
if not hasattr(obj, methodname):
|
||||
return False
|
||||
method = getattr(obj, methodname)
|
||||
return callable(method)
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
from typing import Any, List, Type
|
||||
|
||||
MOCKTYPES = [] # type: List[Type]
|
||||
try:
|
||||
from mock import Mock
|
||||
|
||||
MOCKTYPES += [Mock]
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
from unittest.mock import Mock
|
||||
|
||||
MOCKTYPES += [Mock]
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def ismock(obj: Any) -> bool:
|
||||
return isinstance(obj, tuple(MOCKTYPES))
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
from typing import Type, TypeVar, Union
|
||||
|
||||
from hamcrest.core.base_matcher import Matcher
|
||||
from hamcrest.core.core.isequal import equal_to
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
def wrap_matcher(x: Union[Matcher[T], T]) -> Matcher[T]:
|
||||
"""Wraps argument in a matcher, if necessary.
|
||||
|
||||
:returns: the argument as-is if it is already a matcher, otherwise wrapped
|
||||
in an :py:func:`~hamcrest.core.core.isequal.equal_to` matcher.
|
||||
|
||||
"""
|
||||
if isinstance(x, Matcher):
|
||||
return x
|
||||
else:
|
||||
return equal_to(x)
|
||||
|
||||
|
||||
def is_matchable_type(expected_type: Type) -> bool:
|
||||
if isinstance(expected_type, type):
|
||||
return True
|
||||
|
||||
return False
|
||||
74
venv/lib/python3.9/site-packages/hamcrest/core/matcher.py
Normal file
74
venv/lib/python3.9/site-packages/hamcrest/core/matcher.py
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
from typing import Generic, Optional, TypeVar
|
||||
|
||||
from hamcrest.core.description import Description
|
||||
|
||||
from .selfdescribing import SelfDescribing
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class Matcher(Generic[T], SelfDescribing):
|
||||
"""A matcher over acceptable values.
|
||||
|
||||
A matcher is able to describe itself to give feedback when it fails.
|
||||
|
||||
Matcher implementations should *not* directly implement this protocol.
|
||||
Instead, *extend* the :py:class:`~hamcrest.core.base_matcher.BaseMatcher`
|
||||
class, which will ensure that the
|
||||
:py:class:`~hamcrest.core.matcher.Matcher` API can grow to support new
|
||||
features and remain compatible with all
|
||||
:py:class:`~hamcrest.core.matcher.Matcher` implementations.
|
||||
|
||||
"""
|
||||
|
||||
def matches(self, item: T, mismatch_description: Optional[Description] = None) -> bool:
|
||||
"""Evaluates the matcher for argument item.
|
||||
|
||||
If a mismatch is detected and argument ``mismatch_description`` is
|
||||
provided, it will generate a description of why the matcher has not
|
||||
accepted the item.
|
||||
|
||||
:param item: The object against which the matcher is evaluated.
|
||||
:param mismatch_description:
|
||||
:returns: ``True`` if ``item`` matches, otherwise ``False``.
|
||||
|
||||
"""
|
||||
raise NotImplementedError("matches")
|
||||
|
||||
def describe_mismatch(self, item: T, mismatch_description: Description) -> None:
|
||||
"""Generates a description of why the matcher has not accepted the
|
||||
item.
|
||||
|
||||
The description will be part of a larger description of why a matching
|
||||
failed, so it should be concise.
|
||||
|
||||
This method assumes that ``matches(item)`` is ``False``, but will not
|
||||
check this.
|
||||
|
||||
:param item: The item that the
|
||||
:py:class:`~hamcrest.core.matcher.Matcher` has rejected.
|
||||
:param mismatch_description: The description to be built or appended
|
||||
to.
|
||||
|
||||
"""
|
||||
raise NotImplementedError("describe_mismatch")
|
||||
|
||||
def describe_match(self, item: T, match_description: Description) -> None:
|
||||
"""Generates a description of why the matcher has accepted the item.
|
||||
|
||||
The description may be part of a larger description of why a matching
|
||||
failed, so it should be concise.
|
||||
|
||||
This method assumes that ``matches(item)`` is ``True``, but will not
|
||||
check this.
|
||||
|
||||
:param item: The item that the
|
||||
:py:class:`~hamcrest.core.matcher.Matcher` has accepted.
|
||||
:param match_description: The description to be built or appended to.
|
||||
|
||||
"""
|
||||
raise NotImplementedError("describe_match")
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
from hamcrest.core.description import Description
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
|
||||
class SelfDescribing:
|
||||
"""The ability of an object to describe itself."""
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
"""Generates a description of the object.
|
||||
|
||||
The description may be part of a description of a larger object of
|
||||
which this is just a component, so it should be worded appropriately.
|
||||
|
||||
:param description: The description to be built or appended to.
|
||||
|
||||
"""
|
||||
raise NotImplementedError("describe_to")
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
import warnings
|
||||
from typing import Any
|
||||
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.selfdescribing import SelfDescribing
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
|
||||
class SelfDescribingValue(SelfDescribing):
|
||||
"""Wrap any value in a ``SelfDescribingValue`` to satisfy the
|
||||
:py:class:`~hamcrest.core.selfdescribing.SelfDescribing` interface.
|
||||
|
||||
**Deprecated:** No need for this class now that
|
||||
:py:meth:`~hamcrest.core.description.Description.append_description_of`
|
||||
handles any type of value.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, value: Any) -> None:
|
||||
warnings.warn("SelfDescribingValue no longer needed", DeprecationWarning)
|
||||
self.value = value
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
"""Generates a description of the value."""
|
||||
description.append_description_of(self.value)
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
from hamcrest.core.selfdescribing import SelfDescribing
|
||||
|
||||
from .base_description import BaseDescription
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
|
||||
def tostring(selfdescribing: SelfDescribing) -> str:
|
||||
"""Returns the description of a
|
||||
:py:class:`~hamcrest.core.selfdescribing.SelfDescribing` object as a
|
||||
string.
|
||||
|
||||
:param selfdescribing: The object to be described.
|
||||
:returns: The description of the object.
|
||||
"""
|
||||
return str(StringDescription().append_description_of(selfdescribing))
|
||||
|
||||
|
||||
class StringDescription(BaseDescription):
|
||||
"""A :py:class:`~hamcrest.core.description.Description` that is stored as a
|
||||
string.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.out = ""
|
||||
|
||||
def __str__(self) -> str:
|
||||
"""Returns the description."""
|
||||
return self.out
|
||||
|
||||
def append(self, string: str) -> None:
|
||||
self.out += str(string)
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
"""Library of Matcher implementations."""
|
||||
|
||||
from hamcrest.core import *
|
||||
from hamcrest.library.collection import *
|
||||
from hamcrest.library.integration import *
|
||||
from hamcrest.library.number import *
|
||||
from hamcrest.library.object import *
|
||||
from hamcrest.library.text import *
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
__all__ = [
|
||||
"has_entry",
|
||||
"has_entries",
|
||||
"has_key",
|
||||
"has_value",
|
||||
"is_in",
|
||||
"empty",
|
||||
"has_item",
|
||||
"has_items",
|
||||
"contains_inanyorder",
|
||||
"contains",
|
||||
"contains_exactly",
|
||||
"only_contains",
|
||||
"match_equality",
|
||||
"matches_regexp",
|
||||
"close_to",
|
||||
"greater_than",
|
||||
"greater_than_or_equal_to",
|
||||
"less_than",
|
||||
"less_than_or_equal_to",
|
||||
"has_length",
|
||||
"has_property",
|
||||
"has_properties",
|
||||
"has_string",
|
||||
"equal_to_ignoring_case",
|
||||
"equal_to_ignoring_whitespace",
|
||||
"contains_string",
|
||||
"ends_with",
|
||||
"starts_with",
|
||||
"string_contains_in_order",
|
||||
]
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
"""Matchers of collections."""
|
||||
from .is_empty import empty
|
||||
from .isdict_containing import has_entry
|
||||
from .isdict_containingentries import has_entries
|
||||
from .isdict_containingkey import has_key
|
||||
from .isdict_containingvalue import has_value
|
||||
from .isin import is_in
|
||||
from .issequence_containing import has_item, has_items
|
||||
from .issequence_containinginanyorder import contains_inanyorder
|
||||
from .issequence_containinginorder import contains, contains_exactly
|
||||
from .issequence_onlycontaining import only_contains
|
||||
|
||||
__author__ = "Chris Rose"
|
||||
__copyright__ = "Copyright 2013 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
from typing import Optional, Sized
|
||||
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
__author__ = "Chris Rose"
|
||||
__copyright__ = "Copyright 2012 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
|
||||
class IsEmpty(BaseMatcher[Sized]):
|
||||
def matches(self, item: Sized, mismatch_description: Optional[Description] = None) -> bool:
|
||||
try:
|
||||
if len(item) == 0:
|
||||
return True
|
||||
|
||||
if mismatch_description:
|
||||
mismatch_description.append_text("has %d item(s)" % len(item))
|
||||
|
||||
except TypeError:
|
||||
if mismatch_description:
|
||||
mismatch_description.append_text("does not support length")
|
||||
|
||||
return False
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_text("an empty collection")
|
||||
|
||||
|
||||
def empty() -> Matcher[Sized]:
|
||||
"""
|
||||
This matcher matches any collection-like object that responds to the
|
||||
__len__ method, and has a length of 0.
|
||||
"""
|
||||
return IsEmpty()
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
from typing import Hashable, Mapping, TypeVar, Union
|
||||
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.helpers.hasmethod import hasmethod
|
||||
from hamcrest.core.helpers.wrap_matcher import wrap_matcher
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
|
||||
K = TypeVar("K", bound=Hashable) # TODO - covariant?
|
||||
V = TypeVar("V")
|
||||
|
||||
|
||||
class IsDictContaining(BaseMatcher[Mapping[K, V]]):
|
||||
def __init__(self, key_matcher: Matcher[K], value_matcher: Matcher[V]) -> None:
|
||||
self.key_matcher = key_matcher
|
||||
self.value_matcher = value_matcher
|
||||
|
||||
def _matches(self, item: Mapping[K, V]) -> bool:
|
||||
if hasmethod(item, "items"):
|
||||
for key, value in item.items():
|
||||
if self.key_matcher.matches(key) and self.value_matcher.matches(value):
|
||||
return True
|
||||
return False
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_text("a dictionary containing [").append_description_of(
|
||||
self.key_matcher
|
||||
).append_text(": ").append_description_of(self.value_matcher).append_text("]")
|
||||
|
||||
|
||||
def has_entry(
|
||||
key_match: Union[K, Matcher[K]], value_match: Union[V, Matcher[V]]
|
||||
) -> Matcher[Mapping[K, V]]:
|
||||
"""Matches if dictionary contains key-value entry satisfying a given pair
|
||||
of matchers.
|
||||
|
||||
:param key_match: The matcher to satisfy for the key, or an expected value
|
||||
for :py:func:`~hamcrest.core.core.isequal.equal_to` matching.
|
||||
:param value_match: The matcher to satisfy for the value, or an expected
|
||||
value for :py:func:`~hamcrest.core.core.isequal.equal_to` matching.
|
||||
|
||||
This matcher iterates the evaluated dictionary, searching for any key-value
|
||||
entry that satisfies ``key_match`` and ``value_match``. If a matching entry
|
||||
is found, ``has_entry`` is satisfied.
|
||||
|
||||
Any argument that is not a matcher is implicitly wrapped in an
|
||||
:py:func:`~hamcrest.core.core.isequal.equal_to` matcher to check for
|
||||
equality.
|
||||
|
||||
Examples::
|
||||
|
||||
has_entry(equal_to('foo'), equal_to(1))
|
||||
has_entry('foo', 1)
|
||||
|
||||
"""
|
||||
return IsDictContaining(wrap_matcher(key_match), wrap_matcher(value_match))
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
from typing import Any, Hashable, Mapping, Optional, TypeVar, Union, overload
|
||||
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.helpers.wrap_matcher import wrap_matcher
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
K = TypeVar("K", bound=Hashable)
|
||||
V = TypeVar("V")
|
||||
|
||||
|
||||
class IsDictContainingEntries(BaseMatcher[Mapping[K, V]]):
|
||||
def __init__(self, value_matchers) -> None:
|
||||
self.value_matchers = sorted(value_matchers.items())
|
||||
|
||||
def _not_a_dictionary(
|
||||
self, item: Mapping[K, V], mismatch_description: Optional[Description]
|
||||
) -> bool:
|
||||
if mismatch_description:
|
||||
mismatch_description.append_description_of(item).append_text(" is not a mapping object")
|
||||
return False
|
||||
|
||||
def matches(
|
||||
self, item: Mapping[K, V], mismatch_description: Optional[Description] = None
|
||||
) -> bool:
|
||||
for key, value_matcher in self.value_matchers:
|
||||
|
||||
try:
|
||||
if not key in item:
|
||||
if mismatch_description:
|
||||
mismatch_description.append_text("no ").append_description_of(
|
||||
key
|
||||
).append_text(" key in ").append_description_of(item)
|
||||
return False
|
||||
except TypeError:
|
||||
return self._not_a_dictionary(item, mismatch_description)
|
||||
|
||||
try:
|
||||
actual_value = item[key]
|
||||
except TypeError:
|
||||
return self._not_a_dictionary(item, mismatch_description)
|
||||
|
||||
if not value_matcher.matches(actual_value):
|
||||
if mismatch_description:
|
||||
mismatch_description.append_text("value for ").append_description_of(
|
||||
key
|
||||
).append_text(" ")
|
||||
value_matcher.describe_mismatch(actual_value, mismatch_description)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def describe_mismatch(self, item: Mapping[K, V], mismatch_description: Description) -> None:
|
||||
self.matches(item, mismatch_description)
|
||||
|
||||
def describe_keyvalue(self, index: int, value: V, description: Description) -> None:
|
||||
"""Describes key-value pair at given index."""
|
||||
description.append_description_of(index).append_text(": ").append_description_of(value)
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_text("a dictionary containing {")
|
||||
first = True
|
||||
for key, value in self.value_matchers:
|
||||
if not first:
|
||||
description.append_text(", ")
|
||||
self.describe_keyvalue(key, value, description)
|
||||
first = False
|
||||
description.append_text("}")
|
||||
|
||||
|
||||
# Keyword argument form
|
||||
@overload
|
||||
def has_entries(**keys_valuematchers: Union[Matcher[V], V]) -> Matcher[Mapping[str, V]]:
|
||||
...
|
||||
|
||||
|
||||
# Key to matcher dict form
|
||||
@overload
|
||||
def has_entries(keys_valuematchers: Mapping[K, Union[Matcher[V], V]]) -> Matcher[Mapping[K, V]]:
|
||||
...
|
||||
|
||||
|
||||
# Alternating key/matcher form
|
||||
@overload
|
||||
def has_entries(*keys_valuematchers: Any) -> Matcher[Mapping[Any, Any]]:
|
||||
...
|
||||
|
||||
|
||||
def has_entries(*keys_valuematchers, **kv_args):
|
||||
"""Matches if dictionary contains entries satisfying a dictionary of keys
|
||||
and corresponding value matchers.
|
||||
|
||||
:param matcher_dict: A dictionary mapping keys to associated value matchers,
|
||||
or to expected values for
|
||||
:py:func:`~hamcrest.core.core.isequal.equal_to` matching.
|
||||
|
||||
Note that the keys must be actual keys, not matchers. Any value argument
|
||||
that is not a matcher is implicitly wrapped in an
|
||||
:py:func:`~hamcrest.core.core.isequal.equal_to` matcher to check for
|
||||
equality.
|
||||
|
||||
Examples::
|
||||
|
||||
has_entries({'foo':equal_to(1), 'bar':equal_to(2)})
|
||||
has_entries({'foo':1, 'bar':2})
|
||||
|
||||
``has_entries`` also accepts a list of keyword arguments:
|
||||
|
||||
.. function:: has_entries(keyword1=value_matcher1[, keyword2=value_matcher2[, ...]])
|
||||
|
||||
:param keyword1: A keyword to look up.
|
||||
:param valueMatcher1: The matcher to satisfy for the value, or an expected
|
||||
value for :py:func:`~hamcrest.core.core.isequal.equal_to` matching.
|
||||
|
||||
Examples::
|
||||
|
||||
has_entries(foo=equal_to(1), bar=equal_to(2))
|
||||
has_entries(foo=1, bar=2)
|
||||
|
||||
Finally, ``has_entries`` also accepts a list of alternating keys and their
|
||||
value matchers:
|
||||
|
||||
.. function:: has_entries(key1, value_matcher1[, ...])
|
||||
|
||||
:param key1: A key (not a matcher) to look up.
|
||||
:param valueMatcher1: The matcher to satisfy for the value, or an expected
|
||||
value for :py:func:`~hamcrest.core.core.isequal.equal_to` matching.
|
||||
|
||||
Examples::
|
||||
|
||||
has_entries('foo', equal_to(1), 'bar', equal_to(2))
|
||||
has_entries('foo', 1, 'bar', 2)
|
||||
|
||||
"""
|
||||
if len(keys_valuematchers) == 1:
|
||||
try:
|
||||
base_dict = keys_valuematchers[0].copy()
|
||||
for key in base_dict:
|
||||
base_dict[key] = wrap_matcher(base_dict[key])
|
||||
except AttributeError:
|
||||
raise ValueError(
|
||||
"single-argument calls to has_entries must pass a dict as the argument"
|
||||
)
|
||||
else:
|
||||
if len(keys_valuematchers) % 2:
|
||||
raise ValueError("has_entries requires key-value pairs")
|
||||
base_dict = {}
|
||||
for index in range(int(len(keys_valuematchers) / 2)):
|
||||
base_dict[keys_valuematchers[2 * index]] = wrap_matcher(
|
||||
keys_valuematchers[2 * index + 1]
|
||||
)
|
||||
|
||||
for key, value in kv_args.items():
|
||||
base_dict[key] = wrap_matcher(value)
|
||||
|
||||
return IsDictContainingEntries(base_dict)
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
from typing import Any, Hashable, Mapping, TypeVar, Union
|
||||
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.helpers.hasmethod import hasmethod
|
||||
from hamcrest.core.helpers.wrap_matcher import wrap_matcher
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
K = TypeVar("K", bound=Hashable)
|
||||
|
||||
|
||||
class IsDictContainingKey(BaseMatcher[Mapping[K, Any]]):
|
||||
def __init__(self, key_matcher: Matcher[K]) -> None:
|
||||
self.key_matcher = key_matcher
|
||||
|
||||
def _matches(self, item: Mapping[K, Any]) -> bool:
|
||||
if hasmethod(item, "keys"):
|
||||
for key in item.keys():
|
||||
if self.key_matcher.matches(key):
|
||||
return True
|
||||
return False
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_text("a dictionary containing key ").append_description_of(
|
||||
self.key_matcher
|
||||
)
|
||||
|
||||
|
||||
def has_key(key_match: Union[K, Matcher[K]]) -> Matcher[Mapping[K, Any]]:
|
||||
"""Matches if dictionary contains an entry whose key satisfies a given
|
||||
matcher.
|
||||
|
||||
:param key_match: The matcher to satisfy for the key, or an expected value
|
||||
for :py:func:`~hamcrest.core.core.isequal.equal_to` matching.
|
||||
|
||||
This matcher iterates the evaluated dictionary, searching for any key-value
|
||||
entry whose key satisfies the given matcher. If a matching entry is found,
|
||||
``has_key`` is satisfied.
|
||||
|
||||
Any argument that is not a matcher is implicitly wrapped in an
|
||||
:py:func:`~hamcrest.core.core.isequal.equal_to` matcher to check for
|
||||
equality.
|
||||
|
||||
Examples::
|
||||
|
||||
has_key(equal_to('foo'))
|
||||
has_key('foo')
|
||||
|
||||
"""
|
||||
return IsDictContainingKey(wrap_matcher(key_match))
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
from typing import Any, Mapping, TypeVar, Union
|
||||
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.helpers.hasmethod import hasmethod
|
||||
from hamcrest.core.helpers.wrap_matcher import wrap_matcher
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
V = TypeVar("V")
|
||||
|
||||
|
||||
class IsDictContainingValue(BaseMatcher[Mapping[Any, V]]):
|
||||
def __init__(self, value_matcher: Matcher[V]) -> None:
|
||||
self.value_matcher = value_matcher
|
||||
|
||||
def _matches(self, item: Mapping[Any, V]) -> bool:
|
||||
if hasmethod(item, "values"):
|
||||
for value in item.values():
|
||||
if self.value_matcher.matches(value):
|
||||
return True
|
||||
return False
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_text("a dictionary containing value ").append_description_of(
|
||||
self.value_matcher
|
||||
)
|
||||
|
||||
|
||||
def has_value(value: Union[V, Matcher[V]]) -> Matcher[Mapping[Any, V]]:
|
||||
"""Matches if dictionary contains an entry whose value satisfies a given
|
||||
matcher.
|
||||
|
||||
:param value_match: The matcher to satisfy for the value, or an expected
|
||||
value for :py:func:`~hamcrest.core.core.isequal.equal_to` matching.
|
||||
|
||||
This matcher iterates the evaluated dictionary, searching for any key-value
|
||||
entry whose value satisfies the given matcher. If a matching entry is
|
||||
found, ``has_value`` is satisfied.
|
||||
|
||||
Any argument that is not a matcher is implicitly wrapped in an
|
||||
:py:func:`~hamcrest.core.core.isequal.equal_to` matcher to check for
|
||||
equality.
|
||||
|
||||
Examples::
|
||||
|
||||
has_value(equal_to('bar'))
|
||||
has_value('bar')
|
||||
|
||||
"""
|
||||
return IsDictContainingValue(wrap_matcher(value))
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
from typing import Sequence, TypeVar
|
||||
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class IsIn(BaseMatcher[T]):
|
||||
def __init__(self, sequence: Sequence[T]) -> None:
|
||||
self.sequence = sequence
|
||||
|
||||
def _matches(self, item: T) -> bool:
|
||||
return item in self.sequence
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_text("one of ").append_list("(", ", ", ")", self.sequence)
|
||||
|
||||
|
||||
def is_in(sequence: Sequence[T]) -> Matcher[T]:
|
||||
"""Matches if evaluated object is present in a given sequence.
|
||||
|
||||
:param sequence: The sequence to search.
|
||||
|
||||
This matcher invokes the ``in`` membership operator to determine if the
|
||||
evaluated object is a member of the sequence.
|
||||
|
||||
"""
|
||||
return IsIn(sequence)
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
from typing import Sequence, TypeVar, Union, cast
|
||||
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.core.allof import all_of
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.helpers.wrap_matcher import wrap_matcher
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class IsSequenceContaining(BaseMatcher[Sequence[T]]):
|
||||
def __init__(self, element_matcher: Matcher[T]) -> None:
|
||||
self.element_matcher = element_matcher
|
||||
|
||||
def _matches(self, item: Sequence[T]) -> bool:
|
||||
try:
|
||||
for element in item:
|
||||
if self.element_matcher.matches(element):
|
||||
return True
|
||||
except TypeError: # not a sequence
|
||||
pass
|
||||
return False
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_text("a sequence containing ").append_description_of(
|
||||
self.element_matcher
|
||||
)
|
||||
|
||||
|
||||
# It'd be great to make use of all_of, but we can't be sure we won't
|
||||
# be seeing a one-time sequence here (like a generator); see issue #20
|
||||
# Instead, we wrap it inside a class that will convert the sequence into
|
||||
# a concrete list and then hand it off to the all_of matcher.
|
||||
class IsSequenceContainingEvery(BaseMatcher[Sequence[T]]):
|
||||
def __init__(self, *element_matchers: Matcher[T]) -> None:
|
||||
delegates = [cast(Matcher[Sequence[T]], has_item(e)) for e in element_matchers]
|
||||
self.matcher = all_of(*delegates) # type: Matcher[Sequence[T]]
|
||||
|
||||
def _matches(self, item: Sequence[T]) -> bool:
|
||||
try:
|
||||
return self.matcher.matches(list(item))
|
||||
except TypeError:
|
||||
return False
|
||||
|
||||
def describe_mismatch(self, item: Sequence[T], mismatch_description: Description) -> None:
|
||||
self.matcher.describe_mismatch(item, mismatch_description)
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
self.matcher.describe_to(description)
|
||||
|
||||
|
||||
def has_item(match: Union[Matcher[T], T]) -> Matcher[Sequence[T]]:
|
||||
"""Matches if any element of sequence satisfies a given matcher.
|
||||
|
||||
:param match: The matcher to satisfy, or an expected value for
|
||||
:py:func:`~hamcrest.core.core.isequal.equal_to` matching.
|
||||
|
||||
This matcher iterates the evaluated sequence, searching for any element
|
||||
that satisfies a given matcher. If a matching element is found,
|
||||
``has_item`` is satisfied.
|
||||
|
||||
If the ``match`` argument is not a matcher, it is implicitly wrapped in an
|
||||
:py:func:`~hamcrest.core.core.isequal.equal_to` matcher to check for
|
||||
equality.
|
||||
|
||||
"""
|
||||
return IsSequenceContaining(wrap_matcher(match))
|
||||
|
||||
|
||||
def has_items(*items: Union[Matcher[T], T]) -> Matcher[Sequence[T]]:
|
||||
"""Matches if all of the given matchers are satisfied by any elements of
|
||||
the sequence.
|
||||
|
||||
:param match1,...: A comma-separated list of matchers.
|
||||
|
||||
This matcher iterates the given matchers, searching for any elements in the
|
||||
evaluated sequence that satisfy them. If each matcher is satisfied, then
|
||||
``has_items`` is satisfied.
|
||||
|
||||
Any argument that is not a matcher is implicitly wrapped in an
|
||||
:py:func:`~hamcrest.core.core.isequal.equal_to` matcher to check for
|
||||
equality.
|
||||
|
||||
"""
|
||||
matchers = []
|
||||
for item in items:
|
||||
matchers.append(wrap_matcher(item))
|
||||
return IsSequenceContainingEvery(*matchers)
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
from typing import MutableSequence, Optional, Sequence, TypeVar, Union, cast
|
||||
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.helpers.wrap_matcher import wrap_matcher
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class MatchInAnyOrder(object):
|
||||
def __init__(
|
||||
self, matchers: Sequence[Matcher[T]], mismatch_description: Optional[Description]
|
||||
) -> None:
|
||||
self.matchers = cast(MutableSequence[Matcher[T]], matchers[:])
|
||||
self.mismatch_description = mismatch_description
|
||||
|
||||
def matches(self, item: T) -> bool:
|
||||
return self.isnotsurplus(item) and self.ismatched(item)
|
||||
|
||||
def isfinished(self, item: Sequence[T]) -> bool:
|
||||
if not self.matchers:
|
||||
return True
|
||||
if self.mismatch_description:
|
||||
self.mismatch_description.append_text("no item matches: ").append_list(
|
||||
"", ", ", "", self.matchers
|
||||
).append_text(" in ").append_list("[", ", ", "]", item)
|
||||
return False
|
||||
|
||||
def isnotsurplus(self, item: T) -> bool:
|
||||
if not self.matchers:
|
||||
if self.mismatch_description:
|
||||
self.mismatch_description.append_text("not matched: ").append_description_of(item)
|
||||
return False
|
||||
return True
|
||||
|
||||
def ismatched(self, item: T) -> bool:
|
||||
for index, matcher in enumerate(self.matchers):
|
||||
if matcher.matches(item):
|
||||
del self.matchers[index]
|
||||
return True
|
||||
|
||||
if self.mismatch_description:
|
||||
self.mismatch_description.append_text("not matched: ").append_description_of(item)
|
||||
return False
|
||||
|
||||
|
||||
class IsSequenceContainingInAnyOrder(BaseMatcher[Sequence[T]]):
|
||||
def __init__(self, matchers: Sequence[Matcher[T]]) -> None:
|
||||
self.matchers = matchers
|
||||
|
||||
def matches(
|
||||
self, item: Sequence[T], mismatch_description: Optional[Description] = None
|
||||
) -> bool:
|
||||
try:
|
||||
sequence = list(item)
|
||||
matchsequence = MatchInAnyOrder(self.matchers, mismatch_description)
|
||||
for element in sequence:
|
||||
if not matchsequence.matches(element):
|
||||
return False
|
||||
return matchsequence.isfinished(sequence)
|
||||
except TypeError:
|
||||
if mismatch_description:
|
||||
super(IsSequenceContainingInAnyOrder, self).describe_mismatch(
|
||||
item, mismatch_description
|
||||
)
|
||||
return False
|
||||
|
||||
def describe_mismatch(self, item: Sequence[T], mismatch_description: Description) -> None:
|
||||
self.matches(item, mismatch_description)
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_text("a sequence over ").append_list(
|
||||
"[", ", ", "]", self.matchers
|
||||
).append_text(" in any order")
|
||||
|
||||
|
||||
def contains_inanyorder(*items: Union[Matcher[T], T]) -> Matcher[Sequence[T]]:
|
||||
"""Matches if sequences's elements, in any order, satisfy a given list of
|
||||
matchers.
|
||||
|
||||
:param match1,...: A comma-separated list of matchers.
|
||||
|
||||
This matcher iterates the evaluated sequence, seeing if each element
|
||||
satisfies any of the given matchers. The matchers are tried from left to
|
||||
right, and when a satisfied matcher is found, it is no longer a candidate
|
||||
for the remaining elements. If a one-to-one correspondence is established
|
||||
between elements and matchers, ``contains_inanyorder`` is satisfied.
|
||||
|
||||
Any argument that is not a matcher is implicitly wrapped in an
|
||||
:py:func:`~hamcrest.core.core.isequal.equal_to` matcher to check for
|
||||
equality.
|
||||
|
||||
"""
|
||||
|
||||
matchers = []
|
||||
for item in items:
|
||||
matchers.append(wrap_matcher(item))
|
||||
return IsSequenceContainingInAnyOrder(matchers)
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
import warnings
|
||||
from typing import Optional, Sequence, TypeVar, Union
|
||||
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.helpers.wrap_matcher import wrap_matcher
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class MatchingInOrder(object):
|
||||
def __init__(
|
||||
self, matchers: Sequence[Matcher[T]], mismatch_description: Optional[Description]
|
||||
) -> None:
|
||||
self.matchers = matchers
|
||||
self.mismatch_description = mismatch_description
|
||||
self.next_match_index = 0
|
||||
|
||||
def matches(self, item: T) -> bool:
|
||||
return self.isnotsurplus(item) and self.ismatched(item)
|
||||
|
||||
def isfinished(self) -> bool:
|
||||
if self.next_match_index < len(self.matchers):
|
||||
if self.mismatch_description:
|
||||
self.mismatch_description.append_text("No item matched: ").append_description_of(
|
||||
self.matchers[self.next_match_index]
|
||||
)
|
||||
return False
|
||||
return True
|
||||
|
||||
def ismatched(self, item: T) -> bool:
|
||||
matcher = self.matchers[self.next_match_index]
|
||||
if not matcher.matches(item):
|
||||
if self.mismatch_description:
|
||||
self.mismatch_description.append_text("item " + str(self.next_match_index) + ": ")
|
||||
matcher.describe_mismatch(item, self.mismatch_description)
|
||||
return False
|
||||
self.next_match_index += 1
|
||||
return True
|
||||
|
||||
def isnotsurplus(self, item: T) -> bool:
|
||||
if len(self.matchers) <= self.next_match_index:
|
||||
if self.mismatch_description:
|
||||
self.mismatch_description.append_text("Not matched: ").append_description_of(item)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class IsSequenceContainingInOrder(BaseMatcher[Sequence[T]]):
|
||||
def __init__(self, matchers: Sequence[Matcher[T]]) -> None:
|
||||
self.matchers = matchers
|
||||
|
||||
def matches(
|
||||
self, item: Sequence[T], mismatch_description: Optional[Description] = None
|
||||
) -> bool:
|
||||
try:
|
||||
matchsequence = MatchingInOrder(self.matchers, mismatch_description)
|
||||
for element in item:
|
||||
if not matchsequence.matches(element):
|
||||
return False
|
||||
return matchsequence.isfinished()
|
||||
except TypeError:
|
||||
if mismatch_description:
|
||||
super(IsSequenceContainingInOrder, self).describe_mismatch(
|
||||
item, mismatch_description
|
||||
)
|
||||
return False
|
||||
|
||||
def describe_mismatch(self, item: Sequence[T], mismatch_description: Description) -> None:
|
||||
self.matches(item, mismatch_description)
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_text("a sequence containing ").append_list("[", ", ", "]", self.matchers)
|
||||
|
||||
|
||||
def contains_exactly(*items: Union[Matcher[T], T]) -> Matcher[Sequence[T]]:
|
||||
"""Matches if sequence's elements satisfy a given list of matchers, in order.
|
||||
|
||||
:param match1,...: A comma-separated list of matchers.
|
||||
|
||||
This matcher iterates the evaluated sequence and a given list of matchers,
|
||||
seeing if each element satisfies its corresponding matcher.
|
||||
|
||||
Any argument that is not a matcher is implicitly wrapped in an
|
||||
:py:func:`~hamcrest.core.core.isequal.equal_to` matcher to check for
|
||||
equality.
|
||||
|
||||
"""
|
||||
matchers = []
|
||||
for item in items:
|
||||
matchers.append(wrap_matcher(item))
|
||||
return IsSequenceContainingInOrder(matchers)
|
||||
|
||||
|
||||
def contains(*items: Union[Matcher[T], T]) -> Matcher[Sequence[T]]:
|
||||
"""Deprecated - use contains_exactly(*items)"""
|
||||
warnings.warn("deprecated - use contains_exactly(*items)", DeprecationWarning)
|
||||
return contains_exactly(*items)
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
from typing import Sequence, TypeVar, Union
|
||||
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.core.anyof import any_of
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.helpers.wrap_matcher import wrap_matcher
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class IsSequenceOnlyContaining(BaseMatcher[Sequence[T]]):
|
||||
def __init__(self, matcher: Matcher[T]) -> None:
|
||||
self.matcher = matcher
|
||||
|
||||
def _matches(self, item: Sequence[T]) -> bool:
|
||||
try:
|
||||
sequence = list(item)
|
||||
if len(sequence) == 0:
|
||||
return False
|
||||
for element in sequence:
|
||||
if not self.matcher.matches(element):
|
||||
return False
|
||||
return True
|
||||
except TypeError:
|
||||
return False
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_text("a sequence containing items matching ").append_description_of(
|
||||
self.matcher
|
||||
)
|
||||
|
||||
|
||||
def only_contains(*items: Union[Matcher[T], T]) -> Matcher[Sequence[T]]:
|
||||
"""Matches if each element of sequence satisfies any of the given matchers.
|
||||
|
||||
:param match1,...: A comma-separated list of matchers.
|
||||
|
||||
This matcher iterates the evaluated sequence, confirming whether each
|
||||
element satisfies any of the given matchers.
|
||||
|
||||
Example::
|
||||
|
||||
only_contains(less_than(4))
|
||||
|
||||
will match ``[3,1,2]``.
|
||||
|
||||
Any argument that is not a matcher is implicitly wrapped in an
|
||||
:py:func:`~hamcrest.core.core.isequal.equal_to` matcher to check for
|
||||
equality.
|
||||
|
||||
"""
|
||||
matchers = []
|
||||
for item in items:
|
||||
matchers.append(wrap_matcher(item))
|
||||
return IsSequenceOnlyContaining(any_of(*matchers))
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
"""Utilities for integrating Hamcrest with other libraries."""
|
||||
|
||||
from .match_equality import match_equality
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
from typing import Any
|
||||
|
||||
from hamcrest.core.helpers.wrap_matcher import wrap_matcher
|
||||
from hamcrest.core.matcher import Matcher
|
||||
from hamcrest.core.string_description import tostring
|
||||
|
||||
__author__ = "Chris Rose"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
__unittest = True
|
||||
|
||||
|
||||
class EqualityWrapper(object):
|
||||
def __init__(self, matcher: Matcher) -> None:
|
||||
self.matcher = matcher
|
||||
|
||||
def __eq__(self, obj: Any) -> bool:
|
||||
return self.matcher.matches(obj)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return repr(self)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return tostring(self.matcher)
|
||||
|
||||
|
||||
def match_equality(matcher: Matcher) -> EqualityWrapper:
|
||||
"""Wraps a matcher to define equality in terms of satisfying the matcher.
|
||||
|
||||
``match_equality`` allows Hamcrest matchers to be used in libraries that
|
||||
are not Hamcrest-aware. They might use the equality operator::
|
||||
|
||||
assert match_equality(matcher) == object
|
||||
|
||||
Or they might provide a method that uses equality for its test::
|
||||
|
||||
library.method_that_tests_eq(match_equality(matcher))
|
||||
|
||||
One concrete example is integrating with the ``assert_called_with`` methods
|
||||
in Michael Foord's `mock <http://www.voidspace.org.uk/python/mock/>`_
|
||||
library.
|
||||
|
||||
"""
|
||||
return EqualityWrapper(wrap_matcher(matcher))
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
"""Matchers that perform numeric comparisons."""
|
||||
|
||||
from .iscloseto import close_to
|
||||
from .ordering_comparison import (
|
||||
greater_than,
|
||||
greater_than_or_equal_to,
|
||||
less_than,
|
||||
less_than_or_equal_to,
|
||||
)
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
from decimal import Decimal
|
||||
from math import fabs
|
||||
from typing import Any, Union, overload
|
||||
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
Number = Union[float, Decimal] # Argh, https://github.com/python/mypy/issues/3186
|
||||
|
||||
|
||||
def isnumeric(value: Any) -> bool:
|
||||
"""Confirm that 'value' can be treated numerically; duck-test accordingly
|
||||
"""
|
||||
if isinstance(value, (float, complex, int)):
|
||||
return True
|
||||
|
||||
try:
|
||||
_ = (fabs(value) + 0 - 0) * 1
|
||||
return True
|
||||
except ArithmeticError:
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
class IsCloseTo(BaseMatcher[Number]):
|
||||
def __init__(self, value: Number, delta: Number) -> None:
|
||||
if not isnumeric(value):
|
||||
raise TypeError("IsCloseTo value must be numeric")
|
||||
if not isnumeric(delta):
|
||||
raise TypeError("IsCloseTo delta must be numeric")
|
||||
|
||||
self.value = value
|
||||
self.delta = delta
|
||||
|
||||
def _matches(self, item: Number) -> bool:
|
||||
if not isnumeric(item):
|
||||
return False
|
||||
return self._diff(item) <= self.delta
|
||||
|
||||
def _diff(self, item: Number) -> float:
|
||||
# TODO - Fails for mixed floats & Decimals
|
||||
return fabs(item - self.value) # type: ignore
|
||||
|
||||
def describe_mismatch(self, item: Number, mismatch_description: Description) -> None:
|
||||
if not isnumeric(item):
|
||||
super(IsCloseTo, self).describe_mismatch(item, mismatch_description)
|
||||
else:
|
||||
actual_delta = self._diff(item)
|
||||
mismatch_description.append_description_of(item).append_text(
|
||||
" differed by "
|
||||
).append_description_of(actual_delta)
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_text("a numeric value within ").append_description_of(
|
||||
self.delta
|
||||
).append_text(" of ").append_description_of(self.value)
|
||||
|
||||
|
||||
@overload
|
||||
def close_to(value: float, delta: float) -> Matcher[float]:
|
||||
...
|
||||
|
||||
|
||||
@overload
|
||||
def close_to(value: Decimal, delta: Decimal) -> Matcher[Decimal]:
|
||||
...
|
||||
|
||||
|
||||
def close_to(value, delta):
|
||||
"""Matches if object is a number close to a given value, within a given
|
||||
delta.
|
||||
|
||||
:param value: The value to compare against as the expected value.
|
||||
:param delta: The maximum delta between the values for which the numbers
|
||||
are considered close.
|
||||
|
||||
This matcher compares the evaluated object against ``value`` to see if the
|
||||
difference is within a positive ``delta``.
|
||||
|
||||
Example::
|
||||
|
||||
close_to(3.0, 0.25)
|
||||
|
||||
"""
|
||||
return IsCloseTo(value, delta)
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
import operator
|
||||
from typing import Any, Callable
|
||||
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
|
||||
class OrderingComparison(BaseMatcher[Any]):
|
||||
def __init__(
|
||||
self,
|
||||
value: Any,
|
||||
comparison_function: Callable[[Any, Any], bool],
|
||||
comparison_description: str,
|
||||
) -> None:
|
||||
self.value = value
|
||||
self.comparison_function = comparison_function
|
||||
self.comparison_description = comparison_description
|
||||
|
||||
def _matches(self, item: Any) -> bool:
|
||||
return self.comparison_function(item, self.value)
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_text("a value ").append_text(self.comparison_description).append_text(
|
||||
" "
|
||||
).append_description_of(self.value)
|
||||
|
||||
|
||||
def greater_than(value: Any) -> Matcher[Any]:
|
||||
"""Matches if object is greater than a given value.
|
||||
|
||||
:param value: The value to compare against.
|
||||
|
||||
"""
|
||||
return OrderingComparison(value, operator.gt, "greater than")
|
||||
|
||||
|
||||
def greater_than_or_equal_to(value: Any) -> Matcher[Any]:
|
||||
"""Matches if object is greater than or equal to a given value.
|
||||
|
||||
:param value: The value to compare against.
|
||||
|
||||
"""
|
||||
return OrderingComparison(value, operator.ge, "greater than or equal to")
|
||||
|
||||
|
||||
def less_than(value: Any) -> Matcher[Any]:
|
||||
"""Matches if object is less than a given value.
|
||||
|
||||
:param value: The value to compare against.
|
||||
|
||||
"""
|
||||
return OrderingComparison(value, operator.lt, "less than")
|
||||
|
||||
|
||||
def less_than_or_equal_to(value: Any) -> Matcher[Any]:
|
||||
"""Matches if object is less than or equal to a given value.
|
||||
|
||||
:param value: The value to compare against.
|
||||
|
||||
"""
|
||||
return OrderingComparison(value, operator.le, "less than or equal to")
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
"""Matchers that inspect objects and classes."""
|
||||
|
||||
from .haslength import has_length
|
||||
from .hasproperty import has_properties, has_property
|
||||
from .hasstring import has_string
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
from collections.abc import Sized
|
||||
from typing import Union
|
||||
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.helpers.hasmethod import hasmethod
|
||||
from hamcrest.core.helpers.wrap_matcher import wrap_matcher
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
|
||||
class HasLength(BaseMatcher[Sized]):
|
||||
def __init__(self, len_matcher: Matcher[int]) -> None:
|
||||
self.len_matcher = len_matcher
|
||||
|
||||
def _matches(self, item: Sized) -> bool:
|
||||
if not hasmethod(item, "__len__"):
|
||||
return False
|
||||
return self.len_matcher.matches(len(item))
|
||||
|
||||
def describe_mismatch(self, item: Sized, mismatch_description: Description) -> None:
|
||||
super(HasLength, self).describe_mismatch(item, mismatch_description)
|
||||
if hasmethod(item, "__len__"):
|
||||
mismatch_description.append_text(" with length of ").append_description_of(len(item))
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_text("an object with length of ").append_description_of(self.len_matcher)
|
||||
|
||||
|
||||
def has_length(match: Union[int, Matcher[int]]) -> Matcher[Sized]:
|
||||
"""Matches if ``len(item)`` satisfies a given matcher.
|
||||
|
||||
:param match: The matcher to satisfy, or an expected value for
|
||||
:py:func:`~hamcrest.core.core.isequal.equal_to` matching.
|
||||
|
||||
This matcher invokes the :py:func:`len` function on the evaluated object to
|
||||
get its length, passing the result to a given matcher for evaluation.
|
||||
|
||||
If the ``match`` argument is not a matcher, it is implicitly wrapped in an
|
||||
:py:func:`~hamcrest.core.core.isequal.equal_to` matcher to check for
|
||||
:equality.
|
||||
|
||||
Examples::
|
||||
|
||||
has_length(greater_than(6))
|
||||
has_length(5)
|
||||
|
||||
"""
|
||||
return HasLength(wrap_matcher(match))
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
from typing import Any, Mapping, TypeVar, Union, overload
|
||||
|
||||
from hamcrest import described_as
|
||||
from hamcrest.core import anything
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.core.allof import AllOf
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.helpers.wrap_matcher import wrap_matcher as wrap_shortcut
|
||||
from hamcrest.core.matcher import Matcher
|
||||
from hamcrest.core.string_description import StringDescription
|
||||
|
||||
__author__ = "Chris Rose"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
V = TypeVar("V")
|
||||
|
||||
|
||||
class IsObjectWithProperty(BaseMatcher[object]):
|
||||
def __init__(self, property_name: str, value_matcher: Matcher[V]) -> None:
|
||||
self.property_name = property_name
|
||||
self.value_matcher = value_matcher
|
||||
|
||||
def _matches(self, item: object) -> bool:
|
||||
if item is None:
|
||||
return False
|
||||
|
||||
if not hasattr(item, self.property_name):
|
||||
return False
|
||||
|
||||
value = getattr(item, self.property_name)
|
||||
return self.value_matcher.matches(value)
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_text("an object with a property '").append_text(
|
||||
self.property_name
|
||||
).append_text("' matching ").append_description_of(self.value_matcher)
|
||||
|
||||
def describe_mismatch(self, item: object, mismatch_description: Description) -> None:
|
||||
if item is None:
|
||||
mismatch_description.append_text("was None")
|
||||
return
|
||||
|
||||
if not hasattr(item, self.property_name):
|
||||
mismatch_description.append_description_of(item).append_text(
|
||||
" did not have the "
|
||||
).append_description_of(self.property_name).append_text(" property")
|
||||
return
|
||||
|
||||
mismatch_description.append_text("property ").append_description_of(
|
||||
self.property_name
|
||||
).append_text(" ")
|
||||
value = getattr(item, self.property_name)
|
||||
self.value_matcher.describe_mismatch(value, mismatch_description)
|
||||
|
||||
def __str__(self):
|
||||
d = StringDescription()
|
||||
self.describe_to(d)
|
||||
return str(d)
|
||||
|
||||
|
||||
def has_property(name: str, match: Union[None, Matcher[V], V] = None) -> Matcher[object]:
|
||||
"""Matches if object has a property with a given name whose value satisfies
|
||||
a given matcher.
|
||||
|
||||
:param name: The name of the property.
|
||||
:param match: Optional matcher to satisfy.
|
||||
|
||||
This matcher determines if the evaluated object has a property with a given
|
||||
name. If no such property is found, ``has_property`` is not satisfied.
|
||||
|
||||
If the property is found, its value is passed to a given matcher for
|
||||
evaluation. If the ``match`` argument is not a matcher, it is implicitly
|
||||
wrapped in an :py:func:`~hamcrest.core.core.isequal.equal_to` matcher to
|
||||
check for equality.
|
||||
|
||||
If the ``match`` argument is not provided, the
|
||||
:py:func:`~hamcrest.core.core.isanything.anything` matcher is used so that
|
||||
``has_property`` is satisfied if a matching property is found.
|
||||
|
||||
Examples::
|
||||
|
||||
has_property('name', starts_with('J'))
|
||||
has_property('name', 'Jon')
|
||||
has_property('name')
|
||||
|
||||
"""
|
||||
|
||||
if match is None:
|
||||
match = anything()
|
||||
|
||||
return IsObjectWithProperty(name, wrap_shortcut(match))
|
||||
|
||||
|
||||
# Keyword argument form
|
||||
@overload
|
||||
def has_properties(**keys_valuematchers: Union[Matcher[V], V]) -> Matcher[object]:
|
||||
...
|
||||
|
||||
|
||||
# Name to matcher dict form
|
||||
@overload
|
||||
def has_properties(keys_valuematchers: Mapping[str, Union[Matcher[V], V]]) -> Matcher[object]:
|
||||
...
|
||||
|
||||
|
||||
# Alternating name/matcher form
|
||||
@overload
|
||||
def has_properties(*keys_valuematchers: Any) -> Matcher[object]:
|
||||
...
|
||||
|
||||
|
||||
def has_properties(*keys_valuematchers, **kv_args):
|
||||
"""Matches if an object has properties satisfying all of a dictionary
|
||||
of string property names and corresponding value matchers.
|
||||
|
||||
:param matcher_dict: A dictionary mapping keys to associated value matchers,
|
||||
or to expected values for
|
||||
:py:func:`~hamcrest.core.core.isequal.equal_to` matching.
|
||||
|
||||
Note that the keys must be actual keys, not matchers. Any value argument
|
||||
that is not a matcher is implicitly wrapped in an
|
||||
:py:func:`~hamcrest.core.core.isequal.equal_to` matcher to check for
|
||||
equality.
|
||||
|
||||
Examples::
|
||||
|
||||
has_properties({'foo':equal_to(1), 'bar':equal_to(2)})
|
||||
has_properties({'foo':1, 'bar':2})
|
||||
|
||||
``has_properties`` also accepts a list of keyword arguments:
|
||||
|
||||
.. function:: has_properties(keyword1=value_matcher1[, keyword2=value_matcher2[, ...]])
|
||||
|
||||
:param keyword1: A keyword to look up.
|
||||
:param valueMatcher1: The matcher to satisfy for the value, or an expected
|
||||
value for :py:func:`~hamcrest.core.core.isequal.equal_to` matching.
|
||||
|
||||
Examples::
|
||||
|
||||
has_properties(foo=equal_to(1), bar=equal_to(2))
|
||||
has_properties(foo=1, bar=2)
|
||||
|
||||
Finally, ``has_properties`` also accepts a list of alternating keys and their
|
||||
value matchers:
|
||||
|
||||
.. function:: has_properties(key1, value_matcher1[, ...])
|
||||
|
||||
:param key1: A key (not a matcher) to look up.
|
||||
:param valueMatcher1: The matcher to satisfy for the value, or an expected
|
||||
value for :py:func:`~hamcrest.core.core.isequal.equal_to` matching.
|
||||
|
||||
Examples::
|
||||
|
||||
has_properties('foo', equal_to(1), 'bar', equal_to(2))
|
||||
has_properties('foo', 1, 'bar', 2)
|
||||
|
||||
"""
|
||||
if len(keys_valuematchers) == 1:
|
||||
try:
|
||||
base_dict = keys_valuematchers[0].copy()
|
||||
for key in base_dict:
|
||||
base_dict[key] = wrap_shortcut(base_dict[key])
|
||||
except AttributeError:
|
||||
raise ValueError(
|
||||
"single-argument calls to has_properties must pass a dict as the argument"
|
||||
)
|
||||
else:
|
||||
if len(keys_valuematchers) % 2:
|
||||
raise ValueError("has_properties requires key-value pairs")
|
||||
base_dict = {}
|
||||
for index in range(int(len(keys_valuematchers) / 2)):
|
||||
base_dict[keys_valuematchers[2 * index]] = wrap_shortcut(
|
||||
keys_valuematchers[2 * index + 1]
|
||||
)
|
||||
|
||||
for key, value in kv_args.items():
|
||||
base_dict[key] = wrap_shortcut(value)
|
||||
|
||||
if len(base_dict) > 1:
|
||||
description = StringDescription().append_text("an object with properties ")
|
||||
for i, (property_name, property_value_matcher) in enumerate(sorted(base_dict.items())):
|
||||
description.append_description_of(property_name).append_text(
|
||||
" matching "
|
||||
).append_description_of(property_value_matcher)
|
||||
if i < len(base_dict) - 1:
|
||||
description.append_text(" and ")
|
||||
|
||||
return described_as(
|
||||
str(description),
|
||||
AllOf(
|
||||
*[
|
||||
has_property(property_name, property_value_matcher)
|
||||
for property_name, property_value_matcher in sorted(base_dict.items())
|
||||
],
|
||||
describe_all_mismatches=True,
|
||||
describe_matcher_in_mismatch=False,
|
||||
),
|
||||
)
|
||||
else:
|
||||
property_name, property_value_matcher = base_dict.popitem()
|
||||
return has_property(property_name, property_value_matcher)
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.helpers.wrap_matcher import wrap_matcher
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
|
||||
class HasString(BaseMatcher[object]):
|
||||
def __init__(self, str_matcher: Matcher[str]) -> None:
|
||||
self.str_matcher = str_matcher
|
||||
|
||||
def _matches(self, item: object) -> bool:
|
||||
return self.str_matcher.matches(str(item))
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_text("an object with str ").append_description_of(self.str_matcher)
|
||||
|
||||
|
||||
def has_string(match) -> Matcher[object]:
|
||||
"""Matches if ``str(item)`` satisfies a given matcher.
|
||||
|
||||
:param match: The matcher to satisfy, or an expected value for
|
||||
:py:func:`~hamcrest.core.core.isequal.equal_to` matching.
|
||||
|
||||
This matcher invokes the :py:func:`str` function on the evaluated object to
|
||||
get its length, passing the result to a given matcher for evaluation. If
|
||||
the ``match`` argument is not a matcher, it is implicitly wrapped in an
|
||||
:py:func:`~hamcrest.core.core.isequal.equal_to` matcher to check for
|
||||
equality.
|
||||
|
||||
Examples::
|
||||
|
||||
has_string(starts_with('foo'))
|
||||
has_string('bar')
|
||||
|
||||
"""
|
||||
return HasString(wrap_matcher(match))
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
"""Matchers that perform text comparisons."""
|
||||
|
||||
from .isequal_ignoring_case import equal_to_ignoring_case
|
||||
from .isequal_ignoring_whitespace import equal_to_ignoring_whitespace
|
||||
from .stringcontains import contains_string
|
||||
from .stringcontainsinorder import string_contains_in_order
|
||||
from .stringendswith import ends_with
|
||||
from .stringmatches import matches_regexp
|
||||
from .stringstartswith import starts_with
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
|
||||
class IsEqualIgnoringCase(BaseMatcher[str]):
|
||||
def __init__(self, string: str) -> None:
|
||||
if not isinstance(string, str):
|
||||
raise TypeError("IsEqualIgnoringCase requires string")
|
||||
self.original_string = string
|
||||
self.lowered_string = string.lower()
|
||||
|
||||
def _matches(self, item: str) -> bool:
|
||||
if not isinstance(item, str):
|
||||
return False
|
||||
return self.lowered_string == item.lower()
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_description_of(self.original_string).append_text(" ignoring case")
|
||||
|
||||
|
||||
def equal_to_ignoring_case(string: str) -> Matcher[str]:
|
||||
"""Matches if object is a string equal to a given string, ignoring case
|
||||
differences.
|
||||
|
||||
:param string: The string to compare against as the expected value.
|
||||
|
||||
This matcher first checks whether the evaluated object is a string. If so,
|
||||
it compares it with ``string``, ignoring differences of case.
|
||||
|
||||
Example::
|
||||
|
||||
equal_to_ignoring_case("hello world")
|
||||
|
||||
will match "heLLo WorlD".
|
||||
|
||||
"""
|
||||
return IsEqualIgnoringCase(string)
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
|
||||
def stripspace(string: str) -> str:
|
||||
result = ""
|
||||
last_was_space = True
|
||||
for character in string:
|
||||
if character.isspace():
|
||||
if not last_was_space:
|
||||
result += " "
|
||||
last_was_space = True
|
||||
else:
|
||||
result += character
|
||||
last_was_space = False
|
||||
return result.strip()
|
||||
|
||||
|
||||
class IsEqualIgnoringWhiteSpace(BaseMatcher[str]):
|
||||
def __init__(self, string) -> None:
|
||||
if not isinstance(string, str):
|
||||
raise TypeError("IsEqualIgnoringWhiteSpace requires string")
|
||||
self.original_string = string
|
||||
self.stripped_string = stripspace(string)
|
||||
|
||||
def _matches(self, item: str) -> bool:
|
||||
if not isinstance(item, str):
|
||||
return False
|
||||
return self.stripped_string == stripspace(item)
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_description_of(self.original_string).append_text(" ignoring whitespace")
|
||||
|
||||
|
||||
def equal_to_ignoring_whitespace(string: str) -> Matcher[str]:
|
||||
"""Matches if object is a string equal to a given string, ignoring
|
||||
differences in whitespace.
|
||||
|
||||
:param string: The string to compare against as the expected value.
|
||||
|
||||
This matcher first checks whether the evaluated object is a string. If so,
|
||||
it compares it with ``string``, ignoring differences in runs of whitespace.
|
||||
|
||||
Example::
|
||||
|
||||
equal_to_ignoring_whitespace("hello world")
|
||||
|
||||
will match ``"hello world"``.
|
||||
|
||||
"""
|
||||
return IsEqualIgnoringWhiteSpace(string)
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
from hamcrest.core.helpers.hasmethod import hasmethod
|
||||
from hamcrest.core.matcher import Matcher
|
||||
from hamcrest.library.text.substringmatcher import SubstringMatcher
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
|
||||
class StringContains(SubstringMatcher):
|
||||
def __init__(self, substring) -> None:
|
||||
super(StringContains, self).__init__(substring)
|
||||
|
||||
def _matches(self, item: str) -> bool:
|
||||
if not hasmethod(item, "find"):
|
||||
return False
|
||||
return item.find(self.substring) >= 0
|
||||
|
||||
def relationship(self):
|
||||
return "containing"
|
||||
|
||||
|
||||
def contains_string(substring: str) -> Matcher[str]:
|
||||
"""Matches if object is a string containing a given string.
|
||||
|
||||
:param string: The string to search for.
|
||||
|
||||
This matcher first checks whether the evaluated object is a string. If so,
|
||||
it checks whether it contains ``string``.
|
||||
|
||||
Example::
|
||||
|
||||
contains_string("def")
|
||||
|
||||
will match "abcdefg".
|
||||
|
||||
"""
|
||||
return StringContains(substring)
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.helpers.hasmethod import hasmethod
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
__author__ = "Romilly Cocking"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
|
||||
class StringContainsInOrder(BaseMatcher[str]):
|
||||
def __init__(self, *substrings) -> None:
|
||||
for substring in substrings:
|
||||
if not isinstance(substring, str):
|
||||
raise TypeError(self.__class__.__name__ + " requires string arguments")
|
||||
self.substrings = substrings
|
||||
|
||||
def _matches(self, item: str) -> bool:
|
||||
if not hasmethod(item, "find"):
|
||||
return False
|
||||
from_index = 0
|
||||
for substring in self.substrings:
|
||||
from_index = item.find(substring, from_index)
|
||||
if from_index == -1:
|
||||
return False
|
||||
return True
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_list("a string containing ", ", ", " in order", self.substrings)
|
||||
|
||||
|
||||
def string_contains_in_order(*substrings: str) -> Matcher[str]:
|
||||
"""Matches if object is a string containing a given list of substrings in
|
||||
relative order.
|
||||
|
||||
:param string1,...: A comma-separated list of strings.
|
||||
|
||||
This matcher first checks whether the evaluated object is a string. If so,
|
||||
it checks whether it contains a given list of strings, in relative order to
|
||||
each other. The searches are performed starting from the beginning of the
|
||||
evaluated string.
|
||||
|
||||
Example::
|
||||
|
||||
string_contains_in_order("bc", "fg", "jkl")
|
||||
|
||||
will match "abcdefghijklm".
|
||||
|
||||
"""
|
||||
return StringContainsInOrder(*substrings)
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
from hamcrest.core.helpers.hasmethod import hasmethod
|
||||
from hamcrest.core.matcher import Matcher
|
||||
from hamcrest.library.text.substringmatcher import SubstringMatcher
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
|
||||
class StringEndsWith(SubstringMatcher):
|
||||
def __init__(self, substring) -> None:
|
||||
super(StringEndsWith, self).__init__(substring)
|
||||
|
||||
def _matches(self, item: str) -> bool:
|
||||
if not hasmethod(item, "endswith"):
|
||||
return False
|
||||
return item.endswith(self.substring)
|
||||
|
||||
def relationship(self):
|
||||
return "ending with"
|
||||
|
||||
|
||||
def ends_with(string: str) -> Matcher[str]:
|
||||
"""Matches if object is a string ending with a given string.
|
||||
|
||||
:param string: The string to search for.
|
||||
|
||||
This matcher first checks whether the evaluated object is a string. If so,
|
||||
it checks if ``string`` matches the ending characters of the evaluated
|
||||
object.
|
||||
|
||||
Example::
|
||||
|
||||
ends_with("bar")
|
||||
|
||||
will match "foobar".
|
||||
|
||||
"""
|
||||
return StringEndsWith(string)
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
import re
|
||||
from typing import Pattern, Union
|
||||
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
from hamcrest.core.matcher import Matcher
|
||||
|
||||
__author__ = "Chris Rose"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
|
||||
class StringMatchesPattern(BaseMatcher[str]):
|
||||
def __init__(self, pattern) -> None:
|
||||
self.pattern = pattern
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_text("a string matching '").append_text(
|
||||
self.pattern.pattern
|
||||
).append_text("'")
|
||||
|
||||
def _matches(self, item: str) -> bool:
|
||||
return self.pattern.search(item) is not None
|
||||
|
||||
|
||||
def matches_regexp(pattern: Union[str, Pattern[str]]) -> Matcher[str]:
|
||||
"""Matches if object is a string containing a match for a given regular
|
||||
expression.
|
||||
|
||||
:param pattern: The regular expression to search for.
|
||||
|
||||
This matcher first checks whether the evaluated object is a string. If so,
|
||||
it checks if the regular expression ``pattern`` matches anywhere within the
|
||||
evaluated object.
|
||||
|
||||
"""
|
||||
if isinstance(pattern, str):
|
||||
pattern = re.compile(pattern)
|
||||
|
||||
return StringMatchesPattern(pattern)
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
from hamcrest.core.helpers.hasmethod import hasmethod
|
||||
from hamcrest.core.matcher import Matcher
|
||||
from hamcrest.library.text.substringmatcher import SubstringMatcher
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
|
||||
class StringStartsWith(SubstringMatcher):
|
||||
def __init__(self, substring) -> None:
|
||||
super(StringStartsWith, self).__init__(substring)
|
||||
|
||||
def _matches(self, item: str) -> bool:
|
||||
if not hasmethod(item, "startswith"):
|
||||
return False
|
||||
return item.startswith(self.substring)
|
||||
|
||||
def relationship(self):
|
||||
return "starting with"
|
||||
|
||||
|
||||
def starts_with(substring: str) -> Matcher[str]:
|
||||
"""Matches if object is a string starting with a given string.
|
||||
|
||||
:param string: The string to search for.
|
||||
|
||||
This matcher first checks whether the evaluated object is a string. If so,
|
||||
it checks if ``string`` matches the beginning characters of the evaluated
|
||||
object.
|
||||
|
||||
Example::
|
||||
|
||||
starts_with("foo")
|
||||
|
||||
will match "foobar".
|
||||
|
||||
"""
|
||||
return StringStartsWith(substring)
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
from hamcrest.core.base_matcher import BaseMatcher
|
||||
from hamcrest.core.description import Description
|
||||
|
||||
__author__ = "Jon Reid"
|
||||
__copyright__ = "Copyright 2011 hamcrest.org"
|
||||
__license__ = "BSD, see License.txt"
|
||||
|
||||
|
||||
class SubstringMatcher(BaseMatcher[str], metaclass=ABCMeta):
|
||||
def __init__(self, substring) -> None:
|
||||
if not isinstance(substring, str):
|
||||
raise TypeError(self.__class__.__name__ + " requires string")
|
||||
self.substring = substring
|
||||
|
||||
def describe_to(self, description: Description) -> None:
|
||||
description.append_text("a string ").append_text(self.relationship()).append_text(
|
||||
" "
|
||||
).append_description_of(self.substring)
|
||||
|
||||
@abstractmethod
|
||||
def relationship(self):
|
||||
...
|
||||
0
venv/lib/python3.9/site-packages/hamcrest/py.typed
Normal file
0
venv/lib/python3.9/site-packages/hamcrest/py.typed
Normal file
Loading…
Add table
Add a link
Reference in a new issue