Completed
Pull Request — master (#75)
by Cézar
01:32
created

IsDictContainingEntries.matches()   D

Complexity

Conditions 8

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 8
c 2
b 0
f 0
dl 0
loc 28
rs 4
1
from hamcrest.core.base_matcher import BaseMatcher
2
from hamcrest.core.helpers.hasmethod import hasmethod
3
from hamcrest.core.helpers.wrap_matcher import wrap_matcher
4
5
__author__ = "Jon Reid"
6
__copyright__ = "Copyright 2011 hamcrest.org"
7
__license__ = "BSD, see License.txt"
8
9
10
class IsDictContainingEntries(BaseMatcher):
11
12
    def __init__(self, value_matchers):
13
        self.value_matchers = sorted(value_matchers.items())
14
15
    def _not_a_dictionary(self, dictionary, mismatch_description):
16
        if mismatch_description:
17
            mismatch_description.append_description_of(dictionary) \
18
                                .append_text(' is not a mapping object')
19
        return False
20
21
    def matches(self, dictionary, mismatch_description=None):
22
        for key, value_matcher in self.value_matchers:
23
24
            try:
25
                if not key in dictionary:
26
                    if mismatch_description:
27
                        mismatch_description.append_text('no ')             \
28
                                            .append_description_of(key)     \
29
                                            .append_text(' key in ')        \
30
                                            .append_description_of(dictionary)
31
                    return False
32
            except TypeError:
33
                return self._not_a_dictionary(dictionary, mismatch_description)
34
35
            try:
36
                actual_value = dictionary[key]
37
            except TypeError:
38
                return self._not_a_dictionary(dictionary, mismatch_description)
39
40
            if not value_matcher.matches(actual_value):
41
                if mismatch_description:
42
                    mismatch_description.append_text('value for ')  \
43
                                        .append_description_of(key) \
44
                                        .append_text(' ')
45
                    value_matcher.describe_mismatch(actual_value, mismatch_description)
46
                return False
47
48
        return True
49
50
    def describe_mismatch(self, item, mismatch_description):
51
        self.matches(item, mismatch_description)
52
53
    def describe_keyvalue(self, index, value, description):
54
        """Describes key-value pair at given index."""
55
        description.append_description_of(index)                        \
56
                   .append_text(': ')                                   \
57
                   .append_description_of(value)
58
59
    def describe_to(self, description):
60
        description.append_text('a dictionary containing {')
61
        first = True
62
        for key, value in self.value_matchers:
63
            if not first:
64
                description.append_text(', ')
65
            self.describe_keyvalue(key, value, description)
66
            first = False
67
        description.append_text('}')
68
69
70 View Code Duplication
def has_entries(*keys_valuematchers, **kv_args):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
71
    """Matches if dictionary contains entries satisfying a dictionary of keys
72
    and corresponding value matchers.
73
74
    :param matcher_dict: A dictionary mapping keys to associated value matchers,
75
        or to expected values for
76
        :py:func:`~hamcrest.core.core.isequal.equal_to` matching.
77
78
    Note that the keys must be actual keys, not matchers. Any value argument
79
    that is not a matcher is implicitly wrapped in an
80
    :py:func:`~hamcrest.core.core.isequal.equal_to` matcher to check for
81
    equality.
82
83
    Examples::
84
85
        has_entries({'foo':equal_to(1), 'bar':equal_to(2)})
86
        has_entries({'foo':1, 'bar':2})
87
88
    ``has_entries`` also accepts a list of keyword arguments:
89
90
    .. function:: has_entries(keyword1=value_matcher1[, keyword2=value_matcher2[, ...]])
91
92
    :param keyword1: A keyword to look up.
93
    :param valueMatcher1: The matcher to satisfy for the value, or an expected
94
        value for :py:func:`~hamcrest.core.core.isequal.equal_to` matching.
95
96
    Examples::
97
98
        has_entries(foo=equal_to(1), bar=equal_to(2))
99
        has_entries(foo=1, bar=2)
100
101
    Finally, ``has_entries`` also accepts a list of alternating keys and their
102
    value matchers:
103
104
    .. function:: has_entries(key1, value_matcher1[, ...])
105
106
    :param key1: A key (not a matcher) to look up.
107
    :param valueMatcher1: The matcher to satisfy for the value, or an expected
108
        value for :py:func:`~hamcrest.core.core.isequal.equal_to` matching.
109
110
    Examples::
111
112
        has_entries('foo', equal_to(1), 'bar', equal_to(2))
113
        has_entries('foo', 1, 'bar', 2)
114
115
    """
116
    if len(keys_valuematchers) == 1:
117
        try:
118
            base_dict = keys_valuematchers[0].copy()
119
            for key in base_dict:
120
                base_dict[key] = wrap_matcher(base_dict[key])
121
        except AttributeError:
122
            raise ValueError('single-argument calls to has_entries must pass a dict as the argument')
123
    else:
124
        if len(keys_valuematchers) % 2:
125
            raise ValueError('has_entries requires key-value pairs')
126
        base_dict = {}
127
        for index in range(int(len(keys_valuematchers) / 2)):
128
            base_dict[keys_valuematchers[2 * index]] = wrap_matcher(keys_valuematchers[2 * index + 1])
129
130
    for key, value in kv_args.items():
131
        base_dict[key] = wrap_matcher(value)
132
133
    return IsDictContainingEntries(base_dict)
134