Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: Dynamic "from" import

Tags:

python

So i'm trying to turn a bunch of "from x import x" statements, that look like this:

from class_foo import class_foo

into something dynamic. I'm trying to pass a path to a directory and have it import all of the modules therein.

def dynamicImport(dirPath):
    filez = os.listdir(dirPath)
    for file in filez:
        if "class" in file:
            oname = file[:-3] #cut off the file extension, trivial

            imp_statement = "from " + oname + " import " + oname
            #when I print imp_statement, I can verify it's being concatenated correctly

            exec(imp_statement)

When I run this function and pass it a path, the statement strings are being created properly and it produces no errors, yet then later I'll try to access one of the imported objects, and this happens:

foo = class_foo()

NameError: name 'class_foo' is not defined

Clearly I'm doing something wrong. Any help would be appreciated.

like image 382
learningKnight Avatar asked Jun 19 '12 20:06

learningKnight


People also ask

How do you dynamically import a module using a function?

To load dynamically a module call import(path) as a function with an argument indicating the specifier (aka path) to a module. const module = await import(path) returns a promise that resolves to an object containing the components of the imported module. } = await import(path);

What is __ import __ in Python?

__import__() . This means all semantics of the function are derived from importlib. __import__() . The most important difference between these two functions is that import_module() returns the specified package or module (e.g. pkg. mod ), while __import__() returns the top-level package or module (e.g. pkg ).

What is __ all __ in Python?

In short, __all__ on package level does approximately the same thing as for modules, except it deals with modules within the package (in contrast to specifying names within the module). So __all__ specifies all modules that shall be loaded and imported into the current namespace when us use from package import * .


1 Answers

You're execing your import statement in your function's local namespace, so that's where the names are defined. This namespace goes away when the function ends, leaving you with nothing. What you probably want is something like exec imp_statement in globals().

Why not just use __import__() instead of string-munging? Then you get a reference to your module. You can then fish out the class reference using getattr() on the module object and insert that into globals() (or just pass a dictionary back to the caller, who can then do globals().update() with it).

import sys, os

def getClasses(directory):
    classes = {}
    oldcwd = os.getcwd()
    os.chdir(directory)   # change working directory so we know import will work
    for filename in os.listdir(directory):
        if filename.endswith(".py"):
            modname = filename[:-3]
            classes[modname] = getattr(__import__(modname), modname)
    os.setcwd(oldcwd)
    return classes

globals().update(getClasses(r"C:\plugin_classes"))

Something like that. Or rather than updating globals() with your modules, which could clobber a global variable you care about, just leave the classes in the dictionary and reference them from there:

classes = getClasess(r"C:\plugin_classes")
for clas in classes.itervalues():
    instance = clas(1, 2, 3)       # instantiate
    instance.dosomething_cool(42)  # call method
like image 174
kindall Avatar answered Oct 28 '22 10:10

kindall