Как вызвать globals () из другой импортированной функции в Python? - PullRequest
1 голос
/ 23 февраля 2020

В настоящее время я разрабатываю автоматизированный тестер функций в Python.

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

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

Эта начальная итерация работает хорошо. Теперь я пытаюсь импортировать функцию и использовать ее из другого файла .py.

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

Исходный файл - главное приложение


from math import floor
import random

#declaring test variables
test_string = 'test_string'
test_float = float(random.random() * 10)
test_int = int(floor(random.random() * 10))

#Currently supported test types (input and return)
supported_types = ['int', 'float', 'str']

autotest_result = {}


def int_ret(number: int) -> str:
    string = "cactusmonster"
    return string

def false_test(number: int) -> str:
    floating = 3.2222
    return floating

def test_typematching():
    for name in list(globals()):
        if not name.startswith('__'):
            try:
                return_type = str((globals()[name].__annotations__)['return'])
                autotest_result.update({name: return_type.replace("<class '", "").replace("'>", "")})
            except:
                continue
    for func in autotest_result:
        if autotest_result[func] != None:
            this_func = globals()[func].__annotations__
            for arg in this_func:
                if arg != 'return':
                    input_type = str(this_func[arg]).replace("<class '", "").replace("'>", "")
                    for available in supported_types:
                        if available == input_type:
                            func_return = globals()[func]("test_" + input_type)
                            func_return = globals()[func]("test_" + input_type)
                            actual_return_type = str(type(func_return)).replace("<class '", "").replace("'>", "")
                            if actual_return_type == autotest_result[func]:
                                autotest_result[func] = 'Passed'
                            else:
                                autotest_result[func] = 'Failed'
    return autotest_result


Тестовый файл - куда я импортирую Функция "test_typematching ()"

from auto_test import test_typematching

print(test_typematching())
def int_ret_newfile(number: int) -> str:
    string="cactusmonster"
    # print(string)
    # return type(number)
    return string

Независимо от того, запускаю ли я свой основной файл "auto_test.py" или файл "tester.py", я все равно получаю следующий вывод: {'int_ret': 'Passed', ' false_test ':' Failed '}

Я предполагаю, что это означает, что даже когда я запускаю функцию из auto_test.py в моем файле tester.py, она все равно просто сканирует себя. Я хотел бы, чтобы он сканировал файл, в котором функция вызывается в данный момент. Например, я ожидаю, что он протестирует функцию int_ret_newfile в tester.py.

Любой совет или помощь будут высоко оценены.

1 Ответ

2 голосов
/ 23 февраля 2020

globals() немного неверно. Получает вызывающий модуль __dict__. (Истинное «глобальное» пространство имен Python на самом деле builtins.)

Как globals() может получить __dict_ своего вызывающего, когда оно определено в модуле builtins? Вот подсказка:

PyObject *
PyEval_GetGlobals(void)
{
    PyThreadState *tstate = _PyThreadState_GET();
    PyFrameObject *current_frame = _PyEval_GetFrame(tstate);
    if (current_frame == NULL) {
        return NULL;
    }

    assert(current_frame->f_globals != NULL);
    return current_frame->f_globals;
}

globals() - это одна из тех встроенных функций, которые реализованы в C (в CPython), но вы получите суть. Он читает глобальные значения фреймов из текущего стекового фрейма, поэтому в Python,

import inspect
inspect.currentframe().f_globals

будет делать то же самое, что и globals(). Но вы не можете просто поместить это в функцию и ожидать, что она будет работать так же, потому что ее вызов добавит кадр стека, и глобальные переменные этого кадра зависят от атрибута .__globals__ функции, который установлен в .__dict__ модуля, который его определил. Вам нужен кадр вызывающего абонента .

def myglobals():
    """Behaves like the builtin globals(), but written in Python!"""
    return inspect.currentframe().f_back.f_globals

Вы можете сделать то же самое в test_typematching. Но ходить по стеку к предыдущему кадру, как это, странная вещь. Это может быть удивительно и хрупко. Это равносильно передаче фрейма вызывающего в качестве неявного скрытого аргумента, что обычно не имеет значения. Подумайте, что произойдет, если вы поместите его в декоратор. Теперь, из какого фрейма стека вы получаете глобальные переменные?

Итак, на самом деле вы должны передавать globals() в качестве явного аргумента test_typematching(), например test_typematching(globals()). Определенный и задокументированный параметр будет намного менее запутанным, чем неявный самоанализ. «Явное лучше, чем неявное».

Тем не менее, стандартная библиотека Python иногда делает подобные вещи, и сам globals() является ярким примером. И exec() может использовать текущее пространство имен, если вы не зададите ему другое. Также super() теперь может работать без аргументов в Python 3. Таким образом, проверка стекового фрейма имеет прецедент для этого вида использования.

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