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