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