Import module error in Maya

I am trying to import a dictionary from a different script into my core script however, I keep getting this error:

# Error: No module named FaceRetargetDictionary
# Traceback (most recent call last):
#   File "<maya console>", line 7, in <module>
# ImportError: No module named FaceRetargetDictionary #

I feel like I’ve tried every possible solution on the internet and I can’t find any other reason why it wouldn’t work. Here are some things I have already tried:

  • check if there are init files in all the folders
  • check if all the names are spelled correctly
  • check if I can run it in PyCharm (this works)
  • move the dictionary to a sub folder and call that instead
  • call ThisProject.FaceRetargetDictionary or any other longer version of the path

This is what the scripts and the folder structure look like:

I assume it’s an issue with Maya considering it works in PyCharm. Does anyone have any other idea?

Which python version are you using? If you’re using python 2, then you should probably have from __future__ import absolute_import at the very top of ALL of your files so this will remain consistent. Plus, you’re gonna move to py3 eventually, and you may as well learn the new way. If you’re in py3, then ignore this.

Now that that’s out of the way, you should always be using dotted imports within modules.
So in this case, you’d use from . import FaceRetargetDictionary as RDict

Why do I think that will work? Because I’m guessing there’s a difference in how you’re running the code in PyCharm vs Maya.
In Maya you’ll be doing something like this in a shelf button or something:
from ThisProject.FaceRetarget import FaceRetarget.
Meaning, you’re importing your code as a module. Vs in PyCharm, where you will run the current file directly rather than importing it. And that makes the difference.

When you’re just running code at the top level (PyCharm style), Python will add the current folder as a place that’s top-level importable, meaning you don’t need dots.
But when you’re importing your code as a module, the py3 absolute import style forces you to make the distinction between top-level importable (like other modules) and dot importable (within the current module)

1 Like

Personally I avoid relative imports, always use the explicit full path:
import root.directory.subdirectory.module_name

But for that to work, mayapy.exe needs to have the directory L:\Pers_Personal\Pers_Scripts\Scripts_Me\ThisProject in sys.path

if you print sys.path in Maya , is it in the path list?

Sorry. I’m gonna be annoying on this one. I very much disagree because I’ve been bit by that before.

Absolute imports work, of course. And they’re easy to use for a personal project. But they come at the cost of versatility. As soon as you want to rename root, or move it to a new place, or set up versioned folders, or vendor your code, you’re stuck renaming all of your import paths. (These are all things I’ve done when ingesting other people’s code)

If you’re planning on distributing your code, please use relative imports.

3 Likes

Hi, thank you so much for the quick response! I am using Python 2 in Maya because it needs to be compatible with some of our older tools. I tried to add absolute_import to my script as well as use a dotted import.

See below how I’m calling the script in Maya and the error it still gives. I printed sys.path and the directory is indeed not in there. I’ve tried to mess with the maya.env file and append this path but that doesn’t work.

The "Attempted relative import in non-package" error occurs when attempting to use a relative import within a script that is not part of a package. To fix this, you should make sure that the script is part of a package by creating an empty __init__.py file in the directory containing the script.

First, the folder you’re adding to sys.path should be L:\Pers_Personal\Pers_Scripts\Scripts_Me. If you think about it, you’re going to want to have multiple modules in your Scripts_Me folder. Maybe ThisProject ThatProject and AnotherProject. So you want to add the folder where all your projects are going to live instead of adding each project individually. That way you can access any of them.
With only that change, the import will be from ThisProject.FaceRetarget import FaceRetarget

Also, you don’t need to do the saved_path = sys.path thing. In that case, you’re just aliasing sys.path, and it’s just an extra step that does absolutely nothing behind the scenes, and adds confusion. Just check if current_folder not in sys.path

Also, I see your folder path is using backslashes, but you’re not using a rawstring. You should definitely be doing that. Note the “r” in front of the quote: current_folder = r"L:\Pers_Personal\Pers_Scripts\Scripts_Me"

If you’re dealing with reload, then my personal preference is to only deal with the top level module. Meaning, just import ThisProject. Then your reload is reload(ThisProject). And after that line, you can do FaceRetarget = ThisProject.FaceRetarget.FaceRetarget. This ensures that FaceRetarget is always from the currently imported version of ThisProject, and any calls to objects defined outside of your FaceRetarget.py file, but still in ThisProject will ALSO get reloaded at the same time.
IMO, It’s a good pattern to save you a bunch of hassle down the road.

Finally, you should be using newstyle classes by inheriting from object: class FaceRetarget(object):
I’m sure it won’t actually change anything for you in this project. But it’s a good habit to get into. Otherwise, when it actually does matter it’s just a pain to go back and fix.

import sys
import os

current_folder = r"L:\Pers_Personal\Pers_Scripts\Scripts_Me"

if current_folder not in sys.path:
    sys.path.append(current_folder)

import ThisProject
reload(ThisProject)

FaceRetarget = ThisProject.FaceRetarget.FaceRetarget
FaceRetarget().run_the_script()
2 Likes