1

I have a number of functions that need to get called from various imported files.

The functions are formated along the lines of this:

a.foo
b.foo2
a.bar.foo4
a.c.d.foo5

and they are passed in to my script as a raw string.

I'm looking for a clean way to run these, with arguments, and get the return values

Right now I have a messy system of splitting the strings then feeding them to the right getattr call but this feels kind of clumsy and is very un-scalable. Is there a way I can just pass the object portion of getattr as a string? Or some other way of doing this?

import a, b, a.bar, a.c.d

if "." in raw_script:
    split_script = raw_script.split(".")
    if 'a' in raw_script:
        if 'a.bar' in raw_script:
            out = getattr(a.bar, split_script[-1])(args)
        if 'a.c.d' in raw_script:
            out = getattr(a.c.d, split_script[-1])(args)
        else:
            out = getattr(a, split_script[-1])(args)
    elif 'b' in raw_script:
        out = getattr(b, split_script[-1])(args)
Indigo
  • 962
  • 1
  • 8
  • 23

2 Answers2

2

It's hard to tell from your question, but it sounds like you have a command line tool you run as my-tool <function> [options]. You could use importlib like this, avoiding most of the getattr calls:

import importlib

def run_function(name, args):
    module, function = name.rsplit('.', 1)
    module = importlib.import_module(module)
    function = getattr(module, function)
    function(*args)

if __name__ == '__main__':
    # Elided: retrieve function name and args from command line
    run_function(name, args)
1

Try this:

def lookup(path):
    obj = globals()
    for element in path.split('.'):
        try:
            obj = obj[element]
        except KeyError:
            obj = getattr(obj, element)
    return obj

Note that this will handle a path starting with ANY global name, not just your a and b imported modules. If there are any possible concerns with untrusted input being provided to the function, you should start with a dict containing the allowed starting points, not the entire globals dict.

jasonharper
  • 9,450
  • 2
  • 18
  • 42