Using __import__() for variables - modules from subfolders

Hey there!

I am trying to import the modules from my subfolders. Normally I would just import them at the top of the script, but there are going to be a lot, and they are going to be directly related to buttons on a ui.
The folders/modules are laid out like this:

smiles_toolkit
|__init__.py
|__smiles_tool_kit.py
|
----frequent
    |__init__.py
    |
    ----center_pivot
        |__init__.py
        |center_pivot.py # inside class named MainFunc()
    |
    ----conform
        |__init__.py
        |conform.py # inside class named MainFunc()
    |
    ----set_to_face
        |__init__.py
        |set_to_face.py # inside class named MainFunc()

I am calling the modules with __import__().

section = "frequent"
btn_name = "center_pivot"
btn_module = "smiles_toolkit." + section + "." + btn_name + "." + btn_name
try:
    btn_code = __import__(btn_module, 0)
    btn_main = btn_code.MainFunc()

At this point I get an # AttributeError: 'module' object has no attribute 'MainFunc' #

Would anyone know how to deal with this?

My first instinct is to say:
So what if there’s a lot of them?
Unless you’re planning on doing something more than just a lot of imports (like a plugin system), do your imports at the top like normal. Having lots of imports doesn’t hurt anything.


Now to actually answer the question you asked.
Your code is doing this:

import smiles_toolkit.frequent.center_pivot.center_pivot

So if you had that exact import statement in your code, to instantiate the MainFunc class, you’d have to write this whole line: smiles_toolkit.frequent.center_pivot.center_pivot.MainFunc(), right?
That’s because an import like that only adds smiles_toolkit to the current namespace, and ensures that the rest of the submodules after that are accessible.

What it looks like you want is an import more like this:

from smiles_toolkit.frequent.center_pivot.center_pivot import MainFunc

So you need to use the fromlist kwarg of the __import__ function like this:

btn_code = __import__(btn_module, fromlist=['MainFunc'])
btn_main = btn_code.MainFunc()

Also note, you don’t need the 0 argument for the import.


Here’s an SO thread I found that explains why you have to have MainFunc in the fromlist, as well as calling it in the next line down:

Thank you so much for answering my question, even if it’s not something I should be doing!

I’ve tested it and it works just like you say. I’ve also read that link (thanks for sharing) and it seems like the biggest reason you need to use fromlist is because import only returns 1 item. That’s at least what I could understand of it. The handling of the modules seems pretty complex. It also seems that it doesn’t really matter what you call as your fromlist if I’m not mistaken. But that was a little confusing to read.

When it comes to your first instinct, I’d like to touch back on that if you wouldn’t mind. As you most definitely have far more experience than I!

The reason I was doing this, was because I am creating a Tool Box with Qt, where I am creating a category and it’s buttons from each module that is in the folder structure, and on creation I am importing the module functions and assigning them to the button’s command (at least that’s my intention).

So in the example above:
frequent = Category
center_pivot = Button
conform = Button
set_to_face = Button

There are about 8 categories, and approximately 10 different buttons in each category.
The idea is to be able to grow or remove the tools and categories as artists require tools. So creating the Tool Box dynamically would be beneficial so that you don’t have to make every button in the UI manually. Unsure if I explained that very well, but that was the theory behind it. Perhaps you know a cleaner/better way for doing something like this? With my lack of experience, I feel like manually adding those import statements, buttons/etc and maintaining that would be more of a struggle, than to maintain the folder structure.

Any critique or thoughts you have on it though is greatly appreciated!

When you start dealing with dunder functions (“dunder” == “Double Underscore”) all bets on simple explanations are off… Actually, the more I think about it, a better way to handle this would probably be to use importlib. I mean, it was created for exactly this purpose.

from importlib import import_module

btn_code = import_module(btn_module)
btn_main = btn_code.MainFunc()

So what you’re describing here is (aside from some hair-splitting) a plugin system, and using dynamic imports like this is absolutely a way to deal with it. We use a similar mechanism for our Auto-QC system at work, and I have dynamically built menus in a couple of my tools that do this.

importlib_module still works with python 2.7? I wasn’t sure, I saw it was updated for 3.5 I think? But maya uses Python 2.7. Or at least pre 2021 maya I think haha.

Ahhh okay cool! Yeah I didn’t really know what a plugin system was. But that makes sense. Okay, so this logic that I’m using isn’t completely off the wall then! :smiley: Good to hear, thanks for the help and the explanations!

:smiley:

Each version of python comes with its own standard library, created for that version. So Python 3.5 has a newer importlib included with it than Python 2.7 does… But 2.7 still has it’s own older version included with it.

If you go to the official python docs, up in the upper left corner is a dropdown list where you can choose the version of python to see the docs for.

Absolutely, yeah that makes sense, couldn’t remember if it still had an import_module for python or if it was new for 3.5. I’ll be sure to check the documentation next time!

Thanks again tfox_TD!~~