In my Flask server app, I wanted to split up my routes into separate files so I used Blueprint. However this caused logging to fail within the constructor function used by a route. Can anyone see what I might have done wrong to cause this?
Simplified example ...
main.py ...
#!/usr/bin/python
import logging
import logging.handlers
from flask import Flask, Blueprint
from my_routes import *
logger = logging.getLogger("")
logger.setLevel(logging.DEBUG)
handler = logging.handlers.RotatingFileHandler("flask.log",
maxBytes=3000000, backupCount=2)
formatter = logging.Formatter(
'[%(asctime)s] {%(filename)s:%(lineno)d} %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logging.getLogger().addHandler(logging.StreamHandler())
logging.debug("started app")
app = Flask(__name__)
app.register_blueprint(api_v1_0)
if __name__ == '__main__':
logging.info("Starting server")
app.run(host="0.0.0.0", port=9000, debug=True)
my_routes.py ...
import logging
import logging.handlers
from flask import Flask, Blueprint
class Class1():
def __init__(self):
logging.debug("Class1.__init__()") # This statement does not get logged
self.prop1=11
def method1(self):
logging.debug("Class1.method1()")
return self.prop1
obj1 = Class1()
api_v1_0 = Blueprint('api_v1_0', __name__)
#api_v1_0.route("/route1", methods=["GET"])
def route1():
logging.debug("route1()")
return(str(obj1.method1()))
You create an instance of Class1 in the global scope of module my_routes.py, so the constructor runs at the time you import that module, the from my_routes import * line in main.py. This is before your logging handler is configured, so there is nowhere to log at that time.
The solution is simple, move your import statement below the chunk of code that sets up the logging handler.
Related
I am new to flask and I have set up a simple flask example and two tests using pytest(see here). When I let run only one test it works, but if I run both tests it does not work.
Anyone knows why? I think I am missing here some basics of how flask works.
code structure:
app/__init__.py
from flask import Flask
def create_app():
app = Flask(__name__)
with app.app_context():
from app import views
return app
app/views.py
from flask import current_app as app
#app.route('/')
def index():
return 'Index Page'
#app.route('/hello')
def hello():
return 'Hello World!'
tests/conftest.py
import pytest
from app import create_app
#pytest.fixture
def client():
app = create_app()
yield app.test_client()
tests/test_app.py
from app import create_app
def test_index(client):
response = client.get("/")
assert response.data == b"Index Page"
def test_hello(client):
response = client.get("/hello")
assert response.data == b"Hello World!"
The problem is with your registration of the routes in app/views.py when you register them with current_app as app. I'm not sure how you would apply the application factory pattern without using blueprints as the pattern description in the documentation implies they are mandatory for the pattern:
If you are already using packages and blueprints for your application [...]
So I adjusted your code to use a blueprint instead:
app/main/__init__.py:
from flask import Blueprint
bp = Blueprint('main', __name__)
from app.main import views
app/views.py -> app/main/views.py:
from app.main import bp
#bp.route('/')
def index():
return 'Index Page'
#bp.route('/hello')
def hello():
return 'Hello World!'
app/__init__.py:
from flask import Flask
def create_app():
app = Flask(__name__)
# register routes with app instead of current_app:
from app.main import bp as main_bp
app.register_blueprint(main_bp)
return app
Then your tests work as intended:
$ python -m pytest tests
============================== test session starts ==============================
platform darwin -- Python 3.6.5, pytest-6.1.0, py-1.9.0, pluggy-0.13.1
rootdir: /Users/oschlueter/github/simple-flask-example-with-pytest
collected 2 items
tests/test_app.py .. [100%]
=============================== 2 passed in 0.02s ===============================
I have the following structure:
app_dir/
| myapi/
| __init__.py
| myapi_app.py
where myapi_app.py is
from myapi import create_app, db
app = create_app()
and myapi/__init__.py is
import logging
import os
from logging.handlers import RotatingFileHandler
from flask import Flask, request, current_app
from flask_sqlalchemy import SQLAlchemy
from myapi.config import Config
db = SQLAlchemy()
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
db.init_app(app)
...
return app
When I set FLASK_APP=myapi_app.py and run flask run from the app_dir directory, the flask service starts. However, when I make a request, I get the following error: flask.cli.NoAppException: Could not import "myapi_app". Where am I going wrong?
Your problem is that you are setting $FLASK_APP to the file in which the app variable is stored, you should instead set it to the python object path, e.g.
FLASK_APP=myapi_app:app
However, this is not necessary, as you could also just do:
FLASK_APP=myapi
as Flask will look for a create_app function in the package on its own.
I have the following three files.
app.py
from flask_restful import Api
from lib import globals
from flask import Flask
from flask.ext.cache import Cache
globals.algos_app = Flask(__name__)
#cache in file system
globals.cache = Cache(globals.algos_app, config={'CACHE_TYPE': 'filesystem', 'CACHE_DIR': '/tmp'})
api = Api(globals.algos_app)
api.add_resource(Test, '/test')
if __name__ == '__main__':
globals.algos_app.run(host='0.0.0.0', debug=True)
globals.py
global algos_app
global cache
Test.py
from flask_restful import Resource
from lib import globals
from flask_restful import Resource
import time
class Test(Resource):
def get(self):
return self.someMethod()
def post(self):
globals.cache.clear()
return self.someMethod()
#globals.cache.cached()
def someMethod(self):
return str(time.ctime())
I have a GET method which needs to the value from the cache and a POST method which updates the cache by first clearing the cache.
However, no matter I call the GET or the POST method, it always gives me the value from the cache.
PS: At the moment I am simply testing on the development server however I do need to deploy it using WSGI later.
I am not sure if it is the best way, but I did it using the following way.
class Test(Resource):
def get(self):
return globals.cache.get('curr_time')
def post(self):
result = self.someMethod()
globals.cache.set('curr_time', result, timeout=3600)
def someMethod(self):
return str(time.ctime())
I am starting to write tests for a Django app, which relies on several environment variables. When I am testing it in the shell, I can import os and specify the variables and my tests work just fine. However, when I put them into tests.py, I still get a key error because those variables are not found. here's what my test looks like:
from django.utils import unittest
from django.test.utils import setup_test_environment
from django.test.client import Client
import os
os.environ['a'] = 'a'
os.environ['b'] = 'b'
class ViewTests(unittest.TestCase):
def setUp(self):
setup_test_environment()
def test_login_returning_right_template(self):
""" get / should return login.html template """
c = Client()
resp = c.get('/')
self.assertEqual(resp.templates[0].name, 'login.html')
Is this the wrong place to initialize those variables? I tried to do it on setUp, but with the same result - they are not found. Any suggestions on how to initialize environment variables before running a test suite?
Thanks!
Luka
You should not relay on os.envior in your views. If you have to, do It in your settings.py
MY_CUSTOM_SETTING = os.environ.get('a', 'default_value')
And in views use settings variable:
from django.conf.settings import MY_CUSTOM_SETTING
print MY_CUSTOM_SETTING
Then in your test you can set this setting:
from django.test import TestCase
class MyTestCase(TestCase):
def test_something(self):
with self.settings(MY_CUSTOM_SETTING='a'):
c = Client()
resp = c.get('/')
self.assertEqual(resp.templates[0].name, 'login.html')
I'm tring to use Django 1.1 in GAE, But when I uncomment
use_library('django', '1.1')
in this script
import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
from google.appengine.dist import use_library
#use_library('django', '1.1')
# Google App Engine imports.
from google.appengine.ext.webapp import util
# Force Django to reload its settings.
from django.conf import settings
settings._target = None
import django.core.handlers.wsgi
import django.core.signals
import django.db
import django.dispatch.dispatcher
# Unregister the rollback event handler.
django.dispatch.dispatcher.disconnect(
django.db._rollback_on_exception,
django.core.signals.got_request_exception)
def main():
# Create a Django application for WSGI.
application = django.core.handlers.wsgi.WSGIHandler()
# Run the WSGI CGI handler with that application.
util.run_wsgi_app(application)
if __name__ == "__main__":
main()
I receives
AttributeError: 'module' object has no
attribute 'disconnect'
What is going on?
From http://justinlilly.com/blog/2009/feb/06/django-app-engine-doc-fix/
For those setting up Django on Google
App Engine on version after the
signals refactor, the following fix is
needed for the code supplied by
Google.
# Log errors.
django.dispatch.dispatcher.connect(
log_exception, django.core.signals.got_request_exception)
# Unregister the rollback event handler.
django.dispatch.dispatcher.disconnect(
django.db._rollback_on_exception,
django.core.signals.got_request_exception)
becomes:
# Log errors.
django.dispatch.Signal.connect(
django.core.signals.got_request_exception, log_exception)
# Unregister the rollback event handler.
django.dispatch.Signal.disconnect(
django.core.signals.got_request_exception,
django.db._rollback_on_exception)