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.
- ripple_down_rules/__init__.py +1 -1
- ripple_down_rules/experts.py +7 -3
- ripple_down_rules/rdr_decorators.py +3 -7
- ripple_down_rules/user_interface/ipython_custom_shell.py +3 -1
- ripple_down_rules/user_interface/prompt.py +6 -2
- ripple_down_rules/user_interface/template_file_creator.py +1 -2
- ripple_down_rules/utils.py +34 -20
- {ripple_down_rules-0.5.85.dist-info → ripple_down_rules-0.5.87.dist-info}/METADATA +1 -1
- {ripple_down_rules-0.5.85.dist-info → ripple_down_rules-0.5.87.dist-info}/RECORD +12 -12
- {ripple_down_rules-0.5.85.dist-info → ripple_down_rules-0.5.87.dist-info}/WHEEL +0 -0
- {ripple_down_rules-0.5.85.dist-info → ripple_down_rules-0.5.87.dist-info}/licenses/LICENSE +0 -0
- {ripple_down_rules-0.5.85.dist-info → ripple_down_rules-0.5.87.dist-info}/top_level.txt +0 -0
ripple_down_rules/__init__.py
CHANGED
ripple_down_rules/experts.py
CHANGED
@@ -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
|
289
|
+
if expert_input is None:
|
288
290
|
self.all_expert_answers.append(({}, None))
|
289
|
-
|
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,
|
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[
|
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
|
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
|
|
ripple_down_rules/utils.py
CHANGED
@@ -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(
|
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(
|
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(
|
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
|
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.
|
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
|
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
|
-
|
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
|
-
|
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[:
|
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=[
|
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.
|
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=
|
2
|
-
ripple_down_rules/experts.py,sha256=
|
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=
|
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=
|
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=
|
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=
|
19
|
-
ripple_down_rules/user_interface/template_file_creator.py,sha256=
|
20
|
-
ripple_down_rules-0.5.
|
21
|
-
ripple_down_rules-0.5.
|
22
|
-
ripple_down_rules-0.5.
|
23
|
-
ripple_down_rules-0.5.
|
24
|
-
ripple_down_rules-0.5.
|
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,,
|
File without changes
|
File without changes
|
File without changes
|