Python import from dynamic filename

Viewed 4822

I'm in the following situation: I have a python script main.py, that runs some actions depending on a config file. The config file is a python script/class itself. I want to be able to pass different config files from the command line as parameter and import it in the main script.

Is it even possible in python to load classes dynamically? If so, how can I accomplish that task?

See the following minimal example for clarification.

This is my script main.py:

import argparse

def get_arguments():
    parser = argparse.ArgumentParser(description="foo")
    parser.add_argument("--configfile", required=True)
    return parser.parse_args()

def main():
    args = get_arguments()

    from args.configfile import MyConfig # <-- how is that possible?

    if MyConfig.SETTING1:
        do_something()

if __name__ == '__main__':
    main()

This is an example config.py:

class MyConfig(BaseConfig):
    SETTING1 = True
    SETTING2 = 1 

This is another example config-special.py:

class MyConfig(BaseConfig):
    SETTING1 = False
    SETTING2 = 42 

I'm calling it like this:

$ python3 main.py --configfile config.py
$ python3 main.py --configfile config-special.py
3 Answers

Use import_module to dynamic import modules, detail explain see in the code.

Still use your next command to call them.

$ python3 main.py --configfile config.py
$ python3 main.py --configfile config-special.py

main.py

import argparse
# import sys
from importlib import import_module

def get_arguments():
    parser = argparse.ArgumentParser(description="foo")
    parser.add_argument("--configfile", required=True)
    return parser.parse_args()

def main():
    args = get_arguments()
    module_name = args.configfile[:-3] # here, the result is the file name, e.g. config or config-special

    # Not use __import__, use import_module instead according to @bruno desthuilliers's suggestion
    # __import__(module_name) # here, dynamic load the config module
    # MyConfig = sys.modules[module_name].MyConfig # here, get the MyConfig class
    MyConfig = import_module(module_name).MyConfig

    # from args.configfile import MyConfig # <-- how is that possible?

    if MyConfig.SETTING1:
        #do_something()
        print("do_something")

if __name__ == '__main__':
    main()

I use this:

from importlib import import_module
from importlib.util import find_spec

if not find_spec(modulename):
    print('%s: No such module.' % modulename, file=sys.stderr)
    exit(1)

module = import_module(modulename)

try:
    klass = getattr(module, classname)
except AttributeError:
    print('%s: No such class.' % classname, file=sys.stderr)
    exit(1)

You would also likely want to manipulate sys.path if your file is not in the standard PYTHON_PATH hierarchy.

I went explicit with all the checks (especially find_spec) because if you just import and try to catch an exception, it is very hard to distinguish if you got the exception because your import failed, or because an import inside the file you imported failed.

I would suggest not using python file as configuration. You could use configparser method like this:

from configparser import ConfigParser
parser = ConfigParser()
parser.read(filename)
if parser.has_section(section):
    items = parser.items(section)

config.ini

[MyBaseSettings]
SETTING1 = True
SETTING2 = 1 
Related