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.
@@ -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 prompt_toolkit import PromptSession
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 Case, PromptFor, CallableExpression, create_case, parse_string_to_expression, CaseQuery
14
- from .utils import capture_variable_assignment
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
- def __init__(self, variable_to_capture: str, scope: Optional[Dict] = None, header: Optional[str] = None):
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: InteractiveShellEmbed = self._init_shell()
34
- self._register_hooks()
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 = InteractiveShellEmbed(config=cfg, user_ns=self.scope, banner1=self.header)
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
- def prompt_user_for_expression(case_query: CaseQuery, prompt_for: PromptFor,
69
- session: Optional[Session] = None) -> Tuple[str, CallableExpression]:
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, session=session)
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(prompt_for.value, scope=scope, header=prompt_str)
103
- user_input, expression_tree = prompt_user_input_and_parse_to_expression(shell=shell)
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) -> Tuple[str, ast.AST]:
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