ripple-down-rules 0.2.2__py3-none-any.whl → 0.2.4__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/datastructures/callable_expression.py +2 -0
- ripple_down_rules/datastructures/dataclasses.py +25 -3
- ripple_down_rules/prompt.py +105 -55
- {ripple_down_rules-0.2.2.dist-info → ripple_down_rules-0.2.4.dist-info}/METADATA +2 -2
- {ripple_down_rules-0.2.2.dist-info → ripple_down_rules-0.2.4.dist-info}/RECORD +8 -8
- {ripple_down_rules-0.2.2.dist-info → ripple_down_rules-0.2.4.dist-info}/WHEEL +0 -0
- {ripple_down_rules-0.2.2.dist-info → ripple_down_rules-0.2.4.dist-info}/licenses/LICENSE +0 -0
- {ripple_down_rules-0.2.2.dist-info → ripple_down_rules-0.2.4.dist-info}/top_level.txt +0 -0
@@ -134,6 +134,8 @@ class CallableExpression(SubclassJSONSerializer):
|
|
134
134
|
if output is None:
|
135
135
|
output = scope['_get_value'](case)
|
136
136
|
if self.conclusion_type is not None:
|
137
|
+
if not any([issubclass(ct, (list, set)) for ct in self.conclusion_type]) and is_iterable(output):
|
138
|
+
raise ValueError(f"Expected output to be {self.conclusion_type}, but got {type(output)}")
|
137
139
|
output_types = {type(o) for o in make_list(output)}
|
138
140
|
output_types.add(type(output))
|
139
141
|
if not are_results_subclass_of_types(output_types, self.conclusion_type):
|
@@ -3,8 +3,9 @@ from __future__ import annotations
|
|
3
3
|
import inspect
|
4
4
|
from dataclasses import dataclass, field
|
5
5
|
|
6
|
+
import typing_extensions
|
6
7
|
from sqlalchemy.orm import DeclarativeBase as SQLTable
|
7
|
-
from typing_extensions import Any, Optional, Dict, Type, Tuple, Union
|
8
|
+
from typing_extensions import Any, Optional, Dict, Type, Tuple, Union, List
|
8
9
|
|
9
10
|
from .callable_expression import CallableExpression
|
10
11
|
from .case import create_case, Case
|
@@ -61,6 +62,13 @@ class CaseQuery:
|
|
61
62
|
The conditions that must be satisfied for the target value to be valid.
|
62
63
|
"""
|
63
64
|
|
65
|
+
@property
|
66
|
+
def case_type(self) -> Type:
|
67
|
+
"""
|
68
|
+
:return: The type of the case that the attribute belongs to.
|
69
|
+
"""
|
70
|
+
return self.original_case._obj_type if isinstance(self.original_case, Case) else type(self.original_case)
|
71
|
+
|
64
72
|
@property
|
65
73
|
def case(self) -> Any:
|
66
74
|
"""
|
@@ -83,19 +91,33 @@ class CaseQuery:
|
|
83
91
|
raise ValueError("The case must be a Case or SQLTable object.")
|
84
92
|
self._case = value
|
85
93
|
|
94
|
+
@property
|
95
|
+
def attribute_type_hint(self) -> str:
|
96
|
+
"""
|
97
|
+
:return: The type hint of the attribute as a typing object.
|
98
|
+
"""
|
99
|
+
if len(self.core_attribute_type) > 1:
|
100
|
+
attribute_types_str = f"Union[{', '.join([t.__name__ for t in self.core_attribute_type])}]"
|
101
|
+
else:
|
102
|
+
attribute_types_str = self.core_attribute_type[0].__name__
|
103
|
+
if list in self.attribute_type:
|
104
|
+
return f"List[{attribute_types_str}]"
|
105
|
+
else:
|
106
|
+
return attribute_types_str
|
107
|
+
|
86
108
|
@property
|
87
109
|
def core_attribute_type(self) -> Tuple[Type]:
|
88
110
|
"""
|
89
111
|
:return: The core type of the attribute.
|
90
112
|
"""
|
91
|
-
return (t for t in self.attribute_type if t not in (set, list))
|
113
|
+
return tuple(t for t in self.attribute_type if t not in (set, list))
|
92
114
|
|
93
115
|
@property
|
94
116
|
def attribute_type(self) -> Tuple[Type]:
|
95
117
|
"""
|
96
118
|
:return: The type of the attribute.
|
97
119
|
"""
|
98
|
-
if not self.mutually_exclusive and (
|
120
|
+
if not self.mutually_exclusive and (list not in make_list(self._attribute_types)):
|
99
121
|
self._attribute_types = tuple(set(make_list(self._attribute_types) + [set, list]))
|
100
122
|
elif not isinstance(self._attribute_types, tuple):
|
101
123
|
self._attribute_types = tuple(make_list(self._attribute_types))
|
ripple_down_rules/prompt.py
CHANGED
@@ -9,6 +9,9 @@ from textwrap import indent, dedent
|
|
9
9
|
|
10
10
|
from IPython.core.magic import register_line_magic, line_magic, Magics, magics_class
|
11
11
|
from IPython.terminal.embed import InteractiveShellEmbed
|
12
|
+
from pygments import highlight
|
13
|
+
from pygments.formatters.terminal import TerminalFormatter
|
14
|
+
from pygments.lexers.python import PythonLexer
|
12
15
|
from traitlets.config import Config
|
13
16
|
from typing_extensions import List, Optional, Tuple, Dict, Type, Union, Any
|
14
17
|
|
@@ -19,33 +22,40 @@ from .datastructures.dataclasses import CaseQuery
|
|
19
22
|
from .utils import extract_dependencies, contains_return_statement, make_set, get_imports_from_scope, make_list, \
|
20
23
|
get_import_from_type, get_imports_from_types, is_iterable, extract_function_source, encapsulate_user_input, \
|
21
24
|
are_results_subclass_of_types
|
25
|
+
from colorama import Fore, Style, init
|
22
26
|
|
23
27
|
|
24
28
|
@magics_class
|
25
29
|
class MyMagics(Magics):
|
26
30
|
def __init__(self, shell, scope, output_type: Optional[Type] = None, func_name: str = "user_case",
|
27
31
|
func_doc: str = "User defined function to be executed on the case.",
|
28
|
-
code_to_modify: Optional[str] = None
|
32
|
+
code_to_modify: Optional[str] = None,
|
33
|
+
attribute_type_hint: Optional[str] = None,
|
34
|
+
prompt_for: Optional[PromptFor] = None):
|
29
35
|
super().__init__(shell)
|
30
36
|
self.scope = scope
|
31
37
|
self.temp_file_path = None
|
32
38
|
self.func_name = func_name
|
33
39
|
self.func_doc = func_doc
|
34
40
|
self.code_to_modify = code_to_modify
|
41
|
+
self.attribute_type_hint = attribute_type_hint
|
42
|
+
self.prompt_for = prompt_for
|
35
43
|
self.output_type = make_list(output_type) if output_type is not None else None
|
36
44
|
self.user_edit_line = 0
|
37
45
|
self.function_signature: Optional[str] = None
|
38
46
|
self.build_function_signature()
|
39
47
|
|
40
48
|
@line_magic
|
41
|
-
def
|
49
|
+
def edit(self, line):
|
42
50
|
|
43
51
|
boilerplate_code = self.build_boilerplate_code()
|
44
52
|
|
45
53
|
self.write_to_file(boilerplate_code)
|
46
54
|
|
47
55
|
print(f"Opening {self.temp_file_path} in PyCharm...")
|
48
|
-
subprocess.Popen(["pycharm", "--line", str(self.user_edit_line), self.temp_file_path]
|
56
|
+
subprocess.Popen(["pycharm", "--line", str(self.user_edit_line), self.temp_file_path],
|
57
|
+
stdout=subprocess.DEVNULL,
|
58
|
+
stderr=subprocess.DEVNULL)
|
49
59
|
|
50
60
|
def build_boilerplate_code(self):
|
51
61
|
imports = self.get_imports()
|
@@ -59,12 +69,11 @@ class MyMagics(Magics):
|
|
59
69
|
return boilerplate
|
60
70
|
|
61
71
|
def build_function_signature(self):
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
output_type_hint = f" -> Union[{', '.join([t.__name__ for t in self.output_type])}]"
|
72
|
+
output_type_hint = ""
|
73
|
+
if self.prompt_for == PromptFor.Conditions:
|
74
|
+
output_type_hint = " -> bool"
|
75
|
+
elif self.prompt_for == PromptFor.Conclusion:
|
76
|
+
output_type_hint = f" -> {self.attribute_type_hint}"
|
68
77
|
self.function_signature = f"def {self.func_name}(case: {self.case_type.__name__}){output_type_hint}:"
|
69
78
|
|
70
79
|
def write_to_file(self, code: str):
|
@@ -83,7 +92,8 @@ class MyMagics(Magics):
|
|
83
92
|
output_type_imports = get_imports_from_types(self.output_type)
|
84
93
|
if len(self.output_type) > 1:
|
85
94
|
output_type_imports.append("from typing_extensions import Union")
|
86
|
-
|
95
|
+
if list in self.output_type:
|
96
|
+
output_type_imports.append("from typing_extensions import List")
|
87
97
|
imports = get_imports_from_scope(self.scope)
|
88
98
|
imports = [i for i in imports if ("get_ipython" not in i)]
|
89
99
|
if case_type_import not in imports:
|
@@ -103,9 +113,9 @@ class MyMagics(Magics):
|
|
103
113
|
return case._obj_type if isinstance(case, Case) else type(case)
|
104
114
|
|
105
115
|
@line_magic
|
106
|
-
def
|
116
|
+
def load(self, line):
|
107
117
|
if not self.temp_file_path:
|
108
|
-
print("No file to load. Run %
|
118
|
+
print(f"{Fore.RED}ERROR:: No file to load. Run %edit first.{Style.RESET_ALL}")
|
109
119
|
return
|
110
120
|
|
111
121
|
with open(self.temp_file_path, 'r') as f:
|
@@ -118,18 +128,36 @@ class MyMagics(Magics):
|
|
118
128
|
exec(source, self.scope, exec_globals)
|
119
129
|
user_function = exec_globals[self.func_name]
|
120
130
|
self.shell.user_ns[self.func_name] = user_function
|
121
|
-
print(f"Loaded `{self.func_name}` function into user namespace.")
|
131
|
+
print(f"{Fore.BLUE}Loaded `{self.func_name}` function into user namespace.{Style.RESET_ALL}")
|
122
132
|
return
|
123
133
|
|
124
|
-
print(f"Function `{self.func_name}` not found.")
|
134
|
+
print(f"{Fore.RED}ERROR:: Function `{self.func_name}` not found.{Style.RESET_ALL}")
|
135
|
+
|
136
|
+
@line_magic
|
137
|
+
def help(self, line):
|
138
|
+
"""
|
139
|
+
Display help information for the Ipython shell.
|
140
|
+
"""
|
141
|
+
help_text = f"""
|
142
|
+
Directly write python code in the shell, and then `{Fore.GREEN}return {Fore.RESET}output`. Or use
|
143
|
+
the magic commands to write the code in a temporary file and edit it in PyCharm:
|
144
|
+
{Fore.MAGENTA}Usage: %edit{Style.RESET_ALL}
|
145
|
+
Opens a temporary file in PyCharm for editing a function (conclusion or conditions for case)
|
146
|
+
that will be executed on the case object.
|
147
|
+
{Fore.MAGENTA}Usage: %load{Style.RESET_ALL}
|
148
|
+
Loads the function defined in the temporary file into the user namespace, that can then be used inside the
|
149
|
+
Ipython shell. You can then do `{Fore.GREEN}return {Fore.RESET}function_name(case)`.
|
150
|
+
"""
|
151
|
+
print(help_text)
|
125
152
|
|
126
153
|
|
127
154
|
class CustomInteractiveShell(InteractiveShellEmbed):
|
128
155
|
def __init__(self, output_type: Union[Type, Tuple[Type], None] = None, func_name: Optional[str] = None,
|
129
|
-
func_doc: Optional[str] = None, code_to_modify: Optional[str] = None,
|
156
|
+
func_doc: Optional[str] = None, code_to_modify: Optional[str] = None,
|
157
|
+
attribute_type_hint: Optional[str] = None, prompt_for: Optional[PromptFor] = None, **kwargs):
|
130
158
|
super().__init__(**kwargs)
|
131
|
-
keys = ['output_type', 'func_name', 'func_doc', 'code_to_modify']
|
132
|
-
values = [output_type, func_name, func_doc, code_to_modify]
|
159
|
+
keys = ['output_type', 'func_name', 'func_doc', 'code_to_modify', 'attribute_type_hint', 'prompt_for']
|
160
|
+
values = [output_type, func_name, func_doc, code_to_modify, attribute_type_hint, prompt_for]
|
133
161
|
magics_kwargs = {key: value for key, value in zip(keys, values) if value is not None}
|
134
162
|
self.my_magics = MyMagics(self, self.user_ns, **magics_kwargs)
|
135
163
|
self.register_magics(self.my_magics)
|
@@ -145,7 +173,6 @@ class CustomInteractiveShell(InteractiveShellEmbed):
|
|
145
173
|
self.my_magics.func_name,
|
146
174
|
join_lines=False)[self.my_magics.func_name]
|
147
175
|
self.all_lines.append(raw_cell)
|
148
|
-
print("Exiting shell on `return` statement.")
|
149
176
|
self.history_manager.store_inputs(line_num=self.execution_count, source=raw_cell)
|
150
177
|
self.ask_exit()
|
151
178
|
return None
|
@@ -161,26 +188,28 @@ class IPythonShell:
|
|
161
188
|
"""
|
162
189
|
|
163
190
|
def __init__(self, scope: Optional[Dict] = None, header: Optional[str] = None,
|
164
|
-
|
165
|
-
attribute_name: Optional[str] = None, attribute_type: Optional[Type] = None,
|
191
|
+
prompt_for: Optional[PromptFor] = None, case_query: Optional[CaseQuery] = None,
|
166
192
|
code_to_modify: Optional[str] = None):
|
167
193
|
"""
|
168
194
|
Initialize the Ipython shell with the given scope and header.
|
169
195
|
|
170
196
|
:param scope: The scope to use for the shell.
|
171
197
|
:param header: The header to display when the shell is started.
|
172
|
-
:param output_type: The type of the output from user input.
|
173
198
|
:param prompt_for: The type of information to ask the user about.
|
174
|
-
:param
|
175
|
-
:param attribute_type: The type of the attribute of the case.
|
199
|
+
:param case_query: The case query which contains the case and the attribute to ask about.
|
176
200
|
:param code_to_modify: The code to modify. If given, will be used as a start for user to modify.
|
177
201
|
"""
|
178
202
|
self.scope: Dict = scope or {}
|
179
203
|
self.header: str = header or ">>> Embedded Ipython Shell"
|
204
|
+
output_type = None
|
205
|
+
if prompt_for is not None:
|
206
|
+
if prompt_for == PromptFor.Conclusion and case_query is not None:
|
207
|
+
output_type = case_query.attribute_type
|
208
|
+
elif prompt_for == PromptFor.Conditions:
|
209
|
+
output_type = bool
|
210
|
+
self.case_query: Optional[CaseQuery] = case_query
|
180
211
|
self.output_type: Optional[Type] = output_type
|
181
212
|
self.prompt_for: Optional[PromptFor] = prompt_for
|
182
|
-
self.attribute_name: Optional[str] = attribute_name
|
183
|
-
self.attribute_type: Optional[Type] = attribute_type
|
184
213
|
self.code_to_modify: Optional[str] = code_to_modify
|
185
214
|
self.user_input: Optional[str] = None
|
186
215
|
self.func_name: str = ""
|
@@ -196,7 +225,9 @@ class IPythonShell:
|
|
196
225
|
self.build_func_name_and_doc()
|
197
226
|
shell = CustomInteractiveShell(config=cfg, user_ns=self.scope, banner1=self.header,
|
198
227
|
output_type=self.output_type, func_name=self.func_name, func_doc=self.func_doc,
|
199
|
-
code_to_modify=self.code_to_modify
|
228
|
+
code_to_modify=self.code_to_modify,
|
229
|
+
attribute_type_hint=self.case_query.attribute_type_hint,
|
230
|
+
prompt_for=self.prompt_for)
|
200
231
|
return shell
|
201
232
|
|
202
233
|
def build_func_name_and_doc(self) -> Tuple[str, str]:
|
@@ -210,31 +241,38 @@ class IPythonShell:
|
|
210
241
|
self.func_name = self.build_func_name(case_type)
|
211
242
|
self.func_doc = self.build_func_doc(case_type)
|
212
243
|
|
213
|
-
def build_func_doc(self, case_type: Type):
|
244
|
+
def build_func_doc(self, case_type: Type) -> Optional[str]:
|
245
|
+
if self.case_query is None or self.prompt_for is None:
|
246
|
+
return
|
247
|
+
|
214
248
|
if self.prompt_for == PromptFor.Conditions:
|
215
249
|
func_doc = (f"Get conditions on whether it's possible to conclude a value"
|
216
|
-
f" for {case_type.__name__}.{self.attribute_name}")
|
250
|
+
f" for {case_type.__name__}.{self.case_query.attribute_name}")
|
251
|
+
elif self.prompt_for == PromptFor.Conclusion:
|
252
|
+
func_doc = f"Get possible value(s) for {case_type.__name__}.{self.case_query.attribute_name}"
|
217
253
|
else:
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
254
|
+
return
|
255
|
+
|
256
|
+
possible_types = [t.__name__ for t in self.case_query.attribute_type if t not in [list, set]]
|
257
|
+
if list in self.case_query.attribute_type:
|
258
|
+
func_doc += f" of type list of {' and/or '.join(possible_types)}"
|
222
259
|
else:
|
223
|
-
func_doc += f" of type {
|
260
|
+
func_doc += f" of type(s) {', '.join(possible_types)}"
|
261
|
+
|
224
262
|
return func_doc
|
225
263
|
|
226
|
-
def build_func_name(self, case_type: Type):
|
227
|
-
func_name =
|
228
|
-
|
229
|
-
|
230
|
-
func_name += f"_{
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
output_names = [
|
235
|
-
if output_names is not None:
|
264
|
+
def build_func_name(self, case_type: Type) -> Optional[str]:
|
265
|
+
func_name = None
|
266
|
+
if self.prompt_for is not None:
|
267
|
+
func_name = f"get_{self.prompt_for.value.lower()}_for"
|
268
|
+
func_name += f"_{case_type.__name__}"
|
269
|
+
|
270
|
+
if self.case_query is not None:
|
271
|
+
func_name += f"_{self.case_query.attribute_name}"
|
272
|
+
output_names = [f"{t.__name__}" for t in self.case_query.attribute_type if t not in [list, set]]
|
236
273
|
func_name += '_of_type_' + '_'.join(output_names)
|
237
|
-
|
274
|
+
|
275
|
+
return func_name.lower() if func_name is not None else None
|
238
276
|
|
239
277
|
def run(self):
|
240
278
|
"""
|
@@ -247,7 +285,7 @@ class IPythonShell:
|
|
247
285
|
break
|
248
286
|
except Exception as e:
|
249
287
|
logging.error(e)
|
250
|
-
print(e)
|
288
|
+
print(f"{Fore.RED}ERROR::{e}{Style.RESET_ALL}")
|
251
289
|
|
252
290
|
def update_user_input_from_code_lines(self):
|
253
291
|
"""
|
@@ -285,10 +323,10 @@ def prompt_user_for_expression(case_query: CaseQuery, prompt_for: PromptFor, pro
|
|
285
323
|
prev_user_input = '\n'.join(user_input.split('\n')[2:-1])
|
286
324
|
if user_input is None:
|
287
325
|
if prompt_for == PromptFor.Conclusion:
|
288
|
-
print("No conclusion provided. Exiting.")
|
326
|
+
print(f"{Fore.YELLOW}No conclusion provided. Exiting.{Style.RESET_ALL}")
|
289
327
|
return None, None
|
290
328
|
else:
|
291
|
-
print("Conditions must be provided. Please try again.")
|
329
|
+
print(f"{Fore.RED}Conditions must be provided. Please try again.{Style.RESET_ALL}")
|
292
330
|
continue
|
293
331
|
conclusion_type = bool if prompt_for == PromptFor.Conditions else case_query.attribute_type
|
294
332
|
callable_expression = CallableExpression(user_input, conclusion_type, expression_tree=expression_tree,
|
@@ -296,12 +334,13 @@ def prompt_user_for_expression(case_query: CaseQuery, prompt_for: PromptFor, pro
|
|
296
334
|
try:
|
297
335
|
result = callable_expression(case_query.case)
|
298
336
|
if len(make_list(result)) == 0:
|
299
|
-
print(f"The given expression gave an empty result for case {case_query.name}.
|
337
|
+
print(f"{Fore.YELLOW}The given expression gave an empty result for case {case_query.name}."
|
338
|
+
f" Please modify!{Style.RESET_ALL}")
|
300
339
|
continue
|
301
340
|
break
|
302
341
|
except Exception as e:
|
303
342
|
logging.error(e)
|
304
|
-
print(e)
|
343
|
+
print(f"{Fore.RED}{e}{Style.RESET_ALL}")
|
305
344
|
return user_input, callable_expression
|
306
345
|
|
307
346
|
|
@@ -318,11 +357,20 @@ def prompt_user_about_case(case_query: CaseQuery, prompt_for: PromptFor,
|
|
318
357
|
:return: The user input, and the executable expression that was parsed from the user input.
|
319
358
|
"""
|
320
359
|
if prompt_str is None:
|
321
|
-
|
360
|
+
if prompt_for == PromptFor.Conclusion:
|
361
|
+
prompt_str = f"Give possible value(s) for:\n"
|
362
|
+
else:
|
363
|
+
prompt_str = f"Give conditions on when can the rule be evaluated for:\n"
|
364
|
+
prompt_str += (f"{Fore.CYAN}{case_query.name}{Fore.MAGENTA} of type(s) "
|
365
|
+
f"{Fore.CYAN}({', '.join(map(lambda x: x.__name__, case_query.core_attribute_type))}){Fore.MAGENTA}")
|
366
|
+
if prompt_for == PromptFor.Conditions:
|
367
|
+
prompt_str += (f"\ne.g. `{Fore.GREEN}return {Fore.BLUE}len{Fore.RESET}(case.attribute) > {Fore.BLUE}0` "
|
368
|
+
f"{Fore.MAGENTA}\nOR `{Fore.GREEN}return {Fore.YELLOW}True`{Fore.MAGENTA} (If you want the"
|
369
|
+
f" rule to be always evaluated) \n"
|
370
|
+
f"You can also do {Fore.YELLOW}%edit{Fore.MAGENTA} for more complex conditions.")
|
371
|
+
prompt_str = f"{Fore.MAGENTA}{prompt_str}{Fore.YELLOW}\n(Write %help for guide){Fore.RESET}"
|
322
372
|
scope = {'case': case_query.case, **case_query.scope}
|
323
|
-
|
324
|
-
shell = IPythonShell(scope=scope, header=prompt_str, output_type=output_type, prompt_for=prompt_for,
|
325
|
-
attribute_name=case_query.attribute_name, attribute_type=case_query.attribute_type,
|
373
|
+
shell = IPythonShell(scope=scope, header=prompt_str, prompt_for=prompt_for, case_query=case_query,
|
326
374
|
code_to_modify=code_to_modify)
|
327
375
|
return prompt_user_input_and_parse_to_expression(shell=shell)
|
328
376
|
|
@@ -344,11 +392,13 @@ def prompt_user_input_and_parse_to_expression(shell: Optional[IPythonShell] = No
|
|
344
392
|
user_input = shell.user_input
|
345
393
|
if user_input is None:
|
346
394
|
return None, None
|
347
|
-
print(
|
395
|
+
print(f"{Fore.BLUE}Captured User input: {Style.RESET_ALL}")
|
396
|
+
highlighted_code = highlight(user_input, PythonLexer(), TerminalFormatter())
|
397
|
+
print(highlighted_code)
|
348
398
|
try:
|
349
399
|
return user_input, parse_string_to_expression(user_input)
|
350
400
|
except Exception as e:
|
351
401
|
msg = f"Error parsing expression: {e}"
|
352
402
|
logging.error(msg)
|
353
|
-
print(msg)
|
403
|
+
print(f"{Fore.RED}{msg}{Style.RESET_ALL}")
|
354
404
|
user_input = None
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: ripple_down_rules
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.4
|
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
|
@@ -680,7 +680,7 @@ License: GNU GENERAL PUBLIC LICENSE
|
|
680
680
|
Project-URL: Homepage, https://github.com/AbdelrhmanBassiouny/ripple_down_rules
|
681
681
|
Keywords: robotics,knowledge,reasoning,representation
|
682
682
|
Classifier: Programming Language :: Python :: 3
|
683
|
-
Requires-Python: >=3.
|
683
|
+
Requires-Python: >=3.10
|
684
684
|
Description-Content-Type: text/markdown
|
685
685
|
License-File: LICENSE
|
686
686
|
Dynamic: license-file
|
@@ -3,18 +3,18 @@ ripple_down_rules/datasets.py,sha256=mjJh1GLD_5qMgHaukdDWSGphXS9k_BPEF001ZXPchr8
|
|
3
3
|
ripple_down_rules/experts.py,sha256=JGVvSNiWhm4FpRpg76f98tl8Ii_C7x_aWD9FxD-JDLQ,6130
|
4
4
|
ripple_down_rules/failures.py,sha256=E6ajDUsw3Blom8eVLbA7d_Qnov2conhtZ0UmpQ9ZtSE,302
|
5
5
|
ripple_down_rules/helpers.py,sha256=TvTJU0BA3dPcAyzvZFvAu7jZqsp8Lu0HAAwvuizlGjg,2018
|
6
|
-
ripple_down_rules/prompt.py,sha256=
|
6
|
+
ripple_down_rules/prompt.py,sha256=8RJB8pRaK-MKdDeIbUwylrjaCOk3cIFraTxgtGW8pxU,19070
|
7
7
|
ripple_down_rules/rdr.py,sha256=vxNZckp6sLAUD92JQgfCzhBhg9CXfMZ_7W4VgrIUFjU,43366
|
8
8
|
ripple_down_rules/rdr_decorators.py,sha256=8SclpceI3EtrsbuukWJu8HGLh7Q1ZCgYGLX-RPlG-w0,2018
|
9
9
|
ripple_down_rules/rules.py,sha256=QQy7NBG6mKiowxVG_LjQJBxLTDW2hMyx5zAgwUxdCMM,17183
|
10
10
|
ripple_down_rules/utils.py,sha256=EdVdIf93TAqbxRTzbf_1422FjenRSI4MI_Ecp3e10z8,44007
|
11
11
|
ripple_down_rules/datastructures/__init__.py,sha256=V2aNgf5C96Y5-IGghra3n9uiefpoIm_QdT7cc_C8cxQ,111
|
12
|
-
ripple_down_rules/datastructures/callable_expression.py,sha256=
|
12
|
+
ripple_down_rules/datastructures/callable_expression.py,sha256=PDOTdfrLOMg0XPIEiKXvwEvJpkXfcKm8cOGtfX-HHRw,11144
|
13
13
|
ripple_down_rules/datastructures/case.py,sha256=nJDKOjyhGLx4gt0sHxJNxBLdy9X2SLcDn89_SmKzwoc,14035
|
14
|
-
ripple_down_rules/datastructures/dataclasses.py,sha256=
|
14
|
+
ripple_down_rules/datastructures/dataclasses.py,sha256=CLOTr0MzMdrFHQcz4ny_Q43b-BPNWMGY_s6e4HrQ8As,6990
|
15
15
|
ripple_down_rules/datastructures/enums.py,sha256=hlE6LAa1jUafnG_6UazvaPDfhC1ClI7hKvD89zOyAO8,4661
|
16
|
-
ripple_down_rules-0.2.
|
17
|
-
ripple_down_rules-0.2.
|
18
|
-
ripple_down_rules-0.2.
|
19
|
-
ripple_down_rules-0.2.
|
20
|
-
ripple_down_rules-0.2.
|
16
|
+
ripple_down_rules-0.2.4.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
|
17
|
+
ripple_down_rules-0.2.4.dist-info/METADATA,sha256=7WAVU6ymoPo5fcQTRZc7ex5BngsI0NqWOBEPR_-xVro,42576
|
18
|
+
ripple_down_rules-0.2.4.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
|
19
|
+
ripple_down_rules-0.2.4.dist-info/top_level.txt,sha256=VeoLhEhyK46M1OHwoPbCQLI1EifLjChqGzhQ6WEUqeM,18
|
20
|
+
ripple_down_rules-0.2.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|