Dynamically Loading Python Classes
The design for our fault injection framework has required the writing of a suite class which loads in other defined case classes located in their own directory. This means we need to fill a list in the suite class with case classes defined somewhere else. After doing some research we found a few methods of performing this, including using imp and import. We ended up approaching the problem by appending the directory containing the classes to the Python path using sys.path.append(). Then we could import each case by removing the ".py" part of the filename and feeding them into import(). After importing the class as a module we could use the getattr() function to retrieve its constructor and create a new instance. However we hit a problem here; in order to use getattr() function you must know the name of the attribute you want to remove. We then made the decision that all case classes in our framework must contain the string "Case" in their name. By doing that we could use the dir() function to get a list of all properties the module had, take the first one containing "Case" in it, and feed it into getattr to get the class definition. After getting the definition for the class we could then instantiate our own and add it to a list of cases. The code to perform this functionality ended up looking like this:
# Create path to classes and get list of filenames in that directory
suite_to_import = "suites/" + suite_name
cases = os.listdir(suite_to_import)
# Append folder to path
sys.path.append(suite_to_import)
# And import every file
for case in cases:
# Safety to make sure we don't "index()" a string not containing a "."
if "." not in case:
print "Not a valid python file for case: " + case
sys.exit(1)
# Remove the ".py" section
case = case[:case.index(".")]
# Import the file
case = __import__(case)
# Find the class name
for name in dir(case):
if "Case" in name:
attr = name
# Get the class object
case_class = getattr(case, attr)
# Create a new instance of it and store
case = case_class()
self.cases.append(case)