Вызов функции из другого файла, в котором имя файла и имя функции считываются из списка. - PullRequest
1 голос
/ 22 октября 2019

У меня есть несколько функций, хранящихся в разных файлах. Имена файлов, и имена функций хранятся в списках. Есть ли возможность вызвать требуемую функцию без условных операторов? Например, file1 имеет функции function11 и function12,

def function11():
    pass
def function12():
    pass

file2 имеет функции function21 и function22

def function21():
    pass
def function22():
    pass

, и у меня есть списки

file_name = ["file1", "file2", "file1"]
function_name = ["function12", "function22", "funciton12"]

Я получу индекс списка из другой функции, основываясь на том, что мне нужно вызвать функцию и получить вывод.

Ответы [ 3 ]

3 голосов
/ 22 октября 2019

Если другая функция выдаст вам индекс списка напрямую, вам не нужно иметь дело с именами функций в виде строк. Вместо этого непосредственно сохраняйте (без вызова) функции в списке:

import file1, file2

functions = [file1.function12, file2.function22, file1.function12]

И затем вызывайте их, как только у вас будет индекс:

function[index]()

Там есть способы сделать то, что называется «отражением» в Python и получить из строки функцию с соответствующим именем. Но они решают проблему, которая более сложна, чем то, что вы описываете, и они более сложны (особенно если вам также приходится работать с именами модулей).


Если у вас есть «белый список» изФункции и модули, которые разрешено вызывать из файла конфигурации, но которые все же необходимо найти их по строке, вы можете явно создать отображение с помощью dict:

allowed_functions = {
    'file1': {
        'function11': file1.function11,
        'function12': file1.function12
    },
    'file2': {
        'function21': file2.function21,
        'function22': file2.function22
    }
}

И затем вызвать функцию:

try:
    func = allowed_functions[module_name][function_name]
except KeyError:
    raise ValueError("this function/module name is not allowed")
else:
    func()

Наиболее продвинутый подход заключается в том, если вам нужно загрузить код из модуля «плагина», созданного автором. Вы можете использовать модуль import, чтобы использовать имя строки, чтобы найти файл для импорта в качестве модуля и импортировать его динамически. Это выглядит примерно так:

from importlib.util import spec_from_file_location, module_from_spec

# Look for the file at the specified path, figure out the module name
# from the base file name, import it and make a module object.
def load_module(path):
    folder, filename = os.path.split(path)
    basename, extension = os.path.splitext(filename)
    spec = spec_from_file_location(basename, path)
    module = module_from_spec(spec)
    spec.loader.exec_module(module)
    assert module.__name__ == basename
    return module

Это все еще небезопасно , в том смысле, что он может искать модуль в файловой системе в любом месте. Лучше, если вы сами укажете папку и разрешите использовать только имя файла в файле конфигурации;но тогда вы все же должны защититься от взлома пути, используя такие вещи, как ".." и "/" в "имени файла".

(у меня есть проект, который делает что-то вродеэто. Он выбирает пути из белого списка, который также находится под контролем пользователя, поэтому я должен предупредить своих пользователей, чтобы они не доверяли файлу белого списка путей друг от друга. Я также ищу в каталогах модули и затем создаю белый список изплагины, которые можно использовать, основанные только на плагинах, которые есть в каталоге - так что никаких забавных игр с "..". И я все еще беспокоюсь, что что-то забыл.)

Однаждыу вас есть имя модуля, вы можете получить из него функцию по имени, например:

dynamic_module = load_module(some_path)
try:
    func = getattr(dynamic_module, function_name)
except AttributeError:
    raise ValueError("function not in module")

В любом случае, нет никаких оснований для eval чего-либо, или для генерации и импорта кода на основе пользовательского ввода. Это наиболее небезопасно из всех.

0 голосов
/ 22 октября 2019

Еще одна альтернатива. Это не намного безопаснее, чем eval().

Кто-то, имеющий доступ к спискам, которые вы читаете из файла конфигурации, может внедрить вредоносный код в импортируемые вами списки.

Т.е.

'from subprocess import call; subprocess.call(["rm", "-rf", "./*" stdout=/dev/null, stderr=/dev/null, shell=True)'

Код:

import re
# You must first create a directory named "test_module"
# You can do this with code if needed. 
# Python recognizes a "module" as a module by the existence of an __init__.py
# It will load that __init__.py at the "import" command, and you can access the methods it imports
m = ["os", "sys", "subprocess"] # Modules to import from
f = ["getcwd", "exit", "call; call('do', '---terrible-things')"] # Methods to import

# Create an __init__.py
with open("./test_module/__init__.py", "w") as FH:
    for count in range(0, len(m), 1):
        # Writes "from module import method" to __init.py 
        line = "from {} import {}\n".format(m[count], f[count])
        # !!!! SANITIZE THE LINE !!!!!
        if not re.match("^from [a-zA-Z0-9._]+ import [a-zA-Z0-9._]+$", line):
            print("The line '{}' is suspicious. Will not be entered into __init__.py!!".format(line))
            continue
        FH.write(line)

import test_module
print(test_module.getcwd())

ВЫХОД:

The line 'from subprocess import call; call('do', '---terrible-things')' is suspicious. Will not be entered into __init__.py!!
/home/rightmire/eclipse-workspace/junkcode
0 голосов
/ 22 октября 2019

Я не уверен на 100%, что понимаю необходимость. Может быть, более подробно в вопросе.

Это то, что вы ищете?

m = ["os"]
f = ["getcwd"]
command = ''.join([m[0], ".", f[0], "()"])
# Put in some minimum sanity checking and sanitization!!!
if ";" in command or <other dangerous string> in command:
        print("The line '{}' is suspicious. Will not run".format(command))
        sys.exit(1)
print("This will error if the method isnt imported...")
print(eval(''.join([m[0], ".", f[0], "()"])) )

ВЫХОД:

This will error if the method isnt imported...
/home/rightmire/eclipse-workspace/junkcode

Как указывает @KarlKnechtel,получение команд из внешнего файла - огромная угроза безопасности!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...