deltafi 2.0rc1714076410617__tar.gz → 2.0rc1716000130240__tar.gz

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.

Files changed (26) hide show
  1. {deltafi-2.0rc1714076410617 → deltafi-2.0rc1716000130240}/PKG-INFO +5 -5
  2. {deltafi-2.0rc1714076410617 → deltafi-2.0rc1716000130240}/deltafi/action.py +13 -9
  3. {deltafi-2.0rc1714076410617 → deltafi-2.0rc1716000130240}/deltafi/actiontype.py +2 -0
  4. {deltafi-2.0rc1714076410617 → deltafi-2.0rc1716000130240}/deltafi/domain.py +52 -24
  5. {deltafi-2.0rc1714076410617 → deltafi-2.0rc1716000130240}/deltafi/plugin.py +7 -4
  6. {deltafi-2.0rc1714076410617 → deltafi-2.0rc1716000130240}/deltafi/result.py +6 -6
  7. {deltafi-2.0rc1714076410617 → deltafi-2.0rc1716000130240}/deltafi/test_kit/compare_helpers.py +23 -18
  8. {deltafi-2.0rc1714076410617 → deltafi-2.0rc1716000130240}/deltafi/test_kit/framework.py +18 -53
  9. {deltafi-2.0rc1714076410617 → deltafi-2.0rc1716000130240}/pyproject.toml +8 -8
  10. {deltafi-2.0rc1714076410617 → deltafi-2.0rc1716000130240}/README.md +0 -0
  11. {deltafi-2.0rc1714076410617 → deltafi-2.0rc1716000130240}/deltafi/__init__.py +0 -0
  12. {deltafi-2.0rc1714076410617 → deltafi-2.0rc1716000130240}/deltafi/actioneventqueue.py +0 -0
  13. {deltafi-2.0rc1714076410617 → deltafi-2.0rc1716000130240}/deltafi/exception.py +0 -0
  14. {deltafi-2.0rc1714076410617 → deltafi-2.0rc1716000130240}/deltafi/genericmodel.py +0 -0
  15. {deltafi-2.0rc1714076410617 → deltafi-2.0rc1716000130240}/deltafi/input.py +0 -0
  16. {deltafi-2.0rc1714076410617 → deltafi-2.0rc1716000130240}/deltafi/logger.py +0 -0
  17. {deltafi-2.0rc1714076410617 → deltafi-2.0rc1716000130240}/deltafi/metric.py +0 -0
  18. {deltafi-2.0rc1714076410617 → deltafi-2.0rc1716000130240}/deltafi/storage.py +0 -0
  19. {deltafi-2.0rc1714076410617 → deltafi-2.0rc1716000130240}/deltafi/test_kit/__init__.py +0 -0
  20. {deltafi-2.0rc1714076410617 → deltafi-2.0rc1716000130240}/deltafi/test_kit/assertions.py +0 -0
  21. {deltafi-2.0rc1714076410617 → deltafi-2.0rc1716000130240}/deltafi/test_kit/constants.py +0 -0
  22. {deltafi-2.0rc1714076410617 → deltafi-2.0rc1716000130240}/deltafi/test_kit/domain.py +0 -0
  23. {deltafi-2.0rc1714076410617 → deltafi-2.0rc1716000130240}/deltafi/test_kit/egress.py +0 -0
  24. {deltafi-2.0rc1714076410617 → deltafi-2.0rc1716000130240}/deltafi/test_kit/enrich.py +0 -0
  25. {deltafi-2.0rc1714076410617 → deltafi-2.0rc1716000130240}/deltafi/test_kit/transform.py +0 -0
  26. {deltafi-2.0rc1714076410617 → deltafi-2.0rc1716000130240}/deltafi/test_kit/validate.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: deltafi
3
- Version: 2.0rc1714076410617
3
+ Version: 2.0rc1716000130240
4
4
  Summary: SDK for DeltaFi plugins and actions
5
5
  License: Apache License, Version 2.0
6
6
  Keywords: deltafi
@@ -20,11 +20,11 @@ Classifier: Programming Language :: Python :: 3.12
20
20
  Classifier: Topic :: Software Development
21
21
  Requires-Dist: deepdiff (>=6.7.1)
22
22
  Requires-Dist: json-logging (>=1.3.0)
23
- Requires-Dist: minio (>=7.2.3)
24
- Requires-Dist: pydantic (>=2.5.3)
25
- Requires-Dist: redis (>=5.0.1)
23
+ Requires-Dist: minio (>=7.2.5)
24
+ Requires-Dist: pydantic (>=2.7.1)
25
+ Requires-Dist: redis (>=5.0.4)
26
26
  Requires-Dist: requests (>=2.31.0)
27
- Requires-Dist: urllib3 (>=2.1.0)
27
+ Requires-Dist: urllib3 (>=2.2.1)
28
28
  Project-URL: Bug Reports, https://chat.deltafi.org/deltafi/channels/bug-reports
29
29
  Project-URL: Documentation, https://docs.deltafi.org/#/
30
30
  Project-URL: Source Code, https://gitlab.com/deltafi/deltafi
@@ -20,8 +20,8 @@ from abc import ABC, abstractmethod
20
20
  from typing import Any, List
21
21
 
22
22
  from deltafi.actiontype import ActionType
23
- from deltafi.genericmodel import GenericModel
24
23
  from deltafi.domain import Context, DeltaFileMessage
24
+ from deltafi.genericmodel import GenericModel
25
25
  from deltafi.input import EgressInput, TransformInput
26
26
  from deltafi.result import *
27
27
  from pydantic import BaseModel
@@ -48,20 +48,23 @@ class Action(ABC):
48
48
  def execute_action(self, event):
49
49
  if event.delta_file_messages is None or not len(event.delta_file_messages):
50
50
  raise RuntimeError(f"Received event with no delta file messages for did {event.context.did}")
51
-
52
51
  if event.context.collect is not None:
53
- result = self.execute(event.context, self.collect([self.build_input(event.context, delta_file_message)
54
- for delta_file_message in event.delta_file_messages]),
55
- self.param_class().model_validate(event.params))
52
+ result = self.execute(
53
+ event.context,
54
+ self.collect([self.build_input(event.context, delta_file_message)
55
+ for delta_file_message in event.delta_file_messages]),
56
+ self.param_class().model_validate(event.params))
56
57
  else:
57
- result = self.execute(event.context, self.build_input(event.context, event.delta_file_messages[0]),
58
- self.param_class().model_validate(event.params))
58
+ result = self.execute(
59
+ event.context,
60
+ self.build_input(event.context, event.delta_file_messages[0]),
61
+ self.param_class().model_validate(event.params))
59
62
 
60
63
  self.validate_type(result)
61
64
  return result
62
65
 
63
66
  @staticmethod
64
- def param_class( ):
67
+ def param_class():
65
68
  """Factory method to create and return an empty GenericModel instance.
66
69
 
67
70
  Returns
@@ -110,7 +113,8 @@ class TimedIngressAction(Action, ABC):
110
113
 
111
114
  class TransformAction(Action, ABC):
112
115
  def __init__(self, description: str):
113
- super().__init__(ActionType.TRANSFORM, description, (TransformResult, TransformResults, ErrorResult, FilterResult))
116
+ super().__init__(ActionType.TRANSFORM, description,
117
+ (TransformResult, TransformResults, ErrorResult, FilterResult))
114
118
 
115
119
  def build_input(self, context: Context, delta_file_message: DeltaFileMessage):
116
120
  return TransformInput(content=delta_file_message.content_list, metadata=delta_file_message.metadata)
@@ -20,7 +20,9 @@ from enum import Enum
20
20
 
21
21
 
22
22
  class ActionType(Enum):
23
+ INGRESS = "INGRESS"
23
24
  TIMED_INGRESS = "TIMED_INGRESS"
24
25
  TRANSFORM = "TRANSFORM"
25
26
  EGRESS = "EGRESS"
27
+ PUBLISH = "PUBLISH"
26
28
  UNKNOWN = "UNKNOWN"
@@ -40,13 +40,15 @@ class ActionExecution(NamedTuple):
40
40
 
41
41
  class Context(NamedTuple):
42
42
  did: str
43
- action_flow: str
43
+ delta_file_name: str
44
+ data_source: str
45
+ flow_name: str
46
+ flow_id: str
44
47
  action_name: str
45
- source_filename: str
46
- ingress_flow: str
47
- egress_flow: str
48
- system: str
48
+ action_id: str
49
+ action_version: str
49
50
  hostname: str
51
+ system_name: str
50
52
  content_service: ContentService
51
53
  collect: dict = None
52
54
  collected_dids: List[str] = None
@@ -56,19 +58,42 @@ class Context(NamedTuple):
56
58
  @classmethod
57
59
  def create(cls, context: dict, hostname: str, content_service: ContentService, logger: Logger):
58
60
  did = context['did']
59
- action_name_parts = context['name'].split(".")
60
- action_flow = action_name_parts[0]
61
- action_name = action_name_parts[1]
62
- if 'sourceFilename' in context:
63
- source_filename = context['sourceFilename']
61
+ if 'deltaFileName' in context:
62
+ delta_file_name = context['deltaFileName']
64
63
  else:
65
- source_filename = None
66
- ingress_flow = context['ingressFlow']
67
- if 'egressFlow' in context:
68
- egress_flow = context['egressFlow']
64
+ delta_file_name = None
65
+ if 'dataSource' in context:
66
+ data_source = context['dataSource']
69
67
  else:
70
- egress_flow = None
71
- system = context['systemName']
68
+ data_source = None
69
+ if 'flowName' in context:
70
+ flow_name = context['flowName']
71
+ else:
72
+ flow_name = None
73
+ if 'flowId' in context:
74
+ flow_id = context['flowId']
75
+ else:
76
+ flow_id = None
77
+ if 'actionName' in context:
78
+ action_name = context['actionName']
79
+ else:
80
+ action_name = None
81
+ if 'actionId' in context:
82
+ action_id = context['actionId']
83
+ else:
84
+ action_id = None
85
+ if 'actionVersion' in context:
86
+ action_version = context['actionVersion']
87
+ else:
88
+ action_version = None
89
+ if 'hostname' in context:
90
+ hostname = context['hostname']
91
+ else:
92
+ hostname = None
93
+ if 'systemName' in context:
94
+ system_name = context['systemName']
95
+ else:
96
+ system_name = None
72
97
  if 'collect' in context:
73
98
  collect = context['collect']
74
99
  else:
@@ -81,18 +106,21 @@ class Context(NamedTuple):
81
106
  memo = context['memo']
82
107
  else:
83
108
  memo = None
109
+
84
110
  return Context(did=did,
85
- action_flow=action_flow,
111
+ delta_file_name=delta_file_name,
112
+ data_source=data_source,
113
+ flow_name=flow_name,
114
+ flow_id=flow_id,
86
115
  action_name=action_name,
87
- source_filename=source_filename,
88
- ingress_flow=ingress_flow,
89
- egress_flow=egress_flow,
90
- system=system,
116
+ action_id=action_id,
117
+ action_version=action_version,
91
118
  hostname=hostname,
92
- content_service=content_service,
119
+ system_name=system_name,
93
120
  collect=collect,
94
121
  collected_dids=collected_dids,
95
122
  memo=memo,
123
+ content_service=content_service,
96
124
  logger=logger)
97
125
 
98
126
 
@@ -197,7 +225,6 @@ class Content:
197
225
 
198
226
  return new_segments
199
227
 
200
-
201
228
  def get_size(self):
202
229
  """
203
230
  Returns the size of the content in bytes.
@@ -318,7 +345,8 @@ class Event(NamedTuple):
318
345
 
319
346
  @classmethod
320
347
  def create(cls, event: dict, hostname: str, content_service: ContentService, logger: Logger):
321
- delta_file_messages = [DeltaFileMessage.from_dict(delta_file_message, content_service) for delta_file_message in event['deltaFileMessages']]
348
+ delta_file_messages = [DeltaFileMessage.from_dict(delta_file_message, content_service) for delta_file_message in
349
+ event['deltaFileMessages']]
322
350
  context = Context.create(event['actionContext'], hostname, content_service, logger)
323
351
  params = event['actionParams']
324
352
  queue_name = None
@@ -46,9 +46,9 @@ def _coordinates():
46
46
 
47
47
 
48
48
  def _setup_queue(max_connections):
49
- redis_url = os.getenv('REDIS_URL', 'http://deltafi-redis-master:6379')
50
- password = os.getenv('REDIS_PASSWORD')
51
- return ActionEventQueue(redis_url, max_connections, password)
49
+ url = os.getenv('VALKEY_URL', 'http://deltafi-valkey-master:6379')
50
+ password = os.getenv('VALKEY_PASSWORD')
51
+ return ActionEventQueue(url, max_connections, password)
52
52
 
53
53
 
54
54
  def _setup_content_service():
@@ -286,7 +286,10 @@ class Plugin(object):
286
286
 
287
287
  response = {
288
288
  'did': event.context.did,
289
- 'action': event.context.action_flow + "." + event.context.action_name,
289
+ 'flowName': event.context.flow_name,
290
+ 'flowId': event.context.flow_id,
291
+ 'actionName': event.context.action_name,
292
+ 'actionId': event.context.action_id,
290
293
  'start': start_time,
291
294
  'stop': time.time(),
292
295
  'type': result.result_type,
@@ -96,12 +96,12 @@ class FilterResult(Result):
96
96
 
97
97
 
98
98
  class IngressResultItem:
99
- def __init__(self, context: Context, filename: str):
99
+ def __init__(self, context: Context, delta_file_name: str):
100
100
  self.context = context
101
- self.filename = filename
102
101
  self._did = str(uuid.uuid4())
103
102
  self.content = []
104
103
  self.metadata = {}
104
+ self.delta_file_name = delta_file_name
105
105
 
106
106
  @property
107
107
  def did(self):
@@ -140,7 +140,7 @@ class IngressResultItem:
140
140
  def response(self):
141
141
  return {
142
142
  'did': self._did,
143
- 'filename': self.filename,
143
+ 'deltaFileName': self.delta_file_name,
144
144
  'metadata': self.metadata,
145
145
  'content': [content.json() for content in self.content]
146
146
  }
@@ -156,10 +156,10 @@ class IngressResult(Result):
156
156
  def __init__(self, context: Context):
157
157
  super().__init__('ingress', 'INGRESS', context)
158
158
  self.memo = None
159
- self.execute_immediate = False
160
159
  self.ingress_result_items = []
160
+ self.execute_immediate = False
161
161
  self.status = IngressStatusEnum.HEALTHY
162
- self.statusMessage = None
162
+ self.status_message = None
163
163
 
164
164
  def add_item(self, ingress_result_item: IngressResultItem):
165
165
  self.ingress_result_items.append(ingress_result_item)
@@ -171,7 +171,7 @@ class IngressResult(Result):
171
171
  'executeImmediate': self.execute_immediate,
172
172
  'ingressItems': [ingress_result_item.response() for ingress_result_item in self.ingress_result_items],
173
173
  'status': self.status.value,
174
- 'statusMessage': self.statusMessage
174
+ 'statusMessage': self.status_message
175
175
  }
176
176
 
177
177
 
@@ -69,14 +69,17 @@ class JsonCompareHelper(CompareHelper):
69
69
  self.ignore_order = ignore_order
70
70
 
71
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)
72
+ """Returns a dict of matches of the 'item' in the object 'obj'. Both keys and values of dicts are included in
73
+ the search. The item may be compiled regex pattern or a string that compiles to a regex pattern. The
74
+ returned dict is empty if there are no matches. Excludes path(s) determined by the constructor."""
75
+ return DeepSearch(obj, item, verbose_level=2, exclude_regex_paths=self.excludes, use_regexp=True)
75
76
 
76
77
  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."""
78
+ """Returns None if there are no occurrences of 'item' in object 'obj' else raises a ValueError. Both keys and
79
+ values of dicts are included in the search. If 'item' is a list, then all elements of item must not be
80
+ found in list, else a ValueError is raised. The argument 'item' may be a compiled regex pattern, a
81
+ string that compiles to a regex pattern, or a list of either or both. Excludes path(s) and failure on
82
+ ordering of elements are determined by the constructor."""
80
83
 
81
84
  all_matches = []
82
85
 
@@ -91,31 +94,33 @@ class JsonCompareHelper(CompareHelper):
91
94
  all_matches.append(matches)
92
95
 
93
96
  if len(all_matches) > 0:
94
- raise ValueError(f"{all_matches}")
97
+ raise ValueError("Matches found for items '" + f"{all_matches}" + "'")
95
98
 
96
99
  assert len(all_matches) == 0
97
100
 
98
101
  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
+ """Returns None if 'item' occurs in object 'obj' else raises a ValueError. Both keys and values of dicts are
103
+ included in the search. If 'item' is a list, then all elements of item must occur in the object else a
104
+ ValueError is returned. The argument 'item' may be a compiled regex pattern, a string that compiles to
105
+ a regex pattern, or a list of either or both. Excludes path(s) and failure on ordering of elements are
106
+ determined by the constructor."""
102
107
 
103
- all_matches = []
108
+ not_found_items = []
104
109
 
105
110
  if isinstance(item, list):
106
111
  for value in item:
107
112
  matches = self.__perform_find(obj, value)
108
- if len(matches) > 0:
109
- all_matches.append(matches)
113
+ if len(matches) == 0:
114
+ not_found_items.append(value)
110
115
  else:
111
116
  matches = self.__perform_find(obj, item)
112
- if len(matches) > 0:
113
- all_matches.append(matches)
117
+ if len(matches) == 0:
118
+ not_found_items.append(item)
114
119
 
115
- if len(all_matches) == 0:
116
- raise ValueError("No matches found for '" + str(item) + "'")
120
+ if len(not_found_items) > 0:
121
+ raise ValueError("No matches found for items '" + f"{not_found_items}" + "'")
117
122
 
118
- assert len(all_matches) > 0
123
+ assert len(not_found_items) == 0
119
124
 
120
125
  def __perform_diff(self, expected, actual):
121
126
  """Returns a dict with differences between 'expected' and 'actual'. The returned dict is empty if 'expected'
@@ -57,6 +57,7 @@ class IOContent:
57
57
  self.content_type = content_type
58
58
  self.offset = offset
59
59
  self.content_bytes = content_bytes
60
+ self.segment_uuid = uuid.uuid4()
60
61
 
61
62
  @classmethod
62
63
  def file_type(cls, name: str):
@@ -80,7 +81,7 @@ class LoadedContent:
80
81
  else:
81
82
  self.data = ioc.content_bytes
82
83
  self.segment = Segment.from_dict(
83
- {"uuid": str(uuid.uuid4()), "offset": self.offset, "size": len(self.data), "did": did})
84
+ {"uuid": str(ioc.segment_uuid), "offset": self.offset, "size": len(self.data), "did": did})
84
85
 
85
86
 
86
87
  class InternalContentService:
@@ -188,21 +189,10 @@ class ActionTest(ABC):
188
189
 
189
190
  def __reset__(self, did: str):
190
191
  self.content_service = InternalContentService()
191
- <<<<<<< HEAD
192
192
  if did is None:
193
193
  self.did = str(uuid.uuid4())
194
194
  else:
195
195
  self.did = did
196
- self.expected_outputs = []
197
- ||||||| parent of 831733c7 (2.0 Refactor)
198
- self.did = str(uuid.uuid4())
199
- self.expected_outputs = []
200
- =======
201
- if did is None:
202
- self.did = str(uuid.uuid4())
203
- else:
204
- self.did = did
205
- >>>>>>> 831733c7 (2.0 Refactor)
206
196
  self.loaded_inputs = []
207
197
  self.res_path = ""
208
198
 
@@ -237,36 +227,23 @@ class ActionTest(ABC):
237
227
  content_list = self.make_content_list(test_case)
238
228
  self.content_service.load(self.loaded_inputs)
239
229
 
240
- <<<<<<< HEAD
241
- return DeltaFileMessage(
242
- metadata=test_case.in_meta,
243
- content_list=content_list,
244
- domains=test_case.in_domains,
245
- enrichments=test_case.in_enrichments)
246
- ||||||| parent of 831733c7 (2.0 Refactor)
247
- return DeltaFileMessage(metadata=test_case.in_meta,
248
- content_list=content_list,
249
- domains=test_case.in_domains,
250
- enrichments=test_case.in_enrichments)
251
- =======
252
230
  return DeltaFileMessage(metadata=test_case.in_meta,
253
231
  content_list=content_list)
254
- >>>>>>> 831733c7 (2.0 Refactor)
255
232
 
256
233
  def make_context(self, test_case: TestCaseBase):
257
234
  action_name = INGRESS_FLOW + "." + test_case.action.__class__.__name__
258
235
  return Context(
259
236
  did=self.did,
260
- action_flow=INGRESS_FLOW,
237
+ delta_file_name=test_case.file_name,
238
+ data_source="DATASRC",
239
+ flow_name=INGRESS_FLOW,
240
+ flow_id="FLOWID",
261
241
  action_name=action_name,
262
- source_filename=test_case.file_name,
263
- ingress_flow=INGRESS_FLOW,
264
- egress_flow=EGRESS_FLOW,
265
- system=SYSTEM,
242
+ action_id="ACTIONID",
243
+ action_version="1.0",
266
244
  hostname=HOSTNAME,
245
+ system_name=SYSTEM,
267
246
  content_service=self.content_service,
268
- collect=None,
269
- collected_dids=None,
270
247
  logger=get_logger())
271
248
 
272
249
  def make_event(self, test_case: TestCaseBase):
@@ -312,39 +289,27 @@ class ActionTest(ABC):
312
289
  else:
313
290
  raise ValueError(f"unknown type: {test_case.expected_result_type}")
314
291
 
315
- def compare_content_details(self, expected: LoadedContent, actual: Content):
292
+ @staticmethod
293
+ def compare_content_details(expected: LoadedContent, actual: Content):
316
294
  assert_equal(expected.content_type, actual.media_type)
317
295
  assert_equal(expected.name, actual.name)
318
296
 
319
- def compare_one_content(self, comparitor: CompareHelper, expected: LoadedContent, actual, index):
297
+ def compare_one_content(self, comparator: CompareHelper, expected: LoadedContent, actual, index):
320
298
  self.compare_content_details(expected, actual)
321
299
  seg_id = actual.segments[0].uuid
322
- comparitor.compare(expected.data, self.content_service.get_output(seg_id), f"Content[{index}]")
300
+ comparator.compare(expected.data, self.content_service.get_output(seg_id), f"Content[{index}]")
323
301
 
324
- def compare_content_list(self, comparitor: CompareHelper, expected_outputs: List[IOContent], content: List):
302
+ def compare_content_list(self, comparator: CompareHelper, expected_outputs: List[IOContent], content: List):
325
303
  assert_equal_len(expected_outputs, content)
326
304
  for index, expected_ioc in enumerate(expected_outputs):
327
305
  if len(expected_ioc.content_bytes) == 0:
328
- expected = LoadedContent(self.did, expected_ioc, self.load_file(output_ioc))
306
+ expected = LoadedContent(self.did, expected_ioc, self.load_file(expected_ioc))
329
307
  else:
330
308
  expected = LoadedContent(self.did, expected_ioc, None)
331
- self.compare_one_content(comparitor, expected, content[index], index)
332
-
333
- def compare_one_metric(self, expected: Metric, result: Metric):
334
- assert expected.name == result.name
335
- assert_equal_with_label(expected.value, result.value, expected.name)
336
- assert_keys_and_values(expected.tags, result.tags)
337
-
338
- expected_value = expected['value']
339
- if type(expected_value) == str:
340
- comparitor.compare(expected_value, actual['value'], f"Domain[{index}]")
341
- elif type(expected_value) == IOContent:
342
- expected_data = self.load_file(expected_value)
343
- comparitor.compare(expected_data, actual['value'], f"Domain[{index}]")
344
- else:
345
- raise ValueError(f"unknown expected_value type: {type(expected_value)}")
309
+ self.compare_one_content(comparator, expected, content[index], index)
346
310
 
347
- def compare_one_metric(self, expected: Metric, result: Metric):
311
+ @staticmethod
312
+ def compare_one_metric(expected: Metric, result: Metric):
348
313
  assert expected.name == result.name
349
314
  assert_equal_with_label(expected.value, result.value, expected.name)
350
315
  assert_keys_and_values(expected.tags, result.tags)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "deltafi"
3
- version = "2.0rc1714076410617"
3
+ version = "2.0rc1716000130240"
4
4
  description = "SDK for DeltaFi plugins and actions"
5
5
  authors = ["DeltaFi <deltafi@systolic.com>"]
6
6
  license = "Apache License, Version 2.0"
@@ -23,19 +23,19 @@ classifiers = [
23
23
  python = "^3.9"
24
24
  deepdiff = ">=6.7.1"
25
25
  json-logging = ">=1.3.0"
26
- minio = ">=7.2.3"
27
- pydantic = ">=2.5.3"
28
- redis = ">=5.0.1"
26
+ minio = ">=7.2.5"
27
+ pydantic = ">=2.7.1"
28
+ redis = ">=5.0.4"
29
29
  requests = ">=2.31.0"
30
- urllib3 = ">=2.1.0"
30
+ urllib3 = ">=2.2.1"
31
31
 
32
32
  [tool.poetry.group.test]
33
33
  optional = true
34
34
 
35
35
  [tool.poetry.group.test.dependencies]
36
- pytest = ">=7.4.4"
37
- pytest-mock = ">=3.12.0"
38
- mockito = ">=1.4.0"
36
+ pytest = ">=8.1.1"
37
+ pytest-mock = ">=3.14.0"
38
+ mockito = ">=1.5.0"
39
39
 
40
40
  [tool.poetry.urls]
41
41
  'Source Code' = "https://gitlab.com/deltafi/deltafi"