1
1
import ast
2
2
import copy
3
3
import inspect
4
+ import re
4
5
import sys
5
6
import textwrap
6
7
import typing
@@ -89,6 +90,55 @@ def wrapper(*args, **kwargs):
89
90
return wrapper
90
91
91
92
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
+ print (reassembled_function )
135
+ else :
136
+ break
137
+ break
138
+ starting_index_of_function -= 1
139
+ return reassembled_function
140
+
141
+
92
142
class BaseParser (ast .NodeTransformer , metaclass = ABCMeta ):
93
143
_DEBUG_PARSE = False
94
144
@@ -111,7 +161,13 @@ def __init__(self, name, raw_func, func_frame, debug_print=False, add_cache_cont
111
161
self ._func_globals_modified ["csp" ] = csp
112
162
self ._func_globals_modified .update (self ._func_frame .f_globals )
113
163
114
- source = textwrap .dedent (inspect .getsource (raw_func ))
164
+ if raw_func .__code__ .co_filename == "<stdin>" :
165
+ raw_source = _get_source_from_interpreter_function (raw_func )
166
+ else :
167
+ raw_source = inspect .getsource (raw_func )
168
+
169
+ source = textwrap .dedent (raw_source )
170
+
115
171
body = ast .parse (source )
116
172
self ._funcdef = body .body [0 ]
117
173
self ._type_annotation_normalizer .normalize_type_annotations (self ._funcdef )
0 commit comments