10
votes

Possible Duplicate:
Dynamic module import in Python

Probably a simple question! I need to iterate through a list of classes (as strings) passed from a settings file. The classes are listed like below:

TWO_FACTOR_BACKENDS = (
    'id.backends.AllowToBeDisabled', # Disable this to enforce Two Factor Authentication
    'id.backends.TOTPBackend',
    'id.backends.HOTPBackend',
    #'id.backends.YubikeyBackend',
    #'id.backends.OneTimePadBackend',
    #'id.backends.EmailBackend',
)

I now need to call the authenticate() function on each of these classes (unless commented out, of course). I'm happily iterating through the list, I just need to know how to convert the strings to a Class object in my foreach loop so that I can call the authenticate method on it. Is there an easy way to do this?

1
Do you want authenticate() called on the classes, or on objects of those classes? - robert
Sorry, authenticate() is called on the classes, not the objects of the classes. Should have made that more clear sorry! - TC Fox

1 Answers

42
votes

You want to use the importlib module to handle loading of modules like this, then simply use getattr() to get the classes.

For example, say I have a module, somemodule.py which contains the class Test:

import importlib

cls = "somemodule.Test"
module_name, class_name = cls.split(".")

somemodule = importlib.import_module(module_name)

print(getattr(somemodule, class_name))

Gives me:

<class 'somemodule.Test'>

It's trivial to add in things like packages:

cls = "test.somemodule.Test"
module_name, class_name = cls.rsplit(".", 1)

somemodule = importlib.import_module(module_name)

And it will not import a module/package if it's already been imported, so you can happily do this without keeping track of loading modules:

import importlib

TWO_FACTOR_BACKENDS = (
    'id.backends.AllowToBeDisabled', # Disable this to enforce Two Factor Authentication
    'id.backends.TOTPBackend',
    'id.backends.HOTPBackend',
    #'id.backends.YubikeyBackend',
    #'id.backends.OneTimePadBackend',
    #'id.backends.EmailBackend',
)

backends = [getattr(importlib.import_module(mod), cls) for (mod, cls) in (backend.rsplit(".", 1) for backend in TWO_FACTOR_BACKENDS)]