Most Pythonic way to provide function metadata at compile time?

I am creating a very basic platform as a Python 2.7 module. This module has a read-eval-print loop in which user commands entered are mapped to function calls. Since I am trying to simplify the creation of plug-in modules for my platform, function calls will be from my main module to an arbitrary plug-in module. I would like the builder plugin to be able to specify the command that it wants to run, and therefore I was looking for a Python way to remotely input the display in the command-> dict function in the main module from the plugins module.

I looked through a few things:

  • Method name parsing: The main module will import the plug-in module and scan it for method names that correspond to a specific format. For example, he can add the download_file_command (file) method to his dict as a “download file” → download_file_command. However, getting a short, simple command name (say "dl") requires that the function name also be short, which is not suitable for code readability. It also requires the plugin developer to conform to the exact naming format.

  • Cross-module decorators: decorators the plug-in developer called his function what he wants, and just add something like @ Main.register ("dl"), but they definitely require me to change another module namespace and keep the global state in the main module. I understand that this is very bad.

  • Decorators of the same module: using the same logic as above, I could add a decorator that adds the function name to some command name-> function mapping local to the plugin module and get the mapping to the main module using the API Call. This requires that certain methods are always present or inherited, and if my understanding of decorators is correct, the function will be registered only at the first start and without having to re-register every subsequent time thereafter.

, Pythonic , , . name- > function , - .

, - Python; .

+5
3

. , , . :

def a():
  return 1

a.command_name = 'get_one'

:

import inspect #from standard library

import plugin

mapping = {}

for v in plugin.__dict__.itervalues():
    if inspect.isfunction(v) and v.hasattr('command_name'):
        mapping[v.command_name] = v

, .

+4

@ericstalbot, , .

################################################################################
import functools
def register(command_name):
    def wrapped(fn):
        @functools.wraps(fn)
        def wrapped_f(*args, **kwargs):
            return fn(*args, **kwargs)
        wrapped_f.__doc__ += "(command=%s)" % command_name
        wrapped_f.command_name = command_name
        return wrapped_f
    return wrapped
################################################################################
@register('cp')
def copy_all_the_files(*args, **kwargs):
    """Copy many files."""
    print "copy_all_the_files:", args, kwargs
################################################################################

print  "Command Name: ", copy_all_the_files.command_name
print  "Docstring   : ", copy_all_the_files.__doc__

copy_all_the_files("a", "b", keep=True)

:

Command Name:  cp
Docstring   :  Copy many files.(command=cp)
copy_all_the_files: ('a', 'b') {'keep': True}
+4

:

.

, , , :

plugins = some_package.plugin_for_your_app
    another_plugin_module
    # ...

:

plugins = [importlib.import_module(name) for name in config.get("plugins")]

: command name -> function:

commands = {name: func 
            for plugin in plugins
            for name, func in plugin.get_commands().items()}

get_commands() , - , get_commands() .

, some_plugin.py ( ):

def f(a, b):
    return a + b

def get_commands():
    return {"add": f, "multiply": lambda x,y: x*y}

It defines two teams add, multiply.

+1
source

All Articles