简体   繁体   中英

How do I run global code without calling the file

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!

You can use the __import__() builtin to import the plugins, and then include the register_plugin() call in either the plugin file example_plugin.py or in __init__.py if it's a directory.

For example, let's say this is your project structure:

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

Plugins have these contents:

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

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

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

In plugins_manager.py , identify the plugins and import them in:

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 ]

Should get output similar to:

Plugin 1
Plugin 2
Plugin 3

Note that in this case the plugins variable contains a list of the module objects imported.

Strictly speaking I'd say there is no way to run code without it being invoked somehow. To do this, the running program can use

import importlib

so that once you've found the file you can import it with:

mod = importlib.import_module(import_name, pkg_name)

and if that file provides a known function (Run in this case) you can call it with:

mod.Run(your_args)

This works for Python 2.7. Version 3 might be different.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM