ripple-down-rules 0.5.85__py3-none-any.whl → 0.5.87__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.
@@ -1,4 +1,4 @@
1
- __version__ = "0.5.85"
1
+ __version__ = "0.5.87"
2
2
 
3
3
  import logging
4
4
  logger = logging.Logger("rdr")
@@ -252,6 +252,8 @@ class Human(Expert):
252
252
  condition = CallableExpression(user_input, bool, scope=case_query.scope)
253
253
  else:
254
254
  user_input, condition = self.user_prompt.prompt_user_for_expression(case_query, PromptFor.Conditions)
255
+ if user_input == 'exit':
256
+ exit()
255
257
  if not self.use_loaded_answers:
256
258
  self.all_expert_answers.append((condition.scope, user_input))
257
259
  if self.answers_save_path is not None:
@@ -284,11 +286,13 @@ class Human(Expert):
284
286
  if self.user_prompt.viewer is None:
285
287
  show_current_and_corner_cases(case_query.case)
286
288
  expert_input, expression = self.user_prompt.prompt_user_for_expression(case_query, PromptFor.Conclusion)
287
- if expression is None:
289
+ if expert_input is None:
288
290
  self.all_expert_answers.append(({}, None))
289
- else:
291
+ elif expert_input != 'exit':
290
292
  self.all_expert_answers.append((expression.scope, expert_input))
291
- if self.answers_save_path is not None:
293
+ if self.answers_save_path is not None and expert_input != 'exit':
292
294
  self.save_answers()
295
+ if expert_input == 'exit':
296
+ exit()
293
297
  case_query.target = expression
294
298
  return expression
@@ -29,7 +29,6 @@ class RDRDecorator:
29
29
  output_name: str = "output_",
30
30
  fit: bool = True,
31
31
  expert: Optional[Expert] = None,
32
- ask_always: bool = False,
33
32
  update_existing_rules: bool = True,
34
33
  viewer: Optional[RDRCaseViewer] = None,
35
34
  package_name: Optional[str] = None):
@@ -44,7 +43,6 @@ class RDRDecorator:
44
43
  classification mode. This means that the RDR will classify the function's output based on the RDR model.
45
44
  :param expert: The expert that will be used to prompt the user for the correct output. If None, a Human
46
45
  expert will be used.
47
- :param ask_always: If True, the function will ask the user for a target if it doesn't exist.
48
46
  :param update_existing_rules: If True, the function will update the existing RDR rules
49
47
  even if they gave an output.
50
48
  :param viewer: The viewer to use for the RDR model. If None, no viewer will be used.
@@ -59,7 +57,6 @@ class RDRDecorator:
59
57
  self.output_name = output_name
60
58
  self.fit: bool = fit
61
59
  self.expert: Optional[Expert] = expert
62
- self.ask_always = ask_always
63
60
  self.update_existing_rules = update_existing_rules
64
61
  self.viewer = viewer
65
62
  self.package_name = package_name
@@ -76,7 +73,7 @@ class RDRDecorator:
76
73
  self.initialize_rdr_model_name_and_load(func)
77
74
  if self.expert is None:
78
75
  self.expert = Human(viewer=self.viewer,
79
- answers_save_path=self.rdr_models_dir + f'/expert_answers')
76
+ answers_save_path=self.rdr_models_dir + f'/{self.model_name}/expert_answers')
80
77
 
81
78
  func_output = {self.output_name: func(*args, **kwargs)}
82
79
 
@@ -86,7 +83,6 @@ class RDRDecorator:
86
83
  self.mutual_exclusive,
87
84
  *args, **kwargs)
88
85
  output = self.rdr.fit_case(case_query, expert=self.expert,
89
- ask_always_for_target=self.ask_always,
90
86
  update_existing_rules=self.update_existing_rules,
91
87
  viewer=self.viewer)
92
88
  else:
@@ -170,7 +166,7 @@ class RDRDecorator:
170
166
  """
171
167
  Save the RDR model to the specified directory.
172
168
  """
173
- self.rdr.save(self.rdr_models_dir, package_name=self.package_name)
169
+ self.rdr.save(self.rdr_models_dir, self.model_name, package_name=self.package_name)
174
170
 
175
171
  def load(self):
176
172
  """
@@ -190,4 +186,4 @@ class RDRDecorator:
190
186
  """
191
187
  Update the RDR model from a python file.
192
188
  """
193
- self.rdr.update_from_python(self.rdr_models_dir, self.model_name, package_name=self.package_name)
189
+ self.rdr.update_from_python(self.rdr_models_dir, package_name=self.package_name)
@@ -134,7 +134,9 @@ class IPythonShell:
134
134
  """
135
135
  Update the user input from the code lines captured in the shell.
136
136
  """
137
- if self.shell.all_lines[0].replace('return', '').strip() == '':
137
+ if self.shell.all_lines[-1] in ['quit', 'exit']:
138
+ self.user_input = 'exit'
139
+ elif self.shell.all_lines[0].replace('return', '').strip() == '':
138
140
  self.user_input = None
139
141
  else:
140
142
  self.all_code_lines = extract_dependencies(self.shell.all_lines)
@@ -58,6 +58,10 @@ class UserPrompt:
58
58
  else:
59
59
  self.print_func(f"{Fore.RED}Conditions must be provided. Please try again.{Style.RESET_ALL}")
60
60
  continue
61
+ elif user_input == "exit":
62
+ self.print_func(f"{Fore.YELLOW}Exiting.{Style.RESET_ALL}")
63
+ return user_input, None
64
+
61
65
  prev_user_input = '\n'.join(user_input.split('\n')[2:-1])
62
66
  conclusion_type = bool if prompt_for == PromptFor.Conditions else case_query.attribute_type
63
67
  callable_expression = CallableExpression(user_input, conclusion_type, expression_tree=expression_tree,
@@ -150,8 +154,8 @@ class UserPrompt:
150
154
  self.viewer.show()
151
155
  app.exec()
152
156
  user_input = self.viewer.user_input
153
- if user_input is None:
154
- return None, None
157
+ if user_input is None or user_input == 'exit':
158
+ return user_input, None
155
159
  self.print_func(f"{Fore.GREEN}Captured User input: {Style.RESET_ALL}")
156
160
  highlighted_code = highlight(user_input, PythonLexer(), TerminalFormatter())
157
161
  self.print_func(highlighted_code)
@@ -222,10 +222,9 @@ class TemplateFileCreator:
222
222
  if list in self.output_type:
223
223
  output_type_imports.append(List)
224
224
  import_types = list(self.case_query.scope.values())
225
- # imports = [i for i in imports if ("get_ipython" not in i)]
226
225
  import_types.extend(case_type_imports)
227
226
  import_types.extend(output_type_imports)
228
- imports = get_imports_from_types(import_types)
227
+ imports = get_imports_from_types(import_types, excluded_modules=["IPython.core.interactiveshell"])
229
228
  imports = set(imports)
230
229
  return '\n'.join(imports)
231
230
 
@@ -863,14 +863,14 @@ def get_relative_import(target_file_path, imported_module_path: Optional[str] =
863
863
  # Convert to absolute paths
864
864
  target_path = Path(target_file_path).resolve()
865
865
  if package_name is not None:
866
- target_path = Path(get_path_starting_from(str(target_path), package_name))
866
+ target_path = Path(get_path_starting_from_latest_encounter_of(str(target_path), package_name))
867
867
  if module is not None:
868
868
  imported_module_path = sys.modules[module].__file__
869
869
  if imported_module_path is None:
870
870
  raise ValueError("Either imported_module_path or module must be provided")
871
871
  imported_path = Path(imported_module_path).resolve()
872
872
  if package_name is not None:
873
- imported_path = Path(get_path_starting_from(str(imported_path), package_name))
873
+ imported_path = Path(get_path_starting_from_latest_encounter_of(str(imported_path), package_name))
874
874
 
875
875
  # Compute relative path from target to imported module
876
876
  rel_path = os.path.relpath(imported_path.parent, target_path.parent)
@@ -878,15 +878,18 @@ def get_relative_import(target_file_path, imported_module_path: Optional[str] =
878
878
  # Convert path to Python import format
879
879
  rel_parts = [part.replace('..', '.') for part in Path(rel_path).parts]
880
880
  rel_parts = rel_parts if rel_parts else ['']
881
+ dot_parts = [part for part in rel_parts if part == '.']
882
+ non_dot_parts = [part for part in rel_parts if part != '.']
883
+
881
884
 
882
885
  # Join the parts and add the module name
883
- joined_parts = "".join(rel_parts) + f".{imported_path.stem}"
886
+ joined_parts = "".join(dot_parts) + ".".join(non_dot_parts) + f".{imported_path.stem}"
884
887
  joined_parts = f".{joined_parts}" if not joined_parts.startswith(".") else joined_parts
885
888
 
886
889
  return joined_parts
887
890
 
888
891
 
889
- def get_path_starting_from(path: str, package_name: str) -> Optional[str]:
892
+ def get_path_starting_from_latest_encounter_of(path: str, package_name: str) -> Optional[str]:
890
893
  """
891
894
  Get the path starting from the package name.
892
895
 
@@ -895,57 +898,67 @@ def get_path_starting_from(path: str, package_name: str) -> Optional[str]:
895
898
  :return: The path starting from the package name or None if not found.
896
899
  """
897
900
  if package_name in path:
898
- idx = path.index(package_name)
901
+ idx = path.rfind(package_name)
899
902
  return path[idx:]
900
903
  return None
901
904
 
902
905
 
903
906
  def get_imports_from_types(type_objs: Iterable[Type],
904
907
  target_file_path: Optional[str] = None,
905
- package_name: Optional[str] = None) -> List[str]:
908
+ package_name: Optional[str] = None,
909
+ exclueded_names: Optional[List[str]] = None,
910
+ excluded_modules: Optional[List[str]] = None) -> List[str]:
906
911
  """
907
912
  Format import lines from type objects.
908
913
 
909
914
  :param type_objs: A list of type objects to format.
910
915
  :param target_file_path: The file path to which the imports should be relative.
911
916
  :param package_name: The name of the package to use for relative imports.
917
+ :param exclueded_names: A list of names to exclude from the imports.
918
+ :param excluded_modules: A list of modules to exclude from the imports.
919
+ :return: A list of formatted import lines.
912
920
  """
913
-
921
+ excluded_modules = [] if excluded_modules is None else excluded_modules
922
+ exclueded_names = [] if exclueded_names is None else exclueded_names
914
923
  module_to_types = defaultdict(list)
915
- module_to_path = {}
916
- other_imports = []
917
924
  for tp in type_objs:
918
925
  try:
919
926
  if isinstance(tp, type) or is_typing_type(tp):
920
927
  module = tp.__module__
921
- file = getattr(tp, '__file__', None)
922
928
  name = tp.__qualname__
923
929
  elif callable(tp):
924
930
  module, name = get_function_import_data(tp)
925
- file = get_method_file_name(tp)
926
931
  elif hasattr(type(tp), "__module__"):
927
932
  module = type(tp).__module__
928
- file = getattr(tp, '__file__', None)
929
933
  name = type(tp).__qualname__
930
934
  else:
931
935
  continue
932
- if module is None or module == 'builtins' or module.startswith('_'):
936
+ if module is None or module == 'builtins' or module.startswith('_')\
937
+ or module in sys.builtin_module_names or module in excluded_modules or "<" in module \
938
+ or name in exclueded_names:
933
939
  continue
934
940
  module_to_types[module].append(name)
935
- if file:
936
- module_to_path[module] = file
937
941
  except AttributeError:
938
942
  continue
939
943
 
940
944
  lines = []
945
+ stem_imports = []
941
946
  for module, names in module_to_types.items():
942
- joined = ", ".join(sorted(set(names)))
947
+ filtered_names = set()
948
+ for name in set(names):
949
+ if '.' in name:
950
+ stem = '.'.join(name.split('.')[1:])
951
+ name_to_import = name.split('.')[0]
952
+ filtered_names.add(name_to_import)
953
+ stem_imports.append(f"{stem} = {name_to_import}.{stem}")
954
+ else:
955
+ filtered_names.add(name)
956
+ joined = ", ".join(sorted(set(filtered_names)))
943
957
  import_path = module
944
958
  if (target_file_path is not None) and (package_name is not None) and (package_name in module):
945
959
  import_path = get_relative_import(target_file_path, module=module, package_name=package_name)
946
960
  lines.append(f"from {import_path} import {joined}")
947
- if other_imports:
948
- lines.extend(other_imports)
961
+ lines.extend(stem_imports)
949
962
  return sorted(lines)
950
963
 
951
964
 
@@ -1373,6 +1386,7 @@ def table_rows_as_str(row_dicts: List[Dict[str, Any]], columns_per_row: int = 20
1373
1386
  :param row_dicts: The rows to print.
1374
1387
  :param columns_per_row: The maximum number of columns per row.
1375
1388
  """
1389
+ max_line_sze = 100
1376
1390
  all_row_dicts_items = [list(row_dict.items()) for row_dict in row_dicts]
1377
1391
  # make items a list of n rows such that each row has a max size of 4
1378
1392
  all_items = [all_items[i:i + columns_per_row] for all_items in all_row_dicts_items
@@ -1385,9 +1399,9 @@ def table_rows_as_str(row_dicts: List[Dict[str, Any]], columns_per_row: int = 20
1385
1399
  keys_values = [list(r[0]) + list(r[1]) if len(r) > 1 else r[0] for r in keys_values]
1386
1400
  all_table_rows = []
1387
1401
  row_values = [list(map(lambda v: str(v) if v is not None else "", row)) for row in keys_values]
1388
- row_values = [list(map(lambda v: v[:150] + '...' if len(v) > 150 else v, row)) for row in row_values]
1402
+ row_values = [list(map(lambda v: v[:max_line_sze] + '...' if len(v) > max_line_sze else v, row)) for row in row_values]
1389
1403
  row_values = [list(map(lambda v: v.lower() if v in ["True", "False"] else v, row)) for row in row_values]
1390
- table = tabulate(row_values, tablefmt='simple_grid', maxcolwidths=[150] * 2)
1404
+ table = tabulate(row_values, tablefmt='simple_grid', maxcolwidths=[max_line_sze] * 2)
1391
1405
  all_table_rows.append(table)
1392
1406
  return "\n".join(all_table_rows)
1393
1407
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ripple_down_rules
3
- Version: 0.5.85
3
+ Version: 0.5.87
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
@@ -1,11 +1,11 @@
1
- ripple_down_rules/__init__.py,sha256=49RvNTpGUXv7fYtLSLpbwGCoAhuRvpyyEmnErrZRwHs,100
2
- ripple_down_rules/experts.py,sha256=bwozulI1rv0uyaMZQqEgapDO-s8wvW0D6Jqxmvu5fik,12610
1
+ ripple_down_rules/__init__.py,sha256=GkugTXRcQu2cnGcyrT7Ra2SGmRhDvnkbdHlrvXqCmfw,100
2
+ ripple_down_rules/experts.py,sha256=J4VaYPKrAMNcoP0JRenZpx9IHLLoeFdl0QZKZzjFFkM,12768
3
3
  ripple_down_rules/helpers.py,sha256=v4oE7C5PfQUVJfSUs1FfLHEwrJXEHJLn4vJhJMvyCR8,4453
4
4
  ripple_down_rules/rdr.py,sha256=C6bvlq6MsKa2Mym1wW6JUMj705aCaoihTP279TM6eT0,55218
5
- ripple_down_rules/rdr_decorators.py,sha256=amXUjgc-rfYxPKFW16Xm8Z9vfaaslNl5OvDOg9fKaTg,9447
5
+ ripple_down_rules/rdr_decorators.py,sha256=pj7SwswB9UVdv4-c8pzcOsGTG0Elt7JEG6k65fsnOdk,9202
6
6
  ripple_down_rules/rules.py,sha256=iVevv6iZ-6L2IPI0ZYbBjxBymXEQMmJGRFhiKUS-NmA,20352
7
7
  ripple_down_rules/start-code-server.sh,sha256=otClk7VmDgBOX2TS_cjws6K0UwvgAUJhoA0ugkPCLqQ,949
8
- ripple_down_rules/utils.py,sha256=VMOjMU2YyhUMDfBSZSjQlqi6ka-0cu014hl3OGTu3pQ,60962
8
+ ripple_down_rules/utils.py,sha256=KvD-uqPfHw5THsIrR0D52oKqKggvKk5sfachARDg7PM,61978
9
9
  ripple_down_rules/datastructures/__init__.py,sha256=V2aNgf5C96Y5-IGghra3n9uiefpoIm_QdT7cc_C8cxQ,111
10
10
  ripple_down_rules/datastructures/callable_expression.py,sha256=f3wUPTrLa1INO-1qfgVz87ryrCABronfyq0_JKWoZCs,12800
11
11
  ripple_down_rules/datastructures/case.py,sha256=1zSaXUljaH6z3SgMGzYPoDyjotNam791KpYgvxuMh90,15463
@@ -13,12 +13,12 @@ ripple_down_rules/datastructures/dataclasses.py,sha256=qoTFHV8Hi-X8VtfC9VdvH4tif
13
13
  ripple_down_rules/datastructures/enums.py,sha256=ce7tqS0otfSTNAOwsnXlhsvIn4iW_Y_N3TNebF3YoZs,5700
14
14
  ripple_down_rules/user_interface/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
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
16
+ ripple_down_rules/user_interface/ipython_custom_shell.py,sha256=6v99tlNcfRyZpnPxq3FfqgFgxbJ5zkA2jVAXamTH1C0,6575
17
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=xWUt-RHRqrvoMo74o-bMLo8xNxil68wgbOZAMADZp2A,13570
20
- ripple_down_rules-0.5.85.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
21
- ripple_down_rules-0.5.85.dist-info/METADATA,sha256=1tFTBGloFOMxSnYhJT6h5obVH_-UNfKQumICBkDmmwU,48214
22
- ripple_down_rules-0.5.85.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
- ripple_down_rules-0.5.85.dist-info/top_level.txt,sha256=VeoLhEhyK46M1OHwoPbCQLI1EifLjChqGzhQ6WEUqeM,18
24
- ripple_down_rules-0.5.85.dist-info/RECORD,,
18
+ ripple_down_rules/user_interface/prompt.py,sha256=Liu24-wuL12mg2xI7FBvnL6T1eyykCcMZ3AeKkkYwpA,8283
19
+ ripple_down_rules/user_interface/template_file_creator.py,sha256=ELHYhZEjHsMdG5ebbOghfZtPBpPtuJDWQRAkLpfcSFM,13553
20
+ ripple_down_rules-0.5.87.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
21
+ ripple_down_rules-0.5.87.dist-info/METADATA,sha256=L_EDvPOt6ibGEoNS6qgGXSFQ5zJkJdpPCKlB937_vzc,48214
22
+ ripple_down_rules-0.5.87.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
+ ripple_down_rules-0.5.87.dist-info/top_level.txt,sha256=VeoLhEhyK46M1OHwoPbCQLI1EifLjChqGzhQ6WEUqeM,18
24
+ ripple_down_rules-0.5.87.dist-info/RECORD,,