Reimport Updated Python Packages Without Restarting the REPL

When working with Python, especially during the debugging or development phase, it’s common to face situations where you make changes to a module but can’t immediately see those changes reflected in your running program. This can be particularly frustrating when working in the Python REPL (Read-Eval-Print Loop). Often, developers think they need to restart the REPL and re-import everything to use the updated functionality. However, there’s a more efficient solution: refreshing your modules dynamically. Let’s explore how this can be done using Python’s importlib library.

Understanding the Problem

Imagine you are writing a Python script and working interactively in the REPL. You create a module, make changes to it, and want to test those changes. By default, once a module is imported, Python caches it. Subsequent import calls won’t reload the updated code unless explicitly instructed.

To illustrate this, let’s use a simple example.

Example Setup:

Create a file named person.py with the following content:

code# person.py
class Person:
def __init__(self, name):
self.name = name

def say_hello(self):
print(f"Hello {self.name}")

Now, launch the Python REPL by typing python in your terminal and run the following commands:

codefrom person import Person

Person(name="Mark").say_hello()

Output:

 codeHello Mark

So far, everything works as expected.

Adding New Functionality

Now, let’s say you modify the Person class to include a new method, say_goodbye. The updated person.py file looks like this:

code# person.py
class Person:
def __init__(self, name):
self.name = name

def say_hello(self):
print(f"Hello {self.name}")

def say_goodbye(self):
print(f"Good Bye {self.name}")

Back in the REPL, you attempt to use the new say_goodbye method:

codefrom person import Person

Person(name="Mark").say_goodbye()

Output:

codeTraceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Person' object has no attribute 'say_goodbye'

Even if you run from person import Person again, the new method still isn’t available. What gives?

Introducing importlib.reload

The Python importlib library allows you to reload modules dynamically. Initially, you might try the following:

codeimport importlib
importlib.reload(person)

However, this leads to an error:

Output:

codeTraceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'person' is not defined. Did you mean: 'Person'?

The issue here is that the person module itself hasn’t been explicitly imported yet.

Correct Way to Reload and Re-import

To fully reload the module and make the updated functionality available, follow these steps:

  1. Import the module.
  2. Reload the module using importlib.reload().
  3. Re-import specific classes or functions.

Here’s how it looks in practice:

codeimport person  # Step 1: Import the module
import importlib
importlib.reload(person) # Step 2: Reload the module
from person import Person # Step 3: Re-import the class or function

Now, when you call the new method, it works as expected:

codePerson(name="Mark").say_goodbye()

Output:

codeGood Bye Mark

Why Does This Work?

When Python imports a module, it stores it in sys.modules, a cache that prevents redundant imports of the same module. The importlib.reload() function clears the module’s entry in this cache and re-executes the module’s code. However, previously imported classes or functions (e.g., Person) are not automatically updated. That’s why you need to explicitly re-import them.

Best Practices for Module Reloading

  1. Avoid Reloading in Production Code: Module reloading is a convenient debugging tool but may introduce subtle bugs if used in production.
  2. Use Reload for Rapid Prototyping: When working interactively in the REPL or Jupyter Notebooks, reloading saves time and eliminates the need to restart the environment.
  3. Consider Structured Development Tools: For larger projects, structured tools like pytest or unittest can be used to test changes without manual reloads.

Wrapping Up

Reloading modules in Python is a powerful feature that can save you significant time during development. By using importlib.reload() effectively, you can dynamically refresh your code without restarting your environment. This technique is especially useful when prototyping or debugging in the REPL or Jupyter Notebook.

Related blog posts