Skip to content

Commit d1ab44a

Browse files
committed
allow for csp nodes and graphs to be defined in repl
Signed-off-by: Tim Paine <[email protected]>
1 parent a357200 commit d1ab44a

File tree

1 file changed

+58
-1
lines changed

1 file changed

+58
-1
lines changed

Diff for: csp/impl/wiring/base_parser.py

+58-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import ast
22
import copy
33
import inspect
4+
import re
45
import sys
56
import textwrap
67
import typing
@@ -89,6 +90,54 @@ def wrapper(*args, **kwargs):
8990
return wrapper
9091

9192

93+
def _get_source_from_interpreter_function(raw_func):
94+
try:
95+
import readline
96+
except ImportError as exc:
97+
raise OSError(
98+
"Could not get source code for interpreter-defined function without `pyreadline` installed."
99+
) from exc
100+
101+
current_interpreter_history = readline.get_current_history_length()
102+
103+
try:
104+
search_pattern = re.compile(r"^(\s*def\s)")
105+
decorator_pattern = re.compile(r"^(\s*@)")
106+
func_name = raw_func.__name__
107+
code_object = raw_func.__func__ if inspect.ismethod(raw_func) else raw_func.__code__
108+
except Exception:
109+
raise OSError("Could not get source code for interpreter-defined function.")
110+
111+
if not hasattr(code_object, "co_firstlineno"):
112+
raise OSError("Could not find function definition for interpreter-defined function.")
113+
114+
reassembled_function = ""
115+
starting_index_of_function = current_interpreter_history
116+
117+
# walk back through history to find the function definition
118+
while starting_index_of_function > 0:
119+
line = readline.get_history_item(starting_index_of_function)
120+
121+
# if its a def name_of_function(...
122+
if search_pattern.match(line):
123+
# go through to get decorators
124+
if func_name in line:
125+
# reassemble function
126+
for i in range(starting_index_of_function, current_interpreter_history + 1):
127+
reassembled_function += f"{readline.get_history_item(i)}\n"
128+
129+
for line_number_with_decorator in range(starting_index_of_function - 1, -1, -1):
130+
if decorator_pattern.match(readline.get_history_item(line_number_with_decorator)):
131+
reassembled_function = (
132+
f"{readline.get_history_item(line_number_with_decorator)}\n" + reassembled_function
133+
)
134+
else:
135+
break
136+
break
137+
starting_index_of_function -= 1
138+
return reassembled_function
139+
140+
92141
class BaseParser(ast.NodeTransformer, metaclass=ABCMeta):
93142
_DEBUG_PARSE = False
94143

@@ -111,7 +160,15 @@ def __init__(self, name, raw_func, func_frame, debug_print=False, add_cache_cont
111160
self._func_globals_modified["csp"] = csp
112161
self._func_globals_modified.update(self._func_frame.f_globals)
113162

114-
source = textwrap.dedent(inspect.getsource(raw_func))
163+
if raw_func.__code__.co_filename == "<stdin>":
164+
raw_source = _get_source_from_interpreter_function(raw_func)
165+
elif raw_func.__code__.co_filename == "<string>":
166+
raise OSError("Could not find function definition for exec'd function.")
167+
else:
168+
raw_source = inspect.getsource(raw_func)
169+
170+
source = textwrap.dedent(raw_source)
171+
115172
body = ast.parse(source)
116173
self._funcdef = body.body[0]
117174
self._type_annotation_normalizer.normalize_type_annotations(self._funcdef)

0 commit comments

Comments
 (0)