Live Reload your Python Code in Maya

Far too often I’ve seen TDs close Maya and reopen it just to check how the changes they made to the tool they are creating is now running. I’ve done it myself! It’s needed because once imported, python module lives in memory and to refresh the memory you need to close the session and start anew.

But this is a huge waste of time. Python is supposed to be fast to iterate and fast to prototype with. What we basically do by restarting Maya each time is we introduced the “compilation step”!

As Raymond Hettinger would say: there must be a better way!

But firstly…

reload() is not the way to go. What this built-in function does is, it reloads a single file modules. Now, you can scatter your code base with a bunch of reloads, but it’s bad practice and, eventually, you will mess up. And, apart from very simple utility libraries, your tools should not be written as a single python file. Maintainability matters!

Another habit you should say goodbye to is using Maya’s script editor. It’s fine for quick and dirty scripting, but once you are over 20-or-so lines of code, you should switch to an external text editor (of your choice). Not least because Maya tends to crash, and than tends to “forget” whatever you had in your script editor tabs and present you with an empty void of darkness as you realize you’ll now need to rewrite all the code you had already written once and probably forgot good chunks of it by that point.

So how do we do this?

The tools you write need to be proper python packages. There are so many ways to structure your project skeleton and I’m not going to go into that. It also may depend on what you’re actually creating. If you want some examples, there are many on the internet but you can take a look at pyvfx-boilerplate by Fredrik Averpil for inspiration – a nice cross-DCC boilerplate for creating PySide/PyQt applications.

I will mention, though, you should pay attention to your import statements. My personal preference is to use absolute import but explicit relative imports are fine as well. You should not use implicit relative imports!

Reloading your package

In order for python to import your package, it needs to be found in PYTHONPATH. You can keep them in scripts folder in maya’s Documents location or add to MAYA_SCRIPT_PATHmore info here. I’m confident this is well known and documented so I won’t dwell on it.

In Maya you would than import the package and run the code in whichever way your tool gets initiated. For example:

import myawesometool
myawesometool.show()

At this point, you could restart maya and run the same two lines again, OR you could try to do what reload() does but for the whole package.

import unload_packages
unload_packages(silent=False, packages=["myawesometool"])
import myawesometool
myawesometool.show()

There is a function there called unload_packages which I stole from someone long time ago and can’t even remember from whom (probably found it on stack overflow), but here’s the code:

import sys

# if you have some packages that you often reload, you can put them here
# and they will get reloaded if "packages" attribute is not explicitly stated
DEFAULT_RELOAD_PACKAGES = [] 

def unload_packages(silent=True, packages=None):
    if packages is None:
        packages = DEFAULT_RELOAD_PACKAGES

    # construct reload list
    reloadList = []
    for i in sys.modules.keys():
        for package in packages:
            if i.startswith(package):
                reloadList.append(i)

    # unload everything
    for i in reloadList:
        try:
            if sys.modules[i] is not None:
                del(sys.modules[i])
                if not silent:
                    print("Unloaded: %s" % i)
        except:
            print("Failed to unload: %s" % i)

You obviously need to have this function available, so I recommend putting it somewhere it will be easy to reach (maybe among other dev utils and, preferably, available facility-wide).

What does it do

It basically creates the list of modules of the top package and than “unloads” them. Next time you import the package, everything gets “refreshed”. This takes a literal second. Depending how heavy your DCC startup is, this can potentially save you hours each day!

Notes

  • This little function has served me well for years. If you have ideas how to improve it, please get in touch!
  • You should avoid calling this in your production code. This is for development and should be used while developing. If you find yourself leaving calls to this function in production, you’ve done much of the same mistake as scattering reload everywhere.

It’s not a silver bullet!

I also must state this is not going to work in ALL cases. Particularly with old code-bases where, depending on how they are structured, the function may not work as intended… Though I’ve encountered very few such cases myself.