Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Telling __import__ where to look - am I stuck with sys.path?

Tags:

python

I have a project in pure Python with a rudimentary plugin system: you write a module that defines a class with a specific interface and name, and the program imports the module and subsequently instantiates the class as needed.

Currently, the plugins all come from a specific folder (subdirectory of where the main .py file is located). I would like to be able to have them elsewhere on disk, and instruct the program to look for plugins in a specific place. Can I do this, for one-off dynamic imports, in a cleaner way than modifying sys.path? I don't want to pollute this global.

Related: can I count on sys.path[0] being the path to the script, even if that differs from the current working directory (os.getcwd())?

EDIT: I forgot to mention - I want to be able to get plugins from several different folders, with the user specifying paths to plugin folders. Currently, each of these folders is set up as a package (with an __init__.py); I can trivially scrap this if it causes a problem.

like image 772
Karl Knechtel Avatar asked Aug 28 '11 02:08

Karl Knechtel


People also ask

Where is SYS path located Python?

sys. path is a built-in variable within the sys module. It contains a list of directories that the interpreter will search in for the required module.

How do I undo SYS path append?

To undo the sys. path. append , you just need to remove that line from your script. Since the path is only modified for your current script and not system wide until you edit the PYTHONPATH .

What is import * in Python?

In Python, you use the import keyword to make code in one module available in another. Imports in Python are important for structuring your code effectively. Using imports properly will make you more productive, allowing you to reuse code while keeping your projects maintainable.


1 Answers

This might seem weird, but you can modify a module's __path__ variable and then import from it. Then you're not messing with the global import space in sys.path.

Edit: If the directories are loaded at run time, then you don't need a plugins.py file to store them. You can create the module dynamically:

main.py:

#create the plugins module (pseudo-package)

import sys, os

sys.modules['plugins'] = plugins = type(sys)('plugins')

plugins.__path__ = []
for plugin_dir in ['plugins1', 'plugins2']:
    path = os.path.join(sys.path[0], 'addons', plugin_dir)
    plugins.__path__.append(path)

After creating the dynamic module, you can load the plugins as before, using either import_module or __import__:

from importlib import import_module

myplugins = []
for plugin in ['myplugin1', 'myplugin2']:
    myplugins.append(import_module('plugins.' + plugin))
    myplugins[-1].init()

##or using __import__:

myplugins = []
for plugin in ['myplugin1', 'myplugin2']:
    myplugins.append(getattr(__import__('plugins.' + plugin), plugin))
    myplugins[-1].init()

addons/plugins1/myplugin1.py:

def init():
    print('myplugin1')

addons/plugins2/myplugin2.py:

def init():
    print('myplugin2')

I've never used this, but it does work in both Python 2 & 3.

like image 81
Eryk Sun Avatar answered Sep 28 '22 06:09

Eryk Sun