1

I have a function that takes in an external path that holds some python files and discovers any classes that exist within those files. It returns a map of the class name and the class itself so that I can create an object from it, if I want. The following code below works how I intended it to, but I am trying to do it without appending the sys.path.

def find_plugins(path):
    sys.path.append(path)   
    plugin_map = {}
    current_module_name = os.path.splitext(os.path.basename(path))[0]
    for file in glob.glob(path + '/*.py'):
        name = os.path.splitext(os.path.basename(file))[0]

        if name.startswith('__'):
            continue

        module = importlib.import_module(name, package=current_module_name)
        for member in dir(module):
            plugin_class = getattr(module, member)
            if plugin_class and inspect.isclass(plugin_class):
                plugin_map[member] = plugin_class
    return plugin_map

If I remove the sys.path line, I get the following stack trace:

ModuleNotFoundError: No module named 'plugin'

where 'plugin' is the plugin.py file in the path provided to the function. Is it even possible to do this without appending the sys.path?

Snuh
  • 310
  • 2
  • 10

1 Answers1

1

You could do it using the technique shown in the accepted answer to the question
How to import a module given the full path which doesn't require manipulating sys.path.

import importlib.util
import inspect
from pathlib import Path


def find_plugins(path):
    plugins = {}
    for module_path in Path(path).glob('*.py'):
        module_name = module_path.stem
        if not module_name.startswith('__'):
            # Load module.
            spec = importlib.util.spec_from_file_location(module_name, module_path)
            module = importlib.util.module_from_spec(spec)
            spec.loader.exec_module(module)  # Execute module in its own namespace.
            # Extract classes defined in module.
            for member in dir(module):
                plugin_class = getattr(module, member)
                if plugin_class and inspect.isclass(plugin_class):
                    plugins[member] = plugin_class

    return plugins


if __name__ == '__main__':
    from pprint import pprint

    plugins_folder = '/path/to/plugins'
    plugins = find_plugins(plugins_folder)

    pprint(plugins)

martineau
  • 119,623
  • 25
  • 170
  • 301