ripple-down-rules 0.5.4__py3-none-any.whl → 0.5.7__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.
@@ -10,13 +10,16 @@ import os
10
10
  import re
11
11
  import threading
12
12
  import uuid
13
- from collections import UserDict
13
+ from collections import UserDict, defaultdict
14
14
  from copy import deepcopy, copy
15
15
  from dataclasses import is_dataclass, fields
16
16
  from enum import Enum
17
+ from os.path import dirname
17
18
  from textwrap import dedent
18
19
  from types import NoneType
19
- from typing import List
20
+
21
+ from sqlalchemy.exc import NoInspectionAvailable
22
+
20
23
 
21
24
  try:
22
25
  import matplotlib
@@ -41,8 +44,7 @@ from sqlalchemy import MetaData, inspect
41
44
  from sqlalchemy.orm import Mapped, registry, class_mapper, DeclarativeBase as SQLTable, Session
42
45
  from tabulate import tabulate
43
46
  from typing_extensions import Callable, Set, Any, Type, Dict, TYPE_CHECKING, get_type_hints, \
44
- get_origin, get_args, Tuple, Optional, List, Union, Self
45
-
47
+ get_origin, get_args, Tuple, Optional, List, Union, Self, ForwardRef
46
48
 
47
49
  if TYPE_CHECKING:
48
50
  from .datastructures.case import Case
@@ -81,7 +83,7 @@ def are_results_subclass_of_types(result_types: List[Any], types_: List[Type]) -
81
83
  return True
82
84
 
83
85
 
84
- def get_imports_from_types(types: List[Type]) -> List[str]:
86
+ def _get_imports_from_types(types: List[Type]) -> List[str]:
85
87
  """
86
88
  Get the import statements for a list of types.
87
89
 
@@ -114,7 +116,7 @@ def get_imports_from_scope(scope: Dict[str, Any]) -> List[str]:
114
116
  """
115
117
  imports = []
116
118
  for k, v in scope.items():
117
- if not hasattr(v, "__module__") or not hasattr(v, "__name__"):
119
+ if not hasattr(v, "__module__") or not hasattr(v, "__name__") or v.__module__ is None:
118
120
  continue
119
121
  imports.append(f"from {v.__module__} import {v.__name__}")
120
122
  return imports
@@ -157,7 +159,7 @@ def extract_function_source(file_path: str,
157
159
  return_line_numbers: bool = False,
158
160
  include_signature: bool = True) \
159
161
  -> Union[Dict[str, Union[str, List[str]]],
160
- Tuple[Dict[str, Union[str, List[str]]], List[Tuple[int, int]]]]:
162
+ Tuple[Dict[str, Union[str, List[str]]], Dict[str, Tuple[int, int]]]]:
161
163
  """
162
164
  Extract the source code of a function from a file.
163
165
 
@@ -176,7 +178,7 @@ def extract_function_source(file_path: str,
176
178
  tree = ast.parse(source)
177
179
  function_names = make_list(function_names)
178
180
  functions_source: Dict[str, Union[str, List[str]]] = {}
179
- line_numbers = []
181
+ line_numbers: Dict[str, Tuple[int, int]] = {}
180
182
  for node in tree.body:
181
183
  if isinstance(node, ast.FunctionDef) and (node.name in function_names or len(function_names) == 0):
182
184
  # Get the line numbers of the function
@@ -184,7 +186,7 @@ def extract_function_source(file_path: str,
184
186
  func_lines = lines[node.lineno - 1:node.end_lineno]
185
187
  if not include_signature:
186
188
  func_lines = func_lines[1:]
187
- line_numbers.append((node.lineno, node.end_lineno))
189
+ line_numbers[node.name] = (node.lineno, node.end_lineno)
188
190
  functions_source[node.name] = dedent("\n".join(func_lines)) if join_lines else func_lines
189
191
  if (len(functions_source) >= len(function_names)) and (not len(function_names) == 0):
190
192
  break
@@ -496,16 +498,8 @@ def serialize_dataclass(obj: Any, seen=None) -> Any:
496
498
  value = getattr(obj, f.name)
497
499
  result['fields'][f.name] = serialize_dataclass(value, seen)
498
500
  return result
499
- elif isinstance(obj, list):
500
- return [serialize_dataclass(v, seen) for v in obj]
501
- elif isinstance(obj, dict):
502
- return {k: serialize_dataclass(v, seen) for k, v in obj.items()}
503
501
  else:
504
- try:
505
- json.dumps(obj) # Check if the object is JSON serializable
506
- return obj
507
- except TypeError:
508
- return None
502
+ return SubclassJSONSerializer.to_json_static(obj, seen)
509
503
 
510
504
 
511
505
  def deserialize_dataclass(data: Any, refs: Optional[Dict[str, Any]] = None) -> Any:
@@ -667,56 +661,203 @@ def get_func_rdr_model_name(func: Callable, include_file_name: bool = False) ->
667
661
  return model_name
668
662
 
669
663
 
670
- def extract_bracket_arguments(val: str) -> List[str]:
671
- """
672
- Extract arguments inside brackets into a list.
673
-
674
- :param val: The string containing brackets.
675
- :return: List of arguments inside brackets.
676
- """
677
- if '[' not in val:
678
- return [val]
679
- args_start = val.find('[')
680
- args_end = val.rfind(']')
681
- if args_end == -1:
682
- return [val]
683
- base_type = val[:args_start]
684
- args = val[args_start + 1:args_end].split(',')
685
- args = [arg.strip() for arg in args]
686
- return [base_type] + args
687
-
688
-
689
- def typing_hint_to_str(type_hint: Any) -> Tuple[str, List[str]]:
690
- """
691
- Convert a typing hint to a string.
692
-
693
- :param type_hint: The typing hint to convert.
694
- :return: The string representation of the typing hint.
695
- """
696
- val = (str(type_hint).strip("<>")
697
- .replace("class ", "")
698
- # .replace("typing.", "")
699
- .replace("'", ""))
700
- all_args = []
701
- if '[' in val:
702
- args = extract_bracket_arguments(val)
703
- args_with_brackets = [arg for arg in args if '[' in arg]
704
- all_args.extend([arg for arg in args if '[' not in arg])
705
- while args_with_brackets:
706
- for arg in args:
707
- if '[' in arg:
708
- sub_args = extract_bracket_arguments(arg)
709
- args_with_brackets.remove(arg)
710
- all_args.extend([sarg for sarg in sub_args if '[' not in sarg])
711
- args_with_brackets.extend([sarg for sarg in sub_args if '[' in sarg])
712
- elif arg not in all_args:
713
- all_args.append(arg)
714
- args = args_with_brackets
715
- for arg in all_args:
716
- val = val.replace(arg, arg.split('.')[-1])
664
+ def stringify_hint(tp):
665
+ """Recursively convert a type hint to a string."""
666
+ if isinstance(tp, str):
667
+ return tp
668
+
669
+ # Handle ForwardRef (string annotations not yet evaluated)
670
+ if isinstance(tp, ForwardRef):
671
+ return tp.__forward_arg__
672
+
673
+ # Handle typing generics like List[int], Dict[str, List[int]], etc.
674
+ origin = get_origin(tp)
675
+ args = get_args(tp)
676
+
677
+ if origin is not None:
678
+ origin_str = getattr(origin, '__name__', str(origin)).capitalize()
679
+ args_str = ", ".join(stringify_hint(arg) for arg in args)
680
+ return f"{origin_str}[{args_str}]"
681
+
682
+ # Handle built-in types like int, str, etc.
683
+ if isinstance(tp, type):
684
+ if tp.__module__ == 'builtins':
685
+ return tp.__name__
686
+ return f"{tp.__qualname__}"
687
+
688
+ return str(tp)
689
+
690
+
691
+ def is_builtin_type(tp):
692
+ return isinstance(tp, type) and tp.__module__ == "builtins"
693
+
694
+
695
+ def is_typing_type(tp):
696
+ return tp.__module__ == "typing"
697
+
698
+ origin_type_to_hint = {
699
+ list: List,
700
+ set: Set,
701
+ dict: Dict,
702
+ tuple: Tuple,
703
+ }
704
+
705
+ def extract_types(tp, seen: Set = None) -> Set[type]:
706
+ """Recursively extract all base types from a type hint."""
707
+ if seen is None:
708
+ seen = set()
709
+
710
+ if tp in seen or isinstance(tp, str):
711
+ return seen
712
+
713
+ # seen.add(tp)
714
+
715
+ if isinstance(tp, ForwardRef):
716
+ # Can't resolve until evaluated
717
+ return seen
718
+
719
+ origin = get_origin(tp)
720
+ args = get_args(tp)
721
+
722
+ if origin:
723
+ if origin in origin_type_to_hint:
724
+ seen.add(origin_type_to_hint[origin])
725
+ else:
726
+ seen.add(origin)
727
+ for arg in args:
728
+ extract_types(arg, seen)
729
+
730
+ elif isinstance(tp, type):
731
+ seen.add(tp)
732
+
733
+ return seen
734
+
735
+
736
+ def get_types_to_import_from_func_type_hints(func: Callable) -> Set[Type]:
737
+ """
738
+ Extract importable types from a function's annotations.
739
+
740
+ :param func: The function to extract type hints from.
741
+ """
742
+ hints = get_type_hints(func)
743
+
744
+ sig = inspect.signature(func)
745
+ all_hints = list(hints.values())
746
+ if sig.return_annotation != inspect.Signature.empty:
747
+ all_hints.append(sig.return_annotation)
748
+
749
+ for param in sig.parameters.values():
750
+ if param.annotation != inspect.Parameter.empty:
751
+ all_hints.append(param.annotation)
752
+
753
+ return get_types_to_import_from_type_hints(all_hints)
754
+
755
+
756
+ def get_types_to_import_from_type_hints(hints: List[Type]) -> Set[Type]:
757
+ """
758
+ Extract importable types from a list of type hints.
759
+
760
+ :param hints: A list of type hints to extract types from.
761
+ :return: A set of types that need to be imported.
762
+ """
763
+ seen_types = set()
764
+ for hint in hints:
765
+ extract_types(hint, seen_types)
766
+
767
+ # Filter out built-in and internal types
768
+ to_import = set()
769
+ for tp in seen_types:
770
+ if isinstance(tp, ForwardRef) or isinstance(tp, str):
771
+ continue
772
+ if not is_builtin_type(tp):
773
+ to_import.add(tp)
774
+
775
+ return to_import
776
+
777
+
778
+ def get_import_path_from_path(path: str) -> Optional[str]:
779
+ """
780
+ Convert a file system path to a Python import path.
781
+
782
+ :param path: The file system path to convert.
783
+ :return: The Python import path.
784
+ """
785
+ package_name = os.path.abspath(path)
786
+ formated_package_name = package_name.strip('./').replace('/', '.')
787
+ parent_package_idx = 0
788
+ packages = formated_package_name.split('.')
789
+ for i, possible_pacakge in enumerate(reversed(packages)):
790
+ if i == 0:
791
+ current_path = package_name
792
+ else:
793
+ current_path = '/' + '/'.join(packages[:-i])
794
+ if os.path.exists(os.path.join(current_path, '__init__.py')):
795
+ parent_package_idx -= 1
796
+ else:
797
+ break
798
+ package_name = '.'.join(packages[parent_package_idx:]) if parent_package_idx < 0 else None
799
+ return package_name
800
+
801
+
802
+ def get_function_import_path_and_representation(func: Callable) -> Tuple[str, str]:
803
+ """
804
+ Get the import path of a function.
805
+
806
+ :param func: The function to get the import path for.
807
+ :return: The import path of the function.
808
+ """
809
+ func_name = get_method_name(func)
810
+ func_class_name = get_method_class_name_if_exists(func)
811
+ func_file_path = get_method_file_name(func)
812
+ func_file_name = func_file_path.split('/')[-1].split('.')[0] # Get the file name without extension
813
+ func_import_path = get_import_path_from_path(dirname(func_file_path))
814
+ func_import_path = f"{func_import_path}.{func_file_name}" if func_import_path else func_file_name
815
+ if func_class_name and func_class_name != func_name:
816
+ import_path = f"from {func_import_path} import {func_class_name}"
817
+ func_representation = f"{func_class_name}.{func_name}"
717
818
  else:
718
- val = val.split('.')[-1]
719
- return val, all_args
819
+ import_path = f"from {func_import_path} import {func_name}"
820
+ func_representation = func_name
821
+ return import_path, func_representation
822
+
823
+
824
+ def get_imports_from_types(type_objs: List[Type]) -> List[str]:
825
+ """
826
+ Format import lines from type objects.
827
+
828
+ :param type_objs: A list of type objects to format.
829
+ """
830
+
831
+ module_to_types = defaultdict(list)
832
+ other_imports = []
833
+ for tp in type_objs:
834
+ try:
835
+ if isinstance(tp, type) or is_typing_type(tp):
836
+ module = tp.__module__
837
+ name = tp.__qualname__
838
+ elif callable(tp):
839
+ import_, _ = get_function_import_path_and_representation(tp)
840
+ if import_ is not None:
841
+ other_imports.append(import_)
842
+ module = None
843
+ elif hasattr(type(tp), "__module__"):
844
+ module = type(tp).__module__
845
+ name = type(tp).__qualname__
846
+ else:
847
+ continue
848
+ if module is None or module == 'builtins' or module.startswith('_'):
849
+ continue
850
+ module_to_types[module].append(name)
851
+ except AttributeError:
852
+ continue
853
+
854
+ lines = []
855
+ for module, names in module_to_types.items():
856
+ joined = ", ".join(sorted(set(names)))
857
+ lines.append(f"from {module} import {joined}")
858
+ if other_imports:
859
+ lines.extend(other_imports)
860
+ return sorted(lines)
720
861
 
721
862
 
722
863
  def get_method_args_as_dict(method: Callable, *args, **kwargs) -> Dict[str, Any]:
@@ -753,7 +894,9 @@ def get_method_class_name_if_exists(method: Callable) -> Optional[str]:
753
894
  :return: The class name of the method.
754
895
  """
755
896
  if hasattr(method, "__self__"):
756
- if hasattr(method.__self__, "__class__"):
897
+ if hasattr(method.__self__, "__name__"):
898
+ return method.__self__.__name__
899
+ elif hasattr(method.__self__, "__class__"):
757
900
  return method.__self__.__class__.__name__
758
901
  return method.__qualname__.split('.')[0] if hasattr(method, "__qualname__") else None
759
902
 
@@ -869,10 +1012,28 @@ class SubclassJSONSerializer:
869
1012
  return data
870
1013
 
871
1014
  @staticmethod
872
- def to_json_static(obj) -> Dict[str, Any]:
873
- if is_dataclass(obj):
874
- return serialize_dataclass(obj)
875
- return {"_type": get_full_class_name(obj.__class__), **obj._to_json()}
1015
+ def to_json_static(obj, seen=None) -> Any:
1016
+ if isinstance(obj, SubclassJSONSerializer):
1017
+ return {"_type": get_full_class_name(obj.__class__), **obj._to_json()}
1018
+ elif isinstance(obj, type):
1019
+ return {"_type": get_full_class_name(obj)}
1020
+ elif is_dataclass(obj):
1021
+ return serialize_dataclass(obj, seen)
1022
+ elif isinstance(obj, list):
1023
+ return [SubclassJSONSerializer.to_json_static(v, seen) for v in obj]
1024
+ elif isinstance(obj, dict):
1025
+ serialized_dict = {}
1026
+ for k, v in obj.items():
1027
+ if not isinstance(k, (str, int, bool, float, type(None))):
1028
+ continue
1029
+ serialized_dict[k] = SubclassJSONSerializer.to_json_static(v, seen)
1030
+ return serialized_dict
1031
+ else:
1032
+ try:
1033
+ json.dumps(obj) # Check if the object is JSON serializable
1034
+ return obj
1035
+ except TypeError:
1036
+ return None
876
1037
 
877
1038
  def to_json(self) -> Dict[str, Any]:
878
1039
  return self.to_json_static(self)
@@ -1008,13 +1169,20 @@ def copy_orm_instance(instance: SQLTable) -> SQLTable:
1008
1169
  :param instance: The instance to copy.
1009
1170
  :return: The copied instance.
1010
1171
  """
1011
- session: Session = inspect(instance).session
1172
+ try:
1173
+ session: Session = inspect(instance).session
1174
+ except NoInspectionAvailable:
1175
+ session = None
1012
1176
  if session is not None:
1013
1177
  session.expunge(instance)
1014
1178
  new_instance = deepcopy(instance)
1015
1179
  session.add(instance)
1016
1180
  else:
1017
- new_instance = instance
1181
+ try:
1182
+ new_instance = deepcopy(instance)
1183
+ except Exception as e:
1184
+ logging.debug(e)
1185
+ new_instance = instance
1018
1186
  return new_instance
1019
1187
 
1020
1188
 
@@ -1028,8 +1196,12 @@ def copy_orm_instance_with_relationships(instance: SQLTable) -> SQLTable:
1028
1196
  instance_cp = copy_orm_instance(instance)
1029
1197
  for rel in class_mapper(instance.__class__).relationships:
1030
1198
  related_obj = getattr(instance, rel.key)
1199
+ related_obj_cp = copy_orm_instance(related_obj)
1031
1200
  if related_obj is not None:
1032
- setattr(instance_cp, rel.key, related_obj)
1201
+ try:
1202
+ setattr(instance_cp, rel.key, related_obj_cp)
1203
+ except Exception as e:
1204
+ logging.debug(e)
1033
1205
  return instance_cp
1034
1206
 
1035
1207
 
@@ -1040,7 +1212,17 @@ def get_value_type_from_type_hint(attr_name: str, obj: Any) -> Type:
1040
1212
  :param attr_name: The name of the attribute.
1041
1213
  :param obj: The object to get the attributes from.
1042
1214
  """
1043
- hint, origin, args = get_hint_for_attribute(attr_name, obj)
1215
+ # check first if obj is a function object
1216
+ if hasattr(obj, '__code__'):
1217
+ func_type_hints = get_type_hints(obj)
1218
+ if attr_name in func_type_hints:
1219
+ hint = func_type_hints[attr_name]
1220
+ origin = get_origin(hint)
1221
+ args = get_args(hint)
1222
+ else:
1223
+ raise ValueError(f"Unknown type hint: {attr_name}")
1224
+ else:
1225
+ hint, origin, args = get_hint_for_attribute(attr_name, obj)
1044
1226
  if not origin and not hint:
1045
1227
  if hasattr(obj, attr_name):
1046
1228
  attr_value = getattr(obj, attr_name)
@@ -1370,8 +1552,8 @@ def draw_tree(root: Node, fig: Figure):
1370
1552
  """
1371
1553
  Draw the tree using matplotlib and networkx.
1372
1554
  """
1373
- if matplotlib.get_backend().lower() not in ['qt5agg', 'qt4agg', 'qt6agg']:
1374
- matplotlib.use("Qt6Agg") # or "Qt6Agg", depending on availability
1555
+ # if matplotlib.get_backend().lower() not in ['qt5agg', 'qt4agg', 'qt6agg']:
1556
+ # matplotlib.use("Qt6Agg") # or "Qt6Agg", depending on availability
1375
1557
 
1376
1558
  if root is None:
1377
1559
  return
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ripple_down_rules
3
- Version: 0.5.4
3
+ Version: 0.5.7
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
@@ -693,7 +693,8 @@ Requires-Dist: colorama
693
693
  Requires-Dist: pygments
694
694
  Requires-Dist: sqlalchemy
695
695
  Requires-Dist: pandas
696
- Requires-Dist: pyparsing
696
+ Requires-Dist: pyparsing>=3.2.3
697
+ Requires-Dist: omegaconf
697
698
  Provides-Extra: viz
698
699
  Requires-Dist: networkx>=3.1; extra == "viz"
699
700
  Requires-Dist: matplotlib>=3.7.5; extra == "viz"
@@ -799,6 +800,19 @@ cat = grdr.classify(all_cases[50])['species']
799
800
  assert cat == targets[50]
800
801
  ```
801
802
 
803
+ When prompted to write a rule, I wrote the following inside the template function that the Ripple Down Rules created:
804
+ ```python
805
+ return case.milk == 1
806
+ ```
807
+ then
808
+ ```python
809
+ return case.aquatic == 1
810
+ ```
811
+
812
+ The rule tree generated from fitting all the dataset will look like this:
813
+ ![species_rdr](https://raw.githubusercontent.com/AbdelrhmanBassiouny/ripple_down_rules/main/images/scrdr.png)
814
+
815
+
802
816
  ### Relational Example
803
817
 
804
818
  By relational, I mean that each rule conclusion is not a constant value, but is related to the case being classified,
@@ -0,0 +1,24 @@
1
+ ripple_down_rules/__init__.py,sha256=rFBuJJIKkCvrrCTAQhdvytf-dk066UDS2ltMWPx7WIo,99
2
+ ripple_down_rules/experts.py,sha256=bwozulI1rv0uyaMZQqEgapDO-s8wvW0D6Jqxmvu5fik,12610
3
+ ripple_down_rules/helpers.py,sha256=v4oE7C5PfQUVJfSUs1FfLHEwrJXEHJLn4vJhJMvyCR8,4453
4
+ ripple_down_rules/rdr.py,sha256=5VeA6YT16SL-sZK3VYiZ6vqazqZzCj_Uhk7xKYyqwVA,51354
5
+ ripple_down_rules/rdr_decorators.py,sha256=pEVupcFqtHzPCaxZoxphHWlrSN6vCishdwUQ1hXiWtc,9193
6
+ ripple_down_rules/rules.py,sha256=rIFE5OIJpuXrquTPAXXOTuatWBD7CkWqRMnDmItLwUc,20176
7
+ ripple_down_rules/start-code-server.sh,sha256=otClk7VmDgBOX2TS_cjws6K0UwvgAUJhoA0ugkPCLqQ,949
8
+ ripple_down_rules/utils.py,sha256=yeSW2KvnxHTL2FMi-95-K6RmcMdzubJBc-jHeMPLL0g,56961
9
+ ripple_down_rules/datastructures/__init__.py,sha256=V2aNgf5C96Y5-IGghra3n9uiefpoIm_QdT7cc_C8cxQ,111
10
+ ripple_down_rules/datastructures/callable_expression.py,sha256=f3wUPTrLa1INO-1qfgVz87ryrCABronfyq0_JKWoZCs,12800
11
+ ripple_down_rules/datastructures/case.py,sha256=1zSaXUljaH6z3SgMGzYPoDyjotNam791KpYgvxuMh90,15463
12
+ ripple_down_rules/datastructures/dataclasses.py,sha256=vlMmlsFJs3s71fwE8d79x_f8z6ZNcLGC5gjSAqabWxY,11021
13
+ ripple_down_rules/datastructures/enums.py,sha256=ce7tqS0otfSTNAOwsnXlhsvIn4iW_Y_N3TNebF3YoZs,5700
14
+ ripple_down_rules/user_interface/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ ripple_down_rules/user_interface/gui.py,sha256=_lgZAUXxxaBUFQJAHjA5TBPp6XEvJ62t-kSN8sPsocE,27379
16
+ ripple_down_rules/user_interface/ipython_custom_shell.py,sha256=Jrf7NxOdlrwGXH0Xyz3vzQprY-PNx9etfePOTpm2Gu8,6479
17
+ ripple_down_rules/user_interface/object_diagram.py,sha256=FEa2HaYR9QmTE6NsOwBvZ0jqmu3DKyg6mig2VE5ZP4Y,4956
18
+ ripple_down_rules/user_interface/prompt.py,sha256=AkkltdDIaioN43lkRKDPKSjJcmdSSGZDMYz7AL7X9lE,8082
19
+ ripple_down_rules/user_interface/template_file_creator.py,sha256=VLS9Nxg6gPNa-YYliJ_VNsTvLPlZ003EVkJ2t8zuDgE,13563
20
+ ripple_down_rules-0.5.7.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
21
+ ripple_down_rules-0.5.7.dist-info/METADATA,sha256=IfSm2_mYstp-Xn7qpzoEOSJo_nXQ4Aw5Uc6Oke65nBk,48213
22
+ ripple_down_rules-0.5.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
+ ripple_down_rules-0.5.7.dist-info/top_level.txt,sha256=VeoLhEhyK46M1OHwoPbCQLI1EifLjChqGzhQ6WEUqeM,18
24
+ ripple_down_rules-0.5.7.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.8.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,24 +0,0 @@
1
- ripple_down_rules/__init__.py,sha256=8e_vgenmdqtVY41f0q59EP97pFvgSGxtQgPyO9fnp64,99
2
- ripple_down_rules/experts.py,sha256=rPRJU2py5wCopZADVWOP3Vzp077KL6ArDlkxUQ0Mh6w,12130
3
- ripple_down_rules/helpers.py,sha256=TvTJU0BA3dPcAyzvZFvAu7jZqsp8Lu0HAAwvuizlGjg,2018
4
- ripple_down_rules/rdr.py,sha256=FJYuRXgpUYSSK1pYrp2yeXb_ZZ2xjPED31tzxofokL4,48865
5
- ripple_down_rules/rdr_decorators.py,sha256=pYCKLgMKgQ6x_252WQtF2t4ZNjWPBxnaWtJ6TpGdcc0,7820
6
- ripple_down_rules/rules.py,sha256=TPNVMqW9T-_46BS4WemrspLg5uG8kP6tsPvWWBAzJxg,17515
7
- ripple_down_rules/start-code-server.sh,sha256=otClk7VmDgBOX2TS_cjws6K0UwvgAUJhoA0ugkPCLqQ,949
8
- ripple_down_rules/utils.py,sha256=cv40XBj-tp11aRYcAPhPtkrYatMvAKk_1d5P7PB1-tw,51123
9
- ripple_down_rules/datastructures/__init__.py,sha256=V2aNgf5C96Y5-IGghra3n9uiefpoIm_QdT7cc_C8cxQ,111
10
- ripple_down_rules/datastructures/callable_expression.py,sha256=D2KD1RdShzxYZPAERgywZ5ZPE4ar8WmMtXINqvYo_Tc,12497
11
- ripple_down_rules/datastructures/case.py,sha256=r8kjL9xP_wk84ThXusspgPMrAoed2bGQmKi54fzhmH8,15258
12
- ripple_down_rules/datastructures/dataclasses.py,sha256=PuD-7zWqWT2p4FnGvnihHvZlZKg9A1ctnFgVYf2cs-8,8554
13
- ripple_down_rules/datastructures/enums.py,sha256=ce7tqS0otfSTNAOwsnXlhsvIn4iW_Y_N3TNebF3YoZs,5700
14
- ripple_down_rules/user_interface/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- ripple_down_rules/user_interface/gui.py,sha256=SB0gUhgReJ3yx-NEHRPMGVuNRLPRUwW8-qup-Kd4Cfo,27182
16
- ripple_down_rules/user_interface/ipython_custom_shell.py,sha256=24MIFwqnAhC6ofObEO6x5xRWRnyQmPpPmTvxbCKBrzM,6514
17
- ripple_down_rules/user_interface/object_diagram.py,sha256=tsB6iuLNEbHxp5lR2WjyejjWbnAX_nHF9xS8jNPOQVk,4548
18
- ripple_down_rules/user_interface/prompt.py,sha256=AkkltdDIaioN43lkRKDPKSjJcmdSSGZDMYz7AL7X9lE,8082
19
- ripple_down_rules/user_interface/template_file_creator.py,sha256=ycCbddy_BJP8d0Q2Sj21UzamhGtqGZuK_e73VTJqznY,13766
20
- ripple_down_rules-0.5.4.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
21
- ripple_down_rules-0.5.4.dist-info/METADATA,sha256=2t5F_Z7AqI7lb2zZxTREAGEUyipcISlZ00Dk-0BAEx4,47796
22
- ripple_down_rules-0.5.4.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
23
- ripple_down_rules-0.5.4.dist-info/top_level.txt,sha256=VeoLhEhyK46M1OHwoPbCQLI1EifLjChqGzhQ6WEUqeM,18
24
- ripple_down_rules-0.5.4.dist-info/RECORD,,