Flask set #app decorator outside main module - flask

How do you set an #app... decorator outside of the main module?
I'm sorry if this a dumb question, but I am completely lost.
I wish to be able to decorate functions with #app.context_processor all over the place in a larg-ish app. All the examples I can find have these in main module, where app is defined. Thus in main.py I have e.g.
from app import create_app
app = create_app()
#app.context_processor
def test_me():
return {'test_me': 'test_me!!!'}
and in __init__.py I have
def create_app(config_class=Config):
app = Flask(__name__)
return app
That's fine, but how do I do same in some other module? app is not defined.
Let's say I have just that def test_me() in some other module. The module (presently) has no imports, doesn't use blueprints, etc.
Most questions asking about "how to access app in another module" use from flask import current_app, but you can only use current_app during a request, such as inside a def. Attempting to somehow use it for a decorator will give you the "not available in this context`, so that's not the way to go.

To answer my own question.
I needed to move the
app = create_app()
out of main.py and into __init__.py. Then app is visible to every module, and each one can go
from app import app
#app.context_processor
def something():
return {'something': 'something'}

Related

Using Flask-HTTPAuth when serving a static folder

I'm using Flask to serve a static folder:
from flask import Flask, send_from_directory
from flask_httpauth import HTTPBasicAuth
app = Flask(__name__,
static_url_path='',
static_folder='html_files')
...
#app.route('/')
#auth.login_required
def send_html_files():
return send_from_directory('html_files', 'main.html')
I used the first example in Flask-HTTPAuth docs in order to add basic authentication to my website. Just a regular username and password is enough for me.
The problem is that the authentication dialog is not showing when the user go directly to http://localhost:5000/a/b/c (it works on http://localhost:5000/)
What is the proper way of doing this? On the other hand, what is the quick and dirty way?
#app.route('/') matches your root path only.
Try something like this to match every path:
#app.route('/<path:filename>')
#auth.login_required
def send_html_files(filename):
return send_from_directory('html_files', filename)

Moving Custom CLI commands to another file

I have a few custom cli commands for a flask app I'm writing. I'm following the instructions here:
Command Line Interface
The problem is I do not want to put them all in my app.py file, it will get overbloated. What I would like to do is have my project structure:
project
|_ app.py
|_ cli.py
I thought about using a blueprint, but I get "Blueprint has no attribute 'cli'"
This is what I tried:
cli = Blueprint('cli', __name__) # I knew this would not work but I had to try
#cli.cli.command()
#click.argument('name')
def create_user(name):
print("hello")
Thanks
I would do something like this:
cli.py:
from flask import Flask
import click
def register_cli(app: Flask):
#app.cli.command()
#click.argument('name')
def create_user(name):
print("hello", name)
app.py:
from flask import Flask
from cli import register_cli
app = Flask(__name__)
register_cli(app)
It's common to create and configure (or just configure) app in factory functions.

Using Python Flask-restful with mod-wsgi

I am trying to use mod-wsgi with Apache 2.2
I have the following directory structure:
scheduling-algos
-lib
-common
-config
-config.json
resources
-Optimization.py
optimization.wsgi
optimization_app.py
My optimization_app.py is the following:
from flask import Flask
from flask_restful import Api
from resources.Optimization import OptimizationAlgo
def optimizeInstances():
optimization_app = Flask(__name__)
api = Api(optimization_app)
api.add_resource(OptimizationAlgo, '/instances')
if __name__ == '__main__':
optimizeInstances()
optimization_app.run(host='0.0.0.0', debug=True)
My Optimization.py code looks like the following:
class OptimizationAlgo(Resource):
def post(self):
return "success"
When I make a POST request to the url http://<host>:5000/instances, it works just as expected. I want make this work using WSGI. I have mod-wsgi installed with Apache 2.2.
My optimization.wsgi file looks like the following
import sys
sys.path.insert(0, '<path to app>')
from optimization_app import optimizeInstances as application
I get the following error: TypeError: optimizeInstances() takes no arguments (2 given) . Apparently this is not the correct way to use WSGI. What is the correct way to use WSGI?
Apparently, this is not the correct way to use WSGI.
As I told you in your other question, you should perhaps go back and read the Flask documentation again. That way you will learn and understand properly. By ignoring advice and expecting others to tell you, it only annoys people and they will stop helping you. Would suggest you take heed of that rather than leave a trail of separate questions hoping someone will solve your problems for you.
That said, I can't see how the code you give can even work with the Flask development server as you claim. The problem is that optimization_app = Flask(__name__) is setting a local variable within function scope. It isn't setting a global variable. As a result the call of optimization_app.run(host='0.0.0.0', debug=True) should fail with a LookupError as it will not see a variable called optimization_app. Not even sure why you are bothering with the function.
If you go look at the Flask documentation, the pattern it would likely use is:
# optimisation.wsgi
import sys
sys.path.insert(0, '<path to app>')
# We alias 'app' to 'application' here as mod_wsgi expects it to be called 'application'.
from optimization_app import app as application
# optimization_app.py
from flask import Flask
from flask_restful import Api
from resources.Optimization import OptimizationAlgo
app = Flask(__name__)
api = Api(app)
api.add_resource(OptimizationAlgo, '/instances')
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)

Importing Flask app when using app factory and flask script

This is Flask app context
app = Flask(__name__)
with app.app_context():
# insert code here
Most of the use cases of app context involves having 'app' initialized on the same script or importing app from the base.
My application is structured as the following:
# application/__init__.py
def create_app(config):
app = Flask(__name__)
return app
# manage.py
from application import create_app
from flask_script import Manager
manager = Manager(create_app)
manager.add_command("debug", Server(host='0.0.0.0', port=7777))
This might be really trivial issue, but how I should call 'with app.app_context()' if my application is structured like this?
Flask-Script calls everything inside the test context, so you can use current_app and other idioms:
The Manager runs the command inside a Flask test context. This means that you can access request-local proxies where appropriate, such as current_app, which may be used by extensions.
http://flask-script.readthedocs.org/en/latest/#accessing-local-proxies
So you don't need to use with app.app_context() with Manager scripts. If you're trying to do something else, then you'd have to create the app first:
from application import create_app
app = create_app()
with app.app_context():
# stuff here

Detect django testing mode

I'm writing a reusable django app and I need to ensure that its models are only sync'ed when the app is in test mode. I've tried to use a custom DjangoTestRunner, but I found no examples of how to do that (the documentation only shows how to define a custom test runner).
So, does anybody have an idea of how to do it?
EDIT
Here's how I'm doing it:
#in settings.py
import sys
TEST = 'test' in sys.argv
Hope it helps.
I think the answer provided here https://stackoverflow.com/a/7651002/465673 is a much cleaner way of doing it:
Put this in your settings.py:
import sys
TESTING = sys.argv[1:2] == ['test']
The selected answer is a massive hack. :)
A less-massive hack would be to create your own TestSuiteRunner subclass and change a setting or do whatever else you need to for the rest of your application. You specify the test runner in your settings:
TEST_RUNNER = 'your.project.MyTestSuiteRunner'
In general, you don't want to do this, but it works if you absolutely need it.
from django.conf import settings
from django.test.simple import DjangoTestSuiteRunner
class MyTestSuiteRunner(DjangoTestSuiteRunner):
def __init__(self, *args, **kwargs):
settings.IM_IN_TEST_MODE = True
super(MyTestSuiteRunner, self).__init__(*args, **kwargs)
NOTE: As of Django 1.8, DjangoTestSuiteRunner has been deprecated.
You should use DiscoverRunner instead:
from django.conf import settings
from django.test.runner import DiscoverRunner
class MyTestSuiteRunner(DiscoverRunner):
def __init__(self, *args, **kwargs):
settings.IM_IN_TEST_MODE = True
super(MyTestSuiteRunner, self).__init__(*args, **kwargs)
Not quite sure about your use case but one way I've seen to detect when the test suite is running is to check if django.core.mail has a outbox attribute such as:
from django.core import mail
if hasattr(mail, 'outbox'):
# We are in test mode!
pass
else:
# Not in test mode...
pass
This attributed is added by the Django test runner in setup_test_environment and removed in teardown_test_environment. You can check the source here: https://code.djangoproject.com/browser/django/trunk/django/test/utils.py
Edit: If you want models defined for testing only then you should check out Django ticket #7835 in particular comment #24 part of which is given below:
Apparently you can simply define models directly in your tests.py.
Syncdb never imports tests.py, so those models won't get synced to the
normal db, but they will get synced to the test database, and can be
used in tests.
I'm using settings.py overrides. I have a global settings.py, which contains most stuff, and then I have overrides for it. Each settings file starts with:
from myproject.settings import settings
and then goes on to override some of the settings.
prod_settings.py - Production settings (e.g. overrides DEBUG=False)
dev_settings.py - Development settings (e.g. more logging)
test_settings.py
And then I can define UNIT_TESTS=False in the base settings.py, and override it to UNIT_TESTS=True in test_settings.py.
Then whenever I run a command, I need to decide which settings to run against (e.g. DJANGO_SETTINGS_MODULE=myproject.test_settings ./manage.py test). I like that clarity.
Well, you can just simply use environment variables in this way:
export MYAPP_TEST=1 && python manage.py test
then in your settings.py file:
import os
TEST = os.environ.get('MYAPP_TEST')
if TEST:
# Do something
Although there are lots of good answers on this page, I think there is also another way to check if your project is in the test mode or not (if in some cases you couldn't use sys.argv[1:2] == ["test"]).
As you all may know DATABASE name will change to something like "test_*" (DATABASE default name will be prefixed with test) when you are in the test mode (or you can simply print it out to find your database name when you are running tests). Since I used pytest in one of my projects, I couldn't use
sys.argv[1:2] == ["test"]
because this argument wasn't there. So I simply used this one as my shortcut to check if I'm in the test environment or not (you know that your DATABASE name prefixed with test and if not just change test to your prefixed part of DATABASE name):
1) Any places other than settings module
from django.conf import settings
TESTING_MODE = "test" in settings.DATABASES["default"]["NAME"]
2) Inside the settings module
TESTING_MODE = "test" in DATABASES["default"]["NAME"]
or
TESTING_MODE = DATABASES["default"]["NAME"].startswith("test") # for more strict checks
And if this solution is doable, you don't even need to import sys for checking this mode inside your settings.py module.
I've been using Django class based settings. I use the 'switcher' from the package and load a different config/class for testing=True:
switcher.register(TestingSettings, testing=True)
In my configuration, I have a BaseSettings, ProductionSettings, DevelopmentSettings, TestingSettings, etc. They subclass off of each other as needed. In BaseSettings I have IS_TESTING=False, and then in TestingSettings I set it to True.
It works well if you keep your class inheritance clean. But I find it works better than the import * method Django developers usually use.