IPython supports several ways to interactively run Python code. In today's lesson, you'll learn how to execute scripts from within the IPython REPL using the %run
magic function. You'll also learn how to pass arguments to scripts that you execute with %run
and how %run
handles namespaces.
Learning Outcomes
By the end of today, you'll know:
- What the
%run
magic command is and how to use it - How to share your IPython namespace with a script using the
-i
flag - How namespace conflicts are resolved when using
%run
Pre-requisites
Before you start today's lessons, make sure you have a working Python installation on your computer (preferably Python 3.8 or greater). You should also have IPython installed and have worked through the Day 4 lesson:

Running Files With %run
The %run
magic is used to run a Python file from within the IPython REPL. This is useful for loading code into an interactive session and then using objects from the file interactively.
For instance, say you have a file greet.py
with a function greet()
defined inside of it:
# greet.py
def greet(name):
print(f"Hello, {name}")
Open a new IPython session and run %whos
to verify that you have an empty namespace:
In [1]: %whos
Interactive namespace is empty.
Now type %run greet.py
to execute greet.py
and load the greet()
function into your IPython namespace:
In [2]: %run greet.py
In [3]: %whos
Variable Type Data/Info
--------------------------------
greet function <function greet at 0x103346d40>
Now that greet()
is in your namespace, you can interact with it:
In [4]: greet("David")
Hello, David
So, why load a file this way instead of just importing it and using the contents? %run
a great way to test executing a script while also getting all of the objects defined within it into your namespace. You'll be able to test execution and also inspect the state of the program when it is done running.
It's true that when you import a module in IPython, or in any Python program for that matter, the module gets executed. However, any code protected by an if __name__ == "__main__"
clause won't get executed during import but will get executed with %run
. Using %run
is roughly equivalent to executing a Python script using the python -i
command.
To see this in action, change the greet.py
file to include a protected call to greet()
:
# greet.py
def greet(name):
print(f"Hello, {name}")
if __name__ == "__main__":
greet("David")
Now when you %run
the file, you'll see "Hello, David"
printed to the console:
In [5]: %run greet.py
Hello, David
By default, the %run
magic runs Python scripts in an empty namespace, so even if you already have a greet()
so referencing names defined in your IPython namespace in the script will result in a NameError
.
For example, suppose you change the greet.py
script to call greet()
with an argument called my_name
:
# greet.py
def greet(name):
print(f"Hello, {name}")
if __name__ == "__main__":
greet(my_name)
When you %run greet.py
, you'll get a NameError
:
In [6]: %run greet.py
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
File ~/greet.py:6, in <module>
2 print(f"Hello, {name}")
5 if __name__ == "__main__":
----> 6 greet(my_name)
NameError: name 'my_name' is not defined
However, creating a variable called my_name
in IPython and then running the script using the -i
flag will execute the file using the IPython namespace:
In [7]: my_name = "David"
In [8]: %run -i greet.py
Hello, David
Sharing your IPython namespace with a script might seem like a strange thing to do, but it opens up a powerful way to build up a script from within an interactive session.
You can start by experimenting with code in IPython and then incrementally move snippets of code to your script as they become more solidified. When you are done, you can run the script without the -i
flag to check that everything works from a blank namespace.
Passing Arguments To a Script With %run
When you write scripts that automate a process, it's common to accept command-line arguments. You can pass these arguments to the script when you execute it with %run
similar to how you'd pass arguments to the script from the command line using the python
command.
For instance, suppose the greet.py
scripts uses sys.argv
to accept an argument for the name to greet:
# greet.py
def greet(name):
print(f"Hello, {name}")
if __name__ == "__main__":
import sys
args = sys.argv
name = args[1] # args[0] is the filename!
greet(name)
To pass an argument to greet.py
with %run
, all you need to do is include it after the script name:
In [9]: %run greet.py David
Hello, David
Since the namespace used to run the file gets dumped into the IPython namespace after execution, you can even see the value of the args
and name
variables in the script from within IPython after running the program:
In [10]: whos
Variable Type Data/Info
--------------------------------
args list n=2
greet function <function greet at 0x1080f3d00>
my_name str David
name str David
sys module <module 'sys' (built-in)>
Notice that the sys
module is also available to you, so you can now use sys
in IPython as if you'd explicitly imported it:
In [11]: sys.platform
Out[11]: 'darwin'
In [12]: sys.version
Out[12]: '3.10.2 (main, Feb 2 2022, 05:51:25) [Clang 13.0.0 (clang-1300.0.29.3)]'
You can pass arguments to any Python script that accepts them, even if they are parsed by argparse
instead of being captured by sys.argv
. For instance, rewrite the greet.py
script to use argparse
, as follows:
# greet.py
def greet(name):
print(f"Hello, {name}")
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Greet someone by name")
parser.add_argument("name", type=str, help="Name of the person to greet")
args = parser.parse_args()
greet(args.name)
Passing the argument to greet.py
from %run
works as expected:
In [13]: %run greet.py David
Hello, David
You can even pass arguments like --help
to the script to see the help page generated by argparse
:
In [14]: %run greet.py --help
usage: greet.py [-h] name
Greet someone by name
positional arguments:
name Name of the person to greet
options:
-h, --help show this help message and exit
Namespaces And The %run
Command
By default, scripts executed with %run
are executed inside a blank namespace, just like running a script with the python
command from the command line. As you saw previously, you can share your current IPython namespace with a script by running it with %run -i
.
In both cases, the IPython namespace gets updated with the state of the script's namespace when it is done executing. But what if there are namespace conflicts? There are two situations where conflicts could occur:
- You execute a script with
%run
and the script assigns a value to a name that currently exists in your IPython namespace. - You execute a script with
%run -i
and the script overwrites a name that is currently in your namespace.
In the first case, there is no conflict to deal with when the script is running, since %run
executes the script in a blank namespace. The issue is what happens when the script finishes running and the script's namespace gets dumped into the IPython namespace.
In both cases, though, the question is: what is the value of the original name in the IPython namespace after the script runs?
Let's go back to the greet.py
example and see what happens. Re-write greet.py
to declare a my_name
variable that gets passed to greet()
:
# greet.py
def greet(name):
print(f"Hello, {name}")
if __name__ == "__main__":
my_name = "David"
greet(my_name)
Now, restart your IPython session so that you have a blank IPython namespace. Confirm this with the %whos
command:
In [1]: %whos
Interactive namespace is empty.
Now create a variable called my_name
and assign to it the string "Amos"
. Then run the script and inspect the IPython namespace again to see how it's changed:
In [2]: my_name = "Amos"
In [3]: %run greet.py
Hello, David
In [4]: %whos
Variable Type Data/Info
--------------------------------
greet function <function greet at 0x10733e830>
my_name str David
my_name
has now taken on the value that it was set to inside of greet.py
. If you're familiar with how namespace updates work in Python, then this result may not surprise you. However, it's important to keep this in mind when working with files interactively.
Calling %run
with -i
does not change this behavior. For instance, setting my_name
back to "Amos"
and running greet.py
with %run -i
results in the same namespace after the script executes:
In [5]: my_name = "Amos"
In [6]: %run -i greet.py
Hello, David
In [7]: %whos
Variable Type Data/Info
--------------------------------
greet function <function greet at 0x10733fe20>
my_name str David
The key takeaway is: neither Python nor IPython notifies you of namespace conflicts. A name always takes on the last value assigned to it, and in the case of a conflict — such as when the contents of a script's namespace are dumped into the IPython namespace — the value from the script takes precedence.
Day 5 Activity
Continue to use IPython as your Python REPL. Rather than closing IPython or opening a new terminal tab or window to run scripts, practice running them from within IPython and inspecting the namespace once the script executes.
Take some notes while you do this:
- How does running scripts with
%run
affect your productivity? Do you feel more or less productive? - What advantages have you found by running scripts with
%run
? - What pain points have you encountered while using
%run
? Does switching between IPython and your editor to edit scripts between runs slow you down at all?
Thanks for sticking around for Day 5 of Learn IPython in 10 days. See you tomorrow for Day 6!
Enjoying this course? Why not share it with a friend or colleague that you think will benefit from it? Just right-click on this link, copy the URL, and send it to them in an email!