guri
guri

Reputation: 1701

RuntimeError: working outside of application context

app.py

from flask import Flask, render_template, request,jsonify,json,g
import mysql.connector

app = Flask(__name__)

class TestMySQL():
    @app.before_request
    def before_request():
        try:
            g.db = mysql.connector.connect(user='root', password='root', database='mysql')
        except mysql.connector.errors.Error as err:
           resp = jsonify({'status': 500, 'error': "Error:{}".format(err)})
           resp.status_code = 500
           return resp

    @app.route('/')
    def input_info(self):
        try:     
            cursor = g.db.cursor()
            cursor.execute ('CREATE TABLE IF NOT EXISTS testmysql (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, name VARCHAR(40) NOT NULL, \
                     email VARCHAR(40) NOT NULL UNIQUE)')
            cursor.close()

test.py

from app import *
class Test(unittest.TestCase):         
    def test_connection1(self):  
        with patch('__main__.mysql.connector.connect') as mock_mysql_connector_connect:
            object = TestMySQL()
            object.before_request()  # Runtime error on calling this

I am importing app into test.py for unit testing. On calling 'before_request' function into test.py, it is throwing a RuntimeError: working outside of application context, the same is happening on calling 'input_info()'

Upvotes: 95

Views: 206400

Answers (5)

Yilmaz
Yilmaz

Reputation: 49581

I had this error because I created a thread inside the app:

from threading import Thread

Thread(target=handle_offchain).start()

when flask app receives a request, Flask creates both the application context and the request context Request Context to handle that particular request.

The request context keeps track of the request-level data during a request. Rather than passing the request object to each function that runs during a request, the request and session proxies are accessed instead

When the Flask application handles a request, it creates a Request object based on the environment it received from the WSGI server. Because a worker (thread, process, or coroutine depending on the server) handles only one request at a time, the request data can be considered global to that worker during that request. Flask uses the term context local for this.

This is application context

The application context keeps track of the application-level data during a request, CLI command, or other activity. Rather than passing the application around to each function, the current_app and g proxies are accessed instead.

Flask's context locals (current_app, request, etc.) are tied to the thread handling the current request, and they are not shared or available in other threads by default. If you create a separate thread manually within a route handler or view function, it won't automatically have access to the Flask application or request context.

     def handle_offchain(app_context):
        # this will give access to context
        app_context.push()
        ....
    
    # args will be passed to  `handle_offchain` method
    Thread(target=handle_offchain,args=[current_app.app_context()]).start()

Upvotes: 3

brenns10
brenns10

Reputation: 3379

Flask has an Application Context, and it seems like you'll need to do something like:

def test_connection(self):
    with app.app_context():
        #test code

You can probably also shove the app.app_context() call into a test setup method as well.

Upvotes: 147

sathish
sathish

Reputation: 289

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///todo.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)

app.app_context().push()

Run in terminal
    >python
    >>>from app import app
    >>>from app import db
    >>>db.create_all()

Now it should work

Upvotes: 28

smart.aleck
smart.aleck

Reputation: 225

I am using python3.8 and had to use a small variation to the answers already posted. I included the the below in pytests and didn't have to change anything else in the rest of the test file.

from flask import Flask

@pytest.fixture(autouse=True)
def app_context():
    app = Flask(__name__)
    with app.app_context():
        yield

This can also be used with a context manager as well. The main different to note here is the creation of the Flask app within the test file rather than it being imported from the main application file.

Upvotes: 1

Paddy Alton
Paddy Alton

Reputation: 2358

I followed the answer from @brenns10 when I ran into a similar problem when using pytest.

I followed the suggestion of putting it into test setup, this works:

import pytest
from src.app import app


@pytest.fixture
def app_context():
    with app.app_context():
        yield


def some_test(app_context):
    # <test code that needs the app context>

Upvotes: 6

Related Questions