deltafi 2.0rc1713530263087__py3-none-any.whl → 2.0rc1714076410617__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of deltafi might be problematic. Click here for more details.

@@ -17,11 +17,12 @@
17
17
  #
18
18
 
19
19
  import json
20
+ import re
20
21
  from abc import ABC
21
22
  from abc import abstractmethod
22
- from typing import List
23
+ from itertools import repeat
23
24
 
24
- from deepdiff import DeepDiff
25
+ from deepdiff import DeepDiff, DeepSearch
25
26
 
26
27
  from .assertions import *
27
28
 
@@ -38,13 +39,250 @@ class GenericCompareHelper(CompareHelper):
38
39
 
39
40
 
40
41
  class JsonCompareHelper(CompareHelper):
41
- def __init__(self, regex_exclusion_list: List):
42
+ """Provides helper functions for comparing JSON/dict objects.
43
+
44
+ Are these two JSON/dict objects equivalent?
45
+ - compare(...)
46
+
47
+ Are these two lists equivalent?
48
+ - compare_lists(...)
49
+
50
+ Is this list a subset/superset of that list?
51
+ - compare_lists_subset(...)
52
+ - compare_lists_superset(...)
53
+
54
+ Select a list of values from an existing list and put them into a new list to facilitate list comparisons:
55
+ - create_list_from_list_using_filter_regex(...)
56
+
57
+ Is this value found (or not) in this JSON/dict object?
58
+ - is_found(...)
59
+ - is_not_found(...)
60
+ """
61
+
62
+ def __init__(self, regex_exclusion_list=None, ignore_order=True):
63
+ """Creates and configures a JsonCompareHelper object. If the optional 'ignore_order' is true, then the order of
64
+ data is ignored when checking else order is enforced. The optional 'regex_exclusion_list' excludes
65
+ paths within the object from comparison; if empty or not provided, then no excludes are applied."""
66
+ if regex_exclusion_list is None:
67
+ regex_exclusion_list = []
42
68
  self.excludes = regex_exclusion_list
69
+ self.ignore_order = ignore_order
70
+
71
+ def __perform_find(self, obj: object, item):
72
+ """Returns a dict of matches of the 'item' in the object 'obj'. The returned dict is empty if there are no
73
+ matches. Excludes path determined by the constructor."""
74
+ return DeepSearch(obj, item, verbose_level=2, exclude_regex_paths=self.excludes)
75
+
76
+ def is_not_found(self, obj: object, item):
77
+ """Returns None if there are no occurrences of 'item' in object 'obj' else returns a ValueError. The argument
78
+ 'item' may be a scalar or a list. Excludes path and failure on ordering of elements are determined by
79
+ the constructor."""
80
+
81
+ all_matches = []
82
+
83
+ if isinstance(item, list):
84
+ for value in item:
85
+ matches = self.__perform_find(obj, value)
86
+ if len(matches) > 0:
87
+ all_matches.append(matches)
88
+ else:
89
+ matches = self.__perform_find(obj, item)
90
+ if len(matches) > 0:
91
+ all_matches.append(matches)
92
+
93
+ if len(all_matches) > 0:
94
+ raise ValueError(f"{all_matches}")
95
+
96
+ assert len(all_matches) == 0
97
+
98
+ def is_found(self, obj: object, item):
99
+ """Returns None if there are no occurrences of 'item' in object 'obj' else returns a ValueError. The argument
100
+ 'item' may be a scalar or a list. Excludes path and failure on ordering of elements are determined by
101
+ the constructor."""
102
+
103
+ all_matches = []
104
+
105
+ if isinstance(item, list):
106
+ for value in item:
107
+ matches = self.__perform_find(obj, value)
108
+ if len(matches) > 0:
109
+ all_matches.append(matches)
110
+ else:
111
+ matches = self.__perform_find(obj, item)
112
+ if len(matches) > 0:
113
+ all_matches.append(matches)
114
+
115
+ if len(all_matches) == 0:
116
+ raise ValueError("No matches found for '" + str(item) + "'")
117
+
118
+ assert len(all_matches) > 0
119
+
120
+ def __perform_diff(self, expected, actual):
121
+ """Returns a dict with differences between 'expected' and 'actual'. The returned dict is empty if 'expected'
122
+ and 'actual' are equivalent. Both 'expected' and 'actual' must be dicts. Excludes path and failure on
123
+ ordering of elements are determined by the constructor. Elements must match number of repetitions."""
124
+ return DeepDiff(expected, actual, ignore_order=self.ignore_order, report_repetition=True,
125
+ exclude_regex_paths=self.excludes)
126
+
127
+ def __perform_diff_with_eval(self, expected, actual):
128
+ """Returns None if 'expected' and 'actual' are equivalent else returns a ValueError. Both 'expected' and
129
+ 'actual' must be dicts. Excludes path and failure on ordering of elements are determined by the
130
+ constructor. Elements must match number of repetitions."""
131
+
132
+ diffs = self.__perform_diff(expected, actual)
43
133
 
44
- def compare(self, expected: str, actual: str, label: str):
45
- exp = json.loads(expected)
46
- act = json.loads(actual)
47
- diffs = DeepDiff(exp, act, exclude_regex_paths=self.excludes)
48
134
  if len(diffs) > 0:
49
135
  raise ValueError(f"{diffs}")
136
+
50
137
  assert len(diffs) == 0
138
+
139
+ def compare(self, expected, actual, label: str):
140
+ """Returns None if 'expected' and 'actual' are equivalent else returns a ValueError. Both 'expected' and
141
+ 'actual' must be either dicts or strings that parse as JSON to dicts. Excludes path and failure on
142
+ ordering of elements are determined by the constructor. Elements must match number of repetitions."""
143
+
144
+ if isinstance(expected, str):
145
+ exp = json.loads(expected)
146
+ else:
147
+ exp = expected
148
+
149
+ if isinstance(actual, str):
150
+ act = json.loads(actual)
151
+ else:
152
+ act = actual
153
+
154
+ return self.__perform_diff_with_eval(exp, act)
155
+
156
+ def compare_lists(self, expected, actual: list, label: str):
157
+ """Returns None if 'actual' is equivalent to 'expected' else returns a ValueError.
158
+
159
+ The 'actual' argument must be a list. The 'expected' argument may be a list or a dict. If a list, then
160
+ 'expected' and 'actual' are compared against each other. If a dict, then 'actual' is equivalent if it
161
+ contains elements with the same repetitions as defined in the 'expected' dict with the key equal to the
162
+ element in 'actual' and the value equal to the number of repetitions.
163
+
164
+ Order of elements is ignored. Elements must match number of repetitions."""
165
+
166
+ expected_list = []
167
+
168
+ if isinstance(expected, dict):
169
+ for key, value in expected.items():
170
+ expected_list += list(repeat(key, value))
171
+ else:
172
+ expected_list = expected
173
+
174
+ return self.__perform_diff_with_eval({"json-compare-helper-internal-list": expected_list},
175
+ {"json-compare-helper-internal-list": actual})
176
+
177
+ @staticmethod
178
+ def compare_lists_subset(expected_subset, actual: list):
179
+ """Returns None if the 'actual' list contains at least 'expected_subset' else returns a ValueError. The
180
+ 'actual' list may contain 0 or more additional elements than defined in 'expected_subset'.
181
+
182
+ The 'actual' argument must be a list. The argument 'expected_subset' may be a list or a dict. In the
183
+ latter case, the key defines the item that must appear in 'actual' and the value defines the number of
184
+ repetitions.
185
+
186
+ Order of elements is ignored. Elements must match number of repetitions."""
187
+
188
+ expected_subset_map = {}
189
+
190
+ if isinstance(expected_subset, list):
191
+ for item in expected_subset:
192
+ value = expected_subset_map.get(item)
193
+ if value is None:
194
+ expected_subset_map[item] = 1
195
+ else:
196
+ expected_subset_map[item] = value + 1
197
+ else:
198
+ expected_subset_map = expected_subset
199
+
200
+ actual_map = {}
201
+
202
+ for item in actual:
203
+ value = actual_map.get(item)
204
+ if value is None:
205
+ actual_map[item] = 1
206
+ else:
207
+ actual_map[item] = value + 1
208
+
209
+ for key, value in expected_subset_map.items():
210
+ actual_value = actual_map.get(key)
211
+ if actual_value is None:
212
+ raise ValueError("Actual list did not contain element '" + str(key) + "'")
213
+ else:
214
+ if actual_value != value:
215
+ raise ValueError("Actual list had item '" + str(key) + "' with repetition " + str(actual_value)
216
+ + " but required repetition " + str(value))
217
+
218
+ return None
219
+
220
+ @staticmethod
221
+ def compare_lists_superset(expected_superset, actual: list):
222
+ """Returns None if the 'actual' list contains only elements that appear in 'expected_superset' else returns a
223
+ ValueError. The 'actual' list cannot contain more elements than are defined in 'expected_superset'; the
224
+ 'expected_superset' may define 0 or more values not contained in 'actual'.
225
+
226
+ The 'actual' argument must be a list. The argument 'expected_superset' may be a list or a dict. In the
227
+ latter case, the key defines the item that must appear in 'actual' and the value defines the number of
228
+ repetitions.
229
+
230
+ Order of elements is ignored. Elements must match number of repetitions."""
231
+
232
+ expected_superset_map = {}
233
+
234
+ if isinstance(expected_superset, list):
235
+ for item in expected_superset:
236
+ value = expected_superset_map.get(item)
237
+ if value is None:
238
+ expected_superset_map[item] = 1
239
+ else:
240
+ expected_superset_map[item] = value + 1
241
+ else:
242
+ expected_superset_map = expected_superset
243
+
244
+ actual_map = {}
245
+
246
+ for item in actual:
247
+ value = actual_map.get(item)
248
+ if value is None:
249
+ actual_map[item] = 1
250
+ else:
251
+ actual_map[item] = value + 1
252
+
253
+ for key, value in actual_map.items():
254
+ expected_value = expected_superset_map.get(key)
255
+ if expected_value is None:
256
+ raise ValueError("Actual list contained element '" + str(key)
257
+ + "' that did not appear in the expected superset")
258
+ else:
259
+ if expected_value != value:
260
+ raise ValueError("Actual list had item '" + str(key) + "' with repetition " + str(value)
261
+ + " but required repetition " + str(expected_value))
262
+
263
+ return None
264
+
265
+ @staticmethod
266
+ def create_list_from_list_using_filter_regex(regex, source_list: list):
267
+ """Creates and returns a list of those items in the 'source_list' matching the regex in the 'regex'. The
268
+ argument 'regex' may be a string or a compiled regex object.
269
+
270
+ For the 'regex', consider using anchors to explicitly match from the start (or end) of a string. For
271
+ example, a string such as "^malware--" will explicitly match from the start of a string."""
272
+
273
+ def search_funct(element):
274
+
275
+ if isinstance(regex, str):
276
+ # 'regex' is a string
277
+ match = re.search(regex, element)
278
+ else:
279
+ # assume 'regex' is a compiled regex pattern object
280
+ match = regex.match(element)
281
+
282
+ if match is None:
283
+ return False
284
+ else:
285
+ return True
286
+
287
+ return list(filter(search_funct, source_list))
288
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: deltafi
3
- Version: 2.0rc1713530263087
3
+ Version: 2.0rc1714076410617
4
4
  Summary: SDK for DeltaFi plugins and actions
5
5
  License: Apache License, Version 2.0
6
6
  Keywords: deltafi
@@ -13,7 +13,7 @@ deltafi/result.py,sha256=rugAkh9NZdl0QDHzyxDoZbkE8dnJNAFLbqBNdIjjn_k,8177
13
13
  deltafi/storage.py,sha256=toq58EPZgzj2TfDF-YpFUmRnsWSjACA0KQAZzkM04xU,2740
14
14
  deltafi/test_kit/__init__.py,sha256=qv6y7PpBG0ekTN9EuD9Lj8JYOLVqZA6tvHwgjplncAM,709
15
15
  deltafi/test_kit/assertions.py,sha256=2eahqmbedhnHXMguiJHdaV-6sx4_jvCbxH4HSzx6PV8,1378
16
- deltafi/test_kit/compare_helpers.py,sha256=Oi07zsq7jlpAHxqjwHsea84acC3lAL3ooG0VC42lLAc,1581
16
+ deltafi/test_kit/compare_helpers.py,sha256=eRA6x1LcFAmKVT7kV00pHXz6crl_FPcEEez9BUJGJqA,12178
17
17
  deltafi/test_kit/constants.py,sha256=epz0OS-qILcc8t7iIs5B3m2-wvZx8FpYpyb19ZsImbI,833
18
18
  deltafi/test_kit/domain.py,sha256=uqpDravlb6E3Ddm4QpRaJTdiVuQ2MMdZnzc4Oi9EZJA,2090
19
19
  deltafi/test_kit/egress.py,sha256=nF7YYak-_X35m7h2rSIcju86IJBf8cVaFHs7xw7-_b8,1899
@@ -21,6 +21,6 @@ deltafi/test_kit/enrich.py,sha256=M682cqInVS4aipI0jOrR25x7m0ErrpjGpebXGZDPwxo,25
21
21
  deltafi/test_kit/framework.py,sha256=m11V6JMZHn4oXnmB_pEgI5p7fZWCWAO7HjHLunSh5AA,14602
22
22
  deltafi/test_kit/transform.py,sha256=q9zQdZswTKwU5Om8E9Lwg0G5JI8LEXuRZjT9oam-Zoo,4085
23
23
  deltafi/test_kit/validate.py,sha256=NnXD5amOE_9a4Zr-v9DKYIv5f_HvwtmlI-IdFtVp7ko,1933
24
- deltafi-2.0rc1713530263087.dist-info/METADATA,sha256=M8bj2tsQIgAkHIKkYzYQ3eAT6A51colJHptMVKPJnMo,1446
25
- deltafi-2.0rc1713530263087.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
26
- deltafi-2.0rc1713530263087.dist-info/RECORD,,
24
+ deltafi-2.0rc1714076410617.dist-info/METADATA,sha256=LXznZYJQVwFe6BQS9465IqiDxCG46eVusE89NJTX5fE,1446
25
+ deltafi-2.0rc1714076410617.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
26
+ deltafi-2.0rc1714076410617.dist-info/RECORD,,