When you execute a python file with execfile()
you are executing it in the current namespace. The REPL namespace is a built-in module:
>>> import sys
>>> sys.modules['__main__']
<module '__main__' (built-in)>
which means there is no sourcefile for inspect.getsource()
to retrieve:
>>> sys.modules['__main__'].__file__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute '__file__'
>>> import inspect
>>> inspect.getfile(sys.modules['__main__'])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/Cellar/python/2.7.5/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 403, in getfile
raise TypeError('{!r} is a built-in module'.format(object))
TypeError: <module '__main__' (built-in)> is a built-in module
Your next problem is that because you use execfile
the module set for your code is always going to be wrong. inspect.getsource()
cannot determine where you defined the code, as execfile()
bypasses the normal import mechanisms:
$ cat test.py
execfile('classes.py')
$ python test.py
Traceback (most recent call last):
File "test.py", line 1, in <module>
execfile('classes.py')
File "classes.py", line 13, in <module>
class ParentModel(object):
File "classes.py", line 9, in __init__
setattr(cls, "source", inspect.getsource(cls))
File "/usr/local/Cellar/python/2.7.5/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 701, in getsource
lines, lnum = getsourcelines(object)
File "/usr/local/Cellar/python/2.7.5/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 690, in getsourcelines
lines, lnum = findsource(object)
File "/usr/local/Cellar/python/2.7.5/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 564, in findsource
raise IOError('could not find class definition')
IOError: could not find class definition
At no point does your code work with execfile('classes.py', globals())
unless you run it directly from a file that has the same source code. On some platforms it may appear to work because execfile
ends up triggering code that sets the __file__
attribute of the current module.
The only way you actually make this work is to
- Create a fake module object in
sys.modules
and give it a__file__
attribute that points toclasses.py
. - Pass in a namespace to
execfile()
that sets__name__
to match the fake module object.
Demo:
>>> import sys
>>> import types
>>> sys.modules['fake_classes'] = types.ModuleType('fake_classes')
>>> sys.modules['fake_classes'].__file__='classes.py'
>>> ns = {'__name__': 'fake_classes'}
>>> execfile('classes.py', ns)
>>> >>> ns.keys()
['__module__', 'ChildA', '__builtins__', 'inspect', '__package__', 'met', 'ChildB', 'ChildC', 'ParentModel', '__name__', 'test_str']
Just to make it explicit, creating a copy of globals()
only prevents execfile()
from modifying your current module namespace (or your REPL namespace). There is otherwise no difference between the dictionaries being passed to execfile()
.