ripple-down-rules 0.1.2__py3-none-any.whl → 0.1.5__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/datasets.py +2 -1
- ripple_down_rules/datastructures/__init__.py +4 -4
- ripple_down_rules/datastructures/callable_expression.py +74 -129
- ripple_down_rules/datastructures/case.py +1 -1
- ripple_down_rules/datastructures/dataclasses.py +102 -48
- ripple_down_rules/experts.py +24 -22
- ripple_down_rules/prompt.py +68 -68
- ripple_down_rules/rdr.py +290 -153
- ripple_down_rules/rules.py +64 -32
- ripple_down_rules/utils.py +166 -4
- {ripple_down_rules-0.1.2.dist-info → ripple_down_rules-0.1.5.dist-info}/METADATA +1 -1
- ripple_down_rules-0.1.5.dist-info/RECORD +20 -0
- {ripple_down_rules-0.1.2.dist-info → ripple_down_rules-0.1.5.dist-info}/WHEEL +1 -1
- ripple_down_rules-0.1.2.dist-info/RECORD +0 -20
- {ripple_down_rules-0.1.2.dist-info → ripple_down_rules-0.1.5.dist-info}/licenses/LICENSE +0 -0
- {ripple_down_rules-0.1.2.dist-info → ripple_down_rules-0.1.5.dist-info}/top_level.txt +0 -0
ripple_down_rules/prompt.py
CHANGED
@@ -2,84 +2,109 @@ import ast
|
|
2
2
|
import logging
|
3
3
|
from _ast import AST
|
4
4
|
|
5
|
-
from IPython.core.interactiveshell import ExecutionInfo
|
6
5
|
from IPython.terminal.embed import InteractiveShellEmbed
|
7
6
|
from traitlets.config import Config
|
8
|
-
from
|
9
|
-
from prompt_toolkit.completion import WordCompleter
|
10
|
-
from sqlalchemy.orm import DeclarativeBase as SQLTable, Session
|
11
|
-
from typing_extensions import Any, List, Optional, Tuple, Dict, Union, Type
|
7
|
+
from typing_extensions import List, Optional, Tuple, Dict
|
12
8
|
|
13
|
-
from .datastructures import
|
14
|
-
from .
|
9
|
+
from .datastructures.enums import PromptFor
|
10
|
+
from .datastructures.callable_expression import CallableExpression, parse_string_to_expression
|
11
|
+
from .datastructures.dataclasses import CaseQuery
|
12
|
+
from .utils import extract_dependencies, contains_return_statement
|
13
|
+
|
14
|
+
|
15
|
+
class CustomInteractiveShell(InteractiveShellEmbed):
|
16
|
+
def __init__(self, **kwargs):
|
17
|
+
super().__init__(**kwargs)
|
18
|
+
self.all_lines = []
|
19
|
+
|
20
|
+
def run_cell(self, raw_cell: str, **kwargs):
|
21
|
+
"""
|
22
|
+
Override the run_cell method to capture return statements.
|
23
|
+
"""
|
24
|
+
if contains_return_statement(raw_cell):
|
25
|
+
self.all_lines.append(raw_cell)
|
26
|
+
print("Exiting shell on `return` statement.")
|
27
|
+
self.history_manager.store_inputs(line_num=self.execution_count, source=raw_cell)
|
28
|
+
self.ask_exit()
|
29
|
+
return None
|
30
|
+
result = super().run_cell(raw_cell, **kwargs)
|
31
|
+
if not result.error_in_exec:
|
32
|
+
self.all_lines.append(raw_cell)
|
33
|
+
return result
|
15
34
|
|
16
35
|
|
17
36
|
class IpythonShell:
|
18
37
|
"""
|
19
38
|
Create an embedded Ipython shell that can be used to prompt the user for input.
|
20
39
|
"""
|
21
|
-
|
40
|
+
|
41
|
+
def __init__(self, scope: Optional[Dict] = None, header: Optional[str] = None):
|
22
42
|
"""
|
23
43
|
Initialize the Ipython shell with the given scope and header.
|
24
44
|
|
25
|
-
:param variable_to_capture: The variable to capture from the user input.
|
26
45
|
:param scope: The scope to use for the shell.
|
27
46
|
:param header: The header to display when the shell is started.
|
28
47
|
"""
|
29
|
-
self.variable_to_capture: str = variable_to_capture
|
30
48
|
self.scope: Dict = scope or {}
|
31
49
|
self.header: str = header or ">>> Embedded Ipython Shell"
|
32
50
|
self.user_input: Optional[str] = None
|
33
|
-
self.shell:
|
34
|
-
self.
|
51
|
+
self.shell: CustomInteractiveShell = self._init_shell()
|
52
|
+
self.all_code_lines: List[str] = []
|
35
53
|
|
36
54
|
def _init_shell(self):
|
37
55
|
"""
|
38
56
|
Initialize the Ipython shell with a custom configuration.
|
39
57
|
"""
|
40
58
|
cfg = Config()
|
41
|
-
shell =
|
59
|
+
shell = CustomInteractiveShell(config=cfg, user_ns=self.scope, banner1=self.header)
|
42
60
|
return shell
|
43
61
|
|
44
|
-
def _register_hooks(self):
|
45
|
-
"""
|
46
|
-
Register hooks to capture specific events in the Ipython shell.
|
47
|
-
"""
|
48
|
-
def capture_variable(exec_info: ExecutionInfo):
|
49
|
-
code = exec_info.raw_cell
|
50
|
-
if self.variable_to_capture not in code:
|
51
|
-
return
|
52
|
-
# use ast to find if the user is assigning a value to the variable "condition"
|
53
|
-
assignment = capture_variable_assignment(code, self.variable_to_capture)
|
54
|
-
if assignment:
|
55
|
-
# if the user is assigning a value to the variable "condition", update the raw_condition
|
56
|
-
self.user_input = assignment
|
57
|
-
print(f"[Captured {self.variable_to_capture}]:\n{self.user_input}")
|
58
|
-
|
59
|
-
self.shell.events.register('pre_run_cell', capture_variable)
|
60
|
-
|
61
62
|
def run(self):
|
62
63
|
"""
|
63
64
|
Run the embedded shell.
|
64
65
|
"""
|
65
66
|
self.shell()
|
67
|
+
self.update_user_input_from_code_lines()
|
66
68
|
|
67
|
-
|
68
|
-
|
69
|
-
|
69
|
+
def update_user_input_from_code_lines(self):
|
70
|
+
"""
|
71
|
+
Update the user input from the code lines captured in the shell.
|
72
|
+
"""
|
73
|
+
if len(self.shell.all_lines) == 1 and self.shell.all_lines[0].replace('return', '').strip() == '':
|
74
|
+
self.user_input = None
|
75
|
+
else:
|
76
|
+
self.all_code_lines = extract_dependencies(self.shell.all_lines)
|
77
|
+
if len(self.all_code_lines) == 1:
|
78
|
+
if self.all_code_lines[0].strip() == '':
|
79
|
+
self.user_input = None
|
80
|
+
else:
|
81
|
+
self.user_input = self.all_code_lines[0].replace('return', '').strip()
|
82
|
+
else:
|
83
|
+
self.user_input = f"def _get_value(case):\n "
|
84
|
+
self.user_input += '\n '.join(self.all_code_lines)
|
85
|
+
|
86
|
+
|
87
|
+
def prompt_user_for_expression(case_query: CaseQuery, prompt_for: PromptFor)\
|
88
|
+
-> Tuple[Optional[str], Optional[CallableExpression]]:
|
70
89
|
"""
|
71
90
|
Prompt the user for an executable python expression to the given case query.
|
72
91
|
|
73
92
|
:param case_query: The case query to prompt the user for.
|
74
93
|
:param prompt_for: The type of information ask user about.
|
75
|
-
:param session: The sqlalchemy orm session.
|
76
94
|
:return: A callable expression that takes a case and executes user expression on it.
|
77
95
|
"""
|
78
96
|
while True:
|
79
97
|
user_input, expression_tree = prompt_user_about_case(case_query, prompt_for)
|
98
|
+
if user_input is None:
|
99
|
+
if prompt_for == PromptFor.Conclusion:
|
100
|
+
print("No conclusion provided. Exiting.")
|
101
|
+
return None, None
|
102
|
+
else:
|
103
|
+
print("Conditions must be provided. Please try again.")
|
104
|
+
continue
|
80
105
|
conclusion_type = bool if prompt_for == PromptFor.Conditions else case_query.attribute_type
|
81
106
|
callable_expression = CallableExpression(user_input, conclusion_type, expression_tree=expression_tree,
|
82
|
-
scope=case_query.scope
|
107
|
+
scope=case_query.scope)
|
83
108
|
try:
|
84
109
|
callable_expression(case_query.case)
|
85
110
|
break
|
@@ -89,7 +114,7 @@ def prompt_user_for_expression(case_query: CaseQuery, prompt_for: PromptFor,
|
|
89
114
|
return user_input, callable_expression
|
90
115
|
|
91
116
|
|
92
|
-
def prompt_user_about_case(case_query: CaseQuery, prompt_for: PromptFor) -> Tuple[str, AST]:
|
117
|
+
def prompt_user_about_case(case_query: CaseQuery, prompt_for: PromptFor) -> Tuple[Optional[str], Optional[AST]]:
|
93
118
|
"""
|
94
119
|
Prompt the user for input.
|
95
120
|
|
@@ -99,27 +124,13 @@ def prompt_user_about_case(case_query: CaseQuery, prompt_for: PromptFor) -> Tupl
|
|
99
124
|
"""
|
100
125
|
prompt_str = f"Give {prompt_for} for {case_query.name}"
|
101
126
|
scope = {'case': case_query.case, **case_query.scope}
|
102
|
-
shell = IpythonShell(
|
103
|
-
|
104
|
-
return user_input, expression_tree
|
105
|
-
|
106
|
-
|
107
|
-
def get_completions(obj: Any) -> List[str]:
|
108
|
-
"""
|
109
|
-
Get all completions for the object. This is used in the python prompt shell to provide completions for the user.
|
110
|
-
|
111
|
-
:param obj: The object to get completions for.
|
112
|
-
:return: A list of completions.
|
113
|
-
"""
|
114
|
-
# Define completer with all object attributes and comparison operators
|
115
|
-
completions = ['==', '!=', '>', '<', '>=', '<=', 'in', 'not', 'and', 'or', 'is']
|
116
|
-
completions += ["isinstance(", "issubclass(", "type(", "len(", "hasattr(", "getattr(", "setattr(", "delattr("]
|
117
|
-
completions += list(create_case(obj).keys())
|
118
|
-
return completions
|
127
|
+
shell = IpythonShell(scope=scope, header=prompt_str)
|
128
|
+
return prompt_user_input_and_parse_to_expression(shell=shell)
|
119
129
|
|
120
130
|
|
121
131
|
def prompt_user_input_and_parse_to_expression(shell: Optional[IpythonShell] = None,
|
122
|
-
user_input: Optional[str] = None)
|
132
|
+
user_input: Optional[str] = None)\
|
133
|
+
-> Tuple[Optional[str], Optional[ast.AST]]:
|
123
134
|
"""
|
124
135
|
Prompt the user for input.
|
125
136
|
|
@@ -132,23 +143,12 @@ def prompt_user_input_and_parse_to_expression(shell: Optional[IpythonShell] = No
|
|
132
143
|
shell = IpythonShell() if shell is None else shell
|
133
144
|
shell.run()
|
134
145
|
user_input = shell.user_input
|
146
|
+
if user_input is None:
|
147
|
+
return None, None
|
148
|
+
print(user_input)
|
135
149
|
try:
|
136
150
|
return user_input, parse_string_to_expression(user_input)
|
137
151
|
except Exception as e:
|
138
152
|
msg = f"Error parsing expression: {e}"
|
139
153
|
logging.error(msg)
|
140
|
-
print(msg)
|
141
154
|
user_input = None
|
142
|
-
|
143
|
-
|
144
|
-
def get_prompt_session_for_obj(obj: Any) -> PromptSession:
|
145
|
-
"""
|
146
|
-
Get a prompt session for an object.
|
147
|
-
|
148
|
-
:param obj: The object to get the prompt session for.
|
149
|
-
:return: The prompt session.
|
150
|
-
"""
|
151
|
-
completions = get_completions(obj)
|
152
|
-
completer = WordCompleter(completions)
|
153
|
-
session = PromptSession(completer=completer)
|
154
|
-
return session
|