From 8cf32bca519723c5d97f123127cdec81dba868f6 Mon Sep 17 00:00:00 2001 From: Kyle Lawlor Date: Sat, 1 Apr 2017 20:27:28 -0400 Subject: [PATCH 1/2] Adds in blueprints and an application factory --- examples/flaskr/flaskr/__init__.py | 1 - examples/flaskr/flaskr/_cliapp.py | 3 + examples/flaskr/flaskr/blueprints/__init__.py | 0 .../flaskr/flaskr/{ => blueprints}/flaskr.py | 55 +++++-------------- examples/flaskr/flaskr/factory.py | 52 ++++++++++++++++++ examples/flaskr/flaskr/templates/layout.html | 4 +- examples/flaskr/flaskr/templates/login.html | 2 +- .../flaskr/flaskr/templates/show_entries.html | 2 +- examples/flaskr/setup.py | 4 +- examples/flaskr/tests/test_flaskr.py | 34 +++++++----- 10 files changed, 95 insertions(+), 62 deletions(-) create mode 100644 examples/flaskr/flaskr/_cliapp.py create mode 100644 examples/flaskr/flaskr/blueprints/__init__.py rename examples/flaskr/flaskr/{ => blueprints}/flaskr.py (55%) create mode 100644 examples/flaskr/flaskr/factory.py diff --git a/examples/flaskr/flaskr/__init__.py b/examples/flaskr/flaskr/__init__.py index d096b1e7..e69de29b 100644 --- a/examples/flaskr/flaskr/__init__.py +++ b/examples/flaskr/flaskr/__init__.py @@ -1 +0,0 @@ -from .flaskr import app diff --git a/examples/flaskr/flaskr/_cliapp.py b/examples/flaskr/flaskr/_cliapp.py new file mode 100644 index 00000000..faed660c --- /dev/null +++ b/examples/flaskr/flaskr/_cliapp.py @@ -0,0 +1,3 @@ +from flaskr.factory import create_app + +app = create_app() \ No newline at end of file diff --git a/examples/flaskr/flaskr/blueprints/__init__.py b/examples/flaskr/flaskr/blueprints/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/flaskr/flaskr/flaskr.py b/examples/flaskr/flaskr/blueprints/flaskr.py similarity index 55% rename from examples/flaskr/flaskr/flaskr.py rename to examples/flaskr/flaskr/blueprints/flaskr.py index b4c1d6bd..7b64dd9e 100644 --- a/examples/flaskr/flaskr/flaskr.py +++ b/examples/flaskr/flaskr/blueprints/flaskr.py @@ -10,29 +10,18 @@ :license: BSD, see LICENSE for more details. """ -import os from sqlite3 import dbapi2 as sqlite3 -from flask import Flask, request, session, g, redirect, url_for, abort, \ - render_template, flash +from flask import Blueprint, request, session, g, redirect, url_for, abort, \ + render_template, flash, current_app -# create our little application :) -app = Flask(__name__) - -# Load default config and override config from an environment variable -app.config.update(dict( - DATABASE=os.path.join(app.root_path, 'flaskr.db'), - DEBUG=True, - SECRET_KEY='development key', - USERNAME='admin', - PASSWORD='default' -)) -app.config.from_envvar('FLASKR_SETTINGS', silent=True) +# create our blueprint :) +bp = Blueprint('flaskr', __name__) def connect_db(): """Connects to the specific database.""" - rv = sqlite3.connect(app.config['DATABASE']) + rv = sqlite3.connect(current_app.config['DATABASE']) rv.row_factory = sqlite3.Row return rv @@ -40,18 +29,11 @@ def connect_db(): def init_db(): """Initializes the database.""" db = get_db() - with app.open_resource('schema.sql', mode='r') as f: + with current_app.open_resource('schema.sql', mode='r') as f: db.cursor().executescript(f.read()) db.commit() -@app.cli.command('initdb') -def initdb_command(): - """Creates the database tables.""" - init_db() - print('Initialized the database.') - - def get_db(): """Opens a new database connection if there is none yet for the current application context. @@ -61,14 +43,7 @@ def get_db(): return g.sqlite_db -@app.teardown_appcontext -def close_db(error): - """Closes the database again at the end of the request.""" - if hasattr(g, 'sqlite_db'): - g.sqlite_db.close() - - -@app.route('/') +@bp.route('/') def show_entries(): db = get_db() cur = db.execute('select title, text from entries order by id desc') @@ -76,7 +51,7 @@ def show_entries(): return render_template('show_entries.html', entries=entries) -@app.route('/add', methods=['POST']) +@bp.route('/add', methods=['POST']) def add_entry(): if not session.get('logged_in'): abort(401) @@ -85,26 +60,26 @@ def add_entry(): [request.form['title'], request.form['text']]) db.commit() flash('New entry was successfully posted') - return redirect(url_for('show_entries')) + return redirect(url_for('flaskr.show_entries')) -@app.route('/login', methods=['GET', 'POST']) +@bp.route('/login', methods=['GET', 'POST']) def login(): error = None if request.method == 'POST': - if request.form['username'] != app.config['USERNAME']: + if request.form['username'] != current_app.config['USERNAME']: error = 'Invalid username' - elif request.form['password'] != app.config['PASSWORD']: + elif request.form['password'] != current_app.config['PASSWORD']: error = 'Invalid password' else: session['logged_in'] = True flash('You were logged in') - return redirect(url_for('show_entries')) + return redirect(url_for('flaskr.show_entries')) return render_template('login.html', error=error) -@app.route('/logout') +@bp.route('/logout') def logout(): session.pop('logged_in', None) flash('You were logged out') - return redirect(url_for('show_entries')) + return redirect(url_for('flaskr.show_entries')) diff --git a/examples/flaskr/flaskr/factory.py b/examples/flaskr/flaskr/factory.py new file mode 100644 index 00000000..e71a6704 --- /dev/null +++ b/examples/flaskr/flaskr/factory.py @@ -0,0 +1,52 @@ +import os +from flask import Flask, g +from werkzeug.utils import find_modules, import_string +from flaskr.blueprints.flaskr import init_db + + +def create_app(config=None): + app = Flask(__name__) + + app.config.update(dict( + DATABASE=os.path.join(app.root_path, 'flaskr.db'), + DEBUG=True, + SECRET_KEY='development key', + USERNAME='admin', + PASSWORD='default' + )) + app.config.update(config or {}) + app.config.from_envvar('FLASKR_SETTINGS', silent=True) + + register_blueprints(app) + register_cli(app) + register_teardowns(app) + + return app + + +def register_blueprints(app): + """Register all blueprint modules + + Reference: Armin Ronacher, "Flask for Fun and for Profit" PyBay 2016. + """ + for name in find_modules('flaskr.blueprints'): + mod = import_string(name) + if hasattr(mod, 'bp'): + app.register_blueprint(mod.bp) + return None + + +def register_cli(app): + @app.cli.command('initdb') + def initdb_command(): + """Creates the database tables.""" + init_db() + print('Initialized the database.') + + +def register_teardowns(app): + @app.teardown_appcontext + def close_db(error): + """Closes the database again at the end of the request.""" + if hasattr(g, 'sqlite_db'): + g.sqlite_db.close() \ No newline at end of file diff --git a/examples/flaskr/flaskr/templates/layout.html b/examples/flaskr/flaskr/templates/layout.html index 737b51b2..862a9f4a 100644 --- a/examples/flaskr/flaskr/templates/layout.html +++ b/examples/flaskr/flaskr/templates/layout.html @@ -5,9 +5,9 @@

Flaskr

{% if not session.logged_in %} - log in + log in {% else %} - log out + log out {% endif %}
{% for message in get_flashed_messages() %} diff --git a/examples/flaskr/flaskr/templates/login.html b/examples/flaskr/flaskr/templates/login.html index ed09aeba..505d2f66 100644 --- a/examples/flaskr/flaskr/templates/login.html +++ b/examples/flaskr/flaskr/templates/login.html @@ -2,7 +2,7 @@ {% block body %}

Login

{% if error %}

Error: {{ error }}{% endif %} -

+
Username:
diff --git a/examples/flaskr/flaskr/templates/show_entries.html b/examples/flaskr/flaskr/templates/show_entries.html index 2f68b9d3..cf8fbb86 100644 --- a/examples/flaskr/flaskr/templates/show_entries.html +++ b/examples/flaskr/flaskr/templates/show_entries.html @@ -1,7 +1,7 @@ {% extends "layout.html" %} {% block body %} {% if session.logged_in %} - +
Title:
diff --git a/examples/flaskr/setup.py b/examples/flaskr/setup.py index 910f23ac..f52aacd8 100644 --- a/examples/flaskr/setup.py +++ b/examples/flaskr/setup.py @@ -1,8 +1,8 @@ -from setuptools import setup +from setuptools import setup, find_packages setup( name='flaskr', - packages=['flaskr'], + packages=find_packages(), include_package_data=True, install_requires=[ 'flask', diff --git a/examples/flaskr/tests/test_flaskr.py b/examples/flaskr/tests/test_flaskr.py index 663e92e0..c8115829 100644 --- a/examples/flaskr/tests/test_flaskr.py +++ b/examples/flaskr/tests/test_flaskr.py @@ -12,20 +12,24 @@ import os import tempfile import pytest -from flaskr import flaskr +from flaskr.factory import create_app +from flaskr.blueprints.flaskr import init_db + + +test_app = create_app() @pytest.fixture def client(request): - db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp() - flaskr.app.config['TESTING'] = True - client = flaskr.app.test_client() - with flaskr.app.app_context(): - flaskr.init_db() + db_fd, test_app.config['DATABASE'] = tempfile.mkstemp() + test_app.config['TESTING'] = True + client = test_app.test_client() + with test_app.app_context(): + init_db() def teardown(): os.close(db_fd) - os.unlink(flaskr.app.config['DATABASE']) + os.unlink(test_app.config['DATABASE']) request.addfinalizer(teardown) return client @@ -50,23 +54,23 @@ def test_empty_db(client): def test_login_logout(client): """Make sure login and logout works""" - rv = login(client, flaskr.app.config['USERNAME'], - flaskr.app.config['PASSWORD']) + rv = login(client, test_app.config['USERNAME'], + test_app.config['PASSWORD']) assert b'You were logged in' in rv.data rv = logout(client) assert b'You were logged out' in rv.data - rv = login(client, flaskr.app.config['USERNAME'] + 'x', - flaskr.app.config['PASSWORD']) + rv = login(client,test_app.config['USERNAME'] + 'x', + test_app.config['PASSWORD']) assert b'Invalid username' in rv.data - rv = login(client, flaskr.app.config['USERNAME'], - flaskr.app.config['PASSWORD'] + 'x') + rv = login(client, test_app.config['USERNAME'], + test_app.config['PASSWORD'] + 'x') assert b'Invalid password' in rv.data def test_messages(client): """Test that messages work""" - login(client, flaskr.app.config['USERNAME'], - flaskr.app.config['PASSWORD']) + login(client, test_app.config['USERNAME'], + test_app.config['PASSWORD']) rv = client.post('/add', data=dict( title='', text='HTML allowed here' From 54b6fc8de689a726aa01a0f2a39ce428e7a69712 Mon Sep 17 00:00:00 2001 From: Kyle Lawlor Date: Sun, 2 Apr 2017 11:55:05 -0400 Subject: [PATCH 2/2] Add in a fixture utilizing app factory --- examples/flaskr/README | 8 +++-- examples/flaskr/flaskr/_cliapp.py | 12 +++++++ examples/flaskr/flaskr/factory.py | 12 +++++++ examples/flaskr/setup.cfg | 4 +-- examples/flaskr/setup.py | 11 +++++++ examples/flaskr/tests/test_flaskr.py | 49 +++++++++++++++++----------- 6 files changed, 72 insertions(+), 24 deletions(-) diff --git a/examples/flaskr/README b/examples/flaskr/README index 90860ff2..22ae857f 100644 --- a/examples/flaskr/README +++ b/examples/flaskr/README @@ -9,9 +9,11 @@ ~ How do I use it? - 1. edit the configuration in the flaskr.py file or + 1. edit the configuration in the factory.py file or export an FLASKR_SETTINGS environment variable - pointing to a configuration file. + pointing to a configuration file or pass in a + dictionary with config values using the create_app + function. 2. install the app from the root of the project directory @@ -19,7 +21,7 @@ 3. Instruct flask to use the right application - export FLASK_APP=flaskr + export FLASK_APP=flaskr._cliapp 4. initialize the database with this command: diff --git a/examples/flaskr/flaskr/_cliapp.py b/examples/flaskr/flaskr/_cliapp.py index faed660c..b13bf79a 100644 --- a/examples/flaskr/flaskr/_cliapp.py +++ b/examples/flaskr/flaskr/_cliapp.py @@ -1,3 +1,15 @@ +# -*- coding: utf-8 -*- +""" + Flaskr + ~~~~~~ + + A microblog example application written as Flask tutorial with + Flask and sqlite3. + + :copyright: (c) 2015 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" + from flaskr.factory import create_app app = create_app() \ No newline at end of file diff --git a/examples/flaskr/flaskr/factory.py b/examples/flaskr/flaskr/factory.py index e71a6704..a0f2d235 100644 --- a/examples/flaskr/flaskr/factory.py +++ b/examples/flaskr/flaskr/factory.py @@ -1,3 +1,15 @@ +# -*- coding: utf-8 -*- +""" + Flaskr + ~~~~~~ + + A microblog example application written as Flask tutorial with + Flask and sqlite3. + + :copyright: (c) 2015 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" + import os from flask import Flask, g from werkzeug.utils import find_modules, import_string diff --git a/examples/flaskr/setup.cfg b/examples/flaskr/setup.cfg index db50667a..9af7e6f1 100644 --- a/examples/flaskr/setup.cfg +++ b/examples/flaskr/setup.cfg @@ -1,2 +1,2 @@ -[tool:pytest] -test=pytest +[aliases] +test=pytest \ No newline at end of file diff --git a/examples/flaskr/setup.py b/examples/flaskr/setup.py index f52aacd8..7f1dae53 100644 --- a/examples/flaskr/setup.py +++ b/examples/flaskr/setup.py @@ -1,3 +1,14 @@ +# -*- coding: utf-8 -*- +""" + Flaskr Tests + ~~~~~~~~~~~~ + + Tests the Flaskr application. + + :copyright: (c) 2015 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" + from setuptools import setup, find_packages setup( diff --git a/examples/flaskr/tests/test_flaskr.py b/examples/flaskr/tests/test_flaskr.py index c8115829..b5ade2ec 100644 --- a/examples/flaskr/tests/test_flaskr.py +++ b/examples/flaskr/tests/test_flaskr.py @@ -16,20 +16,31 @@ from flaskr.factory import create_app from flaskr.blueprints.flaskr import init_db -test_app = create_app() +@pytest.fixture +def app(request): + db_fd, temp_db_location = tempfile.mkstemp() + config = { + 'DATABASE': temp_db_location, + 'TESTING': True, + 'DB_FD': db_fd + } -@pytest.fixture -def client(request): - db_fd, test_app.config['DATABASE'] = tempfile.mkstemp() - test_app.config['TESTING'] = True - client = test_app.test_client() - with test_app.app_context(): + app = create_app(config=config) + + with app.app_context(): init_db() + yield app + + +@pytest.fixture +def client(request, app): + + client = app.test_client() def teardown(): - os.close(db_fd) - os.unlink(test_app.config['DATABASE']) + os.close(app.config['DB_FD']) + os.unlink(app.config['DATABASE']) request.addfinalizer(teardown) return client @@ -52,25 +63,25 @@ def test_empty_db(client): assert b'No entries here so far' in rv.data -def test_login_logout(client): +def test_login_logout(client, app): """Make sure login and logout works""" - rv = login(client, test_app.config['USERNAME'], - test_app.config['PASSWORD']) + rv = login(client, app.config['USERNAME'], + app.config['PASSWORD']) assert b'You were logged in' in rv.data rv = logout(client) assert b'You were logged out' in rv.data - rv = login(client,test_app.config['USERNAME'] + 'x', - test_app.config['PASSWORD']) + rv = login(client,app.config['USERNAME'] + 'x', + app.config['PASSWORD']) assert b'Invalid username' in rv.data - rv = login(client, test_app.config['USERNAME'], - test_app.config['PASSWORD'] + 'x') + rv = login(client, app.config['USERNAME'], + app.config['PASSWORD'] + 'x') assert b'Invalid password' in rv.data -def test_messages(client): +def test_messages(client, app): """Test that messages work""" - login(client, test_app.config['USERNAME'], - test_app.config['PASSWORD']) + login(client, app.config['USERNAME'], + app.config['PASSWORD']) rv = client.post('/add', data=dict( title='', text='HTML allowed here'