How could I share common hook function for multiple flask servers - python

Suppose I need to do before_request for each flask servers
How can I share the following snippet to each servers without COPY-PASTE
#app.before_request
def before_request(*args, **kwargs):
params = get_params()
if params.has_key('start_dt') and params.has_key('end_dt'):
g.mongo_query = Mongo.get_date_range_query(params)
else:
g.mongo_query = {}

You could use application factory for this. If you initialize your flask applications like so:
from flask import Flask
import yourdb
def create_app(config_filename):
app = Flask(__name__)
app.config.from_pyfile(config_filename)
yourdb.init_app(app)
#add_extensions
#add_blueprints/views
# ... some other configuration ...
#app.before_request
def before_request(*args, **kwargs):
#Your code
return app
From manage/run, you would then
from somewhere import create_app
app = create_app(<your_config>)

Related

How to always provide a context for Flask app tested with PyTest?

I try to implement unit tests with Pytest on a Flask app and I have a hard time to do it.
My Flask application uses configuration files on most of the functions (here some_method) to illustrate. So it seems that I should provide a context for each call to any method that I would like to test. It seems that I can achieve it with "with app.app_context():" on each call.
I read the official testing documentation but they talk about creating a client. As I would like to do unit tests, I need to call sub functions which are not top level.
Is there a way to always provide a context without pushing the context manually on each call?
Please find below my current implementation:
main.py
from flask import current_app
def main(request):
current_app.config.from_envvar('APPLICATION_SETTINGS')
print(some_method())
return 'OK'
def some_method():
# doing some stuff using the context
world = current_app.config['SECRET_KEY']
return world
test_main.py
import pytest
from flask import current_app, Flask
from main import main, some_method
#pytest.fixture
def app():
app = Flask(__name__)
# load here any potential configuration configuration
return app
def test_some_method(app):
with app.app_context():
# calling and doing some assertion
some_method()
PS: I do not have app = Flask(name) in my main file because I am running on the Functions Framework
pytest-flask seems to configure the context on any call.
conftest.py
import pytest
from flask import Flask
#pytest.fixture
def app():
app = Flask(__name__)
return app
test_main.py
import pytest
from flask import current_app, Flask
from main import main, some_method
def test_some_method(app):
#with app.app_context():
# calling and doing some assertion
some_method()
works.

generating flask route from class method

i am trying to generate Flask route using a basic DI i.e mapping methods as route handlers, i am a total beginner at Flask so mind my basic skills
class myClass():
def __init__(self):
self.dbConnObj = DbToolsMySql('someconnection', 'slave')
self.dbConnObj.connect()
self.blueprint = Blueprint('myClass', __name__)
self.blueprint.add_url_rule('/my_method', view_func=self.my_method)
def my_method(self, event):
retun "hello"
and then in my handler file
from flask import Flask
from flask_restful import Api, Resource
from src.app.services.myClassimport myClass
app = Flask(__name__)
app.register_blueprint(myClass.blueprint)
if __name__ == "main":
app.run()
Quite simple ehh???? but not working... i am getting following message
Not Found The requested URL was not found on the server. If you
entered the URL manually please check your spelling and try again.
typically you add routes to the Flask app with decorators like so:
app = Flask(__name__)
#app.route('/some-endpoint')
def some_endpoint_handler():
# do something
pass
Or you can add without a decorator like so:
def some_endpoint_handler():
# do something
pass
app = Flask(__name__)
app.route('/some-endpoint', methods=['GET'])(some_endpoint_handler)
So in your scenario, you can pass the app.route call to your myClass object and set the route like this:
class myClass():
def __init__(self, router):
self.dbConnObj = DbToolsMySql('someconnection', 'slave')
self.dbConnObj.connect()
self.blueprint = Blueprint('myClass', __name__)
#self.blueprint.add_url_rule('/my_method', view_func=self.my_method)
router('/my_method', ['GET'])(self.my_method)
def my_method(self, event):
retun "hello"
myObj = myClass( app.route )
or, invert the dependency:
app = Flask(__name__)
#app.route(myClass.blueprint.some_endpoint_string)
def some_endpoint_handler():
myClass.blueprint.call_some_endpoint_handler()
pass
if __name__ == "main":
app.run()

How to access flask config variables outside application context according to my project structure?

spinngod.py - flask app starter code
from app import create_app
import sys
run_profile = str(sys.argv[1]) if len(sys.argv) >= 2 else 'development'
app = create_app(run_profile)
print("App Root Path:" + app.root_path)
if __name__ == '__main__':
print sys.path
app.run(debug=True, host='0.0.0.0')
app/init.py - creates flask app
def create_app(profile_name):
print "currently active profile:" + profile_name
app = Flask(__name__)
############# configurations ####################
app.config.from_object(config[profile_name])
configure_app(app)
configure_app_logger(app)
#################### blueprint registration and rest_plus namespace additions ###############
from api_1_0 import api as api_1_0_blueprint
from api_1_0.restplus import api_restplus
# ************************************************** #
api_restplus.init_app(api_1_0_blueprint)
api_restplus.add_namespace(application_namespace)
api_restplus.add_namespace(pipeline_template_namespace)
api_restplus.add_namespace(loadbalancer_namespace)
api_restplus.add_namespace(servergroup_namespace)
api_restplus.add_namespace(task_namespace)
# ************************************************** #
app.register_blueprint(api_1_0_blueprint)
##############################################################
return app
I want to access flask config variables defined in config.py in some other files which are outside application context. The app configuration depends on which profile it is started with (dev,stage or production) which is being passed from command line as an arg.
The only way that I can think of accessing config variables outside app context is to set profile (dev,stage or prod) as an environment variable and
then import directly from config file.
The second way that I tried was to move creation of flask app in app/init.py outside method.
This is how I am trying to access config variables in another class.
import requests
class Client(object):
def __init__(self):
from app import app
print "fjaijflkajsf" + app.config['SPINNAKER_BASE_URL']
pass
Is there a way better of doing this in flask ?
From the docs:
Rather than passing the application around to each function, the current_app and g proxies are accessed instead.
The Flask application object has attributes, such as config, that are useful to access within views and CLI commands. However, importing the app instance within the modules in your project is prone to circular import issues.
Flask solves this issue with the application context. Rather than referring to an app directly, you use the the current_app proxy, which points to the application handling the current activity.
You import current_app like this:
from flask import current_app
and then access the config or other attributes like this:
config = current_app.config
Example:
src/application.py (where config is set in the context)
create_app():
app = Flask('app')
app.config.from_object(some_class)
return app
src/module/another_module.py
from flask import current_app
def function_that_requires_config():
config = current_app.config
Alternative:
src/application.py (where config is set in the context)
APP = create_app(os.environ.get('FLASK_ENV'))
src/module/another_module.py
from src.application import APP
def function_that_requires_config():
config_value = APP.config.get(config_key, default_value)
Not sure if it is good to put it here as it may not respond to the question directly, but here is the cleanest way i've figured to use config values outside of requests, without having to pass config as a param.
The solution is actually pretty simple, juste consider the part of your code as a flask_extension.
my exemple will be the use of external api, with ROOT_URL in the config file, and i don't want to make api call from within my routes, so the api is in its own module.
in my create_app fuction:
from flask import Flask
from .api import api
from .configmodule import Config
from .model import db
def create_app(environment):
app = Flask(__name__)
app.config.from_object(Config.get_config(environment))
db.init_app(app)
api.init_app(app) # here i use api.init_app the same way i do for sqlalchemy
and in api/init.py
class Api:
def init_app(self, app):
self.config = app.config
api = Api()
and in any files in my api modude i can now write
from . import api
def foo():
print(api.config.get("API_ROOT_URL"))
this can even be improved if you feel the need to access some other global app vars from your module.

How do I get the application context in a Blueprint, but not in a request?

I am attempting to convert a collection of Flask apps to a single app with several Blueprints.
In one of my apps, I have a task that runs periodically in the background, not related to a request. It looks something like this:
import apscheduler.schedulers.background
import flask
app = flask.Flask(__name__)
app.config['DATABASE']
scheduler = apscheduler.schedulers.background.BackgroundScheduler()
scheduler.start()
def db():
_db = flask.g.get('_db')
if _db is None:
_db = get_db_connection_somehow(app.config['DATABASE'])
flask.g._db = _db
return _db
#scheduler.scheduled_job('interval', hours=1)
def do_a_thing():
with app.app_context():
db().do_a_thing()
When I convert this app to a Blueprint, I lose access to the app object and I can't figure out how to create an application context when I need one. This is what I tried:
import apscheduler.schedulers.background
import flask
bp = flask.Blueprint('my_blueprint', __name__)
scheduler = apscheduler.schedulers.background.BackgroundScheduler()
scheduler.start()
def db():
_db = flask.g.get('_db')
if _db is None:
_db = get_db_connection_somehow(flask.current_app.config['DATABASE'])
flask.g._db = _db
return _db
#bp.record
def record(state):
with state.app.app_context():
flask.g._app = state.app
#scheduler.scheduled_job('interval', hours=1)
def do_a_thing():
with flask.g._app.app_context():
db().do_a_thing()
The error I get is:
RuntimeError: Working outside of application context.
So, how can I get the application context in a blueprint but outside a request?
I solved this problem with the following changes. First, I set up a scheduler object on my Flask app:
app = flask.Flask(__name__)
app.scheduler = apscheduler.schedulers.background.BackgroundScheduler()
app.scheduler.start()
Next, I changed the function that runs my background task to accept the app as an argument, so I could read the database connection information from app.config:
def do_a_thing(app: flask.Flask):
db = get_db_connection_somehow(app.config['DATABASE'])
db.do_a_thing()
Finally, I set up the scheduled job in Blueprint.record():
#bp.record
def record(state):
state.app.scheduler.add_job(do_a_thing, trigger='interval', args=[state.app], hours=1)

Extending Flask class as main App

I'm learning Flask and am a bit confused about how to structure my code. So I tried to extend Flask main class as follows:
from flask import Flask, ...
class App(Flask):
def __init__(self, import_name, *args, **kwargs):
super(App, self).__init__(import_name, *args, **kwargs)
Note that I am aware of that this may be a completely wrong approach.
So that when I want to start the app I do:
app = App(__name__)
if __name__ == '__main__':
app.run()
This way I can order my methods and routes in the class, but the problem is when using self-decorators:
#route('/')
def home(self, context=None):
context = context or dict()
return render_template('home.html', **context)
Which raises an error as unresolved reference 'route'. I guess this is not the way I should be structuring the app. How should I do it instead or how do I get the error fixed?
Doing this doesn't make sense. You would subclass Flask to change its internal behavior, not to define your routes as class methods.
Instead, you're looking for blueprints and the app factory pattern. Blueprints divide your views into groups without requiring an app, and the factory creates and sets up the app only when called.
my_app/users/__init__.py
from flask import Blueprint
bp = Blueprint('users', __name__, url_prefix='/users')
my_app/users/views.py
from flask import render_template
from my_app.users import bp
#bp.route('/')
def index():
return render_template('users/index.html')
my_app/__init__.py
def create_app():
app = Flask(__name__)
# set up the app here
# for example, register a blueprint
from my_app.users import bp
app.register_blueprint(bp)
return app
run.py
from my_app import create_app
app = create_app()
Run the dev server with:
FLASK_APP=run.py
FLASK_DEBUG=True
flask run
If you need access to the app in a view, use current_app, just like request gives access to the request in the view.
from flask import current_app
from itsdangerous import URLSafeSerializer
#bp.route('/token')
def token():
s = URLSafeSerializer(current_app.secret_key)
return s.dumps('secret')
If you really want to define routes as methods of a Flask subclass, you'll need to use self.add_url_rule in __init__ rather than decorating each route locally.
class MyFlask(Flask):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.add_url_rule('/', view_func=self.index)
def index(self):
return render_template('index.html')
The reason route (and self) won't work is because it's an instance method, but you don't have an instance when you're defining the class.

Categories