Skip to content

allow for csp nodes and graphs to be defined in repl #178

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 59 additions & 1 deletion csp/impl/wiring/base_parser.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import ast
import copy
import inspect
import re
import sys
import textwrap
import typing
Expand Down Expand Up @@ -87,6 +88,55 @@ def wrapper(*args, **kwargs):
return wrapper


def _get_source_from_interpreter_function(raw_func):
try:
import readline
except ImportError as exc:
raise OSError(
"Could not get source code for interpreter-defined function without `pyreadline` installed."
) from exc

current_interpreter_history = readline.get_current_history_length()

try:
search_pattern = re.compile(r"^(\s*def\s*" + raw_func.__name__ + r"\s*\()")
decorator_pattern = re.compile(r"^(\s*@)")
code_object = raw_func.__func__ if inspect.ismethod(raw_func) else raw_func.__code__
except Exception:
raise OSError("Could not get source code for interpreter-defined function.")

if not hasattr(code_object, "co_firstlineno"):
raise OSError("Could not find function definition for interpreter-defined function.")

reassembled_function = ""
starting_index_of_function = current_interpreter_history

# walk back through history to find the function definition
while starting_index_of_function > 0:
line = readline.get_history_item(starting_index_of_function)

# if its a def name_of_function(...
if search_pattern.match(line):
# reassemble function
for i in range(starting_index_of_function, current_interpreter_history + 1):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could be a little smarter here and go block-by-block to ensure we only grab 1

reassembled_function += f"{readline.get_history_item(i)}\n"

for line_number_with_decorator in range(starting_index_of_function - 1, -1, -1):
if decorator_pattern.match(readline.get_history_item(line_number_with_decorator)):
reassembled_function = (
f"{readline.get_history_item(line_number_with_decorator)}\n" + reassembled_function
)
else:
break
break
starting_index_of_function -= 1

if reassembled_function == "":
raise OSError("Could not find function definition for interpreter-defined function.")

return reassembled_function


class BaseParser(ast.NodeTransformer, metaclass=ABCMeta):
_DEBUG_PARSE = False

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

source = textwrap.dedent(inspect.getsource(raw_func))
if raw_func.__code__.co_filename == "<stdin>":
raw_source = _get_source_from_interpreter_function(raw_func)
elif raw_func.__code__.co_filename == "<string>":
raise OSError("Could not find function definition for exec'd function.")
else:
raw_source = inspect.getsource(raw_func)

source = textwrap.dedent(raw_source)

body = ast.parse(source)
self._funcdef = body.body[0]
self._type_annotation_normalizer.normalize_type_annotations(self._funcdef)
Expand Down
Loading