RuntimeError: Работа вне контекста приложения. с app.app_context () не решает проблему - PullRequest
1 голос
/ 14 февраля 2020

Я пытаюсь запустить l oop, требующий много времени, с помощью пула процессов, работающих на нем с использованием pool.map. L oop является частью функции представления, которую я поместил в новую функцию, чтобы я мог передать ее в pool.map. Но это выдает ошибку -

RuntimeError: Работа вне контекста приложения.

Как правило, это означает, что вы пытались использовать функциональность, необходимую для взаимодействия с текущим объектом приложения. Чтобы решить эту проблему, настройте контекст приложения с помощью app.app_context (). См. Документацию для получения дополнительной информации.

Я поместил вызов метода в with app.app_context() (как упоминалось здесь ). Но ошибка не исчезла. Пожалуйста, скажите мне, как я могу решить эту проблему.

@app.route('/some_url', methods= ['POST'])
def view_function ():
    start_time = time.time()
    data = request.get_json()
    a = round(data.get('a', '') * data.get('a', ''))
    b = round(data.get('b', '') * data.get('b', ''))
    c = round(data.get('c', '') * data.get('c', ''))
    d = round(data.get('d', '') * data.get('d', ''))

    a_id = select.get_id(data.get('property', ''), session['somedata'][1])
    some_list, a_ids, loc = AnotherClassInDifferentDir.get_list(a_id, session['somedata'][1])
    value = select.get_value(//some arguments)

Здесь я использую многопроцессорность, и где я использую with app.app_context(): (это часть той же функции, view_function) -

    with app.app_context():
        e = data.get('e', '')
        stuff = session['somedata'][1]
        pool = Pool(processes = 2)
        func = partial(loopTask,e, a_id, a_ids, a, b, c, d, loc, stuff)
        stuff_array = [(index, item) for index, item in enumerate(some_list)]
        print("stuff_array =", stuff_array)
        pool.map(func, stuff_array)
        pool.close()
        pool.join()

    print("--- %s seconds ---" % (time.time() - start_time))
    return ''

def loopTask(e, a_ids, a, b, c, d, loc, stuff, stuff_item):

    index, s = stuff_item
    c_id = document_ids[index]
    done = AnotherClassInDifferentDir.work(s)
    f = AnotherClassInDifferentDir.more_work(done, a, b, c, d, loc)
    if f != '':
        update.update_work(//some arguments)
        g.cnxn.commit()
        if (moreDB.check(//some arguments) ==0):
            update.work(//some arguments)
            g.cnxn.commit()
    else:
        pass

Я полагаю, что g.cnxn.commit() является причиной этой проблемы, поскольку она раскрывается контекстом приложения, но я не уверен. Пожалуйста, помогите!

РЕДАКТИРОВАТЬ

g.cnxn устанавливается другим способом с помощью декоратора @app.before_request

@app.before_request
def connect_to_database():
    if request.endpoint != 'static':
        if not hasattr(g,'cnxn'):
            g.cnxn = pyodbc.connect('DRIVER={{Some Driver}};config1={};config2={};Trusted_connection=yes'.format(app.config['config1'],app.config['config2']))  

1 Ответ

1 голос
/ 14 февраля 2020

Как указано в Flask документах , контекст приложения недоступен вне запроса, что и происходит, когда loopTask запускается в другом процессе. Подумайте о том, чтобы передать экземпляр вашего приложения в функцию loopTask и обернуть разделы кода под ним, которые используют объект пространства имен g внутри блока with. На самом деле не нужен блок with внутри вашего view_function, поскольку контекст приложения уже существует во время запроса.

РЕДАКТИРОВАТЬ: поскольку мы настраиваем соединение БД перед каждым запросом, давайте перейдем к test_request_context. Вы можете прочитать больше об этом здесь . Он предназначен для тестирования, но для наших целей он позволит нам иметь соединение db в порожденном процессе.

def loopTask(e, a_ids, a, b, c, d, loc, stuff, stuff_item, app):  # added app parameter 

    index, s = stuff_item
    c_id = document_ids[index]

    with app.test_request_context('/some_url'):
        app.preprocess_request()  # triggers 'connect_to_database'

        done = AnotherClassInDifferentDir.work(s)
        f = AnotherClassInDifferentDir.more_work(done, a, b, c, d, loc)
        if f != '':
            update.update_work(//some arguments)
            g.cnxn.commit()
            if (moreDB.check(//some arguments) ==0):
                update.work(//some arguments)
                g.cnxn.commit()
        else:
            pass

Это означает, что блок with изменится на:

    e = data.get('e', '')
    stuff = session['somedata'][1]
    pool = Pool(processes = 2)
    func = partial(loopTask,e, a_id, a_ids, a, b, c, d, loc, stuff, stuff_item)
    stuff_array = [(index, item) for index, item in enumerate(some_list)]
    print("stuff_array =", stuff_array)
    pool.map(func, (stuff_array, app))  # passing the `app` Flask instance here
    pool.close()
    pool.join()

Это должно сработать, но в идеале мы должны настроить соединение db в функции, которую мы можем использовать в нашем loopTask. Таким образом, нам не понадобится test_request_context и вместо него будет использоваться app_context.

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