4

this simple python program that is extracted from more complex codebase:

#insp.py
import inspect
L = lambda x: x+1
print("L(10)=" + str(L(10)))
code = inspect.getsource(L)
print(code)

works if i run it from the command line as:

$ python insp.py

If I copy and paste each line in the python interpreter it fails:

d:\>python
Python 3.5.1 (v3.5.1:37a07cee5969, Dec  6 2015, 01:38:48) [MSC v.1900 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import inspect
>>> L = lambda x: x+1
>>> print("L(10)=" + str(L(10)))
L(10)=11
>>> code = inspect.getsource(L)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "d:\Users\Cimino\AppData\Local\Programs\Python\Python35-32\Lib\inspect.py", line 944, in getsource
    lines, lnum = getsourcelines(object)
  File "d:\Users\Cimino\AppData\Local\Programs\Python\Python35-32\Lib\inspect.py", line 931, in getsourcelines
    lines, lnum = findsource(object)
  File "d:\Users\Cimino\AppData\Local\Programs\Python\Python35-32\Lib\inspect.py", line 762, in findsource
    raise OSError('could not get source code')
OSError: could not get source code

Note that using IPython instead of the plain Python interpreter, it works!

Does anybody know why?

I use Python 3.5 32 bits under Windows7.

Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
Charlie
  • 1,750
  • 15
  • 20
  • I'd guess this is because the inspect module actually goes and looks for a source file which the interpreter tracks. Note the interface can also give you line numbers, comments before the definition, etc. None of these exist when you're in the REPL. I imagine IPython does some magic to ensure inspect works correctly in the interactive case. – pvg Dec 06 '16 at 17:32

1 Answers1

4

It works in IPython because it caches each and every command you enter there using linecache module.

For example:

$ ipy  ## Equivalent to ipython --classic
Python 2.7.10 (default, Jul 30 2016, 18:31:42)
Type "copyright", "credits" or "license" for more information.

IPython 3.0.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.
>>> print a
Traceback (most recent call last):
  File "<ipython-input-1-9d7b17ad5387>", line 1, in <module>
    print a
NameError: name 'a' is not defined

Notice the <ipython-input-1-9d7b17ad5387> part here, this is something specific to IPython. In normal Python shell you would see <stdin>:

$ python
Python 2.7.10 (default, Jul 30 2016, 18:31:42)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.34)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> print a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined

Now let's run your code:

>>> import inspect
>>> L = lambda x: x+1
>>> code = inspect.getsource(L)

Time to find out the filename related to L:

>>> L.func_code.co_filename
'<ipython-input-2-0c0d6f325784>'

Now let's see if we have the source in linecache.cache for this file:

>>> import linecache
>>> linecache.cache[L.func_code.co_filename]
(18, 1481047125.479239, [u'L = lambda x: x+1\n'], '<ipython-input-2-0c0d6f325784>')

So, using this information IPython is able to find the required source but Python shell doesn't because it is not storing any.


The related details about how inspect gets the source can be found in getsourcefile and findsource functions in the source code.

Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
  • For Python 3: https://github.com/python/cpython/blob/master/Lib/inspect.py – pydsigner Jun 26 '19 at 21:15
  • Is there a workaround to this? What if we would like to build ast's interactively from python objects in the interpreter? Seems like a severe limitation! – user32882 Mar 22 '20 at 08:05