12

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?

Community
  • 1
  • 1
TC Fox
  • 980
  • 4
  • 13
  • 25
  • Do you want `authenticate()` called on the classes, or on objects of those classes? – robert May 27 '12 at 10:54
  • Sorry, `authenticate()` is called on the classes, not the objects of the classes. Should have made that more clear sorry! – TC Fox May 27 '12 at 12:48

1 Answers1

44

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)]

 

Gareth Latty
  • 86,389
  • 17
  • 178
  • 183