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,54 @@ 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 for interpreter-defined function." )
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
+ # go through to get decorators
124
+ if func_name in line :
125
+ # reassemble function
126
+ for i in range (starting_index_of_function , current_interpreter_history + 1 ):
127
+ reassembled_function += f"{ readline .get_history_item (i )} \n "
128
+
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
+
92
141
class BaseParser (ast .NodeTransformer , metaclass = ABCMeta ):
93
142
_DEBUG_PARSE = False
94
143
@@ -111,7 +160,15 @@ def __init__(self, name, raw_func, func_frame, debug_print=False, add_cache_cont
111
160
self ._func_globals_modified ["csp" ] = csp
112
161
self ._func_globals_modified .update (self ._func_frame .f_globals )
113
162
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
+ elif raw_func .__code__ .co_filename == "<string>" :
166
+ raise OSError ("Could not find function definition for exec'd function." )
167
+ else :
168
+ raw_source = inspect .getsource (raw_func )
169
+
170
+ source = textwrap .dedent (raw_source )
171
+
115
172
body = ast .parse (source )
116
173
self ._funcdef = body .body [0 ]
117
174
self ._type_annotation_normalizer .normalize_type_annotations (self ._funcdef )
0 commit comments