Snowman
Snowman

Reputation: 32061

Import class dynamically in Python

I want to dynamically load a class from a given string. However, I do not know which file the class will be in, so I will have to search all files. I've tried this, but I get AttributeError: 'module' object has no attribute 'MyClass' even though I'm 100% sure that that module (in the current iteration) has that class:

target = 'MyClass'
module_names = [mf[0:-3] for mf in os.listdir('application/models') if mf.endswith(".py")]
modules = [imp.new_module(x) for x in module_names]
for module in modules:
    try:
        target_class = getattr(module, target)
    except ImportError, AttributeError:
        continue

if target_class:
    print 'found class'

It seems I'm getting really close. What I want is not to limit the search to just one folder, but perhaps multiple folders. What's wrong with my code?

Edit: Ok now I'm trying something like this, but still getting the same error:

    for m in module_names:
        try:
            x = reload(__import__(m))
            target_class = getattr(x, target)
        except ImportError, AttributeError:
            continue
        else:
            break

    if target_class:
        print 'found class'

Upvotes: 1

Views: 3166

Answers (3)

Brett Miller
Brett Miller

Reputation: 1

Following the example in the imp documentation:

File in same directory named hello.py:

def myFunction():
        return "Hello World!"

Import hello dynamically (w/o try except finally):

fp, pathname, description = imp.find_module("hello")
hello = imp.load_module("hello", fp, pathname, description)
hello.myFunction() # returns 'Hello World!'

Upvotes: 0

iabdalkader
iabdalkader

Reputation: 17312

According to the documentation new_module returns and empty module:

imp.new_module(name)
Return a new empty module object called name. This object is not inserted in sys.modules.

You may want to look at imp.load_source instead. This is just a simple example:

class Test:
    pass

And

In [19]: m = imp.load_source("test", "./test.py")
In [20]: getattr(m, "Test")
Out[20]: <class test.Test at 0x1fe6120>

Upvotes: 1

mgilson
mgilson

Reputation: 309891

From the documentation on imp.new_module, the returned module is empty. Meaning that it will never contain your class.

Perhaps what you want to do is add your target directory to sys.path and use __import__ to dynamically import those modules, then check for your class?


The following code works for me:

modules = ['foo','bar']
for mod in modules:
    try:
        x = reload(__import__(mod))
    except ImportError:
        print "bargh! import error!"
        continue
    try:
        cls = getattr(x,'qux')
    except AttributeError:
        continue

a = cls()
print a.__class__.__name__

Where foo.py and bar.py are in the same directory:

#foo.py
class foo(object):
    pass

and:

#bar.py
class qux(object):
    pass

Upvotes: 2

Related Questions