Skip to content

Commit 9c52981

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

File tree

1 file changed

+58
-1
lines changed

1 file changed

+58
-1
lines changed

csp/impl/wiring/base_parser.py

Lines changed: 58 additions & 1 deletion
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
@@ -87,6 +88,54 @@ def wrapper(*args, **kwargs):
8788
return wrapper
8889

8990

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

@@ -109,7 +158,15 @@ def __init__(self, name, raw_func, func_frame, debug_print=False):
109158
self._func_globals_modified["csp"] = csp
110159
self._func_globals_modified.update(self._func_frame.f_globals)
111160

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

0 commit comments

Comments
 (0)