Return the entire source file and starting line number for an object. The argument may be a module, class, method, function, traceback, frame, or code object. The source code is returned as a list of all the lines in the file and the line number indexes a line in that list. An IOError
(object)
| 158 | # test case (IPython.core.tests.test_ultratb.ChangedPyFileTest) that fails if |
| 159 | # the monkeypatch is not applied. TK, Aug 2012. |
| 160 | def findsource(object): |
| 161 | """Return the entire source file and starting line number for an object. |
| 162 | |
| 163 | The argument may be a module, class, method, function, traceback, frame, |
| 164 | or code object. The source code is returned as a list of all the lines |
| 165 | in the file and the line number indexes a line in that list. An IOError |
| 166 | is raised if the source code cannot be retrieved. |
| 167 | |
| 168 | FIXED version with which we monkeypatch the stdlib to work around a bug.""" |
| 169 | |
| 170 | file = getsourcefile(object) or getfile(object) |
| 171 | # If the object is a frame, then trying to get the globals dict from its |
| 172 | # module won't work. Instead, the frame object itself has the globals |
| 173 | # dictionary. |
| 174 | globals_dict = None |
| 175 | if inspect.isframe(object): |
| 176 | # XXX: can this ever be false? |
| 177 | globals_dict = object.f_globals |
| 178 | else: |
| 179 | module = getmodule(object, file) |
| 180 | if module: |
| 181 | globals_dict = module.__dict__ |
| 182 | lines = linecache.getlines(file, globals_dict) |
| 183 | if not lines: |
| 184 | raise IOError('could not get source code') |
| 185 | |
| 186 | if ismodule(object): |
| 187 | return lines, 0 |
| 188 | |
| 189 | if isclass(object): |
| 190 | name = object.__name__ |
| 191 | pat = re.compile(r'^(\s*)class\s*' + name + r'\b') |
| 192 | # make some effort to find the best matching class definition: |
| 193 | # use the one with the least indentation, which is the one |
| 194 | # that's most probably not inside a function definition. |
| 195 | candidates = [] |
| 196 | for i, line in enumerate(lines): |
| 197 | match = pat.match(line) |
| 198 | if match: |
| 199 | # if it's at toplevel, it's already the best one |
| 200 | if line[0] == 'c': |
| 201 | return lines, i |
| 202 | # else add whitespace to candidate list |
| 203 | candidates.append((match.group(1), i)) |
| 204 | if candidates: |
| 205 | # this will sort by whitespace, and by line number, |
| 206 | # less whitespace first |
| 207 | candidates.sort() |
| 208 | return lines, candidates[0][1] |
| 209 | else: |
| 210 | raise IOError('could not find class definition') |
| 211 | |
| 212 | if ismethod(object): |
| 213 | object = object.__func__ |
| 214 | if isfunction(object): |
| 215 | object = object.__code__ |
| 216 | if istraceback(object): |
| 217 | object = object.tb_frame |