Issues (6)

library/collection/isdict_containingentries.py (1 issue)

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
def has_entries(*keys_valuematchers, **kv_args):
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:
0 ignored issues
show
This code seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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