How do I run global code without calling the file

Refresh

December 2018

Views

95 time

3

I'm writing a python application that allows users to write their own plugins and extend the core functionality I provide -

$ tree project_dir/
.
├── application.py
├── plugins
│   ├── __init__.py
│   ├── example_plugin.py
│   ├── plugin1.py
│   ├── plugin2.py
│   └── plugin3
│       ├── sounds
│       │   └── test.wav
│       └── player.py
└── plugins_manager.py

plugins_manager.py -

class Manager(object):

    def __init__(self):
        self.plugins = {}

    def register_plugin(self, name, plugin_func):
        self.plugins[name] = plugin_func

application.py initializes Manager instance globally -

manager = Manager()

def main():
    print manager.plugins

main()

Each plugin is required to import the Manager instance from application.py and register itself like, plugin1.py -

from application import manager

PLUGIN_NAME = "AAC_Player"

def plugin_function(options):
    # do something

manager.register_plugin(PLUGIN_NAME, plugin_function)

Now when I run application.py, obviously nothing gets printed. How do I make the plugins register themselves (call .register_plugin()) at program startup?

So on that lines, a more generalised question would be - How can I make python execute a line of code that's global in a file without actually running the file?

Suggestions on improving the plugin architecture welcome!

2 answers

2

Вы можете использовать __import__()встроенную команду для импорта плагинов, а затем включить register_plugin()вызов в любом файле плагина example_plugin.pyили в __init__.pyесли это каталог.

Например, предположим, что это ваша структура проекта:

./
application.py
plugins_manager.py
plugins/
    __init__.py
    plugin1.py
    plugin2.py
    plugin3/
        __init__.py

Плагины имеют такое содержание:

$ cat plugins/plugin1.py
print 'Plugin 1'

$ cat plugins/plugin2.py
print 'Plugin 2'

$ cat plugins/plugin3/__init__.py
print 'Plugin 3'

В plugins_manager.py, определить модули и импортировать их в:

from os import listdir
from os.path import exists, isdir, basename, join, splitext

def is_plugin(filename):
  filepath = join('plugins', filename)
  _, ext = splitext(filepath)

  # Ignore plugins/__init__.py
  if filename == '__init__.py':
    return False

  # Find single file plugins
  if ext == '.py':
    return True

  # Find plugins packaged in directories
  if isdir(filepath) and exists(join(filepath, '__init__.py')):
    return True

  return False

plugin_names = [ splitext(p)[0] for p in listdir('plugins/') if is_plugin(p) ]
plugins = [ __import__('plugins.' + p) for p in plugin_names ]

Если получить результаты, аналогичные:

Plugin 1
Plugin 2
Plugin 3

Обратите внимание , что в данном случае pluginsпеременная содержит список объектов модуля импортируемых.

0

Строго говоря, я бы сказал, что нет никакого способа запуска кода без его вызова какого-то образом. Чтобы сделать это, запущенная программа может использовать

import importlib

так что, как только вы нашли файл, который вы можете импортировать его с:

mod = importlib.import_module(import_name, pkg_name)

и если этот файл содержит известную функцию (Run в данном случае), вы можете назвать его:

mod.Run(your_args)

Это работает для Python 2.7. Версия 3 может отличаться.