I'm trying to find an IPython counterpart to Spyder's runfile. According to this page, "exec() will execute the input code in the current scope" by default. Therefore, I expect the following to create objects TestFunc and Doggy in the current scope:
# Script.py
#----------
def TestFunc():
printf("I am TestFunc.")
Doggy = "Doggy"
To "source" the code from the IPython REPL, I found the following function from this tutorial, which I paste into the REPL:
def execute_python_file_with_exec(file_path):
try:
with open(file_path, 'r') as file:
code = file.read()
exec(code)
except FileNotFoundError:
print(f"Error: The file '{file_path}' does not exist.")
except Exception as e:
print(f"An error occurred: {e}")
I then use it to run Script.py and query the local and global namespace:
execute_python_file_with_exec('Script.py')
print("Locals:")
for item in dir():
print( item, end=", " )
print("Globals:")
for item in globals():
print( item, end=", " )
Neither of the namespaces contain TestFunc or Doggy.
Locals:
In, Out, _, _2, _5, _6, __, ___, __builtin__,
__builtins__, __doc__, __loader__, __name__,
__package__, __spec__, _dh, _i, _i1, _i2, _i3,
_i4, _i5, _i6, _i7, _i8, _ih, _ii, _iii, _oh,
execute_python_file_with_exec, exit,
get_ipython, item, open,
Globals:
__name__, __doc__, __package__, __loader__,
__spec__, __builtin__, __builtins__, _ih, _oh,
_dh, In, Out, get_ipython, exit, quit, open,
_, __, ___, _i, _ii, _iii, _i1,
execute_python_file_with_exec, _i2, _2, _i3,
_i4, _i5, _5, _i6, _6, _i7, item, _i8, _i9, In
[10]:
What am I misunderstanding about exec()?
I am using IPython version 8.15.0 from Anaconda. The %run command only works from the IPython prompt, but I'm also trying to replace the use of runfile within scripts. If I invoke a script using %run from the IPython prompt, and the script also contains %run, it is flagged as an error.
I also ruled out import, subprocess, and os.system(), but that is starting to drift from the topic of my question. For those interested, I describe the problems with those commands here.
Ideally, there would be an alternative to runfile that executes statements in a source file, but does so in the local scope of code (or REPL) from which runfile was issued. Furthermore, the alternative doesn't require a lot of busy code (like runfile). I realize that I'm wishing for the moon -- hoping that it exists, but prepared for the likelihood that it does not.
I considered @jasonharper's approach of explicity supplying "locals" and "globals" dictionaries as arguments to execute_python_file_with_exec, which then passes them to exec(). Unlike globals(), however, locals() only returns a copy of the local variables. Consequently, script Script.py will not be able to add objects to that scope. In fact, this SO answer confirms @jasonharper's explanation that local variables are determined at compile time, and therefore cannot be added to.
execwas insideexecute_python_file_with_exec; if you ranprint(locals())inside that function you'd see they were indeed added (alongsidecode,fileandfile_path).magic,%run? ipython.readthedocs.io/en/stable/interactive/…%runseems to be literally it, and evenfrom .. import *may meet your needs (although you will have issues doing that more than once)ns = {"__name__":"__main__"}; exec(code, ns, ns); globals().update(ns){'x': 1, 'y': 2}for that example. If that's running 3.13, could be related to docs.python.org/3/whatsnew/….