11import ast
22import copy
33import inspect
4+ import re
45import sys
56import textwrap
67import 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+
90139class 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