From 5b309831ec595ad21c491c257c8ecf60f10c1ddd Mon Sep 17 00:00:00 2001 From: David Baumgold Date: Mon, 6 May 2019 15:39:19 -0400 Subject: [PATCH 1/2] Set up pre-commit to run black Call it automatically via tox --- .pre-commit-config.yaml | 5 +++++ tox.ini | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..e6e23c87 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,5 @@ +repos: + - repo: https://github.com/python/black + rev: 19.3b0 + hooks: + - id: black diff --git a/tox.ini b/tox.ini index d37d2486..22848681 100644 --- a/tox.ini +++ b/tox.ini @@ -37,6 +37,11 @@ commands = # pytest-cov doesn't seem to play nice with -p coverage run -p -m pytest tests examples +[testenv:stylecheck] +deps = pre-commit +skip_install = true +commands = pre-commit run --all-files --show-diff-on-failure + [testenv:docs-html] deps = -r docs/requirements.txt From 025589ee766249652e2e097da05808fe64911ddc Mon Sep 17 00:00:00 2001 From: David Baumgold Date: Mon, 6 May 2019 15:39:41 -0400 Subject: [PATCH 2/2] Reformat with black https://github.com/python/black --- examples/javascript/js_example/views.py | 12 +- examples/javascript/setup.py | 28 +- examples/javascript/tests/conftest.py | 2 +- examples/javascript/tests/test_js_example.py | 28 +- examples/tutorial/flaskr/__init__.py | 14 +- examples/tutorial/flaskr/auth.py | 77 +- examples/tutorial/flaskr/blog.py | 76 +- examples/tutorial/flaskr/db.py | 15 +- examples/tutorial/setup.py | 27 +- examples/tutorial/tests/conftest.py | 16 +- examples/tutorial/tests/test_auth.py | 51 +- examples/tutorial/tests/test_blog.py | 60 +- examples/tutorial/tests/test_db.py | 10 +- examples/tutorial/tests/test_factory.py | 6 +- flask/__init__.py | 51 +- flask/__main__.py | 3 +- flask/_compat.py | 16 +- flask/app.py | 428 ++--- flask/blueprints.py | 167 +- flask/cli.py | 339 ++-- flask/config.py | 33 +- flask/ctx.py | 51 +- flask/debughelpers.py | 115 +- flask/globals.py | 14 +- flask/helpers.py | 289 ++-- flask/json/__init__.py | 106 +- flask/json/tag.py | 32 +- flask/logging.py | 10 +- flask/sessions.py | 78 +- flask/signals.py | 40 +- flask/templating.py | 26 +- flask/testing.py | 82 +- flask/views.py | 14 +- flask/wrappers.py | 20 +- scripts/make-release.py | 74 +- setup.py | 112 +- tests/conftest.py | 60 +- tests/test_appctx.py | 86 +- tests/test_apps/blueprintapp/__init__.py | 3 +- .../blueprintapp/apps/admin/__init__.py | 18 +- .../blueprintapp/apps/frontend/__init__.py | 10 +- tests/test_apps/cliapp/app.py | 2 +- tests/test_apps/cliapp/factory.py | 6 +- tests/test_apps/cliapp/importerrorapp.py | 2 +- tests/test_apps/cliapp/multiapp.py | 4 +- tests/test_apps/helloworld/hello.py | 2 + .../test_apps/subdomaintestmodule/__init__.py | 2 +- tests/test_basic.py | 1412 ++++++++--------- tests/test_blueprints.py | 632 ++++---- tests/test_cli.py | 349 ++-- tests/test_config.py | 134 +- tests/test_helpers.py | 766 ++++----- tests/test_instance_config.py | 128 +- tests/test_json_tag.py | 39 +- tests/test_logging.py | 34 +- tests/test_regression.py | 33 +- tests/test_reqctx.py | 119 +- tests/test_signals.py | 98 +- tests/test_subclassing.py | 8 +- tests/test_templating.py | 316 ++-- tests/test_testing.py | 224 +-- tests/test_user_error_handler.py | 92 +- tests/test_views.py | 142 +- 63 files changed, 3784 insertions(+), 3459 deletions(-) diff --git a/examples/javascript/js_example/views.py b/examples/javascript/js_example/views.py index c898e6de..8efee858 100644 --- a/examples/javascript/js_example/views.py +++ b/examples/javascript/js_example/views.py @@ -3,14 +3,14 @@ from flask import jsonify, render_template, request from js_example import app -@app.route('/', defaults={'js': 'plain'}) -@app.route('/') +@app.route("/", defaults={"js": "plain"}) +@app.route("/") def index(js): - return render_template('{0}.html'.format(js), js=js) + return render_template("{0}.html".format(js), js=js) -@app.route('/add', methods=['POST']) +@app.route("/add", methods=["POST"]) def add(): - a = request.form.get('a', 0, type=float) - b = request.form.get('b', 0, type=float) + a = request.form.get("a", 0, type=float) + b = request.form.get("b", 0, type=float) return jsonify(result=a + b) diff --git a/examples/javascript/setup.py b/examples/javascript/setup.py index c699e8f1..89b8373b 100644 --- a/examples/javascript/setup.py +++ b/examples/javascript/setup.py @@ -2,29 +2,21 @@ import io from setuptools import find_packages, setup -with io.open('README.rst', 'rt', encoding='utf8') as f: +with io.open("README.rst", "rt", encoding="utf8") as f: readme = f.read() setup( - name='js_example', - version='1.0.0', - url='http://flask.pocoo.org/docs/patterns/jquery/', - license='BSD', - maintainer='Pallets team', - maintainer_email='contact@palletsprojects.com', - description='Demonstrates making Ajax requests to Flask.', + name="js_example", + version="1.0.0", + url="http://flask.pocoo.org/docs/patterns/jquery/", + license="BSD", + maintainer="Pallets team", + maintainer_email="contact@palletsprojects.com", + description="Demonstrates making Ajax requests to Flask.", long_description=readme, packages=find_packages(), include_package_data=True, zip_safe=False, - install_requires=[ - 'flask', - ], - extras_require={ - 'test': [ - 'pytest', - 'coverage', - 'blinker', - ], - }, + install_requires=["flask"], + extras_require={"test": ["pytest", "coverage", "blinker"]}, ) diff --git a/examples/javascript/tests/conftest.py b/examples/javascript/tests/conftest.py index ea81ba59..e0cabbfd 100644 --- a/examples/javascript/tests/conftest.py +++ b/examples/javascript/tests/conftest.py @@ -3,7 +3,7 @@ import pytest from js_example import app -@pytest.fixture(name='app') +@pytest.fixture(name="app") def fixture_app(): app.testing = True yield app diff --git a/examples/javascript/tests/test_js_example.py b/examples/javascript/tests/test_js_example.py index f1bcf96d..4923f827 100644 --- a/examples/javascript/tests/test_js_example.py +++ b/examples/javascript/tests/test_js_example.py @@ -3,12 +3,15 @@ import pytest from flask import template_rendered -@pytest.mark.parametrize(('path', 'template_name'), ( - ('/', 'plain.html'), - ('/plain', 'plain.html'), - ('/fetch', 'fetch.html'), - ('/jquery', 'jquery.html'), -)) +@pytest.mark.parametrize( + ("path", "template_name"), + ( + ("/", "plain.html"), + ("/plain", "plain.html"), + ("/fetch", "fetch.html"), + ("/jquery", "jquery.html"), + ), +) def test_index(app, client, path, template_name): def check(sender, template, context): assert template.name == template_name @@ -17,12 +20,9 @@ def test_index(app, client, path, template_name): client.get(path) -@pytest.mark.parametrize(('a', 'b', 'result'), ( - (2, 3, 5), - (2.5, 3, 5.5), - (2, None, 2), - (2, 'b', 2), -)) +@pytest.mark.parametrize( + ("a", "b", "result"), ((2, 3, 5), (2.5, 3, 5.5), (2, None, 2), (2, "b", 2)) +) def test_add(client, a, b, result): - response = client.post('/add', data={'a': a, 'b': b}) - assert response.get_json()['result'] == result + response = client.post("/add", data={"a": a, "b": b}) + assert response.get_json()["result"] == result diff --git a/examples/tutorial/flaskr/__init__.py b/examples/tutorial/flaskr/__init__.py index 05316607..bb9cce5a 100644 --- a/examples/tutorial/flaskr/__init__.py +++ b/examples/tutorial/flaskr/__init__.py @@ -8,14 +8,14 @@ def create_app(test_config=None): app = Flask(__name__, instance_relative_config=True) app.config.from_mapping( # a default secret that should be overridden by instance config - SECRET_KEY='dev', + SECRET_KEY="dev", # store the database in the instance folder - DATABASE=os.path.join(app.instance_path, 'flaskr.sqlite'), + DATABASE=os.path.join(app.instance_path, "flaskr.sqlite"), ) if test_config is None: # load the instance config, if it exists, when not testing - app.config.from_pyfile('config.py', silent=True) + app.config.from_pyfile("config.py", silent=True) else: # load the test config if passed in app.config.update(test_config) @@ -26,16 +26,18 @@ def create_app(test_config=None): except OSError: pass - @app.route('/hello') + @app.route("/hello") def hello(): - return 'Hello, World!' + return "Hello, World!" # register the database commands from flaskr import db + db.init_app(app) # apply the blueprints to the app from flaskr import auth, blog + app.register_blueprint(auth.bp) app.register_blueprint(blog.bp) @@ -43,6 +45,6 @@ def create_app(test_config=None): # in another app, you might define a separate main index here with # app.route, while giving the blog blueprint a url_prefix, but for # the tutorial the blog will be the main index - app.add_url_rule('/', endpoint='index') + app.add_url_rule("/", endpoint="index") return app diff --git a/examples/tutorial/flaskr/auth.py b/examples/tutorial/flaskr/auth.py index e8cd2940..1b94b063 100644 --- a/examples/tutorial/flaskr/auth.py +++ b/examples/tutorial/flaskr/auth.py @@ -1,21 +1,29 @@ import functools from flask import ( - Blueprint, flash, g, redirect, render_template, request, session, url_for + Blueprint, + flash, + g, + redirect, + render_template, + request, + session, + url_for, ) from werkzeug.security import check_password_hash, generate_password_hash from flaskr.db import get_db -bp = Blueprint('auth', __name__, url_prefix='/auth') +bp = Blueprint("auth", __name__, url_prefix="/auth") def login_required(view): """View decorator that redirects anonymous users to the login page.""" + @functools.wraps(view) def wrapped_view(**kwargs): if g.user is None: - return redirect(url_for('auth.login')) + return redirect(url_for("auth.login")) return view(**kwargs) @@ -26,83 +34,84 @@ def login_required(view): def load_logged_in_user(): """If a user id is stored in the session, load the user object from the database into ``g.user``.""" - user_id = session.get('user_id') + user_id = session.get("user_id") if user_id is None: g.user = None else: - g.user = get_db().execute( - 'SELECT * FROM user WHERE id = ?', (user_id,) - ).fetchone() + g.user = ( + get_db().execute("SELECT * FROM user WHERE id = ?", (user_id,)).fetchone() + ) -@bp.route('/register', methods=('GET', 'POST')) +@bp.route("/register", methods=("GET", "POST")) def register(): """Register a new user. Validates that the username is not already taken. Hashes the password for security. """ - if request.method == 'POST': - username = request.form['username'] - password = request.form['password'] + if request.method == "POST": + username = request.form["username"] + password = request.form["password"] db = get_db() error = None if not username: - error = 'Username is required.' + error = "Username is required." elif not password: - error = 'Password is required.' - elif db.execute( - 'SELECT id FROM user WHERE username = ?', (username,) - ).fetchone() is not None: - error = 'User {0} is already registered.'.format(username) + error = "Password is required." + elif ( + db.execute("SELECT id FROM user WHERE username = ?", (username,)).fetchone() + is not None + ): + error = "User {0} is already registered.".format(username) if error is None: # the name is available, store it in the database and go to # the login page db.execute( - 'INSERT INTO user (username, password) VALUES (?, ?)', - (username, generate_password_hash(password)) + "INSERT INTO user (username, password) VALUES (?, ?)", + (username, generate_password_hash(password)), ) db.commit() - return redirect(url_for('auth.login')) + return redirect(url_for("auth.login")) flash(error) - return render_template('auth/register.html') + return render_template("auth/register.html") -@bp.route('/login', methods=('GET', 'POST')) +@bp.route("/login", methods=("GET", "POST")) def login(): """Log in a registered user by adding the user id to the session.""" - if request.method == 'POST': - username = request.form['username'] - password = request.form['password'] + if request.method == "POST": + username = request.form["username"] + password = request.form["password"] db = get_db() error = None user = db.execute( - 'SELECT * FROM user WHERE username = ?', (username,) + "SELECT * FROM user WHERE username = ?", (username,) ).fetchone() if user is None: - error = 'Incorrect username.' - elif not check_password_hash(user['password'], password): - error = 'Incorrect password.' + error = "Incorrect username." + elif not check_password_hash(user["password"], password): + error = "Incorrect password." if error is None: # store the user id in a new session and return to the index session.clear() - session['user_id'] = user['id'] - return redirect(url_for('index')) + session["user_id"] = user["id"] + return redirect(url_for("index")) flash(error) - return render_template('auth/login.html') + return render_template("auth/login.html") -@bp.route('/logout') +@bp.route("/logout") def logout(): """Clear the current session, including the stored user id.""" session.clear() - return redirect(url_for('index')) + return redirect(url_for("index")) diff --git a/examples/tutorial/flaskr/blog.py b/examples/tutorial/flaskr/blog.py index 784b1d8c..9804ca13 100644 --- a/examples/tutorial/flaskr/blog.py +++ b/examples/tutorial/flaskr/blog.py @@ -1,24 +1,22 @@ -from flask import ( - Blueprint, flash, g, redirect, render_template, request, url_for -) +from flask import Blueprint, flash, g, redirect, render_template, request, url_for from werkzeug.exceptions import abort from flaskr.auth import login_required from flaskr.db import get_db -bp = Blueprint('blog', __name__) +bp = Blueprint("blog", __name__) -@bp.route('/') +@bp.route("/") def index(): """Show all the posts, most recent first.""" db = get_db() posts = db.execute( - 'SELECT p.id, title, body, created, author_id, username' - ' FROM post p JOIN user u ON p.author_id = u.id' - ' ORDER BY created DESC' + "SELECT p.id, title, body, created, author_id, username" + " FROM post p JOIN user u ON p.author_id = u.id" + " ORDER BY created DESC" ).fetchall() - return render_template('blog/index.html', posts=posts) + return render_template("blog/index.html", posts=posts) def get_post(id, check_author=True): @@ -33,78 +31,80 @@ def get_post(id, check_author=True): :raise 404: if a post with the given id doesn't exist :raise 403: if the current user isn't the author """ - post = get_db().execute( - 'SELECT p.id, title, body, created, author_id, username' - ' FROM post p JOIN user u ON p.author_id = u.id' - ' WHERE p.id = ?', - (id,) - ).fetchone() + post = ( + get_db() + .execute( + "SELECT p.id, title, body, created, author_id, username" + " FROM post p JOIN user u ON p.author_id = u.id" + " WHERE p.id = ?", + (id,), + ) + .fetchone() + ) if post is None: abort(404, "Post id {0} doesn't exist.".format(id)) - if check_author and post['author_id'] != g.user['id']: + if check_author and post["author_id"] != g.user["id"]: abort(403) return post -@bp.route('/create', methods=('GET', 'POST')) +@bp.route("/create", methods=("GET", "POST")) @login_required def create(): """Create a new post for the current user.""" - if request.method == 'POST': - title = request.form['title'] - body = request.form['body'] + if request.method == "POST": + title = request.form["title"] + body = request.form["body"] error = None if not title: - error = 'Title is required.' + error = "Title is required." if error is not None: flash(error) else: db = get_db() db.execute( - 'INSERT INTO post (title, body, author_id)' - ' VALUES (?, ?, ?)', - (title, body, g.user['id']) + "INSERT INTO post (title, body, author_id)" " VALUES (?, ?, ?)", + (title, body, g.user["id"]), ) db.commit() - return redirect(url_for('blog.index')) + return redirect(url_for("blog.index")) - return render_template('blog/create.html') + return render_template("blog/create.html") -@bp.route('//update', methods=('GET', 'POST')) +@bp.route("//update", methods=("GET", "POST")) @login_required def update(id): """Update a post if the current user is the author.""" post = get_post(id) - if request.method == 'POST': - title = request.form['title'] - body = request.form['body'] + if request.method == "POST": + title = request.form["title"] + body = request.form["body"] error = None if not title: - error = 'Title is required.' + error = "Title is required." if error is not None: flash(error) else: db = get_db() db.execute( - 'UPDATE post SET title = ?, body = ? WHERE id = ?', - (title, body, id) + "UPDATE post SET title = ?, body = ? WHERE id = ?", (title, body, id) ) db.commit() - return redirect(url_for('blog.index')) + return redirect(url_for("blog.index")) - return render_template('blog/update.html', post=post) + return render_template("blog/update.html", post=post) -@bp.route('//delete', methods=('POST',)) +@bp.route("//delete", methods=("POST",)) @login_required def delete(id): """Delete a post. @@ -114,6 +114,6 @@ def delete(id): """ get_post(id) db = get_db() - db.execute('DELETE FROM post WHERE id = ?', (id,)) + db.execute("DELETE FROM post WHERE id = ?", (id,)) db.commit() - return redirect(url_for('blog.index')) + return redirect(url_for("blog.index")) diff --git a/examples/tutorial/flaskr/db.py b/examples/tutorial/flaskr/db.py index 03bd3b3c..36b7f514 100644 --- a/examples/tutorial/flaskr/db.py +++ b/examples/tutorial/flaskr/db.py @@ -10,10 +10,9 @@ def get_db(): is unique for each request and will be reused if this is called again. """ - if 'db' not in g: + if "db" not in g: g.db = sqlite3.connect( - current_app.config['DATABASE'], - detect_types=sqlite3.PARSE_DECLTYPES + current_app.config["DATABASE"], detect_types=sqlite3.PARSE_DECLTYPES ) g.db.row_factory = sqlite3.Row @@ -24,7 +23,7 @@ def close_db(e=None): """If this request connected to the database, close the connection. """ - db = g.pop('db', None) + db = g.pop("db", None) if db is not None: db.close() @@ -34,16 +33,16 @@ def init_db(): """Clear existing data and create new tables.""" db = get_db() - with current_app.open_resource('schema.sql') as f: - db.executescript(f.read().decode('utf8')) + with current_app.open_resource("schema.sql") as f: + db.executescript(f.read().decode("utf8")) -@click.command('init-db') +@click.command("init-db") @with_appcontext def init_db_command(): """Clear existing data and create new tables.""" init_db() - click.echo('Initialized the database.') + click.echo("Initialized the database.") def init_app(app): diff --git a/examples/tutorial/setup.py b/examples/tutorial/setup.py index 460d789b..b6ac73e9 100644 --- a/examples/tutorial/setup.py +++ b/examples/tutorial/setup.py @@ -2,28 +2,21 @@ import io from setuptools import find_packages, setup -with io.open('README.rst', 'rt', encoding='utf8') as f: +with io.open("README.rst", "rt", encoding="utf8") as f: readme = f.read() setup( - name='flaskr', - version='1.0.0', - url='http://flask.pocoo.org/docs/tutorial/', - license='BSD', - maintainer='Pallets team', - maintainer_email='contact@palletsprojects.com', - description='The basic blog app built in the Flask tutorial.', + name="flaskr", + version="1.0.0", + url="http://flask.pocoo.org/docs/tutorial/", + license="BSD", + maintainer="Pallets team", + maintainer_email="contact@palletsprojects.com", + description="The basic blog app built in the Flask tutorial.", long_description=readme, packages=find_packages(), include_package_data=True, zip_safe=False, - install_requires=[ - 'flask', - ], - extras_require={ - 'test': [ - 'pytest', - 'coverage', - ], - }, + install_requires=["flask"], + extras_require={"test": ["pytest", "coverage"]}, ) diff --git a/examples/tutorial/tests/conftest.py b/examples/tutorial/tests/conftest.py index 143d6924..869b19e7 100644 --- a/examples/tutorial/tests/conftest.py +++ b/examples/tutorial/tests/conftest.py @@ -6,8 +6,8 @@ from flaskr import create_app from flaskr.db import get_db, init_db # read in SQL for populating test data -with open(os.path.join(os.path.dirname(__file__), 'data.sql'), 'rb') as f: - _data_sql = f.read().decode('utf8') +with open(os.path.join(os.path.dirname(__file__), "data.sql"), "rb") as f: + _data_sql = f.read().decode("utf8") @pytest.fixture @@ -16,10 +16,7 @@ def app(): # create a temporary file to isolate the database for each test db_fd, db_path = tempfile.mkstemp() # create the app with common test config - app = create_app({ - 'TESTING': True, - 'DATABASE': db_path, - }) + app = create_app({"TESTING": True, "DATABASE": db_path}) # create the database and load test data with app.app_context(): @@ -49,14 +46,13 @@ class AuthActions(object): def __init__(self, client): self._client = client - def login(self, username='test', password='test'): + def login(self, username="test", password="test"): return self._client.post( - '/auth/login', - data={'username': username, 'password': password} + "/auth/login", data={"username": username, "password": password} ) def logout(self): - return self._client.get('/auth/logout') + return self._client.get("/auth/logout") @pytest.fixture diff --git a/examples/tutorial/tests/test_auth.py b/examples/tutorial/tests/test_auth.py index 884bdf49..7b7b84fc 100644 --- a/examples/tutorial/tests/test_auth.py +++ b/examples/tutorial/tests/test_auth.py @@ -5,54 +5,55 @@ from flaskr.db import get_db def test_register(client, app): # test that viewing the page renders without template errors - assert client.get('/auth/register').status_code == 200 + assert client.get("/auth/register").status_code == 200 # test that successful registration redirects to the login page - response = client.post( - '/auth/register', data={'username': 'a', 'password': 'a'} - ) - assert 'http://localhost/auth/login' == response.headers['Location'] + response = client.post("/auth/register", data={"username": "a", "password": "a"}) + assert "http://localhost/auth/login" == response.headers["Location"] # test that the user was inserted into the database with app.app_context(): - assert get_db().execute( - "select * from user where username = 'a'", - ).fetchone() is not None + assert ( + get_db().execute("select * from user where username = 'a'").fetchone() + is not None + ) -@pytest.mark.parametrize(('username', 'password', 'message'), ( - ('', '', b'Username is required.'), - ('a', '', b'Password is required.'), - ('test', 'test', b'already registered'), -)) +@pytest.mark.parametrize( + ("username", "password", "message"), + ( + ("", "", b"Username is required."), + ("a", "", b"Password is required."), + ("test", "test", b"already registered"), + ), +) def test_register_validate_input(client, username, password, message): response = client.post( - '/auth/register', - data={'username': username, 'password': password} + "/auth/register", data={"username": username, "password": password} ) assert message in response.data def test_login(client, auth): # test that viewing the page renders without template errors - assert client.get('/auth/login').status_code == 200 + assert client.get("/auth/login").status_code == 200 # test that successful login redirects to the index page response = auth.login() - assert response.headers['Location'] == 'http://localhost/' + assert response.headers["Location"] == "http://localhost/" # login request set the user_id in the session # check that the user is loaded from the session with client: - client.get('/') - assert session['user_id'] == 1 - assert g.user['username'] == 'test' + client.get("/") + assert session["user_id"] == 1 + assert g.user["username"] == "test" -@pytest.mark.parametrize(('username', 'password', 'message'), ( - ('a', 'test', b'Incorrect username.'), - ('test', 'a', b'Incorrect password.'), -)) +@pytest.mark.parametrize( + ("username", "password", "message"), + (("a", "test", b"Incorrect username."), ("test", "a", b"Incorrect password.")), +) def test_login_validate_input(auth, username, password, message): response = auth.login(username, password) assert message in response.data @@ -63,4 +64,4 @@ def test_logout(client, auth): with client: auth.logout() - assert 'user_id' not in session + assert "user_id" not in session diff --git a/examples/tutorial/tests/test_blog.py b/examples/tutorial/tests/test_blog.py index 11700458..29468742 100644 --- a/examples/tutorial/tests/test_blog.py +++ b/examples/tutorial/tests/test_blog.py @@ -3,47 +3,40 @@ from flaskr.db import get_db def test_index(client, auth): - response = client.get('/') + response = client.get("/") assert b"Log In" in response.data assert b"Register" in response.data auth.login() - response = client.get('/') - assert b'test title' in response.data - assert b'by test on 2018-01-01' in response.data - assert b'test\nbody' in response.data + response = client.get("/") + assert b"test title" in response.data + assert b"by test on 2018-01-01" in response.data + assert b"test\nbody" in response.data assert b'href="/1/update"' in response.data -@pytest.mark.parametrize('path', ( - '/create', - '/1/update', - '/1/delete', -)) +@pytest.mark.parametrize("path", ("/create", "/1/update", "/1/delete")) def test_login_required(client, path): response = client.post(path) - assert response.headers['Location'] == 'http://localhost/auth/login' + assert response.headers["Location"] == "http://localhost/auth/login" def test_author_required(app, client, auth): # change the post author to another user with app.app_context(): db = get_db() - db.execute('UPDATE post SET author_id = 2 WHERE id = 1') + db.execute("UPDATE post SET author_id = 2 WHERE id = 1") db.commit() auth.login() # current user can't modify other user's post - assert client.post('/1/update').status_code == 403 - assert client.post('/1/delete').status_code == 403 + assert client.post("/1/update").status_code == 403 + assert client.post("/1/delete").status_code == 403 # current user doesn't see edit link - assert b'href="/1/update"' not in client.get('/').data + assert b'href="/1/update"' not in client.get("/").data -@pytest.mark.parametrize('path', ( - '/2/update', - '/2/delete', -)) +@pytest.mark.parametrize("path", ("/2/update", "/2/delete")) def test_exists_required(client, auth, path): auth.login() assert client.post(path).status_code == 404 @@ -51,42 +44,39 @@ def test_exists_required(client, auth, path): def test_create(client, auth, app): auth.login() - assert client.get('/create').status_code == 200 - client.post('/create', data={'title': 'created', 'body': ''}) + assert client.get("/create").status_code == 200 + client.post("/create", data={"title": "created", "body": ""}) with app.app_context(): db = get_db() - count = db.execute('SELECT COUNT(id) FROM post').fetchone()[0] + count = db.execute("SELECT COUNT(id) FROM post").fetchone()[0] assert count == 2 def test_update(client, auth, app): auth.login() - assert client.get('/1/update').status_code == 200 - client.post('/1/update', data={'title': 'updated', 'body': ''}) + assert client.get("/1/update").status_code == 200 + client.post("/1/update", data={"title": "updated", "body": ""}) with app.app_context(): db = get_db() - post = db.execute('SELECT * FROM post WHERE id = 1').fetchone() - assert post['title'] == 'updated' + post = db.execute("SELECT * FROM post WHERE id = 1").fetchone() + assert post["title"] == "updated" -@pytest.mark.parametrize('path', ( - '/create', - '/1/update', -)) +@pytest.mark.parametrize("path", ("/create", "/1/update")) def test_create_update_validate(client, auth, path): auth.login() - response = client.post(path, data={'title': '', 'body': ''}) - assert b'Title is required.' in response.data + response = client.post(path, data={"title": "", "body": ""}) + assert b"Title is required." in response.data def test_delete(client, auth, app): auth.login() - response = client.post('/1/delete') - assert response.headers['Location'] == 'http://localhost/' + response = client.post("/1/delete") + assert response.headers["Location"] == "http://localhost/" with app.app_context(): db = get_db() - post = db.execute('SELECT * FROM post WHERE id = 1').fetchone() + post = db.execute("SELECT * FROM post WHERE id = 1").fetchone() assert post is None diff --git a/examples/tutorial/tests/test_db.py b/examples/tutorial/tests/test_db.py index 99c46d04..59eaddff 100644 --- a/examples/tutorial/tests/test_db.py +++ b/examples/tutorial/tests/test_db.py @@ -10,9 +10,9 @@ def test_get_close_db(app): assert db is get_db() with pytest.raises(sqlite3.ProgrammingError) as e: - db.execute('SELECT 1') + db.execute("SELECT 1") - assert 'closed' in str(e) + assert "closed" in str(e) def test_init_db_command(runner, monkeypatch): @@ -22,7 +22,7 @@ def test_init_db_command(runner, monkeypatch): def fake_init_db(): Recorder.called = True - monkeypatch.setattr('flaskr.db.init_db', fake_init_db) - result = runner.invoke(args=['init-db']) - assert 'Initialized' in result.output + monkeypatch.setattr("flaskr.db.init_db", fake_init_db) + result = runner.invoke(args=["init-db"]) + assert "Initialized" in result.output assert Recorder.called diff --git a/examples/tutorial/tests/test_factory.py b/examples/tutorial/tests/test_factory.py index b7afeae7..9b7ca57f 100644 --- a/examples/tutorial/tests/test_factory.py +++ b/examples/tutorial/tests/test_factory.py @@ -4,9 +4,9 @@ from flaskr import create_app def test_config(): """Test create_app without passing test config.""" assert not create_app().testing - assert create_app({'TESTING': True}).testing + assert create_app({"TESTING": True}).testing def test_hello(client): - response = client.get('/hello') - assert response.data == b'Hello, World!' + response = client.get("/hello") + assert response.data == b"Hello, World!" diff --git a/flask/__init__.py b/flask/__init__.py index 2e5670f5..173b7f28 100644 --- a/flask/__init__.py +++ b/flask/__init__.py @@ -10,7 +10,7 @@ :license: BSD, see LICENSE for more details. """ -__version__ = '1.1.dev' +__version__ = "1.1.dev" # utilities we import from Werkzeug and Jinja2 that are unused # in the module but are exported as public interface. @@ -20,21 +20,48 @@ from jinja2 import Markup, escape from .app import Flask, Request, Response from .config import Config -from .helpers import url_for, flash, send_file, send_from_directory, \ - get_flashed_messages, get_template_attribute, make_response, safe_join, \ - stream_with_context -from .globals import current_app, g, request, session, _request_ctx_stack, \ - _app_ctx_stack -from .ctx import has_request_context, has_app_context, \ - after_this_request, copy_current_request_context +from .helpers import ( + url_for, + flash, + send_file, + send_from_directory, + get_flashed_messages, + get_template_attribute, + make_response, + safe_join, + stream_with_context, +) +from .globals import ( + current_app, + g, + request, + session, + _request_ctx_stack, + _app_ctx_stack, +) +from .ctx import ( + has_request_context, + has_app_context, + after_this_request, + copy_current_request_context, +) from .blueprints import Blueprint from .templating import render_template, render_template_string # the signals -from .signals import signals_available, template_rendered, request_started, \ - request_finished, got_request_exception, request_tearing_down, \ - appcontext_tearing_down, appcontext_pushed, \ - appcontext_popped, message_flashed, before_render_template +from .signals import ( + signals_available, + template_rendered, + request_started, + request_finished, + got_request_exception, + request_tearing_down, + appcontext_tearing_down, + appcontext_pushed, + appcontext_popped, + message_flashed, + before_render_template, +) # We're not exposing the actual json module but a convenient wrapper around # it. diff --git a/flask/__main__.py b/flask/__main__.py index 4aee6543..db25bbd2 100644 --- a/flask/__main__.py +++ b/flask/__main__.py @@ -9,6 +9,7 @@ :license: BSD, see LICENSE for more details. """ -if __name__ == '__main__': +if __name__ == "__main__": from .cli import main + main(as_module=True) diff --git a/flask/_compat.py b/flask/_compat.py index 85aeee01..952af569 100644 --- a/flask/_compat.py +++ b/flask/_compat.py @@ -50,11 +50,11 @@ else: from cStringIO import StringIO import collections as collections_abc - exec('def reraise(tp, value, tb=None):\n raise tp, value, tb') + exec("def reraise(tp, value, tb=None):\n raise tp, value, tb") def implements_to_string(cls): cls.__unicode__ = cls.__str__ - cls.__str__ = lambda x: x.__unicode__().encode('utf-8') + cls.__str__ = lambda x: x.__unicode__().encode("utf-8") return cls @@ -66,7 +66,8 @@ def with_metaclass(meta, *bases): class metaclass(type): def __new__(cls, name, this_bases, d): return meta(name, bases, d) - return type.__new__(metaclass, 'temporary_class', (), {}) + + return type.__new__(metaclass, "temporary_class", (), {}) # Certain versions of pypy have a bug where clearing the exception stack @@ -81,14 +82,17 @@ def with_metaclass(meta, *bases): # # Ubuntu 14.04 has PyPy 2.2.1, which does exhibit this bug. BROKEN_PYPY_CTXMGR_EXIT = False -if hasattr(sys, 'pypy_version_info'): +if hasattr(sys, "pypy_version_info"): + class _Mgr(object): def __enter__(self): return self + def __exit__(self, *args): - if hasattr(sys, 'exc_clear'): + if hasattr(sys, "exc_clear"): # Python 3 (PyPy3) doesn't have exc_clear sys.exc_clear() + try: try: with _Mgr(): @@ -107,4 +111,4 @@ except ImportError: # Backwards compatibility as proposed in PEP 0519: # https://www.python.org/dev/peps/pep-0519/#backwards-compatibility def fspath(path): - return path.__fspath__() if hasattr(path, '__fspath__') else path + return path.__fspath__() if hasattr(path, "__fspath__") else path diff --git a/flask/app.py b/flask/app.py index 92731cca..6e77bd91 100644 --- a/flask/app.py +++ b/flask/app.py @@ -18,10 +18,15 @@ from itertools import chain from threading import Lock from werkzeug.datastructures import Headers, ImmutableDict -from werkzeug.exceptions import BadRequest, BadRequestKeyError, HTTPException, \ - InternalServerError, MethodNotAllowed, default_exceptions -from werkzeug.routing import BuildError, Map, RequestRedirect, \ - RoutingException, Rule +from werkzeug.exceptions import ( + BadRequest, + BadRequestKeyError, + HTTPException, + InternalServerError, + MethodNotAllowed, + default_exceptions, +) +from werkzeug.routing import BuildError, Map, RequestRedirect, RoutingException, Rule from . import cli, json from ._compat import integer_types, reraise, string_types, text_type @@ -30,15 +35,29 @@ from .ctx import AppContext, RequestContext, _AppCtxGlobals from .globals import _request_ctx_stack, g, request, session from .helpers import ( _PackageBoundObject, - _endpoint_from_view_func, find_package, get_env, get_debug_flag, - get_flashed_messages, locked_cached_property, url_for, get_load_dotenv + _endpoint_from_view_func, + find_package, + get_env, + get_debug_flag, + get_flashed_messages, + locked_cached_property, + url_for, + get_load_dotenv, ) from .logging import create_logger from .sessions import SecureCookieSessionInterface -from .signals import appcontext_tearing_down, got_request_exception, \ - request_finished, request_started, request_tearing_down -from .templating import DispatchingJinjaLoader, Environment, \ - _default_template_ctx_processor +from .signals import ( + appcontext_tearing_down, + got_request_exception, + request_finished, + request_started, + request_tearing_down, +) +from .templating import ( + DispatchingJinjaLoader, + Environment, + _default_template_ctx_processor, +) from .wrappers import Request, Response # a singleton sentinel value for parameter defaults @@ -55,16 +74,20 @@ def setupmethod(f): """Wraps a method so that it performs a check in debug mode if the first request was already handled. """ + def wrapper_func(self, *args, **kwargs): if self.debug and self._got_first_request: - raise AssertionError('A setup function was called after the ' - 'first request was handled. This usually indicates a bug ' - 'in the application where a module was not imported ' - 'and decorators or other functionality was called too late.\n' - 'To fix this make sure to import all your view modules, ' - 'database models and everything related at a central place ' - 'before the application starts serving requests.') + raise AssertionError( + "A setup function was called after the " + "first request was handled. This usually indicates a bug " + "in the application where a module was not imported " + "and decorators or other functionality was called too late.\n" + "To fix this make sure to import all your view modules, " + "database models and everything related at a central place " + "before the application starts serving requests." + ) return f(self, *args, **kwargs) + return update_wrapper(wrapper_func, f) @@ -217,7 +240,7 @@ class Flask(_PackageBoundObject): #: #: This attribute can also be configured from the config with the #: ``TESTING`` configuration key. Defaults to ``False``. - testing = ConfigAttribute('TESTING') + testing = ConfigAttribute("TESTING") #: If a secret key is set, cryptographic components can use this to #: sign cookies and other things. Set this to a complex random value @@ -225,13 +248,13 @@ class Flask(_PackageBoundObject): #: #: This attribute can also be configured from the config with the #: :data:`SECRET_KEY` configuration key. Defaults to ``None``. - secret_key = ConfigAttribute('SECRET_KEY') + secret_key = ConfigAttribute("SECRET_KEY") #: The secure cookie uses this for the name of the session cookie. #: #: This attribute can also be configured from the config with the #: ``SESSION_COOKIE_NAME`` configuration key. Defaults to ``'session'`` - session_cookie_name = ConfigAttribute('SESSION_COOKIE_NAME') + session_cookie_name = ConfigAttribute("SESSION_COOKIE_NAME") #: A :class:`~datetime.timedelta` which is used to set the expiration #: date of a permanent session. The default is 31 days which makes a @@ -240,8 +263,9 @@ class Flask(_PackageBoundObject): #: This attribute can also be configured from the config with the #: ``PERMANENT_SESSION_LIFETIME`` configuration key. Defaults to #: ``timedelta(days=31)`` - permanent_session_lifetime = ConfigAttribute('PERMANENT_SESSION_LIFETIME', - get_converter=_make_timedelta) + permanent_session_lifetime = ConfigAttribute( + "PERMANENT_SESSION_LIFETIME", get_converter=_make_timedelta + ) #: A :class:`~datetime.timedelta` which is used as default cache_timeout #: for the :func:`send_file` functions. The default is 12 hours. @@ -250,8 +274,9 @@ class Flask(_PackageBoundObject): #: ``SEND_FILE_MAX_AGE_DEFAULT`` configuration key. This configuration #: variable can also be set with an integer value used as seconds. #: Defaults to ``timedelta(hours=12)`` - send_file_max_age_default = ConfigAttribute('SEND_FILE_MAX_AGE_DEFAULT', - get_converter=_make_timedelta) + send_file_max_age_default = ConfigAttribute( + "SEND_FILE_MAX_AGE_DEFAULT", get_converter=_make_timedelta + ) #: Enable this if you want to use the X-Sendfile feature. Keep in #: mind that the server has to support this. This only affects files @@ -261,7 +286,7 @@ class Flask(_PackageBoundObject): #: #: This attribute can also be configured from the config with the #: ``USE_X_SENDFILE`` configuration key. Defaults to ``False``. - use_x_sendfile = ConfigAttribute('USE_X_SENDFILE') + use_x_sendfile = ConfigAttribute("USE_X_SENDFILE") #: The JSON encoder class to use. Defaults to :class:`~flask.json.JSONEncoder`. #: @@ -275,41 +300,43 @@ class Flask(_PackageBoundObject): #: Options that are passed directly to the Jinja2 environment. jinja_options = ImmutableDict( - extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_'] + extensions=["jinja2.ext.autoescape", "jinja2.ext.with_"] ) #: Default configuration parameters. - default_config = ImmutableDict({ - 'ENV': None, - 'DEBUG': None, - 'TESTING': False, - 'PROPAGATE_EXCEPTIONS': None, - 'PRESERVE_CONTEXT_ON_EXCEPTION': None, - 'SECRET_KEY': None, - 'PERMANENT_SESSION_LIFETIME': timedelta(days=31), - 'USE_X_SENDFILE': False, - 'SERVER_NAME': None, - 'APPLICATION_ROOT': '/', - 'SESSION_COOKIE_NAME': 'session', - 'SESSION_COOKIE_DOMAIN': None, - 'SESSION_COOKIE_PATH': None, - 'SESSION_COOKIE_HTTPONLY': True, - 'SESSION_COOKIE_SECURE': False, - 'SESSION_COOKIE_SAMESITE': None, - 'SESSION_REFRESH_EACH_REQUEST': True, - 'MAX_CONTENT_LENGTH': None, - 'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12), - 'TRAP_BAD_REQUEST_ERRORS': None, - 'TRAP_HTTP_EXCEPTIONS': False, - 'EXPLAIN_TEMPLATE_LOADING': False, - 'PREFERRED_URL_SCHEME': 'http', - 'JSON_AS_ASCII': True, - 'JSON_SORT_KEYS': True, - 'JSONIFY_PRETTYPRINT_REGULAR': False, - 'JSONIFY_MIMETYPE': 'application/json', - 'TEMPLATES_AUTO_RELOAD': None, - 'MAX_COOKIE_SIZE': 4093, - }) + default_config = ImmutableDict( + { + "ENV": None, + "DEBUG": None, + "TESTING": False, + "PROPAGATE_EXCEPTIONS": None, + "PRESERVE_CONTEXT_ON_EXCEPTION": None, + "SECRET_KEY": None, + "PERMANENT_SESSION_LIFETIME": timedelta(days=31), + "USE_X_SENDFILE": False, + "SERVER_NAME": None, + "APPLICATION_ROOT": "/", + "SESSION_COOKIE_NAME": "session", + "SESSION_COOKIE_DOMAIN": None, + "SESSION_COOKIE_PATH": None, + "SESSION_COOKIE_HTTPONLY": True, + "SESSION_COOKIE_SECURE": False, + "SESSION_COOKIE_SAMESITE": None, + "SESSION_REFRESH_EACH_REQUEST": True, + "MAX_CONTENT_LENGTH": None, + "SEND_FILE_MAX_AGE_DEFAULT": timedelta(hours=12), + "TRAP_BAD_REQUEST_ERRORS": None, + "TRAP_HTTP_EXCEPTIONS": False, + "EXPLAIN_TEMPLATE_LOADING": False, + "PREFERRED_URL_SCHEME": "http", + "JSON_AS_ASCII": True, + "JSON_SORT_KEYS": True, + "JSONIFY_PRETTYPRINT_REGULAR": False, + "JSONIFY_MIMETYPE": "application/json", + "TEMPLATES_AUTO_RELOAD": None, + "MAX_COOKIE_SIZE": 4093, + } + ) #: The rule object to use for URL rules created. This is used by #: :meth:`add_url_rule`. Defaults to :class:`werkzeug.routing.Rule`. @@ -355,20 +382,17 @@ class Flask(_PackageBoundObject): self, import_name, static_url_path=None, - static_folder='static', + static_folder="static", static_host=None, host_matching=False, subdomain_matching=False, - template_folder='templates', + template_folder="templates", instance_path=None, instance_relative_config=False, - root_path=None + root_path=None, ): _PackageBoundObject.__init__( - self, - import_name, - template_folder=template_folder, - root_path=root_path + self, import_name, template_folder=template_folder, root_path=root_path ) if static_url_path is not None: @@ -381,8 +405,8 @@ class Flask(_PackageBoundObject): instance_path = self.auto_find_instance_path() elif not os.path.isabs(instance_path): raise ValueError( - 'If an instance path is provided it must be absolute.' - ' A relative path was given instead.' + "If an instance path is provided it must be absolute." + " A relative path was given instead." ) #: Holds the path to the instance folder. @@ -490,9 +514,7 @@ class Flask(_PackageBoundObject): #: requests. Each returns a dictionary that the template context is #: updated with. To register a function here, use the #: :meth:`context_processor` decorator. - self.template_context_processors = { - None: [_default_template_ctx_processor] - } + self.template_context_processors = {None: [_default_template_ctx_processor]} #: A list of shell context processor functions that should be run #: when a shell context is created. @@ -555,12 +577,14 @@ class Flask(_PackageBoundObject): # For one, it might be created while the server is running (e.g. during # development). Also, Google App Engine stores static files somewhere if self.has_static_folder: - assert bool(static_host) == host_matching, 'Invalid static_host/host_matching combination' + assert ( + bool(static_host) == host_matching + ), "Invalid static_host/host_matching combination" self.add_url_rule( - self.static_url_path + '/', - endpoint='static', + self.static_url_path + "/", + endpoint="static", host=static_host, - view_func=self.send_static_file + view_func=self.send_static_file, ) #: The click command line context for this application. Commands @@ -581,10 +605,10 @@ class Flask(_PackageBoundObject): .. versionadded:: 0.8 """ - if self.import_name == '__main__': - fn = getattr(sys.modules['__main__'], '__file__', None) + if self.import_name == "__main__": + fn = getattr(sys.modules["__main__"], "__file__", None) if fn is None: - return '__main__' + return "__main__" return os.path.splitext(os.path.basename(fn))[0] return self.import_name @@ -595,7 +619,7 @@ class Flask(_PackageBoundObject): .. versionadded:: 0.7 """ - rv = self.config['PROPAGATE_EXCEPTIONS'] + rv = self.config["PROPAGATE_EXCEPTIONS"] if rv is not None: return rv return self.testing or self.debug @@ -608,7 +632,7 @@ class Flask(_PackageBoundObject): .. versionadded:: 0.7 """ - rv = self.config['PRESERVE_CONTEXT_ON_EXCEPTION'] + rv = self.config["PRESERVE_CONTEXT_ON_EXCEPTION"] if rv is not None: return rv return self.debug @@ -663,8 +687,8 @@ class Flask(_PackageBoundObject): if instance_relative: root_path = self.instance_path defaults = dict(self.default_config) - defaults['ENV'] = get_env() - defaults['DEBUG'] = get_debug_flag() + defaults["ENV"] = get_env() + defaults["DEBUG"] = get_debug_flag() return self.config_class(root_path, defaults) def auto_find_instance_path(self): @@ -677,10 +701,10 @@ class Flask(_PackageBoundObject): """ prefix, package_path = find_package(self.import_name) if prefix is None: - return os.path.join(package_path, 'instance') - return os.path.join(prefix, 'var', self.name + '-instance') + return os.path.join(package_path, "instance") + return os.path.join(prefix, "var", self.name + "-instance") - def open_instance_resource(self, resource, mode='rb'): + def open_instance_resource(self, resource, mode="rb"): """Opens a resource from the application's instance folder (:attr:`instance_path`). Otherwise works like :meth:`open_resource`. Instance resources can also be opened for @@ -703,11 +727,11 @@ class Flask(_PackageBoundObject): This property was added but the underlying config and behavior already existed. """ - rv = self.config['TEMPLATES_AUTO_RELOAD'] + rv = self.config["TEMPLATES_AUTO_RELOAD"] return rv if rv is not None else self.debug def _set_templates_auto_reload(self, value): - self.config['TEMPLATES_AUTO_RELOAD'] = value + self.config["TEMPLATES_AUTO_RELOAD"] = value templates_auto_reload = property( _get_templates_auto_reload, _set_templates_auto_reload @@ -727,11 +751,11 @@ class Flask(_PackageBoundObject): """ options = dict(self.jinja_options) - if 'autoescape' not in options: - options['autoescape'] = self.select_jinja_autoescape + if "autoescape" not in options: + options["autoescape"] = self.select_jinja_autoescape - if 'auto_reload' not in options: - options['auto_reload'] = self.templates_auto_reload + if "auto_reload" not in options: + options["auto_reload"] = self.templates_auto_reload rv = self.jinja_environment(self, **options) rv.globals.update( @@ -743,9 +767,9 @@ class Flask(_PackageBoundObject): # templates we also want the proxies in there. request=request, session=session, - g=g + g=g, ) - rv.filters['tojson'] = json.tojson_filter + rv.filters["tojson"] = json.tojson_filter return rv def create_global_jinja_loader(self): @@ -769,7 +793,7 @@ class Flask(_PackageBoundObject): """ if filename is None: return True - return filename.endswith(('.html', '.htm', '.xml', '.xhtml')) + return filename.endswith((".html", ".htm", ".xml", ".xhtml")) def update_template_context(self, context): """Update the template context with some commonly used variables. @@ -803,7 +827,7 @@ class Flask(_PackageBoundObject): .. versionadded:: 0.11 """ - rv = {'app': self, 'g': g} + rv = {"app": self, "g": g} for processor in self.shell_context_processors: rv.update(processor()) return rv @@ -817,13 +841,13 @@ class Flask(_PackageBoundObject): #: **Do not enable development when deploying in production.** #: #: Default: ``'production'`` - env = ConfigAttribute('ENV') + env = ConfigAttribute("ENV") def _get_debug(self): - return self.config['DEBUG'] + return self.config["DEBUG"] def _set_debug(self, value): - self.config['DEBUG'] = value + self.config["DEBUG"] = value self.jinja_env.auto_reload = self.templates_auto_reload #: Whether debug mode is enabled. When using ``flask run`` to start @@ -841,8 +865,7 @@ class Flask(_PackageBoundObject): debug = property(_get_debug, _set_debug) del _get_debug, _set_debug - def run(self, host=None, port=None, debug=None, - load_dotenv=True, **options): + def run(self, host=None, port=None, debug=None, load_dotenv=True, **options): """Runs the application on a local development server. Do not use ``run()`` in a production setting. It is not intended to @@ -902,8 +925,9 @@ class Flask(_PackageBoundObject): """ # Change this into a no-op if the server is invoked from the # command line. Have a look at cli.py for more information. - if os.environ.get('FLASK_RUN_FROM_CLI') == 'true': + if os.environ.get("FLASK_RUN_FROM_CLI") == "true": from .debughelpers import explain_ignored_app_run + explain_ignored_app_run() return @@ -911,30 +935,30 @@ class Flask(_PackageBoundObject): cli.load_dotenv() # if set, let env vars override previous values - if 'FLASK_ENV' in os.environ: + if "FLASK_ENV" in os.environ: self.env = get_env() self.debug = get_debug_flag() - elif 'FLASK_DEBUG' in os.environ: + elif "FLASK_DEBUG" in os.environ: self.debug = get_debug_flag() # debug passed to method overrides all other sources if debug is not None: self.debug = bool(debug) - _host = '127.0.0.1' + _host = "127.0.0.1" _port = 5000 - server_name = self.config.get('SERVER_NAME') + server_name = self.config.get("SERVER_NAME") sn_host, sn_port = None, None if server_name: - sn_host, _, sn_port = server_name.partition(':') + sn_host, _, sn_port = server_name.partition(":") host = host or sn_host or _host port = int(port or sn_port or _port) - options.setdefault('use_reloader', self.debug) - options.setdefault('use_debugger', self.debug) - options.setdefault('threaded', True) + options.setdefault("use_reloader", self.debug) + options.setdefault("use_debugger", self.debug) + options.setdefault("threaded", True) cli.show_server_banner(self.env, self.debug, self.name, False) @@ -1034,10 +1058,12 @@ class Flask(_PackageBoundObject): :param request: an instance of :attr:`request_class`. """ - warnings.warn(DeprecationWarning( - '"open_session" is deprecated and will be removed in 1.1. Use' - ' "session_interface.open_session" instead.' - )) + warnings.warn( + DeprecationWarning( + '"open_session" is deprecated and will be removed in 1.1. Use' + ' "session_interface.open_session" instead.' + ) + ) return self.session_interface.open_session(self, request) def save_session(self, session, response): @@ -1055,10 +1081,12 @@ class Flask(_PackageBoundObject): :param response: an instance of :attr:`response_class` """ - warnings.warn(DeprecationWarning( - '"save_session" is deprecated and will be removed in 1.1. Use' - ' "session_interface.save_session" instead.' - )) + warnings.warn( + DeprecationWarning( + '"save_session" is deprecated and will be removed in 1.1. Use' + ' "session_interface.save_session" instead.' + ) + ) return self.session_interface.save_session(self, session, response) def make_null_session(self): @@ -1072,10 +1100,12 @@ class Flask(_PackageBoundObject): .. versionadded:: 0.7 """ - warnings.warn(DeprecationWarning( - '"make_null_session" is deprecated and will be removed in 1.1. Use' - ' "session_interface.make_null_session" instead.' - )) + warnings.warn( + DeprecationWarning( + '"make_null_session" is deprecated and will be removed in 1.1. Use' + ' "session_interface.make_null_session" instead.' + ) + ) return self.session_interface.make_null_session(self) @setupmethod @@ -1102,11 +1132,10 @@ class Flask(_PackageBoundObject): if blueprint.name in self.blueprints: assert self.blueprints[blueprint.name] is blueprint, ( - 'A name collision occurred between blueprints %r and %r. Both' + "A name collision occurred between blueprints %r and %r. Both" ' share the same name "%s". Blueprints that are created on the' - ' fly need unique names.' % ( - blueprint, self.blueprints[blueprint.name], blueprint.name - ) + " fly need unique names." + % (blueprint, self.blueprints[blueprint.name], blueprint.name) ) else: self.blueprints[blueprint.name] = blueprint @@ -1123,8 +1152,14 @@ class Flask(_PackageBoundObject): return iter(self._blueprint_order) @setupmethod - def add_url_rule(self, rule, endpoint=None, view_func=None, - provide_automatic_options=None, **options): + def add_url_rule( + self, + rule, + endpoint=None, + view_func=None, + provide_automatic_options=None, + **options + ): """Connects a URL rule. Works exactly like the :meth:`route` decorator. If a view_func is provided it will be registered with the endpoint. @@ -1179,32 +1214,35 @@ class Flask(_PackageBoundObject): """ if endpoint is None: endpoint = _endpoint_from_view_func(view_func) - options['endpoint'] = endpoint - methods = options.pop('methods', None) + options["endpoint"] = endpoint + methods = options.pop("methods", None) # if the methods are not given and the view_func object knows its # methods we can use that instead. If neither exists, we go with # a tuple of only ``GET`` as default. if methods is None: - methods = getattr(view_func, 'methods', None) or ('GET',) + methods = getattr(view_func, "methods", None) or ("GET",) if isinstance(methods, string_types): - raise TypeError('Allowed methods have to be iterables of strings, ' - 'for example: @app.route(..., methods=["POST"])') + raise TypeError( + "Allowed methods have to be iterables of strings, " + 'for example: @app.route(..., methods=["POST"])' + ) methods = set(item.upper() for item in methods) # Methods that should always be added - required_methods = set(getattr(view_func, 'required_methods', ())) + required_methods = set(getattr(view_func, "required_methods", ())) # starting with Flask 0.8 the view_func object can disable and # force-enable the automatic options handling. if provide_automatic_options is None: - provide_automatic_options = getattr(view_func, - 'provide_automatic_options', None) + provide_automatic_options = getattr( + view_func, "provide_automatic_options", None + ) if provide_automatic_options is None: - if 'OPTIONS' not in methods: + if "OPTIONS" not in methods: provide_automatic_options = True - required_methods.add('OPTIONS') + required_methods.add("OPTIONS") else: provide_automatic_options = False @@ -1218,8 +1256,10 @@ class Flask(_PackageBoundObject): if view_func is not None: old_func = self.view_functions.get(endpoint) if old_func is not None and old_func != view_func: - raise AssertionError('View function mapping is overwriting an ' - 'existing endpoint function: %s' % endpoint) + raise AssertionError( + "View function mapping is overwriting an " + "existing endpoint function: %s" % endpoint + ) self.view_functions[endpoint] = view_func def route(self, rule, **options): @@ -1246,10 +1286,12 @@ class Flask(_PackageBoundObject): Starting with Flask 0.6, ``OPTIONS`` is implicitly added and handled by the standard request handling. """ + def decorator(f): - endpoint = options.pop('endpoint', None) + endpoint = options.pop("endpoint", None) self.add_url_rule(rule, endpoint, f, **options) return f + return decorator @setupmethod @@ -1263,9 +1305,11 @@ class Flask(_PackageBoundObject): :param endpoint: the name of the endpoint """ + def decorator(f): self.view_functions[endpoint] = f return f + return decorator @staticmethod @@ -1313,9 +1357,11 @@ class Flask(_PackageBoundObject): :param code_or_exception: the code as integer for the handler, or an arbitrary exception """ + def decorator(f): self._register_error_handler(None, code_or_exception, f) return f + return decorator @setupmethod @@ -1337,9 +1383,9 @@ class Flask(_PackageBoundObject): """ if isinstance(code_or_exception, HTTPException): # old broken behavior raise ValueError( - 'Tried to register a handler for an exception instance {0!r}.' - ' Handlers can only be registered for exception classes or' - ' HTTP error codes.'.format(code_or_exception) + "Tried to register a handler for an exception instance {0!r}." + " Handlers can only be registered for exception classes or" + " HTTP error codes.".format(code_or_exception) ) try: @@ -1366,9 +1412,11 @@ class Flask(_PackageBoundObject): :param name: the optional name of the filter, otherwise the function name will be used. """ + def decorator(f): self.add_template_filter(f, name=name) return f + return decorator @setupmethod @@ -1401,9 +1449,11 @@ class Flask(_PackageBoundObject): :param name: the optional name of the test, otherwise the function name will be used. """ + def decorator(f): self.add_template_test(f, name=name) return f + return decorator @setupmethod @@ -1433,9 +1483,11 @@ class Flask(_PackageBoundObject): :param name: the optional name of the global function, otherwise the function name will be used. """ + def decorator(f): self.add_template_global(f, name=name) return f + return decorator @setupmethod @@ -1613,8 +1665,10 @@ class Flask(_PackageBoundObject): exc_class, code = self._get_exc_class_and_code(type(e)) for name, c in ( - (request.blueprint, code), (None, code), - (request.blueprint, None), (None, None) + (request.blueprint, code), + (None, code), + (request.blueprint, None), + (None, None), ): handler_map = self.error_handler_spec.setdefault(name, {}).get(c) @@ -1677,14 +1731,15 @@ class Flask(_PackageBoundObject): .. versionadded:: 0.8 """ - if self.config['TRAP_HTTP_EXCEPTIONS']: + if self.config["TRAP_HTTP_EXCEPTIONS"]: return True - trap_bad_request = self.config['TRAP_BAD_REQUEST_ERRORS'] + trap_bad_request = self.config["TRAP_BAD_REQUEST_ERRORS"] # if unset, trap key errors in debug mode if ( - trap_bad_request is None and self.debug + trap_bad_request is None + and self.debug and isinstance(e, BadRequestKeyError) ): return True @@ -1773,10 +1828,9 @@ class Flask(_PackageBoundObject): .. versionadded:: 0.8 """ - self.logger.error('Exception on %s [%s]' % ( - request.path, - request.method - ), exc_info=exc_info) + self.logger.error( + "Exception on %s [%s]" % (request.path, request.method), exc_info=exc_info + ) def raise_routing_exception(self, request): """Exceptions that are recording during routing are reraised with @@ -1786,12 +1840,15 @@ class Flask(_PackageBoundObject): :internal: """ - if not self.debug \ - or not isinstance(request.routing_exception, RequestRedirect) \ - or request.method in ('GET', 'HEAD', 'OPTIONS'): + if ( + not self.debug + or not isinstance(request.routing_exception, RequestRedirect) + or request.method in ("GET", "HEAD", "OPTIONS") + ): raise request.routing_exception from .debughelpers import FormDataRoutingRedirect + raise FormDataRoutingRedirect(request) def dispatch_request(self): @@ -1810,8 +1867,10 @@ class Flask(_PackageBoundObject): rule = req.url_rule # if we provide automatic options for this URL and the # request came with the OPTIONS method, reply automatically - if getattr(rule, 'provide_automatic_options', False) \ - and req.method == 'OPTIONS': + if ( + getattr(rule, "provide_automatic_options", False) + and req.method == "OPTIONS" + ): return self.make_default_options_response() # otherwise dispatch to the handler for that endpoint return self.view_functions[rule.endpoint](**req.view_args) @@ -1853,8 +1912,9 @@ class Flask(_PackageBoundObject): except Exception: if not from_error_handler: raise - self.logger.exception('Request finalizing failed with an ' - 'error while handling an error') + self.logger.exception( + "Request finalizing failed with an " "error while handling an error" + ) return response def try_trigger_before_first_request_functions(self): @@ -1881,13 +1941,13 @@ class Flask(_PackageBoundObject): .. versionadded:: 0.7 """ adapter = _request_ctx_stack.top.url_adapter - if hasattr(adapter, 'allowed_methods'): + if hasattr(adapter, "allowed_methods"): methods = adapter.allowed_methods() else: # fallback for Werkzeug < 0.7 methods = [] try: - adapter.match(method='--') + adapter.match(method="--") except MethodNotAllowed as e: methods = e.valid_methods except HTTPException as e: @@ -1964,17 +2024,17 @@ class Flask(_PackageBoundObject): # other sized tuples are not allowed else: raise TypeError( - 'The view function did not return a valid response tuple.' - ' The tuple must have the form (body, status, headers),' - ' (body, status), or (body, headers).' + "The view function did not return a valid response tuple." + " The tuple must have the form (body, status, headers)," + " (body, status), or (body, headers)." ) # the body must not be None if rv is None: raise TypeError( - 'The view function did not return a valid response. The' - ' function either returned None or ended without a return' - ' statement.' + "The view function did not return a valid response. The" + " function either returned None or ended without a return" + " statement." ) # make sure the body is an instance of the response class @@ -1992,10 +2052,10 @@ class Flask(_PackageBoundObject): rv = self.response_class.force_type(rv, request.environ) except TypeError as e: new_error = TypeError( - '{e}\nThe view function did not return a valid' - ' response. The return type must be a string, tuple,' - ' Response instance, or WSGI callable, but it was a' - ' {rv.__class__.__name__}.'.format(e=e, rv=rv) + "{e}\nThe view function did not return a valid" + " response. The return type must be a string, tuple," + " Response instance, or WSGI callable, but it was a" + " {rv.__class__.__name__}.".format(e=e, rv=rv) ) reraise(TypeError, new_error, sys.exc_info()[2]) @@ -2031,19 +2091,24 @@ class Flask(_PackageBoundObject): # If subdomain matching is disabled (the default), use the # default subdomain in all cases. This should be the default # in Werkzeug but it currently does not have that feature. - subdomain = ((self.url_map.default_subdomain or None) - if not self.subdomain_matching else None) + subdomain = ( + (self.url_map.default_subdomain or None) + if not self.subdomain_matching + else None + ) return self.url_map.bind_to_environ( request.environ, - server_name=self.config['SERVER_NAME'], - subdomain=subdomain) + server_name=self.config["SERVER_NAME"], + subdomain=subdomain, + ) # We need at the very least the server name to be set for this # to work. - if self.config['SERVER_NAME'] is not None: + if self.config["SERVER_NAME"] is not None: return self.url_map.bind( - self.config['SERVER_NAME'], - script_name=self.config['APPLICATION_ROOT'], - url_scheme=self.config['PREFERRED_URL_SCHEME']) + self.config["SERVER_NAME"], + script_name=self.config["APPLICATION_ROOT"], + url_scheme=self.config["PREFERRED_URL_SCHEME"], + ) def inject_url_defaults(self, endpoint, values): """Injects the URL defaults for the given endpoint directly into @@ -2053,8 +2118,8 @@ class Flask(_PackageBoundObject): .. versionadded:: 0.7 """ funcs = self.url_default_functions.get(None, ()) - if '.' in endpoint: - bp = endpoint.rsplit('.', 1)[0] + if "." in endpoint: + bp = endpoint.rsplit(".", 1)[0] funcs = chain(funcs, self.url_default_functions.get(bp, ())) for func in funcs: func(endpoint, values) @@ -2327,7 +2392,4 @@ class Flask(_PackageBoundObject): return self.wsgi_app(environ, start_response) def __repr__(self): - return '<%s %r>' % ( - self.__class__.__name__, - self.name, - ) + return "<%s %r>" % (self.__class__.__name__, self.name) diff --git a/flask/blueprints.py b/flask/blueprints.py index 5ce5561e..42702d6d 100644 --- a/flask/blueprints.py +++ b/flask/blueprints.py @@ -39,7 +39,7 @@ class BlueprintSetupState(object): #: out if the blueprint was registered in the past already. self.first_registration = first_registration - subdomain = self.options.get('subdomain') + subdomain = self.options.get("subdomain") if subdomain is None: subdomain = self.blueprint.subdomain @@ -47,7 +47,7 @@ class BlueprintSetupState(object): #: otherwise. self.subdomain = subdomain - url_prefix = self.options.get('url_prefix') + url_prefix = self.options.get("url_prefix") if url_prefix is None: url_prefix = self.blueprint.url_prefix #: The prefix that should be used for all URLs defined on the @@ -57,7 +57,7 @@ class BlueprintSetupState(object): #: A dictionary with URL defaults that is added to each and every #: URL that was defined with the blueprint. self.url_defaults = dict(self.blueprint.url_values_defaults) - self.url_defaults.update(self.options.get('url_defaults', ())) + self.url_defaults.update(self.options.get("url_defaults", ())) def add_url_rule(self, rule, endpoint=None, view_func=None, **options): """A helper method to register a rule (and optionally a view function) @@ -66,18 +66,22 @@ class BlueprintSetupState(object): """ if self.url_prefix is not None: if rule: - rule = '/'.join(( - self.url_prefix.rstrip('/'), rule.lstrip('/'))) + rule = "/".join((self.url_prefix.rstrip("/"), rule.lstrip("/"))) else: rule = self.url_prefix - options.setdefault('subdomain', self.subdomain) + options.setdefault("subdomain", self.subdomain) if endpoint is None: endpoint = _endpoint_from_view_func(view_func) defaults = self.url_defaults - if 'defaults' in options: - defaults = dict(defaults, **options.pop('defaults')) - self.app.add_url_rule(rule, '%s.%s' % (self.blueprint.name, endpoint), - view_func, defaults=defaults, **options) + if "defaults" in options: + defaults = dict(defaults, **options.pop("defaults")) + self.app.add_url_rule( + rule, + "%s.%s" % (self.blueprint.name, endpoint), + view_func, + defaults=defaults, + **options + ) class Blueprint(_PackageBoundObject): @@ -115,12 +119,21 @@ class Blueprint(_PackageBoundObject): #: resources contained in the package. root_path = None - def __init__(self, name, import_name, static_folder=None, - static_url_path=None, template_folder=None, - url_prefix=None, subdomain=None, url_defaults=None, - root_path=None): - _PackageBoundObject.__init__(self, import_name, template_folder, - root_path=root_path) + def __init__( + self, + name, + import_name, + static_folder=None, + static_url_path=None, + template_folder=None, + url_prefix=None, + subdomain=None, + url_defaults=None, + root_path=None, + ): + _PackageBoundObject.__init__( + self, import_name, template_folder, root_path=root_path + ) self.name = name self.url_prefix = url_prefix self.subdomain = subdomain @@ -139,9 +152,14 @@ class Blueprint(_PackageBoundObject): """ if self._got_registered_once and self.warn_on_modifications: from warnings import warn - warn(Warning('The blueprint was already registered once ' - 'but is getting modified now. These changes ' - 'will not show up.')) + + warn( + Warning( + "The blueprint was already registered once " + "but is getting modified now. These changes " + "will not show up." + ) + ) self.deferred_functions.append(func) def record_once(self, func): @@ -150,9 +168,11 @@ class Blueprint(_PackageBoundObject): blueprint is registered a second time on the application, the function passed is not called. """ + def wrapper(state): if state.first_registration: func(state) + return self.record(update_wrapper(wrapper, func)) def make_setup_state(self, app, options, first_registration=False): @@ -179,8 +199,9 @@ class Blueprint(_PackageBoundObject): if self.has_static_folder: state.add_url_rule( - self.static_url_path + '/', - view_func=self.send_static_file, endpoint='static' + self.static_url_path + "/", + view_func=self.send_static_file, + endpoint="static", ) for deferred in self.deferred_functions: @@ -190,10 +211,12 @@ class Blueprint(_PackageBoundObject): """Like :meth:`Flask.route` but for a blueprint. The endpoint for the :func:`url_for` function is prefixed with the name of the blueprint. """ + def decorator(f): endpoint = options.pop("endpoint", f.__name__) self.add_url_rule(rule, endpoint, f, **options) return f + return decorator def add_url_rule(self, rule, endpoint=None, view_func=None, **options): @@ -201,11 +224,12 @@ class Blueprint(_PackageBoundObject): the :func:`url_for` function is prefixed with the name of the blueprint. """ if endpoint: - assert '.' not in endpoint, "Blueprint endpoints should not contain dots" - if view_func and hasattr(view_func, '__name__'): - assert '.' not in view_func.__name__, "Blueprint view function name should not contain dots" - self.record(lambda s: - s.add_url_rule(rule, endpoint, view_func, **options)) + assert "." not in endpoint, "Blueprint endpoints should not contain dots" + if view_func and hasattr(view_func, "__name__"): + assert ( + "." not in view_func.__name__ + ), "Blueprint view function name should not contain dots" + self.record(lambda s: s.add_url_rule(rule, endpoint, view_func, **options)) def endpoint(self, endpoint): """Like :meth:`Flask.endpoint` but for a blueprint. This does not @@ -214,11 +238,14 @@ class Blueprint(_PackageBoundObject): with a `.` it will be registered to the current blueprint, otherwise it's an application independent endpoint. """ + def decorator(f): def register_endpoint(state): state.app.view_functions[endpoint] = f + self.record_once(register_endpoint) return f + return decorator def app_template_filter(self, name=None): @@ -228,9 +255,11 @@ class Blueprint(_PackageBoundObject): :param name: the optional name of the filter, otherwise the function name will be used. """ + def decorator(f): self.add_app_template_filter(f, name=name) return f + return decorator def add_app_template_filter(self, f, name=None): @@ -241,8 +270,10 @@ class Blueprint(_PackageBoundObject): :param name: the optional name of the filter, otherwise the function name will be used. """ + def register_template(state): state.app.jinja_env.filters[name or f.__name__] = f + self.record_once(register_template) def app_template_test(self, name=None): @@ -254,9 +285,11 @@ class Blueprint(_PackageBoundObject): :param name: the optional name of the test, otherwise the function name will be used. """ + def decorator(f): self.add_app_template_test(f, name=name) return f + return decorator def add_app_template_test(self, f, name=None): @@ -269,8 +302,10 @@ class Blueprint(_PackageBoundObject): :param name: the optional name of the test, otherwise the function name will be used. """ + def register_template(state): state.app.jinja_env.tests[name or f.__name__] = f + self.record_once(register_template) def app_template_global(self, name=None): @@ -282,9 +317,11 @@ class Blueprint(_PackageBoundObject): :param name: the optional name of the global, otherwise the function name will be used. """ + def decorator(f): self.add_app_template_global(f, name=name) return f + return decorator def add_app_template_global(self, f, name=None): @@ -297,8 +334,10 @@ class Blueprint(_PackageBoundObject): :param name: the optional name of the global, otherwise the function name will be used. """ + def register_template(state): state.app.jinja_env.globals[name or f.__name__] = f + self.record_once(register_template) def before_request(self, f): @@ -306,16 +345,18 @@ class Blueprint(_PackageBoundObject): is only executed before each request that is handled by a function of that blueprint. """ - self.record_once(lambda s: s.app.before_request_funcs - .setdefault(self.name, []).append(f)) + self.record_once( + lambda s: s.app.before_request_funcs.setdefault(self.name, []).append(f) + ) return f def before_app_request(self, f): """Like :meth:`Flask.before_request`. Such a function is executed before each request, even if outside of a blueprint. """ - self.record_once(lambda s: s.app.before_request_funcs - .setdefault(None, []).append(f)) + self.record_once( + lambda s: s.app.before_request_funcs.setdefault(None, []).append(f) + ) return f def before_app_first_request(self, f): @@ -330,16 +371,18 @@ class Blueprint(_PackageBoundObject): is only executed after each request that is handled by a function of that blueprint. """ - self.record_once(lambda s: s.app.after_request_funcs - .setdefault(self.name, []).append(f)) + self.record_once( + lambda s: s.app.after_request_funcs.setdefault(self.name, []).append(f) + ) return f def after_app_request(self, f): """Like :meth:`Flask.after_request` but for a blueprint. Such a function is executed after each request, even if outside of the blueprint. """ - self.record_once(lambda s: s.app.after_request_funcs - .setdefault(None, []).append(f)) + self.record_once( + lambda s: s.app.after_request_funcs.setdefault(None, []).append(f) + ) return f def teardown_request(self, f): @@ -349,8 +392,9 @@ class Blueprint(_PackageBoundObject): when the request context is popped, even when no actual request was performed. """ - self.record_once(lambda s: s.app.teardown_request_funcs - .setdefault(self.name, []).append(f)) + self.record_once( + lambda s: s.app.teardown_request_funcs.setdefault(self.name, []).append(f) + ) return f def teardown_app_request(self, f): @@ -358,33 +402,40 @@ class Blueprint(_PackageBoundObject): function is executed when tearing down each request, even if outside of the blueprint. """ - self.record_once(lambda s: s.app.teardown_request_funcs - .setdefault(None, []).append(f)) + self.record_once( + lambda s: s.app.teardown_request_funcs.setdefault(None, []).append(f) + ) return f def context_processor(self, f): """Like :meth:`Flask.context_processor` but for a blueprint. This function is only executed for requests handled by a blueprint. """ - self.record_once(lambda s: s.app.template_context_processors - .setdefault(self.name, []).append(f)) + self.record_once( + lambda s: s.app.template_context_processors.setdefault( + self.name, [] + ).append(f) + ) return f def app_context_processor(self, f): """Like :meth:`Flask.context_processor` but for a blueprint. Such a function is executed each request, even if outside of the blueprint. """ - self.record_once(lambda s: s.app.template_context_processors - .setdefault(None, []).append(f)) + self.record_once( + lambda s: s.app.template_context_processors.setdefault(None, []).append(f) + ) return f def app_errorhandler(self, code): """Like :meth:`Flask.errorhandler` but for a blueprint. This handler is used for all requests, even if outside of the blueprint. """ + def decorator(f): self.record_once(lambda s: s.app.errorhandler(code)(f)) return f + return decorator def url_value_preprocessor(self, f): @@ -392,8 +443,9 @@ class Blueprint(_PackageBoundObject): blueprint. It's called before the view functions are called and can modify the url values provided. """ - self.record_once(lambda s: s.app.url_value_preprocessors - .setdefault(self.name, []).append(f)) + self.record_once( + lambda s: s.app.url_value_preprocessors.setdefault(self.name, []).append(f) + ) return f def url_defaults(self, f): @@ -401,22 +453,25 @@ class Blueprint(_PackageBoundObject): with the endpoint and values and should update the values passed in place. """ - self.record_once(lambda s: s.app.url_default_functions - .setdefault(self.name, []).append(f)) + self.record_once( + lambda s: s.app.url_default_functions.setdefault(self.name, []).append(f) + ) return f def app_url_value_preprocessor(self, f): """Same as :meth:`url_value_preprocessor` but application wide. """ - self.record_once(lambda s: s.app.url_value_preprocessors - .setdefault(None, []).append(f)) + self.record_once( + lambda s: s.app.url_value_preprocessors.setdefault(None, []).append(f) + ) return f def app_url_defaults(self, f): """Same as :meth:`url_defaults` but application wide. """ - self.record_once(lambda s: s.app.url_default_functions - .setdefault(None, []).append(f)) + self.record_once( + lambda s: s.app.url_default_functions.setdefault(None, []).append(f) + ) return f def errorhandler(self, code_or_exception): @@ -430,10 +485,13 @@ class Blueprint(_PackageBoundObject): Otherwise works as the :meth:`~flask.Flask.errorhandler` decorator of the :class:`~flask.Flask` object. """ + def decorator(f): - self.record_once(lambda s: s.app._register_error_handler( - self.name, code_or_exception, f)) + self.record_once( + lambda s: s.app._register_error_handler(self.name, code_or_exception, f) + ) return f + return decorator def register_error_handler(self, code_or_exception, f): @@ -444,5 +502,6 @@ class Blueprint(_PackageBoundObject): .. versionadded:: 0.11 """ - self.record_once(lambda s: s.app._register_error_handler( - self.name, code_or_exception, f)) + self.record_once( + lambda s: s.app._register_error_handler(self.name, code_or_exception, f) + ) diff --git a/flask/cli.py b/flask/cli.py index 5a49636a..6bb01a1f 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -48,16 +48,14 @@ def find_best_app(script_info, module): from . import Flask # Search for the most common names first. - for attr_name in ('app', 'application'): + for attr_name in ("app", "application"): app = getattr(module, attr_name, None) if isinstance(app, Flask): return app # Otherwise find the only object that is a Flask instance. - matches = [ - v for v in itervalues(module.__dict__) if isinstance(v, Flask) - ] + matches = [v for v in itervalues(module.__dict__) if isinstance(v, Flask)] if len(matches) == 1: return matches[0] @@ -65,11 +63,11 @@ def find_best_app(script_info, module): raise NoAppException( 'Detected multiple Flask applications in module "{module}". Use ' '"FLASK_APP={module}:name" to specify the correct ' - 'one.'.format(module=module.__name__) + "one.".format(module=module.__name__) ) # Search for app factory functions. - for attr_name in ('create_app', 'make_app'): + for attr_name in ("create_app", "make_app"): app_factory = getattr(module, attr_name, None) if inspect.isfunction(app_factory): @@ -83,18 +81,14 @@ def find_best_app(script_info, module): raise raise NoAppException( 'Detected factory "{factory}" in module "{module}", but ' - 'could not call it without arguments. Use ' - '"FLASK_APP=\'{module}:{factory}(args)\'" to specify ' - 'arguments.'.format( - factory=attr_name, module=module.__name__ - ) + "could not call it without arguments. Use " + "\"FLASK_APP='{module}:{factory}(args)'\" to specify " + "arguments.".format(factory=attr_name, module=module.__name__) ) raise NoAppException( 'Failed to find Flask application or factory in module "{module}". ' - 'Use "FLASK_APP={module}:name to specify one.'.format( - module=module.__name__ - ) + 'Use "FLASK_APP={module}:name to specify one.'.format(module=module.__name__) ) @@ -107,7 +101,7 @@ def call_factory(script_info, app_factory, arguments=()): arg_names = args_spec.args arg_defaults = args_spec.defaults - if 'script_info' in arg_names: + if "script_info" in arg_names: return app_factory(*arguments, script_info=script_info) elif arguments: return app_factory(*arguments) @@ -148,12 +142,13 @@ def find_app_by_string(script_info, module, app_name): arguments. """ from flask import Flask - match = re.match(r'^ *([^ ()]+) *(?:\((.*?) *,? *\))? *$', app_name) + + match = re.match(r"^ *([^ ()]+) *(?:\((.*?) *,? *\))? *$", app_name) if not match: raise NoAppException( '"{name}" is not a valid variable name or function ' - 'expression.'.format(name=app_name) + "expression.".format(name=app_name) ) name, args = match.groups() @@ -166,10 +161,10 @@ def find_app_by_string(script_info, module, app_name): if inspect.isfunction(attr): if args: try: - args = ast.literal_eval('({args},)'.format(args=args)) - except (ValueError, SyntaxError)as e: + args = ast.literal_eval("({args},)".format(args=args)) + except (ValueError, SyntaxError) as e: raise NoAppException( - 'Could not parse the arguments in ' + "Could not parse the arguments in " '"{app_name}".'.format(e=e, app_name=app_name) ) else: @@ -183,7 +178,7 @@ def find_app_by_string(script_info, module, app_name): raise NoAppException( '{e}\nThe factory "{app_name}" in module "{module}" could not ' - 'be called with the specified arguments.'.format( + "be called with the specified arguments.".format( e=e, app_name=app_name, module=module.__name__ ) ) @@ -194,10 +189,8 @@ def find_app_by_string(script_info, module, app_name): return app raise NoAppException( - 'A valid Flask application was not obtained from ' - '"{module}:{app_name}".'.format( - module=module.__name__, app_name=app_name - ) + "A valid Flask application was not obtained from " + '"{module}:{app_name}".'.format(module=module.__name__, app_name=app_name) ) @@ -208,10 +201,10 @@ def prepare_import(path): path = os.path.realpath(path) fname, ext = os.path.splitext(path) - if ext == '.py': + if ext == ".py": path = fname - if os.path.basename(path) == '__init__': + if os.path.basename(path) == "__init__": path = os.path.dirname(path) module_name = [] @@ -221,13 +214,13 @@ def prepare_import(path): path, name = os.path.split(path) module_name.append(name) - if not os.path.exists(os.path.join(path, '__init__.py')): + if not os.path.exists(os.path.join(path, "__init__.py")): break if sys.path[0] != path: sys.path.insert(0, path) - return '.'.join(module_name[::-1]) + return ".".join(module_name[::-1]) def locate_app(script_info, module_name, app_name, raise_if_not_found=True): @@ -241,12 +234,10 @@ def locate_app(script_info, module_name, app_name, raise_if_not_found=True): if sys.exc_info()[-1].tb_next: raise NoAppException( 'While importing "{name}", an ImportError was raised:' - '\n\n{tb}'.format(name=module_name, tb=traceback.format_exc()) + "\n\n{tb}".format(name=module_name, tb=traceback.format_exc()) ) elif raise_if_not_found: - raise NoAppException( - 'Could not import "{name}".'.format(name=module_name) - ) + raise NoAppException('Could not import "{name}".'.format(name=module_name)) else: return @@ -262,26 +253,27 @@ def get_version(ctx, param, value): if not value or ctx.resilient_parsing: return import werkzeug - message = ( - 'Python %(python)s\n' - 'Flask %(flask)s\n' - 'Werkzeug %(werkzeug)s' + + message = "Python %(python)s\n" "Flask %(flask)s\n" "Werkzeug %(werkzeug)s" + click.echo( + message + % { + "python": platform.python_version(), + "flask": __version__, + "werkzeug": werkzeug.__version__, + }, + color=ctx.color, ) - click.echo(message % { - 'python': platform.python_version(), - 'flask': __version__, - 'werkzeug': werkzeug.__version__, - }, color=ctx.color) ctx.exit() version_option = click.Option( - ['--version'], - help='Show the flask version', + ["--version"], + help="Show the flask version", expose_value=False, callback=get_version, is_flag=True, - is_eager=True + is_eager=True, ) @@ -310,6 +302,7 @@ class DispatchingApp(object): self._load_unlocked() except Exception: self._bg_loading_exc_info = sys.exc_info() + t = Thread(target=_load_app, args=()) t.start() @@ -348,10 +341,9 @@ class ScriptInfo(object): onwards as click object. """ - def __init__(self, app_import_path=None, create_app=None, - set_debug_flag=True): + def __init__(self, app_import_path=None, create_app=None, set_debug_flag=True): #: Optionally the import path for the Flask application. - self.app_import_path = app_import_path or os.environ.get('FLASK_APP') + self.app_import_path = app_import_path or os.environ.get("FLASK_APP") #: Optionally a function that is passed the script info to create #: the instance of the application. self.create_app = create_app @@ -377,21 +369,22 @@ class ScriptInfo(object): app = call_factory(self, self.create_app) else: if self.app_import_path: - path, name = (re.split(r':(?![\\/])', self.app_import_path, 1) + [None])[:2] + path, name = ( + re.split(r":(?![\\/])", self.app_import_path, 1) + [None] + )[:2] import_name = prepare_import(path) app = locate_app(self, import_name, name) else: - for path in ('wsgi.py', 'app.py'): + for path in ("wsgi.py", "app.py"): import_name = prepare_import(path) - app = locate_app(self, import_name, None, - raise_if_not_found=False) + app = locate_app(self, import_name, None, raise_if_not_found=False) if app: break if not app: raise NoAppException( - 'Could not locate a Flask application. You did not provide ' + "Could not locate a Flask application. You did not provide " 'the "FLASK_APP" environment variable, and a "wsgi.py" or ' '"app.py" module was not found in the current directory.' ) @@ -414,10 +407,12 @@ def with_appcontext(f): to the ``app.cli`` object then they are wrapped with this function by default unless it's disabled. """ + @click.pass_context def decorator(__ctx, *args, **kwargs): with __ctx.ensure_object(ScriptInfo).load_app().app_context(): return __ctx.invoke(f, *args, **kwargs) + return update_wrapper(decorator, f) @@ -434,11 +429,13 @@ class AppGroup(click.Group): :class:`click.Group` but it wraps callbacks in :func:`with_appcontext` unless it's disabled by passing ``with_appcontext=False``. """ - wrap_for_ctx = kwargs.pop('with_appcontext', True) + wrap_for_ctx = kwargs.pop("with_appcontext", True) + def decorator(f): if wrap_for_ctx: f = with_appcontext(f) return click.Group.command(self, *args, **kwargs)(f) + return decorator def group(self, *args, **kwargs): @@ -446,7 +443,7 @@ class AppGroup(click.Group): :class:`click.Group` but it defaults the group class to :class:`AppGroup`. """ - kwargs.setdefault('cls', AppGroup) + kwargs.setdefault("cls", AppGroup) return click.Group.group(self, *args, **kwargs) @@ -475,10 +472,16 @@ class FlaskGroup(AppGroup): from :file:`.env` and :file:`.flaskenv` files. """ - def __init__(self, add_default_commands=True, create_app=None, - add_version_option=True, load_dotenv=True, - set_debug_flag=True, **extra): - params = list(extra.pop('params', None) or ()) + def __init__( + self, + add_default_commands=True, + create_app=None, + add_version_option=True, + load_dotenv=True, + set_debug_flag=True, + **extra + ): + params = list(extra.pop("params", None) or ()) if add_version_option: params.append(version_option) @@ -504,7 +507,7 @@ class FlaskGroup(AppGroup): self._loaded_plugin_commands = True return - for ep in pkg_resources.iter_entry_points('flask.commands'): + for ep in pkg_resources.iter_entry_points("flask.commands"): self.add_command(ep.load(), ep.name) self._loaded_plugin_commands = True @@ -554,19 +557,20 @@ class FlaskGroup(AppGroup): # command line interface. This is detected by Flask.run to make the # call into a no-op. This is necessary to avoid ugly errors when the # script that is loaded here also attempts to start a server. - os.environ['FLASK_RUN_FROM_CLI'] = 'true' + os.environ["FLASK_RUN_FROM_CLI"] = "true" if get_load_dotenv(self.load_dotenv): load_dotenv() - obj = kwargs.get('obj') + obj = kwargs.get("obj") if obj is None: - obj = ScriptInfo(create_app=self.create_app, - set_debug_flag=self.set_debug_flag) + obj = ScriptInfo( + create_app=self.create_app, set_debug_flag=self.set_debug_flag + ) - kwargs['obj'] = obj - kwargs.setdefault('auto_envvar_prefix', 'FLASK') + kwargs["obj"] = obj + kwargs.setdefault("auto_envvar_prefix", "FLASK") return super(FlaskGroup, self).main(*args, **kwargs) @@ -574,7 +578,7 @@ def _path_is_ancestor(path, other): """Take ``other`` and remove the length of ``path`` from it. Then join it to ``path``. If it is the original value, ``path`` is an ancestor of ``other``.""" - return os.path.join(path, other[len(path):].lstrip(os.sep)) == other + return os.path.join(path, other[len(path) :].lstrip(os.sep)) == other def load_dotenv(path=None): @@ -597,11 +601,12 @@ def load_dotenv(path=None): .. versionadded:: 1.0 """ if dotenv is None: - if path or os.path.isfile('.env') or os.path.isfile('.flaskenv'): + if path or os.path.isfile(".env") or os.path.isfile(".flaskenv"): click.secho( - ' * Tip: There are .env or .flaskenv files present.' + " * Tip: There are .env or .flaskenv files present." ' Do "pip install python-dotenv" to use them.', - fg='yellow') + fg="yellow", + ) return if path is not None: @@ -609,7 +614,7 @@ def load_dotenv(path=None): new_dir = None - for name in ('.env', '.flaskenv'): + for name in (".env", ".flaskenv"): path = dotenv.find_dotenv(name, usecwd=True) if not path: @@ -630,27 +635,29 @@ def show_server_banner(env, debug, app_import_path, eager_loading): """Show extra startup messages the first time the server is run, ignoring the reloader. """ - if os.environ.get('WERKZEUG_RUN_MAIN') == 'true': + if os.environ.get("WERKZEUG_RUN_MAIN") == "true": return if app_import_path is not None: message = ' * Serving Flask app "{0}"'.format(app_import_path) if not eager_loading: - message += ' (lazy loading)' + message += " (lazy loading)" click.echo(message) - click.echo(' * Environment: {0}'.format(env)) + click.echo(" * Environment: {0}".format(env)) - if env == 'production': + if env == "production": click.secho( - ' WARNING: Do not use the development server in a production' - ' environment.', fg='red') - click.secho(' Use a production WSGI server instead.', dim=True) + " WARNING: Do not use the development server in a production" + " environment.", + fg="red", + ) + click.secho(" Use a production WSGI server instead.", dim=True) if debug is not None: - click.echo(' * Debug mode: {0}'.format('on' if debug else 'off')) + click.echo(" * Debug mode: {0}".format("on" if debug else "off")) class CertParamType(click.ParamType): @@ -659,11 +666,10 @@ class CertParamType(click.ParamType): :class:`~ssl.SSLContext` object. """ - name = 'path' + name = "path" def __init__(self): - self.path_type = click.Path( - exists=True, dir_okay=False, resolve_path=True) + self.path_type = click.Path(exists=True, dir_okay=False, resolve_path=True) def convert(self, value, param, ctx): try: @@ -671,13 +677,13 @@ class CertParamType(click.ParamType): except click.BadParameter: value = click.STRING(value, param, ctx).lower() - if value == 'adhoc': + if value == "adhoc": try: import OpenSSL except ImportError: raise click.BadParameter( - 'Using ad-hoc certificates requires pyOpenSSL.', - ctx, param) + "Using ad-hoc certificates requires pyOpenSSL.", ctx, param + ) return value @@ -697,8 +703,8 @@ def _validate_key(ctx, param, value): """The ``--key`` option must be specified when ``--cert`` is a file. Modifies the ``cert`` param to be a ``(cert, key)`` pair if needed. """ - cert = ctx.params.get('cert') - is_adhoc = cert == 'adhoc' + cert = ctx.params.get("cert") + is_adhoc = cert == "adhoc" if sys.version_info < (2, 7, 9): is_context = cert and not isinstance(cert, (text_type, bytes)) @@ -708,55 +714,64 @@ def _validate_key(ctx, param, value): if value is not None: if is_adhoc: raise click.BadParameter( - 'When "--cert" is "adhoc", "--key" is not used.', - ctx, param) + 'When "--cert" is "adhoc", "--key" is not used.', ctx, param + ) if is_context: raise click.BadParameter( - 'When "--cert" is an SSLContext object, "--key is not used.', - ctx, param) + 'When "--cert" is an SSLContext object, "--key is not used.', ctx, param + ) if not cert: - raise click.BadParameter( - '"--cert" must also be specified.', - ctx, param) + raise click.BadParameter('"--cert" must also be specified.', ctx, param) - ctx.params['cert'] = cert, value + ctx.params["cert"] = cert, value else: if cert and not (is_adhoc or is_context): - raise click.BadParameter( - 'Required when using "--cert".', - ctx, param) + raise click.BadParameter('Required when using "--cert".', ctx, param) return value -@click.command('run', short_help='Run a development server.') -@click.option('--host', '-h', default='127.0.0.1', - help='The interface to bind to.') -@click.option('--port', '-p', default=5000, - help='The port to bind to.') -@click.option('--cert', type=CertParamType(), - help='Specify a certificate file to use HTTPS.') -@click.option('--key', - type=click.Path(exists=True, dir_okay=False, resolve_path=True), - callback=_validate_key, expose_value=False, - help='The key file to use when specifying a certificate.') -@click.option('--reload/--no-reload', default=None, - help='Enable or disable the reloader. By default the reloader ' - 'is active if debug is enabled.') -@click.option('--debugger/--no-debugger', default=None, - help='Enable or disable the debugger. By default the debugger ' - 'is active if debug is enabled.') -@click.option('--eager-loading/--lazy-loader', default=None, - help='Enable or disable eager loading. By default eager ' - 'loading is enabled if the reloader is disabled.') -@click.option('--with-threads/--without-threads', default=True, - help='Enable or disable multithreading.') +@click.command("run", short_help="Run a development server.") +@click.option("--host", "-h", default="127.0.0.1", help="The interface to bind to.") +@click.option("--port", "-p", default=5000, help="The port to bind to.") +@click.option( + "--cert", type=CertParamType(), help="Specify a certificate file to use HTTPS." +) +@click.option( + "--key", + type=click.Path(exists=True, dir_okay=False, resolve_path=True), + callback=_validate_key, + expose_value=False, + help="The key file to use when specifying a certificate.", +) +@click.option( + "--reload/--no-reload", + default=None, + help="Enable or disable the reloader. By default the reloader " + "is active if debug is enabled.", +) +@click.option( + "--debugger/--no-debugger", + default=None, + help="Enable or disable the debugger. By default the debugger " + "is active if debug is enabled.", +) +@click.option( + "--eager-loading/--lazy-loader", + default=None, + help="Enable or disable eager loading. By default eager " + "loading is enabled if the reloader is disabled.", +) +@click.option( + "--with-threads/--without-threads", + default=True, + help="Enable or disable multithreading.", +) @pass_script_info -def run_command(info, host, port, reload, debugger, eager_loading, - with_threads, cert): +def run_command(info, host, port, reload, debugger, eager_loading, with_threads, cert): """Run a local development server. This server is for development purposes only. It does not provide @@ -780,11 +795,19 @@ def run_command(info, host, port, reload, debugger, eager_loading, app = DispatchingApp(info.load_app, use_eager_loading=eager_loading) from werkzeug.serving import run_simple - run_simple(host, port, app, use_reloader=reload, use_debugger=debugger, - threaded=with_threads, ssl_context=cert) + + run_simple( + host, + port, + app, + use_reloader=reload, + use_debugger=debugger, + threaded=with_threads, + ssl_context=cert, + ) -@click.command('shell', short_help='Run a shell in the app context.') +@click.command("shell", short_help="Run a shell in the app context.") @with_appcontext def shell_command(): """Run an interactive Python shell in the context of a given @@ -796,8 +819,9 @@ def shell_command(): """ import code from flask.globals import _app_ctx_stack + app = _app_ctx_stack.top.app - banner = 'Python %s on %s\nApp: %s [%s]\nInstance: %s' % ( + banner = "Python %s on %s\nApp: %s [%s]\nInstance: %s" % ( sys.version, sys.platform, app.import_name, @@ -808,68 +832,64 @@ def shell_command(): # Support the regular Python interpreter startup script if someone # is using it. - startup = os.environ.get('PYTHONSTARTUP') + startup = os.environ.get("PYTHONSTARTUP") if startup and os.path.isfile(startup): - with open(startup, 'r') as f: - eval(compile(f.read(), startup, 'exec'), ctx) + with open(startup, "r") as f: + eval(compile(f.read(), startup, "exec"), ctx) ctx.update(app.make_shell_context()) code.interact(banner=banner, local=ctx) -@click.command('routes', short_help='Show the routes for the app.') +@click.command("routes", short_help="Show the routes for the app.") @click.option( - '--sort', '-s', - type=click.Choice(('endpoint', 'methods', 'rule', 'match')), - default='endpoint', + "--sort", + "-s", + type=click.Choice(("endpoint", "methods", "rule", "match")), + default="endpoint", help=( 'Method to sort routes by. "match" is the order that Flask will match ' - 'routes when dispatching a request.' - ) -) -@click.option( - '--all-methods', - is_flag=True, - help="Show HEAD and OPTIONS methods." + "routes when dispatching a request." + ), ) +@click.option("--all-methods", is_flag=True, help="Show HEAD and OPTIONS methods.") @with_appcontext def routes_command(sort, all_methods): """Show all registered routes with endpoints and methods.""" rules = list(current_app.url_map.iter_rules()) if not rules: - click.echo('No routes were registered.') + click.echo("No routes were registered.") return - ignored_methods = set(() if all_methods else ('HEAD', 'OPTIONS')) + ignored_methods = set(() if all_methods else ("HEAD", "OPTIONS")) - if sort in ('endpoint', 'rule'): + if sort in ("endpoint", "rule"): rules = sorted(rules, key=attrgetter(sort)) - elif sort == 'methods': + elif sort == "methods": rules = sorted(rules, key=lambda rule: sorted(rule.methods)) - rule_methods = [ - ', '.join(sorted(rule.methods - ignored_methods)) for rule in rules - ] + rule_methods = [", ".join(sorted(rule.methods - ignored_methods)) for rule in rules] - headers = ('Endpoint', 'Methods', 'Rule') + headers = ("Endpoint", "Methods", "Rule") widths = ( max(len(rule.endpoint) for rule in rules), max(len(methods) for methods in rule_methods), max(len(rule.rule) for rule in rules), ) widths = [max(len(h), w) for h, w in zip(headers, widths)] - row = '{{0:<{0}}} {{1:<{1}}} {{2:<{2}}}'.format(*widths) + row = "{{0:<{0}}} {{1:<{1}}} {{2:<{2}}}".format(*widths) click.echo(row.format(*headers).strip()) - click.echo(row.format(*('-' * width for width in widths))) + click.echo(row.format(*("-" * width for width in widths))) for rule, methods in zip(rules, rule_methods): click.echo(row.format(rule.endpoint, methods, rule.rule).rstrip()) -cli = FlaskGroup(help="""\ +cli = FlaskGroup( + help="""\ A general utility script for Flask applications. Provides commands from Flask, extensions, and the application. Loads the @@ -882,30 +902,31 @@ debug mode. {prefix}{cmd} FLASK_ENV=development {prefix}flask run """.format( - cmd='export' if os.name == 'posix' else 'set', - prefix='$ ' if os.name == 'posix' else '> ' -)) + cmd="export" if os.name == "posix" else "set", + prefix="$ " if os.name == "posix" else "> ", + ) +) def main(as_module=False): args = sys.argv[1:] if as_module: - this_module = 'flask' + this_module = "flask" if sys.version_info < (2, 7): - this_module += '.cli' + this_module += ".cli" - name = 'python -m ' + this_module + name = "python -m " + this_module # Python rewrites "python -m flask" to the path to the file in argv. # Restore the original command so that the reloader works. - sys.argv = ['-m', this_module] + args + sys.argv = ["-m", this_module] + args else: name = None cli.main(args=args, prog_name=name) -if __name__ == '__main__': +if __name__ == "__main__": main(as_module=True) diff --git a/flask/config.py b/flask/config.py index a5475ed1..2f207a57 100644 --- a/flask/config.py +++ b/flask/config.py @@ -101,11 +101,12 @@ class Config(dict): if not rv: if silent: return False - raise RuntimeError('The environment variable %r is not set ' - 'and as such configuration could not be ' - 'loaded. Set this variable and make it ' - 'point to a configuration file' % - variable_name) + raise RuntimeError( + "The environment variable %r is not set " + "and as such configuration could not be " + "loaded. Set this variable and make it " + "point to a configuration file" % variable_name + ) return self.from_pyfile(rv, silent=silent) def from_pyfile(self, filename, silent=False): @@ -123,17 +124,15 @@ class Config(dict): `silent` parameter. """ filename = os.path.join(self.root_path, filename) - d = types.ModuleType('config') + d = types.ModuleType("config") d.__file__ = filename try: - with open(filename, mode='rb') as config_file: - exec(compile(config_file.read(), filename, 'exec'), d.__dict__) + with open(filename, mode="rb") as config_file: + exec(compile(config_file.read(), filename, "exec"), d.__dict__) except IOError as e: - if silent and e.errno in ( - errno.ENOENT, errno.EISDIR, errno.ENOTDIR - ): + if silent and e.errno in (errno.ENOENT, errno.EISDIR, errno.ENOTDIR): return False - e.strerror = 'Unable to load configuration file (%s)' % e.strerror + e.strerror = "Unable to load configuration file (%s)" % e.strerror raise self.from_object(d) return True @@ -197,7 +196,7 @@ class Config(dict): except IOError as e: if silent and e.errno in (errno.ENOENT, errno.EISDIR): return False - e.strerror = 'Unable to load configuration file (%s)' % e.strerror + e.strerror = "Unable to load configuration file (%s)" % e.strerror raise return self.from_mapping(obj) @@ -209,13 +208,13 @@ class Config(dict): """ mappings = [] if len(mapping) == 1: - if hasattr(mapping[0], 'items'): + if hasattr(mapping[0], "items"): mappings.append(mapping[0].items()) else: mappings.append(mapping[0]) elif len(mapping) > 1: raise TypeError( - 'expected at most 1 positional argument, got %d' % len(mapping) + "expected at most 1 positional argument, got %d" % len(mapping) ) mappings.append(kwargs.items()) for mapping in mappings: @@ -257,7 +256,7 @@ class Config(dict): if not k.startswith(namespace): continue if trim_namespace: - key = k[len(namespace):] + key = k[len(namespace) :] else: key = k if lowercase: @@ -266,4 +265,4 @@ class Config(dict): return rv def __repr__(self): - return '<%s %s>' % (self.__class__.__name__, dict.__repr__(self)) + return "<%s %s>" % (self.__class__.__name__, dict.__repr__(self)) diff --git a/flask/ctx.py b/flask/ctx.py index f8cdb60b..817086d7 100644 --- a/flask/ctx.py +++ b/flask/ctx.py @@ -89,7 +89,7 @@ class _AppCtxGlobals(object): def __repr__(self): top = _app_ctx_stack.top if top is not None: - return '' % top.app.name + return "" % top.app.name return object.__repr__(self) @@ -144,13 +144,17 @@ def copy_current_request_context(f): """ top = _request_ctx_stack.top if top is None: - raise RuntimeError('This decorator can only be used at local scopes ' - 'when a request context is on the stack. For instance within ' - 'view functions.') + raise RuntimeError( + "This decorator can only be used at local scopes " + "when a request context is on the stack. For instance within " + "view functions." + ) reqctx = top.copy() + def wrapper(*args, **kwargs): with reqctx: return f(*args, **kwargs) + return update_wrapper(wrapper, f) @@ -217,7 +221,7 @@ class AppContext(object): def push(self): """Binds the app context to the current context.""" self._refcnt += 1 - if hasattr(sys, 'exc_clear'): + if hasattr(sys, "exc_clear"): sys.exc_clear() _app_ctx_stack.push(self) appcontext_pushed.send(self.app) @@ -232,8 +236,7 @@ class AppContext(object): self.app.do_teardown_appcontext(exc) finally: rv = _app_ctx_stack.pop() - assert rv is self, 'Popped wrong app context. (%r instead of %r)' \ - % (rv, self) + assert rv is self, "Popped wrong app context. (%r instead of %r)" % (rv, self) appcontext_popped.send(self.app) def __enter__(self): @@ -314,8 +317,10 @@ class RequestContext(object): def _get_g(self): return _app_ctx_stack.top.g + def _set_g(self, value): _app_ctx_stack.top.g = value + g = property(_get_g, _set_g) del _get_g, _set_g @@ -332,10 +337,11 @@ class RequestContext(object): The current session object is used instead of reloading the original data. This prevents `flask.session` pointing to an out-of-date object. """ - return self.__class__(self.app, + return self.__class__( + self.app, environ=self.request.environ, request=self.request, - session=self.session + session=self.session, ) def match_request(self): @@ -343,8 +349,7 @@ class RequestContext(object): of the request. """ try: - url_rule, self.request.view_args = \ - self.url_adapter.match(return_rule=True) + url_rule, self.request.view_args = self.url_adapter.match(return_rule=True) self.request.url_rule = url_rule except HTTPException as e: self.request.routing_exception = e @@ -373,7 +378,7 @@ class RequestContext(object): else: self._implicit_app_ctx_stack.append(None) - if hasattr(sys, 'exc_clear'): + if hasattr(sys, "exc_clear"): sys.exc_clear() _request_ctx_stack.push(self) @@ -384,9 +389,7 @@ class RequestContext(object): # pushed, otherwise stream_with_context loses the session. if self.session is None: session_interface = self.app.session_interface - self.session = session_interface.open_session( - self.app, self.request - ) + self.session = session_interface.open_session(self.app, self.request) if self.session is None: self.session = session_interface.make_null_session(self.app) @@ -414,10 +417,10 @@ class RequestContext(object): # we do that now. This will only go into effect on Python 2.x, # on 3.x it disappears automatically at the end of the exception # stack. - if hasattr(sys, 'exc_clear'): + if hasattr(sys, "exc_clear"): sys.exc_clear() - request_close = getattr(self.request, 'close', None) + request_close = getattr(self.request, "close", None) if request_close is not None: request_close() clear_request = True @@ -427,18 +430,20 @@ class RequestContext(object): # get rid of circular dependencies at the end of the request # so that we don't require the GC to be active. if clear_request: - rv.request.environ['werkzeug.request'] = None + rv.request.environ["werkzeug.request"] = None # Get rid of the app as well if necessary. if app_ctx is not None: app_ctx.pop(exc) - assert rv is self, 'Popped wrong request context. ' \ - '(%r instead of %r)' % (rv, self) + assert ( + rv is self + ), "Popped wrong request context. " "(%r instead of %r)" % (rv, self) def auto_pop(self, exc): - if self.request.environ.get('flask._preserve_context') or \ - (exc is not None and self.app.preserve_context_on_exception): + if self.request.environ.get("flask._preserve_context") or ( + exc is not None and self.app.preserve_context_on_exception + ): self.preserved = True self._preserved_exc = exc else: @@ -460,7 +465,7 @@ class RequestContext(object): reraise(exc_type, exc_value, tb) def __repr__(self): - return '<%s \'%s\' [%s] of %s>' % ( + return "<%s '%s' [%s] of %s>" % ( self.__class__.__name__, self.request.url, self.request.method, diff --git a/flask/debughelpers.py b/flask/debughelpers.py index e9765f20..c92b0153 100644 --- a/flask/debughelpers.py +++ b/flask/debughelpers.py @@ -32,17 +32,20 @@ class DebugFilesKeyError(KeyError, AssertionError): def __init__(self, request, key): form_matches = request.form.getlist(key) - buf = ['You tried to access the file "%s" in the request.files ' - 'dictionary but it does not exist. The mimetype for the request ' - 'is "%s" instead of "multipart/form-data" which means that no ' - 'file contents were transmitted. To fix this error you should ' - 'provide enctype="multipart/form-data" in your form.' % - (key, request.mimetype)] + buf = [ + 'You tried to access the file "%s" in the request.files ' + "dictionary but it does not exist. The mimetype for the request " + 'is "%s" instead of "multipart/form-data" which means that no ' + "file contents were transmitted. To fix this error you should " + 'provide enctype="multipart/form-data" in your form.' + % (key, request.mimetype) + ] if form_matches: - buf.append('\n\nThe browser instead transmitted some file names. ' - 'This was submitted: %s' % ', '.join('"%s"' % x - for x in form_matches)) - self.msg = ''.join(buf) + buf.append( + "\n\nThe browser instead transmitted some file names. " + "This was submitted: %s" % ", ".join('"%s"' % x for x in form_matches) + ) + self.msg = "".join(buf) def __str__(self): return self.msg @@ -56,23 +59,28 @@ class FormDataRoutingRedirect(AssertionError): def __init__(self, request): exc = request.routing_exception - buf = ['A request was sent to this URL (%s) but a redirect was ' - 'issued automatically by the routing system to "%s".' - % (request.url, exc.new_url)] + buf = [ + "A request was sent to this URL (%s) but a redirect was " + 'issued automatically by the routing system to "%s".' + % (request.url, exc.new_url) + ] # In case just a slash was appended we can be extra helpful - if request.base_url + '/' == exc.new_url.split('?')[0]: - buf.append(' The URL was defined with a trailing slash so ' - 'Flask will automatically redirect to the URL ' - 'with the trailing slash if it was accessed ' - 'without one.') + if request.base_url + "/" == exc.new_url.split("?")[0]: + buf.append( + " The URL was defined with a trailing slash so " + "Flask will automatically redirect to the URL " + "with the trailing slash if it was accessed " + "without one." + ) - buf.append(' Make sure to directly send your %s-request to this URL ' - 'since we can\'t make browsers or HTTP clients redirect ' - 'with form data reliably or without user interaction.' % - request.method) - buf.append('\n\nNote: this exception is only raised in debug mode') - AssertionError.__init__(self, ''.join(buf).encode('utf-8')) + buf.append( + " Make sure to directly send your %s-request to this URL " + "since we can't make browsers or HTTP clients redirect " + "with form data reliably or without user interaction." % request.method + ) + buf.append("\n\nNote: this exception is only raised in debug mode") + AssertionError.__init__(self, "".join(buf).encode("utf-8")) def attach_enctype_error_multidict(request): @@ -81,6 +89,7 @@ def attach_enctype_error_multidict(request): object is accessed. """ oldcls = request.files.__class__ + class newcls(oldcls): def __getitem__(self, key): try: @@ -89,26 +98,27 @@ def attach_enctype_error_multidict(request): if key not in request.form: raise raise DebugFilesKeyError(request, key) + newcls.__name__ = oldcls.__name__ newcls.__module__ = oldcls.__module__ request.files.__class__ = newcls def _dump_loader_info(loader): - yield 'class: %s.%s' % (type(loader).__module__, type(loader).__name__) + yield "class: %s.%s" % (type(loader).__module__, type(loader).__name__) for key, value in sorted(loader.__dict__.items()): - if key.startswith('_'): + if key.startswith("_"): continue if isinstance(value, (tuple, list)): if not all(isinstance(x, (str, text_type)) for x in value): continue - yield '%s:' % key + yield "%s:" % key for item in value: - yield ' - %s' % item + yield " - %s" % item continue elif not isinstance(value, (str, text_type, int, float, bool)): continue - yield '%s: %r' % (key, value) + yield "%s: %r" % (key, value) def explain_template_loading_attempts(app, template, attempts): @@ -124,45 +134,50 @@ def explain_template_loading_attempts(app, template, attempts): if isinstance(srcobj, Flask): src_info = 'application "%s"' % srcobj.import_name elif isinstance(srcobj, Blueprint): - src_info = 'blueprint "%s" (%s)' % (srcobj.name, - srcobj.import_name) + src_info = 'blueprint "%s" (%s)' % (srcobj.name, srcobj.import_name) else: src_info = repr(srcobj) - info.append('% 5d: trying loader of %s' % ( - idx + 1, src_info)) + info.append("% 5d: trying loader of %s" % (idx + 1, src_info)) for line in _dump_loader_info(loader): - info.append(' %s' % line) + info.append(" %s" % line) if triple is None: - detail = 'no match' + detail = "no match" else: - detail = 'found (%r)' % (triple[1] or '') + detail = "found (%r)" % (triple[1] or "") total_found += 1 - info.append(' -> %s' % detail) + info.append(" -> %s" % detail) seems_fishy = False if total_found == 0: - info.append('Error: the template could not be found.') + info.append("Error: the template could not be found.") seems_fishy = True elif total_found > 1: - info.append('Warning: multiple loaders returned a match for the template.') + info.append("Warning: multiple loaders returned a match for the template.") seems_fishy = True if blueprint is not None and seems_fishy: - info.append(' The template was looked up from an endpoint that ' - 'belongs to the blueprint "%s".' % blueprint) - info.append(' Maybe you did not place a template in the right folder?') - info.append(' See http://flask.pocoo.org/docs/blueprints/#templates') + info.append( + " The template was looked up from an endpoint that " + 'belongs to the blueprint "%s".' % blueprint + ) + info.append(" Maybe you did not place a template in the right folder?") + info.append(" See http://flask.pocoo.org/docs/blueprints/#templates") - app.logger.info('\n'.join(info)) + app.logger.info("\n".join(info)) def explain_ignored_app_run(): - if os.environ.get('WERKZEUG_RUN_MAIN') != 'true': - warn(Warning('Silently ignoring app.run() because the ' - 'application is run from the flask command line ' - 'executable. Consider putting app.run() behind an ' - 'if __name__ == "__main__" guard to silence this ' - 'warning.'), stacklevel=3) + if os.environ.get("WERKZEUG_RUN_MAIN") != "true": + warn( + Warning( + "Silently ignoring app.run() because the " + "application is run from the flask command line " + "executable. Consider putting app.run() behind an " + 'if __name__ == "__main__" guard to silence this ' + "warning." + ), + stacklevel=3, + ) diff --git a/flask/globals.py b/flask/globals.py index 7d50a6f6..8fc3ffe4 100644 --- a/flask/globals.py +++ b/flask/globals.py @@ -14,21 +14,21 @@ from functools import partial from werkzeug.local import LocalStack, LocalProxy -_request_ctx_err_msg = '''\ +_request_ctx_err_msg = """\ Working outside of request context. This typically means that you attempted to use functionality that needed an active HTTP request. Consult the documentation on testing for information about how to avoid this problem.\ -''' -_app_ctx_err_msg = '''\ +""" +_app_ctx_err_msg = """\ Working outside of application context. This typically means that you attempted to use functionality that needed to interface with the current application object in some way. To solve this, set up an application context with app.app_context(). See the documentation for more information.\ -''' +""" def _lookup_req_object(name): @@ -56,6 +56,6 @@ def _find_app(): _request_ctx_stack = LocalStack() _app_ctx_stack = LocalStack() current_app = LocalProxy(_find_app) -request = LocalProxy(partial(_lookup_req_object, 'request')) -session = LocalProxy(partial(_lookup_req_object, 'session')) -g = LocalProxy(partial(_lookup_app_object, 'g')) +request = LocalProxy(partial(_lookup_req_object, "request")) +session = LocalProxy(partial(_lookup_req_object, "session")) +g = LocalProxy(partial(_lookup_app_object, "g")) diff --git a/flask/helpers.py b/flask/helpers.py index 03129df9..389a86df 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -24,15 +24,13 @@ from functools import update_wrapper from werkzeug.urls import url_quote from werkzeug.datastructures import Headers, Range -from werkzeug.exceptions import BadRequest, NotFound, \ - RequestedRangeNotSatisfiable +from werkzeug.exceptions import BadRequest, NotFound, RequestedRangeNotSatisfiable from werkzeug.wsgi import wrap_file from jinja2 import FileSystemLoader from .signals import message_flashed -from .globals import session, _request_ctx_stack, _app_ctx_stack, \ - current_app, request +from .globals import session, _request_ctx_stack, _app_ctx_stack, current_app, request from ._compat import string_types, text_type, PY2, fspath # sentinel @@ -42,8 +40,9 @@ _missing = object() # what separators does this operating system provide that are not a slash? # this is used by the send_from_directory function to ensure that nobody is # able to access files from outside the filesystem. -_os_alt_seps = list(sep for sep in [os.path.sep, os.path.altsep] - if sep not in (None, '/')) +_os_alt_seps = list( + sep for sep in [os.path.sep, os.path.altsep] if sep not in (None, "/") +) def get_env(): @@ -51,7 +50,7 @@ def get_env(): :envvar:`FLASK_ENV` environment variable. The default is ``'production'``. """ - return os.environ.get('FLASK_ENV') or 'production' + return os.environ.get("FLASK_ENV") or "production" def get_debug_flag(): @@ -60,12 +59,12 @@ def get_debug_flag(): ``True`` if :func:`.get_env` returns ``'development'``, or ``False`` otherwise. """ - val = os.environ.get('FLASK_DEBUG') + val = os.environ.get("FLASK_DEBUG") if not val: - return get_env() == 'development' + return get_env() == "development" - return val.lower() not in ('0', 'false', 'no') + return val.lower() not in ("0", "false", "no") def get_load_dotenv(default=True): @@ -75,20 +74,19 @@ def get_load_dotenv(default=True): :param default: What to return if the env var isn't set. """ - val = os.environ.get('FLASK_SKIP_DOTENV') + val = os.environ.get("FLASK_SKIP_DOTENV") if not val: return default - return val.lower() in ('0', 'false', 'no') + return val.lower() in ("0", "false", "no") def _endpoint_from_view_func(view_func): """Internal helper that returns the default endpoint for a given function. This always is the function name. """ - assert view_func is not None, 'expected view func if endpoint ' \ - 'is not provided.' + assert view_func is not None, "expected view func if endpoint " "is not provided." return view_func.__name__ @@ -129,16 +127,20 @@ def stream_with_context(generator_or_function): try: gen = iter(generator_or_function) except TypeError: + def decorator(*args, **kwargs): gen = generator_or_function(*args, **kwargs) return stream_with_context(gen) + return update_wrapper(decorator, generator_or_function) def generator(): ctx = _request_ctx_stack.top if ctx is None: - raise RuntimeError('Attempted to stream with context but ' - 'there was no context in the first place to keep around.') + raise RuntimeError( + "Attempted to stream with context but " + "there was no context in the first place to keep around." + ) with ctx: # Dummy sentinel. Has to be inside the context block or we're # not actually keeping the context around. @@ -152,7 +154,7 @@ def stream_with_context(generator_or_function): for item in gen: yield item finally: - if hasattr(gen, 'close'): + if hasattr(gen, "close"): gen.close() # The trick is to start the generator. Then the code execution runs until @@ -291,9 +293,9 @@ def url_for(endpoint, **values): if appctx is None: raise RuntimeError( - 'Attempted to generate a URL without the application context being' - ' pushed. This has to be executed when application context is' - ' available.' + "Attempted to generate a URL without the application context being" + " pushed. This has to be executed when application context is" + " available." ) # If request specific information is available we have some extra @@ -302,13 +304,13 @@ def url_for(endpoint, **values): url_adapter = reqctx.url_adapter blueprint_name = request.blueprint - if endpoint[:1] == '.': + if endpoint[:1] == ".": if blueprint_name is not None: endpoint = blueprint_name + endpoint else: endpoint = endpoint[1:] - external = values.pop('_external', False) + external = values.pop("_external", False) # Otherwise go with the url adapter from the appctx and make # the URLs external by default. @@ -317,16 +319,16 @@ def url_for(endpoint, **values): if url_adapter is None: raise RuntimeError( - 'Application was not able to create a URL adapter for request' - ' independent URL generation. You might be able to fix this by' - ' setting the SERVER_NAME config variable.' + "Application was not able to create a URL adapter for request" + " independent URL generation. You might be able to fix this by" + " setting the SERVER_NAME config variable." ) - external = values.pop('_external', True) + external = values.pop("_external", True) - anchor = values.pop('_anchor', None) - method = values.pop('_method', None) - scheme = values.pop('_scheme', None) + anchor = values.pop("_anchor", None) + method = values.pop("_method", None) + scheme = values.pop("_scheme", None) appctx.app.inject_url_defaults(endpoint, values) # This is not the best way to deal with this but currently the @@ -335,28 +337,29 @@ def url_for(endpoint, **values): old_scheme = None if scheme is not None: if not external: - raise ValueError('When specifying _scheme, _external must be True') + raise ValueError("When specifying _scheme, _external must be True") old_scheme = url_adapter.url_scheme url_adapter.url_scheme = scheme try: try: - rv = url_adapter.build(endpoint, values, method=method, - force_external=external) + rv = url_adapter.build( + endpoint, values, method=method, force_external=external + ) finally: if old_scheme is not None: url_adapter.url_scheme = old_scheme except BuildError as error: # We need to inject the values again so that the app callback can # deal with that sort of stuff. - values['_external'] = external - values['_anchor'] = anchor - values['_method'] = method - values['_scheme'] = scheme + values["_external"] = external + values["_anchor"] = anchor + values["_method"] = method + values["_scheme"] = scheme return appctx.app.handle_url_build_error(error, endpoint, values) if anchor is not None: - rv += '#' + url_quote(anchor) + rv += "#" + url_quote(anchor) return rv @@ -379,11 +382,10 @@ def get_template_attribute(template_name, attribute): :param template_name: the name of the template :param attribute: the name of the variable of macro to access """ - return getattr(current_app.jinja_env.get_template(template_name).module, - attribute) + return getattr(current_app.jinja_env.get_template(template_name).module, attribute) -def flash(message, category='message'): +def flash(message, category="message"): """Flashes a message to the next request. In order to remove the flashed message from the session and to display it to the user, the template has to call :func:`get_flashed_messages`. @@ -405,11 +407,12 @@ def flash(message, category='message'): # This assumed that changes made to mutable structures in the session are # always in sync with the session object, which is not true for session # implementations that use external storage for keeping their keys/values. - flashes = session.get('_flashes', []) + flashes = session.get("_flashes", []) flashes.append((category, message)) - session['_flashes'] = flashes - message_flashed.send(current_app._get_current_object(), - message=message, category=category) + session["_flashes"] = flashes + message_flashed.send( + current_app._get_current_object(), message=message, category=category + ) def get_flashed_messages(with_categories=False, category_filter=[]): @@ -442,8 +445,9 @@ def get_flashed_messages(with_categories=False, category_filter=[]): """ flashes = _request_ctx_stack.top.flashes if flashes is None: - _request_ctx_stack.top.flashes = flashes = session.pop('_flashes') \ - if '_flashes' in session else [] + _request_ctx_stack.top.flashes = flashes = ( + session.pop("_flashes") if "_flashes" in session else [] + ) if category_filter: flashes = list(filter(lambda f: f[0] in category_filter, flashes)) if not with_categories: @@ -451,9 +455,16 @@ def get_flashed_messages(with_categories=False, category_filter=[]): return flashes -def send_file(filename_or_fp, mimetype=None, as_attachment=False, - attachment_filename=None, add_etags=True, - cache_timeout=None, conditional=False, last_modified=None): +def send_file( + filename_or_fp, + mimetype=None, + as_attachment=False, + attachment_filename=None, + add_etags=True, + cache_timeout=None, + conditional=False, + last_modified=None, +): """Sends the contents of a file to the client. This will use the most efficient method available and configured. By default it will try to use the WSGI server's file_wrapper support. Alternatively @@ -545,7 +556,7 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False, mtime = None fsize = None - if hasattr(filename_or_fp, '__fspath__'): + if hasattr(filename_or_fp, "__fspath__"): filename_or_fp = fspath(filename_or_fp) if isinstance(filename_or_fp, string_types): @@ -561,62 +572,67 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False, if mimetype is None: if attachment_filename is not None: - mimetype = mimetypes.guess_type(attachment_filename)[0] \ - or 'application/octet-stream' + mimetype = ( + mimetypes.guess_type(attachment_filename)[0] + or "application/octet-stream" + ) if mimetype is None: raise ValueError( - 'Unable to infer MIME-type because no filename is available. ' - 'Please set either `attachment_filename`, pass a filepath to ' - '`filename_or_fp` or set your own MIME-type via `mimetype`.' + "Unable to infer MIME-type because no filename is available. " + "Please set either `attachment_filename`, pass a filepath to " + "`filename_or_fp` or set your own MIME-type via `mimetype`." ) headers = Headers() if as_attachment: if attachment_filename is None: - raise TypeError('filename unavailable, required for ' - 'sending as attachment') + raise TypeError( + "filename unavailable, required for " "sending as attachment" + ) if not isinstance(attachment_filename, text_type): - attachment_filename = attachment_filename.decode('utf-8') + attachment_filename = attachment_filename.decode("utf-8") try: - attachment_filename = attachment_filename.encode('ascii') + attachment_filename = attachment_filename.encode("ascii") except UnicodeEncodeError: filenames = { - 'filename': unicodedata.normalize( - 'NFKD', attachment_filename).encode('ascii', 'ignore'), - 'filename*': "UTF-8''%s" % url_quote(attachment_filename), + "filename": unicodedata.normalize("NFKD", attachment_filename).encode( + "ascii", "ignore" + ), + "filename*": "UTF-8''%s" % url_quote(attachment_filename), } else: - filenames = {'filename': attachment_filename} + filenames = {"filename": attachment_filename} - headers.add('Content-Disposition', 'attachment', **filenames) + headers.add("Content-Disposition", "attachment", **filenames) if current_app.use_x_sendfile and filename: if file is not None: file.close() - headers['X-Sendfile'] = filename + headers["X-Sendfile"] = filename fsize = os.path.getsize(filename) - headers['Content-Length'] = fsize + headers["Content-Length"] = fsize data = None else: if file is None: - file = open(filename, 'rb') + file = open(filename, "rb") mtime = os.path.getmtime(filename) fsize = os.path.getsize(filename) - headers['Content-Length'] = fsize + headers["Content-Length"] = fsize elif isinstance(file, io.BytesIO): try: fsize = file.getbuffer().nbytes except AttributeError: # Python 2 doesn't have getbuffer fsize = len(file.getvalue()) - headers['Content-Length'] = fsize + headers["Content-Length"] = fsize data = wrap_file(request.environ, file) - rv = current_app.response_class(data, mimetype=mimetype, headers=headers, - direct_passthrough=True) + rv = current_app.response_class( + data, mimetype=mimetype, headers=headers, direct_passthrough=True + ) if last_modified is not None: rv.last_modified = last_modified @@ -634,22 +650,29 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False, from warnings import warn try: - rv.set_etag('%s-%s-%s' % ( - os.path.getmtime(filename), - os.path.getsize(filename), - adler32( - filename.encode('utf-8') if isinstance(filename, text_type) - else filename - ) & 0xffffffff - )) + rv.set_etag( + "%s-%s-%s" + % ( + os.path.getmtime(filename), + os.path.getsize(filename), + adler32( + filename.encode("utf-8") + if isinstance(filename, text_type) + else filename + ) + & 0xFFFFFFFF, + ) + ) except OSError: - warn('Access %s failed, maybe it does not exist, so ignore etags in ' - 'headers' % filename, stacklevel=2) + warn( + "Access %s failed, maybe it does not exist, so ignore etags in " + "headers" % filename, + stacklevel=2, + ) if conditional: try: - rv = rv.make_conditional(request, accept_ranges=True, - complete_length=fsize) + rv = rv.make_conditional(request, accept_ranges=True, complete_length=fsize) except RequestedRangeNotSatisfiable: if file is not None: file.close() @@ -657,7 +680,7 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False, # make sure we don't send x-sendfile for servers that # ignore the 304 status code for x-sendfile. if rv.status_code == 304: - rv.headers.pop('x-sendfile', None) + rv.headers.pop("x-sendfile", None) return rv @@ -682,14 +705,14 @@ def safe_join(directory, *pathnames): parts = [directory] for filename in pathnames: - if filename != '': + if filename != "": filename = posixpath.normpath(filename) if ( any(sep in filename for sep in _os_alt_seps) or os.path.isabs(filename) - or filename == '..' - or filename.startswith('../') + or filename == ".." + or filename.startswith("../") ): raise NotFound() @@ -735,7 +758,7 @@ def send_from_directory(directory, filename, **options): raise NotFound() except (TypeError, ValueError): raise BadRequest() - options.setdefault('conditional', True) + options.setdefault("conditional", True) return send_file(filename, **options) @@ -747,7 +770,7 @@ def get_root_path(import_name): """ # Module already imported and has a file attribute. Use that first. mod = sys.modules.get(import_name) - if mod is not None and hasattr(mod, '__file__'): + if mod is not None and hasattr(mod, "__file__"): return os.path.dirname(os.path.abspath(mod.__file__)) # Next attempt: check the loader. @@ -756,30 +779,32 @@ def get_root_path(import_name): # Loader does not exist or we're referring to an unloaded main module # or a main module without path (interactive sessions), go with the # current working directory. - if loader is None or import_name == '__main__': + if loader is None or import_name == "__main__": return os.getcwd() # For .egg, zipimporter does not have get_filename until Python 2.7. # Some other loaders might exhibit the same behavior. - if hasattr(loader, 'get_filename'): + if hasattr(loader, "get_filename"): filepath = loader.get_filename(import_name) else: # Fall back to imports. __import__(import_name) mod = sys.modules[import_name] - filepath = getattr(mod, '__file__', None) + filepath = getattr(mod, "__file__", None) # If we don't have a filepath it might be because we are a # namespace package. In this case we pick the root path from the # first module that is contained in our package. if filepath is None: - raise RuntimeError('No root path can be found for the provided ' - 'module "%s". This can happen because the ' - 'module came from an import hook that does ' - 'not provide file name information or because ' - 'it\'s a namespace package. In this case ' - 'the root path needs to be explicitly ' - 'provided.' % import_name) + raise RuntimeError( + "No root path can be found for the provided " + 'module "%s". This can happen because the ' + "module came from an import hook that does " + "not provide file name information or because " + "it's a namespace package. In this case " + "the root path needs to be explicitly " + "provided." % import_name + ) # filepath is import_name.py for a module, or __init__.py for a package. return os.path.dirname(os.path.abspath(filepath)) @@ -791,21 +816,26 @@ def _matching_loader_thinks_module_is_package(loader, mod_name): """ # If the loader can tell us if something is a package, we can # directly ask the loader. - if hasattr(loader, 'is_package'): + if hasattr(loader, "is_package"): return loader.is_package(mod_name) # importlib's namespace loaders do not have this functionality but # all the modules it loads are packages, so we can take advantage of # this information. - elif (loader.__class__.__module__ == '_frozen_importlib' and - loader.__class__.__name__ == 'NamespaceLoader'): + elif ( + loader.__class__.__module__ == "_frozen_importlib" + and loader.__class__.__name__ == "NamespaceLoader" + ): return True # Otherwise we need to fail with an error that explains what went # wrong. raise AttributeError( - ('%s.is_package() method is missing but is required by Flask of ' - 'PEP 302 import hooks. If you do not use import hooks and ' - 'you encounter this error please file a bug against Flask.') % - loader.__class__.__name__) + ( + "%s.is_package() method is missing but is required by Flask of " + "PEP 302 import hooks. If you do not use import hooks and " + "you encounter this error please file a bug against Flask." + ) + % loader.__class__.__name__ + ) def find_package(import_name): @@ -816,16 +846,16 @@ def find_package(import_name): import the module. The prefix is the path below which a UNIX like folder structure exists (lib, share etc.). """ - root_mod_name = import_name.split('.')[0] + root_mod_name = import_name.split(".")[0] loader = pkgutil.get_loader(root_mod_name) - if loader is None or import_name == '__main__': + if loader is None or import_name == "__main__": # import name is not found, or interactive/main module package_path = os.getcwd() else: # For .egg, zipimporter does not have get_filename until Python 2.7. - if hasattr(loader, 'get_filename'): + if hasattr(loader, "get_filename"): filename = loader.get_filename(root_mod_name) - elif hasattr(loader, 'archive'): + elif hasattr(loader, "archive"): # zipimporter's loader.archive points to the .egg or .zip # archive filename is dropped in call to dirname below. filename = loader.archive @@ -841,21 +871,20 @@ def find_package(import_name): # In case the root module is a package we need to chop of the # rightmost part. This needs to go through a helper function # because of python 3.3 namespace packages. - if _matching_loader_thinks_module_is_package( - loader, root_mod_name): + if _matching_loader_thinks_module_is_package(loader, root_mod_name): package_path = os.path.dirname(package_path) site_parent, site_folder = os.path.split(package_path) py_prefix = os.path.abspath(sys.prefix) if package_path.startswith(py_prefix): return py_prefix, package_path - elif site_folder.lower() == 'site-packages': + elif site_folder.lower() == "site-packages": parent, folder = os.path.split(site_parent) # Windows like installations - if folder.lower() == 'lib': + if folder.lower() == "lib": base_dir = parent # UNIX like installations - elif os.path.basename(parent).lower() == 'lib': + elif os.path.basename(parent).lower() == "lib": base_dir = os.path.dirname(parent) else: base_dir = site_parent @@ -921,8 +950,9 @@ class _PackageBoundObject(object): self._static_folder = value static_folder = property( - _get_static_folder, _set_static_folder, - doc='The absolute path to the configured static folder.' + _get_static_folder, + _set_static_folder, + doc="The absolute path to the configured static folder.", ) del _get_static_folder, _set_static_folder @@ -931,14 +961,15 @@ class _PackageBoundObject(object): return self._static_url_path if self.static_folder is not None: - return '/' + os.path.basename(self.static_folder) + return "/" + os.path.basename(self.static_folder) def _set_static_url_path(self, value): self._static_url_path = value static_url_path = property( - _get_static_url_path, _set_static_url_path, - doc='The URL prefix that the static route will be registered for.' + _get_static_url_path, + _set_static_url_path, + doc="The URL prefix that the static route will be registered for.", ) del _get_static_url_path, _set_static_url_path @@ -958,8 +989,7 @@ class _PackageBoundObject(object): .. versionadded:: 0.5 """ if self.template_folder is not None: - return FileSystemLoader(os.path.join(self.root_path, - self.template_folder)) + return FileSystemLoader(os.path.join(self.root_path, self.template_folder)) def get_send_file_max_age(self, filename): """Provides default cache_timeout for the :func:`send_file` functions. @@ -994,14 +1024,15 @@ class _PackageBoundObject(object): .. versionadded:: 0.5 """ if not self.has_static_folder: - raise RuntimeError('No static folder for this object') + raise RuntimeError("No static folder for this object") # Ensure get_send_file_max_age is called in all cases. # Here, we ensure get_send_file_max_age is called for Blueprints. cache_timeout = self.get_send_file_max_age(filename) - return send_from_directory(self.static_folder, filename, - cache_timeout=cache_timeout) + return send_from_directory( + self.static_folder, filename, cache_timeout=cache_timeout + ) - def open_resource(self, resource, mode='rb'): + def open_resource(self, resource, mode="rb"): """Opens a resource from the application's resource folder. To see how this works, consider the following folder structure:: @@ -1024,8 +1055,8 @@ class _PackageBoundObject(object): subfolders use forward slashes as separator. :param mode: resource file opening mode, default is 'rb'. """ - if mode not in ('r', 'rb'): - raise ValueError('Resources can only be opened for reading') + if mode not in ("r", "rb"): + raise ValueError("Resources can only be opened for reading") return open(os.path.join(self.root_path, resource), mode) @@ -1052,7 +1083,7 @@ def is_ip(value): :return: True if string is an IP address :rtype: bool """ - if PY2 and os.name == 'nt': + if PY2 and os.name == "nt": try: socket.inet_aton(value) return True diff --git a/flask/json/__init__.py b/flask/json/__init__.py index fbe6b92f..80019250 100644 --- a/flask/json/__init__.py +++ b/flask/json/__init__.py @@ -23,12 +23,20 @@ from itsdangerous import json as _json # Figure out if simplejson escapes slashes. This behavior was changed # from one version to another without reason. -_slash_escape = '\\/' not in _json.dumps('/') +_slash_escape = "\\/" not in _json.dumps("/") -__all__ = ['dump', 'dumps', 'load', 'loads', 'htmlsafe_dump', - 'htmlsafe_dumps', 'JSONDecoder', 'JSONEncoder', - 'jsonify'] +__all__ = [ + "dump", + "dumps", + "load", + "loads", + "htmlsafe_dump", + "htmlsafe_dumps", + "JSONDecoder", + "JSONEncoder", + "jsonify", +] def _wrap_reader_for_text(fp, encoding): @@ -39,7 +47,7 @@ def _wrap_reader_for_text(fp, encoding): def _wrap_writer_for_text(fp, encoding): try: - fp.write('') + fp.write("") except TypeError: fp = io.TextIOWrapper(fp, encoding) return fp @@ -76,7 +84,7 @@ class JSONEncoder(_json.JSONEncoder): return http_date(o.timetuple()) if isinstance(o, uuid.UUID): return str(o) - if hasattr(o, '__html__'): + if hasattr(o, "__html__"): return text_type(o.__html__()) return _json.JSONEncoder.default(self, o) @@ -94,18 +102,17 @@ def _dump_arg_defaults(kwargs): if current_app: bp = current_app.blueprints.get(request.blueprint) if request else None kwargs.setdefault( - 'cls', - bp.json_encoder if bp and bp.json_encoder - else current_app.json_encoder + "cls", + bp.json_encoder if bp and bp.json_encoder else current_app.json_encoder, ) - if not current_app.config['JSON_AS_ASCII']: - kwargs.setdefault('ensure_ascii', False) + if not current_app.config["JSON_AS_ASCII"]: + kwargs.setdefault("ensure_ascii", False) - kwargs.setdefault('sort_keys', current_app.config['JSON_SORT_KEYS']) + kwargs.setdefault("sort_keys", current_app.config["JSON_SORT_KEYS"]) else: - kwargs.setdefault('sort_keys', True) - kwargs.setdefault('cls', JSONEncoder) + kwargs.setdefault("sort_keys", True) + kwargs.setdefault("cls", JSONEncoder) def _load_arg_defaults(kwargs): @@ -113,12 +120,11 @@ def _load_arg_defaults(kwargs): if current_app: bp = current_app.blueprints.get(request.blueprint) if request else None kwargs.setdefault( - 'cls', - bp.json_decoder if bp and bp.json_decoder - else current_app.json_decoder + "cls", + bp.json_decoder if bp and bp.json_decoder else current_app.json_decoder, ) else: - kwargs.setdefault('cls', JSONDecoder) + kwargs.setdefault("cls", JSONDecoder) def detect_encoding(data): @@ -134,34 +140,34 @@ def detect_encoding(data): head = data[:4] if head[:3] == codecs.BOM_UTF8: - return 'utf-8-sig' + return "utf-8-sig" - if b'\x00' not in head: - return 'utf-8' + if b"\x00" not in head: + return "utf-8" if head in (codecs.BOM_UTF32_BE, codecs.BOM_UTF32_LE): - return 'utf-32' + return "utf-32" if head[:2] in (codecs.BOM_UTF16_BE, codecs.BOM_UTF16_LE): - return 'utf-16' + return "utf-16" if len(head) == 4: - if head[:3] == b'\x00\x00\x00': - return 'utf-32-be' + if head[:3] == b"\x00\x00\x00": + return "utf-32-be" - if head[::2] == b'\x00\x00': - return 'utf-16-be' + if head[::2] == b"\x00\x00": + return "utf-16-be" - if head[1:] == b'\x00\x00\x00': - return 'utf-32-le' + if head[1:] == b"\x00\x00\x00": + return "utf-32-le" - if head[1::2] == b'\x00\x00': - return 'utf-16-le' + if head[1::2] == b"\x00\x00": + return "utf-16-le" if len(head) == 2: - return 'utf-16-be' if head.startswith(b'\x00') else 'utf-16-le' + return "utf-16-be" if head.startswith(b"\x00") else "utf-16-le" - return 'utf-8' + return "utf-8" def dumps(obj, **kwargs): @@ -175,7 +181,7 @@ def dumps(obj, **kwargs): and can be overridden by the simplejson ``ensure_ascii`` parameter. """ _dump_arg_defaults(kwargs) - encoding = kwargs.pop('encoding', None) + encoding = kwargs.pop("encoding", None) rv = _json.dumps(obj, **kwargs) if encoding is not None and isinstance(rv, text_type): rv = rv.encode(encoding) @@ -185,7 +191,7 @@ def dumps(obj, **kwargs): def dump(obj, fp, **kwargs): """Like :func:`dumps` but writes into a file object.""" _dump_arg_defaults(kwargs) - encoding = kwargs.pop('encoding', None) + encoding = kwargs.pop("encoding", None) if encoding is not None: fp = _wrap_writer_for_text(fp, encoding) _json.dump(obj, fp, **kwargs) @@ -198,7 +204,7 @@ def loads(s, **kwargs): """ _load_arg_defaults(kwargs) if isinstance(s, bytes): - encoding = kwargs.pop('encoding', None) + encoding = kwargs.pop("encoding", None) if encoding is None: encoding = detect_encoding(s) s = s.decode(encoding) @@ -210,7 +216,7 @@ def load(fp, **kwargs): """ _load_arg_defaults(kwargs) if not PY2: - fp = _wrap_reader_for_text(fp, kwargs.pop('encoding', None) or 'utf-8') + fp = _wrap_reader_for_text(fp, kwargs.pop("encoding", None) or "utf-8") return _json.load(fp, **kwargs) @@ -239,13 +245,15 @@ def htmlsafe_dumps(obj, **kwargs): quoted. Always single quote attributes if you use the ``|tojson`` filter. Alternatively use ``|tojson|forceescape``. """ - rv = dumps(obj, **kwargs) \ - .replace(u'<', u'\\u003c') \ - .replace(u'>', u'\\u003e') \ - .replace(u'&', u'\\u0026') \ - .replace(u"'", u'\\u0027') + rv = ( + dumps(obj, **kwargs) + .replace(u"<", u"\\u003c") + .replace(u">", u"\\u003e") + .replace(u"&", u"\\u0026") + .replace(u"'", u"\\u0027") + ) if not _slash_escape: - rv = rv.replace('\\/', '/') + rv = rv.replace("\\/", "/") return rv @@ -304,22 +312,22 @@ def jsonify(*args, **kwargs): """ indent = None - separators = (',', ':') + separators = (",", ":") - if current_app.config['JSONIFY_PRETTYPRINT_REGULAR'] or current_app.debug: + if current_app.config["JSONIFY_PRETTYPRINT_REGULAR"] or current_app.debug: indent = 2 - separators = (', ', ': ') + separators = (", ", ": ") if args and kwargs: - raise TypeError('jsonify() behavior undefined when passed both args and kwargs') + raise TypeError("jsonify() behavior undefined when passed both args and kwargs") elif len(args) == 1: # single args are passed directly to dumps() data = args[0] else: data = args or kwargs return current_app.response_class( - dumps(data, indent=indent, separators=separators) + '\n', - mimetype=current_app.config['JSONIFY_MIMETYPE'] + dumps(data, indent=indent, separators=separators) + "\n", + mimetype=current_app.config["JSONIFY_MIMETYPE"], ) diff --git a/flask/json/tag.py b/flask/json/tag.py index 11c966c5..197aa1aa 100644 --- a/flask/json/tag.py +++ b/flask/json/tag.py @@ -56,7 +56,7 @@ from flask.json import dumps, loads class JSONTag(object): """Base class for defining type tags for :class:`TaggedJSONSerializer`.""" - __slots__ = ('serializer',) + __slots__ = ("serializer",) #: The tag to mark the serialized object with. If ``None``, this tag is #: only used as an intermediate step during tagging. @@ -94,7 +94,7 @@ class TagDict(JSONTag): """ __slots__ = () - key = ' di' + key = " di" def check(self, value): return ( @@ -105,7 +105,7 @@ class TagDict(JSONTag): def to_json(self, value): key = next(iter(value)) - return {key + '__': self.serializer.tag(value[key])} + return {key + "__": self.serializer.tag(value[key])} def to_python(self, value): key = next(iter(value)) @@ -128,7 +128,7 @@ class PassDict(JSONTag): class TagTuple(JSONTag): __slots__ = () - key = ' t' + key = " t" def check(self, value): return isinstance(value, tuple) @@ -154,13 +154,13 @@ class PassList(JSONTag): class TagBytes(JSONTag): __slots__ = () - key = ' b' + key = " b" def check(self, value): return isinstance(value, bytes) def to_json(self, value): - return b64encode(value).decode('ascii') + return b64encode(value).decode("ascii") def to_python(self, value): return b64decode(value) @@ -172,10 +172,10 @@ class TagMarkup(JSONTag): deserializes to an instance of :class:`~flask.Markup`.""" __slots__ = () - key = ' m' + key = " m" def check(self, value): - return callable(getattr(value, '__html__', None)) + return callable(getattr(value, "__html__", None)) def to_json(self, value): return text_type(value.__html__()) @@ -186,7 +186,7 @@ class TagMarkup(JSONTag): class TagUUID(JSONTag): __slots__ = () - key = ' u' + key = " u" def check(self, value): return isinstance(value, UUID) @@ -200,7 +200,7 @@ class TagUUID(JSONTag): class TagDateTime(JSONTag): __slots__ = () - key = ' d' + key = " d" def check(self, value): return isinstance(value, datetime) @@ -227,12 +227,18 @@ class TaggedJSONSerializer(object): * :class:`~datetime.datetime` """ - __slots__ = ('tags', 'order') + __slots__ = ("tags", "order") #: Tag classes to bind when creating the serializer. Other tags can be #: added later using :meth:`~register`. default_tags = [ - TagDict, PassDict, TagTuple, PassList, TagBytes, TagMarkup, TagUUID, + TagDict, + PassDict, + TagTuple, + PassList, + TagBytes, + TagMarkup, + TagUUID, TagDateTime, ] @@ -293,7 +299,7 @@ class TaggedJSONSerializer(object): def dumps(self, value): """Tag the value and dump it to a compact JSON string.""" - return dumps(self.tag(value), separators=(',', ':')) + return dumps(self.tag(value), separators=(",", ":")) def loads(self, value): """Load data from a JSON string and deserialized any tagged objects.""" diff --git a/flask/logging.py b/flask/logging.py index 389c2c22..2282ea0a 100644 --- a/flask/logging.py +++ b/flask/logging.py @@ -27,7 +27,7 @@ def wsgi_errors_stream(): can't import this directly, you can refer to it as ``ext://flask.logging.wsgi_errors_stream``. """ - return request.environ['wsgi.errors'] if request else sys.stderr + return request.environ["wsgi.errors"] if request else sys.stderr def has_level_handler(logger): @@ -52,9 +52,9 @@ def has_level_handler(logger): #: Log messages to :func:`~flask.logging.wsgi_errors_stream` with the format #: ``[%(asctime)s] %(levelname)s in %(module)s: %(message)s``. default_handler = logging.StreamHandler(wsgi_errors_stream) -default_handler.setFormatter(logging.Formatter( - '[%(asctime)s] %(levelname)s in %(module)s: %(message)s' -)) +default_handler.setFormatter( + logging.Formatter("[%(asctime)s] %(levelname)s in %(module)s: %(message)s") +) def create_logger(app): @@ -67,7 +67,7 @@ def create_logger(app): :class:`~logging.StreamHandler` for :func:`~flask.logging.wsgi_errors_stream` with a basic format. """ - logger = logging.getLogger('flask.app') + logger = logging.getLogger("flask.app") if app.debug and logger.level == logging.NOTSET: logger.setLevel(logging.DEBUG) diff --git a/flask/sessions.py b/flask/sessions.py index c8b7d4e9..61180c22 100644 --- a/flask/sessions.py +++ b/flask/sessions.py @@ -27,11 +27,11 @@ class SessionMixin(collections_abc.MutableMapping): @property def permanent(self): """This reflects the ``'_permanent'`` key in the dict.""" - return self.get('_permanent', False) + return self.get("_permanent", False) @permanent.setter def permanent(self, value): - self['_permanent'] = bool(value) + self["_permanent"] = bool(value) #: Some implementations can detect whether a session is newly #: created, but that is not guaranteed. Use with caution. The mixin @@ -98,11 +98,13 @@ class NullSession(SecureCookieSession): """ def _fail(self, *args, **kwargs): - raise RuntimeError('The session is unavailable because no secret ' - 'key was set. Set the secret_key on the ' - 'application to something unique and secret.') - __setitem__ = __delitem__ = clear = pop = popitem = \ - update = setdefault = _fail + raise RuntimeError( + "The session is unavailable because no secret " + "key was set. Set the secret_key on the " + "application to something unique and secret." + ) + + __setitem__ = __delitem__ = clear = pop = popitem = update = setdefault = _fail del _fail @@ -180,52 +182,52 @@ class SessionInterface(object): updated to avoid re-running the logic. """ - rv = app.config['SESSION_COOKIE_DOMAIN'] + rv = app.config["SESSION_COOKIE_DOMAIN"] # set explicitly, or cached from SERVER_NAME detection # if False, return None if rv is not None: return rv if rv else None - rv = app.config['SERVER_NAME'] + rv = app.config["SERVER_NAME"] # server name not set, cache False to return none next time if not rv: - app.config['SESSION_COOKIE_DOMAIN'] = False + app.config["SESSION_COOKIE_DOMAIN"] = False return None # chop off the port which is usually not supported by browsers # remove any leading '.' since we'll add that later - rv = rv.rsplit(':', 1)[0].lstrip('.') + rv = rv.rsplit(":", 1)[0].lstrip(".") - if '.' not in rv: + if "." not in rv: # Chrome doesn't allow names without a '.' # this should only come up with localhost # hack around this by not setting the name, and show a warning warnings.warn( '"{rv}" is not a valid cookie domain, it must contain a ".".' - ' Add an entry to your hosts file, for example' + " Add an entry to your hosts file, for example" ' "{rv}.localdomain", and use that instead.'.format(rv=rv) ) - app.config['SESSION_COOKIE_DOMAIN'] = False + app.config["SESSION_COOKIE_DOMAIN"] = False return None ip = is_ip(rv) if ip: warnings.warn( - 'The session cookie domain is an IP address. This may not work' - ' as intended in some browsers. Add an entry to your hosts' + "The session cookie domain is an IP address. This may not work" + " as intended in some browsers. Add an entry to your hosts" ' file, for example "localhost.localdomain", and use that' - ' instead.' + " instead." ) # if this is not an ip and app is mounted at the root, allow subdomain # matching by adding a '.' prefix - if self.get_cookie_path(app) == '/' and not ip: - rv = '.' + rv + if self.get_cookie_path(app) == "/" and not ip: + rv = "." + rv - app.config['SESSION_COOKIE_DOMAIN'] = rv + app.config["SESSION_COOKIE_DOMAIN"] = rv return rv def get_cookie_path(self, app): @@ -234,28 +236,27 @@ class SessionInterface(object): config var if it's set, and falls back to ``APPLICATION_ROOT`` or uses ``/`` if it's ``None``. """ - return app.config['SESSION_COOKIE_PATH'] \ - or app.config['APPLICATION_ROOT'] + return app.config["SESSION_COOKIE_PATH"] or app.config["APPLICATION_ROOT"] def get_cookie_httponly(self, app): """Returns True if the session cookie should be httponly. This currently just returns the value of the ``SESSION_COOKIE_HTTPONLY`` config var. """ - return app.config['SESSION_COOKIE_HTTPONLY'] + return app.config["SESSION_COOKIE_HTTPONLY"] def get_cookie_secure(self, app): """Returns True if the cookie should be secure. This currently just returns the value of the ``SESSION_COOKIE_SECURE`` setting. """ - return app.config['SESSION_COOKIE_SECURE'] + return app.config["SESSION_COOKIE_SECURE"] def get_cookie_samesite(self, app): """Return ``'Strict'`` or ``'Lax'`` if the cookie should use the ``SameSite`` attribute. This currently just returns the value of the :data:`SESSION_COOKIE_SAMESITE` setting. """ - return app.config['SESSION_COOKIE_SAMESITE'] + return app.config["SESSION_COOKIE_SAMESITE"] def get_expiration_time(self, app, session): """A helper method that returns an expiration date for the session @@ -279,7 +280,7 @@ class SessionInterface(object): """ return session.modified or ( - session.permanent and app.config['SESSION_REFRESH_EACH_REQUEST'] + session.permanent and app.config["SESSION_REFRESH_EACH_REQUEST"] ) def open_session(self, app, request): @@ -306,14 +307,15 @@ class SecureCookieSessionInterface(SessionInterface): """The default session interface that stores sessions in signed cookies through the :mod:`itsdangerous` module. """ + #: the salt that should be applied on top of the secret key for the #: signing of cookie based sessions. - salt = 'cookie-session' + salt = "cookie-session" #: the hash function to use for the signature. The default is sha1 digest_method = staticmethod(hashlib.sha1) #: the name of the itsdangerous supported key derivation. The default #: is hmac. - key_derivation = 'hmac' + key_derivation = "hmac" #: A python serializer for the payload. The default is a compact #: JSON derived serializer with support for some extra Python types #: such as datetime objects or tuples. @@ -324,12 +326,14 @@ class SecureCookieSessionInterface(SessionInterface): if not app.secret_key: return None signer_kwargs = dict( - key_derivation=self.key_derivation, - digest_method=self.digest_method + key_derivation=self.key_derivation, digest_method=self.digest_method + ) + return URLSafeTimedSerializer( + app.secret_key, + salt=self.salt, + serializer=self.serializer, + signer_kwargs=signer_kwargs, ) - return URLSafeTimedSerializer(app.secret_key, salt=self.salt, - serializer=self.serializer, - signer_kwargs=signer_kwargs) def open_session(self, app, request): s = self.get_signing_serializer(app) @@ -354,16 +358,14 @@ class SecureCookieSessionInterface(SessionInterface): if not session: if session.modified: response.delete_cookie( - app.session_cookie_name, - domain=domain, - path=path + app.session_cookie_name, domain=domain, path=path ) return # Add a "Vary: Cookie" header if the session was accessed at all. if session.accessed: - response.vary.add('Cookie') + response.vary.add("Cookie") if not self.should_set_cookie(app, session): return @@ -381,5 +383,5 @@ class SecureCookieSessionInterface(SessionInterface): domain=domain, path=path, secure=secure, - samesite=samesite + samesite=samesite, ) diff --git a/flask/signals.py b/flask/signals.py index 18f26307..14d9c619 100644 --- a/flask/signals.py +++ b/flask/signals.py @@ -13,8 +13,10 @@ signals_available = False try: from blinker import Namespace + signals_available = True except ImportError: + class Namespace(object): def signal(self, name, doc=None): return _FakeSignal(name, doc) @@ -29,15 +31,23 @@ except ImportError: def __init__(self, name, doc=None): self.name = name self.__doc__ = doc + def _fail(self, *args, **kwargs): - raise RuntimeError('signalling support is unavailable ' - 'because the blinker library is ' - 'not installed.') + raise RuntimeError( + "signalling support is unavailable " + "because the blinker library is " + "not installed." + ) + send = lambda *a, **kw: None - connect = disconnect = has_receivers_for = receivers_for = \ - temporarily_connected_to = connected_to = _fail + connect = ( + disconnect + ) = ( + has_receivers_for + ) = receivers_for = temporarily_connected_to = connected_to = _fail del _fail + # The namespace for code signals. If you are not Flask code, do # not put signals in here. Create your own namespace instead. _signals = Namespace() @@ -45,13 +55,13 @@ _signals = Namespace() # Core signals. For usage examples grep the source code or consult # the API documentation in docs/api.rst as well as docs/signals.rst -template_rendered = _signals.signal('template-rendered') -before_render_template = _signals.signal('before-render-template') -request_started = _signals.signal('request-started') -request_finished = _signals.signal('request-finished') -request_tearing_down = _signals.signal('request-tearing-down') -got_request_exception = _signals.signal('got-request-exception') -appcontext_tearing_down = _signals.signal('appcontext-tearing-down') -appcontext_pushed = _signals.signal('appcontext-pushed') -appcontext_popped = _signals.signal('appcontext-popped') -message_flashed = _signals.signal('message-flashed') +template_rendered = _signals.signal("template-rendered") +before_render_template = _signals.signal("before-render-template") +request_started = _signals.signal("request-started") +request_finished = _signals.signal("request-finished") +request_tearing_down = _signals.signal("request-tearing-down") +got_request_exception = _signals.signal("got-request-exception") +appcontext_tearing_down = _signals.signal("appcontext-tearing-down") +appcontext_pushed = _signals.signal("appcontext-pushed") +appcontext_popped = _signals.signal("appcontext-popped") +message_flashed = _signals.signal("message-flashed") diff --git a/flask/templating.py b/flask/templating.py index 02402008..e3ea62f5 100644 --- a/flask/templating.py +++ b/flask/templating.py @@ -9,8 +9,7 @@ :license: BSD, see LICENSE for more details. """ -from jinja2 import BaseLoader, Environment as BaseEnvironment, \ - TemplateNotFound +from jinja2 import BaseLoader, Environment as BaseEnvironment, TemplateNotFound from .globals import _request_ctx_stack, _app_ctx_stack from .signals import template_rendered, before_render_template @@ -24,10 +23,10 @@ def _default_template_ctx_processor(): appctx = _app_ctx_stack.top rv = {} if appctx is not None: - rv['g'] = appctx.g + rv["g"] = appctx.g if reqctx is not None: - rv['request'] = reqctx.request - rv['session'] = reqctx.session + rv["request"] = reqctx.request + rv["session"] = reqctx.session return rv @@ -38,8 +37,8 @@ class Environment(BaseEnvironment): """ def __init__(self, app, **options): - if 'loader' not in options: - options['loader'] = app.create_global_jinja_loader() + if "loader" not in options: + options["loader"] = app.create_global_jinja_loader() BaseEnvironment.__init__(self, **options) self.app = app @@ -53,7 +52,7 @@ class DispatchingJinjaLoader(BaseLoader): self.app = app def get_source(self, environment, template): - if self.app.config['EXPLAIN_TEMPLATE_LOADING']: + if self.app.config["EXPLAIN_TEMPLATE_LOADING"]: return self._get_source_explained(environment, template) return self._get_source_fast(environment, template) @@ -71,6 +70,7 @@ class DispatchingJinjaLoader(BaseLoader): attempts.append((loader, srcobj, rv)) from .debughelpers import explain_template_loading_attempts + explain_template_loading_attempts(self.app, template, attempts) if trv is not None: @@ -131,8 +131,11 @@ def render_template(template_name_or_list, **context): """ ctx = _app_ctx_stack.top ctx.app.update_template_context(context) - return _render(ctx.app.jinja_env.get_or_select_template(template_name_or_list), - context, ctx.app) + return _render( + ctx.app.jinja_env.get_or_select_template(template_name_or_list), + context, + ctx.app, + ) def render_template_string(source, **context): @@ -146,5 +149,4 @@ def render_template_string(source, **context): """ ctx = _app_ctx_stack.top ctx.app.update_template_context(context) - return _render(ctx.app.jinja_env.from_string(source), - context, ctx.app) + return _render(ctx.app.jinja_env.from_string(source), context, ctx.app) diff --git a/flask/testing.py b/flask/testing.py index 4bf0ebc1..514a63b4 100644 --- a/flask/testing.py +++ b/flask/testing.py @@ -22,8 +22,7 @@ from werkzeug.urls import url_parse def make_test_environ_builder( - app, path='/', base_url=None, subdomain=None, url_scheme=None, - *args, **kwargs + app, path="/", base_url=None, subdomain=None, url_scheme=None, *args, **kwargs ): """Create a :class:`~werkzeug.test.EnvironBuilder`, taking some defaults from the application. @@ -46,44 +45,41 @@ def make_test_environ_builder( :class:`~werkzeug.test.EnvironBuilder`. """ - assert ( - not (base_url or subdomain or url_scheme) - or (base_url is not None) != bool(subdomain or url_scheme) + assert not (base_url or subdomain or url_scheme) or (base_url is not None) != bool( + subdomain or url_scheme ), 'Cannot pass "subdomain" or "url_scheme" with "base_url".' if base_url is None: - http_host = app.config.get('SERVER_NAME') or 'localhost' - app_root = app.config['APPLICATION_ROOT'] + http_host = app.config.get("SERVER_NAME") or "localhost" + app_root = app.config["APPLICATION_ROOT"] if subdomain: - http_host = '{0}.{1}'.format(subdomain, http_host) + http_host = "{0}.{1}".format(subdomain, http_host) if url_scheme is None: - url_scheme = app.config['PREFERRED_URL_SCHEME'] + url_scheme = app.config["PREFERRED_URL_SCHEME"] url = url_parse(path) - base_url = '{scheme}://{netloc}/{path}'.format( + base_url = "{scheme}://{netloc}/{path}".format( scheme=url.scheme or url_scheme, netloc=url.netloc or http_host, - path=app_root.lstrip('/') + path=app_root.lstrip("/"), ) path = url.path if url.query: - sep = b'?' if isinstance(url.query, bytes) else '?' + sep = b"?" if isinstance(url.query, bytes) else "?" path += sep + url.query - if 'json' in kwargs: - assert 'data' not in kwargs, ( - "Client cannot provide both 'json' and 'data'." - ) + if "json" in kwargs: + assert "data" not in kwargs, "Client cannot provide both 'json' and 'data'." # push a context so flask.json can use app's json attributes with app.app_context(): - kwargs['data'] = json_dumps(kwargs.pop('json')) + kwargs["data"] = json_dumps(kwargs.pop("json")) - if 'content_type' not in kwargs: - kwargs['content_type'] = 'application/json' + if "content_type" not in kwargs: + kwargs["content_type"] = "application/json" return EnvironBuilder(path, base_url, *args, **kwargs) @@ -109,7 +105,7 @@ class FlaskClient(Client): super(FlaskClient, self).__init__(*args, **kwargs) self.environ_base = { "REMOTE_ADDR": "127.0.0.1", - "HTTP_USER_AGENT": "werkzeug/" + werkzeug.__version__ + "HTTP_USER_AGENT": "werkzeug/" + werkzeug.__version__, } @contextmanager @@ -131,18 +127,20 @@ class FlaskClient(Client): passed through. """ if self.cookie_jar is None: - raise RuntimeError('Session transactions only make sense ' - 'with cookies enabled.') + raise RuntimeError( + "Session transactions only make sense " "with cookies enabled." + ) app = self.application - environ_overrides = kwargs.setdefault('environ_overrides', {}) + environ_overrides = kwargs.setdefault("environ_overrides", {}) self.cookie_jar.inject_wsgi(environ_overrides) outer_reqctx = _request_ctx_stack.top with app.test_request_context(*args, **kwargs) as c: session_interface = app.session_interface sess = session_interface.open_session(app, c.request) if sess is None: - raise RuntimeError('Session backend did not open a session. ' - 'Check the configuration') + raise RuntimeError( + "Session backend did not open a session. " "Check the configuration" + ) # Since we have to open a new request context for the session # handling we want to make sure that we hide out own context @@ -164,12 +162,13 @@ class FlaskClient(Client): self.cookie_jar.extract_wsgi(c.request.environ, headers) def open(self, *args, **kwargs): - as_tuple = kwargs.pop('as_tuple', False) - buffered = kwargs.pop('buffered', False) - follow_redirects = kwargs.pop('follow_redirects', False) + as_tuple = kwargs.pop("as_tuple", False) + buffered = kwargs.pop("buffered", False) + follow_redirects = kwargs.pop("follow_redirects", False) if ( - not kwargs and len(args) == 1 + not kwargs + and len(args) == 1 and isinstance(args[0], (EnvironBuilder, dict)) ): environ = self.environ_base.copy() @@ -179,14 +178,13 @@ class FlaskClient(Client): else: environ.update(args[0]) - environ['flask._preserve_context'] = self.preserve_context + environ["flask._preserve_context"] = self.preserve_context else: - kwargs.setdefault('environ_overrides', {}) \ - ['flask._preserve_context'] = self.preserve_context - kwargs.setdefault('environ_base', self.environ_base) - builder = make_test_environ_builder( - self.application, *args, **kwargs - ) + kwargs.setdefault("environ_overrides", {})[ + "flask._preserve_context" + ] = self.preserve_context + kwargs.setdefault("environ_base", self.environ_base) + builder = make_test_environ_builder(self.application, *args, **kwargs) try: environ = builder.get_environ() @@ -194,15 +192,16 @@ class FlaskClient(Client): builder.close() return Client.open( - self, environ, + self, + environ, as_tuple=as_tuple, buffered=buffered, - follow_redirects=follow_redirects + follow_redirects=follow_redirects, ) def __enter__(self): if self.preserve_context: - raise RuntimeError('Cannot nest client invocations') + raise RuntimeError("Cannot nest client invocations") self.preserve_context = True return self @@ -222,6 +221,7 @@ class FlaskCliRunner(CliRunner): CLI commands. Typically created using :meth:`~flask.Flask.test_cli_runner`. See :ref:`testing-cli`. """ + def __init__(self, app, **kwargs): self.app = app super(FlaskCliRunner, self).__init__(**kwargs) @@ -244,7 +244,7 @@ class FlaskCliRunner(CliRunner): if cli is None: cli = self.app.cli - if 'obj' not in kwargs: - kwargs['obj'] = ScriptInfo(create_app=lambda: self.app) + if "obj" not in kwargs: + kwargs["obj"] = ScriptInfo(create_app=lambda: self.app) return super(FlaskCliRunner, self).invoke(cli, args, **kwargs) diff --git a/flask/views.py b/flask/views.py index 1f2c997b..debd19e7 100644 --- a/flask/views.py +++ b/flask/views.py @@ -13,8 +13,9 @@ from .globals import request from ._compat import with_metaclass -http_method_funcs = frozenset(['get', 'post', 'head', 'options', - 'delete', 'put', 'trace', 'patch']) +http_method_funcs = frozenset( + ["get", "post", "head", "options", "delete", "put", "trace", "patch"] +) class View(object): @@ -83,6 +84,7 @@ class View(object): The arguments passed to :meth:`as_view` are forwarded to the constructor of the class. """ + def view(*args, **kwargs): self = view.view_class(*class_args, **class_kwargs) return self.dispatch_request(*args, **kwargs) @@ -115,7 +117,7 @@ class MethodViewType(type): def __init__(cls, name, bases, d): super(MethodViewType, cls).__init__(name, bases, d) - if 'methods' not in d: + if "methods" not in d: methods = set() for key in http_method_funcs: @@ -151,8 +153,8 @@ class MethodView(with_metaclass(MethodViewType, View)): # If the request method is HEAD and we don't have a handler for it # retry with GET. - if meth is None and request.method == 'HEAD': - meth = getattr(self, 'get', None) + if meth is None and request.method == "HEAD": + meth = getattr(self, "get", None) - assert meth is not None, 'Unimplemented method %r' % request.method + assert meth is not None, "Unimplemented method %r" % request.method return meth(*args, **kwargs) diff --git a/flask/wrappers.py b/flask/wrappers.py index 12eff2c7..66f0fea4 100644 --- a/flask/wrappers.py +++ b/flask/wrappers.py @@ -34,8 +34,9 @@ class JSONMixin(object): """ mt = self.mimetype return ( - mt == 'application/json' - or (mt.startswith('application/')) and mt.endswith('+json') + mt == "application/json" + or (mt.startswith("application/")) + and mt.endswith("+json") ) @property @@ -103,7 +104,7 @@ class JSONMixin(object): .. versionadded:: 0.8 """ if current_app is not None and current_app.debug: - raise BadRequest('Failed to decode JSON object: {0}'.format(e)) + raise BadRequest("Failed to decode JSON object: {0}".format(e)) raise BadRequest() @@ -146,7 +147,7 @@ class Request(RequestBase, JSONMixin): def max_content_length(self): """Read-only view of the ``MAX_CONTENT_LENGTH`` config key.""" if current_app: - return current_app.config['MAX_CONTENT_LENGTH'] + return current_app.config["MAX_CONTENT_LENGTH"] @property def endpoint(self): @@ -161,8 +162,8 @@ class Request(RequestBase, JSONMixin): @property def blueprint(self): """The name of the current blueprint""" - if self.url_rule and '.' in self.url_rule.endpoint: - return self.url_rule.endpoint.rsplit('.', 1)[0] + if self.url_rule and "." in self.url_rule.endpoint: + return self.url_rule.endpoint.rsplit(".", 1)[0] def _load_form_data(self): RequestBase._load_form_data(self) @@ -172,10 +173,11 @@ class Request(RequestBase, JSONMixin): if ( current_app and current_app.debug - and self.mimetype != 'multipart/form-data' + and self.mimetype != "multipart/form-data" and not self.files ): from .debughelpers import attach_enctype_error_multidict + attach_enctype_error_multidict(self) @@ -197,7 +199,7 @@ class Response(ResponseBase, JSONMixin): Added :attr:`max_cookie_size`. """ - default_mimetype = 'text/html' + default_mimetype = "text/html" def _get_data_for_json(self, cache): return self.get_data() @@ -210,7 +212,7 @@ class Response(ResponseBase, JSONMixin): Werkzeug's docs. """ if current_app: - return current_app.config['MAX_COOKIE_SIZE'] + return current_app.config["MAX_COOKIE_SIZE"] # return Werkzeug's default when not in an app context return super(Response, self).max_cookie_size diff --git a/scripts/make-release.py b/scripts/make-release.py index e1ca54f8..9c05b4d8 100755 --- a/scripts/make-release.py +++ b/scripts/make-release.py @@ -7,21 +7,21 @@ import sys from datetime import date, datetime from subprocess import PIPE, Popen -_date_strip_re = re.compile(r'(?<=\d)(st|nd|rd|th)') +_date_strip_re = re.compile(r"(?<=\d)(st|nd|rd|th)") def parse_changelog(): - with open('CHANGES.rst') as f: + with open("CHANGES.rst") as f: lineiter = iter(f) for line in lineiter: - match = re.search('^Version\s+(.*)', line.strip()) + match = re.search("^Version\s+(.*)", line.strip()) if match is None: continue version = match.group(1).strip() - if next(lineiter).count('-') != len(match.group(0)): + if next(lineiter).count("-") != len(match.group(0)): continue while 1: @@ -31,9 +31,9 @@ def parse_changelog(): break match = re.search( - r'released on (\w+\s+\d+\w+\s+\d+)(?:, codename (.*))?', + r"released on (\w+\s+\d+\w+\s+\d+)(?:, codename (.*))?", change_info, - flags=re.IGNORECASE + flags=re.IGNORECASE, ) if match is None: @@ -45,17 +45,17 @@ def parse_changelog(): def bump_version(version): try: - parts = [int(i) for i in version.split('.')] + parts = [int(i) for i in version.split(".")] except ValueError: - fail('Current version is not numeric') + fail("Current version is not numeric") parts[-1] += 1 - return '.'.join(map(str, parts)) + return ".".join(map(str, parts)) def parse_date(string): - string = _date_strip_re.sub('', string) - return datetime.strptime(string, '%B %d %Y') + string = _date_strip_re.sub("", string) + return datetime.strptime(string, "%B %d %Y") def set_filename_version(filename, version_number, pattern): @@ -69,29 +69,30 @@ def set_filename_version(filename, version_number, pattern): with open(filename) as f: contents = re.sub( r"^(\s*%s\s*=\s*')(.+?)(')" % pattern, - inject_version, f.read(), - flags=re.DOTALL | re.MULTILINE + inject_version, + f.read(), + flags=re.DOTALL | re.MULTILINE, ) if not changed: - fail('Could not find %s in %s', pattern, filename) + fail("Could not find %s in %s", pattern, filename) - with open(filename, 'w') as f: + with open(filename, "w") as f: f.write(contents) def set_init_version(version): - info('Setting __init__.py version to %s', version) - set_filename_version('flask/__init__.py', version, '__version__') + info("Setting __init__.py version to %s", version) + set_filename_version("flask/__init__.py", version, "__version__") def build(): - cmd = [sys.executable, 'setup.py', 'sdist', 'bdist_wheel'] + cmd = [sys.executable, "setup.py", "sdist", "bdist_wheel"] Popen(cmd).wait() def fail(message, *args): - print('Error:', message % args, file=sys.stderr) + print("Error:", message % args, file=sys.stderr) sys.exit(1) @@ -100,39 +101,39 @@ def info(message, *args): def get_git_tags(): - return set( - Popen(['git', 'tag'], stdout=PIPE).communicate()[0].splitlines() - ) + return set(Popen(["git", "tag"], stdout=PIPE).communicate()[0].splitlines()) def git_is_clean(): - return Popen(['git', 'diff', '--quiet']).wait() == 0 + return Popen(["git", "diff", "--quiet"]).wait() == 0 def make_git_commit(message, *args): message = message % args - Popen(['git', 'commit', '-am', message]).wait() + Popen(["git", "commit", "-am", message]).wait() def make_git_tag(tag): info('Tagging "%s"', tag) - Popen(['git', 'tag', tag]).wait() + Popen(["git", "tag", tag]).wait() def main(): - os.chdir(os.path.join(os.path.dirname(__file__), '..')) + os.chdir(os.path.join(os.path.dirname(__file__), "..")) rv = parse_changelog() if rv is None: - fail('Could not parse changelog') + fail("Could not parse changelog") version, release_date, codename = rv - dev_version = bump_version(version) + '.dev' + dev_version = bump_version(version) + ".dev" info( - 'Releasing %s (codename %s, release date %s)', - version, codename, release_date.strftime('%d/%m/%Y') + "Releasing %s (codename %s, release date %s)", + version, + codename, + release_date.strftime("%d/%m/%Y"), ) tags = get_git_tags() @@ -140,25 +141,22 @@ def main(): fail('Version "%s" is already tagged', version) if release_date.date() != date.today(): - fail( - 'Release date is not today (%s != %s)', - release_date.date(), date.today() - ) + fail("Release date is not today (%s != %s)", release_date.date(), date.today()) if not git_is_clean(): - fail('You have uncommitted changes in git') + fail("You have uncommitted changes in git") try: import wheel # noqa: F401 except ImportError: - fail('You need to install the wheel package.') + fail("You need to install the wheel package.") set_init_version(version) - make_git_commit('Bump version number to %s', version) + make_git_commit("Bump version number to %s", version) make_git_tag(version) build() set_init_version(dev_version) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/setup.py b/setup.py index 0bc1c90b..c9cf21bd 100755 --- a/setup.py +++ b/setup.py @@ -6,78 +6,72 @@ from collections import OrderedDict from setuptools import setup -with io.open('README.rst', 'rt', encoding='utf8') as f: +with io.open("README.rst", "rt", encoding="utf8") as f: readme = f.read() -with io.open('flask/__init__.py', 'rt', encoding='utf8') as f: - version = re.search(r'__version__ = \'(.*?)\'', f.read()).group(1) +with io.open("flask/__init__.py", "rt", encoding="utf8") as f: + version = re.search(r"__version__ = \"(.*?)\"", f.read()).group(1) setup( - name='Flask', + name="Flask", version=version, - url='https://www.palletsprojects.com/p/flask/', - project_urls=OrderedDict(( - ('Documentation', 'http://flask.pocoo.org/docs/'), - ('Code', 'https://github.com/pallets/flask'), - ('Issue tracker', 'https://github.com/pallets/flask/issues'), - )), - license='BSD', - author='Armin Ronacher', - author_email='armin.ronacher@active-4.com', - maintainer='Pallets team', - maintainer_email='contact@palletsprojects.com', - description='A simple framework for building complex web applications.', + url="https://www.palletsprojects.com/p/flask/", + project_urls=OrderedDict( + ( + ("Documentation", "http://flask.pocoo.org/docs/"), + ("Code", "https://github.com/pallets/flask"), + ("Issue tracker", "https://github.com/pallets/flask/issues"), + ) + ), + license="BSD", + author="Armin Ronacher", + author_email="armin.ronacher@active-4.com", + maintainer="Pallets team", + maintainer_email="contact@palletsprojects.com", + description="A simple framework for building complex web applications.", long_description=readme, - packages=['flask', 'flask.json'], + packages=["flask", "flask.json"], include_package_data=True, zip_safe=False, - platforms='any', - python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*', + platforms="any", + python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*", install_requires=[ - 'Werkzeug>=0.14', - 'Jinja2>=2.10.1', - 'itsdangerous>=0.24', - 'click>=5.1', + "Werkzeug>=0.14", + "Jinja2>=2.10.1", + "itsdangerous>=0.24", + "click>=5.1", ], extras_require={ - 'dotenv': ['python-dotenv'], - 'dev': [ - 'pytest>=3', - 'coverage', - 'tox', - 'sphinx', - 'pallets-sphinx-themes', - 'sphinxcontrib-log-cabinet', + "dotenv": ["python-dotenv"], + "dev": [ + "pytest>=3", + "coverage", + "tox", + "sphinx", + "pallets-sphinx-themes", + "sphinxcontrib-log-cabinet", ], - 'docs': [ - 'sphinx', - 'pallets-sphinx-themes', - 'sphinxcontrib-log-cabinet', - ] + "docs": ["sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet"], }, classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Environment :: Web Environment', - 'Framework :: Flask', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', - 'Topic :: Internet :: WWW/HTTP :: WSGI :: Application', - 'Topic :: Software Development :: Libraries :: Application Frameworks', - 'Topic :: Software Development :: Libraries :: Python Modules', + "Development Status :: 5 - Production/Stable", + "Environment :: Web Environment", + "Framework :: Flask", + "Intended Audience :: Developers", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Topic :: Internet :: WWW/HTTP :: Dynamic Content", + "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", + "Topic :: Software Development :: Libraries :: Application Frameworks", + "Topic :: Software Development :: Libraries :: Python Modules", ], - entry_points={ - 'console_scripts': [ - 'flask = flask.cli:main', - ], - }, + entry_points={"console_scripts": ["flask = flask.cli:main"]}, ) diff --git a/tests/conftest.py b/tests/conftest.py index 2c19c763..0996467c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -20,7 +20,7 @@ import flask from flask import Flask as _Flask -@pytest.fixture(scope='session', autouse=True) +@pytest.fixture(scope="session", autouse=True) def _standard_os_environ(): """Set up ``os.environ`` at the start of the test session to have standard values. Returns a list of operations that is used by @@ -28,11 +28,11 @@ def _standard_os_environ(): """ mp = monkeypatch.MonkeyPatch() out = ( - (os.environ, 'FLASK_APP', monkeypatch.notset), - (os.environ, 'FLASK_ENV', monkeypatch.notset), - (os.environ, 'FLASK_DEBUG', monkeypatch.notset), - (os.environ, 'FLASK_RUN_FROM_CLI', monkeypatch.notset), - (os.environ, 'WERKZEUG_RUN_MAIN', monkeypatch.notset), + (os.environ, "FLASK_APP", monkeypatch.notset), + (os.environ, "FLASK_ENV", monkeypatch.notset), + (os.environ, "FLASK_DEBUG", monkeypatch.notset), + (os.environ, "FLASK_RUN_FROM_CLI", monkeypatch.notset), + (os.environ, "WERKZEUG_RUN_MAIN", monkeypatch.notset), ) for _, key, value in out: @@ -55,12 +55,12 @@ def _reset_os_environ(monkeypatch, _standard_os_environ): class Flask(_Flask): testing = True - secret_key = 'test key' + secret_key = "test key" @pytest.fixture def app(): - app = Flask('flask_test', root_path=os.path.dirname(__file__)) + app = Flask("flask_test", root_path=os.path.dirname(__file__)) return app @@ -84,8 +84,7 @@ def client(app): @pytest.fixture def test_apps(monkeypatch): monkeypatch.syspath_prepend( - os.path.abspath(os.path.join( - os.path.dirname(__file__), 'test_apps')) + os.path.abspath(os.path.join(os.path.dirname(__file__), "test_apps")) ) @@ -120,8 +119,8 @@ def limit_loader(request, monkeypatch): self.loader = loader def __getattr__(self, name): - if name in ('archive', 'get_filename'): - msg = 'Mocking a loader which does not have `%s.`' % name + if name in ("archive", "get_filename"): + msg = "Mocking a loader which does not have `%s.`" % name raise AttributeError(msg) return getattr(self.loader, name) @@ -130,30 +129,31 @@ def limit_loader(request, monkeypatch): def get_loader(*args, **kwargs): return LimitedLoader(old_get_loader(*args, **kwargs)) - monkeypatch.setattr(pkgutil, 'get_loader', get_loader) + monkeypatch.setattr(pkgutil, "get_loader", get_loader) @pytest.fixture def modules_tmpdir(tmpdir, monkeypatch): """A tmpdir added to sys.path.""" - rv = tmpdir.mkdir('modules_tmpdir') + rv = tmpdir.mkdir("modules_tmpdir") monkeypatch.syspath_prepend(str(rv)) return rv @pytest.fixture def modules_tmpdir_prefix(modules_tmpdir, monkeypatch): - monkeypatch.setattr(sys, 'prefix', str(modules_tmpdir)) + monkeypatch.setattr(sys, "prefix", str(modules_tmpdir)) return modules_tmpdir @pytest.fixture def site_packages(modules_tmpdir, monkeypatch): """Create a fake site-packages.""" - rv = modules_tmpdir \ - .mkdir('lib') \ - .mkdir('python{x[0]}.{x[1]}'.format(x=sys.version_info)) \ - .mkdir('site-packages') + rv = ( + modules_tmpdir.mkdir("lib") + .mkdir("python{x[0]}.{x[1]}".format(x=sys.version_info)) + .mkdir("site-packages") + ) monkeypatch.syspath_prepend(str(rv)) return rv @@ -167,23 +167,29 @@ def install_egg(modules_tmpdir, monkeypatch): if not isinstance(name, str): raise ValueError(name) base.join(name).ensure_dir() - base.join(name).join('__init__.py').ensure() + base.join(name).join("__init__.py").ensure() - egg_setup = base.join('setup.py') - egg_setup.write(textwrap.dedent(""" + egg_setup = base.join("setup.py") + egg_setup.write( + textwrap.dedent( + """ from setuptools import setup setup(name='{0}', version='1.0', packages=['site_egg'], zip_safe=True) - """.format(name))) + """.format( + name + ) + ) + ) import subprocess + subprocess.check_call( - [sys.executable, 'setup.py', 'bdist_egg'], - cwd=str(modules_tmpdir) + [sys.executable, "setup.py", "bdist_egg"], cwd=str(modules_tmpdir) ) - egg_path, = modules_tmpdir.join('dist/').listdir() + egg_path, = modules_tmpdir.join("dist/").listdir() monkeypatch.syspath_prepend(str(egg_path)) return egg_path @@ -202,4 +208,4 @@ def purge_module(request): def catch_deprecation_warnings(recwarn): yield gc.collect() - assert not recwarn.list, '\n'.join(str(w.message) for w in recwarn.list) + assert not recwarn.list, "\n".join(str(w.message) for w in recwarn.list) diff --git a/tests/test_appctx.py b/tests/test_appctx.py index 251764cf..7e903a86 100644 --- a/tests/test_appctx.py +++ b/tests/test_appctx.py @@ -15,27 +15,27 @@ import flask def test_basic_url_generation(app): - app.config['SERVER_NAME'] = 'localhost' - app.config['PREFERRED_URL_SCHEME'] = 'https' + app.config["SERVER_NAME"] = "localhost" + app.config["PREFERRED_URL_SCHEME"] = "https" - @app.route('/') + @app.route("/") def index(): pass with app.app_context(): - rv = flask.url_for('index') - assert rv == 'https://localhost/' + rv = flask.url_for("index") + assert rv == "https://localhost/" def test_url_generation_requires_server_name(app): with app.app_context(): with pytest.raises(RuntimeError): - flask.url_for('index') + flask.url_for("index") def test_url_generation_without_context_fails(): with pytest.raises(RuntimeError): - flask.url_for('index') + flask.url_for("index") def test_request_context_means_app_context(app): @@ -71,7 +71,7 @@ def test_app_tearing_down_with_previous_exception(app): cleanup_stuff.append(exception) try: - raise Exception('dummy') + raise Exception("dummy") except Exception: pass @@ -90,7 +90,7 @@ def test_app_tearing_down_with_handled_exception_by_except_block(app): with app.app_context(): try: - raise Exception('dummy') + raise Exception("dummy") except Exception: pass @@ -98,79 +98,79 @@ def test_app_tearing_down_with_handled_exception_by_except_block(app): def test_app_tearing_down_with_handled_exception_by_app_handler(app, client): - app.config['PROPAGATE_EXCEPTIONS'] = True + app.config["PROPAGATE_EXCEPTIONS"] = True cleanup_stuff = [] @app.teardown_appcontext def cleanup(exception): cleanup_stuff.append(exception) - @app.route('/') + @app.route("/") def index(): - raise Exception('dummy') + raise Exception("dummy") @app.errorhandler(Exception) def handler(f): return flask.jsonify(str(f)) with app.app_context(): - client.get('/') + client.get("/") assert cleanup_stuff == [None] def test_app_tearing_down_with_unhandled_exception(app, client): - app.config['PROPAGATE_EXCEPTIONS'] = True + app.config["PROPAGATE_EXCEPTIONS"] = True cleanup_stuff = [] @app.teardown_appcontext def cleanup(exception): cleanup_stuff.append(exception) - @app.route('/') + @app.route("/") def index(): - raise Exception('dummy') + raise Exception("dummy") with pytest.raises(Exception): with app.app_context(): - client.get('/') + client.get("/") assert len(cleanup_stuff) == 1 assert isinstance(cleanup_stuff[0], Exception) - assert str(cleanup_stuff[0]) == 'dummy' + assert str(cleanup_stuff[0]) == "dummy" def test_app_ctx_globals_methods(app, app_ctx): # get - assert flask.g.get('foo') is None - assert flask.g.get('foo', 'bar') == 'bar' + assert flask.g.get("foo") is None + assert flask.g.get("foo", "bar") == "bar" # __contains__ - assert 'foo' not in flask.g - flask.g.foo = 'bar' - assert 'foo' in flask.g + assert "foo" not in flask.g + flask.g.foo = "bar" + assert "foo" in flask.g # setdefault - flask.g.setdefault('bar', 'the cake is a lie') - flask.g.setdefault('bar', 'hello world') - assert flask.g.bar == 'the cake is a lie' + flask.g.setdefault("bar", "the cake is a lie") + flask.g.setdefault("bar", "hello world") + assert flask.g.bar == "the cake is a lie" # pop - assert flask.g.pop('bar') == 'the cake is a lie' + assert flask.g.pop("bar") == "the cake is a lie" with pytest.raises(KeyError): - flask.g.pop('bar') - assert flask.g.pop('bar', 'more cake') == 'more cake' + flask.g.pop("bar") + assert flask.g.pop("bar", "more cake") == "more cake" # __iter__ - assert list(flask.g) == ['foo'] - #__repr__ + assert list(flask.g) == ["foo"] + # __repr__ assert repr(flask.g) == "" def test_custom_app_ctx_globals_class(app): class CustomRequestGlobals(object): def __init__(self): - self.spam = 'eggs' + self.spam = "eggs" app.app_ctx_globals_class = CustomRequestGlobals with app.app_context(): - assert flask.render_template_string('{{ g.spam }}') == 'eggs' + assert flask.render_template_string("{{ g.spam }}") == "eggs" def test_context_refcounts(app, client): @@ -178,25 +178,25 @@ def test_context_refcounts(app, client): @app.teardown_request def teardown_req(error=None): - called.append('request') + called.append("request") @app.teardown_appcontext def teardown_app(error=None): - called.append('app') + called.append("app") - @app.route('/') + @app.route("/") def index(): with flask._app_ctx_stack.top: with flask._request_ctx_stack.top: pass env = flask._request_ctx_stack.top.request.environ - assert env['werkzeug.request'] is not None - return u'' + assert env["werkzeug.request"] is not None + return u"" - res = client.get('/') + res = client.get("/") assert res.status_code == 200 - assert res.data == b'' - assert called == ['request', 'app'] + assert res.data == b"" + assert called == ["request", "app"] def test_clean_pop(app): @@ -209,7 +209,7 @@ def test_clean_pop(app): @app.teardown_appcontext def teardown_app(error=None): - called.append('TEARDOWN') + called.append("TEARDOWN") try: with app.test_request_context(): @@ -217,5 +217,5 @@ def test_clean_pop(app): except ZeroDivisionError: pass - assert called == ['flask_test', 'TEARDOWN'] + assert called == ["flask_test", "TEARDOWN"] assert not flask.current_app diff --git a/tests/test_apps/blueprintapp/__init__.py b/tests/test_apps/blueprintapp/__init__.py index 93d94ddd..4b057985 100644 --- a/tests/test_apps/blueprintapp/__init__.py +++ b/tests/test_apps/blueprintapp/__init__.py @@ -1,8 +1,9 @@ from flask import Flask app = Flask(__name__) -app.config['DEBUG'] = True +app.config["DEBUG"] = True from blueprintapp.apps.admin import admin from blueprintapp.apps.frontend import frontend + app.register_blueprint(admin) app.register_blueprint(frontend) diff --git a/tests/test_apps/blueprintapp/apps/admin/__init__.py b/tests/test_apps/blueprintapp/apps/admin/__init__.py index 3f714d95..69a5a29d 100644 --- a/tests/test_apps/blueprintapp/apps/admin/__init__.py +++ b/tests/test_apps/blueprintapp/apps/admin/__init__.py @@ -1,15 +1,19 @@ from flask import Blueprint, render_template -admin = Blueprint('admin', __name__, url_prefix='/admin', - template_folder='templates', - static_folder='static') +admin = Blueprint( + "admin", + __name__, + url_prefix="/admin", + template_folder="templates", + static_folder="static", +) -@admin.route('/') +@admin.route("/") def index(): - return render_template('admin/index.html') + return render_template("admin/index.html") -@admin.route('/index2') +@admin.route("/index2") def index2(): - return render_template('./admin/index.html') + return render_template("./admin/index.html") diff --git a/tests/test_apps/blueprintapp/apps/frontend/__init__.py b/tests/test_apps/blueprintapp/apps/frontend/__init__.py index 344a5abb..48faf34d 100644 --- a/tests/test_apps/blueprintapp/apps/frontend/__init__.py +++ b/tests/test_apps/blueprintapp/apps/frontend/__init__.py @@ -1,13 +1,13 @@ from flask import Blueprint, render_template -frontend = Blueprint('frontend', __name__, template_folder='templates') +frontend = Blueprint("frontend", __name__, template_folder="templates") -@frontend.route('/') +@frontend.route("/") def index(): - return render_template('frontend/index.html') + return render_template("frontend/index.html") -@frontend.route('/missing') +@frontend.route("/missing") def missing_template(): - return render_template('missing_template.html') + return render_template("missing_template.html") diff --git a/tests/test_apps/cliapp/app.py b/tests/test_apps/cliapp/app.py index f142bc47..273bcce6 100644 --- a/tests/test_apps/cliapp/app.py +++ b/tests/test_apps/cliapp/app.py @@ -2,4 +2,4 @@ from __future__ import absolute_import, print_function from flask import Flask -testapp = Flask('testapp') +testapp = Flask("testapp") diff --git a/tests/test_apps/cliapp/factory.py b/tests/test_apps/cliapp/factory.py index 95d60396..c299a8a7 100644 --- a/tests/test_apps/cliapp/factory.py +++ b/tests/test_apps/cliapp/factory.py @@ -4,15 +4,15 @@ from flask import Flask def create_app(): - return Flask('app') + return Flask("app") def create_app2(foo, bar): - return Flask('_'.join(['app2', foo, bar])) + return Flask("_".join(["app2", foo, bar])) def create_app3(foo, script_info): - return Flask('_'.join(['app3', foo, script_info.data['test']])) + return Flask("_".join(["app3", foo, script_info.data["test"]])) def no_app(): diff --git a/tests/test_apps/cliapp/importerrorapp.py b/tests/test_apps/cliapp/importerrorapp.py index fb87c9b1..e6692c80 100644 --- a/tests/test_apps/cliapp/importerrorapp.py +++ b/tests/test_apps/cliapp/importerrorapp.py @@ -4,4 +4,4 @@ from flask import Flask raise ImportError() -testapp = Flask('testapp') +testapp = Flask("testapp") diff --git a/tests/test_apps/cliapp/multiapp.py b/tests/test_apps/cliapp/multiapp.py index 67ed6fba..37e7ceb7 100644 --- a/tests/test_apps/cliapp/multiapp.py +++ b/tests/test_apps/cliapp/multiapp.py @@ -2,5 +2,5 @@ from __future__ import absolute_import, print_function from flask import Flask -app1 = Flask('app1') -app2 = Flask('app2') +app1 = Flask("app1") +app2 = Flask("app2") diff --git a/tests/test_apps/helloworld/hello.py b/tests/test_apps/helloworld/hello.py index bbf7e467..71a2f90c 100644 --- a/tests/test_apps/helloworld/hello.py +++ b/tests/test_apps/helloworld/hello.py @@ -1,6 +1,8 @@ from flask import Flask + app = Flask(__name__) + @app.route("/") def hello(): return "Hello World!" diff --git a/tests/test_apps/subdomaintestmodule/__init__.py b/tests/test_apps/subdomaintestmodule/__init__.py index 3c5e3583..9b83c0d7 100644 --- a/tests/test_apps/subdomaintestmodule/__init__.py +++ b/tests/test_apps/subdomaintestmodule/__init__.py @@ -1,4 +1,4 @@ from flask import Module -mod = Module(__name__, 'foo', subdomain='foo') +mod = Module(__name__, "foo", subdomain="foo") diff --git a/tests/test_basic.py b/tests/test_basic.py index 08b4058c..3158ee09 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -26,48 +26,48 @@ from flask._compat import text_type def test_options_work(app, client): - @app.route('/', methods=['GET', 'POST']) + @app.route("/", methods=["GET", "POST"]) def index(): - return 'Hello World' + return "Hello World" - rv = client.open('/', method='OPTIONS') - assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST'] - assert rv.data == b'' + rv = client.open("/", method="OPTIONS") + assert sorted(rv.allow) == ["GET", "HEAD", "OPTIONS", "POST"] + assert rv.data == b"" def test_options_on_multiple_rules(app, client): - @app.route('/', methods=['GET', 'POST']) + @app.route("/", methods=["GET", "POST"]) def index(): - return 'Hello World' + return "Hello World" - @app.route('/', methods=['PUT']) + @app.route("/", methods=["PUT"]) def index_put(): - return 'Aha!' + return "Aha!" - rv = client.open('/', method='OPTIONS') - assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'] + rv = client.open("/", method="OPTIONS") + assert sorted(rv.allow) == ["GET", "HEAD", "OPTIONS", "POST", "PUT"] def test_provide_automatic_options_attr(): app = flask.Flask(__name__) def index(): - return 'Hello World!' + return "Hello World!" index.provide_automatic_options = False - app.route('/')(index) - rv = app.test_client().open('/', method='OPTIONS') + app.route("/")(index) + rv = app.test_client().open("/", method="OPTIONS") assert rv.status_code == 405 app = flask.Flask(__name__) def index2(): - return 'Hello World!' + return "Hello World!" index2.provide_automatic_options = True - app.route('/', methods=['OPTIONS'])(index2) - rv = app.test_client().open('/', method='OPTIONS') - assert sorted(rv.allow) == ['OPTIONS'] + app.route("/", methods=["OPTIONS"])(index2) + rv = app.test_client().open("/", method="OPTIONS") + assert sorted(rv.allow) == ["OPTIONS"] def test_provide_automatic_options_kwarg(app, client): @@ -77,69 +77,72 @@ def test_provide_automatic_options_kwarg(app, client): def more(): return flask.request.method - app.add_url_rule('/', view_func=index, provide_automatic_options=False) + app.add_url_rule("/", view_func=index, provide_automatic_options=False) app.add_url_rule( - '/more', view_func=more, methods=['GET', 'POST'], - provide_automatic_options=False + "/more", + view_func=more, + methods=["GET", "POST"], + provide_automatic_options=False, ) - assert client.get('/').data == b'GET' + assert client.get("/").data == b"GET" - rv = client.post('/') + rv = client.post("/") assert rv.status_code == 405 - assert sorted(rv.allow) == ['GET', 'HEAD'] + assert sorted(rv.allow) == ["GET", "HEAD"] # Older versions of Werkzeug.test.Client don't have an options method - if hasattr(client, 'options'): - rv = client.options('/') + if hasattr(client, "options"): + rv = client.options("/") else: - rv = client.open('/', method='OPTIONS') + rv = client.open("/", method="OPTIONS") assert rv.status_code == 405 - rv = client.head('/') + rv = client.head("/") assert rv.status_code == 200 assert not rv.data # head truncates - assert client.post('/more').data == b'POST' - assert client.get('/more').data == b'GET' + assert client.post("/more").data == b"POST" + assert client.get("/more").data == b"GET" - rv = client.delete('/more') + rv = client.delete("/more") assert rv.status_code == 405 - assert sorted(rv.allow) == ['GET', 'HEAD', 'POST'] + assert sorted(rv.allow) == ["GET", "HEAD", "POST"] - if hasattr(client, 'options'): - rv = client.options('/more') + if hasattr(client, "options"): + rv = client.options("/more") else: - rv = client.open('/more', method='OPTIONS') + rv = client.open("/more", method="OPTIONS") assert rv.status_code == 405 def test_request_dispatching(app, client): - @app.route('/') + @app.route("/") def index(): return flask.request.method - @app.route('/more', methods=['GET', 'POST']) + @app.route("/more", methods=["GET", "POST"]) def more(): return flask.request.method - assert client.get('/').data == b'GET' - rv = client.post('/') + assert client.get("/").data == b"GET" + rv = client.post("/") assert rv.status_code == 405 - assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS'] - rv = client.head('/') + assert sorted(rv.allow) == ["GET", "HEAD", "OPTIONS"] + rv = client.head("/") assert rv.status_code == 200 assert not rv.data # head truncates - assert client.post('/more').data == b'POST' - assert client.get('/more').data == b'GET' - rv = client.delete('/more') + assert client.post("/more").data == b"POST" + assert client.get("/more").data == b"GET" + rv = client.delete("/more") assert rv.status_code == 405 - assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST'] + assert sorted(rv.allow) == ["GET", "HEAD", "OPTIONS", "POST"] def test_disallow_string_for_allowed_methods(app): with pytest.raises(TypeError): - @app.route('/', methods='GET POST') + + @app.route("/", methods="GET POST") def index(): return "Hey" @@ -156,136 +159,129 @@ def test_url_mapping(app, client): def options(): return random_uuid4 - app.add_url_rule('/', 'index', index) - app.add_url_rule('/more', 'more', more, methods=['GET', 'POST']) + app.add_url_rule("/", "index", index) + app.add_url_rule("/more", "more", more, methods=["GET", "POST"]) # Issue 1288: Test that automatic options are not added when non-uppercase 'options' in methods - app.add_url_rule('/options', 'options', options, methods=['options']) + app.add_url_rule("/options", "options", options, methods=["options"]) - assert client.get('/').data == b'GET' - rv = client.post('/') + assert client.get("/").data == b"GET" + rv = client.post("/") assert rv.status_code == 405 - assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS'] - rv = client.head('/') + assert sorted(rv.allow) == ["GET", "HEAD", "OPTIONS"] + rv = client.head("/") assert rv.status_code == 200 assert not rv.data # head truncates - assert client.post('/more').data == b'POST' - assert client.get('/more').data == b'GET' - rv = client.delete('/more') + assert client.post("/more").data == b"POST" + assert client.get("/more").data == b"GET" + rv = client.delete("/more") assert rv.status_code == 405 - assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST'] - rv = client.open('/options', method='OPTIONS') + assert sorted(rv.allow) == ["GET", "HEAD", "OPTIONS", "POST"] + rv = client.open("/options", method="OPTIONS") assert rv.status_code == 200 assert random_uuid4 in rv.data.decode("utf-8") def test_werkzeug_routing(app, client): from werkzeug.routing import Submount, Rule - app.url_map.add(Submount('/foo', [ - Rule('/bar', endpoint='bar'), - Rule('/', endpoint='index') - ])) + + app.url_map.add( + Submount("/foo", [Rule("/bar", endpoint="bar"), Rule("/", endpoint="index")]) + ) def bar(): - return 'bar' + return "bar" def index(): - return 'index' + return "index" - app.view_functions['bar'] = bar - app.view_functions['index'] = index + app.view_functions["bar"] = bar + app.view_functions["index"] = index - assert client.get('/foo/').data == b'index' - assert client.get('/foo/bar').data == b'bar' + assert client.get("/foo/").data == b"index" + assert client.get("/foo/bar").data == b"bar" def test_endpoint_decorator(app, client): from werkzeug.routing import Submount, Rule - app.url_map.add(Submount('/foo', [ - Rule('/bar', endpoint='bar'), - Rule('/', endpoint='index') - ])) - @app.endpoint('bar') + app.url_map.add( + Submount("/foo", [Rule("/bar", endpoint="bar"), Rule("/", endpoint="index")]) + ) + + @app.endpoint("bar") def bar(): - return 'bar' + return "bar" - @app.endpoint('index') + @app.endpoint("index") def index(): - return 'index' + return "index" - assert client.get('/foo/').data == b'index' - assert client.get('/foo/bar').data == b'bar' + assert client.get("/foo/").data == b"index" + assert client.get("/foo/bar").data == b"bar" def test_session(app, client): - @app.route('/set', methods=['POST']) + @app.route("/set", methods=["POST"]) def set(): assert not flask.session.accessed assert not flask.session.modified - flask.session['value'] = flask.request.form['value'] + flask.session["value"] = flask.request.form["value"] assert flask.session.accessed assert flask.session.modified - return 'value set' + return "value set" - @app.route('/get') + @app.route("/get") def get(): assert not flask.session.accessed assert not flask.session.modified - v = flask.session.get('value', 'None') + v = flask.session.get("value", "None") assert flask.session.accessed assert not flask.session.modified return v - assert client.post('/set', data={'value': '42'}).data == b'value set' - assert client.get('/get').data == b'42' + assert client.post("/set", data={"value": "42"}).data == b"value set" + assert client.get("/get").data == b"42" def test_session_using_server_name(app, client): - app.config.update( - SERVER_NAME='example.com' - ) + app.config.update(SERVER_NAME="example.com") - @app.route('/') + @app.route("/") def index(): - flask.session['testing'] = 42 - return 'Hello World' + flask.session["testing"] = 42 + return "Hello World" - rv = client.get('/', 'http://example.com/') - assert 'domain=.example.com' in rv.headers['set-cookie'].lower() - assert 'httponly' in rv.headers['set-cookie'].lower() + rv = client.get("/", "http://example.com/") + assert "domain=.example.com" in rv.headers["set-cookie"].lower() + assert "httponly" in rv.headers["set-cookie"].lower() def test_session_using_server_name_and_port(app, client): - app.config.update( - SERVER_NAME='example.com:8080' - ) + app.config.update(SERVER_NAME="example.com:8080") - @app.route('/') + @app.route("/") def index(): - flask.session['testing'] = 42 - return 'Hello World' + flask.session["testing"] = 42 + return "Hello World" - rv = client.get('/', 'http://example.com:8080/') - assert 'domain=.example.com' in rv.headers['set-cookie'].lower() - assert 'httponly' in rv.headers['set-cookie'].lower() + rv = client.get("/", "http://example.com:8080/") + assert "domain=.example.com" in rv.headers["set-cookie"].lower() + assert "httponly" in rv.headers["set-cookie"].lower() def test_session_using_server_name_port_and_path(app, client): - app.config.update( - SERVER_NAME='example.com:8080', - APPLICATION_ROOT='/foo' - ) + app.config.update(SERVER_NAME="example.com:8080", APPLICATION_ROOT="/foo") - @app.route('/') + @app.route("/") def index(): - flask.session['testing'] = 42 - return 'Hello World' + flask.session["testing"] = 42 + return "Hello World" - rv = client.get('/', 'http://example.com:8080/foo') - assert 'domain=example.com' in rv.headers['set-cookie'].lower() - assert 'path=/foo' in rv.headers['set-cookie'].lower() - assert 'httponly' in rv.headers['set-cookie'].lower() + rv = client.get("/", "http://example.com:8080/foo") + assert "domain=example.com" in rv.headers["set-cookie"].lower() + assert "path=/foo" in rv.headers["set-cookie"].lower() + assert "httponly" in rv.headers["set-cookie"].lower() def test_session_using_application_root(app, client): @@ -295,105 +291,99 @@ def test_session_using_application_root(app, client): self.prefix = prefix def __call__(self, environ, start_response): - environ['SCRIPT_NAME'] = self.prefix + environ["SCRIPT_NAME"] = self.prefix return self.app(environ, start_response) - app.wsgi_app = PrefixPathMiddleware(app.wsgi_app, '/bar') - app.config.update( - APPLICATION_ROOT='/bar' - ) + app.wsgi_app = PrefixPathMiddleware(app.wsgi_app, "/bar") + app.config.update(APPLICATION_ROOT="/bar") - @app.route('/') + @app.route("/") def index(): - flask.session['testing'] = 42 - return 'Hello World' + flask.session["testing"] = 42 + return "Hello World" - rv = client.get('/', 'http://example.com:8080/') - assert 'path=/bar' in rv.headers['set-cookie'].lower() + rv = client.get("/", "http://example.com:8080/") + assert "path=/bar" in rv.headers["set-cookie"].lower() def test_session_using_session_settings(app, client): app.config.update( - SERVER_NAME='www.example.com:8080', - APPLICATION_ROOT='/test', - SESSION_COOKIE_DOMAIN='.example.com', + SERVER_NAME="www.example.com:8080", + APPLICATION_ROOT="/test", + SESSION_COOKIE_DOMAIN=".example.com", SESSION_COOKIE_HTTPONLY=False, SESSION_COOKIE_SECURE=True, - SESSION_COOKIE_SAMESITE='Lax', - SESSION_COOKIE_PATH='/' + SESSION_COOKIE_SAMESITE="Lax", + SESSION_COOKIE_PATH="/", ) - @app.route('/') + @app.route("/") def index(): - flask.session['testing'] = 42 - return 'Hello World' + flask.session["testing"] = 42 + return "Hello World" - rv = client.get('/', 'http://www.example.com:8080/test/') - cookie = rv.headers['set-cookie'].lower() - assert 'domain=.example.com' in cookie - assert 'path=/' in cookie - assert 'secure' in cookie - assert 'httponly' not in cookie - assert 'samesite' in cookie + rv = client.get("/", "http://www.example.com:8080/test/") + cookie = rv.headers["set-cookie"].lower() + assert "domain=.example.com" in cookie + assert "path=/" in cookie + assert "secure" in cookie + assert "httponly" not in cookie + assert "samesite" in cookie def test_session_using_samesite_attribute(app, client): - @app.route('/') + @app.route("/") def index(): - flask.session['testing'] = 42 - return 'Hello World' + flask.session["testing"] = 42 + return "Hello World" - app.config.update(SESSION_COOKIE_SAMESITE='invalid') + app.config.update(SESSION_COOKIE_SAMESITE="invalid") with pytest.raises(ValueError): - client.get('/') + client.get("/") app.config.update(SESSION_COOKIE_SAMESITE=None) - rv = client.get('/') - cookie = rv.headers['set-cookie'].lower() - assert 'samesite' not in cookie + rv = client.get("/") + cookie = rv.headers["set-cookie"].lower() + assert "samesite" not in cookie - app.config.update(SESSION_COOKIE_SAMESITE='Strict') - rv = client.get('/') - cookie = rv.headers['set-cookie'].lower() - assert 'samesite=strict' in cookie + app.config.update(SESSION_COOKIE_SAMESITE="Strict") + rv = client.get("/") + cookie = rv.headers["set-cookie"].lower() + assert "samesite=strict" in cookie - app.config.update(SESSION_COOKIE_SAMESITE='Lax') - rv = client.get('/') - cookie = rv.headers['set-cookie'].lower() - assert 'samesite=lax' in cookie + app.config.update(SESSION_COOKIE_SAMESITE="Lax") + rv = client.get("/") + cookie = rv.headers["set-cookie"].lower() + assert "samesite=lax" in cookie def test_session_localhost_warning(recwarn, app, client): - app.config.update( - SERVER_NAME='localhost:5000', - ) + app.config.update(SERVER_NAME="localhost:5000") - @app.route('/') + @app.route("/") def index(): - flask.session['testing'] = 42 - return 'testing' + flask.session["testing"] = 42 + return "testing" - rv = client.get('/', 'http://localhost:5000/') - assert 'domain' not in rv.headers['set-cookie'].lower() + rv = client.get("/", "http://localhost:5000/") + assert "domain" not in rv.headers["set-cookie"].lower() w = recwarn.pop(UserWarning) assert '"localhost" is not a valid cookie domain' in str(w.message) def test_session_ip_warning(recwarn, app, client): - app.config.update( - SERVER_NAME='127.0.0.1:5000', - ) + app.config.update(SERVER_NAME="127.0.0.1:5000") - @app.route('/') + @app.route("/") def index(): - flask.session['testing'] = 42 - return 'testing' + flask.session["testing"] = 42 + return "testing" - rv = client.get('/', 'http://127.0.0.1:5000/') - assert 'domain=127.0.0.1' in rv.headers['set-cookie'].lower() + rv = client.get("/", "http://127.0.0.1:5000/") + assert "domain=127.0.0.1" in rv.headers["set-cookie"].lower() w = recwarn.pop(UserWarning) - assert 'cookie domain is an IP' in str(w.message) + assert "cookie domain is an IP" in str(w.message) def test_missing_session(app): @@ -401,194 +391,194 @@ def test_missing_session(app): def expect_exception(f, *args, **kwargs): e = pytest.raises(RuntimeError, f, *args, **kwargs) - assert e.value.args and 'session is unavailable' in e.value.args[0] + assert e.value.args and "session is unavailable" in e.value.args[0] with app.test_request_context(): - assert flask.session.get('missing_key') is None - expect_exception(flask.session.__setitem__, 'foo', 42) - expect_exception(flask.session.pop, 'foo') + assert flask.session.get("missing_key") is None + expect_exception(flask.session.__setitem__, "foo", 42) + expect_exception(flask.session.pop, "foo") def test_session_expiration(app, client): permanent = True - @app.route('/') + @app.route("/") def index(): - flask.session['test'] = 42 + flask.session["test"] = 42 flask.session.permanent = permanent - return '' + return "" - @app.route('/test') + @app.route("/test") def test(): return text_type(flask.session.permanent) - rv = client.get('/') - assert 'set-cookie' in rv.headers - match = re.search(r'(?i)\bexpires=([^;]+)', rv.headers['set-cookie']) + rv = client.get("/") + assert "set-cookie" in rv.headers + match = re.search(r"(?i)\bexpires=([^;]+)", rv.headers["set-cookie"]) expires = parse_date(match.group()) expected = datetime.utcnow() + app.permanent_session_lifetime assert expires.year == expected.year assert expires.month == expected.month assert expires.day == expected.day - rv = client.get('/test') - assert rv.data == b'True' + rv = client.get("/test") + assert rv.data == b"True" permanent = False - rv = client.get('/') - assert 'set-cookie' in rv.headers - match = re.search(r'\bexpires=([^;]+)', rv.headers['set-cookie']) + rv = client.get("/") + assert "set-cookie" in rv.headers + match = re.search(r"\bexpires=([^;]+)", rv.headers["set-cookie"]) assert match is None def test_session_stored_last(app, client): @app.after_request def modify_session(response): - flask.session['foo'] = 42 + flask.session["foo"] = 42 return response - @app.route('/') + @app.route("/") def dump_session_contents(): - return repr(flask.session.get('foo')) + return repr(flask.session.get("foo")) - assert client.get('/').data == b'None' - assert client.get('/').data == b'42' + assert client.get("/").data == b"None" + assert client.get("/").data == b"42" def test_session_special_types(app, client): now = datetime.utcnow().replace(microsecond=0) the_uuid = uuid.uuid4() - @app.route('/') + @app.route("/") def dump_session_contents(): - flask.session['t'] = (1, 2, 3) - flask.session['b'] = b'\xff' - flask.session['m'] = flask.Markup('') - flask.session['u'] = the_uuid - flask.session['d'] = now - flask.session['t_tag'] = {' t': 'not-a-tuple'} - flask.session['di_t_tag'] = {' t__': 'not-a-tuple'} - flask.session['di_tag'] = {' di': 'not-a-dict'} - return '', 204 + flask.session["t"] = (1, 2, 3) + flask.session["b"] = b"\xff" + flask.session["m"] = flask.Markup("") + flask.session["u"] = the_uuid + flask.session["d"] = now + flask.session["t_tag"] = {" t": "not-a-tuple"} + flask.session["di_t_tag"] = {" t__": "not-a-tuple"} + flask.session["di_tag"] = {" di": "not-a-dict"} + return "", 204 with client: - client.get('/') + client.get("/") s = flask.session - assert s['t'] == (1, 2, 3) - assert type(s['b']) == bytes - assert s['b'] == b'\xff' - assert type(s['m']) == flask.Markup - assert s['m'] == flask.Markup('') - assert s['u'] == the_uuid - assert s['d'] == now - assert s['t_tag'] == {' t': 'not-a-tuple'} - assert s['di_t_tag'] == {' t__': 'not-a-tuple'} - assert s['di_tag'] == {' di': 'not-a-dict'} + assert s["t"] == (1, 2, 3) + assert type(s["b"]) == bytes + assert s["b"] == b"\xff" + assert type(s["m"]) == flask.Markup + assert s["m"] == flask.Markup("") + assert s["u"] == the_uuid + assert s["d"] == now + assert s["t_tag"] == {" t": "not-a-tuple"} + assert s["di_t_tag"] == {" t__": "not-a-tuple"} + assert s["di_tag"] == {" di": "not-a-dict"} def test_session_cookie_setting(app): is_permanent = True - @app.route('/bump') + @app.route("/bump") def bump(): - rv = flask.session['foo'] = flask.session.get('foo', 0) + 1 + rv = flask.session["foo"] = flask.session.get("foo", 0) + 1 flask.session.permanent = is_permanent return str(rv) - @app.route('/read') + @app.route("/read") def read(): - return str(flask.session.get('foo', 0)) + return str(flask.session.get("foo", 0)) def run_test(expect_header): with app.test_client() as c: - assert c.get('/bump').data == b'1' - assert c.get('/bump').data == b'2' - assert c.get('/bump').data == b'3' + assert c.get("/bump").data == b"1" + assert c.get("/bump").data == b"2" + assert c.get("/bump").data == b"3" - rv = c.get('/read') - set_cookie = rv.headers.get('set-cookie') + rv = c.get("/read") + set_cookie = rv.headers.get("set-cookie") assert (set_cookie is not None) == expect_header - assert rv.data == b'3' + assert rv.data == b"3" is_permanent = True - app.config['SESSION_REFRESH_EACH_REQUEST'] = True + app.config["SESSION_REFRESH_EACH_REQUEST"] = True run_test(expect_header=True) is_permanent = True - app.config['SESSION_REFRESH_EACH_REQUEST'] = False + app.config["SESSION_REFRESH_EACH_REQUEST"] = False run_test(expect_header=False) is_permanent = False - app.config['SESSION_REFRESH_EACH_REQUEST'] = True + app.config["SESSION_REFRESH_EACH_REQUEST"] = True run_test(expect_header=False) is_permanent = False - app.config['SESSION_REFRESH_EACH_REQUEST'] = False + app.config["SESSION_REFRESH_EACH_REQUEST"] = False run_test(expect_header=False) def test_session_vary_cookie(app, client): - @app.route('/set') + @app.route("/set") def set_session(): - flask.session['test'] = 'test' - return '' + flask.session["test"] = "test" + return "" - @app.route('/get') + @app.route("/get") def get(): - return flask.session.get('test') + return flask.session.get("test") - @app.route('/getitem') + @app.route("/getitem") def getitem(): - return flask.session['test'] + return flask.session["test"] - @app.route('/setdefault') + @app.route("/setdefault") def setdefault(): - return flask.session.setdefault('test', 'default') + return flask.session.setdefault("test", "default") - @app.route('/vary-cookie-header-set') + @app.route("/vary-cookie-header-set") def vary_cookie_header_set(): response = flask.Response() - response.vary.add('Cookie') - flask.session['test'] = 'test' + response.vary.add("Cookie") + flask.session["test"] = "test" return response - @app.route('/vary-header-set') + @app.route("/vary-header-set") def vary_header_set(): response = flask.Response() - response.vary.update(('Accept-Encoding', 'Accept-Language')) - flask.session['test'] = 'test' + response.vary.update(("Accept-Encoding", "Accept-Language")) + flask.session["test"] = "test" return response - @app.route('/no-vary-header') + @app.route("/no-vary-header") def no_vary_header(): - return '' + return "" - def expect(path, header_value='Cookie'): + def expect(path, header_value="Cookie"): rv = client.get(path) if header_value: # The 'Vary' key should exist in the headers only once. - assert len(rv.headers.get_all('Vary')) == 1 - assert rv.headers['Vary'] == header_value + assert len(rv.headers.get_all("Vary")) == 1 + assert rv.headers["Vary"] == header_value else: - assert 'Vary' not in rv.headers + assert "Vary" not in rv.headers - expect('/set') - expect('/get') - expect('/getitem') - expect('/setdefault') - expect('/vary-cookie-header-set') - expect('/vary-header-set', 'Accept-Encoding, Accept-Language, Cookie') - expect('/no-vary-header', None) + expect("/set") + expect("/get") + expect("/getitem") + expect("/setdefault") + expect("/vary-cookie-header-set") + expect("/vary-header-set", "Accept-Encoding, Accept-Language, Cookie") + expect("/no-vary-header", None) def test_flashes(app, req_ctx): assert not flask.session.modified - flask.flash('Zap') + flask.flash("Zap") flask.session.modified = False - flask.flash('Zip') + flask.flash("Zip") assert flask.session.modified - assert list(flask.get_flashed_messages()) == ['Zap', 'Zip'] + assert list(flask.get_flashed_messages()) == ["Zap", "Zip"] def test_extended_flashing(app): @@ -598,77 +588,78 @@ def test_extended_flashing(app): # in the view functions will cause a 500 response to the test client # instead of propagating exceptions. - @app.route('/') + @app.route("/") def index(): - flask.flash(u'Hello World') - flask.flash(u'Hello World', 'error') - flask.flash(flask.Markup(u'Testing'), 'warning') - return '' + flask.flash(u"Hello World") + flask.flash(u"Hello World", "error") + flask.flash(flask.Markup(u"Testing"), "warning") + return "" - @app.route('/test/') + @app.route("/test/") def test(): messages = flask.get_flashed_messages() assert list(messages) == [ - u'Hello World', - u'Hello World', - flask.Markup(u'Testing') + u"Hello World", + u"Hello World", + flask.Markup(u"Testing"), ] - return '' + return "" - @app.route('/test_with_categories/') + @app.route("/test_with_categories/") def test_with_categories(): messages = flask.get_flashed_messages(with_categories=True) assert len(messages) == 3 assert list(messages) == [ - ('message', u'Hello World'), - ('error', u'Hello World'), - ('warning', flask.Markup(u'Testing')) + ("message", u"Hello World"), + ("error", u"Hello World"), + ("warning", flask.Markup(u"Testing")), ] - return '' + return "" - @app.route('/test_filter/') + @app.route("/test_filter/") def test_filter(): messages = flask.get_flashed_messages( - category_filter=['message'], with_categories=True) - assert list(messages) == [('message', u'Hello World')] - return '' + category_filter=["message"], with_categories=True + ) + assert list(messages) == [("message", u"Hello World")] + return "" - @app.route('/test_filters/') + @app.route("/test_filters/") def test_filters(): messages = flask.get_flashed_messages( - category_filter=['message', 'warning'], with_categories=True) + category_filter=["message", "warning"], with_categories=True + ) assert list(messages) == [ - ('message', u'Hello World'), - ('warning', flask.Markup(u'Testing')) + ("message", u"Hello World"), + ("warning", flask.Markup(u"Testing")), ] - return '' + return "" - @app.route('/test_filters_without_returning_categories/') + @app.route("/test_filters_without_returning_categories/") def test_filters2(): - messages = flask.get_flashed_messages( - category_filter=['message', 'warning']) + messages = flask.get_flashed_messages(category_filter=["message", "warning"]) assert len(messages) == 2 - assert messages[0] == u'Hello World' - assert messages[1] == flask.Markup(u'Testing') - return '' + assert messages[0] == u"Hello World" + assert messages[1] == flask.Markup(u"Testing") + return "" # Create new test client on each test to clean flashed messages. client = app.test_client() - client.get('/') - client.get('/test_with_categories/') + client.get("/") + client.get("/test_with_categories/") client = app.test_client() - client.get('/') - client.get('/test_filter/') + client.get("/") + client.get("/test_filter/") client = app.test_client() - client.get('/') - client.get('/test_filters/') + client.get("/") + client.get("/test_filters/") client = app.test_client() - client.get('/') - client.get('/test_filters_without_returning_categories/') + client.get("/") + client.get("/test_filters_without_returning_categories/") def test_request_processing(app, client): @@ -676,24 +667,24 @@ def test_request_processing(app, client): @app.before_request def before_request(): - evts.append('before') + evts.append("before") @app.after_request def after_request(response): - response.data += b'|after' - evts.append('after') + response.data += b"|after" + evts.append("after") return response - @app.route('/') + @app.route("/") def index(): - assert 'before' in evts - assert 'after' not in evts - return 'request' + assert "before" in evts + assert "after" not in evts + return "request" - assert 'after' not in evts - rv = client.get('/').data - assert 'after' in evts - assert rv == b'request|after' + assert "after" not in evts + rv = client.get("/").data + assert "after" in evts + assert rv == b"request|after" def test_request_preprocessing_early_return(app, client): @@ -713,29 +704,29 @@ def test_request_preprocessing_early_return(app, client): evts.append(3) return "bye" - @app.route('/') + @app.route("/") def index(): - evts.append('index') + evts.append("index") return "damnit" - rv = client.get('/').data.strip() - assert rv == b'hello' + rv = client.get("/").data.strip() + assert rv == b"hello" assert evts == [1, 2] def test_after_request_processing(app, client): - @app.route('/') + @app.route("/") def index(): @flask.after_this_request def foo(response): - response.headers['X-Foo'] = 'a header' + response.headers["X-Foo"] = "a header" return response - return 'Test' + return "Test" - resp = client.get('/') + resp = client.get("/") assert resp.status_code == 200 - assert resp.headers['X-Foo'] == 'a header' + assert resp.headers["X-Foo"] == "a header" def test_teardown_request_handler(app, client): @@ -746,13 +737,13 @@ def test_teardown_request_handler(app, client): called.append(True) return "Ignored" - @app.route('/') + @app.route("/") def root(): return "Response" - rv = client.get('/') + rv = client.get("/") assert rv.status_code == 200 - assert b'Response' in rv.data + assert b"Response" in rv.data assert len(called) == 1 @@ -764,13 +755,13 @@ def test_teardown_request_handler_debug_mode(app, client): called.append(True) return "Ignored" - @app.route('/') + @app.route("/") def root(): return "Response" - rv = client.get('/') + rv = client.get("/") assert rv.status_code == 200 - assert b'Response' in rv.data + assert b"Response" in rv.data assert len(called) == 1 @@ -802,13 +793,13 @@ def test_teardown_request_handler_error(app, client): except: pass - @app.route('/') + @app.route("/") def fails(): 1 // 0 - rv = client.get('/') + rv = client.get("/") assert rv.status_code == 500 - assert b'Internal Server Error' in rv.data + assert b"Internal Server Error" in rv.data assert len(called) == 2 @@ -841,12 +832,12 @@ def test_before_after_request_order(app, client): def finish2(exc): called.append(5) - @app.route('/') + @app.route("/") def index(): - return '42' + return "42" - rv = client.get('/') - assert rv.data == b'42' + rv = client.get("/") + assert rv.data == b"42" assert called == [1, 2, 3, 4, 5, 6] @@ -855,44 +846,44 @@ def test_error_handling(app, client): @app.errorhandler(404) def not_found(e): - return 'not found', 404 + return "not found", 404 @app.errorhandler(500) def internal_server_error(e): - return 'internal server error', 500 + return "internal server error", 500 @app.errorhandler(Forbidden) def forbidden(e): - return 'forbidden', 403 + return "forbidden", 403 - @app.route('/') + @app.route("/") def index(): flask.abort(404) - @app.route('/error') + @app.route("/error") def error(): 1 // 0 - @app.route('/forbidden') + @app.route("/forbidden") def error2(): flask.abort(403) - rv = client.get('/') + rv = client.get("/") assert rv.status_code == 404 - assert rv.data == b'not found' - rv = client.get('/error') + assert rv.data == b"not found" + rv = client.get("/error") assert rv.status_code == 500 - assert b'internal server error' == rv.data - rv = client.get('/forbidden') + assert b"internal server error" == rv.data + rv = client.get("/forbidden") assert rv.status_code == 403 - assert b'forbidden' == rv.data + assert b"forbidden" == rv.data def test_error_handler_unknown_code(app): with pytest.raises(KeyError) as exc_info: - app.register_error_handler(999, lambda e: ('999', 999)) + app.register_error_handler(999, lambda e: ("999", 999)) - assert 'Use a subclass' in exc_info.value.args[0] + assert "Use a subclass" in exc_info.value.args[0] def test_error_handling_processing(app, client): @@ -900,31 +891,31 @@ def test_error_handling_processing(app, client): @app.errorhandler(500) def internal_server_error(e): - return 'internal server error', 500 + return "internal server error", 500 - @app.route('/') + @app.route("/") def broken_func(): 1 // 0 @app.after_request def after_request(resp): - resp.mimetype = 'text/x-special' + resp.mimetype = "text/x-special" return resp - resp = client.get('/') - assert resp.mimetype == 'text/x-special' - assert resp.data == b'internal server error' + resp = client.get("/") + assert resp.mimetype == "text/x-special" + assert resp.data == b"internal server error" def test_baseexception_error_handling(app, client): app.testing = False - @app.route('/') + @app.route("/") def broken_func(): raise KeyboardInterrupt() with pytest.raises(KeyboardInterrupt): - client.get('/') + client.get("/") ctx = flask._request_ctx_stack.top assert ctx.preserved @@ -934,15 +925,15 @@ def test_baseexception_error_handling(app, client): def test_before_request_and_routing_errors(app, client): @app.before_request def attach_something(): - flask.g.something = 'value' + flask.g.something = "value" @app.errorhandler(404) def return_something(error): return flask.g.something, 404 - rv = client.get('/') + rv = client.get("/") assert rv.status_code == 404 - assert rv.data == b'value' + assert rv.data == b"value" def test_user_error_handling(app, client): @@ -952,13 +943,13 @@ def test_user_error_handling(app, client): @app.errorhandler(MyException) def handle_my_exception(e): assert isinstance(e, MyException) - return '42' + return "42" - @app.route('/') + @app.route("/") def index(): raise MyException() - assert client.get('/').data == b'42' + assert client.get("/").data == b"42" def test_http_error_subclass_handling(app, client): @@ -968,29 +959,29 @@ def test_http_error_subclass_handling(app, client): @app.errorhandler(ForbiddenSubclass) def handle_forbidden_subclass(e): assert isinstance(e, ForbiddenSubclass) - return 'banana' + return "banana" @app.errorhandler(403) def handle_forbidden_subclass(e): assert not isinstance(e, ForbiddenSubclass) assert isinstance(e, Forbidden) - return 'apple' + return "apple" - @app.route('/1') + @app.route("/1") def index1(): raise ForbiddenSubclass() - @app.route('/2') + @app.route("/2") def index2(): flask.abort(403) - @app.route('/3') + @app.route("/3") def index3(): raise Forbidden() - assert client.get('/1').data == b'banana' - assert client.get('/2').data == b'apple' - assert client.get('/3').data == b'apple' + assert client.get("/1").data == b"banana" + assert client.get("/2").data == b"apple" + assert client.get("/3").data == b"apple" def test_errorhandler_precedence(app, client): @@ -1005,67 +996,67 @@ def test_errorhandler_precedence(app, client): @app.errorhandler(E2) def handle_e2(e): - return 'E2' + return "E2" @app.errorhandler(Exception) def handle_exception(e): - return 'Exception' + return "Exception" - @app.route('/E1') + @app.route("/E1") def raise_e1(): raise E1 - @app.route('/E3') + @app.route("/E3") def raise_e3(): raise E3 - rv = client.get('/E1') - assert rv.data == b'Exception' + rv = client.get("/E1") + assert rv.data == b"Exception" - rv = client.get('/E3') - assert rv.data == b'E2' + rv = client.get("/E3") + assert rv.data == b"E2" def test_trapping_of_bad_request_key_errors(app, client): - @app.route('/key') + @app.route("/key") def fail(): - flask.request.form['missing_key'] + flask.request.form["missing_key"] - @app.route('/abort') + @app.route("/abort") def allow_abort(): flask.abort(400) - rv = client.get('/key') + rv = client.get("/key") assert rv.status_code == 400 - assert b'missing_key' not in rv.data - rv = client.get('/abort') + assert b"missing_key" not in rv.data + rv = client.get("/abort") assert rv.status_code == 400 app.debug = True with pytest.raises(KeyError) as e: client.get("/key") assert e.errisinstance(BadRequest) - assert 'missing_key' in e.value.get_description() - rv = client.get('/abort') + assert "missing_key" in e.value.get_description() + rv = client.get("/abort") assert rv.status_code == 400 app.debug = False - app.config['TRAP_BAD_REQUEST_ERRORS'] = True + app.config["TRAP_BAD_REQUEST_ERRORS"] = True with pytest.raises(KeyError): - client.get('/key') + client.get("/key") with pytest.raises(BadRequest): - client.get('/abort') + client.get("/abort") def test_trapping_of_all_http_exceptions(app, client): - app.config['TRAP_HTTP_EXCEPTIONS'] = True + app.config["TRAP_HTTP_EXCEPTIONS"] = True - @app.route('/fail') + @app.route("/fail") def fail(): flask.abort(404) with pytest.raises(NotFound): - client.get('/fail') + client.get("/fail") def test_error_handler_after_processor_error(app, client): @@ -1073,121 +1064,120 @@ def test_error_handler_after_processor_error(app, client): @app.before_request def before_request(): - if trigger == 'before': + if trigger == "before": 1 // 0 @app.after_request def after_request(response): - if trigger == 'after': + if trigger == "after": 1 // 0 return response - @app.route('/') + @app.route("/") def index(): - return 'Foo' + return "Foo" @app.errorhandler(500) def internal_server_error(e): - return 'Hello Server Error', 500 + return "Hello Server Error", 500 - for trigger in 'before', 'after': - rv = client.get('/') + for trigger in "before", "after": + rv = client.get("/") assert rv.status_code == 500 - assert rv.data == b'Hello Server Error' + assert rv.data == b"Hello Server Error" def test_enctype_debug_helper(app, client): from flask.debughelpers import DebugFilesKeyError + app.debug = True - @app.route('/fail', methods=['POST']) + @app.route("/fail", methods=["POST"]) def index(): - return flask.request.files['foo'].filename + return flask.request.files["foo"].filename # with statement is important because we leave an exception on the # stack otherwise and we want to ensure that this is not the case # to not negatively affect other tests. with client: with pytest.raises(DebugFilesKeyError) as e: - client.post('/fail', data={'foo': 'index.txt'}) - assert 'no file contents were transmitted' in str(e.value) + client.post("/fail", data={"foo": "index.txt"}) + assert "no file contents were transmitted" in str(e.value) assert 'This was submitted: "index.txt"' in str(e.value) def test_response_types(app, client): - @app.route('/text') + @app.route("/text") def from_text(): - return u'Hällo Wörld' + return u"Hällo Wörld" - @app.route('/bytes') + @app.route("/bytes") def from_bytes(): - return u'Hällo Wörld'.encode('utf-8') + return u"Hällo Wörld".encode("utf-8") - @app.route('/full_tuple') + @app.route("/full_tuple") def from_full_tuple(): - return 'Meh', 400, { - 'X-Foo': 'Testing', - 'Content-Type': 'text/plain; charset=utf-8' - } + return ( + "Meh", + 400, + {"X-Foo": "Testing", "Content-Type": "text/plain; charset=utf-8"}, + ) - @app.route('/text_headers') + @app.route("/text_headers") def from_text_headers(): - return 'Hello', { - 'X-Foo': 'Test', - 'Content-Type': 'text/plain; charset=utf-8' - } + return "Hello", {"X-Foo": "Test", "Content-Type": "text/plain; charset=utf-8"} - @app.route('/text_status') + @app.route("/text_status") def from_text_status(): - return 'Hi, status!', 400 + return "Hi, status!", 400 - @app.route('/response_headers') + @app.route("/response_headers") def from_response_headers(): - return flask.Response('Hello world', 404, {'X-Foo': 'Baz'}), { - "X-Foo": "Bar", - "X-Bar": "Foo" - } + return ( + flask.Response("Hello world", 404, {"X-Foo": "Baz"}), + {"X-Foo": "Bar", "X-Bar": "Foo"}, + ) - @app.route('/response_status') + @app.route("/response_status") def from_response_status(): - return app.response_class('Hello world', 400), 500 + return app.response_class("Hello world", 400), 500 - @app.route('/wsgi') + @app.route("/wsgi") def from_wsgi(): return NotFound() - assert client.get('/text').data == u'Hällo Wörld'.encode('utf-8') - assert client.get('/bytes').data == u'Hällo Wörld'.encode('utf-8') + assert client.get("/text").data == u"Hällo Wörld".encode("utf-8") + assert client.get("/bytes").data == u"Hällo Wörld".encode("utf-8") - rv = client.get('/full_tuple') - assert rv.data == b'Meh' - assert rv.headers['X-Foo'] == 'Testing' + rv = client.get("/full_tuple") + assert rv.data == b"Meh" + assert rv.headers["X-Foo"] == "Testing" assert rv.status_code == 400 - assert rv.mimetype == 'text/plain' + assert rv.mimetype == "text/plain" - rv = client.get('/text_headers') - assert rv.data == b'Hello' - assert rv.headers['X-Foo'] == 'Test' + rv = client.get("/text_headers") + assert rv.data == b"Hello" + assert rv.headers["X-Foo"] == "Test" assert rv.status_code == 200 - assert rv.mimetype == 'text/plain' + assert rv.mimetype == "text/plain" - rv = client.get('/text_status') - assert rv.data == b'Hi, status!' + rv = client.get("/text_status") + assert rv.data == b"Hi, status!" assert rv.status_code == 400 - assert rv.mimetype == 'text/html' + assert rv.mimetype == "text/html" - rv = client.get('/response_headers') - assert rv.data == b'Hello world' - assert rv.headers.getlist('X-Foo') == ['Baz', 'Bar'] - assert rv.headers['X-Bar'] == 'Foo' + rv = client.get("/response_headers") + assert rv.data == b"Hello world" + assert rv.headers.getlist("X-Foo") == ["Baz", "Bar"] + assert rv.headers["X-Bar"] == "Foo" assert rv.status_code == 404 - rv = client.get('/response_status') - assert rv.data == b'Hello world' + rv = client.get("/response_status") + assert rv.data == b"Hello world" assert rv.status_code == 500 - rv = client.get('/wsgi') - assert b'Not Found' in rv.data + rv = client.get("/wsgi") + assert b"Not Found" in rv.data assert rv.status_code == 404 @@ -1195,160 +1185,152 @@ def test_response_type_errors(): app = flask.Flask(__name__) app.testing = True - @app.route('/none') + @app.route("/none") def from_none(): pass - @app.route('/small_tuple') + @app.route("/small_tuple") def from_small_tuple(): - return 'Hello', + return ("Hello",) - @app.route('/large_tuple') + @app.route("/large_tuple") def from_large_tuple(): - return 'Hello', 234, {'X-Foo': 'Bar'}, '???' + return "Hello", 234, {"X-Foo": "Bar"}, "???" - @app.route('/bad_type') + @app.route("/bad_type") def from_bad_type(): return True - @app.route('/bad_wsgi') + @app.route("/bad_wsgi") def from_bad_wsgi(): return lambda: None c = app.test_client() with pytest.raises(TypeError) as e: - c.get('/none') - assert 'returned None' in str(e) + c.get("/none") + assert "returned None" in str(e) with pytest.raises(TypeError) as e: - c.get('/small_tuple') - assert 'tuple must have the form' in str(e) + c.get("/small_tuple") + assert "tuple must have the form" in str(e) - pytest.raises(TypeError, c.get, '/large_tuple') + pytest.raises(TypeError, c.get, "/large_tuple") with pytest.raises(TypeError) as e: - c.get('/bad_type') - assert 'it was a bool' in str(e) + c.get("/bad_type") + assert "it was a bool" in str(e) - pytest.raises(TypeError, c.get, '/bad_wsgi') + pytest.raises(TypeError, c.get, "/bad_wsgi") def test_make_response(app, req_ctx): rv = flask.make_response() assert rv.status_code == 200 - assert rv.data == b'' - assert rv.mimetype == 'text/html' + assert rv.data == b"" + assert rv.mimetype == "text/html" - rv = flask.make_response('Awesome') + rv = flask.make_response("Awesome") assert rv.status_code == 200 - assert rv.data == b'Awesome' - assert rv.mimetype == 'text/html' + assert rv.data == b"Awesome" + assert rv.mimetype == "text/html" - rv = flask.make_response('W00t', 404) + rv = flask.make_response("W00t", 404) assert rv.status_code == 404 - assert rv.data == b'W00t' - assert rv.mimetype == 'text/html' + assert rv.data == b"W00t" + assert rv.mimetype == "text/html" def test_make_response_with_response_instance(app, req_ctx): - rv = flask.make_response( - flask.jsonify({'msg': 'W00t'}), 400) + rv = flask.make_response(flask.jsonify({"msg": "W00t"}), 400) assert rv.status_code == 400 assert rv.data == b'{"msg":"W00t"}\n' - assert rv.mimetype == 'application/json' + assert rv.mimetype == "application/json" + + rv = flask.make_response(flask.Response(""), 400) + assert rv.status_code == 400 + assert rv.data == b"" + assert rv.mimetype == "text/html" rv = flask.make_response( - flask.Response(''), 400) + flask.Response("", headers={"Content-Type": "text/html"}), + 400, + [("X-Foo", "bar")], + ) assert rv.status_code == 400 - assert rv.data == b'' - assert rv.mimetype == 'text/html' - - rv = flask.make_response( - flask.Response('', headers={'Content-Type': 'text/html'}), - 400, [('X-Foo', 'bar')]) - assert rv.status_code == 400 - assert rv.headers['Content-Type'] == 'text/html' - assert rv.headers['X-Foo'] == 'bar' + assert rv.headers["Content-Type"] == "text/html" + assert rv.headers["X-Foo"] == "bar" def test_jsonify_no_prettyprint(app, req_ctx): app.config.update({"JSONIFY_PRETTYPRINT_REGULAR": False}) compressed_msg = b'{"msg":{"submsg":"W00t"},"msg2":"foobar"}\n' - uncompressed_msg = { - "msg": { - "submsg": "W00t" - }, - "msg2": "foobar" - } + uncompressed_msg = {"msg": {"submsg": "W00t"}, "msg2": "foobar"} - rv = flask.make_response( - flask.jsonify(uncompressed_msg), 200) + rv = flask.make_response(flask.jsonify(uncompressed_msg), 200) assert rv.data == compressed_msg def test_jsonify_prettyprint(app, req_ctx): app.config.update({"JSONIFY_PRETTYPRINT_REGULAR": True}) compressed_msg = {"msg": {"submsg": "W00t"}, "msg2": "foobar"} - pretty_response = \ + pretty_response = ( b'{\n "msg": {\n "submsg": "W00t"\n }, \n "msg2": "foobar"\n}\n' + ) - rv = flask.make_response( - flask.jsonify(compressed_msg), 200) + rv = flask.make_response(flask.jsonify(compressed_msg), 200) assert rv.data == pretty_response def test_jsonify_mimetype(app, req_ctx): - app.config.update({"JSONIFY_MIMETYPE": 'application/vnd.api+json'}) - msg = { - "msg": {"submsg": "W00t"}, - } - rv = flask.make_response( - flask.jsonify(msg), 200) - assert rv.mimetype == 'application/vnd.api+json' + app.config.update({"JSONIFY_MIMETYPE": "application/vnd.api+json"}) + msg = {"msg": {"submsg": "W00t"}} + rv = flask.make_response(flask.jsonify(msg), 200) + assert rv.mimetype == "application/vnd.api+json" def test_jsonify_args_and_kwargs_check(app, req_ctx): with pytest.raises(TypeError) as e: - flask.jsonify('fake args', kwargs='fake') - assert 'behavior undefined' in str(e.value) + flask.jsonify("fake args", kwargs="fake") + assert "behavior undefined" in str(e.value) def test_url_generation(app, req_ctx): - @app.route('/hello/', methods=['POST']) + @app.route("/hello/", methods=["POST"]) def hello(): pass - assert flask.url_for('hello', name='test x') == '/hello/test%20x' - assert flask.url_for('hello', name='test x', _external=True) == \ - 'http://localhost/hello/test%20x' + assert flask.url_for("hello", name="test x") == "/hello/test%20x" + assert ( + flask.url_for("hello", name="test x", _external=True) + == "http://localhost/hello/test%20x" + ) def test_build_error_handler(app): # Test base case, a URL which results in a BuildError. with app.test_request_context(): - pytest.raises(BuildError, flask.url_for, 'spam') + pytest.raises(BuildError, flask.url_for, "spam") # Verify the error is re-raised if not the current exception. try: with app.test_request_context(): - flask.url_for('spam') + flask.url_for("spam") except BuildError as err: error = err try: - raise RuntimeError('Test case where BuildError is not current.') + raise RuntimeError("Test case where BuildError is not current.") except RuntimeError: - pytest.raises( - BuildError, app.handle_url_build_error, error, 'spam', {}) + pytest.raises(BuildError, app.handle_url_build_error, error, "spam", {}) # Test a custom handler. def handler(error, endpoint, values): # Just a test. - return '/test_handler/' + return "/test_handler/" app.url_build_error_handlers.append(handler) with app.test_request_context(): - assert flask.url_for('spam') == '/test_handler/' + assert flask.url_for("spam") == "/test_handler/" def test_build_error_handler_reraise(app): @@ -1359,22 +1341,22 @@ def test_build_error_handler_reraise(app): app.url_build_error_handlers.append(handler_raises_build_error) with app.test_request_context(): - pytest.raises(BuildError, flask.url_for, 'not.existing') + pytest.raises(BuildError, flask.url_for, "not.existing") def test_url_for_passes_special_values_to_build_error_handler(app): @app.url_build_error_handlers.append def handler(error, endpoint, values): assert values == { - '_external': False, - '_anchor': None, - '_method': None, - '_scheme': None, + "_external": False, + "_anchor": None, + "_method": None, + "_scheme": None, } - return 'handled' + return "handled" with app.test_request_context(): - flask.url_for('/') + flask.url_for("/") def test_custom_converters(app, client): @@ -1382,54 +1364,53 @@ def test_custom_converters(app, client): class ListConverter(BaseConverter): def to_python(self, value): - return value.split(',') + return value.split(",") def to_url(self, value): base_to_url = super(ListConverter, self).to_url - return ','.join(base_to_url(x) for x in value) + return ",".join(base_to_url(x) for x in value) - app.url_map.converters['list'] = ListConverter + app.url_map.converters["list"] = ListConverter - @app.route('/') + @app.route("/") def index(args): - return '|'.join(args) + return "|".join(args) - assert client.get('/1,2,3').data == b'1|2|3' + assert client.get("/1,2,3").data == b"1|2|3" def test_static_files(app, client): - rv = client.get('/static/index.html') + rv = client.get("/static/index.html") assert rv.status_code == 200 - assert rv.data.strip() == b'

Hello World!

' + assert rv.data.strip() == b"

Hello World!

" with app.test_request_context(): - assert flask.url_for('static', filename='index.html') == \ - '/static/index.html' + assert flask.url_for("static", filename="index.html") == "/static/index.html" rv.close() def test_static_url_path(): - app = flask.Flask(__name__, static_url_path='/foo') + app = flask.Flask(__name__, static_url_path="/foo") app.testing = True - rv = app.test_client().get('/foo/index.html') + rv = app.test_client().get("/foo/index.html") assert rv.status_code == 200 rv.close() with app.test_request_context(): - assert flask.url_for('static', filename='index.html') == '/foo/index.html' + assert flask.url_for("static", filename="index.html") == "/foo/index.html" def test_static_route_with_host_matching(): - app = flask.Flask(__name__, host_matching=True, static_host='example.com') + app = flask.Flask(__name__, host_matching=True, static_host="example.com") c = app.test_client() - rv = c.get('http://example.com/static/index.html') + rv = c.get("http://example.com/static/index.html") assert rv.status_code == 200 rv.close() with app.test_request_context(): - rv = flask.url_for('static', filename='index.html', _external=True) - assert rv == 'http://example.com/static/index.html' + rv = flask.url_for("static", filename="index.html", _external=True) + assert rv == "http://example.com/static/index.html" # Providing static_host without host_matching=True should error. with pytest.raises(Exception): - flask.Flask(__name__, static_host='example.com') + flask.Flask(__name__, static_host="example.com") # Providing host_matching=True with static_folder but without static_host should error. with pytest.raises(Exception): flask.Flask(__name__, host_matching=True) @@ -1438,41 +1419,39 @@ def test_static_route_with_host_matching(): def test_request_locals(): - assert repr(flask.g) == '' + assert repr(flask.g) == "" assert not flask.g def test_test_app_proper_environ(): app = flask.Flask(__name__, subdomain_matching=True) - app.config.update( - SERVER_NAME='localhost.localdomain:5000' - ) + app.config.update(SERVER_NAME="localhost.localdomain:5000") client = app.test_client() - @app.route('/') + @app.route("/") def index(): - return 'Foo' + return "Foo" - @app.route('/', subdomain='foo') + @app.route("/", subdomain="foo") def subdomain(): - return 'Foo SubDomain' + return "Foo SubDomain" - rv = client.get('/') - assert rv.data == b'Foo' + rv = client.get("/") + assert rv.data == b"Foo" - rv = client.get('/', 'http://localhost.localdomain:5000') - assert rv.data == b'Foo' + rv = client.get("/", "http://localhost.localdomain:5000") + assert rv.data == b"Foo" - rv = client.get('/', 'https://localhost.localdomain:5000') - assert rv.data == b'Foo' + rv = client.get("/", "https://localhost.localdomain:5000") + assert rv.data == b"Foo" - app.config.update(SERVER_NAME='localhost.localdomain') - rv = client.get('/', 'https://localhost.localdomain') - assert rv.data == b'Foo' + app.config.update(SERVER_NAME="localhost.localdomain") + rv = client.get("/", "https://localhost.localdomain") + assert rv.data == b"Foo" try: - app.config.update(SERVER_NAME='localhost.localdomain:443') - rv = client.get('/', 'https://localhost.localdomain') + app.config.update(SERVER_NAME="localhost.localdomain:443") + rv = client.get("/", "https://localhost.localdomain") # Werkzeug 0.8 assert rv.status_code == 404 except ValueError as e: @@ -1484,8 +1463,8 @@ def test_test_app_proper_environ(): ) try: - app.config.update(SERVER_NAME='localhost.localdomain') - rv = client.get('/', 'http://foo.localhost') + app.config.update(SERVER_NAME="localhost.localdomain") + rv = client.get("/", "http://foo.localhost") # Werkzeug 0.8 assert rv.status_code == 404 except ValueError as e: @@ -1496,157 +1475,158 @@ def test_test_app_proper_environ(): "server name from the WSGI environment ('foo.localhost')" ) - rv = client.get('/', 'http://foo.localhost.localdomain') - assert rv.data == b'Foo SubDomain' + rv = client.get("/", "http://foo.localhost.localdomain") + assert rv.data == b"Foo SubDomain" def test_exception_propagation(app, client): def apprunner(config_key): - - @app.route('/') + @app.route("/") def index(): 1 // 0 if config_key is not None: app.config[config_key] = True with pytest.raises(Exception): - client.get('/') + client.get("/") else: - assert client.get('/').status_code == 500 + assert client.get("/").status_code == 500 # we have to run this test in an isolated thread because if the # debug flag is set to true and an exception happens the context is # not torn down. This causes other tests that run after this fail # when they expect no exception on the stack. - for config_key in 'TESTING', 'PROPAGATE_EXCEPTIONS', 'DEBUG', None: + for config_key in "TESTING", "PROPAGATE_EXCEPTIONS", "DEBUG", None: t = Thread(target=apprunner, args=(config_key,)) t.start() t.join() -@pytest.mark.parametrize('debug', [True, False]) -@pytest.mark.parametrize('use_debugger', [True, False]) -@pytest.mark.parametrize('use_reloader', [True, False]) -@pytest.mark.parametrize('propagate_exceptions', [None, True, False]) -def test_werkzeug_passthrough_errors(monkeypatch, debug, use_debugger, - use_reloader, propagate_exceptions, app): +@pytest.mark.parametrize("debug", [True, False]) +@pytest.mark.parametrize("use_debugger", [True, False]) +@pytest.mark.parametrize("use_reloader", [True, False]) +@pytest.mark.parametrize("propagate_exceptions", [None, True, False]) +def test_werkzeug_passthrough_errors( + monkeypatch, debug, use_debugger, use_reloader, propagate_exceptions, app +): rv = {} # Mocks werkzeug.serving.run_simple method def run_simple_mock(*args, **kwargs): - rv['passthrough_errors'] = kwargs.get('passthrough_errors') + rv["passthrough_errors"] = kwargs.get("passthrough_errors") - monkeypatch.setattr(werkzeug.serving, 'run_simple', run_simple_mock) - app.config['PROPAGATE_EXCEPTIONS'] = propagate_exceptions + monkeypatch.setattr(werkzeug.serving, "run_simple", run_simple_mock) + app.config["PROPAGATE_EXCEPTIONS"] = propagate_exceptions app.run(debug=debug, use_debugger=use_debugger, use_reloader=use_reloader) def test_max_content_length(app, client): - app.config['MAX_CONTENT_LENGTH'] = 64 + app.config["MAX_CONTENT_LENGTH"] = 64 @app.before_request def always_first(): - flask.request.form['myfile'] + flask.request.form["myfile"] assert False - @app.route('/accept', methods=['POST']) + @app.route("/accept", methods=["POST"]) def accept_file(): - flask.request.form['myfile'] + flask.request.form["myfile"] assert False @app.errorhandler(413) def catcher(error): - return '42' + return "42" - rv = client.post('/accept', data={'myfile': 'foo' * 100}) - assert rv.data == b'42' + rv = client.post("/accept", data={"myfile": "foo" * 100}) + assert rv.data == b"42" def test_url_processors(app, client): - @app.url_defaults def add_language_code(endpoint, values): - if flask.g.lang_code is not None and \ - app.url_map.is_endpoint_expecting(endpoint, 'lang_code'): - values.setdefault('lang_code', flask.g.lang_code) + if flask.g.lang_code is not None and app.url_map.is_endpoint_expecting( + endpoint, "lang_code" + ): + values.setdefault("lang_code", flask.g.lang_code) @app.url_value_preprocessor def pull_lang_code(endpoint, values): - flask.g.lang_code = values.pop('lang_code', None) + flask.g.lang_code = values.pop("lang_code", None) - @app.route('//') + @app.route("//") def index(): - return flask.url_for('about') + return flask.url_for("about") - @app.route('//about') + @app.route("//about") def about(): - return flask.url_for('something_else') + return flask.url_for("something_else") - @app.route('/foo') + @app.route("/foo") def something_else(): - return flask.url_for('about', lang_code='en') + return flask.url_for("about", lang_code="en") - assert client.get('/de/').data == b'/de/about' - assert client.get('/de/about').data == b'/foo' - assert client.get('/foo').data == b'/en/about' + assert client.get("/de/").data == b"/de/about" + assert client.get("/de/about").data == b"/foo" + assert client.get("/foo").data == b"/en/about" def test_inject_blueprint_url_defaults(app): - bp = flask.Blueprint('foo.bar.baz', __name__, - template_folder='template') + bp = flask.Blueprint("foo.bar.baz", __name__, template_folder="template") @bp.url_defaults def bp_defaults(endpoint, values): - values['page'] = 'login' + values["page"] = "login" - @bp.route('/') + @bp.route("/") def view(page): pass app.register_blueprint(bp) values = dict() - app.inject_url_defaults('foo.bar.baz.view', values) - expected = dict(page='login') + app.inject_url_defaults("foo.bar.baz.view", values) + expected = dict(page="login") assert values == expected - with app.test_request_context('/somepage'): - url = flask.url_for('foo.bar.baz.view') - expected = '/login' + with app.test_request_context("/somepage"): + url = flask.url_for("foo.bar.baz.view") + expected = "/login" assert url == expected def test_nonascii_pathinfo(app, client): - @app.route(u'/киртест') + @app.route(u"/киртест") def index(): - return 'Hello World!' + return "Hello World!" - rv = client.get(u'/киртест') - assert rv.data == b'Hello World!' + rv = client.get(u"/киртест") + assert rv.data == b"Hello World!" def test_debug_mode_complains_after_first_request(app, client): app.debug = True - @app.route('/') + @app.route("/") def index(): - return 'Awesome' + return "Awesome" assert not app.got_first_request - assert client.get('/').data == b'Awesome' + assert client.get("/").data == b"Awesome" with pytest.raises(AssertionError) as e: - @app.route('/foo') + + @app.route("/foo") def broken(): - return 'Meh' - assert 'A setup function was called' in str(e) + return "Meh" + + assert "A setup function was called" in str(e) app.debug = False - @app.route('/foo') + @app.route("/foo") def working(): - return 'Meh' + return "Meh" - assert client.get('/foo').data == b'Meh' + assert client.get("/foo").data == b"Meh" assert app.got_first_request @@ -1657,9 +1637,9 @@ def test_before_first_request_functions(app, client): def foo(): got.append(42) - client.get('/') + client.get("/") assert got == [42] - client.get('/') + client.get("/") assert got == [42] assert app.got_first_request @@ -1686,61 +1666,60 @@ def test_before_first_request_functions_concurrent(app, client): def test_routing_redirect_debugging(app, client): app.debug = True - @app.route('/foo/', methods=['GET', 'POST']) + @app.route("/foo/", methods=["GET", "POST"]) def foo(): - return 'success' + return "success" with client: with pytest.raises(AssertionError) as e: - client.post('/foo', data={}) - assert 'http://localhost/foo/' in str(e) - assert ('Make sure to directly send ' - 'your POST-request to this URL') in str(e) + client.post("/foo", data={}) + assert "http://localhost/foo/" in str(e) + assert ("Make sure to directly send " "your POST-request to this URL") in str(e) - rv = client.get('/foo', data={}, follow_redirects=True) - assert rv.data == b'success' + rv = client.get("/foo", data={}, follow_redirects=True) + assert rv.data == b"success" app.debug = False with client: - rv = client.post('/foo', data={}, follow_redirects=True) - assert rv.data == b'success' + rv = client.post("/foo", data={}, follow_redirects=True) + assert rv.data == b"success" def test_route_decorator_custom_endpoint(app, client): app.debug = True - @app.route('/foo/') + @app.route("/foo/") def foo(): return flask.request.endpoint - @app.route('/bar/', endpoint='bar') + @app.route("/bar/", endpoint="bar") def for_bar(): return flask.request.endpoint - @app.route('/bar/123', endpoint='123') + @app.route("/bar/123", endpoint="123") def for_bar_foo(): return flask.request.endpoint with app.test_request_context(): - assert flask.url_for('foo') == '/foo/' - assert flask.url_for('bar') == '/bar/' - assert flask.url_for('123') == '/bar/123' + assert flask.url_for("foo") == "/foo/" + assert flask.url_for("bar") == "/bar/" + assert flask.url_for("123") == "/bar/123" - assert client.get('/foo/').data == b'foo' - assert client.get('/bar/').data == b'bar' - assert client.get('/bar/123').data == b'123' + assert client.get("/foo/").data == b"foo" + assert client.get("/bar/").data == b"bar" + assert client.get("/bar/123").data == b"123" def test_preserve_only_once(app, client): app.debug = True - @app.route('/fail') + @app.route("/fail") def fail_func(): 1 // 0 for x in range(3): with pytest.raises(ZeroDivisionError): - client.get('/fail') + client.get("/fail") assert flask._request_ctx_stack.top is not None assert flask._app_ctx_stack.top is not None @@ -1754,13 +1733,13 @@ def test_preserve_remembers_exception(app, client): app.debug = True errors = [] - @app.route('/fail') + @app.route("/fail") def fail_func(): 1 // 0 - @app.route('/success') + @app.route("/success") def success_func(): - return 'Okay' + return "Okay" @app.teardown_request def teardown_handler(exc): @@ -1768,127 +1747,127 @@ def test_preserve_remembers_exception(app, client): # After this failure we did not yet call the teardown handler with pytest.raises(ZeroDivisionError): - client.get('/fail') + client.get("/fail") assert errors == [] # But this request triggers it, and it's an error - client.get('/success') + client.get("/success") assert len(errors) == 2 assert isinstance(errors[0], ZeroDivisionError) # At this point another request does nothing. - client.get('/success') + client.get("/success") assert len(errors) == 3 assert errors[1] is None def test_get_method_on_g(app_ctx): - assert flask.g.get('x') is None - assert flask.g.get('x', 11) == 11 + assert flask.g.get("x") is None + assert flask.g.get("x", 11) == 11 flask.g.x = 42 - assert flask.g.get('x') == 42 + assert flask.g.get("x") == 42 assert flask.g.x == 42 def test_g_iteration_protocol(app_ctx): flask.g.foo = 23 flask.g.bar = 42 - assert 'foo' in flask.g - assert 'foos' not in flask.g - assert sorted(flask.g) == ['bar', 'foo'] + assert "foo" in flask.g + assert "foos" not in flask.g + assert sorted(flask.g) == ["bar", "foo"] def test_subdomain_basic_support(): app = flask.Flask(__name__, subdomain_matching=True) - app.config['SERVER_NAME'] = 'localhost.localdomain' + app.config["SERVER_NAME"] = "localhost.localdomain" client = app.test_client() - @app.route('/') + @app.route("/") def normal_index(): - return 'normal index' + return "normal index" - @app.route('/', subdomain='test') + @app.route("/", subdomain="test") def test_index(): - return 'test index' + return "test index" - rv = client.get('/', 'http://localhost.localdomain/') - assert rv.data == b'normal index' + rv = client.get("/", "http://localhost.localdomain/") + assert rv.data == b"normal index" - rv = client.get('/', 'http://test.localhost.localdomain/') - assert rv.data == b'test index' + rv = client.get("/", "http://test.localhost.localdomain/") + assert rv.data == b"test index" def test_subdomain_matching(): app = flask.Flask(__name__, subdomain_matching=True) client = app.test_client() - app.config['SERVER_NAME'] = 'localhost.localdomain' + app.config["SERVER_NAME"] = "localhost.localdomain" - @app.route('/', subdomain='') + @app.route("/", subdomain="") def index(user): - return 'index for %s' % user + return "index for %s" % user - rv = client.get('/', 'http://mitsuhiko.localhost.localdomain/') - assert rv.data == b'index for mitsuhiko' + rv = client.get("/", "http://mitsuhiko.localhost.localdomain/") + assert rv.data == b"index for mitsuhiko" def test_subdomain_matching_with_ports(): app = flask.Flask(__name__, subdomain_matching=True) - app.config['SERVER_NAME'] = 'localhost.localdomain:3000' + app.config["SERVER_NAME"] = "localhost.localdomain:3000" client = app.test_client() - @app.route('/', subdomain='') + @app.route("/", subdomain="") def index(user): - return 'index for %s' % user + return "index for %s" % user - rv = client.get('/', 'http://mitsuhiko.localhost.localdomain:3000/') - assert rv.data == b'index for mitsuhiko' + rv = client.get("/", "http://mitsuhiko.localhost.localdomain:3000/") + assert rv.data == b"index for mitsuhiko" -@pytest.mark.parametrize('matching', (False, True)) +@pytest.mark.parametrize("matching", (False, True)) def test_subdomain_matching_other_name(matching): app = flask.Flask(__name__, subdomain_matching=matching) - app.config['SERVER_NAME'] = 'localhost.localdomain:3000' + app.config["SERVER_NAME"] = "localhost.localdomain:3000" client = app.test_client() - @app.route('/') + @app.route("/") def index(): - return '', 204 + return "", 204 # ip address can't match name - rv = client.get('/', 'http://127.0.0.1:3000/') + rv = client.get("/", "http://127.0.0.1:3000/") assert rv.status_code == 404 if matching else 204 # allow all subdomains if matching is disabled - rv = client.get('/', 'http://www.localhost.localdomain:3000/') + rv = client.get("/", "http://www.localhost.localdomain:3000/") assert rv.status_code == 404 if matching else 204 def test_multi_route_rules(app, client): - @app.route('/') - @app.route('//') - def index(test='a'): + @app.route("/") + @app.route("//") + def index(test="a"): return test - rv = client.open('/') - assert rv.data == b'a' - rv = client.open('/b/') - assert rv.data == b'b' + rv = client.open("/") + assert rv.data == b"a" + rv = client.open("/b/") + assert rv.data == b"b" def test_multi_route_class_views(app, client): class View(object): def __init__(self, app): - app.add_url_rule('/', 'index', self.index) - app.add_url_rule('//', 'index', self.index) + app.add_url_rule("/", "index", self.index) + app.add_url_rule("//", "index", self.index) - def index(self, test='a'): + def index(self, test="a"): return test _ = View(app) - rv = client.open('/') - assert rv.data == b'a' - rv = client.open('/b/') - assert rv.data == b'b' + rv = client.open("/") + assert rv.data == b"a" + rv = client.open("/b/") + assert rv.data == b"b" def test_run_defaults(monkeypatch, app): @@ -1896,11 +1875,11 @@ def test_run_defaults(monkeypatch, app): # Mocks werkzeug.serving.run_simple method def run_simple_mock(*args, **kwargs): - rv['result'] = 'running...' + rv["result"] = "running..." - monkeypatch.setattr(werkzeug.serving, 'run_simple', run_simple_mock) + monkeypatch.setattr(werkzeug.serving, "run_simple", run_simple_mock) app.run() - assert rv['result'] == 'running...' + assert rv["result"] == "running..." def test_run_server_port(monkeypatch, app): @@ -1908,55 +1887,58 @@ def test_run_server_port(monkeypatch, app): # Mocks werkzeug.serving.run_simple method def run_simple_mock(hostname, port, application, *args, **kwargs): - rv['result'] = 'running on %s:%s ...' % (hostname, port) + rv["result"] = "running on %s:%s ..." % (hostname, port) - monkeypatch.setattr(werkzeug.serving, 'run_simple', run_simple_mock) - hostname, port = 'localhost', 8000 + monkeypatch.setattr(werkzeug.serving, "run_simple", run_simple_mock) + hostname, port = "localhost", 8000 app.run(hostname, port, debug=True) - assert rv['result'] == 'running on %s:%s ...' % (hostname, port) + assert rv["result"] == "running on %s:%s ..." % (hostname, port) -@pytest.mark.parametrize('host,port,expect_host,expect_port', ( - (None, None, 'pocoo.org', 8080), - ('localhost', None, 'localhost', 8080), - (None, 80, 'pocoo.org', 80), - ('localhost', 80, 'localhost', 80), -)) +@pytest.mark.parametrize( + "host,port,expect_host,expect_port", + ( + (None, None, "pocoo.org", 8080), + ("localhost", None, "localhost", 8080), + (None, 80, "pocoo.org", 80), + ("localhost", 80, "localhost", 80), + ), +) def test_run_from_config(monkeypatch, host, port, expect_host, expect_port, app): def run_simple_mock(hostname, port, *args, **kwargs): assert hostname == expect_host assert port == expect_port - monkeypatch.setattr(werkzeug.serving, 'run_simple', run_simple_mock) - app.config['SERVER_NAME'] = 'pocoo.org:8080' + monkeypatch.setattr(werkzeug.serving, "run_simple", run_simple_mock) + app.config["SERVER_NAME"] = "pocoo.org:8080" app.run(host, port) def test_max_cookie_size(app, client, recwarn): - app.config['MAX_COOKIE_SIZE'] = 100 + app.config["MAX_COOKIE_SIZE"] = 100 # outside app context, default to Werkzeug static value, # which is also the default config response = flask.Response() - default = flask.Flask.default_config['MAX_COOKIE_SIZE'] + default = flask.Flask.default_config["MAX_COOKIE_SIZE"] assert response.max_cookie_size == default # inside app context, use app config with app.app_context(): assert flask.Response().max_cookie_size == 100 - @app.route('/') + @app.route("/") def index(): - r = flask.Response('', status=204) - r.set_cookie('foo', 'bar' * 100) + r = flask.Response("", status=204) + r.set_cookie("foo", "bar" * 100) return r - client.get('/') + client.get("/") assert len(recwarn) == 1 w = recwarn.pop() - assert 'cookie is too large' in str(w.message) + assert "cookie is too large" in str(w.message) - app.config['MAX_COOKIE_SIZE'] = 0 + app.config["MAX_COOKIE_SIZE"] = 0 - client.get('/') + client.get("/") assert len(recwarn) == 0 diff --git a/tests/test_blueprints.py b/tests/test_blueprints.py index 465079cd..29d5375f 100644 --- a/tests/test_blueprints.py +++ b/tests/test_blueprints.py @@ -20,27 +20,27 @@ from jinja2 import TemplateNotFound def test_blueprint_specific_error_handling(app, client): - frontend = flask.Blueprint('frontend', __name__) - backend = flask.Blueprint('backend', __name__) - sideend = flask.Blueprint('sideend', __name__) + frontend = flask.Blueprint("frontend", __name__) + backend = flask.Blueprint("backend", __name__) + sideend = flask.Blueprint("sideend", __name__) @frontend.errorhandler(403) def frontend_forbidden(e): - return 'frontend says no', 403 + return "frontend says no", 403 - @frontend.route('/frontend-no') + @frontend.route("/frontend-no") def frontend_no(): flask.abort(403) @backend.errorhandler(403) def backend_forbidden(e): - return 'backend says no', 403 + return "backend says no", 403 - @backend.route('/backend-no') + @backend.route("/backend-no") def backend_no(): flask.abort(403) - @sideend.route('/what-is-a-sideend') + @sideend.route("/what-is-a-sideend") def sideend_no(): flask.abort(403) @@ -50,11 +50,11 @@ def test_blueprint_specific_error_handling(app, client): @app.errorhandler(403) def app_forbidden(e): - return 'application itself says no', 403 + return "application itself says no", 403 - assert client.get('/frontend-no').data == b'frontend says no' - assert client.get('/backend-no').data == b'backend says no' - assert client.get('/what-is-a-sideend').data == b'application itself says no' + assert client.get("/frontend-no").data == b"frontend says no" + assert client.get("/backend-no").data == b"backend says no" + assert client.get("/what-is-a-sideend").data == b"application itself says no" def test_blueprint_specific_user_error_handling(app, client): @@ -64,167 +64,174 @@ def test_blueprint_specific_user_error_handling(app, client): class MyFunctionException(Exception): pass - blue = flask.Blueprint('blue', __name__) + blue = flask.Blueprint("blue", __name__) @blue.errorhandler(MyDecoratorException) def my_decorator_exception_handler(e): assert isinstance(e, MyDecoratorException) - return 'boom' + return "boom" def my_function_exception_handler(e): assert isinstance(e, MyFunctionException) - return 'bam' + return "bam" blue.register_error_handler(MyFunctionException, my_function_exception_handler) - @blue.route('/decorator') + @blue.route("/decorator") def blue_deco_test(): raise MyDecoratorException() - @blue.route('/function') + @blue.route("/function") def blue_func_test(): raise MyFunctionException() app.register_blueprint(blue) - assert client.get('/decorator').data == b'boom' - assert client.get('/function').data == b'bam' + assert client.get("/decorator").data == b"boom" + assert client.get("/function").data == b"bam" def test_blueprint_app_error_handling(app, client): - errors = flask.Blueprint('errors', __name__) + errors = flask.Blueprint("errors", __name__) @errors.app_errorhandler(403) def forbidden_handler(e): - return 'you shall not pass', 403 + return "you shall not pass", 403 - @app.route('/forbidden') + @app.route("/forbidden") def app_forbidden(): flask.abort(403) - forbidden_bp = flask.Blueprint('forbidden_bp', __name__) + forbidden_bp = flask.Blueprint("forbidden_bp", __name__) - @forbidden_bp.route('/nope') + @forbidden_bp.route("/nope") def bp_forbidden(): flask.abort(403) app.register_blueprint(errors) app.register_blueprint(forbidden_bp) - assert client.get('/forbidden').data == b'you shall not pass' - assert client.get('/nope').data == b'you shall not pass' + assert client.get("/forbidden").data == b"you shall not pass" + assert client.get("/nope").data == b"you shall not pass" -@pytest.mark.parametrize(('prefix', 'rule', 'url'), ( - ('', '/', '/'), - ('/', '', '/'), - ('/', '/', '/'), - ('/foo', '', '/foo'), - ('/foo/', '', '/foo/'), - ('', '/bar', '/bar'), - ('/foo/', '/bar', '/foo/bar'), - ('/foo/', 'bar', '/foo/bar'), - ('/foo', '/bar', '/foo/bar'), - ('/foo/', '//bar', '/foo/bar'), - ('/foo//', '/bar', '/foo/bar'), -)) +@pytest.mark.parametrize( + ("prefix", "rule", "url"), + ( + ("", "/", "/"), + ("/", "", "/"), + ("/", "/", "/"), + ("/foo", "", "/foo"), + ("/foo/", "", "/foo/"), + ("", "/bar", "/bar"), + ("/foo/", "/bar", "/foo/bar"), + ("/foo/", "bar", "/foo/bar"), + ("/foo", "/bar", "/foo/bar"), + ("/foo/", "//bar", "/foo/bar"), + ("/foo//", "/bar", "/foo/bar"), + ), +) def test_blueprint_prefix_slash(app, client, prefix, rule, url): - bp = flask.Blueprint('test', __name__, url_prefix=prefix) + bp = flask.Blueprint("test", __name__, url_prefix=prefix) @bp.route(rule) def index(): - return '', 204 + return "", 204 app.register_blueprint(bp) assert client.get(url).status_code == 204 def test_blueprint_url_defaults(app, client): - bp = flask.Blueprint('test', __name__) + bp = flask.Blueprint("test", __name__) - @bp.route('/foo', defaults={'baz': 42}) + @bp.route("/foo", defaults={"baz": 42}) def foo(bar, baz): - return '%s/%d' % (bar, baz) + return "%s/%d" % (bar, baz) - @bp.route('/bar') + @bp.route("/bar") def bar(bar): return text_type(bar) - app.register_blueprint(bp, url_prefix='/1', url_defaults={'bar': 23}) - app.register_blueprint(bp, url_prefix='/2', url_defaults={'bar': 19}) + app.register_blueprint(bp, url_prefix="/1", url_defaults={"bar": 23}) + app.register_blueprint(bp, url_prefix="/2", url_defaults={"bar": 19}) - assert client.get('/1/foo').data == b'23/42' - assert client.get('/2/foo').data == b'19/42' - assert client.get('/1/bar').data == b'23' - assert client.get('/2/bar').data == b'19' + assert client.get("/1/foo").data == b"23/42" + assert client.get("/2/foo").data == b"19/42" + assert client.get("/1/bar").data == b"23" + assert client.get("/2/bar").data == b"19" def test_blueprint_url_processors(app, client): - bp = flask.Blueprint('frontend', __name__, url_prefix='/') + bp = flask.Blueprint("frontend", __name__, url_prefix="/") @bp.url_defaults def add_language_code(endpoint, values): - values.setdefault('lang_code', flask.g.lang_code) + values.setdefault("lang_code", flask.g.lang_code) @bp.url_value_preprocessor def pull_lang_code(endpoint, values): - flask.g.lang_code = values.pop('lang_code') + flask.g.lang_code = values.pop("lang_code") - @bp.route('/') + @bp.route("/") def index(): - return flask.url_for('.about') + return flask.url_for(".about") - @bp.route('/about') + @bp.route("/about") def about(): - return flask.url_for('.index') + return flask.url_for(".index") app.register_blueprint(bp) - assert client.get('/de/').data == b'/de/about' - assert client.get('/de/about').data == b'/de/' + assert client.get("/de/").data == b"/de/about" + assert client.get("/de/about").data == b"/de/" def test_templates_and_static(test_apps): from blueprintapp import app + client = app.test_client() - rv = client.get('/') - assert rv.data == b'Hello from the Frontend' - rv = client.get('/admin/') - assert rv.data == b'Hello from the Admin' - rv = client.get('/admin/index2') - assert rv.data == b'Hello from the Admin' - rv = client.get('/admin/static/test.txt') - assert rv.data.strip() == b'Admin File' + rv = client.get("/") + assert rv.data == b"Hello from the Frontend" + rv = client.get("/admin/") + assert rv.data == b"Hello from the Admin" + rv = client.get("/admin/index2") + assert rv.data == b"Hello from the Admin" + rv = client.get("/admin/static/test.txt") + assert rv.data.strip() == b"Admin File" rv.close() - rv = client.get('/admin/static/css/test.css') - assert rv.data.strip() == b'/* nested file */' + rv = client.get("/admin/static/css/test.css") + assert rv.data.strip() == b"/* nested file */" rv.close() # try/finally, in case other tests use this app for Blueprint tests. - max_age_default = app.config['SEND_FILE_MAX_AGE_DEFAULT'] + max_age_default = app.config["SEND_FILE_MAX_AGE_DEFAULT"] try: expected_max_age = 3600 - if app.config['SEND_FILE_MAX_AGE_DEFAULT'] == expected_max_age: + if app.config["SEND_FILE_MAX_AGE_DEFAULT"] == expected_max_age: expected_max_age = 7200 - app.config['SEND_FILE_MAX_AGE_DEFAULT'] = expected_max_age - rv = client.get('/admin/static/css/test.css') - cc = parse_cache_control_header(rv.headers['Cache-Control']) + app.config["SEND_FILE_MAX_AGE_DEFAULT"] = expected_max_age + rv = client.get("/admin/static/css/test.css") + cc = parse_cache_control_header(rv.headers["Cache-Control"]) assert cc.max_age == expected_max_age rv.close() finally: - app.config['SEND_FILE_MAX_AGE_DEFAULT'] = max_age_default + app.config["SEND_FILE_MAX_AGE_DEFAULT"] = max_age_default with app.test_request_context(): - assert flask.url_for('admin.static', filename='test.txt') == '/admin/static/test.txt' + assert ( + flask.url_for("admin.static", filename="test.txt") + == "/admin/static/test.txt" + ) with app.test_request_context(): with pytest.raises(TemplateNotFound) as e: - flask.render_template('missing.html') - assert e.value.name == 'missing.html' + flask.render_template("missing.html") + assert e.value.name == "missing.html" with flask.Flask(__name__).test_request_context(): - assert flask.render_template('nested/nested.txt') == 'I\'m nested' + assert flask.render_template("nested/nested.txt") == "I'm nested" def test_default_static_cache_timeout(app): @@ -232,625 +239,626 @@ def test_default_static_cache_timeout(app): def get_send_file_max_age(self, filename): return 100 - blueprint = MyBlueprint('blueprint', __name__, static_folder='static') + blueprint = MyBlueprint("blueprint", __name__, static_folder="static") app.register_blueprint(blueprint) # try/finally, in case other tests use this app for Blueprint tests. - max_age_default = app.config['SEND_FILE_MAX_AGE_DEFAULT'] + max_age_default = app.config["SEND_FILE_MAX_AGE_DEFAULT"] try: with app.test_request_context(): unexpected_max_age = 3600 - if app.config['SEND_FILE_MAX_AGE_DEFAULT'] == unexpected_max_age: + if app.config["SEND_FILE_MAX_AGE_DEFAULT"] == unexpected_max_age: unexpected_max_age = 7200 - app.config['SEND_FILE_MAX_AGE_DEFAULT'] = unexpected_max_age - rv = blueprint.send_static_file('index.html') - cc = parse_cache_control_header(rv.headers['Cache-Control']) + app.config["SEND_FILE_MAX_AGE_DEFAULT"] = unexpected_max_age + rv = blueprint.send_static_file("index.html") + cc = parse_cache_control_header(rv.headers["Cache-Control"]) assert cc.max_age == 100 rv.close() finally: - app.config['SEND_FILE_MAX_AGE_DEFAULT'] = max_age_default + app.config["SEND_FILE_MAX_AGE_DEFAULT"] = max_age_default def test_templates_list(test_apps): from blueprintapp import app + templates = sorted(app.jinja_env.list_templates()) - assert templates == ['admin/index.html', 'frontend/index.html'] + assert templates == ["admin/index.html", "frontend/index.html"] def test_dotted_names(app, client): - frontend = flask.Blueprint('myapp.frontend', __name__) - backend = flask.Blueprint('myapp.backend', __name__) + frontend = flask.Blueprint("myapp.frontend", __name__) + backend = flask.Blueprint("myapp.backend", __name__) - @frontend.route('/fe') + @frontend.route("/fe") def frontend_index(): - return flask.url_for('myapp.backend.backend_index') + return flask.url_for("myapp.backend.backend_index") - @frontend.route('/fe2') + @frontend.route("/fe2") def frontend_page2(): - return flask.url_for('.frontend_index') + return flask.url_for(".frontend_index") - @backend.route('/be') + @backend.route("/be") def backend_index(): - return flask.url_for('myapp.frontend.frontend_index') + return flask.url_for("myapp.frontend.frontend_index") app.register_blueprint(frontend) app.register_blueprint(backend) - assert client.get('/fe').data.strip() == b'/be' - assert client.get('/fe2').data.strip() == b'/fe' - assert client.get('/be').data.strip() == b'/fe' + assert client.get("/fe").data.strip() == b"/be" + assert client.get("/fe2").data.strip() == b"/fe" + assert client.get("/be").data.strip() == b"/fe" def test_dotted_names_from_app(app, client): - test = flask.Blueprint('test', __name__) + test = flask.Blueprint("test", __name__) - @app.route('/') + @app.route("/") def app_index(): - return flask.url_for('test.index') + return flask.url_for("test.index") - @test.route('/test/') + @test.route("/test/") def index(): - return flask.url_for('app_index') + return flask.url_for("app_index") app.register_blueprint(test) - rv = client.get('/') - assert rv.data == b'/test/' + rv = client.get("/") + assert rv.data == b"/test/" def test_empty_url_defaults(app, client): - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) - @bp.route('/', defaults={'page': 1}) - @bp.route('/page/') + @bp.route("/", defaults={"page": 1}) + @bp.route("/page/") def something(page): return str(page) app.register_blueprint(bp) - assert client.get('/').data == b'1' - assert client.get('/page/2').data == b'2' + assert client.get("/").data == b"1" + assert client.get("/page/2").data == b"2" def test_route_decorator_custom_endpoint(app, client): - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) - @bp.route('/foo') + @bp.route("/foo") def foo(): return flask.request.endpoint - @bp.route('/bar', endpoint='bar') + @bp.route("/bar", endpoint="bar") def foo_bar(): return flask.request.endpoint - @bp.route('/bar/123', endpoint='123') + @bp.route("/bar/123", endpoint="123") def foo_bar_foo(): return flask.request.endpoint - @bp.route('/bar/foo') + @bp.route("/bar/foo") def bar_foo(): return flask.request.endpoint - app.register_blueprint(bp, url_prefix='/py') + app.register_blueprint(bp, url_prefix="/py") - @app.route('/') + @app.route("/") def index(): return flask.request.endpoint - assert client.get('/').data == b'index' - assert client.get('/py/foo').data == b'bp.foo' - assert client.get('/py/bar').data == b'bp.bar' - assert client.get('/py/bar/123').data == b'bp.123' - assert client.get('/py/bar/foo').data == b'bp.bar_foo' + assert client.get("/").data == b"index" + assert client.get("/py/foo").data == b"bp.foo" + assert client.get("/py/bar").data == b"bp.bar" + assert client.get("/py/bar/123").data == b"bp.123" + assert client.get("/py/bar/foo").data == b"bp.bar_foo" def test_route_decorator_custom_endpoint_with_dots(app, client): - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) - @bp.route('/foo') + @bp.route("/foo") def foo(): return flask.request.endpoint try: - @bp.route('/bar', endpoint='bar.bar') + + @bp.route("/bar", endpoint="bar.bar") def foo_bar(): return flask.request.endpoint + except AssertionError: pass else: - raise AssertionError('expected AssertionError not raised') + raise AssertionError("expected AssertionError not raised") try: - @bp.route('/bar/123', endpoint='bar.123') + + @bp.route("/bar/123", endpoint="bar.123") def foo_bar_foo(): return flask.request.endpoint + except AssertionError: pass else: - raise AssertionError('expected AssertionError not raised') + raise AssertionError("expected AssertionError not raised") def foo_foo_foo(): pass pytest.raises( AssertionError, - lambda: bp.add_url_rule( - '/bar/123', endpoint='bar.123', view_func=foo_foo_foo - ) + lambda: bp.add_url_rule("/bar/123", endpoint="bar.123", view_func=foo_foo_foo), ) pytest.raises( - AssertionError, - bp.route('/bar/123', endpoint='bar.123'), - lambda: None + AssertionError, bp.route("/bar/123", endpoint="bar.123"), lambda: None ) - foo_foo_foo.__name__ = 'bar.123' + foo_foo_foo.__name__ = "bar.123" pytest.raises( - AssertionError, - lambda: bp.add_url_rule( - '/bar/123', view_func=foo_foo_foo - ) + AssertionError, lambda: bp.add_url_rule("/bar/123", view_func=foo_foo_foo) ) - bp.add_url_rule('/bar/456', endpoint='foofoofoo', view_func=functools.partial(foo_foo_foo)) + bp.add_url_rule( + "/bar/456", endpoint="foofoofoo", view_func=functools.partial(foo_foo_foo) + ) - app.register_blueprint(bp, url_prefix='/py') + app.register_blueprint(bp, url_prefix="/py") - assert client.get('/py/foo').data == b'bp.foo' + assert client.get("/py/foo").data == b"bp.foo" # The rule's didn't actually made it through - rv = client.get('/py/bar') + rv = client.get("/py/bar") assert rv.status_code == 404 - rv = client.get('/py/bar/123') + rv = client.get("/py/bar/123") assert rv.status_code == 404 def test_endpoint_decorator(app, client): from werkzeug.routing import Rule - app.url_map.add(Rule('/foo', endpoint='bar')) - bp = flask.Blueprint('bp', __name__) + app.url_map.add(Rule("/foo", endpoint="bar")) - @bp.endpoint('bar') + bp = flask.Blueprint("bp", __name__) + + @bp.endpoint("bar") def foobar(): return flask.request.endpoint - app.register_blueprint(bp, url_prefix='/bp_prefix') + app.register_blueprint(bp, url_prefix="/bp_prefix") - assert client.get('/foo').data == b'bar' - assert client.get('/bp_prefix/bar').status_code == 404 + assert client.get("/foo").data == b"bar" + assert client.get("/bp_prefix/bar").status_code == 404 def test_template_filter(app): - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) @bp.app_template_filter() def my_reverse(s): return s[::-1] - app.register_blueprint(bp, url_prefix='/py') - assert 'my_reverse' in app.jinja_env.filters.keys() - assert app.jinja_env.filters['my_reverse'] == my_reverse - assert app.jinja_env.filters['my_reverse']('abcd') == 'dcba' + app.register_blueprint(bp, url_prefix="/py") + assert "my_reverse" in app.jinja_env.filters.keys() + assert app.jinja_env.filters["my_reverse"] == my_reverse + assert app.jinja_env.filters["my_reverse"]("abcd") == "dcba" def test_add_template_filter(app): - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) def my_reverse(s): return s[::-1] bp.add_app_template_filter(my_reverse) - app.register_blueprint(bp, url_prefix='/py') - assert 'my_reverse' in app.jinja_env.filters.keys() - assert app.jinja_env.filters['my_reverse'] == my_reverse - assert app.jinja_env.filters['my_reverse']('abcd') == 'dcba' + app.register_blueprint(bp, url_prefix="/py") + assert "my_reverse" in app.jinja_env.filters.keys() + assert app.jinja_env.filters["my_reverse"] == my_reverse + assert app.jinja_env.filters["my_reverse"]("abcd") == "dcba" def test_template_filter_with_name(app): - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) - @bp.app_template_filter('strrev') + @bp.app_template_filter("strrev") def my_reverse(s): return s[::-1] - app.register_blueprint(bp, url_prefix='/py') - assert 'strrev' in app.jinja_env.filters.keys() - assert app.jinja_env.filters['strrev'] == my_reverse - assert app.jinja_env.filters['strrev']('abcd') == 'dcba' + app.register_blueprint(bp, url_prefix="/py") + assert "strrev" in app.jinja_env.filters.keys() + assert app.jinja_env.filters["strrev"] == my_reverse + assert app.jinja_env.filters["strrev"]("abcd") == "dcba" def test_add_template_filter_with_name(app): - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) def my_reverse(s): return s[::-1] - bp.add_app_template_filter(my_reverse, 'strrev') - app.register_blueprint(bp, url_prefix='/py') - assert 'strrev' in app.jinja_env.filters.keys() - assert app.jinja_env.filters['strrev'] == my_reverse - assert app.jinja_env.filters['strrev']('abcd') == 'dcba' + bp.add_app_template_filter(my_reverse, "strrev") + app.register_blueprint(bp, url_prefix="/py") + assert "strrev" in app.jinja_env.filters.keys() + assert app.jinja_env.filters["strrev"] == my_reverse + assert app.jinja_env.filters["strrev"]("abcd") == "dcba" def test_template_filter_with_template(app, client): - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) @bp.app_template_filter() def super_reverse(s): return s[::-1] - app.register_blueprint(bp, url_prefix='/py') + app.register_blueprint(bp, url_prefix="/py") - @app.route('/') + @app.route("/") def index(): - return flask.render_template('template_filter.html', value='abcd') + return flask.render_template("template_filter.html", value="abcd") - rv = client.get('/') - assert rv.data == b'dcba' + rv = client.get("/") + assert rv.data == b"dcba" def test_template_filter_after_route_with_template(app, client): - @app.route('/') + @app.route("/") def index(): - return flask.render_template('template_filter.html', value='abcd') + return flask.render_template("template_filter.html", value="abcd") - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) @bp.app_template_filter() def super_reverse(s): return s[::-1] - app.register_blueprint(bp, url_prefix='/py') - rv = client.get('/') - assert rv.data == b'dcba' + app.register_blueprint(bp, url_prefix="/py") + rv = client.get("/") + assert rv.data == b"dcba" def test_add_template_filter_with_template(app, client): - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) def super_reverse(s): return s[::-1] bp.add_app_template_filter(super_reverse) - app.register_blueprint(bp, url_prefix='/py') + app.register_blueprint(bp, url_prefix="/py") - @app.route('/') + @app.route("/") def index(): - return flask.render_template('template_filter.html', value='abcd') + return flask.render_template("template_filter.html", value="abcd") - rv = client.get('/') - assert rv.data == b'dcba' + rv = client.get("/") + assert rv.data == b"dcba" def test_template_filter_with_name_and_template(app, client): - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) - @bp.app_template_filter('super_reverse') + @bp.app_template_filter("super_reverse") def my_reverse(s): return s[::-1] - app.register_blueprint(bp, url_prefix='/py') + app.register_blueprint(bp, url_prefix="/py") - @app.route('/') + @app.route("/") def index(): - return flask.render_template('template_filter.html', value='abcd') + return flask.render_template("template_filter.html", value="abcd") - rv = client.get('/') - assert rv.data == b'dcba' + rv = client.get("/") + assert rv.data == b"dcba" def test_add_template_filter_with_name_and_template(app, client): - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) def my_reverse(s): return s[::-1] - bp.add_app_template_filter(my_reverse, 'super_reverse') - app.register_blueprint(bp, url_prefix='/py') + bp.add_app_template_filter(my_reverse, "super_reverse") + app.register_blueprint(bp, url_prefix="/py") - @app.route('/') + @app.route("/") def index(): - return flask.render_template('template_filter.html', value='abcd') + return flask.render_template("template_filter.html", value="abcd") - rv = client.get('/') - assert rv.data == b'dcba' + rv = client.get("/") + assert rv.data == b"dcba" def test_template_test(app): - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) @bp.app_template_test() def is_boolean(value): return isinstance(value, bool) - app.register_blueprint(bp, url_prefix='/py') - assert 'is_boolean' in app.jinja_env.tests.keys() - assert app.jinja_env.tests['is_boolean'] == is_boolean - assert app.jinja_env.tests['is_boolean'](False) + app.register_blueprint(bp, url_prefix="/py") + assert "is_boolean" in app.jinja_env.tests.keys() + assert app.jinja_env.tests["is_boolean"] == is_boolean + assert app.jinja_env.tests["is_boolean"](False) def test_add_template_test(app): - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) def is_boolean(value): return isinstance(value, bool) bp.add_app_template_test(is_boolean) - app.register_blueprint(bp, url_prefix='/py') - assert 'is_boolean' in app.jinja_env.tests.keys() - assert app.jinja_env.tests['is_boolean'] == is_boolean - assert app.jinja_env.tests['is_boolean'](False) + app.register_blueprint(bp, url_prefix="/py") + assert "is_boolean" in app.jinja_env.tests.keys() + assert app.jinja_env.tests["is_boolean"] == is_boolean + assert app.jinja_env.tests["is_boolean"](False) def test_template_test_with_name(app): - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) - @bp.app_template_test('boolean') + @bp.app_template_test("boolean") def is_boolean(value): return isinstance(value, bool) - app.register_blueprint(bp, url_prefix='/py') - assert 'boolean' in app.jinja_env.tests.keys() - assert app.jinja_env.tests['boolean'] == is_boolean - assert app.jinja_env.tests['boolean'](False) + app.register_blueprint(bp, url_prefix="/py") + assert "boolean" in app.jinja_env.tests.keys() + assert app.jinja_env.tests["boolean"] == is_boolean + assert app.jinja_env.tests["boolean"](False) def test_add_template_test_with_name(app): - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) def is_boolean(value): return isinstance(value, bool) - bp.add_app_template_test(is_boolean, 'boolean') - app.register_blueprint(bp, url_prefix='/py') - assert 'boolean' in app.jinja_env.tests.keys() - assert app.jinja_env.tests['boolean'] == is_boolean - assert app.jinja_env.tests['boolean'](False) + bp.add_app_template_test(is_boolean, "boolean") + app.register_blueprint(bp, url_prefix="/py") + assert "boolean" in app.jinja_env.tests.keys() + assert app.jinja_env.tests["boolean"] == is_boolean + assert app.jinja_env.tests["boolean"](False) def test_template_test_with_template(app, client): - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) @bp.app_template_test() def boolean(value): return isinstance(value, bool) - app.register_blueprint(bp, url_prefix='/py') + app.register_blueprint(bp, url_prefix="/py") - @app.route('/') + @app.route("/") def index(): - return flask.render_template('template_test.html', value=False) + return flask.render_template("template_test.html", value=False) - rv = client.get('/') - assert b'Success!' in rv.data + rv = client.get("/") + assert b"Success!" in rv.data def test_template_test_after_route_with_template(app, client): - @app.route('/') + @app.route("/") def index(): - return flask.render_template('template_test.html', value=False) + return flask.render_template("template_test.html", value=False) - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) @bp.app_template_test() def boolean(value): return isinstance(value, bool) - app.register_blueprint(bp, url_prefix='/py') - rv = client.get('/') - assert b'Success!' in rv.data + app.register_blueprint(bp, url_prefix="/py") + rv = client.get("/") + assert b"Success!" in rv.data def test_add_template_test_with_template(app, client): - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) def boolean(value): return isinstance(value, bool) bp.add_app_template_test(boolean) - app.register_blueprint(bp, url_prefix='/py') + app.register_blueprint(bp, url_prefix="/py") - @app.route('/') + @app.route("/") def index(): - return flask.render_template('template_test.html', value=False) + return flask.render_template("template_test.html", value=False) - rv = client.get('/') - assert b'Success!' in rv.data + rv = client.get("/") + assert b"Success!" in rv.data def test_template_test_with_name_and_template(app, client): - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) - @bp.app_template_test('boolean') + @bp.app_template_test("boolean") def is_boolean(value): return isinstance(value, bool) - app.register_blueprint(bp, url_prefix='/py') + app.register_blueprint(bp, url_prefix="/py") - @app.route('/') + @app.route("/") def index(): - return flask.render_template('template_test.html', value=False) + return flask.render_template("template_test.html", value=False) - rv = client.get('/') - assert b'Success!' in rv.data + rv = client.get("/") + assert b"Success!" in rv.data def test_add_template_test_with_name_and_template(app, client): - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) def is_boolean(value): return isinstance(value, bool) - bp.add_app_template_test(is_boolean, 'boolean') - app.register_blueprint(bp, url_prefix='/py') + bp.add_app_template_test(is_boolean, "boolean") + app.register_blueprint(bp, url_prefix="/py") - @app.route('/') + @app.route("/") def index(): - return flask.render_template('template_test.html', value=False) + return flask.render_template("template_test.html", value=False) - rv = client.get('/') - assert b'Success!' in rv.data + rv = client.get("/") + assert b"Success!" in rv.data def test_context_processing(app, client): - answer_bp = flask.Blueprint('answer_bp', __name__) + answer_bp = flask.Blueprint("answer_bp", __name__) template_string = lambda: flask.render_template_string( - '{% if notanswer %}{{ notanswer }} is not the answer. {% endif %}' - '{% if answer %}{{ answer }} is the answer.{% endif %}' + "{% if notanswer %}{{ notanswer }} is not the answer. {% endif %}" + "{% if answer %}{{ answer }} is the answer.{% endif %}" ) # App global context processor @answer_bp.app_context_processor def not_answer_context_processor(): - return {'notanswer': 43} + return {"notanswer": 43} # Blueprint local context processor @answer_bp.context_processor def answer_context_processor(): - return {'answer': 42} + return {"answer": 42} # Setup endpoints for testing - @answer_bp.route('/bp') + @answer_bp.route("/bp") def bp_page(): return template_string() - @app.route('/') + @app.route("/") def app_page(): return template_string() # Register the blueprint app.register_blueprint(answer_bp) - app_page_bytes = client.get('/').data - answer_page_bytes = client.get('/bp').data + app_page_bytes = client.get("/").data + answer_page_bytes = client.get("/bp").data - assert b'43' in app_page_bytes - assert b'42' not in app_page_bytes + assert b"43" in app_page_bytes + assert b"42" not in app_page_bytes - assert b'42' in answer_page_bytes - assert b'43' in answer_page_bytes + assert b"42" in answer_page_bytes + assert b"43" in answer_page_bytes def test_template_global(app): - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) @bp.app_template_global() def get_answer(): return 42 # Make sure the function is not in the jinja_env already - assert 'get_answer' not in app.jinja_env.globals.keys() + assert "get_answer" not in app.jinja_env.globals.keys() app.register_blueprint(bp) # Tests - assert 'get_answer' in app.jinja_env.globals.keys() - assert app.jinja_env.globals['get_answer'] is get_answer - assert app.jinja_env.globals['get_answer']() == 42 + assert "get_answer" in app.jinja_env.globals.keys() + assert app.jinja_env.globals["get_answer"] is get_answer + assert app.jinja_env.globals["get_answer"]() == 42 with app.app_context(): - rv = flask.render_template_string('{{ get_answer() }}') - assert rv == '42' + rv = flask.render_template_string("{{ get_answer() }}") + assert rv == "42" def test_request_processing(app, client): - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) evts = [] @bp.before_request def before_bp(): - evts.append('before') + evts.append("before") @bp.after_request def after_bp(response): - response.data += b'|after' - evts.append('after') + response.data += b"|after" + evts.append("after") return response @bp.teardown_request def teardown_bp(exc): - evts.append('teardown') + evts.append("teardown") # Setup routes for testing - @bp.route('/bp') + @bp.route("/bp") def bp_endpoint(): - return 'request' + return "request" app.register_blueprint(bp) assert evts == [] - rv = client.get('/bp') - assert rv.data == b'request|after' - assert evts == ['before', 'after', 'teardown'] + rv = client.get("/bp") + assert rv.data == b"request|after" + assert evts == ["before", "after", "teardown"] def test_app_request_processing(app, client): - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) evts = [] @bp.before_app_first_request def before_first_request(): - evts.append('first') + evts.append("first") @bp.before_app_request def before_app(): - evts.append('before') + evts.append("before") @bp.after_app_request def after_app(response): - response.data += b'|after' - evts.append('after') + response.data += b"|after" + evts.append("after") return response @bp.teardown_app_request def teardown_app(exc): - evts.append('teardown') + evts.append("teardown") app.register_blueprint(bp) # Setup routes for testing - @app.route('/') + @app.route("/") def bp_endpoint(): - return 'request' + return "request" # before first request assert evts == [] # first request - resp = client.get('/').data - assert resp == b'request|after' - assert evts == ['first', 'before', 'after', 'teardown'] + resp = client.get("/").data + assert resp == b"request|after" + assert evts == ["first", "before", "after", "teardown"] # second request - resp = client.get('/').data - assert resp == b'request|after' - assert evts == ['first'] + ['before', 'after', 'teardown'] * 2 + resp = client.get("/").data + assert resp == b"request|after" + assert evts == ["first"] + ["before", "after", "teardown"] * 2 def test_app_url_processors(app, client): - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) # Register app-wide url defaults and preprocessor on blueprint @bp.app_url_defaults def add_language_code(endpoint, values): - values.setdefault('lang_code', flask.g.lang_code) + values.setdefault("lang_code", flask.g.lang_code) @bp.app_url_value_preprocessor def pull_lang_code(endpoint, values): - flask.g.lang_code = values.pop('lang_code') + flask.g.lang_code = values.pop("lang_code") # Register route rules at the app level - @app.route('//') + @app.route("//") def index(): - return flask.url_for('about') + return flask.url_for("about") - @app.route('//about') + @app.route("//about") def about(): - return flask.url_for('index') + return flask.url_for("index") app.register_blueprint(bp) - assert client.get('/de/').data == b'/de/about' - assert client.get('/de/about').data == b'/de/' + assert client.get("/de/").data == b"/de/about" + assert client.get("/de/about").data == b"/de/" diff --git a/tests/test_cli.py b/tests/test_cli.py index 9643e244..5079cfcc 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -25,15 +25,22 @@ from click.testing import CliRunner from flask import Flask, current_app from flask.cli import ( - AppGroup, FlaskGroup, NoAppException, ScriptInfo, dotenv, find_best_app, - get_version, load_dotenv, locate_app, prepare_import, run_command, - with_appcontext + AppGroup, + FlaskGroup, + NoAppException, + ScriptInfo, + dotenv, + find_best_app, + get_version, + load_dotenv, + locate_app, + prepare_import, + run_command, + with_appcontext, ) cwd = os.getcwd() -test_path = os.path.abspath(os.path.join( - os.path.dirname(__file__), 'test_apps' -)) +test_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "test_apps")) @pytest.fixture @@ -44,6 +51,7 @@ def runner(): def test_cli_name(test_apps): """Make sure the CLI object's name is the app's name and not the app itself""" from cliapp.app import testapp + assert testapp.cli.name == testapp.name @@ -52,67 +60,67 @@ def test_find_best_app(test_apps): script_info = ScriptInfo() class Module: - app = Flask('appname') + app = Flask("appname") assert find_best_app(script_info, Module) == Module.app class Module: - application = Flask('appname') + application = Flask("appname") assert find_best_app(script_info, Module) == Module.application class Module: - myapp = Flask('appname') + myapp = Flask("appname") assert find_best_app(script_info, Module) == Module.myapp class Module: @staticmethod def create_app(): - return Flask('appname') + return Flask("appname") assert isinstance(find_best_app(script_info, Module), Flask) - assert find_best_app(script_info, Module).name == 'appname' + assert find_best_app(script_info, Module).name == "appname" class Module: @staticmethod def create_app(foo): - return Flask('appname') + return Flask("appname") assert isinstance(find_best_app(script_info, Module), Flask) - assert find_best_app(script_info, Module).name == 'appname' + assert find_best_app(script_info, Module).name == "appname" class Module: @staticmethod def create_app(foo=None, script_info=None): - return Flask('appname') + return Flask("appname") assert isinstance(find_best_app(script_info, Module), Flask) - assert find_best_app(script_info, Module).name == 'appname' + assert find_best_app(script_info, Module).name == "appname" class Module: @staticmethod def make_app(): - return Flask('appname') + return Flask("appname") assert isinstance(find_best_app(script_info, Module), Flask) - assert find_best_app(script_info, Module).name == 'appname' + assert find_best_app(script_info, Module).name == "appname" class Module: - myapp = Flask('appname1') + myapp = Flask("appname1") @staticmethod def create_app(): - return Flask('appname2') + return Flask("appname2") assert find_best_app(script_info, Module) == Module.myapp class Module: - myapp = Flask('appname1') + myapp = Flask("appname1") @staticmethod def create_app(): - return Flask('appname2') + return Flask("appname2") assert find_best_app(script_info, Module) == Module.myapp @@ -122,50 +130,56 @@ def test_find_best_app(test_apps): pytest.raises(NoAppException, find_best_app, script_info, Module) class Module: - myapp1 = Flask('appname1') - myapp2 = Flask('appname2') + myapp1 = Flask("appname1") + myapp2 = Flask("appname2") pytest.raises(NoAppException, find_best_app, script_info, Module) class Module: @staticmethod def create_app(foo, bar): - return Flask('appname2') + return Flask("appname2") pytest.raises(NoAppException, find_best_app, script_info, Module) class Module: @staticmethod def create_app(): - raise TypeError('bad bad factory!') + raise TypeError("bad bad factory!") pytest.raises(TypeError, find_best_app, script_info, Module) -@pytest.mark.parametrize('value,path,result', ( - ('test', cwd, 'test'), - ('test.py', cwd, 'test'), - ('a/test', os.path.join(cwd, 'a'), 'test'), - ('test/__init__.py', cwd, 'test'), - ('test/__init__', cwd, 'test'), - # nested package +@pytest.mark.parametrize( + "value,path,result", ( - os.path.join(test_path, 'cliapp', 'inner1', '__init__'), - test_path, 'cliapp.inner1' + ("test", cwd, "test"), + ("test.py", cwd, "test"), + ("a/test", os.path.join(cwd, "a"), "test"), + ("test/__init__.py", cwd, "test"), + ("test/__init__", cwd, "test"), + # nested package + ( + os.path.join(test_path, "cliapp", "inner1", "__init__"), + test_path, + "cliapp.inner1", + ), + ( + os.path.join(test_path, "cliapp", "inner1", "inner2"), + test_path, + "cliapp.inner1.inner2", + ), + # dotted name + ("test.a.b", cwd, "test.a.b"), + (os.path.join(test_path, "cliapp.app"), test_path, "cliapp.app"), + # not a Python file, will be caught during import + ( + os.path.join(test_path, "cliapp", "message.txt"), + test_path, + "cliapp.message.txt", + ), ), - ( - os.path.join(test_path, 'cliapp', 'inner1', 'inner2'), - test_path, 'cliapp.inner1.inner2' - ), - # dotted name - ('test.a.b', cwd, 'test.a.b'), - (os.path.join(test_path, 'cliapp.app'), test_path, 'cliapp.app'), - # not a Python file, will be caught during import - ( - os.path.join(test_path, 'cliapp', 'message.txt'), - test_path, 'cliapp.message.txt' - ), -)) +) def test_prepare_import(request, value, path, result): """Expect the correct path to be set and the correct import and app names to be returned. @@ -185,42 +199,48 @@ def test_prepare_import(request, value, path, result): assert sys.path[0] == path -@pytest.mark.parametrize('iname,aname,result', ( - ('cliapp.app', None, 'testapp'), - ('cliapp.app', 'testapp', 'testapp'), - ('cliapp.factory', None, 'app'), - ('cliapp.factory', 'create_app', 'app'), - ('cliapp.factory', 'create_app()', 'app'), - # no script_info - ('cliapp.factory', 'create_app2("foo", "bar")', 'app2_foo_bar'), - # trailing comma space - ('cliapp.factory', 'create_app2("foo", "bar", )', 'app2_foo_bar'), - # takes script_info - ('cliapp.factory', 'create_app3("foo")', 'app3_foo_spam'), - # strip whitespace - ('cliapp.factory', ' create_app () ', 'app'), -)) +@pytest.mark.parametrize( + "iname,aname,result", + ( + ("cliapp.app", None, "testapp"), + ("cliapp.app", "testapp", "testapp"), + ("cliapp.factory", None, "app"), + ("cliapp.factory", "create_app", "app"), + ("cliapp.factory", "create_app()", "app"), + # no script_info + ("cliapp.factory", 'create_app2("foo", "bar")', "app2_foo_bar"), + # trailing comma space + ("cliapp.factory", 'create_app2("foo", "bar", )', "app2_foo_bar"), + # takes script_info + ("cliapp.factory", 'create_app3("foo")', "app3_foo_spam"), + # strip whitespace + ("cliapp.factory", " create_app () ", "app"), + ), +) def test_locate_app(test_apps, iname, aname, result): info = ScriptInfo() - info.data['test'] = 'spam' + info.data["test"] = "spam" assert locate_app(info, iname, aname).name == result -@pytest.mark.parametrize('iname,aname', ( - ('notanapp.py', None), - ('cliapp/app', None), - ('cliapp.app', 'notanapp'), - # not enough arguments - ('cliapp.factory', 'create_app2("foo")'), - # invalid identifier - ('cliapp.factory', 'create_app('), - # no app returned - ('cliapp.factory', 'no_app'), - # nested import error - ('cliapp.importerrorapp', None), - # not a Python file - ('cliapp.message.txt', None), -)) +@pytest.mark.parametrize( + "iname,aname", + ( + ("notanapp.py", None), + ("cliapp/app", None), + ("cliapp.app", "notanapp"), + # not enough arguments + ("cliapp.factory", 'create_app2("foo")'), + # invalid identifier + ("cliapp.factory", "create_app("), + # no app returned + ("cliapp.factory", "no_app"), + # nested import error + ("cliapp.importerrorapp", None), + # not a Python file + ("cliapp.message.txt", None), + ), +) def test_locate_app_raises(test_apps, iname, aname): info = ScriptInfo() @@ -230,14 +250,12 @@ def test_locate_app_raises(test_apps, iname, aname): def test_locate_app_suppress_raise(): info = ScriptInfo() - app = locate_app(info, 'notanapp.py', None, raise_if_not_found=False) + app = locate_app(info, "notanapp.py", None, raise_if_not_found=False) assert app is None # only direct import error is suppressed with pytest.raises(NoAppException): - locate_app( - info, 'cliapp.importerrorapp', None, raise_if_not_found=False - ) + locate_app(info, "cliapp.importerrorapp", None, raise_if_not_found=False) def test_get_version(test_apps, capsys): @@ -249,7 +267,8 @@ def test_get_version(test_apps, capsys): resilient_parsing = False color = None - def exit(self): return + def exit(self): + return ctx = MockCtx() get_version(ctx, None, "test") @@ -267,15 +286,16 @@ def test_scriptinfo(test_apps, monkeypatch): assert obj.load_app() is app # import app with module's absolute path - cli_app_path = os.path.abspath(os.path.join( - os.path.dirname(__file__), 'test_apps', 'cliapp', 'app.py')) + cli_app_path = os.path.abspath( + os.path.join(os.path.dirname(__file__), "test_apps", "cliapp", "app.py") + ) obj = ScriptInfo(app_import_path=cli_app_path) app = obj.load_app() - assert app.name == 'testapp' + assert app.name == "testapp" assert obj.load_app() is app - obj = ScriptInfo(app_import_path=cli_app_path + ':testapp') + obj = ScriptInfo(app_import_path=cli_app_path + ":testapp") app = obj.load_app() - assert app.name == 'testapp' + assert app.name == "testapp" assert obj.load_app() is app def create_app(info): @@ -290,20 +310,22 @@ def test_scriptinfo(test_apps, monkeypatch): pytest.raises(NoAppException, obj.load_app) # import app from wsgi.py in current directory - monkeypatch.chdir(os.path.abspath(os.path.join( - os.path.dirname(__file__), 'test_apps', 'helloworld' - ))) + monkeypatch.chdir( + os.path.abspath( + os.path.join(os.path.dirname(__file__), "test_apps", "helloworld") + ) + ) obj = ScriptInfo() app = obj.load_app() - assert app.name == 'hello' + assert app.name == "hello" # import app from app.py in current directory - monkeypatch.chdir(os.path.abspath(os.path.join( - os.path.dirname(__file__), 'test_apps', 'cliapp' - ))) + monkeypatch.chdir( + os.path.abspath(os.path.join(os.path.dirname(__file__), "test_apps", "cliapp")) + ) obj = ScriptInfo() app = obj.load_app() - assert app.name == 'testapp' + assert app.name == "testapp" def test_with_appcontext(runner): @@ -318,7 +340,7 @@ def test_with_appcontext(runner): result = runner.invoke(testcmd, obj=obj) assert result.exit_code == 0 - assert result.output == 'testapp\n' + assert result.output == "testapp\n" def test_appgroup(runner): @@ -342,13 +364,13 @@ def test_appgroup(runner): obj = ScriptInfo(create_app=lambda info: Flask("testappgroup")) - result = runner.invoke(cli, ['test'], obj=obj) + result = runner.invoke(cli, ["test"], obj=obj) assert result.exit_code == 0 - assert result.output == 'testappgroup\n' + assert result.output == "testappgroup\n" - result = runner.invoke(cli, ['subgroup', 'test2'], obj=obj) + result = runner.invoke(cli, ["subgroup", "test2"], obj=obj) assert result.exit_code == 0 - assert result.output == 'testappgroup\n' + assert result.output == "testappgroup\n" def test_flaskgroup(runner): @@ -365,12 +387,12 @@ def test_flaskgroup(runner): def test(): click.echo(current_app.name) - result = runner.invoke(cli, ['test']) + result = runner.invoke(cli, ["test"]) assert result.exit_code == 0 - assert result.output == 'flaskgroup\n' + assert result.output == "flaskgroup\n" -@pytest.mark.parametrize('set_debug_flag', (True, False)) +@pytest.mark.parametrize("set_debug_flag", (True, False)) def test_flaskgroup_debug(runner, set_debug_flag): """Test FlaskGroup debug flag behavior.""" @@ -387,9 +409,9 @@ def test_flaskgroup_debug(runner, set_debug_flag): def test(): click.echo(str(current_app.debug)) - result = runner.invoke(cli, ['test']) + result = runner.invoke(cli, ["test"]) assert result.exit_code == 0 - assert result.output == '%s\n' % str(not set_debug_flag) + assert result.output == "%s\n" % str(not set_debug_flag) def test_print_exceptions(runner): @@ -403,10 +425,10 @@ def test_print_exceptions(runner): def cli(**params): pass - result = runner.invoke(cli, ['--help']) + result = runner.invoke(cli, ["--help"]) assert result.exit_code == 0 - assert 'Exception: oh no' in result.output - assert 'Traceback' in result.output + assert "Exception: oh no" in result.output + assert "Traceback" in result.output class TestRoutes: @@ -416,11 +438,11 @@ class TestRoutes: app = Flask(__name__) app.testing = True - @app.route('/get_post//', methods=['GET', 'POST']) + @app.route("/get_post//", methods=["GET", "POST"]) def yyy_get_post(x, y): pass - @app.route('/zzz_post', methods=['POST']) + @app.route("/zzz_post", methods=["POST"]) def aaa_post(): pass @@ -444,138 +466,132 @@ class TestRoutes: # skip the header and match the start of each row for expect, line in zip(order, output.splitlines()[2:]): # do this instead of startswith for nicer pytest output - assert line[:len(expect)] == expect + assert line[: len(expect)] == expect def test_simple(self, invoke): - result = invoke(['routes']) + result = invoke(["routes"]) assert result.exit_code == 0 - self.expect_order( - ['aaa_post', 'static', 'yyy_get_post'], - result.output - ) + self.expect_order(["aaa_post", "static", "yyy_get_post"], result.output) def test_sort(self, invoke): - default_output = invoke(['routes']).output - endpoint_output = invoke(['routes', '-s', 'endpoint']).output + default_output = invoke(["routes"]).output + endpoint_output = invoke(["routes", "-s", "endpoint"]).output assert default_output == endpoint_output self.expect_order( - ['static', 'yyy_get_post', 'aaa_post'], - invoke(['routes', '-s', 'methods']).output + ["static", "yyy_get_post", "aaa_post"], + invoke(["routes", "-s", "methods"]).output, ) self.expect_order( - ['yyy_get_post', 'static', 'aaa_post'], - invoke(['routes', '-s', 'rule']).output + ["yyy_get_post", "static", "aaa_post"], + invoke(["routes", "-s", "rule"]).output, ) self.expect_order( - ['aaa_post', 'yyy_get_post', 'static'], - invoke(['routes', '-s', 'match']).output + ["aaa_post", "yyy_get_post", "static"], + invoke(["routes", "-s", "match"]).output, ) def test_all_methods(self, invoke): - output = invoke(['routes']).output - assert 'GET, HEAD, OPTIONS, POST' not in output - output = invoke(['routes', '--all-methods']).output - assert 'GET, HEAD, OPTIONS, POST' in output + output = invoke(["routes"]).output + assert "GET, HEAD, OPTIONS, POST" not in output + output = invoke(["routes", "--all-methods"]).output + assert "GET, HEAD, OPTIONS, POST" in output def test_no_routes(self, invoke_no_routes): - result = invoke_no_routes(['routes']) + result = invoke_no_routes(["routes"]) assert result.exit_code == 0 - assert 'No routes were registered.' in result.output + assert "No routes were registered." in result.output -need_dotenv = pytest.mark.skipif( - dotenv is None, reason='dotenv is not installed' -) +need_dotenv = pytest.mark.skipif(dotenv is None, reason="dotenv is not installed") @need_dotenv def test_load_dotenv(monkeypatch): # can't use monkeypatch.delitem since the keys don't exist yet - for item in ('FOO', 'BAR', 'SPAM'): + for item in ("FOO", "BAR", "SPAM"): monkeypatch._setitem.append((os.environ, item, notset)) - monkeypatch.setenv('EGGS', '3') - monkeypatch.chdir(os.path.join(test_path, 'cliapp', 'inner1')) + monkeypatch.setenv("EGGS", "3") + monkeypatch.chdir(os.path.join(test_path, "cliapp", "inner1")) load_dotenv() assert os.getcwd() == test_path # .flaskenv doesn't overwrite .env - assert os.environ['FOO'] == 'env' + assert os.environ["FOO"] == "env" # set only in .flaskenv - assert os.environ['BAR'] == 'bar' + assert os.environ["BAR"] == "bar" # set only in .env - assert os.environ['SPAM'] == '1' + assert os.environ["SPAM"] == "1" # set manually, files don't overwrite - assert os.environ['EGGS'] == '3' + assert os.environ["EGGS"] == "3" @need_dotenv def test_dotenv_path(monkeypatch): - for item in ('FOO', 'BAR', 'EGGS'): + for item in ("FOO", "BAR", "EGGS"): monkeypatch._setitem.append((os.environ, item, notset)) cwd = os.getcwd() - load_dotenv(os.path.join(test_path, '.flaskenv')) + load_dotenv(os.path.join(test_path, ".flaskenv")) assert os.getcwd() == cwd - assert 'FOO' in os.environ + assert "FOO" in os.environ def test_dotenv_optional(monkeypatch): - monkeypatch.setattr('flask.cli.dotenv', None) + monkeypatch.setattr("flask.cli.dotenv", None) monkeypatch.chdir(test_path) load_dotenv() - assert 'FOO' not in os.environ + assert "FOO" not in os.environ @need_dotenv def test_disable_dotenv_from_env(monkeypatch, runner): monkeypatch.chdir(test_path) - monkeypatch.setitem(os.environ, 'FLASK_SKIP_DOTENV', '1') + monkeypatch.setitem(os.environ, "FLASK_SKIP_DOTENV", "1") runner.invoke(FlaskGroup()) - assert 'FOO' not in os.environ + assert "FOO" not in os.environ def test_run_cert_path(): # no key with pytest.raises(click.BadParameter): - run_command.make_context('run', ['--cert', __file__]) + run_command.make_context("run", ["--cert", __file__]) # no cert with pytest.raises(click.BadParameter): - run_command.make_context('run', ['--key', __file__]) + run_command.make_context("run", ["--key", __file__]) - ctx = run_command.make_context( - 'run', ['--cert', __file__, '--key', __file__]) - assert ctx.params['cert'] == (__file__, __file__) + ctx = run_command.make_context("run", ["--cert", __file__, "--key", __file__]) + assert ctx.params["cert"] == (__file__, __file__) def test_run_cert_adhoc(monkeypatch): - monkeypatch.setitem(sys.modules, 'OpenSSL', None) + monkeypatch.setitem(sys.modules, "OpenSSL", None) # pyOpenSSL not installed with pytest.raises(click.BadParameter): - run_command.make_context('run', ['--cert', 'adhoc']) + run_command.make_context("run", ["--cert", "adhoc"]) # pyOpenSSL installed - monkeypatch.setitem(sys.modules, 'OpenSSL', types.ModuleType('OpenSSL')) - ctx = run_command.make_context('run', ['--cert', 'adhoc']) - assert ctx.params['cert'] == 'adhoc' + monkeypatch.setitem(sys.modules, "OpenSSL", types.ModuleType("OpenSSL")) + ctx = run_command.make_context("run", ["--cert", "adhoc"]) + assert ctx.params["cert"] == "adhoc" # no key with adhoc with pytest.raises(click.BadParameter): - run_command.make_context('run', ['--cert', 'adhoc', '--key', __file__]) + run_command.make_context("run", ["--cert", "adhoc", "--key", __file__]) def test_run_cert_import(monkeypatch): - monkeypatch.setitem(sys.modules, 'not_here', None) + monkeypatch.setitem(sys.modules, "not_here", None) # ImportError with pytest.raises(click.BadParameter): - run_command.make_context('run', ['--cert', 'not_here']) + run_command.make_context("run", ["--cert", "not_here"]) # not an SSLContext if sys.version_info >= (2, 7, 9): with pytest.raises(click.BadParameter): - run_command.make_context('run', ['--cert', 'flask']) + run_command.make_context("run", ["--cert", "flask"]) # SSLContext if sys.version_info < (2, 7, 9): @@ -583,11 +599,10 @@ def test_run_cert_import(monkeypatch): else: ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - monkeypatch.setitem(sys.modules, 'ssl_context', ssl_context) - ctx = run_command.make_context('run', ['--cert', 'ssl_context']) - assert ctx.params['cert'] is ssl_context + monkeypatch.setitem(sys.modules, "ssl_context", ssl_context) + ctx = run_command.make_context("run", ["--cert", "ssl_context"]) + assert ctx.params["cert"] is ssl_context # no --key with SSLContext with pytest.raises(click.BadParameter): - run_command.make_context( - 'run', ['--cert', 'ssl_context', '--key', __file__]) + run_command.make_context("run", ["--cert", "ssl_context", "--key", __file__]) diff --git a/tests/test_config.py b/tests/test_config.py index 5584ed25..0bdd24a7 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -17,19 +17,19 @@ import pytest # config keys used for the TestConfig -TEST_KEY = 'foo' -SECRET_KEY = 'config' +TEST_KEY = "foo" +SECRET_KEY = "config" def common_object_test(app): - assert app.secret_key == 'config' - assert app.config['TEST_KEY'] == 'foo' - assert 'TestConfig' not in app.config + assert app.secret_key == "config" + assert app.config["TEST_KEY"] == "foo" + assert "TestConfig" not in app.config def test_config_from_file(): app = flask.Flask(__name__) - app.config.from_pyfile(__file__.rsplit('.', 1)[0] + '.py') + app.config.from_pyfile(__file__.rsplit(".", 1)[0] + ".py") common_object_test(app) @@ -42,45 +42,34 @@ def test_config_from_object(): def test_config_from_json(): app = flask.Flask(__name__) current_dir = os.path.dirname(os.path.abspath(__file__)) - app.config.from_json(os.path.join(current_dir, 'static', 'config.json')) + app.config.from_json(os.path.join(current_dir, "static", "config.json")) common_object_test(app) def test_config_from_mapping(): app = flask.Flask(__name__) - app.config.from_mapping({ - 'SECRET_KEY': 'config', - 'TEST_KEY': 'foo' - }) + app.config.from_mapping({"SECRET_KEY": "config", "TEST_KEY": "foo"}) common_object_test(app) app = flask.Flask(__name__) - app.config.from_mapping([ - ('SECRET_KEY', 'config'), - ('TEST_KEY', 'foo') - ]) + app.config.from_mapping([("SECRET_KEY", "config"), ("TEST_KEY", "foo")]) common_object_test(app) app = flask.Flask(__name__) - app.config.from_mapping( - SECRET_KEY='config', - TEST_KEY='foo' - ) + app.config.from_mapping(SECRET_KEY="config", TEST_KEY="foo") common_object_test(app) app = flask.Flask(__name__) with pytest.raises(TypeError): - app.config.from_mapping( - {}, {} - ) + app.config.from_mapping({}, {}) def test_config_from_class(): class Base(object): - TEST_KEY = 'foo' + TEST_KEY = "foo" class Test(Base): - SECRET_KEY = 'config' + SECRET_KEY = "config" app = flask.Flask(__name__) app.config.from_object(Test) @@ -93,12 +82,12 @@ def test_config_from_envvar(): os.environ = {} app = flask.Flask(__name__) with pytest.raises(RuntimeError) as e: - app.config.from_envvar('FOO_SETTINGS') + app.config.from_envvar("FOO_SETTINGS") assert "'FOO_SETTINGS' is not set" in str(e.value) - assert not app.config.from_envvar('FOO_SETTINGS', silent=True) + assert not app.config.from_envvar("FOO_SETTINGS", silent=True) - os.environ = {'FOO_SETTINGS': __file__.rsplit('.', 1)[0] + '.py'} - assert app.config.from_envvar('FOO_SETTINGS') + os.environ = {"FOO_SETTINGS": __file__.rsplit(".", 1)[0] + ".py"} + assert app.config.from_envvar("FOO_SETTINGS") common_object_test(app) finally: os.environ = env @@ -107,15 +96,17 @@ def test_config_from_envvar(): def test_config_from_envvar_missing(): env = os.environ try: - os.environ = {'FOO_SETTINGS': 'missing.cfg'} + os.environ = {"FOO_SETTINGS": "missing.cfg"} with pytest.raises(IOError) as e: app = flask.Flask(__name__) - app.config.from_envvar('FOO_SETTINGS') + app.config.from_envvar("FOO_SETTINGS") msg = str(e.value) - assert msg.startswith('[Errno 2] Unable to load configuration ' - 'file (No such file or directory):') + assert msg.startswith( + "[Errno 2] Unable to load configuration " + "file (No such file or directory):" + ) assert msg.endswith("missing.cfg'") - assert not app.config.from_envvar('FOO_SETTINGS', silent=True) + assert not app.config.from_envvar("FOO_SETTINGS", silent=True) finally: os.environ = env @@ -123,23 +114,25 @@ def test_config_from_envvar_missing(): def test_config_missing(): app = flask.Flask(__name__) with pytest.raises(IOError) as e: - app.config.from_pyfile('missing.cfg') + app.config.from_pyfile("missing.cfg") msg = str(e.value) - assert msg.startswith('[Errno 2] Unable to load configuration ' - 'file (No such file or directory):') + assert msg.startswith( + "[Errno 2] Unable to load configuration " "file (No such file or directory):" + ) assert msg.endswith("missing.cfg'") - assert not app.config.from_pyfile('missing.cfg', silent=True) + assert not app.config.from_pyfile("missing.cfg", silent=True) def test_config_missing_json(): app = flask.Flask(__name__) with pytest.raises(IOError) as e: - app.config.from_json('missing.json') + app.config.from_json("missing.json") msg = str(e.value) - assert msg.startswith('[Errno 2] Unable to load configuration ' - 'file (No such file or directory):') + assert msg.startswith( + "[Errno 2] Unable to load configuration " "file (No such file or directory):" + ) assert msg.endswith("missing.json'") - assert not app.config.from_json('missing.json', silent=True) + assert not app.config.from_json("missing.json", silent=True) def test_custom_config_class(): @@ -148,6 +141,7 @@ def test_custom_config_class(): class Flask(flask.Flask): config_class = Config + app = Flask(__name__) assert isinstance(app.config, Config) app.config.from_object(__name__) @@ -156,52 +150,60 @@ def test_custom_config_class(): def test_session_lifetime(): app = flask.Flask(__name__) - app.config['PERMANENT_SESSION_LIFETIME'] = 42 + app.config["PERMANENT_SESSION_LIFETIME"] = 42 assert app.permanent_session_lifetime.seconds == 42 def test_send_file_max_age(): app = flask.Flask(__name__) - app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 3600 + app.config["SEND_FILE_MAX_AGE_DEFAULT"] = 3600 assert app.send_file_max_age_default.seconds == 3600 - app.config['SEND_FILE_MAX_AGE_DEFAULT'] = timedelta(hours=2) + app.config["SEND_FILE_MAX_AGE_DEFAULT"] = timedelta(hours=2) assert app.send_file_max_age_default.seconds == 7200 def test_get_namespace(): app = flask.Flask(__name__) - app.config['FOO_OPTION_1'] = 'foo option 1' - app.config['FOO_OPTION_2'] = 'foo option 2' - app.config['BAR_STUFF_1'] = 'bar stuff 1' - app.config['BAR_STUFF_2'] = 'bar stuff 2' - foo_options = app.config.get_namespace('FOO_') + app.config["FOO_OPTION_1"] = "foo option 1" + app.config["FOO_OPTION_2"] = "foo option 2" + app.config["BAR_STUFF_1"] = "bar stuff 1" + app.config["BAR_STUFF_2"] = "bar stuff 2" + foo_options = app.config.get_namespace("FOO_") assert 2 == len(foo_options) - assert 'foo option 1' == foo_options['option_1'] - assert 'foo option 2' == foo_options['option_2'] - bar_options = app.config.get_namespace('BAR_', lowercase=False) + assert "foo option 1" == foo_options["option_1"] + assert "foo option 2" == foo_options["option_2"] + bar_options = app.config.get_namespace("BAR_", lowercase=False) assert 2 == len(bar_options) - assert 'bar stuff 1' == bar_options['STUFF_1'] - assert 'bar stuff 2' == bar_options['STUFF_2'] - foo_options = app.config.get_namespace('FOO_', trim_namespace=False) + assert "bar stuff 1" == bar_options["STUFF_1"] + assert "bar stuff 2" == bar_options["STUFF_2"] + foo_options = app.config.get_namespace("FOO_", trim_namespace=False) assert 2 == len(foo_options) - assert 'foo option 1' == foo_options['foo_option_1'] - assert 'foo option 2' == foo_options['foo_option_2'] - bar_options = app.config.get_namespace('BAR_', lowercase=False, trim_namespace=False) + assert "foo option 1" == foo_options["foo_option_1"] + assert "foo option 2" == foo_options["foo_option_2"] + bar_options = app.config.get_namespace( + "BAR_", lowercase=False, trim_namespace=False + ) assert 2 == len(bar_options) - assert 'bar stuff 1' == bar_options['BAR_STUFF_1'] - assert 'bar stuff 2' == bar_options['BAR_STUFF_2'] + assert "bar stuff 1" == bar_options["BAR_STUFF_1"] + assert "bar stuff 2" == bar_options["BAR_STUFF_2"] -@pytest.mark.parametrize('encoding', ['utf-8', 'iso-8859-15', 'latin-1']) +@pytest.mark.parametrize("encoding", ["utf-8", "iso-8859-15", "latin-1"]) def test_from_pyfile_weird_encoding(tmpdir, encoding): - f = tmpdir.join('my_config.py') - f.write_binary(textwrap.dedent(u''' + f = tmpdir.join("my_config.py") + f.write_binary( + textwrap.dedent( + u""" # -*- coding: {0} -*- TEST_VALUE = "föö" - '''.format(encoding)).encode(encoding)) + """.format( + encoding + ) + ).encode(encoding) + ) app = flask.Flask(__name__) app.config.from_pyfile(str(f)) - value = app.config['TEST_VALUE'] + value = app.config["TEST_VALUE"] if PY2: value = value.decode(encoding) - assert value == u'föö' + assert value == u"föö" diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 8f2fc1c9..d65a5f5e 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -17,10 +17,7 @@ import uuid import pytest from werkzeug.datastructures import Range from werkzeug.exceptions import BadRequest, NotFound -from werkzeug.http import ( - http_date, parse_cache_control_header, - parse_options_header -) +from werkzeug.http import http_date, parse_cache_control_header, parse_options_header import flask from flask import json @@ -31,6 +28,7 @@ from flask.helpers import get_debug_flag, get_env def has_encoding(name): try: import codecs + codecs.lookup(name) return True except LookupError: @@ -43,6 +41,7 @@ class FakePath(object): This represents a ``pathlib.Path`` object in python 3. See: https://www.python.org/dev/peps/pep-0519/ """ + def __init__(self, path): self.path = path @@ -72,96 +71,107 @@ class FixedOffset(datetime.tzinfo): class TestJSON(object): - @pytest.mark.parametrize('value', ( - 1, 't', True, False, None, - [], [1, 2, 3], - {}, {'foo': u'🐍'}, - )) - @pytest.mark.parametrize('encoding', ( - 'utf-8', 'utf-8-sig', - 'utf-16-le', 'utf-16-be', 'utf-16', - 'utf-32-le', 'utf-32-be', 'utf-32', - )) + @pytest.mark.parametrize( + "value", (1, "t", True, False, None, [], [1, 2, 3], {}, {"foo": u"🐍"}) + ) + @pytest.mark.parametrize( + "encoding", + ( + "utf-8", + "utf-8-sig", + "utf-16-le", + "utf-16-be", + "utf-16", + "utf-32-le", + "utf-32-be", + "utf-32", + ), + ) def test_detect_encoding(self, value, encoding): data = json.dumps(value).encode(encoding) assert json.detect_encoding(data) == encoding assert json.loads(data) == value def test_ignore_cached_json(self, app): - with app.test_request_context('/', method='POST', data='malformed', - content_type='application/json'): + with app.test_request_context( + "/", method="POST", data="malformed", content_type="application/json" + ): assert flask.request.get_json(silent=True, cache=True) is None with pytest.raises(BadRequest): flask.request.get_json(silent=False, cache=False) def test_different_silent_on_bad_request(self, app): with app.test_request_context( - '/', method='POST', data='malformed', - content_type='application/json'): + "/", method="POST", data="malformed", content_type="application/json" + ): assert flask.request.get_json(silent=True) is None with pytest.raises(BadRequest): flask.request.get_json(silent=False) def test_different_silent_on_normal_request(self, app): - with app.test_request_context('/', method='POST', json={'foo': 'bar'}): + with app.test_request_context("/", method="POST", json={"foo": "bar"}): silent_rv = flask.request.get_json(silent=True) normal_rv = flask.request.get_json(silent=False) assert silent_rv is normal_rv - assert normal_rv['foo'] == 'bar' + assert normal_rv["foo"] == "bar" - def test_post_empty_json_adds_exception_to_response_content_in_debug(self, app, client): - app.config['DEBUG'] = True - app.config['TRAP_BAD_REQUEST_ERRORS'] = False + def test_post_empty_json_adds_exception_to_response_content_in_debug( + self, app, client + ): + app.config["DEBUG"] = True + app.config["TRAP_BAD_REQUEST_ERRORS"] = False - @app.route('/json', methods=['POST']) + @app.route("/json", methods=["POST"]) def post_json(): flask.request.get_json() return None - rv = client.post('/json', data=None, content_type='application/json') + rv = client.post("/json", data=None, content_type="application/json") assert rv.status_code == 400 - assert b'Failed to decode JSON object' in rv.data + assert b"Failed to decode JSON object" in rv.data - def test_post_empty_json_wont_add_exception_to_response_if_no_debug(self, app, client): - app.config['DEBUG'] = False - app.config['TRAP_BAD_REQUEST_ERRORS'] = False + def test_post_empty_json_wont_add_exception_to_response_if_no_debug( + self, app, client + ): + app.config["DEBUG"] = False + app.config["TRAP_BAD_REQUEST_ERRORS"] = False - @app.route('/json', methods=['POST']) + @app.route("/json", methods=["POST"]) def post_json(): flask.request.get_json() return None - rv = client.post('/json', data=None, content_type='application/json') + rv = client.post("/json", data=None, content_type="application/json") assert rv.status_code == 400 - assert b'Failed to decode JSON object' not in rv.data + assert b"Failed to decode JSON object" not in rv.data def test_json_bad_requests(self, app, client): - - @app.route('/json', methods=['POST']) + @app.route("/json", methods=["POST"]) def return_json(): return flask.jsonify(foo=text_type(flask.request.get_json())) - rv = client.post('/json', data='malformed', content_type='application/json') + rv = client.post("/json", data="malformed", content_type="application/json") assert rv.status_code == 400 def test_json_custom_mimetypes(self, app, client): - - @app.route('/json', methods=['POST']) + @app.route("/json", methods=["POST"]) def return_json(): return flask.request.get_json() - rv = client.post('/json', data='"foo"', content_type='application/x+json') - assert rv.data == b'foo' + rv = client.post("/json", data='"foo"', content_type="application/x+json") + assert rv.data == b"foo" - @pytest.mark.parametrize('test_value,expected', [(True, '"\\u2603"'), (False, u'"\u2603"')]) + @pytest.mark.parametrize( + "test_value,expected", [(True, '"\\u2603"'), (False, u'"\u2603"')] + ) def test_json_as_unicode(self, test_value, expected, app, app_ctx): - app.config['JSON_AS_ASCII'] = test_value - rv = flask.json.dumps(u'\N{SNOWMAN}') + app.config["JSON_AS_ASCII"] = test_value + rv = flask.json.dumps(u"\N{SNOWMAN}") assert rv == expected def test_json_dump_to_file(self, app, app_ctx): - test_data = {'name': 'Flask'} + test_data = {"name": "Flask"} out = StringIO() flask.json.dump(test_data, out) @@ -169,108 +179,125 @@ class TestJSON(object): rv = flask.json.load(out) assert rv == test_data - @pytest.mark.parametrize('test_value', [0, -1, 1, 23, 3.14, 's', "longer string", True, False, None]) + @pytest.mark.parametrize( + "test_value", [0, -1, 1, 23, 3.14, "s", "longer string", True, False, None] + ) def test_jsonify_basic_types(self, test_value, app, client): """Test jsonify with basic types.""" - url = '/jsonify_basic_types' + url = "/jsonify_basic_types" app.add_url_rule(url, url, lambda x=test_value: flask.jsonify(x)) rv = client.get(url) - assert rv.mimetype == 'application/json' + assert rv.mimetype == "application/json" assert flask.json.loads(rv.data) == test_value def test_jsonify_dicts(self, app, client): """Test jsonify with dicts and kwargs unpacking.""" - d = {'a': 0, 'b': 23, 'c': 3.14, 'd': 't', - 'e': 'Hi', 'f': True, 'g': False, - 'h': ['test list', 10, False], - 'i': {'test': 'dict'}} + d = { + "a": 0, + "b": 23, + "c": 3.14, + "d": "t", + "e": "Hi", + "f": True, + "g": False, + "h": ["test list", 10, False], + "i": {"test": "dict"}, + } - @app.route('/kw') + @app.route("/kw") def return_kwargs(): return flask.jsonify(**d) - @app.route('/dict') + @app.route("/dict") def return_dict(): return flask.jsonify(d) - for url in '/kw', '/dict': + for url in "/kw", "/dict": rv = client.get(url) - assert rv.mimetype == 'application/json' + assert rv.mimetype == "application/json" assert flask.json.loads(rv.data) == d def test_jsonify_arrays(self, app, client): """Test jsonify of lists and args unpacking.""" l = [ - 0, 42, 3.14, 't', 'hello', True, False, - ['test list', 2, False], - {'test': 'dict'} + 0, + 42, + 3.14, + "t", + "hello", + True, + False, + ["test list", 2, False], + {"test": "dict"}, ] - @app.route('/args_unpack') + @app.route("/args_unpack") def return_args_unpack(): return flask.jsonify(*l) - @app.route('/array') + @app.route("/array") def return_array(): return flask.jsonify(l) - for url in '/args_unpack', '/array': + for url in "/args_unpack", "/array": rv = client.get(url) - assert rv.mimetype == 'application/json' + assert rv.mimetype == "application/json" assert flask.json.loads(rv.data) == l def test_jsonify_date_types(self, app, client): """Test jsonify with datetime.date and datetime.datetime types.""" test_dates = ( datetime.datetime(1973, 3, 11, 6, 30, 45), - datetime.date(1975, 1, 5) + datetime.date(1975, 1, 5), ) for i, d in enumerate(test_dates): - url = '/datetest{0}'.format(i) + url = "/datetest{0}".format(i) app.add_url_rule(url, str(i), lambda val=d: flask.jsonify(x=val)) rv = client.get(url) - assert rv.mimetype == 'application/json' - assert flask.json.loads(rv.data)['x'] == http_date(d.timetuple()) + assert rv.mimetype == "application/json" + assert flask.json.loads(rv.data)["x"] == http_date(d.timetuple()) - @pytest.mark.parametrize('tz', (('UTC', 0), ('PST', -8), ('KST', 9))) + @pytest.mark.parametrize("tz", (("UTC", 0), ("PST", -8), ("KST", 9))) def test_jsonify_aware_datetimes(self, tz): """Test if aware datetime.datetime objects are converted into GMT.""" tzinfo = FixedOffset(hours=tz[1], name=tz[0]) dt = datetime.datetime(2017, 1, 1, 12, 34, 56, tzinfo=tzinfo) - gmt = FixedOffset(hours=0, name='GMT') + gmt = FixedOffset(hours=0, name="GMT") expected = dt.astimezone(gmt).strftime('"%a, %d %b %Y %H:%M:%S %Z"') assert flask.json.JSONEncoder().encode(dt) == expected def test_jsonify_uuid_types(self, app, client): """Test jsonify with uuid.UUID types""" - test_uuid = uuid.UUID(bytes=b'\xDE\xAD\xBE\xEF' * 4) - url = '/uuid_test' + test_uuid = uuid.UUID(bytes=b"\xDE\xAD\xBE\xEF" * 4) + url = "/uuid_test" app.add_url_rule(url, url, lambda: flask.jsonify(x=test_uuid)) rv = client.get(url) - rv_x = flask.json.loads(rv.data)['x'] + rv_x = flask.json.loads(rv.data)["x"] assert rv_x == str(test_uuid) rv_uuid = uuid.UUID(rv_x) assert rv_uuid == test_uuid def test_json_attr(self, app, client): - - @app.route('/add', methods=['POST']) + @app.route("/add", methods=["POST"]) def add(): json = flask.request.get_json() - return text_type(json['a'] + json['b']) + return text_type(json["a"] + json["b"]) - rv = client.post('/add', data=flask.json.dumps({'a': 1, 'b': 2}), - content_type='application/json') - assert rv.data == b'3' + rv = client.post( + "/add", + data=flask.json.dumps({"a": 1, "b": 2}), + content_type="application/json", + ) + assert rv.data == b"3" def test_template_escaping(self, app, req_ctx): render = flask.render_template_string - rv = flask.json.htmlsafe_dumps('') + rv = flask.json.htmlsafe_dumps("") assert rv == u'"\\u003c/script\\u003e"' assert type(rv) == text_type rv = render('{{ ""|tojson }}') @@ -283,8 +310,9 @@ class TestJSON(object): assert rv == '"\\u0026"' rv = render('{{ "\'"|tojson }}') assert rv == '"\\u0027"' - rv = render("", - data={'x': ["foo", "bar", "baz'"]}) + rv = render( + "", data={"x": ["foo", "bar", "baz'"]} + ) assert rv == '' def test_json_customization(self, app, client): @@ -295,29 +323,31 @@ class TestJSON(object): class MyEncoder(flask.json.JSONEncoder): def default(self, o): if isinstance(o, X): - return '<%d>' % o.val + return "<%d>" % o.val return flask.json.JSONEncoder.default(self, o) class MyDecoder(flask.json.JSONDecoder): def __init__(self, *args, **kwargs): - kwargs.setdefault('object_hook', self.object_hook) + kwargs.setdefault("object_hook", self.object_hook) flask.json.JSONDecoder.__init__(self, *args, **kwargs) def object_hook(self, obj): - if len(obj) == 1 and '_foo' in obj: - return X(obj['_foo']) + if len(obj) == 1 and "_foo" in obj: + return X(obj["_foo"]) return obj app.json_encoder = MyEncoder app.json_decoder = MyDecoder - @app.route('/', methods=['POST']) + @app.route("/", methods=["POST"]) def index(): - return flask.json.dumps(flask.request.get_json()['x']) + return flask.json.dumps(flask.request.get_json()["x"]) - rv = client.post('/', data=flask.json.dumps({ - 'x': {'_foo': 42} - }), content_type='application/json') + rv = client.post( + "/", + data=flask.json.dumps({"x": {"_foo": 42}}), + content_type="application/json", + ) assert rv.data == b'"<42>"' def test_blueprint_json_customization(self, app, client): @@ -328,68 +358,70 @@ class TestJSON(object): class MyEncoder(flask.json.JSONEncoder): def default(self, o): if isinstance(o, X): - return '<%d>' % o.val + return "<%d>" % o.val return flask.json.JSONEncoder.default(self, o) class MyDecoder(flask.json.JSONDecoder): def __init__(self, *args, **kwargs): - kwargs.setdefault('object_hook', self.object_hook) + kwargs.setdefault("object_hook", self.object_hook) flask.json.JSONDecoder.__init__(self, *args, **kwargs) def object_hook(self, obj): - if len(obj) == 1 and '_foo' in obj: - return X(obj['_foo']) + if len(obj) == 1 and "_foo" in obj: + return X(obj["_foo"]) return obj - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) bp.json_encoder = MyEncoder bp.json_decoder = MyDecoder - @bp.route('/bp', methods=['POST']) + @bp.route("/bp", methods=["POST"]) def index(): - return flask.json.dumps(flask.request.get_json()['x']) + return flask.json.dumps(flask.request.get_json()["x"]) app.register_blueprint(bp) - rv = client.post('/bp', data=flask.json.dumps({ - 'x': {'_foo': 42} - }), content_type='application/json') + rv = client.post( + "/bp", + data=flask.json.dumps({"x": {"_foo": 42}}), + content_type="application/json", + ) assert rv.data == b'"<42>"' def test_modified_url_encoding(self, app, client): class ModifiedRequest(flask.Request): - url_charset = 'euc-kr' + url_charset = "euc-kr" app.request_class = ModifiedRequest - app.url_map.charset = 'euc-kr' + app.url_map.charset = "euc-kr" - @app.route('/') + @app.route("/") def index(): - return flask.request.args['foo'] + return flask.request.args["foo"] - rv = client.get(u'/?foo=정상처리'.encode('euc-kr')) + rv = client.get(u"/?foo=정상처리".encode("euc-kr")) assert rv.status_code == 200 - assert rv.data == u'정상처리'.encode('utf-8') + assert rv.data == u"정상처리".encode("utf-8") - if not has_encoding('euc-kr'): + if not has_encoding("euc-kr"): test_modified_url_encoding = None def test_json_key_sorting(self, app, client): app.debug = True - assert app.config['JSON_SORT_KEYS'] == True - d = dict.fromkeys(range(20), 'foo') + assert app.config["JSON_SORT_KEYS"] == True + d = dict.fromkeys(range(20), "foo") - @app.route('/') + @app.route("/") def index(): return flask.jsonify(values=d) - rv = client.get('/') - lines = [x.strip() for x in rv.data.strip().decode('utf-8').splitlines()] + rv = client.get("/") + lines = [x.strip() for x in rv.data.strip().decode("utf-8").splitlines()] sorted_by_str = [ - '{', + "{", '"values": {', '"0": "foo",', '"1": "foo",', @@ -411,11 +443,11 @@ class TestJSON(object): '"7": "foo",', '"8": "foo",', '"9": "foo"', - '}', - '}' + "}", + "}", ] sorted_by_int = [ - '{', + "{", '"values": {', '"0": "foo",', '"1": "foo",', @@ -437,8 +469,8 @@ class TestJSON(object): '"17": "foo",', '"18": "foo",', '"19": "foo"', - '}', - '}' + "}", + "}", ] try: @@ -449,67 +481,70 @@ class TestJSON(object): class TestSendfile(object): def test_send_file_regular(self, app, req_ctx): - rv = flask.send_file('static/index.html') + rv = flask.send_file("static/index.html") assert rv.direct_passthrough - assert rv.mimetype == 'text/html' - with app.open_resource('static/index.html') as f: + assert rv.mimetype == "text/html" + with app.open_resource("static/index.html") as f: rv.direct_passthrough = False assert rv.data == f.read() rv.close() def test_send_file_xsendfile(self, app, req_ctx, catch_deprecation_warnings): app.use_x_sendfile = True - rv = flask.send_file('static/index.html') + rv = flask.send_file("static/index.html") assert rv.direct_passthrough - assert 'x-sendfile' in rv.headers - assert rv.headers['x-sendfile'] == \ - os.path.join(app.root_path, 'static/index.html') - assert rv.mimetype == 'text/html' + assert "x-sendfile" in rv.headers + assert rv.headers["x-sendfile"] == os.path.join( + app.root_path, "static/index.html" + ) + assert rv.mimetype == "text/html" rv.close() def test_send_file_last_modified(self, app, client): last_modified = datetime.datetime(1999, 1, 1) - @app.route('/') + @app.route("/") def index(): - return flask.send_file(StringIO("party like it's"), - last_modified=last_modified, - mimetype='text/plain') + return flask.send_file( + StringIO("party like it's"), + last_modified=last_modified, + mimetype="text/plain", + ) - rv = client.get('/') + rv = client.get("/") assert rv.last_modified == last_modified def test_send_file_object_without_mimetype(self, app, req_ctx): with pytest.raises(ValueError) as excinfo: flask.send_file(StringIO("LOL")) - assert 'Unable to infer MIME-type' in str(excinfo) - assert 'no filename is available' in str(excinfo) + assert "Unable to infer MIME-type" in str(excinfo) + assert "no filename is available" in str(excinfo) - flask.send_file(StringIO("LOL"), attachment_filename='filename') + flask.send_file(StringIO("LOL"), attachment_filename="filename") def test_send_file_object(self, app, req_ctx): - with open(os.path.join(app.root_path, 'static/index.html'), mode='rb') as f: - rv = flask.send_file(f, mimetype='text/html') + with open(os.path.join(app.root_path, "static/index.html"), mode="rb") as f: + rv = flask.send_file(f, mimetype="text/html") rv.direct_passthrough = False - with app.open_resource('static/index.html') as f: + with app.open_resource("static/index.html") as f: assert rv.data == f.read() - assert rv.mimetype == 'text/html' + assert rv.mimetype == "text/html" rv.close() app.use_x_sendfile = True - with open(os.path.join(app.root_path, 'static/index.html')) as f: - rv = flask.send_file(f, mimetype='text/html') - assert rv.mimetype == 'text/html' - assert 'x-sendfile' not in rv.headers + with open(os.path.join(app.root_path, "static/index.html")) as f: + rv = flask.send_file(f, mimetype="text/html") + assert rv.mimetype == "text/html" + assert "x-sendfile" not in rv.headers rv.close() app.use_x_sendfile = False - f = StringIO('Test') - rv = flask.send_file(f, mimetype='application/octet-stream') + f = StringIO("Test") + rv = flask.send_file(f, mimetype="application/octet-stream") rv.direct_passthrough = False - assert rv.data == b'Test' - assert rv.mimetype == 'application/octet-stream' + assert rv.data == b"Test" + assert rv.mimetype == "application/octet-stream" rv.close() class PyStringIO(object): @@ -519,175 +554,192 @@ class TestSendfile(object): def __getattr__(self, name): return getattr(self._io, name) - f = PyStringIO('Test') - f.name = 'test.txt' + f = PyStringIO("Test") + f.name = "test.txt" rv = flask.send_file(f, attachment_filename=f.name) rv.direct_passthrough = False - assert rv.data == b'Test' - assert rv.mimetype == 'text/plain' + assert rv.data == b"Test" + assert rv.mimetype == "text/plain" rv.close() - f = StringIO('Test') - rv = flask.send_file(f, mimetype='text/plain') + f = StringIO("Test") + rv = flask.send_file(f, mimetype="text/plain") rv.direct_passthrough = False - assert rv.data == b'Test' - assert rv.mimetype == 'text/plain' + assert rv.data == b"Test" + assert rv.mimetype == "text/plain" rv.close() app.use_x_sendfile = True - f = StringIO('Test') - rv = flask.send_file(f, mimetype='text/html') - assert 'x-sendfile' not in rv.headers + f = StringIO("Test") + rv = flask.send_file(f, mimetype="text/html") + assert "x-sendfile" not in rv.headers rv.close() def test_send_file_pathlike(self, app, req_ctx): - rv = flask.send_file(FakePath('static/index.html')) + rv = flask.send_file(FakePath("static/index.html")) assert rv.direct_passthrough - assert rv.mimetype == 'text/html' - with app.open_resource('static/index.html') as f: + assert rv.mimetype == "text/html" + with app.open_resource("static/index.html") as f: rv.direct_passthrough = False assert rv.data == f.read() rv.close() @pytest.mark.skipif( - not callable(getattr(Range, 'to_content_range_header', None)), - reason="not implemented within werkzeug" + not callable(getattr(Range, "to_content_range_header", None)), + reason="not implemented within werkzeug", ) def test_send_file_range_request(self, app, client): - @app.route('/') + @app.route("/") def index(): - return flask.send_file('static/index.html', conditional=True) + return flask.send_file("static/index.html", conditional=True) - rv = client.get('/', headers={'Range': 'bytes=4-15'}) + rv = client.get("/", headers={"Range": "bytes=4-15"}) assert rv.status_code == 206 - with app.open_resource('static/index.html') as f: + with app.open_resource("static/index.html") as f: assert rv.data == f.read()[4:16] rv.close() - rv = client.get('/', headers={'Range': 'bytes=4-'}) + rv = client.get("/", headers={"Range": "bytes=4-"}) assert rv.status_code == 206 - with app.open_resource('static/index.html') as f: + with app.open_resource("static/index.html") as f: assert rv.data == f.read()[4:] rv.close() - rv = client.get('/', headers={'Range': 'bytes=4-1000'}) + rv = client.get("/", headers={"Range": "bytes=4-1000"}) assert rv.status_code == 206 - with app.open_resource('static/index.html') as f: + with app.open_resource("static/index.html") as f: assert rv.data == f.read()[4:] rv.close() - rv = client.get('/', headers={'Range': 'bytes=-10'}) + rv = client.get("/", headers={"Range": "bytes=-10"}) assert rv.status_code == 206 - with app.open_resource('static/index.html') as f: + with app.open_resource("static/index.html") as f: assert rv.data == f.read()[-10:] rv.close() - rv = client.get('/', headers={'Range': 'bytes=1000-'}) + rv = client.get("/", headers={"Range": "bytes=1000-"}) assert rv.status_code == 416 rv.close() - rv = client.get('/', headers={'Range': 'bytes=-'}) + rv = client.get("/", headers={"Range": "bytes=-"}) assert rv.status_code == 416 rv.close() - rv = client.get('/', headers={'Range': 'somethingsomething'}) + rv = client.get("/", headers={"Range": "somethingsomething"}) assert rv.status_code == 416 rv.close() - last_modified = datetime.datetime.utcfromtimestamp(os.path.getmtime( - os.path.join(app.root_path, 'static/index.html'))).replace( - microsecond=0) + last_modified = datetime.datetime.utcfromtimestamp( + os.path.getmtime(os.path.join(app.root_path, "static/index.html")) + ).replace(microsecond=0) - rv = client.get('/', headers={'Range': 'bytes=4-15', - 'If-Range': http_date(last_modified)}) + rv = client.get( + "/", headers={"Range": "bytes=4-15", "If-Range": http_date(last_modified)} + ) assert rv.status_code == 206 rv.close() - rv = client.get('/', headers={'Range': 'bytes=4-15', 'If-Range': http_date( - datetime.datetime(1999, 1, 1))}) + rv = client.get( + "/", + headers={ + "Range": "bytes=4-15", + "If-Range": http_date(datetime.datetime(1999, 1, 1)), + }, + ) assert rv.status_code == 200 rv.close() def test_send_file_range_request_bytesio(self, app, client): - @app.route('/') + @app.route("/") def index(): - file = io.BytesIO(b'somethingsomething') + file = io.BytesIO(b"somethingsomething") return flask.send_file( - file, attachment_filename='filename', conditional=True + file, attachment_filename="filename", conditional=True ) - rv = client.get('/', headers={'Range': 'bytes=4-15'}) + rv = client.get("/", headers={"Range": "bytes=4-15"}) assert rv.status_code == 206 - assert rv.data == b'somethingsomething'[4:16] + assert rv.data == b"somethingsomething"[4:16] rv.close() @pytest.mark.skipif( - not callable(getattr(Range, 'to_content_range_header', None)), - reason="not implemented within werkzeug" + not callable(getattr(Range, "to_content_range_header", None)), + reason="not implemented within werkzeug", ) def test_send_file_range_request_xsendfile_invalid(self, app, client): # https://github.com/pallets/flask/issues/2526 app.use_x_sendfile = True - @app.route('/') + @app.route("/") def index(): - return flask.send_file('static/index.html', conditional=True) + return flask.send_file("static/index.html", conditional=True) - rv = client.get('/', headers={'Range': 'bytes=1000-'}) + rv = client.get("/", headers={"Range": "bytes=1000-"}) assert rv.status_code == 416 rv.close() def test_attachment(self, app, req_ctx): app = flask.Flask(__name__) with app.test_request_context(): - with open(os.path.join(app.root_path, 'static/index.html')) as f: - rv = flask.send_file(f, as_attachment=True, - attachment_filename='index.html') - value, options = \ - parse_options_header(rv.headers['Content-Disposition']) - assert value == 'attachment' + with open(os.path.join(app.root_path, "static/index.html")) as f: + rv = flask.send_file( + f, as_attachment=True, attachment_filename="index.html" + ) + value, options = parse_options_header(rv.headers["Content-Disposition"]) + assert value == "attachment" rv.close() - with open(os.path.join(app.root_path, 'static/index.html')) as f: - rv = flask.send_file(f, as_attachment=True, - attachment_filename='index.html') - value, options = \ - parse_options_header(rv.headers['Content-Disposition']) - assert value == 'attachment' - assert options['filename'] == 'index.html' - assert 'filename*' not in rv.headers['Content-Disposition'] + with open(os.path.join(app.root_path, "static/index.html")) as f: + rv = flask.send_file( + f, as_attachment=True, attachment_filename="index.html" + ) + value, options = parse_options_header(rv.headers["Content-Disposition"]) + assert value == "attachment" + assert options["filename"] == "index.html" + assert "filename*" not in rv.headers["Content-Disposition"] rv.close() - rv = flask.send_file('static/index.html', as_attachment=True) - value, options = parse_options_header(rv.headers['Content-Disposition']) - assert value == 'attachment' - assert options['filename'] == 'index.html' + rv = flask.send_file("static/index.html", as_attachment=True) + value, options = parse_options_header(rv.headers["Content-Disposition"]) + assert value == "attachment" + assert options["filename"] == "index.html" rv.close() - rv = flask.send_file(StringIO('Test'), as_attachment=True, - attachment_filename='index.txt', - add_etags=False) - assert rv.mimetype == 'text/plain' - value, options = parse_options_header(rv.headers['Content-Disposition']) - assert value == 'attachment' - assert options['filename'] == 'index.txt' + rv = flask.send_file( + StringIO("Test"), + as_attachment=True, + attachment_filename="index.txt", + add_etags=False, + ) + assert rv.mimetype == "text/plain" + value, options = parse_options_header(rv.headers["Content-Disposition"]) + assert value == "attachment" + assert options["filename"] == "index.txt" rv.close() - @pytest.mark.usefixtures('req_ctx') - @pytest.mark.parametrize(('filename', 'ascii', 'utf8'), ( - ('index.html', 'index.html', False), - (u'Ñandú/pingüino.txt', '"Nandu/pinguino.txt"', - '%C3%91and%C3%BA%EF%BC%8Fping%C3%BCino.txt'), - (u'Vögel.txt', 'Vogel.txt', 'V%C3%B6gel.txt'), - # Native string not marked as Unicode on Python 2 - ('tést.txt', 'test.txt', 't%C3%A9st.txt'), - )) + @pytest.mark.usefixtures("req_ctx") + @pytest.mark.parametrize( + ("filename", "ascii", "utf8"), + ( + ("index.html", "index.html", False), + ( + u"Ñandú/pingüino.txt", + '"Nandu/pinguino.txt"', + "%C3%91and%C3%BA%EF%BC%8Fping%C3%BCino.txt", + ), + (u"Vögel.txt", "Vogel.txt", "V%C3%B6gel.txt"), + # Native string not marked as Unicode on Python 2 + ("tést.txt", "test.txt", "t%C3%A9st.txt"), + ), + ) def test_attachment_filename_encoding(self, filename, ascii, utf8): - rv = flask.send_file('static/index.html', as_attachment=True, attachment_filename=filename) + rv = flask.send_file( + "static/index.html", as_attachment=True, attachment_filename=filename + ) rv.close() - content_disposition = rv.headers['Content-Disposition'] - assert 'filename=%s' % ascii in content_disposition + content_disposition = rv.headers["Content-Disposition"] + assert "filename=%s" % ascii in content_disposition if utf8: assert "filename*=UTF-8''" + utf8 in content_disposition else: @@ -697,31 +749,31 @@ class TestSendfile(object): # default cache timeout is 12 hours # Test with static file handler. - rv = app.send_static_file('index.html') - cc = parse_cache_control_header(rv.headers['Cache-Control']) + rv = app.send_static_file("index.html") + cc = parse_cache_control_header(rv.headers["Cache-Control"]) assert cc.max_age == 12 * 60 * 60 rv.close() # Test again with direct use of send_file utility. - rv = flask.send_file('static/index.html') - cc = parse_cache_control_header(rv.headers['Cache-Control']) + rv = flask.send_file("static/index.html") + cc = parse_cache_control_header(rv.headers["Cache-Control"]) assert cc.max_age == 12 * 60 * 60 rv.close() - app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 3600 + app.config["SEND_FILE_MAX_AGE_DEFAULT"] = 3600 # Test with static file handler. - rv = app.send_static_file('index.html') - cc = parse_cache_control_header(rv.headers['Cache-Control']) + rv = app.send_static_file("index.html") + cc = parse_cache_control_header(rv.headers["Cache-Control"]) assert cc.max_age == 3600 rv.close() # Test again with direct use of send_file utility. - rv = flask.send_file('static/index.html') - cc = parse_cache_control_header(rv.headers['Cache-Control']) + rv = flask.send_file("static/index.html") + cc = parse_cache_control_header(rv.headers["Cache-Control"]) assert cc.max_age == 3600 rv.close() # Test with static file handler. - rv = app.send_static_file(FakePath('index.html')) - cc = parse_cache_control_header(rv.headers['Cache-Control']) + rv = app.send_static_file(FakePath("index.html")) + cc = parse_cache_control_header(rv.headers["Cache-Control"]) assert cc.max_age == 3600 rv.close() @@ -732,77 +784,79 @@ class TestSendfile(object): app = StaticFileApp(__name__) with app.test_request_context(): # Test with static file handler. - rv = app.send_static_file('index.html') - cc = parse_cache_control_header(rv.headers['Cache-Control']) + rv = app.send_static_file("index.html") + cc = parse_cache_control_header(rv.headers["Cache-Control"]) assert cc.max_age == 10 rv.close() # Test again with direct use of send_file utility. - rv = flask.send_file('static/index.html') - cc = parse_cache_control_header(rv.headers['Cache-Control']) + rv = flask.send_file("static/index.html") + cc = parse_cache_control_header(rv.headers["Cache-Control"]) assert cc.max_age == 10 rv.close() def test_send_from_directory(self, app, req_ctx): - app.root_path = os.path.join(os.path.dirname(__file__), - 'test_apps', 'subdomaintestmodule') - rv = flask.send_from_directory('static', 'hello.txt') + app.root_path = os.path.join( + os.path.dirname(__file__), "test_apps", "subdomaintestmodule" + ) + rv = flask.send_from_directory("static", "hello.txt") rv.direct_passthrough = False - assert rv.data.strip() == b'Hello Subdomain' + assert rv.data.strip() == b"Hello Subdomain" rv.close() def test_send_from_directory_pathlike(self, app, req_ctx): - app.root_path = os.path.join(os.path.dirname(__file__), - 'test_apps', 'subdomaintestmodule') - rv = flask.send_from_directory(FakePath('static'), FakePath('hello.txt')) + app.root_path = os.path.join( + os.path.dirname(__file__), "test_apps", "subdomaintestmodule" + ) + rv = flask.send_from_directory(FakePath("static"), FakePath("hello.txt")) rv.direct_passthrough = False - assert rv.data.strip() == b'Hello Subdomain' + assert rv.data.strip() == b"Hello Subdomain" rv.close() def test_send_from_directory_bad_request(self, app, req_ctx): - app.root_path = os.path.join(os.path.dirname(__file__), - 'test_apps', 'subdomaintestmodule') + app.root_path = os.path.join( + os.path.dirname(__file__), "test_apps", "subdomaintestmodule" + ) with pytest.raises(BadRequest): - flask.send_from_directory('static', 'bad\x00') + flask.send_from_directory("static", "bad\x00") class TestUrlFor(object): def test_url_for_with_anchor(self, app, req_ctx): - - @app.route('/') + @app.route("/") def index(): - return '42' + return "42" - assert flask.url_for('index', _anchor='x y') == '/#x%20y' + assert flask.url_for("index", _anchor="x y") == "/#x%20y" def test_url_for_with_scheme(self, app, req_ctx): - - @app.route('/') + @app.route("/") def index(): - return '42' + return "42" - assert flask.url_for('index', _external=True, _scheme='https') == 'https://localhost/' + assert ( + flask.url_for("index", _external=True, _scheme="https") + == "https://localhost/" + ) def test_url_for_with_scheme_not_external(self, app, req_ctx): - - @app.route('/') + @app.route("/") def index(): - return '42' + return "42" - pytest.raises(ValueError, - flask.url_for, - 'index', - _scheme='https') + pytest.raises(ValueError, flask.url_for, "index", _scheme="https") def test_url_for_with_alternating_schemes(self, app, req_ctx): - - @app.route('/') + @app.route("/") def index(): - return '42' + return "42" - assert flask.url_for('index', _external=True) == 'http://localhost/' - assert flask.url_for('index', _external=True, _scheme='https') == 'https://localhost/' - assert flask.url_for('index', _external=True) == 'http://localhost/' + assert flask.url_for("index", _external=True) == "http://localhost/" + assert ( + flask.url_for("index", _external=True, _scheme="https") + == "https://localhost/" + ) + assert flask.url_for("index", _external=True) == "http://localhost/" def test_url_with_method(self, app, req_ctx): from flask.views import MethodView @@ -810,23 +864,20 @@ class TestUrlFor(object): class MyView(MethodView): def get(self, id=None): if id is None: - return 'List' - return 'Get %d' % id + return "List" + return "Get %d" % id def post(self): - return 'Create' + return "Create" - myview = MyView.as_view('myview') - app.add_url_rule('/myview/', methods=['GET'], - view_func=myview) - app.add_url_rule('/myview/', methods=['GET'], - view_func=myview) - app.add_url_rule('/myview/create', methods=['POST'], - view_func=myview) + myview = MyView.as_view("myview") + app.add_url_rule("/myview/", methods=["GET"], view_func=myview) + app.add_url_rule("/myview/", methods=["GET"], view_func=myview) + app.add_url_rule("/myview/create", methods=["POST"], view_func=myview) - assert flask.url_for('myview', _method='GET') == '/myview/' - assert flask.url_for('myview', id=42, _method='GET') == '/myview/42' - assert flask.url_for('myview', _method='POST') == '/myview/create' + assert flask.url_for("myview", _method="GET") == "/myview/" + assert flask.url_for("myview", id=42, _method="GET") == "/myview/42" + assert flask.url_for("myview", _method="POST") == "/myview/create" class TestNoImports(object): @@ -841,42 +892,40 @@ class TestNoImports(object): """ def test_name_with_import_error(self, modules_tmpdir): - modules_tmpdir.join('importerror.py').write('raise NotImplementedError()') + modules_tmpdir.join("importerror.py").write("raise NotImplementedError()") try: - flask.Flask('importerror') + flask.Flask("importerror") except NotImplementedError: - assert False, 'Flask(import_name) is importing import_name.' + assert False, "Flask(import_name) is importing import_name." class TestStreaming(object): def test_streaming_with_context(self, app, client): - - @app.route('/') + @app.route("/") def index(): def generate(): - yield 'Hello ' - yield flask.request.args['name'] - yield '!' + yield "Hello " + yield flask.request.args["name"] + yield "!" return flask.Response(flask.stream_with_context(generate())) - rv = client.get('/?name=World') - assert rv.data == b'Hello World!' + rv = client.get("/?name=World") + assert rv.data == b"Hello World!" def test_streaming_with_context_as_decorator(self, app, client): - - @app.route('/') + @app.route("/") def index(): @flask.stream_with_context def generate(hello): yield hello - yield flask.request.args['name'] - yield '!' + yield flask.request.args["name"] + yield "!" - return flask.Response(generate('Hello ')) + return flask.Response(generate("Hello ")) - rv = client.get('/?name=World') - assert rv.data == b'Hello World!' + rv = client.get("/?name=World") + assert rv.data == b"Hello World!" def test_streaming_with_context_and_custom_close(self, app, client): called = [] @@ -896,53 +945,52 @@ class TestStreaming(object): next = __next__ - @app.route('/') + @app.route("/") def index(): def generate(): - yield 'Hello ' - yield flask.request.args['name'] - yield '!' + yield "Hello " + yield flask.request.args["name"] + yield "!" - return flask.Response(flask.stream_with_context( - Wrapper(generate()))) + return flask.Response(flask.stream_with_context(Wrapper(generate()))) - rv = client.get('/?name=World') - assert rv.data == b'Hello World!' + rv = client.get("/?name=World") + assert rv.data == b"Hello World!" assert called == [42] def test_stream_keeps_session(self, app, client): - @app.route('/') + @app.route("/") def index(): - flask.session['test'] = 'flask' + flask.session["test"] = "flask" @flask.stream_with_context def gen(): - yield flask.session['test'] + yield flask.session["test"] return flask.Response(gen()) - rv = client.get('/') - assert rv.data == b'flask' + rv = client.get("/") + assert rv.data == b"flask" class TestSafeJoin(object): def test_safe_join(self): # Valid combinations of *args and expected joined paths. passing = ( - (('a/b/c',), 'a/b/c'), - (('/', 'a/', 'b/', 'c/'), '/a/b/c'), - (('a', 'b', 'c'), 'a/b/c'), - (('/a', 'b/c'), '/a/b/c'), - (('a/b', 'X/../c'), 'a/b/c'), - (('/a/b', 'c/X/..'), '/a/b/c'), + (("a/b/c",), "a/b/c"), + (("/", "a/", "b/", "c/"), "/a/b/c"), + (("a", "b", "c"), "a/b/c"), + (("/a", "b/c"), "/a/b/c"), + (("a/b", "X/../c"), "a/b/c"), + (("/a/b", "c/X/.."), "/a/b/c"), # If last path is '' add a slash - (('/a/b/c', ''), '/a/b/c/'), + (("/a/b/c", ""), "/a/b/c/"), # Preserve dot slash - (('/a/b/c', './'), '/a/b/c/.'), - (('a/b/c', 'X/..'), 'a/b/c/.'), + (("/a/b/c", "./"), "/a/b/c/."), + (("a/b/c", "X/.."), "a/b/c/."), # Base directory is always considered safe - (('../', 'a/b/c'), '../a/b/c'), - (('/..',), '/..'), + (("../", "a/b/c"), "../a/b/c"), + (("/..",), "/.."), ) for args, expected in passing: @@ -952,44 +1000,52 @@ class TestSafeJoin(object): # Should raise werkzeug.exceptions.NotFound on unsafe joins. failing = ( # path.isabs and ``..'' checks - ('/a', 'b', '/c'), - ('/a', '../b/c'), - ('/a', '..', 'b/c'), + ("/a", "b", "/c"), + ("/a", "../b/c"), + ("/a", "..", "b/c"), # Boundaries violations after path normalization - ('/a', 'b/../b/../../c'), - ('/a', 'b', 'c/../..'), - ('/a', 'b/../../c'), + ("/a", "b/../b/../../c"), + ("/a", "b", "c/../.."), + ("/a", "b/../../c"), ) for args in failing: with pytest.raises(NotFound): print(flask.safe_join(*args)) -class TestHelpers(object): - @pytest.mark.parametrize('debug, expected_flag, expected_default_flag', [ - ('', False, False), - ('0', False, False), - ('False', False, False), - ('No', False, False), - ('True', True, True) - ]) - def test_get_debug_flag(self, monkeypatch, debug, expected_flag, expected_default_flag): - monkeypatch.setenv('FLASK_DEBUG', debug) +class TestHelpers(object): + @pytest.mark.parametrize( + "debug, expected_flag, expected_default_flag", + [ + ("", False, False), + ("0", False, False), + ("False", False, False), + ("No", False, False), + ("True", True, True), + ], + ) + def test_get_debug_flag( + self, monkeypatch, debug, expected_flag, expected_default_flag + ): + monkeypatch.setenv("FLASK_DEBUG", debug) if expected_flag is None: assert get_debug_flag() is None else: assert get_debug_flag() == expected_flag assert get_debug_flag() == expected_default_flag - @pytest.mark.parametrize('env, ref_env, debug', [ - ('', 'production', False), - ('production', 'production', False), - ('development', 'development', True), - ('other', 'other', False), - ]) + @pytest.mark.parametrize( + "env, ref_env, debug", + [ + ("", "production", False), + ("production", "production", False), + ("development", "development", True), + ("other", "other", False), + ], + ) def test_get_env(self, monkeypatch, env, ref_env, debug): - monkeypatch.setenv('FLASK_ENV', env) + monkeypatch.setenv("FLASK_ENV", env) assert get_debug_flag() == debug assert get_env() == ref_env @@ -998,9 +1054,9 @@ class TestHelpers(object): with app.test_request_context(): rv = flask.helpers.make_response() assert rv.status_code == 200 - assert rv.mimetype == 'text/html' + assert rv.mimetype == "text/html" - rv = flask.helpers.make_response('Hello') + rv = flask.helpers.make_response("Hello") assert rv.status_code == 200 - assert rv.data == b'Hello' - assert rv.mimetype == 'text/html' + assert rv.data == b"Hello" + assert rv.mimetype == "text/html" diff --git a/tests/test_instance_config.py b/tests/test_instance_config.py index bc912c64..bc26da53 100644 --- a/tests/test_instance_config.py +++ b/tests/test_instance_config.py @@ -17,112 +17,120 @@ from flask._compat import PY2 def test_explicit_instance_paths(modules_tmpdir): with pytest.raises(ValueError) as excinfo: - flask.Flask(__name__, instance_path='instance') - assert 'must be absolute' in str(excinfo.value) + flask.Flask(__name__, instance_path="instance") + assert "must be absolute" in str(excinfo.value) app = flask.Flask(__name__, instance_path=str(modules_tmpdir)) assert app.instance_path == str(modules_tmpdir) def test_main_module_paths(modules_tmpdir, purge_module): - app = modules_tmpdir.join('main_app.py') + app = modules_tmpdir.join("main_app.py") app.write('import flask\n\napp = flask.Flask("__main__")') - purge_module('main_app') + purge_module("main_app") from main_app import app + here = os.path.abspath(os.getcwd()) - assert app.instance_path == os.path.join(here, 'instance') + assert app.instance_path == os.path.join(here, "instance") def test_uninstalled_module_paths(modules_tmpdir, purge_module): - app = modules_tmpdir.join('config_module_app.py').write( - 'import os\n' - 'import flask\n' - 'here = os.path.abspath(os.path.dirname(__file__))\n' - 'app = flask.Flask(__name__)\n' + app = modules_tmpdir.join("config_module_app.py").write( + "import os\n" + "import flask\n" + "here = os.path.abspath(os.path.dirname(__file__))\n" + "app = flask.Flask(__name__)\n" ) - purge_module('config_module_app') + purge_module("config_module_app") from config_module_app import app - assert app.instance_path == str(modules_tmpdir.join('instance')) + + assert app.instance_path == str(modules_tmpdir.join("instance")) def test_uninstalled_package_paths(modules_tmpdir, purge_module): - app = modules_tmpdir.mkdir('config_package_app') - init = app.join('__init__.py') + app = modules_tmpdir.mkdir("config_package_app") + init = app.join("__init__.py") init.write( - 'import os\n' - 'import flask\n' - 'here = os.path.abspath(os.path.dirname(__file__))\n' - 'app = flask.Flask(__name__)\n' + "import os\n" + "import flask\n" + "here = os.path.abspath(os.path.dirname(__file__))\n" + "app = flask.Flask(__name__)\n" ) - purge_module('config_package_app') + purge_module("config_package_app") from config_package_app import app - assert app.instance_path == str(modules_tmpdir.join('instance')) + + assert app.instance_path == str(modules_tmpdir.join("instance")) -def test_installed_module_paths(modules_tmpdir, modules_tmpdir_prefix, - purge_module, site_packages, limit_loader): - site_packages.join('site_app.py').write( - 'import flask\n' - 'app = flask.Flask(__name__)\n' +def test_installed_module_paths( + modules_tmpdir, modules_tmpdir_prefix, purge_module, site_packages, limit_loader +): + site_packages.join("site_app.py").write( + "import flask\n" "app = flask.Flask(__name__)\n" ) - purge_module('site_app') + purge_module("site_app") from site_app import app - assert app.instance_path == \ - modules_tmpdir.join('var').join('site_app-instance') + + assert app.instance_path == modules_tmpdir.join("var").join("site_app-instance") -def test_installed_package_paths(limit_loader, modules_tmpdir, - modules_tmpdir_prefix, purge_module, - monkeypatch): - installed_path = modules_tmpdir.mkdir('path') +def test_installed_package_paths( + limit_loader, modules_tmpdir, modules_tmpdir_prefix, purge_module, monkeypatch +): + installed_path = modules_tmpdir.mkdir("path") monkeypatch.syspath_prepend(installed_path) - app = installed_path.mkdir('installed_package') - init = app.join('__init__.py') - init.write('import flask\napp = flask.Flask(__name__)') - purge_module('installed_package') + app = installed_path.mkdir("installed_package") + init = app.join("__init__.py") + init.write("import flask\napp = flask.Flask(__name__)") + purge_module("installed_package") from installed_package import app - assert app.instance_path == \ - modules_tmpdir.join('var').join('installed_package-instance') + + assert app.instance_path == modules_tmpdir.join("var").join( + "installed_package-instance" + ) -def test_prefix_package_paths(limit_loader, modules_tmpdir, - modules_tmpdir_prefix, purge_module, - site_packages): - app = site_packages.mkdir('site_package') - init = app.join('__init__.py') - init.write('import flask\napp = flask.Flask(__name__)') - purge_module('site_package') +def test_prefix_package_paths( + limit_loader, modules_tmpdir, modules_tmpdir_prefix, purge_module, site_packages +): + app = site_packages.mkdir("site_package") + init = app.join("__init__.py") + init.write("import flask\napp = flask.Flask(__name__)") + purge_module("site_package") import site_package - assert site_package.app.instance_path == \ - modules_tmpdir.join('var').join('site_package-instance') - -def test_egg_installed_paths(install_egg, modules_tmpdir, - modules_tmpdir_prefix): - modules_tmpdir.mkdir('site_egg').join('__init__.py').write( - 'import flask\n\napp = flask.Flask(__name__)' + assert site_package.app.instance_path == modules_tmpdir.join("var").join( + "site_package-instance" ) - install_egg('site_egg') + + +def test_egg_installed_paths(install_egg, modules_tmpdir, modules_tmpdir_prefix): + modules_tmpdir.mkdir("site_egg").join("__init__.py").write( + "import flask\n\napp = flask.Flask(__name__)" + ) + install_egg("site_egg") try: import site_egg - assert site_egg.app.instance_path == \ - str(modules_tmpdir.join('var/').join('site_egg-instance')) + + assert site_egg.app.instance_path == str( + modules_tmpdir.join("var/").join("site_egg-instance") + ) finally: - if 'site_egg' in sys.modules: - del sys.modules['site_egg'] + if "site_egg" in sys.modules: + del sys.modules["site_egg"] -@pytest.mark.skipif(not PY2, reason='This only works under Python 2.') +@pytest.mark.skipif(not PY2, reason="This only works under Python 2.") def test_meta_path_loader_without_is_package(request, modules_tmpdir): - app = modules_tmpdir.join('unimportable.py') - app.write('import flask\napp = flask.Flask(__name__)') + app = modules_tmpdir.join("unimportable.py") + app.write("import flask\napp = flask.Flask(__name__)") class Loader(object): def find_module(self, name, path=None): diff --git a/tests/test_json_tag.py b/tests/test_json_tag.py index da5e6595..41b1dff3 100644 --- a/tests/test_json_tag.py +++ b/tests/test_json_tag.py @@ -16,18 +16,21 @@ from flask import Markup from flask.json.tag import TaggedJSONSerializer, JSONTag -@pytest.mark.parametrize("data", ( - {' t': (1, 2, 3)}, - {' t__': b'a'}, - {' di': ' di'}, - {'x': (1, 2, 3), 'y': 4}, - (1, 2, 3), - [(1, 2, 3)], - b'\xff', - Markup(''), - uuid4(), - datetime.utcnow().replace(microsecond=0), -)) +@pytest.mark.parametrize( + "data", + ( + {" t": (1, 2, 3)}, + {" t__": b"a"}, + {" di": " di"}, + {"x": (1, 2, 3), "y": 4}, + (1, 2, 3), + [(1, 2, 3)], + b"\xff", + Markup(""), + uuid4(), + datetime.utcnow().replace(microsecond=0), + ), +) def test_dump_load_unchanged(data): s = TaggedJSONSerializer() assert s.loads(s.dumps(data)) == data @@ -35,12 +38,12 @@ def test_dump_load_unchanged(data): def test_duplicate_tag(): class TagDict(JSONTag): - key = ' d' + key = " d" s = TaggedJSONSerializer() pytest.raises(KeyError, s.register, TagDict) s.register(TagDict, force=True, index=0) - assert isinstance(s.tags[' d'], TagDict) + assert isinstance(s.tags[" d"], TagDict) assert isinstance(s.order[0], TagDict) @@ -51,7 +54,7 @@ def test_custom_tag(): class TagFoo(JSONTag): __slots__ = () - key = ' f' + key = " f" def check(self, value): return isinstance(value, Foo) @@ -64,7 +67,7 @@ def test_custom_tag(): s = TaggedJSONSerializer() s.register(TagFoo) - assert s.loads(s.dumps(Foo('bar'))).data == 'bar' + assert s.loads(s.dumps(Foo("bar"))).data == "bar" def test_tag_interface(): @@ -76,10 +79,10 @@ def test_tag_interface(): def test_tag_order(): class Tag1(JSONTag): - key = ' 1' + key = " 1" class Tag2(JSONTag): - key = ' 2' + key = " 2" s = TaggedJSONSerializer() diff --git a/tests/test_logging.py b/tests/test_logging.py index 7577ecec..124cc0a3 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -13,8 +13,7 @@ import sys import pytest from flask._compat import StringIO -from flask.logging import default_handler, has_level_handler, \ - wsgi_errors_stream +from flask.logging import default_handler, has_level_handler, wsgi_errors_stream @pytest.fixture(autouse=True) @@ -23,12 +22,11 @@ def reset_logging(pytestconfig): logging.root.handlers = [] root_level = logging.root.level - logger = logging.getLogger('flask.app') + logger = logging.getLogger("flask.app") logger.handlers = [] logger.setLevel(logging.NOTSET) - logging_plugin = pytestconfig.pluginmanager.unregister( - name='logging-plugin') + logging_plugin = pytestconfig.pluginmanager.unregister(name="logging-plugin") yield @@ -39,11 +37,11 @@ def reset_logging(pytestconfig): logger.setLevel(logging.NOTSET) if logging_plugin: - pytestconfig.pluginmanager.register(logging_plugin, 'logging-plugin') + pytestconfig.pluginmanager.register(logging_plugin, "logging-plugin") def test_logger(app): - assert app.logger.name == 'flask.app' + assert app.logger.name == "flask.app" assert app.logger.level == logging.NOTSET assert app.logger.handlers == [default_handler] @@ -61,14 +59,14 @@ def test_existing_handler(app): def test_wsgi_errors_stream(app, client): - @app.route('/') + @app.route("/") def index(): - app.logger.error('test') - return '' + app.logger.error("test") + return "" stream = StringIO() - client.get('/', errors_stream=stream) - assert 'ERROR in test_logging: test' in stream.getvalue() + client.get("/", errors_stream=stream) + assert "ERROR in test_logging: test" in stream.getvalue() assert wsgi_errors_stream._get_current_object() is sys.stderr @@ -77,7 +75,7 @@ def test_wsgi_errors_stream(app, client): def test_has_level_handler(): - logger = logging.getLogger('flask.app') + logger = logging.getLogger("flask.app") assert not has_level_handler(logger) handler = logging.StreamHandler() @@ -93,15 +91,15 @@ def test_has_level_handler(): def test_log_view_exception(app, client): - @app.route('/') + @app.route("/") def index(): - raise Exception('test') + raise Exception("test") app.testing = False stream = StringIO() - rv = client.get('/', errors_stream=stream) + rv = client.get("/", errors_stream=stream) assert rv.status_code == 500 assert rv.data err = stream.getvalue() - assert 'Exception on / [GET]' in err - assert 'Exception: test' in err + assert "Exception on / [GET]" in err + assert "Exception: test" in err diff --git a/tests/test_regression.py b/tests/test_regression.py index d0765354..b87a338c 100644 --- a/tests/test_regression.py +++ b/tests/test_regression.py @@ -22,7 +22,6 @@ _gc_lock = threading.Lock() class assert_no_leak(object): - def __enter__(self): gc.disable() _gc_lock.acquire() @@ -32,7 +31,7 @@ class assert_no_leak(object): # This is necessary since Python only starts tracking # dicts if they contain mutable objects. It's a horrible, # horrible hack but makes this kinda testable. - loc.__storage__['FOOO'] = [1, 2, 3] + loc.__storage__["FOOO"] = [1, 2, 3] gc.collect() self.old_objects = len(gc.get_objects()) @@ -41,7 +40,7 @@ class assert_no_leak(object): gc.collect() new_objects = len(gc.get_objects()) if new_objects > self.old_objects: - pytest.fail('Example code leaked') + pytest.fail("Example code leaked") _gc_lock.release() gc.enable() @@ -49,22 +48,21 @@ class assert_no_leak(object): def test_memory_consumption(): app = flask.Flask(__name__) - @app.route('/') + @app.route("/") def index(): - return flask.render_template('simple_template.html', whiskey=42) + return flask.render_template("simple_template.html", whiskey=42) def fire(): with app.test_client() as c: - rv = c.get('/') + rv = c.get("/") assert rv.status_code == 200 - assert rv.data == b'

42

' + assert rv.data == b"

42

" # Trigger caches fire() # This test only works on CPython 2.7. - if sys.version_info >= (2, 7) and \ - not hasattr(sys, 'pypy_translation_info'): + if sys.version_info >= (2, 7) and not hasattr(sys, "pypy_translation_info"): with assert_no_leak(): for x in range(10): fire() @@ -72,8 +70,9 @@ def test_memory_consumption(): def test_safe_join_toplevel_pardir(): from flask.helpers import safe_join + with pytest.raises(NotFound): - safe_join('/foo', '..') + safe_join("/foo", "..") def test_aborting(app): @@ -84,16 +83,16 @@ def test_aborting(app): def handle_foo(e): return str(e.whatever) - @app.route('/') + @app.route("/") def index(): - raise flask.abort(flask.redirect(flask.url_for('test'))) + raise flask.abort(flask.redirect(flask.url_for("test"))) - @app.route('/test') + @app.route("/test") def test(): raise Foo() with app.test_client() as c: - rv = c.get('/') - assert rv.headers['Location'] == 'http://localhost/test' - rv = c.get('/test') - assert rv.data == b'42' + rv = c.get("/") + assert rv.headers["Location"] == "http://localhost/test" + rv = c.get("/test") + assert rv.data == b"42" diff --git a/tests/test_reqctx.py b/tests/test_reqctx.py index 6cb9c37d..7ac5c370 100644 --- a/tests/test_reqctx.py +++ b/tests/test_reqctx.py @@ -42,7 +42,7 @@ def test_teardown_with_previous_exception(app): buffer.append(exception) try: - raise Exception('dummy') + raise Exception("dummy") except Exception: pass @@ -61,35 +61,39 @@ def test_teardown_with_handled_exception(app): with app.test_request_context(): assert buffer == [] try: - raise Exception('dummy') + raise Exception("dummy") except Exception: pass assert buffer == [None] def test_proper_test_request_context(app): - app.config.update( - SERVER_NAME='localhost.localdomain:5000' - ) + app.config.update(SERVER_NAME="localhost.localdomain:5000") - @app.route('/') + @app.route("/") def index(): return None - @app.route('/', subdomain='foo') + @app.route("/", subdomain="foo") def sub(): return None - with app.test_request_context('/'): - assert flask.url_for('index', _external=True) == \ - 'http://localhost.localdomain:5000/' + with app.test_request_context("/"): + assert ( + flask.url_for("index", _external=True) + == "http://localhost.localdomain:5000/" + ) - with app.test_request_context('/'): - assert flask.url_for('sub', _external=True) == \ - 'http://foo.localhost.localdomain:5000/' + with app.test_request_context("/"): + assert ( + flask.url_for("sub", _external=True) + == "http://foo.localhost.localdomain:5000/" + ) try: - with app.test_request_context('/', environ_overrides={'HTTP_HOST': 'localhost'}): + with app.test_request_context( + "/", environ_overrides={"HTTP_HOST": "localhost"} + ): pass except ValueError as e: assert str(e) == ( @@ -98,28 +102,30 @@ def test_proper_test_request_context(app): "server name from the WSGI environment ('localhost')" ) - app.config.update(SERVER_NAME='localhost') - with app.test_request_context('/', environ_overrides={'SERVER_NAME': 'localhost'}): + app.config.update(SERVER_NAME="localhost") + with app.test_request_context("/", environ_overrides={"SERVER_NAME": "localhost"}): pass - app.config.update(SERVER_NAME='localhost:80') - with app.test_request_context('/', environ_overrides={'SERVER_NAME': 'localhost:80'}): + app.config.update(SERVER_NAME="localhost:80") + with app.test_request_context( + "/", environ_overrides={"SERVER_NAME": "localhost:80"} + ): pass def test_context_binding(app): - @app.route('/') + @app.route("/") def index(): - return 'Hello %s!' % flask.request.args['name'] + return "Hello %s!" % flask.request.args["name"] - @app.route('/meh') + @app.route("/meh") def meh(): return flask.request.url - with app.test_request_context('/?name=World'): - assert index() == 'Hello World!' - with app.test_request_context('/meh'): - assert meh() == 'http://localhost/meh' + with app.test_request_context("/?name=World"): + assert index() == "Hello World!" + with app.test_request_context("/meh"): + assert meh() == "http://localhost/meh" assert flask._request_ctx_stack.top is None @@ -136,27 +142,26 @@ def test_context_test(app): def test_manual_context_binding(app): - @app.route('/') + @app.route("/") def index(): - return 'Hello %s!' % flask.request.args['name'] + return "Hello %s!" % flask.request.args["name"] - ctx = app.test_request_context('/?name=World') + ctx = app.test_request_context("/?name=World") ctx.push() - assert index() == 'Hello World!' + assert index() == "Hello World!" ctx.pop() with pytest.raises(RuntimeError): index() -@pytest.mark.skipif(greenlet is None, reason='greenlet not installed') +@pytest.mark.skipif(greenlet is None, reason="greenlet not installed") class TestGreenletContextCopying(object): - def test_greenlet_context_copying(self, app, client): greenlets = [] - @app.route('/') + @app.route("/") def index(): - flask.session['fizz'] = 'buzz' + flask.session["fizz"] = "buzz" reqctx = flask._request_ctx_stack.top.copy() def g(): @@ -165,17 +170,17 @@ class TestGreenletContextCopying(object): with reqctx: assert flask.request assert flask.current_app == app - assert flask.request.path == '/' - assert flask.request.args['foo'] == 'bar' - assert flask.session.get('fizz') == 'buzz' + assert flask.request.path == "/" + assert flask.request.args["foo"] == "bar" + assert flask.session.get("fizz") == "buzz" assert not flask.request return 42 greenlets.append(greenlet(g)) - return 'Hello World!' + return "Hello World!" - rv = client.get('/?foo=bar') - assert rv.data == b'Hello World!' + rv = client.get("/?foo=bar") + assert rv.data == b"Hello World!" result = greenlets[0].run() assert result == 42 @@ -183,25 +188,25 @@ class TestGreenletContextCopying(object): def test_greenlet_context_copying_api(self, app, client): greenlets = [] - @app.route('/') + @app.route("/") def index(): - flask.session['fizz'] = 'buzz' + flask.session["fizz"] = "buzz" reqctx = flask._request_ctx_stack.top.copy() @flask.copy_current_request_context def g(): assert flask.request assert flask.current_app == app - assert flask.request.path == '/' - assert flask.request.args['foo'] == 'bar' - assert flask.session.get('fizz') == 'buzz' + assert flask.request.path == "/" + assert flask.request.args["foo"] == "bar" + assert flask.session.get("fizz") == "buzz" return 42 greenlets.append(greenlet(g)) - return 'Hello World!' + return "Hello World!" - rv = client.get('/?foo=bar') - assert rv.data == b'Hello World!' + rv = client.get("/?foo=bar") + assert rv.data == b"Hello World!" result = greenlets[0].run() assert result == 42 @@ -220,12 +225,12 @@ def test_session_error_pops_context(): app = CustomFlask(__name__) - @app.route('/') + @app.route("/") def index(): # shouldn't get here assert False - response = app.test_client().get('/') + response = app.test_client().get("/") assert response.status_code == 500 assert not flask.request assert not flask.current_app @@ -239,11 +244,12 @@ def test_bad_environ_raises_bad_request(): # However it works when actually passed to the server. from flask.testing import make_test_environ_builder + builder = make_test_environ_builder(app) environ = builder.get_environ() # use a non-printable character in the Host - this is key to this test - environ['HTTP_HOST'] = u'\x8a' + environ["HTTP_HOST"] = u"\x8a" with app.request_context(environ): response = app.full_dispatch_request() @@ -253,20 +259,21 @@ def test_bad_environ_raises_bad_request(): def test_environ_for_valid_idna_completes(): app = flask.Flask(__name__) - @app.route('/') + @app.route("/") def index(): - return 'Hello World!' + return "Hello World!" # We cannot use app.test_client() for the Unicode-rich Host header, # because werkzeug enforces latin1 on Python 2. # However it works when actually passed to the server. from flask.testing import make_test_environ_builder + builder = make_test_environ_builder(app) environ = builder.get_environ() # these characters are all IDNA-compatible - environ['HTTP_HOST'] = u'ąśźäüжŠßя.com' + environ["HTTP_HOST"] = u"ąśźäüжŠßя.com" with app.request_context(environ): response = app.full_dispatch_request() @@ -277,9 +284,9 @@ def test_environ_for_valid_idna_completes(): def test_normal_environ_completes(): app = flask.Flask(__name__) - @app.route('/') + @app.route("/") def index(): - return 'Hello World!' + return "Hello World!" - response = app.test_client().get('/', headers={'host': 'xn--on-0ia.com'}) + response = app.test_client().get("/", headers={"host": "xn--on-0ia.com"}) assert response.status_code == 200 diff --git a/tests/test_signals.py b/tests/test_signals.py index fccd8e2f..2de7575f 100644 --- a/tests/test_signals.py +++ b/tests/test_signals.py @@ -19,15 +19,14 @@ except ImportError: import flask pytestmark = pytest.mark.skipif( - blinker is None, - reason='Signals require the blinker library.' + blinker is None, reason="Signals require the blinker library." ) def test_template_rendered(app, client): - @app.route('/') + @app.route("/") def index(): - return flask.render_template('simple_template.html', whiskey=42) + return flask.render_template("simple_template.html", whiskey=42) recorded = [] @@ -36,11 +35,11 @@ def test_template_rendered(app, client): flask.template_rendered.connect(record, app) try: - client.get('/') + client.get("/") assert len(recorded) == 1 template, context = recorded[0] - assert template.name == 'simple_template.html' - assert context['whiskey'] == 42 + assert template.name == "simple_template.html" + assert context["whiskey"] == 42 finally: flask.template_rendered.disconnect(record, app) @@ -48,24 +47,24 @@ def test_template_rendered(app, client): def test_before_render_template(): app = flask.Flask(__name__) - @app.route('/') + @app.route("/") def index(): - return flask.render_template('simple_template.html', whiskey=42) + return flask.render_template("simple_template.html", whiskey=42) recorded = [] def record(sender, template, context): - context['whiskey'] = 43 + context["whiskey"] = 43 recorded.append((template, context)) flask.before_render_template.connect(record, app) try: - rv = app.test_client().get('/') + rv = app.test_client().get("/") assert len(recorded) == 1 template, context = recorded[0] - assert template.name == 'simple_template.html' - assert context['whiskey'] == 43 - assert rv.data == b'

43

' + assert template.name == "simple_template.html" + assert context["whiskey"] == 43 + assert rv.data == b"

43

" finally: flask.before_render_template.disconnect(record, app) @@ -75,36 +74,41 @@ def test_request_signals(): calls = [] def before_request_signal(sender): - calls.append('before-signal') + calls.append("before-signal") def after_request_signal(sender, response): - assert response.data == b'stuff' - calls.append('after-signal') + assert response.data == b"stuff" + calls.append("after-signal") @app.before_request def before_request_handler(): - calls.append('before-handler') + calls.append("before-handler") @app.after_request def after_request_handler(response): - calls.append('after-handler') - response.data = 'stuff' + calls.append("after-handler") + response.data = "stuff" return response - @app.route('/') + @app.route("/") def index(): - calls.append('handler') - return 'ignored anyway' + calls.append("handler") + return "ignored anyway" flask.request_started.connect(before_request_signal, app) flask.request_finished.connect(after_request_signal, app) try: - rv = app.test_client().get('/') - assert rv.data == b'stuff' + rv = app.test_client().get("/") + assert rv.data == b"stuff" - assert calls == ['before-signal', 'before-handler', 'handler', - 'after-handler', 'after-signal'] + assert calls == [ + "before-signal", + "before-handler", + "handler", + "after-handler", + "after-signal", + ] finally: flask.request_started.disconnect(before_request_signal, app) flask.request_finished.disconnect(after_request_signal, app) @@ -114,7 +118,7 @@ def test_request_exception_signal(): app = flask.Flask(__name__) recorded = [] - @app.route('/') + @app.route("/") def index(): 1 // 0 @@ -123,7 +127,7 @@ def test_request_exception_signal(): flask.got_request_exception.connect(record, app) try: - assert app.test_client().get('/').status_code == 500 + assert app.test_client().get("/").status_code == 500 assert len(recorded) == 1 assert isinstance(recorded[0], ZeroDivisionError) finally: @@ -135,33 +139,33 @@ def test_appcontext_signals(): recorded = [] def record_push(sender, **kwargs): - recorded.append('push') + recorded.append("push") def record_pop(sender, **kwargs): - recorded.append('pop') + recorded.append("pop") - @app.route('/') + @app.route("/") def index(): - return 'Hello' + return "Hello" flask.appcontext_pushed.connect(record_push, app) flask.appcontext_popped.connect(record_pop, app) try: with app.test_client() as c: - rv = c.get('/') - assert rv.data == b'Hello' - assert recorded == ['push'] - assert recorded == ['push', 'pop'] + rv = c.get("/") + assert rv.data == b"Hello" + assert recorded == ["push"] + assert recorded == ["push", "pop"] finally: flask.appcontext_pushed.disconnect(record_push, app) flask.appcontext_popped.disconnect(record_pop, app) def test_flash_signal(app): - @app.route('/') + @app.route("/") def index(): - flask.flash('This is a flash message', category='notice') - return flask.redirect('/other') + flask.flash("This is a flash message", category="notice") + return flask.redirect("/other") recorded = [] @@ -172,11 +176,11 @@ def test_flash_signal(app): try: client = app.test_client() with client.session_transaction(): - client.get('/') + client.get("/") assert len(recorded) == 1 message, category = recorded[0] - assert message == 'This is a flash message' - assert category == 'notice' + assert message == "This is a flash message" + assert category == "notice" finally: flask.message_flashed.disconnect(record, app) @@ -186,18 +190,18 @@ def test_appcontext_tearing_down_signal(): recorded = [] def record_teardown(sender, **kwargs): - recorded.append(('tear_down', kwargs)) + recorded.append(("tear_down", kwargs)) - @app.route('/') + @app.route("/") def index(): 1 // 0 flask.appcontext_tearing_down.connect(record_teardown, app) try: with app.test_client() as c: - rv = c.get('/') + rv = c.get("/") assert rv.status_code == 500 assert recorded == [] - assert recorded == [('tear_down', {'exc': None})] + assert recorded == [("tear_down", {"exc": None})] finally: flask.appcontext_tearing_down.disconnect(record_teardown, app) diff --git a/tests/test_subclassing.py b/tests/test_subclassing.py index 43a1625a..e317601b 100644 --- a/tests/test_subclassing.py +++ b/tests/test_subclassing.py @@ -23,11 +23,11 @@ def test_suppressed_exception_logging(): out = StringIO() app = SuppressedFlask(__name__) - @app.route('/') + @app.route("/") def index(): - raise Exception('test') + raise Exception("test") - rv = app.test_client().get('/', errors_stream=out) + rv = app.test_client().get("/", errors_stream=out) assert rv.status_code == 500 - assert b'Internal Server Error' in rv.data + assert b"Internal Server Error" in rv.data assert not out.getvalue() diff --git a/tests/test_templating.py b/tests/test_templating.py index 5073ae46..9620a7df 100644 --- a/tests/test_templating.py +++ b/tests/test_templating.py @@ -20,102 +20,104 @@ import werkzeug.serving def test_context_processing(app, client): @app.context_processor def context_processor(): - return {'injected_value': 42} + return {"injected_value": 42} - @app.route('/') + @app.route("/") def index(): - return flask.render_template('context_template.html', value=23) + return flask.render_template("context_template.html", value=23) - rv = client.get('/') - assert rv.data == b'

23|42' + rv = client.get("/") + assert rv.data == b"

23|42" def test_original_win(app, client): - @app.route('/') + @app.route("/") def index(): - return flask.render_template_string('{{ config }}', config=42) + return flask.render_template_string("{{ config }}", config=42) - rv = client.get('/') - assert rv.data == b'42' + rv = client.get("/") + assert rv.data == b"42" def test_request_less_rendering(app, app_ctx): - app.config['WORLD_NAME'] = 'Special World' + app.config["WORLD_NAME"] = "Special World" @app.context_processor def context_processor(): return dict(foo=42) - rv = flask.render_template_string('Hello {{ config.WORLD_NAME }} ' - '{{ foo }}') - assert rv == 'Hello Special World 42' + rv = flask.render_template_string("Hello {{ config.WORLD_NAME }} " "{{ foo }}") + assert rv == "Hello Special World 42" def test_standard_context(app, client): - @app.route('/') + @app.route("/") def index(): flask.g.foo = 23 - flask.session['test'] = 'aha' - return flask.render_template_string(''' + flask.session["test"] = "aha" + return flask.render_template_string( + """ {{ request.args.foo }} {{ g.foo }} {{ config.DEBUG }} {{ session.test }} - ''') + """ + ) - rv = client.get('/?foo=42') - assert rv.data.split() == [b'42', b'23', b'False', b'aha'] + rv = client.get("/?foo=42") + assert rv.data.split() == [b"42", b"23", b"False", b"aha"] def test_escaping(app, client): - text = '

Hello World!' + text = "

Hello World!" - @app.route('/') + @app.route("/") def index(): - return flask.render_template('escaping_template.html', text=text, - html=flask.Markup(text)) + return flask.render_template( + "escaping_template.html", text=text, html=flask.Markup(text) + ) - lines = client.get('/').data.splitlines() + lines = client.get("/").data.splitlines() assert lines == [ - b'<p>Hello World!', - b'

Hello World!', - b'

Hello World!', - b'

Hello World!', - b'<p>Hello World!', - b'

Hello World!' + b"<p>Hello World!", + b"

Hello World!", + b"

Hello World!", + b"

Hello World!", + b"<p>Hello World!", + b"

Hello World!", ] def test_no_escaping(app, client): - text = '

Hello World!' + text = "

Hello World!" - @app.route('/') + @app.route("/") def index(): - return flask.render_template('non_escaping_template.txt', text=text, - html=flask.Markup(text)) + return flask.render_template( + "non_escaping_template.txt", text=text, html=flask.Markup(text) + ) - lines = client.get('/').data.splitlines() + lines = client.get("/").data.splitlines() assert lines == [ - b'

Hello World!', - b'

Hello World!', - b'

Hello World!', - b'

Hello World!', - b'<p>Hello World!', - b'

Hello World!', - b'

Hello World!', - b'

Hello World!' + b"

Hello World!", + b"

Hello World!", + b"

Hello World!", + b"

Hello World!", + b"<p>Hello World!", + b"

Hello World!", + b"

Hello World!", + b"

Hello World!", ] def test_escaping_without_template_filename(app, client, req_ctx): - assert flask.render_template_string( - '{{ foo }}', foo='') == '<test>' - assert flask.render_template('mail.txt', foo='') == ' Mail' + assert flask.render_template_string("{{ foo }}", foo="") == "<test>" + assert flask.render_template("mail.txt", foo="") == " Mail" def test_macros(app, req_ctx): - macro = flask.get_template_attribute('_macro.html', 'hello') - assert macro('World') == 'Hello World!' + macro = flask.get_template_attribute("_macro.html", "hello") + assert macro("World") == "Hello World!" def test_template_filter(app): @@ -123,9 +125,9 @@ def test_template_filter(app): def my_reverse(s): return s[::-1] - assert 'my_reverse' in app.jinja_env.filters.keys() - assert app.jinja_env.filters['my_reverse'] == my_reverse - assert app.jinja_env.filters['my_reverse']('abcd') == 'dcba' + assert "my_reverse" in app.jinja_env.filters.keys() + assert app.jinja_env.filters["my_reverse"] == my_reverse + assert app.jinja_env.filters["my_reverse"]("abcd") == "dcba" def test_add_template_filter(app): @@ -133,29 +135,29 @@ def test_add_template_filter(app): return s[::-1] app.add_template_filter(my_reverse) - assert 'my_reverse' in app.jinja_env.filters.keys() - assert app.jinja_env.filters['my_reverse'] == my_reverse - assert app.jinja_env.filters['my_reverse']('abcd') == 'dcba' + assert "my_reverse" in app.jinja_env.filters.keys() + assert app.jinja_env.filters["my_reverse"] == my_reverse + assert app.jinja_env.filters["my_reverse"]("abcd") == "dcba" def test_template_filter_with_name(app): - @app.template_filter('strrev') + @app.template_filter("strrev") def my_reverse(s): return s[::-1] - assert 'strrev' in app.jinja_env.filters.keys() - assert app.jinja_env.filters['strrev'] == my_reverse - assert app.jinja_env.filters['strrev']('abcd') == 'dcba' + assert "strrev" in app.jinja_env.filters.keys() + assert app.jinja_env.filters["strrev"] == my_reverse + assert app.jinja_env.filters["strrev"]("abcd") == "dcba" def test_add_template_filter_with_name(app): def my_reverse(s): return s[::-1] - app.add_template_filter(my_reverse, 'strrev') - assert 'strrev' in app.jinja_env.filters.keys() - assert app.jinja_env.filters['strrev'] == my_reverse - assert app.jinja_env.filters['strrev']('abcd') == 'dcba' + app.add_template_filter(my_reverse, "strrev") + assert "strrev" in app.jinja_env.filters.keys() + assert app.jinja_env.filters["strrev"] == my_reverse + assert app.jinja_env.filters["strrev"]("abcd") == "dcba" def test_template_filter_with_template(app, client): @@ -163,12 +165,12 @@ def test_template_filter_with_template(app, client): def super_reverse(s): return s[::-1] - @app.route('/') + @app.route("/") def index(): - return flask.render_template('template_filter.html', value='abcd') + return flask.render_template("template_filter.html", value="abcd") - rv = client.get('/') - assert rv.data == b'dcba' + rv = client.get("/") + assert rv.data == b"dcba" def test_add_template_filter_with_template(app, client): @@ -177,39 +179,39 @@ def test_add_template_filter_with_template(app, client): app.add_template_filter(super_reverse) - @app.route('/') + @app.route("/") def index(): - return flask.render_template('template_filter.html', value='abcd') + return flask.render_template("template_filter.html", value="abcd") - rv = client.get('/') - assert rv.data == b'dcba' + rv = client.get("/") + assert rv.data == b"dcba" def test_template_filter_with_name_and_template(app, client): - @app.template_filter('super_reverse') + @app.template_filter("super_reverse") def my_reverse(s): return s[::-1] - @app.route('/') + @app.route("/") def index(): - return flask.render_template('template_filter.html', value='abcd') + return flask.render_template("template_filter.html", value="abcd") - rv = client.get('/') - assert rv.data == b'dcba' + rv = client.get("/") + assert rv.data == b"dcba" def test_add_template_filter_with_name_and_template(app, client): def my_reverse(s): return s[::-1] - app.add_template_filter(my_reverse, 'super_reverse') + app.add_template_filter(my_reverse, "super_reverse") - @app.route('/') + @app.route("/") def index(): - return flask.render_template('template_filter.html', value='abcd') + return flask.render_template("template_filter.html", value="abcd") - rv = client.get('/') - assert rv.data == b'dcba' + rv = client.get("/") + assert rv.data == b"dcba" def test_template_test(app): @@ -217,9 +219,9 @@ def test_template_test(app): def boolean(value): return isinstance(value, bool) - assert 'boolean' in app.jinja_env.tests.keys() - assert app.jinja_env.tests['boolean'] == boolean - assert app.jinja_env.tests['boolean'](False) + assert "boolean" in app.jinja_env.tests.keys() + assert app.jinja_env.tests["boolean"] == boolean + assert app.jinja_env.tests["boolean"](False) def test_add_template_test(app): @@ -227,29 +229,29 @@ def test_add_template_test(app): return isinstance(value, bool) app.add_template_test(boolean) - assert 'boolean' in app.jinja_env.tests.keys() - assert app.jinja_env.tests['boolean'] == boolean - assert app.jinja_env.tests['boolean'](False) + assert "boolean" in app.jinja_env.tests.keys() + assert app.jinja_env.tests["boolean"] == boolean + assert app.jinja_env.tests["boolean"](False) def test_template_test_with_name(app): - @app.template_test('boolean') + @app.template_test("boolean") def is_boolean(value): return isinstance(value, bool) - assert 'boolean' in app.jinja_env.tests.keys() - assert app.jinja_env.tests['boolean'] == is_boolean - assert app.jinja_env.tests['boolean'](False) + assert "boolean" in app.jinja_env.tests.keys() + assert app.jinja_env.tests["boolean"] == is_boolean + assert app.jinja_env.tests["boolean"](False) def test_add_template_test_with_name(app): def is_boolean(value): return isinstance(value, bool) - app.add_template_test(is_boolean, 'boolean') - assert 'boolean' in app.jinja_env.tests.keys() - assert app.jinja_env.tests['boolean'] == is_boolean - assert app.jinja_env.tests['boolean'](False) + app.add_template_test(is_boolean, "boolean") + assert "boolean" in app.jinja_env.tests.keys() + assert app.jinja_env.tests["boolean"] == is_boolean + assert app.jinja_env.tests["boolean"](False) def test_template_test_with_template(app, client): @@ -257,12 +259,12 @@ def test_template_test_with_template(app, client): def boolean(value): return isinstance(value, bool) - @app.route('/') + @app.route("/") def index(): - return flask.render_template('template_test.html', value=False) + return flask.render_template("template_test.html", value=False) - rv = client.get('/') - assert b'Success!' in rv.data + rv = client.get("/") + assert b"Success!" in rv.data def test_add_template_test_with_template(app, client): @@ -271,39 +273,39 @@ def test_add_template_test_with_template(app, client): app.add_template_test(boolean) - @app.route('/') + @app.route("/") def index(): - return flask.render_template('template_test.html', value=False) + return flask.render_template("template_test.html", value=False) - rv = client.get('/') - assert b'Success!' in rv.data + rv = client.get("/") + assert b"Success!" in rv.data def test_template_test_with_name_and_template(app, client): - @app.template_test('boolean') + @app.template_test("boolean") def is_boolean(value): return isinstance(value, bool) - @app.route('/') + @app.route("/") def index(): - return flask.render_template('template_test.html', value=False) + return flask.render_template("template_test.html", value=False) - rv = client.get('/') - assert b'Success!' in rv.data + rv = client.get("/") + assert b"Success!" in rv.data def test_add_template_test_with_name_and_template(app, client): def is_boolean(value): return isinstance(value, bool) - app.add_template_test(is_boolean, 'boolean') + app.add_template_test(is_boolean, "boolean") - @app.route('/') + @app.route("/") def index(): - return flask.render_template('template_test.html', value=False) + return flask.render_template("template_test.html", value=False) - rv = client.get('/') - assert b'Success!' in rv.data + rv = client.get("/") + assert b"Success!" in rv.data def test_add_template_global(app, app_ctx): @@ -311,84 +313,89 @@ def test_add_template_global(app, app_ctx): def get_stuff(): return 42 - assert 'get_stuff' in app.jinja_env.globals.keys() - assert app.jinja_env.globals['get_stuff'] == get_stuff - assert app.jinja_env.globals['get_stuff'](), 42 + assert "get_stuff" in app.jinja_env.globals.keys() + assert app.jinja_env.globals["get_stuff"] == get_stuff + assert app.jinja_env.globals["get_stuff"](), 42 - rv = flask.render_template_string('{{ get_stuff() }}') - assert rv == '42' + rv = flask.render_template_string("{{ get_stuff() }}") + assert rv == "42" def test_custom_template_loader(client): class MyFlask(flask.Flask): def create_global_jinja_loader(self): from jinja2 import DictLoader - return DictLoader({'index.html': 'Hello Custom World!'}) + + return DictLoader({"index.html": "Hello Custom World!"}) app = MyFlask(__name__) - @app.route('/') + @app.route("/") def index(): - return flask.render_template('index.html') + return flask.render_template("index.html") c = app.test_client() - rv = c.get('/') - assert rv.data == b'Hello Custom World!' + rv = c.get("/") + assert rv.data == b"Hello Custom World!" def test_iterable_loader(app, client): @app.context_processor def context_processor(): - return {'whiskey': 'Jameson'} + return {"whiskey": "Jameson"} - @app.route('/') + @app.route("/") def index(): return flask.render_template( - ['no_template.xml', # should skip this one - 'simple_template.html', # should render this - 'context_template.html'], - value=23) + [ + "no_template.xml", # should skip this one + "simple_template.html", # should render this + "context_template.html", + ], + value=23, + ) - rv = client.get('/') - assert rv.data == b'

Jameson

' + rv = client.get("/") + assert rv.data == b"

Jameson

" def test_templates_auto_reload(app): # debug is False, config option is None assert app.debug is False - assert app.config['TEMPLATES_AUTO_RELOAD'] is None + assert app.config["TEMPLATES_AUTO_RELOAD"] is None assert app.jinja_env.auto_reload is False # debug is False, config option is False app = flask.Flask(__name__) - app.config['TEMPLATES_AUTO_RELOAD'] = False + app.config["TEMPLATES_AUTO_RELOAD"] = False assert app.debug is False assert app.jinja_env.auto_reload is False # debug is False, config option is True app = flask.Flask(__name__) - app.config['TEMPLATES_AUTO_RELOAD'] = True + app.config["TEMPLATES_AUTO_RELOAD"] = True assert app.debug is False assert app.jinja_env.auto_reload is True # debug is True, config option is None app = flask.Flask(__name__) - app.config['DEBUG'] = True - assert app.config['TEMPLATES_AUTO_RELOAD'] is None + app.config["DEBUG"] = True + assert app.config["TEMPLATES_AUTO_RELOAD"] is None assert app.jinja_env.auto_reload is True # debug is True, config option is False app = flask.Flask(__name__) - app.config['DEBUG'] = True - app.config['TEMPLATES_AUTO_RELOAD'] = False + app.config["DEBUG"] = True + app.config["TEMPLATES_AUTO_RELOAD"] = False assert app.jinja_env.auto_reload is False # debug is True, config option is True app = flask.Flask(__name__) - app.config['DEBUG'] = True - app.config['TEMPLATES_AUTO_RELOAD'] = True + app.config["DEBUG"] = True + app.config["TEMPLATES_AUTO_RELOAD"] = True assert app.jinja_env.auto_reload is True + def test_templates_auto_reload_debug_run(app, monkeypatch): def run_simple_mock(*args, **kwargs): pass - monkeypatch.setattr(werkzeug.serving, 'run_simple', run_simple_mock) + monkeypatch.setattr(werkzeug.serving, "run_simple", run_simple_mock) app.run() assert app.templates_auto_reload == False @@ -409,25 +416,26 @@ def test_template_loader_debugging(test_apps, monkeypatch): called.append(True) text = str(record.msg) assert '1: trying loader of application "blueprintapp"' in text - assert ('2: trying loader of blueprint "admin" ' - '(blueprintapp.apps.admin)') in text - assert ('trying loader of blueprint "frontend" ' - '(blueprintapp.apps.frontend)') in text - assert 'Error: the template could not be found' in text - assert ('looked up from an endpoint that belongs to ' - 'the blueprint "frontend"') in text - assert 'See http://flask.pocoo.org/docs/blueprints/#templates' in text + assert ( + '2: trying loader of blueprint "admin" ' "(blueprintapp.apps.admin)" + ) in text + assert ( + 'trying loader of blueprint "frontend" ' "(blueprintapp.apps.frontend)" + ) in text + assert "Error: the template could not be found" in text + assert ( + "looked up from an endpoint that belongs to " 'the blueprint "frontend"' + ) in text + assert "See http://flask.pocoo.org/docs/blueprints/#templates" in text with app.test_client() as c: - monkeypatch.setitem(app.config, 'EXPLAIN_TEMPLATE_LOADING', True) - monkeypatch.setattr( - logging.getLogger('flask'), 'handlers', [_TestHandler()] - ) + monkeypatch.setitem(app.config, "EXPLAIN_TEMPLATE_LOADING", True) + monkeypatch.setattr(logging.getLogger("flask"), "handlers", [_TestHandler()]) with pytest.raises(TemplateNotFound) as excinfo: - c.get('/missing') + c.get("/missing") - assert 'missing_template.html' in str(excinfo.value) + assert "missing_template.html" in str(excinfo.value) assert len(called) == 1 diff --git a/tests/test_testing.py b/tests/test_testing.py index 14c66324..ad5ed0cb 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -21,166 +21,166 @@ from flask.testing import make_test_environ_builder, FlaskCliRunner def test_environ_defaults_from_config(app, client): - app.config['SERVER_NAME'] = 'example.com:1234' - app.config['APPLICATION_ROOT'] = '/foo' + app.config["SERVER_NAME"] = "example.com:1234" + app.config["APPLICATION_ROOT"] = "/foo" - @app.route('/') + @app.route("/") def index(): return flask.request.url ctx = app.test_request_context() - assert ctx.request.url == 'http://example.com:1234/foo/' + assert ctx.request.url == "http://example.com:1234/foo/" - rv = client.get('/') - assert rv.data == b'http://example.com:1234/foo/' + rv = client.get("/") + assert rv.data == b"http://example.com:1234/foo/" def test_environ_defaults(app, client, app_ctx, req_ctx): - @app.route('/') + @app.route("/") def index(): return flask.request.url ctx = app.test_request_context() - assert ctx.request.url == 'http://localhost/' + assert ctx.request.url == "http://localhost/" with client: - rv = client.get('/') - assert rv.data == b'http://localhost/' + rv = client.get("/") + assert rv.data == b"http://localhost/" def test_environ_base_default(app, client, app_ctx): - @app.route('/') + @app.route("/") def index(): flask.g.user_agent = flask.request.headers["User-Agent"] return flask.request.remote_addr - rv = client.get('/') - assert rv.data == b'127.0.0.1' - assert flask.g.user_agent == 'werkzeug/' + werkzeug.__version__ + rv = client.get("/") + assert rv.data == b"127.0.0.1" + assert flask.g.user_agent == "werkzeug/" + werkzeug.__version__ def test_environ_base_modified(app, client, app_ctx): - @app.route('/') + @app.route("/") def index(): flask.g.user_agent = flask.request.headers["User-Agent"] return flask.request.remote_addr - client.environ_base['REMOTE_ADDR'] = '0.0.0.0' - client.environ_base['HTTP_USER_AGENT'] = 'Foo' - rv = client.get('/') - assert rv.data == b'0.0.0.0' - assert flask.g.user_agent == 'Foo' + client.environ_base["REMOTE_ADDR"] = "0.0.0.0" + client.environ_base["HTTP_USER_AGENT"] = "Foo" + rv = client.get("/") + assert rv.data == b"0.0.0.0" + assert flask.g.user_agent == "Foo" - client.environ_base['REMOTE_ADDR'] = '0.0.0.1' - client.environ_base['HTTP_USER_AGENT'] = 'Bar' - rv = client.get('/') - assert rv.data == b'0.0.0.1' - assert flask.g.user_agent == 'Bar' + client.environ_base["REMOTE_ADDR"] = "0.0.0.1" + client.environ_base["HTTP_USER_AGENT"] = "Bar" + rv = client.get("/") + assert rv.data == b"0.0.0.1" + assert flask.g.user_agent == "Bar" def test_client_open_environ(app, client, request): - @app.route('/index') + @app.route("/index") def index(): return flask.request.remote_addr - builder = make_test_environ_builder(app, path='/index', method='GET') + builder = make_test_environ_builder(app, path="/index", method="GET") request.addfinalizer(builder.close) rv = client.open(builder) - assert rv.data == b'127.0.0.1' + assert rv.data == b"127.0.0.1" environ = builder.get_environ() - client.environ_base['REMOTE_ADDR'] = '127.0.0.2' + client.environ_base["REMOTE_ADDR"] = "127.0.0.2" rv = client.open(environ) - assert rv.data == b'127.0.0.2' + assert rv.data == b"127.0.0.2" def test_specify_url_scheme(app, client): - @app.route('/') + @app.route("/") def index(): return flask.request.url - ctx = app.test_request_context(url_scheme='https') - assert ctx.request.url == 'https://localhost/' + ctx = app.test_request_context(url_scheme="https") + assert ctx.request.url == "https://localhost/" - rv = client.get('/', url_scheme='https') - assert rv.data == b'https://localhost/' + rv = client.get("/", url_scheme="https") + assert rv.data == b"https://localhost/" def test_path_is_url(app): - eb = make_test_environ_builder(app, 'https://example.com/') - assert eb.url_scheme == 'https' - assert eb.host == 'example.com' - assert eb.script_root == '' - assert eb.path == '/' + eb = make_test_environ_builder(app, "https://example.com/") + assert eb.url_scheme == "https" + assert eb.host == "example.com" + assert eb.script_root == "" + assert eb.path == "/" def test_blueprint_with_subdomain(): app = flask.Flask(__name__, subdomain_matching=True) - app.config['SERVER_NAME'] = 'example.com:1234' - app.config['APPLICATION_ROOT'] = '/foo' + app.config["SERVER_NAME"] = "example.com:1234" + app.config["APPLICATION_ROOT"] = "/foo" client = app.test_client() - bp = flask.Blueprint('company', __name__, subdomain='xxx') + bp = flask.Blueprint("company", __name__, subdomain="xxx") - @bp.route('/') + @bp.route("/") def index(): return flask.request.url app.register_blueprint(bp) - ctx = app.test_request_context('/', subdomain='xxx') - assert ctx.request.url == 'http://xxx.example.com:1234/foo/' + ctx = app.test_request_context("/", subdomain="xxx") + assert ctx.request.url == "http://xxx.example.com:1234/foo/" assert ctx.request.blueprint == bp.name - rv = client.get('/', subdomain='xxx') - assert rv.data == b'http://xxx.example.com:1234/foo/' + rv = client.get("/", subdomain="xxx") + assert rv.data == b"http://xxx.example.com:1234/foo/" def test_redirect_keep_session(app, client, app_ctx): - @app.route('/', methods=['GET', 'POST']) + @app.route("/", methods=["GET", "POST"]) def index(): - if flask.request.method == 'POST': - return flask.redirect('/getsession') - flask.session['data'] = 'foo' - return 'index' + if flask.request.method == "POST": + return flask.redirect("/getsession") + flask.session["data"] = "foo" + return "index" - @app.route('/getsession') + @app.route("/getsession") def get_session(): - return flask.session.get('data', '') + return flask.session.get("data", "") with client: - rv = client.get('/getsession') - assert rv.data == b'' + rv = client.get("/getsession") + assert rv.data == b"" - rv = client.get('/') - assert rv.data == b'index' - assert flask.session.get('data') == 'foo' - rv = client.post('/', data={}, follow_redirects=True) - assert rv.data == b'foo' + rv = client.get("/") + assert rv.data == b"index" + assert flask.session.get("data") == "foo" + rv = client.post("/", data={}, follow_redirects=True) + assert rv.data == b"foo" # This support requires a new Werkzeug version - if not hasattr(client, 'redirect_client'): - assert flask.session.get('data') == 'foo' + if not hasattr(client, "redirect_client"): + assert flask.session.get("data") == "foo" - rv = client.get('/getsession') - assert rv.data == b'foo' + rv = client.get("/getsession") + assert rv.data == b"foo" def test_session_transactions(app, client): - @app.route('/') + @app.route("/") def index(): - return text_type(flask.session['foo']) + return text_type(flask.session["foo"]) with client: with client.session_transaction() as sess: assert len(sess) == 0 - sess['foo'] = [42] + sess["foo"] = [42] assert len(sess) == 1 - rv = client.get('/') - assert rv.data == b'[42]' + rv = client.get("/") + assert rv.data == b"[42]" with client.session_transaction() as sess: assert len(sess) == 1 - assert sess['foo'] == [42] + assert sess["foo"] == [42] def test_session_transactions_no_null_sessions(): @@ -191,11 +191,11 @@ def test_session_transactions_no_null_sessions(): with pytest.raises(RuntimeError) as e: with c.session_transaction() as sess: pass - assert 'Session backend did not open a session' in str(e.value) + assert "Session backend did not open a session" in str(e.value) def test_session_transactions_keep_context(app, client, req_ctx): - rv = client.get('/') + rv = client.get("/") req = flask.request._get_current_object() assert req is not None with client.session_transaction(): @@ -207,30 +207,30 @@ def test_session_transaction_needs_cookies(app): with pytest.raises(RuntimeError) as e: with c.session_transaction() as s: pass - assert 'cookies' in str(e.value) + assert "cookies" in str(e.value) def test_test_client_context_binding(app, client): app.testing = False - @app.route('/') + @app.route("/") def index(): flask.g.value = 42 - return 'Hello World!' + return "Hello World!" - @app.route('/other') + @app.route("/other") def other(): 1 // 0 with client: - resp = client.get('/') + resp = client.get("/") assert flask.g.value == 42 - assert resp.data == b'Hello World!' + assert resp.data == b"Hello World!" assert resp.status_code == 200 - resp = client.get('/other') - assert not hasattr(flask.g, 'value') - assert b'Internal Server Error' in resp.data + resp = client.get("/other") + assert not hasattr(flask.g, "value") + assert b"Internal Server Error" in resp.data assert resp.status_code == 500 flask.g.value = 23 @@ -239,17 +239,17 @@ def test_test_client_context_binding(app, client): except (AttributeError, RuntimeError): pass else: - raise AssertionError('some kind of exception expected') + raise AssertionError("some kind of exception expected") def test_reuse_client(client): c = client with c: - assert client.get('/').status_code == 404 + assert client.get("/").status_code == 404 with c: - assert client.get('/').status_code == 404 + assert client.get("/").status_code == 404 def test_test_client_calls_teardown_handlers(app, client): @@ -261,40 +261,40 @@ def test_test_client_calls_teardown_handlers(app, client): with client: assert called == [] - client.get('/') + client.get("/") assert called == [] assert called == [None] del called[:] with client: assert called == [] - client.get('/') + client.get("/") assert called == [] - client.get('/') + client.get("/") assert called == [None] assert called == [None, None] def test_full_url_request(app, client): - @app.route('/action', methods=['POST']) + @app.route("/action", methods=["POST"]) def action(): - return 'x' + return "x" with client: - rv = client.post('http://domain.com/action?vodka=42', data={'gin': 43}) + rv = client.post("http://domain.com/action?vodka=42", data={"gin": 43}) assert rv.status_code == 200 - assert 'gin' in flask.request.form - assert 'vodka' in flask.request.args + assert "gin" in flask.request.form + assert "vodka" in flask.request.args def test_json_request_and_response(app, client): - @app.route('/echo', methods=['POST']) + @app.route("/echo", methods=["POST"]) def echo(): return jsonify(flask.request.get_json()) with client: - json_data = {'drink': {'gin': 1, 'tonic': True}, 'price': 10} - rv = client.post('/echo', json=json_data) + json_data = {"drink": {"gin": 1, "tonic": True}, "price": 10} + rv = client.post("/echo", json=json_data) # Request should be in JSON assert flask.request.is_json @@ -308,38 +308,38 @@ def test_json_request_and_response(app, client): def test_subdomain(): app = flask.Flask(__name__, subdomain_matching=True) - app.config['SERVER_NAME'] = 'example.com' + app.config["SERVER_NAME"] = "example.com" client = app.test_client() - @app.route('/', subdomain='') + @app.route("/", subdomain="") def view(company_id): return company_id with app.test_request_context(): - url = flask.url_for('view', company_id='xxx') + url = flask.url_for("view", company_id="xxx") with client: response = client.get(url) assert 200 == response.status_code - assert b'xxx' == response.data + assert b"xxx" == response.data def test_nosubdomain(app, client): - app.config['SERVER_NAME'] = 'example.com' + app.config["SERVER_NAME"] = "example.com" - @app.route('/') + @app.route("/") def view(company_id): return company_id with app.test_request_context(): - url = flask.url_for('view', company_id='xxx') + url = flask.url_for("view", company_id="xxx") with client: response = client.get(url) assert 200 == response.status_code - assert b'xxx' == response.data + assert b"xxx" == response.data def test_cli_runner_class(app): @@ -355,17 +355,17 @@ def test_cli_runner_class(app): def test_cli_invoke(app): - @app.cli.command('hello') + @app.cli.command("hello") def hello_command(): - click.echo('Hello, World!') + click.echo("Hello, World!") runner = app.test_cli_runner() # invoke with command name - result = runner.invoke(args=['hello']) - assert 'Hello' in result.output + result = runner.invoke(args=["hello"]) + assert "Hello" in result.output # invoke with command object result = runner.invoke(hello_command) - assert 'Hello' in result.output + assert "Hello" in result.output def test_cli_custom_obj(app): @@ -376,9 +376,9 @@ def test_cli_custom_obj(app): NS.called = True return app - @app.cli.command('hello') + @app.cli.command("hello") def hello_command(): - click.echo('Hello, World!') + click.echo("Hello, World!") script_info = ScriptInfo(create_app=create_app) runner = app.test_cli_runner() diff --git a/tests/test_user_error_handler.py b/tests/test_user_error_handler.py index f4a08f58..f9be1eb7 100644 --- a/tests/test_user_error_handler.py +++ b/tests/test_user_error_handler.py @@ -7,40 +7,34 @@ tests.test_user_error_handler :license: BSD, see LICENSE for more details. """ -from werkzeug.exceptions import ( - Forbidden, - InternalServerError, - HTTPException, - NotFound - ) +from werkzeug.exceptions import Forbidden, InternalServerError, HTTPException, NotFound import flask def test_error_handler_no_match(app, client): - class CustomException(Exception): pass @app.errorhandler(CustomException) def custom_exception_handler(e): assert isinstance(e, CustomException) - return 'custom' + return "custom" @app.errorhandler(500) def handle_500(e): return type(e).__name__ - @app.route('/custom') + @app.route("/custom") def custom_test(): raise CustomException() - @app.route('/keyerror') + @app.route("/keyerror") def key_error(): raise KeyError() app.testing = False - assert client.get('/custom').data == b'custom' - assert client.get('/keyerror').data == b'KeyError' + assert client.get("/custom").data == b"custom" + assert client.get("/keyerror").data == b"KeyError" def test_error_handler_subclass(app): @@ -56,30 +50,30 @@ def test_error_handler_subclass(app): @app.errorhandler(ParentException) def parent_exception_handler(e): assert isinstance(e, ParentException) - return 'parent' + return "parent" @app.errorhandler(ChildExceptionRegistered) def child_exception_handler(e): assert isinstance(e, ChildExceptionRegistered) - return 'child-registered' + return "child-registered" - @app.route('/parent') + @app.route("/parent") def parent_test(): raise ParentException() - @app.route('/child-unregistered') + @app.route("/child-unregistered") def unregistered_test(): raise ChildExceptionUnregistered() - @app.route('/child-registered') + @app.route("/child-registered") def registered_test(): raise ChildExceptionRegistered() c = app.test_client() - assert c.get('/parent').data == b'parent' - assert c.get('/child-unregistered').data == b'parent' - assert c.get('/child-registered').data == b'child-registered' + assert c.get("/parent").data == b"parent" + assert c.get("/child-unregistered").data == b"parent" + assert c.get("/child-registered").data == b"child-registered" def test_error_handler_http_subclass(app): @@ -92,78 +86,78 @@ def test_error_handler_http_subclass(app): @app.errorhandler(403) def code_exception_handler(e): assert isinstance(e, Forbidden) - return 'forbidden' + return "forbidden" @app.errorhandler(ForbiddenSubclassRegistered) def subclass_exception_handler(e): assert isinstance(e, ForbiddenSubclassRegistered) - return 'forbidden-registered' + return "forbidden-registered" - @app.route('/forbidden') + @app.route("/forbidden") def forbidden_test(): raise Forbidden() - @app.route('/forbidden-registered') + @app.route("/forbidden-registered") def registered_test(): raise ForbiddenSubclassRegistered() - @app.route('/forbidden-unregistered') + @app.route("/forbidden-unregistered") def unregistered_test(): raise ForbiddenSubclassUnregistered() c = app.test_client() - assert c.get('/forbidden').data == b'forbidden' - assert c.get('/forbidden-unregistered').data == b'forbidden' - assert c.get('/forbidden-registered').data == b'forbidden-registered' + assert c.get("/forbidden").data == b"forbidden" + assert c.get("/forbidden-unregistered").data == b"forbidden" + assert c.get("/forbidden-registered").data == b"forbidden-registered" def test_error_handler_blueprint(app): - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) @bp.errorhandler(500) def bp_exception_handler(e): - return 'bp-error' + return "bp-error" - @bp.route('/error') + @bp.route("/error") def bp_test(): raise InternalServerError() @app.errorhandler(500) def app_exception_handler(e): - return 'app-error' + return "app-error" - @app.route('/error') + @app.route("/error") def app_test(): raise InternalServerError() - app.register_blueprint(bp, url_prefix='/bp') + app.register_blueprint(bp, url_prefix="/bp") c = app.test_client() - assert c.get('/error').data == b'app-error' - assert c.get('/bp/error').data == b'bp-error' + assert c.get("/error").data == b"app-error" + assert c.get("/bp/error").data == b"bp-error" def test_default_error_handler(): - bp = flask.Blueprint('bp', __name__) + bp = flask.Blueprint("bp", __name__) @bp.errorhandler(HTTPException) def bp_exception_handler(e): assert isinstance(e, HTTPException) assert isinstance(e, NotFound) - return 'bp-default' + return "bp-default" @bp.errorhandler(Forbidden) def bp_exception_handler(e): assert isinstance(e, Forbidden) - return 'bp-forbidden' + return "bp-forbidden" - @bp.route('/undefined') + @bp.route("/undefined") def bp_registered_test(): raise NotFound() - @bp.route('/forbidden') + @bp.route("/forbidden") def bp_forbidden_test(): raise Forbidden() @@ -173,14 +167,14 @@ def test_default_error_handler(): def catchall_errorhandler(e): assert isinstance(e, HTTPException) assert isinstance(e, NotFound) - return 'default' + return "default" @app.errorhandler(Forbidden) def catchall_errorhandler(e): assert isinstance(e, Forbidden) - return 'forbidden' + return "forbidden" - @app.route('/forbidden') + @app.route("/forbidden") def forbidden(): raise Forbidden() @@ -188,12 +182,12 @@ def test_default_error_handler(): def slash(): return "slash" - app.register_blueprint(bp, url_prefix='/bp') + app.register_blueprint(bp, url_prefix="/bp") c = app.test_client() - assert c.get('/bp/undefined').data == b'bp-default' - assert c.get('/bp/forbidden').data == b'bp-forbidden' - assert c.get('/undefined').data == b'default' - assert c.get('/forbidden').data == b'forbidden' + assert c.get("/bp/undefined").data == b"bp-default" + assert c.get("/bp/forbidden").data == b"bp-forbidden" + assert c.get("/undefined").data == b"default" + assert c.get("/forbidden").data == b"forbidden" # Don't handle RequestRedirect raised when adding slash. assert c.get("/slash", follow_redirects=True).data == b"slash" diff --git a/tests/test_views.py b/tests/test_views.py index 69bd9131..27240e1d 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -20,33 +20,33 @@ from werkzeug.http import parse_set_header def common_test(app): c = app.test_client() - assert c.get('/').data == b'GET' - assert c.post('/').data == b'POST' - assert c.put('/').status_code == 405 - meths = parse_set_header(c.open('/', method='OPTIONS').headers['Allow']) - assert sorted(meths) == ['GET', 'HEAD', 'OPTIONS', 'POST'] + assert c.get("/").data == b"GET" + assert c.post("/").data == b"POST" + assert c.put("/").status_code == 405 + meths = parse_set_header(c.open("/", method="OPTIONS").headers["Allow"]) + assert sorted(meths) == ["GET", "HEAD", "OPTIONS", "POST"] def test_basic_view(app): class Index(flask.views.View): - methods = ['GET', 'POST'] + methods = ["GET", "POST"] def dispatch_request(self): return flask.request.method - app.add_url_rule('/', view_func=Index.as_view('index')) + app.add_url_rule("/", view_func=Index.as_view("index")) common_test(app) def test_method_based_view(app): class Index(flask.views.MethodView): def get(self): - return 'GET' + return "GET" def post(self): - return 'POST' + return "POST" - app.add_url_rule('/', view_func=Index.as_view('index')) + app.add_url_rule("/", view_func=Index.as_view("index")) common_test(app) @@ -61,40 +61,40 @@ def test_view_patching(app): class Other(Index): def get(self): - return 'GET' + return "GET" def post(self): - return 'POST' + return "POST" - view = Index.as_view('index') + view = Index.as_view("index") view.view_class = Other - app.add_url_rule('/', view_func=view) + app.add_url_rule("/", view_func=view) common_test(app) def test_view_inheritance(app, client): class Index(flask.views.MethodView): def get(self): - return 'GET' + return "GET" def post(self): - return 'POST' + return "POST" class BetterIndex(Index): def delete(self): - return 'DELETE' + return "DELETE" - app.add_url_rule('/', view_func=BetterIndex.as_view('index')) + app.add_url_rule("/", view_func=BetterIndex.as_view("index")) - meths = parse_set_header(client.open('/', method='OPTIONS').headers['Allow']) - assert sorted(meths) == ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST'] + meths = parse_set_header(client.open("/", method="OPTIONS").headers["Allow"]) + assert sorted(meths) == ["DELETE", "GET", "HEAD", "OPTIONS", "POST"] def test_view_decorators(app, client): def add_x_parachute(f): def new_function(*args, **kwargs): resp = flask.make_response(f(*args, **kwargs)) - resp.headers['X-Parachute'] = 'awesome' + resp.headers["X-Parachute"] = "awesome" return resp return new_function @@ -103,12 +103,12 @@ def test_view_decorators(app, client): decorators = [add_x_parachute] def dispatch_request(self): - return 'Awesome' + return "Awesome" - app.add_url_rule('/', view_func=Index.as_view('index')) - rv = client.get('/') - assert rv.headers['X-Parachute'] == 'awesome' - assert rv.data == b'Awesome' + app.add_url_rule("/", view_func=Index.as_view("index")) + rv = client.get("/") + assert rv.headers["X-Parachute"] == "awesome" + assert rv.data == b"Awesome" def test_view_provide_automatic_options_attr(): @@ -118,84 +118,82 @@ def test_view_provide_automatic_options_attr(): provide_automatic_options = False def dispatch_request(self): - return 'Hello World!' + return "Hello World!" - app.add_url_rule('/', view_func=Index1.as_view('index')) + app.add_url_rule("/", view_func=Index1.as_view("index")) c = app.test_client() - rv = c.open('/', method='OPTIONS') + rv = c.open("/", method="OPTIONS") assert rv.status_code == 405 app = flask.Flask(__name__) class Index2(flask.views.View): - methods = ['OPTIONS'] + methods = ["OPTIONS"] provide_automatic_options = True def dispatch_request(self): - return 'Hello World!' + return "Hello World!" - app.add_url_rule('/', view_func=Index2.as_view('index')) + app.add_url_rule("/", view_func=Index2.as_view("index")) c = app.test_client() - rv = c.open('/', method='OPTIONS') - assert sorted(rv.allow) == ['OPTIONS'] + rv = c.open("/", method="OPTIONS") + assert sorted(rv.allow) == ["OPTIONS"] app = flask.Flask(__name__) class Index3(flask.views.View): def dispatch_request(self): - return 'Hello World!' + return "Hello World!" - app.add_url_rule('/', view_func=Index3.as_view('index')) + app.add_url_rule("/", view_func=Index3.as_view("index")) c = app.test_client() - rv = c.open('/', method='OPTIONS') - assert 'OPTIONS' in rv.allow + rv = c.open("/", method="OPTIONS") + assert "OPTIONS" in rv.allow def test_implicit_head(app, client): class Index(flask.views.MethodView): def get(self): - return flask.Response('Blub', headers={ - 'X-Method': flask.request.method - }) + return flask.Response("Blub", headers={"X-Method": flask.request.method}) - app.add_url_rule('/', view_func=Index.as_view('index')) - rv = client.get('/') - assert rv.data == b'Blub' - assert rv.headers['X-Method'] == 'GET' - rv = client.head('/') - assert rv.data == b'' - assert rv.headers['X-Method'] == 'HEAD' + app.add_url_rule("/", view_func=Index.as_view("index")) + rv = client.get("/") + assert rv.data == b"Blub" + assert rv.headers["X-Method"] == "GET" + rv = client.head("/") + assert rv.data == b"" + assert rv.headers["X-Method"] == "HEAD" def test_explicit_head(app, client): class Index(flask.views.MethodView): def get(self): - return 'GET' + return "GET" def head(self): - return flask.Response('', headers={'X-Method': 'HEAD'}) + return flask.Response("", headers={"X-Method": "HEAD"}) - app.add_url_rule('/', view_func=Index.as_view('index')) - rv = client.get('/') - assert rv.data == b'GET' - rv = client.head('/') - assert rv.data == b'' - assert rv.headers['X-Method'] == 'HEAD' + app.add_url_rule("/", view_func=Index.as_view("index")) + rv = client.get("/") + assert rv.data == b"GET" + rv = client.head("/") + assert rv.data == b"" + assert rv.headers["X-Method"] == "HEAD" def test_endpoint_override(app): app.debug = True class Index(flask.views.View): - methods = ['GET', 'POST'] + methods = ["GET", "POST"] def dispatch_request(self): return flask.request.method - app.add_url_rule('/', view_func=Index.as_view('index')) + app.add_url_rule("/", view_func=Index.as_view("index")) with pytest.raises(AssertionError): - app.add_url_rule('/', view_func=Index.as_view('index')) + app.add_url_rule("/", view_func=Index.as_view("index")) # But these tests should still pass. We just log a warning. common_test(app) @@ -204,36 +202,36 @@ def test_endpoint_override(app): def test_multiple_inheritance(app, client): class GetView(flask.views.MethodView): def get(self): - return 'GET' + return "GET" class DeleteView(flask.views.MethodView): def delete(self): - return 'DELETE' + return "DELETE" class GetDeleteView(GetView, DeleteView): pass - app.add_url_rule('/', view_func=GetDeleteView.as_view('index')) + app.add_url_rule("/", view_func=GetDeleteView.as_view("index")) - assert client.get('/').data == b'GET' - assert client.delete('/').data == b'DELETE' - assert sorted(GetDeleteView.methods) == ['DELETE', 'GET'] + assert client.get("/").data == b"GET" + assert client.delete("/").data == b"DELETE" + assert sorted(GetDeleteView.methods) == ["DELETE", "GET"] def test_remove_method_from_parent(app, client): class GetView(flask.views.MethodView): def get(self): - return 'GET' + return "GET" class OtherView(flask.views.MethodView): def post(self): - return 'POST' + return "POST" class View(GetView, OtherView): - methods = ['GET'] + methods = ["GET"] - app.add_url_rule('/', view_func=View.as_view('index')) + app.add_url_rule("/", view_func=View.as_view("index")) - assert client.get('/').data == b'GET' - assert client.post('/').status_code == 405 - assert sorted(View.methods) == ['GET'] + assert client.get("/").data == b"GET" + assert client.post("/").status_code == 405 + assert sorted(View.methods) == ["GET"]