1

I'm trying to load functions from a script dynamically when I'm inside an ipython interactive shell. For example, suppose I have a python script like this:

# script.py
import IPython as ip

def Reload():
  execfile('routines.py', {}, globals())

if __name__ == "__main__":
  ip.embed()

Suppose the file routines.py is like this:

# routines.py
def f():
  print 'help me please.'
def g():
  f()

Now if I run the script script.py, I'll be entering the interactive shell. If I type the following, my call to g() works:

execfile('routines.py')
g()

However, if I type the following, the call to g() fails:

Reload()
g()

I will get an error message saying that "global name f is not defined.", although I can still see that f and g are in the output when I type globals() in the interactive shell.

What's the difference of these two?

UPDATE:

The following works, however it's not a preferred solution so I would like to have a better solution for the problem above.

If I change script.py to:

# script.py
import IPython as ip

def Reload():
  execfile('routines.py')

if __name__ == "__main__":
  ip.embed()

And change routines.py to:

# routines.py

global f
global g

def f():
  print 'help me please.'
def g():
  f()

Then if I call Reload() in the interactive shell and then call g(), it works. However this is not a preferred approach because I have to declare global names.

UPDATE 2:

It seems that the problem is independent of ipython. With the first version of routines.py if I start the python shell, and type the following by hand:

def Reload():
  execfile('routines.py', {}, globals())

g()

The call to g() also fails. But the following works:

execfile('routines.py')
g()
shaoyl85
  • 1,854
  • 18
  • 30
  • 1
    Why are you using `execfile`? Just import the module: `from routines import *`. – Bakuriu Dec 04 '14 at 07:47
  • Sorry for some reason I'm not able to do that. I also made a mistake in describing the problem and now I updated it. Thank you! – shaoyl85 Dec 04 '14 at 07:57

1 Answers1

0

As @Bakuriu said, importing is much preferred. Ignoring that, what you want is

def Reload():
    execfile('routines.py', globals())

Lets clarify your example to show why it does not work.

# Setup the namespace to use for execfile
global_dict = {}
local_dict = globals()
execfile('routines.py', global_dict, local_dict)
g() # raises NameError

Since you are passing two different dicts to execfile, the file is executed as if it were in a class definition (from the docs). This means your functions are defined in local_dict but not global_dict.

When you then call g(), it is executed using globals global_dict and a fresh empty local dict. Since neither global_dict or the new locals doesn't contain f we get a name error. By instead calling execfile('routines.py', globals()), we are using global_dict = globals() and local_dict = globals() so f is defined in g's globals.

EDIT:

You noticed that local_dict has both f and g, but global_dict does not in the second example. Defining any variable without explicitly marking it global will always make a local variable, this applies to modules too! It just so happens that normally a module has locals() == globals(); however, we broke this standard by using different local and global dicts. This is what I meant when I said "the file is executed as if it were in a class definition".

kalhartt
  • 3,999
  • 20
  • 25
  • In fact, if I ran the second code segment you put, I can see f and g in local_dict but not in global_dict. Can you explain why? – shaoyl85 Dec 05 '14 at 02:47
  • @shaoyl85 see my edit, hopefully that clears it up. If you have anymore questions feel free to ask. – kalhartt Dec 05 '14 at 07:05