11import ast
22import copy
33import inspect
4+ import re
45import sys
56import textwrap
67import 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" )
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+ # 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+ # go through to get decorators
128+ if func_name in line :
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+
92141class BaseParser (ast .NodeTransformer , metaclass = ABCMeta ):
93142 _DEBUG_PARSE = False
94143
@@ -111,7 +160,13 @@ 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+ else :
166+ raw_source = inspect .getsource (raw_func )
167+
168+ source = textwrap .dedent (raw_source )
169+
115170 body = ast .parse (source )
116171 self ._funcdef = body .body [0 ]
117172 self ._type_annotation_normalizer .normalize_type_annotations (self ._funcdef )
0 commit comments