ripple-down-rules 0.1.67__py3-none-any.whl → 0.1.69__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.
@@ -225,6 +225,8 @@ def parse_string_to_expression(expression_str: str) -> AST:
225
225
  :param expression_str: The string which will be parsed.
226
226
  :return: The parsed expression.
227
227
  """
228
+ if not expression_str.startswith('def'):
229
+ expression_str = encapsulate_user_input(expression_str, CallableExpression.encapsulating_function)
228
230
  mode = 'exec' if expression_str.startswith('def') else 'eval'
229
231
  tree = ast.parse(expression_str, mode=mode)
230
232
  logging.debug(f"AST parsed successfully: {ast.dump(tree)}")
@@ -11,7 +11,7 @@ from sqlalchemy.orm import DeclarativeBase as SQLTable, MappedColumn as SQLColum
11
11
  from typing_extensions import Any, Optional, Dict, Type, Set, Hashable, Union, List, TYPE_CHECKING
12
12
 
13
13
  from ..utils import make_set, row_to_dict, table_rows_as_str, get_value_type_from_type_hint, SubclassJSONSerializer, \
14
- get_full_class_name, get_type_from_string, make_list, is_iterable, serialize_dataclass
14
+ get_full_class_name, get_type_from_string, make_list, is_iterable, serialize_dataclass, dataclass_to_dict
15
15
 
16
16
  if TYPE_CHECKING:
17
17
  from ripple_down_rules.rules import Rule
@@ -99,8 +99,7 @@ class Case(UserDict, SubclassJSONSerializer):
99
99
  obj_type = get_type_from_string(data.pop("_obj_type"))
100
100
  name = data.pop("_name")
101
101
  for k, v in data.items():
102
- if isinstance(v, dict) and "_type" in v:
103
- data[k] = SubclassJSONSerializer.from_json(v)
102
+ data[k] = SubclassJSONSerializer.from_json(v)
104
103
  return cls(_obj_type=obj_type, _id=id_, _name=name, **data)
105
104
 
106
105
 
@@ -219,7 +218,7 @@ def create_case(obj: Any, recursion_idx: int = 0, max_recursion_idx: int = 0,
219
218
  obj_name = obj_name or obj.__class__.__name__
220
219
  if isinstance(obj, DataFrame):
221
220
  return create_cases_from_dataframe(obj, obj_name)
222
- if isinstance(obj, Case):
221
+ if isinstance(obj, Case) or (is_dataclass(obj) and not isinstance(obj, SQLTable)):
223
222
  return obj
224
223
  if ((recursion_idx > max_recursion_idx) or (obj.__class__.__module__ == "builtins")
225
224
  or (obj.__class__ in [MetaData, registry])):
@@ -318,6 +317,10 @@ def show_current_and_corner_cases(case: Any, targets: Optional[Dict[str, Any]] =
318
317
  case_dict = row_to_dict(case)
319
318
  if last_evaluated_rule and last_evaluated_rule.fired:
320
319
  corner_row_dict = row_to_dict(last_evaluated_rule.corner_case)
320
+ elif is_dataclass(case):
321
+ case_dict = dataclass_to_dict(case)
322
+ if last_evaluated_rule and last_evaluated_rule.fired:
323
+ corner_row_dict = dataclass_to_dict(last_evaluated_rule.corner_case)
321
324
  else:
322
325
  case_dict = case
323
326
  if last_evaluated_rule and last_evaluated_rule.fired:
ripple_down_rules/rdr.py CHANGED
@@ -211,17 +211,17 @@ class RDRWithCodeWriter(RippleDownRules, ABC):
211
211
  func_def = f"def classify(case: {self.case_type.__name__}) -> {self.conclusion_type_hint}:\n"
212
212
  file_name = file_path + f"/{self.generated_python_file_name}.py"
213
213
  defs_file_name = file_path + f"/{self.generated_python_defs_file_name}.py"
214
- imports = self._get_imports()
214
+ imports, defs_imports = self._get_imports()
215
215
  # clear the files first
216
216
  with open(defs_file_name, "w") as f:
217
- f.write(imports + "\n\n")
217
+ f.write(defs_imports + "\n\n")
218
218
  with open(file_name, "w") as f:
219
219
  imports += f"from .{self.generated_python_defs_file_name} import *\n"
220
220
  imports += f"from ripple_down_rules.rdr import {self.__class__.__name__}\n"
221
221
  f.write(imports + "\n\n")
222
- f.write(f"conclusion_type = ({', '.join([ct.__name__ for ct in self.conclusion_type])},)\n\n")
223
- f.write(f"type_ = {self.__class__.__name__}\n\n")
224
- f.write(func_def)
222
+ f.write(f"conclusion_type = ({', '.join([ct.__name__ for ct in self.conclusion_type])},)\n")
223
+ f.write(f"type_ = {self.__class__.__name__}\n")
224
+ f.write(f"\n\n{func_def}")
225
225
  f.write(f"{' ' * 4}if not isinstance(case, Case):\n"
226
226
  f"{' ' * 4} case = create_case(case, max_recursion_idx=3)\n""")
227
227
  self.write_rules_as_source_code_to_file(self.start_rule, f, " " * 4, defs_file=defs_file_name)
@@ -234,17 +234,11 @@ class RDRWithCodeWriter(RippleDownRules, ABC):
234
234
  """
235
235
  pass
236
236
 
237
- def _get_imports(self) -> str:
237
+ def _get_imports(self) -> Tuple[str, str]:
238
238
  """
239
239
  :return: The imports for the generated python file of the RDR as a string.
240
240
  """
241
- imports = ""
242
- if self.case_type.__module__ != "builtins":
243
- imports += f"from {self.case_type.__module__} import {self.case_type.__name__}\n"
244
- for conclusion_type in self.conclusion_type:
245
- if conclusion_type.__module__ != "builtins":
246
- imports += f"from {conclusion_type.__module__} import {conclusion_type.__name__}\n"
247
- imports += "from ripple_down_rules.datastructures.case import Case, create_case\n"
241
+ defs_imports = ""
248
242
  for rule in [self.start_rule] + list(self.start_rule.descendants):
249
243
  if not rule.conditions:
250
244
  continue
@@ -255,10 +249,21 @@ class RDRWithCodeWriter(RippleDownRules, ABC):
255
249
  if not hasattr(v, "__module__") or not hasattr(v, "__name__"):
256
250
  continue
257
251
  new_imports = f"from {v.__module__} import {v.__name__}\n"
258
- if new_imports in imports:
252
+ if new_imports in defs_imports:
259
253
  continue
260
- imports += new_imports
261
- return imports
254
+ defs_imports += new_imports
255
+ imports = ""
256
+ if self.case_type.__module__ != "builtins":
257
+ new_import = f"from {self.case_type.__module__} import {self.case_type.__name__}\n"
258
+ if new_import not in defs_imports:
259
+ imports += new_import
260
+ for conclusion_type in self.conclusion_type:
261
+ if conclusion_type.__module__ != "builtins":
262
+ new_import = f"from {conclusion_type.__module__} import {conclusion_type.__name__}\n"
263
+ if new_import not in defs_imports:
264
+ imports += new_import
265
+ imports += "from ripple_down_rules.datastructures.case import Case, create_case\n"
266
+ return imports, defs_imports
262
267
 
263
268
  def get_rdr_classifier_from_python_file(self, package_name: str) -> Callable[[Any], Any]:
264
269
  """
@@ -293,7 +298,11 @@ class RDRWithCodeWriter(RippleDownRules, ABC):
293
298
  """
294
299
  :return: The default generated python file name.
295
300
  """
296
- return f"{self.start_rule.corner_case._name.lower()}_{self.attribute_name}_{self.acronym.lower()}"
301
+ if isinstance(self.start_rule.corner_case, Case):
302
+ name = self.start_rule.corner_case._name
303
+ else:
304
+ name = self.start_rule.corner_case.__class__.__name__
305
+ return f"{name.lower()}_{self.attribute_name}_{self.acronym.lower()}"
297
306
 
298
307
  @property
299
308
  def generated_python_defs_file_name(self) -> str:
@@ -539,13 +548,21 @@ class MultiClassRDR(RDRWithCodeWriter):
539
548
 
540
549
  @property
541
550
  def conclusion_type_hint(self) -> str:
542
- return f"Set[Union[{', '.join([ct.__name__ for ct in self.conclusion_type if ct not in [list, set]])}]]"
551
+ conclusion_types = [ct.__name__ for ct in self.conclusion_type if ct not in [list, set]]
552
+ if len(conclusion_types) == 1:
553
+ return f"Set[{conclusion_types[0]}]"
554
+ else:
555
+ return f"Set[Union[{', '.join(conclusion_types)}]]"
543
556
 
544
- def _get_imports(self) -> str:
545
- imports = super()._get_imports()
546
- imports += "from typing_extensions import Set, Union\n"
557
+ def _get_imports(self) -> Tuple[str, str]:
558
+ imports, defs_imports = super()._get_imports()
559
+ conclusion_types = [ct for ct in self.conclusion_type if ct not in [list, set]]
560
+ if len(conclusion_types) == 1:
561
+ imports += f"from typing_extensions import Set\n"
562
+ else:
563
+ imports += "from typing_extensions import Set, Union\n"
547
564
  imports += "from ripple_down_rules.utils import make_set\n"
548
- return imports
565
+ return imports, defs_imports
549
566
 
550
567
  def update_start_rule(self, case_query: CaseQuery, expert: Expert):
551
568
  """
@@ -867,7 +884,11 @@ class GeneralRDR(RippleDownRules):
867
884
  """
868
885
  :return: The default generated python file name.
869
886
  """
870
- return f"{self.start_rule.corner_case._name.lower()}_rdr"
887
+ if isinstance(self.start_rule.corner_case, Case):
888
+ name = self.start_rule.corner_case._name
889
+ else:
890
+ name = self.start_rule.corner_case.__class__.__name__
891
+ return f"{name}_rdr".lower()
871
892
 
872
893
  @property
873
894
  def conclusion_type_hint(self) -> str:
@@ -882,17 +903,12 @@ class GeneralRDR(RippleDownRules):
882
903
  """
883
904
  imports = ""
884
905
  # add type hints
885
- imports += f"from typing_extensions import Dict, Any, Union, Set\n"
906
+ imports += f"from typing_extensions import Dict, Any\n"
886
907
  # import rdr type
887
908
  imports += f"from ripple_down_rules.rdr import GeneralRDR\n"
888
909
  # add case type
889
910
  imports += f"from ripple_down_rules.datastructures.case import Case, create_case\n"
890
911
  imports += f"from {self.case_type.__module__} import {self.case_type.__name__}\n"
891
- # add conclusion type imports
892
- for rdr in self.start_rules_dict.values():
893
- for conclusion_type in rdr.conclusion_type:
894
- if conclusion_type.__module__ != "builtins":
895
- imports += f"from {conclusion_type.__module__} import {conclusion_type.__name__}\n"
896
912
  # add rdr python generated functions.
897
913
  for rdr_key, rdr in self.start_rules_dict.items():
898
914
  imports += (f"from {file_path.strip('./')}"
@@ -93,7 +93,7 @@ class Rule(NodeMixin, SubclassJSONSerializer, ABC):
93
93
  conclusion_func, conclusion_func_call = self._conclusion_source_code(conclusion, parent_indent=parent_indent)
94
94
  if conclusion_func is not None:
95
95
  with open(defs_file, 'a') as f:
96
- f.write(conclusion_func + "\n\n")
96
+ f.write(conclusion_func.strip() + "\n\n\n")
97
97
  return conclusion_func_call
98
98
 
99
99
  @abstractmethod
@@ -120,7 +120,7 @@ class Rule(NodeMixin, SubclassJSONSerializer, ABC):
120
120
  conditions_lines[0] = re.sub(r"def (\w+)", new_function_name, conditions_lines[0])
121
121
  def_code = "\n".join(conditions_lines)
122
122
  with open(defs_file, 'a') as f:
123
- f.write(def_code + "\n\n")
123
+ f.write(def_code.strip() + "\n\n\n")
124
124
  return f"\n{parent_indent}{if_clause} {new_function_name.replace('def ', '')}(case):\n"
125
125
 
126
126
  @abstractmethod
@@ -131,7 +131,7 @@ class Rule(NodeMixin, SubclassJSONSerializer, ABC):
131
131
  json_serialization = {"conditions": self.conditions.to_json(),
132
132
  "conclusion": conclusion_to_json(self.conclusion),
133
133
  "parent": self.parent.json_serialization if self.parent else None,
134
- "corner_case": self.corner_case.to_json() if self.corner_case else None,
134
+ "corner_case": SubclassJSONSerializer.to_json_static(self.corner_case),
135
135
  "conclusion_name": self.conclusion_name,
136
136
  "weight": self.weight}
137
137
  return json_serialization
@@ -7,6 +7,7 @@ import json
7
7
  import logging
8
8
  import os
9
9
  import re
10
+ import uuid
10
11
  from collections import UserDict
11
12
  from copy import deepcopy
12
13
  from dataclasses import is_dataclass, fields
@@ -322,7 +323,7 @@ def extract_dependencies(code_lines):
322
323
  return required_lines
323
324
 
324
325
 
325
- def serialize_dataclass(obj: Any) -> Union[Dict, Any]:
326
+ def serialize_dataclass(obj: Any, seen=None) -> Any:
326
327
  """
327
328
  Recursively serialize a dataclass to a dictionary. If the dataclass contains any nested dataclasses, they will be
328
329
  serialized as well. If the object is not a dataclass, it will be returned as is.
@@ -330,24 +331,44 @@ def serialize_dataclass(obj: Any) -> Union[Dict, Any]:
330
331
  :param obj: The dataclass to serialize.
331
332
  :return: The serialized dataclass as a dictionary or the object itself if it is not a dataclass.
332
333
  """
333
-
334
- def recursive_convert(obj):
335
- if is_dataclass(obj):
336
- return {
337
- "__dataclass__": f"{obj.__class__.__module__}.{obj.__class__.__qualname__}",
338
- "fields": {f.name: recursive_convert(getattr(obj, f.name)) for f in fields(obj)}
339
- }
340
- elif isinstance(obj, list):
341
- return [recursive_convert(item) for item in obj]
342
- elif isinstance(obj, dict):
343
- return {k: recursive_convert(v) for k, v in obj.items()}
344
- else:
334
+ if seen is None:
335
+ seen = {}
336
+
337
+ obj_id = id(obj)
338
+ if obj_id in seen:
339
+ return {'$ref': seen[obj_id]}
340
+
341
+ if is_dataclass(obj):
342
+ uid = str(uuid.uuid4())
343
+ seen[obj_id] = uid
344
+ result = {
345
+ '$id': uid,
346
+ "__dataclass__": f"{obj.__class__.__module__}.{obj.__class__.__qualname__}",
347
+ 'fields': {}
348
+ }
349
+ for f in fields(obj):
350
+ value = getattr(obj, f.name)
351
+ result['fields'][f.name] = serialize_dataclass(value, seen)
352
+ return result
353
+ elif isinstance(obj, list):
354
+ return [serialize_dataclass(v, seen) for v in obj]
355
+ elif isinstance(obj, dict):
356
+ return {k: serialize_dataclass(v, seen) for k, v in obj.items()}
357
+ else:
358
+ try:
359
+ json.dumps(obj) # Check if the object is JSON serializable
345
360
  return obj
361
+ except TypeError:
362
+ return None
346
363
 
347
- return recursive_convert(obj)
348
364
 
365
+ def deserialize_dataclass(data: Any, refs: Optional[Dict[str, Any]] = None) -> Any:
366
+ refs = {} if refs is None else refs
367
+ preloaded = preload_serialized_objects(data, refs)
368
+ return resolve_refs(preloaded, refs)
349
369
 
350
- def deserialize_dataclass(data: dict) -> Any:
370
+
371
+ def preload_serialized_objects(data: Any, refs: Dict[str, Any] = None) -> Any:
351
372
  """
352
373
  Recursively deserialize a dataclass from a dictionary, if the dictionary contains a key "__dataclass__" (Most likely
353
374
  created by the serialize_dataclass function), it will be treated as a dataclass and deserialized accordingly,
@@ -356,25 +377,81 @@ def deserialize_dataclass(data: dict) -> Any:
356
377
  :param data: The dictionary to deserialize.
357
378
  :return: The deserialized dataclass.
358
379
  """
380
+ if refs is None:
381
+ refs = {}
382
+
383
+ if isinstance(data, dict):
384
+
385
+ if '$ref' in data:
386
+ ref_id = data['$ref']
387
+ if ref_id not in refs:
388
+ return {'$ref': data['$ref']}
389
+ return refs[ref_id]
390
+
391
+ elif '$id' in data and '__dataclass__' in data and 'fields' in data:
392
+ cls_path = data['__dataclass__']
393
+ module_name, class_name = cls_path.rsplit('.', 1)
394
+ cls = getattr(importlib.import_module(module_name), class_name)
395
+
396
+ dummy_instance = cls.__new__(cls) # Don't call __init__ yet
397
+ refs[data['$id']] = dummy_instance
398
+
399
+ for f in fields(cls):
400
+ raw_value = data['fields'].get(f.name)
401
+ value = preload_serialized_objects(raw_value, refs)
402
+ setattr(dummy_instance, f.name, value)
403
+
404
+ return dummy_instance
359
405
 
360
- def recursive_load(obj):
361
- if isinstance(obj, dict) and "__dataclass__" in obj:
362
- module_name, class_name = obj["__dataclass__"].rsplit(".", 1)
363
- module = importlib.import_module(module_name)
364
- cls: Type = getattr(module, class_name)
365
- field_values = {
366
- k: recursive_load(v)
367
- for k, v in obj["fields"].items()
368
- }
369
- return cls(**field_values)
370
- elif isinstance(obj, list):
371
- return [recursive_load(item) for item in obj]
372
- elif isinstance(obj, dict):
373
- return {k: recursive_load(v) for k, v in obj.items()}
374
406
  else:
375
- return obj
407
+ return {k: preload_serialized_objects(v, refs) for k, v in data.items()}
408
+
409
+ elif isinstance(data, list):
410
+ return [preload_serialized_objects(item, refs) for item in data]
411
+ elif isinstance(data, dict):
412
+ return {k: preload_serialized_objects(v, refs) for k, v in data.items()}
413
+
414
+ return data # Primitive
376
415
 
377
- return recursive_load(data)
416
+
417
+ def resolve_refs(obj, refs, seen=None):
418
+ if seen is None:
419
+ seen = {}
420
+
421
+ obj_id = id(obj)
422
+ if obj_id in seen:
423
+ return seen[obj_id]
424
+
425
+ # Resolve if dict with $ref
426
+ if isinstance(obj, dict) and '$ref' in obj:
427
+ ref_id = obj['$ref']
428
+ if ref_id not in refs:
429
+ raise KeyError(f"$ref to unknown ID: {ref_id}")
430
+ return refs[ref_id]
431
+
432
+ elif is_dataclass(obj):
433
+ seen[obj_id] = obj # Mark before diving deeper
434
+ for f in fields(obj):
435
+ val = getattr(obj, f.name)
436
+ resolved = resolve_refs(val, refs, seen)
437
+ setattr(obj, f.name, resolved)
438
+ return obj
439
+
440
+ elif isinstance(obj, list):
441
+ resolved_list = []
442
+ seen[obj_id] = resolved_list
443
+ for item in obj:
444
+ resolved_list.append(resolve_refs(item, refs, seen))
445
+ return resolved_list
446
+
447
+ elif isinstance(obj, dict):
448
+ resolved_dict = {}
449
+ seen[obj_id] = resolved_dict
450
+ for k, v in obj.items():
451
+ resolved_dict[k] = resolve_refs(v, refs, seen)
452
+ return resolved_dict
453
+
454
+ return obj # Primitive
378
455
 
379
456
 
380
457
  def typing_to_python_type(typing_hint: Type) -> Type:
@@ -547,6 +624,7 @@ class SubclassJSONSerializer:
547
624
  Classes that inherit from this class can be serialized and deserialized automatically by calling this classes
548
625
  'from_json' method.
549
626
  """
627
+ data_class_refs = {}
550
628
 
551
629
  def to_json_file(self, filename: str):
552
630
  """
@@ -560,8 +638,14 @@ class SubclassJSONSerializer:
560
638
  json.dump(data, f, indent=4)
561
639
  return data
562
640
 
641
+ @staticmethod
642
+ def to_json_static(obj) -> Dict[str, Any]:
643
+ if is_dataclass(obj):
644
+ return serialize_dataclass(obj)
645
+ return {"_type": get_full_class_name(obj.__class__), **obj._to_json()}
646
+
563
647
  def to_json(self) -> Dict[str, Any]:
564
- return {"_type": get_full_class_name(self.__class__), **self._to_json()}
648
+ return self.to_json_static(self)
565
649
 
566
650
  def _to_json(self) -> Dict[str, Any]:
567
651
  """
@@ -582,7 +666,7 @@ class SubclassJSONSerializer:
582
666
  raise NotImplementedError()
583
667
 
584
668
  @classmethod
585
- def from_json_file(cls, filename: str):
669
+ def from_json_file(cls, filename: str) -> Any:
586
670
  """
587
671
  Create an instance of the subclass from the data in the given json file.
588
672
 
@@ -592,7 +676,9 @@ class SubclassJSONSerializer:
592
676
  filename += ".json"
593
677
  with open(filename, "r") as f:
594
678
  scrdr_json = json.load(f)
595
- return cls.from_json(scrdr_json)
679
+ deserialized_obj = cls.from_json(scrdr_json)
680
+ cls.data_class_refs.clear()
681
+ return deserialized_obj
596
682
 
597
683
  @classmethod
598
684
  def from_json(cls, data: Dict[str, Any]) -> Self:
@@ -604,11 +690,17 @@ class SubclassJSONSerializer:
604
690
  """
605
691
  if data is None:
606
692
  return None
607
- if not isinstance(data, dict) or ('_type' not in data):
693
+ if isinstance(data, list):
694
+ # if the data is a list, deserialize it
695
+ return [cls.from_json(d) for d in data]
696
+ elif isinstance(data, dict):
697
+ if '__dataclass__' in data:
698
+ # if the data is a dataclass, deserialize it
699
+ return deserialize_dataclass(data, cls.data_class_refs)
700
+ elif '_type' not in data:
701
+ return {k: cls.from_json(v) for k, v in data.items()}
702
+ elif not isinstance(data, dict):
608
703
  return data
609
- if '__dataclass__' in data:
610
- # if the data is a dataclass, deserialize it
611
- return deserialize_dataclass(data)
612
704
 
613
705
  # check if type module is builtins
614
706
  data_type = get_type_from_string(data["_type"])
@@ -757,6 +849,19 @@ def row_to_dict(obj):
757
849
  }
758
850
 
759
851
 
852
+ def dataclass_to_dict(obj):
853
+ """
854
+ Convert a dataclass to a dictionary.
855
+
856
+ :param obj: The dataclass to convert.
857
+ :return: The dictionary representation of the dataclass.
858
+ """
859
+ if is_dataclass(obj):
860
+ return {f.name: getattr(obj, f.name) for f in fields(obj) if not f.name.startswith("_")}
861
+ else:
862
+ raise ValueError(f"Object {obj} is not a dataclass.")
863
+
864
+
760
865
  def get_attribute_name(obj: Any, attribute: Optional[Any] = None, attribute_type: Optional[Type] = None,
761
866
  possible_value: Optional[Any] = None) -> Optional[str]:
762
867
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ripple_down_rules
3
- Version: 0.1.67
3
+ Version: 0.1.69
4
4
  Summary: Implements the various versions of Ripple Down Rules (RDR) for knowledge representation and reasoning.
5
5
  Author-email: Abdelrhman Bassiouny <abassiou@uni-bremen.de>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -4,17 +4,17 @@ ripple_down_rules/experts.py,sha256=JGVvSNiWhm4FpRpg76f98tl8Ii_C7x_aWD9FxD-JDLQ,
4
4
  ripple_down_rules/failures.py,sha256=E6ajDUsw3Blom8eVLbA7d_Qnov2conhtZ0UmpQ9ZtSE,302
5
5
  ripple_down_rules/helpers.py,sha256=TvTJU0BA3dPcAyzvZFvAu7jZqsp8Lu0HAAwvuizlGjg,2018
6
6
  ripple_down_rules/prompt.py,sha256=gdLV2qhq-1kbmlJhVmkCwGGFhIeDxV0p5u2hwrbGUpk,6370
7
- ripple_down_rules/rdr.py,sha256=LNXkiS-Dc7inGPKvTxZDarYPFsw9mSEWB6YDHl58g2A,41536
7
+ ripple_down_rules/rdr.py,sha256=E8YXSU_VMd3e_LJItjzbBjr0g1KXhhU_HL4FMAgdczc,42203
8
8
  ripple_down_rules/rdr_decorators.py,sha256=8SclpceI3EtrsbuukWJu8HGLh7Q1ZCgYGLX-RPlG-w0,2018
9
- ripple_down_rules/rules.py,sha256=SxP94X7I0XImG5zuEWLDfmINihwBKL-h_j1v2EMcVtg,16557
10
- ripple_down_rules/utils.py,sha256=MABj84x3v6gF5j2HscoQXnAZnupAGio3WjtsrTtVlRo,36473
9
+ ripple_down_rules/rules.py,sha256=WfMWzgfI_5Tqv8a2k7jkpGdvwu-zYCG36EmpCl7GiEQ,16576
10
+ ripple_down_rules/utils.py,sha256=INGRVIUH-SgssUW2T9gUMRf-XyJ3rxY_kuJUxfbsyOg,39683
11
11
  ripple_down_rules/datastructures/__init__.py,sha256=V2aNgf5C96Y5-IGghra3n9uiefpoIm_QdT7cc_C8cxQ,111
12
- ripple_down_rules/datastructures/callable_expression.py,sha256=4AxguiVeSTxzYti5AyOWHdqWzS6KdSPWqf0unR9ZuhA,9636
13
- ripple_down_rules/datastructures/case.py,sha256=uM5YkJOfYfARrZZ3oAxKMWE5QBSnvHLLZa9Atoxb7eY,13800
12
+ ripple_down_rules/datastructures/callable_expression.py,sha256=JLd8ZdIvAGX3mm-tID0buZIlbMF6hW2Z_jn5KA7X_ws,9788
13
+ ripple_down_rules/datastructures/case.py,sha256=nJDKOjyhGLx4gt0sHxJNxBLdy9X2SLcDn89_SmKzwoc,14035
14
14
  ripple_down_rules/datastructures/dataclasses.py,sha256=TAOAeEvh0BeTis3rEHu8rpCeqNNhU0vK3to0JaBwTio,5961
15
15
  ripple_down_rules/datastructures/enums.py,sha256=RdyPUp9Ls1QuLmkcMMkBbCWrmXIZI4xWuM-cLPYZhR0,4666
16
- ripple_down_rules-0.1.67.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
17
- ripple_down_rules-0.1.67.dist-info/METADATA,sha256=qlVXkthupuQp5K2PIWp_aFfGVP28rGWW3UXDx2enFBc,42576
18
- ripple_down_rules-0.1.67.dist-info/WHEEL,sha256=wXxTzcEDnjrTwFYjLPcsW_7_XihufBwmpiBeiXNBGEA,91
19
- ripple_down_rules-0.1.67.dist-info/top_level.txt,sha256=VeoLhEhyK46M1OHwoPbCQLI1EifLjChqGzhQ6WEUqeM,18
20
- ripple_down_rules-0.1.67.dist-info/RECORD,,
16
+ ripple_down_rules-0.1.69.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
17
+ ripple_down_rules-0.1.69.dist-info/METADATA,sha256=hjuztFZVWpCOPfWULTsRdVCrfeMo8nc2XI4DdW4GJ9g,42576
18
+ ripple_down_rules-0.1.69.dist-info/WHEEL,sha256=wXxTzcEDnjrTwFYjLPcsW_7_XihufBwmpiBeiXNBGEA,91
19
+ ripple_down_rules-0.1.69.dist-info/top_level.txt,sha256=VeoLhEhyK46M1OHwoPbCQLI1EifLjChqGzhQ6WEUqeM,18
20
+ ripple_down_rules-0.1.69.dist-info/RECORD,,