forked from orbit-oss/flask
f-strings everywhere
This commit is contained in:
parent
524fd0bc8c
commit
2ae740dd49
35 changed files with 227 additions and 245 deletions
12
docs/api.rst
12
docs/api.rst
|
|
@ -58,12 +58,12 @@ Incoming Request Data
|
||||||
the following:
|
the following:
|
||||||
|
|
||||||
============= ======================================================
|
============= ======================================================
|
||||||
`path` ``u'/π/page.html'``
|
`path` ``'/π/page.html'``
|
||||||
`full_path` ``u'/π/page.html?x=y'``
|
`full_path` ``'/π/page.html?x=y'``
|
||||||
`script_root` ``u'/myapplication'``
|
`script_root` ``'/myapplication'``
|
||||||
`base_url` ``u'http://www.example.com/myapplication/π/page.html'``
|
`base_url` ``'http://www.example.com/myapplication/π/page.html'``
|
||||||
`url` ``u'http://www.example.com/myapplication/π/page.html?x=y'``
|
`url` ``'http://www.example.com/myapplication/π/page.html?x=y'``
|
||||||
`url_root` ``u'http://www.example.com/myapplication/'``
|
`url_root` ``'http://www.example.com/myapplication/'``
|
||||||
============= ======================================================
|
============= ======================================================
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -596,8 +596,8 @@ your configuration classes::
|
||||||
DB_SERVER = '192.168.1.56'
|
DB_SERVER = '192.168.1.56'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def DATABASE_URI(self): # Note: all caps
|
def DATABASE_URI(self): # Note: all caps
|
||||||
return 'mysql://user@{}/foo'.format(self.DB_SERVER)
|
return f"mysql://user@{self.DB_SERVER}/foo"
|
||||||
|
|
||||||
class ProductionConfig(Config):
|
class ProductionConfig(Config):
|
||||||
"""Uses production database server."""
|
"""Uses production database server."""
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ error messages could be displayed with a red background.
|
||||||
To flash a message with a different category, just use the second argument
|
To flash a message with a different category, just use the second argument
|
||||||
to the :func:`~flask.flash` function::
|
to the :func:`~flask.flash` function::
|
||||||
|
|
||||||
flash(u'Invalid password provided', 'error')
|
flash('Invalid password provided', 'error')
|
||||||
|
|
||||||
Inside the template you then have to tell the
|
Inside the template you then have to tell the
|
||||||
:func:`~flask.get_flashed_messages` function to also return the
|
:func:`~flask.get_flashed_messages` function to also return the
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ write this by having a function that calls into
|
||||||
name and a dot, and by wrapping `view_func` in a `LazyView` as needed. ::
|
name and a dot, and by wrapping `view_func` in a `LazyView` as needed. ::
|
||||||
|
|
||||||
def url(import_name, url_rules=[], **options):
|
def url(import_name, url_rules=[], **options):
|
||||||
view = LazyView('yourapplication.' + import_name)
|
view = LazyView(f"yourapplication.{import_name}")
|
||||||
for url_rule in url_rules:
|
for url_rule in url_rules:
|
||||||
app.add_url_rule(url_rule, view_func=view, **options)
|
app.add_url_rule(url_rule, view_func=view, **options)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,4 +52,4 @@ Example usage::
|
||||||
files = request.files
|
files = request.files
|
||||||
# At this point the hash is fully constructed.
|
# At this point the hash is fully constructed.
|
||||||
checksum = hash.hexdigest()
|
checksum = hash.hexdigest()
|
||||||
return 'Hash was: %s' % checksum
|
return f"Hash was: {checksum}"
|
||||||
|
|
|
||||||
|
|
@ -104,9 +104,9 @@ You can insert entries into the database like this:
|
||||||
Querying is simple as well:
|
Querying is simple as well:
|
||||||
|
|
||||||
>>> User.query.all()
|
>>> User.query.all()
|
||||||
[<User u'admin'>]
|
[<User 'admin'>]
|
||||||
>>> User.query.filter(User.name == 'admin').first()
|
>>> User.query.filter(User.name == 'admin').first()
|
||||||
<User u'admin'>
|
<User 'admin'>
|
||||||
|
|
||||||
.. _SQLAlchemy: https://www.sqlalchemy.org/
|
.. _SQLAlchemy: https://www.sqlalchemy.org/
|
||||||
.. _declarative:
|
.. _declarative:
|
||||||
|
|
@ -200,19 +200,19 @@ SQLAlchemy will automatically commit for us.
|
||||||
To query your database, you use the engine directly or use a connection:
|
To query your database, you use the engine directly or use a connection:
|
||||||
|
|
||||||
>>> users.select(users.c.id == 1).execute().first()
|
>>> users.select(users.c.id == 1).execute().first()
|
||||||
(1, u'admin', u'admin@localhost')
|
(1, 'admin', 'admin@localhost')
|
||||||
|
|
||||||
These results are also dict-like tuples:
|
These results are also dict-like tuples:
|
||||||
|
|
||||||
>>> r = users.select(users.c.id == 1).execute().first()
|
>>> r = users.select(users.c.id == 1).execute().first()
|
||||||
>>> r['name']
|
>>> r['name']
|
||||||
u'admin'
|
'admin'
|
||||||
|
|
||||||
You can also pass strings of SQL statements to the
|
You can also pass strings of SQL statements to the
|
||||||
:meth:`~sqlalchemy.engine.base.Connection.execute` method:
|
:meth:`~sqlalchemy.engine.base.Connection.execute` method:
|
||||||
|
|
||||||
>>> engine.execute('select * from users where id = :1', [1]).first()
|
>>> engine.execute('select * from users where id = :1', [1]).first()
|
||||||
(1, u'admin', u'admin@localhost')
|
(1, 'admin', 'admin@localhost')
|
||||||
|
|
||||||
For more information about SQLAlchemy, head over to the
|
For more information about SQLAlchemy, head over to the
|
||||||
`website <https://www.sqlalchemy.org/>`_.
|
`website <https://www.sqlalchemy.org/>`_.
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ data and to then invoke that function and pass it to a response object::
|
||||||
def generate_large_csv():
|
def generate_large_csv():
|
||||||
def generate():
|
def generate():
|
||||||
for row in iter_all_rows():
|
for row in iter_all_rows():
|
||||||
yield ','.join(row) + '\n'
|
yield f"{','.join(row)}\n"
|
||||||
return Response(generate(), mimetype='text/csv')
|
return Response(generate(), mimetype='text/csv')
|
||||||
|
|
||||||
Each ``yield`` expression is directly sent to the browser. Note though
|
Each ``yield`` expression is directly sent to the browser. Note though
|
||||||
|
|
|
||||||
|
|
@ -142,8 +142,7 @@ Here is the code for that decorator::
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
template_name = template
|
template_name = template
|
||||||
if template_name is None:
|
if template_name is None:
|
||||||
template_name = request.endpoint \
|
template_name = f"'{request.endpoint.replace('.', '/')}.html'"
|
||||||
.replace('.', '/') + '.html'
|
|
||||||
ctx = f(*args, **kwargs)
|
ctx = f(*args, **kwargs)
|
||||||
if ctx is None:
|
if ctx is None:
|
||||||
ctx = {}
|
ctx = {}
|
||||||
|
|
|
||||||
|
|
@ -447,11 +447,11 @@ Here is a basic introduction to how the :class:`~markupsafe.Markup` class works:
|
||||||
|
|
||||||
>>> from markupsafe import Markup
|
>>> from markupsafe import Markup
|
||||||
>>> Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>'
|
>>> Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>'
|
||||||
Markup(u'<strong>Hello <blink>hacker</blink>!</strong>')
|
Markup('<strong>Hello <blink>hacker</blink>!</strong>')
|
||||||
>>> Markup.escape('<blink>hacker</blink>')
|
>>> Markup.escape('<blink>hacker</blink>')
|
||||||
Markup(u'<blink>hacker</blink>')
|
Markup('<blink>hacker</blink>')
|
||||||
>>> Markup('<em>Marked up</em> » HTML').striptags()
|
>>> Markup('<em>Marked up</em> » HTML').striptags()
|
||||||
u'Marked up \xbb HTML'
|
'Marked up \xbb HTML'
|
||||||
|
|
||||||
.. versionchanged:: 0.5
|
.. versionchanged:: 0.5
|
||||||
|
|
||||||
|
|
@ -609,8 +609,8 @@ Werkzeug provides for you::
|
||||||
@app.route('/upload', methods=['GET', 'POST'])
|
@app.route('/upload', methods=['GET', 'POST'])
|
||||||
def upload_file():
|
def upload_file():
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
f = request.files['the_file']
|
file = request.files['the_file']
|
||||||
f.save('/var/www/uploads/' + secure_filename(f.filename))
|
file.save(f"/var/www/uploads/{secure_filename(f.filename)}")
|
||||||
...
|
...
|
||||||
|
|
||||||
For some better examples, checkout the :ref:`uploading-files` pattern.
|
For some better examples, checkout the :ref:`uploading-files` pattern.
|
||||||
|
|
|
||||||
|
|
@ -222,8 +222,8 @@ functions)::
|
||||||
|
|
||||||
@app.context_processor
|
@app.context_processor
|
||||||
def utility_processor():
|
def utility_processor():
|
||||||
def format_price(amount, currency=u'€'):
|
def format_price(amount, currency="€"):
|
||||||
return u'{0:.2f}{1}'.format(amount, currency)
|
return f"{amount:.2f}{currency}"
|
||||||
return dict(format_price=format_price)
|
return dict(format_price=format_price)
|
||||||
|
|
||||||
The context processor above makes the `format_price` function available to all
|
The context processor above makes the `format_price` function available to all
|
||||||
|
|
|
||||||
|
|
@ -165,16 +165,19 @@ invalid credentials. Add this new test function::
|
||||||
def test_login_logout(client):
|
def test_login_logout(client):
|
||||||
"""Make sure login and logout works."""
|
"""Make sure login and logout works."""
|
||||||
|
|
||||||
rv = login(client, flaskr.app.config['USERNAME'], flaskr.app.config['PASSWORD'])
|
username = flaskr.app.config["USERNAME"]
|
||||||
|
password = flaskr.app.config["PASSWORD"]
|
||||||
|
|
||||||
|
rv = login(client, username, password)
|
||||||
assert b'You were logged in' in rv.data
|
assert b'You were logged in' in rv.data
|
||||||
|
|
||||||
rv = logout(client)
|
rv = logout(client)
|
||||||
assert b'You were logged out' in rv.data
|
assert b'You were logged out' in rv.data
|
||||||
|
|
||||||
rv = login(client, flaskr.app.config['USERNAME'] + 'x', flaskr.app.config['PASSWORD'])
|
rv = login(client, f"{username}x", password)
|
||||||
assert b'Invalid username' in rv.data
|
assert b'Invalid username' in rv.data
|
||||||
|
|
||||||
rv = login(client, flaskr.app.config['USERNAME'], flaskr.app.config['PASSWORD'] + 'x')
|
rv = login(client, username, f'{password}x')
|
||||||
assert b'Invalid password' in rv.data
|
assert b'Invalid password' in rv.data
|
||||||
|
|
||||||
Test Adding Messages
|
Test Adding Messages
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,7 @@ it from each view.
|
||||||
).fetchone()
|
).fetchone()
|
||||||
|
|
||||||
if post is None:
|
if post is None:
|
||||||
abort(404, "Post id {0} doesn't exist.".format(id))
|
abort(404, f"Post id {id} doesn't exist.")
|
||||||
|
|
||||||
if check_author and post['author_id'] != g.user['id']:
|
if check_author and post['author_id'] != g.user['id']:
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ write templates to generate the HTML form.
|
||||||
elif db.execute(
|
elif db.execute(
|
||||||
'SELECT id FROM user WHERE username = ?', (username,)
|
'SELECT id FROM user WHERE username = ?', (username,)
|
||||||
).fetchone() is not None:
|
).fetchone() is not None:
|
||||||
error = 'User {} is already registered.'.format(username)
|
error = f"User {username} is already registered."
|
||||||
|
|
||||||
if error is None:
|
if error is None:
|
||||||
db.execute(
|
db.execute(
|
||||||
|
|
|
||||||
|
|
@ -585,7 +585,7 @@ class Flask(_PackageBoundObject):
|
||||||
bool(static_host) == host_matching
|
bool(static_host) == host_matching
|
||||||
), "Invalid static_host/host_matching combination"
|
), "Invalid static_host/host_matching combination"
|
||||||
self.add_url_rule(
|
self.add_url_rule(
|
||||||
self.static_url_path + "/<path:filename>",
|
f"{self.static_url_path}/<path:filename>",
|
||||||
endpoint="static",
|
endpoint="static",
|
||||||
host=static_host,
|
host=static_host,
|
||||||
view_func=self.send_static_file,
|
view_func=self.send_static_file,
|
||||||
|
|
@ -711,7 +711,7 @@ class Flask(_PackageBoundObject):
|
||||||
prefix, package_path = find_package(self.import_name)
|
prefix, package_path = find_package(self.import_name)
|
||||||
if prefix is None:
|
if prefix is None:
|
||||||
return os.path.join(package_path, "instance")
|
return os.path.join(package_path, "instance")
|
||||||
return os.path.join(prefix, "var", self.name + "-instance")
|
return os.path.join(prefix, "var", f"{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
|
"""Opens a resource from the application's instance folder
|
||||||
|
|
@ -1084,10 +1084,11 @@ class Flask(_PackageBoundObject):
|
||||||
|
|
||||||
if blueprint.name in self.blueprints:
|
if blueprint.name in self.blueprints:
|
||||||
assert self.blueprints[blueprint.name] is blueprint, (
|
assert self.blueprints[blueprint.name] is blueprint, (
|
||||||
"A name collision occurred between blueprints %r and %r. Both"
|
"A name collision occurred between blueprints"
|
||||||
' share the same name "%s". Blueprints that are created on the'
|
f" {blueprint!r} and {self.blueprints[blueprint.name]!r}."
|
||||||
" fly need unique names."
|
f" Both share the same name {blueprint.name!r}."
|
||||||
% (blueprint, self.blueprints[blueprint.name], blueprint.name)
|
f" Blueprints that are created on the fly need unique"
|
||||||
|
f" names."
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.blueprints[blueprint.name] = blueprint
|
self.blueprints[blueprint.name] = blueprint
|
||||||
|
|
@ -1209,8 +1210,8 @@ class Flask(_PackageBoundObject):
|
||||||
old_func = self.view_functions.get(endpoint)
|
old_func = self.view_functions.get(endpoint)
|
||||||
if old_func is not None and old_func != view_func:
|
if old_func is not None and old_func != view_func:
|
||||||
raise AssertionError(
|
raise AssertionError(
|
||||||
"View function mapping is overwriting an "
|
"View function mapping is overwriting an existing"
|
||||||
"existing endpoint function: %s" % endpoint
|
f" endpoint function: {endpoint}"
|
||||||
)
|
)
|
||||||
self.view_functions[endpoint] = view_func
|
self.view_functions[endpoint] = view_func
|
||||||
|
|
||||||
|
|
@ -1341,17 +1342,18 @@ class Flask(_PackageBoundObject):
|
||||||
"""
|
"""
|
||||||
if isinstance(code_or_exception, HTTPException): # old broken behavior
|
if isinstance(code_or_exception, HTTPException): # old broken behavior
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"Tried to register a handler for an exception instance {!r}."
|
"Tried to register a handler for an exception instance"
|
||||||
" Handlers can only be registered for exception classes or"
|
f" {code_or_exception!r}. Handlers can only be"
|
||||||
" HTTP error codes.".format(code_or_exception)
|
" registered for exception classes or HTTP error codes."
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
exc_class, code = self._get_exc_class_and_code(code_or_exception)
|
exc_class, code = self._get_exc_class_and_code(code_or_exception)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise KeyError(
|
raise KeyError(
|
||||||
"'{}' is not a recognized HTTP error code. Use a subclass of"
|
f"'{code_or_exception}' is not a recognized HTTP error"
|
||||||
" HTTPException with that code instead.".format(code_or_exception)
|
" code. Use a subclass of HTTPException with that code"
|
||||||
|
" instead."
|
||||||
)
|
)
|
||||||
|
|
||||||
handlers = self.error_handler_spec.setdefault(key, {}).setdefault(code, {})
|
handlers = self.error_handler_spec.setdefault(key, {}).setdefault(code, {})
|
||||||
|
|
@ -1730,7 +1732,7 @@ class Flask(_PackageBoundObject):
|
||||||
# message, add it in manually.
|
# message, add it in manually.
|
||||||
# TODO: clean up once Werkzeug >= 0.15.5 is required
|
# TODO: clean up once Werkzeug >= 0.15.5 is required
|
||||||
if e.args[0] not in e.get_description():
|
if e.args[0] not in e.get_description():
|
||||||
e.description = "KeyError: '{}'".format(*e.args)
|
e.description = f"KeyError: {e.args[0]!r}"
|
||||||
elif not hasattr(BadRequestKeyError, "show_exception"):
|
elif not hasattr(BadRequestKeyError, "show_exception"):
|
||||||
e.args = ()
|
e.args = ()
|
||||||
|
|
||||||
|
|
@ -2006,9 +2008,9 @@ class Flask(_PackageBoundObject):
|
||||||
# the body must not be None
|
# the body must not be None
|
||||||
if rv is None:
|
if rv is None:
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
'The view function for "{}" did not return a valid response. The'
|
f"The view function for {request.endpoint!r} did not"
|
||||||
" function either returned None or ended without a return"
|
" return a valid response. The function either returned"
|
||||||
" statement.".format(request.endpoint)
|
" None or ended without a return statement."
|
||||||
)
|
)
|
||||||
|
|
||||||
# make sure the body is an instance of the response class
|
# make sure the body is an instance of the response class
|
||||||
|
|
@ -2028,17 +2030,17 @@ class Flask(_PackageBoundObject):
|
||||||
rv = self.response_class.force_type(rv, request.environ)
|
rv = self.response_class.force_type(rv, request.environ)
|
||||||
except TypeError as e:
|
except TypeError as e:
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
"{e}\nThe view function did not return a valid"
|
f"{e}\nThe view function did not return a valid"
|
||||||
" response. The return type must be a string, dict, tuple,"
|
" response. The return type must be a string,"
|
||||||
" Response instance, or WSGI callable, but it was a"
|
" dict, tuple, Response instance, or WSGI"
|
||||||
" {rv.__class__.__name__}.".format(e=e, rv=rv)
|
f" callable, but it was a {type(rv).__name__}."
|
||||||
).with_traceback(sys.exc_info()[2])
|
).with_traceback(sys.exc_info()[2])
|
||||||
else:
|
else:
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
"The view function did not return a valid"
|
"The view function did not return a valid"
|
||||||
" response. The return type must be a string, dict, tuple,"
|
" response. The return type must be a string,"
|
||||||
" Response instance, or WSGI callable, but it was a"
|
" dict, tuple, Response instance, or WSGI"
|
||||||
" {rv.__class__.__name__}.".format(rv=rv)
|
f" callable, but it was a {type(rv).__name__}."
|
||||||
)
|
)
|
||||||
|
|
||||||
# prefer the status if it was provided
|
# prefer the status if it was provided
|
||||||
|
|
@ -2375,4 +2377,4 @@ class Flask(_PackageBoundObject):
|
||||||
return self.wsgi_app(environ, start_response)
|
return self.wsgi_app(environ, start_response)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<{self.__class__.__name__} {self.name!r}>"
|
return f"<{type(self).__name__} {self.name!r}>"
|
||||||
|
|
|
||||||
|
|
@ -246,7 +246,7 @@ class Blueprint(_PackageBoundObject):
|
||||||
|
|
||||||
if self.has_static_folder:
|
if self.has_static_folder:
|
||||||
state.add_url_rule(
|
state.add_url_rule(
|
||||||
self.static_url_path + "/<path:filename>",
|
f"{self.static_url_path}/<path:filename>",
|
||||||
view_func=self.send_static_file,
|
view_func=self.send_static_file,
|
||||||
endpoint="static",
|
endpoint="static",
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -62,13 +62,13 @@ def find_best_app(script_info, module):
|
||||||
return matches[0]
|
return matches[0]
|
||||||
elif len(matches) > 1:
|
elif len(matches) > 1:
|
||||||
raise NoAppException(
|
raise NoAppException(
|
||||||
'Detected multiple Flask applications in module "{module}". Use '
|
"Detected multiple Flask applications in module"
|
||||||
'"FLASK_APP={module}:name" to specify the correct '
|
f" {module.__name__!r}. Use 'FLASK_APP={module.__name__}:name'"
|
||||||
"one.".format(module=module.__name__)
|
f" to specify the correct one."
|
||||||
)
|
)
|
||||||
|
|
||||||
# Search for app factory functions.
|
# 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)
|
app_factory = getattr(module, attr_name, None)
|
||||||
|
|
||||||
if inspect.isfunction(app_factory):
|
if inspect.isfunction(app_factory):
|
||||||
|
|
@ -81,15 +81,16 @@ def find_best_app(script_info, module):
|
||||||
if not _called_with_wrong_args(app_factory):
|
if not _called_with_wrong_args(app_factory):
|
||||||
raise
|
raise
|
||||||
raise NoAppException(
|
raise NoAppException(
|
||||||
'Detected factory "{factory}" in module "{module}", but '
|
f"Detected factory {attr_name!r} in module {module.__name__!r},"
|
||||||
"could not call it without arguments. Use "
|
" but could not call it without arguments. Use"
|
||||||
"\"FLASK_APP='{module}:{factory}(args)'\" to specify "
|
f" \"FLASK_APP='{module.__name__}:{attr_name}(args)'\""
|
||||||
"arguments.".format(factory=attr_name, module=module.__name__)
|
" to specify arguments."
|
||||||
)
|
)
|
||||||
|
|
||||||
raise NoAppException(
|
raise NoAppException(
|
||||||
'Failed to find Flask application or factory in module "{module}". '
|
"Failed to find Flask application or factory in module"
|
||||||
'Use "FLASK_APP={module}:name to specify one.'.format(module=module.__name__)
|
f" {module.__name__!r}. Use 'FLASK_APP={module.__name__}:name'"
|
||||||
|
" to specify one."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -150,8 +151,7 @@ def find_app_by_string(script_info, module, app_name):
|
||||||
|
|
||||||
if not match:
|
if not match:
|
||||||
raise NoAppException(
|
raise NoAppException(
|
||||||
'"{name}" is not a valid variable name or function '
|
f"{app_name!r} is not a valid variable name or function expression."
|
||||||
"expression.".format(name=app_name)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
name, args = match.groups()
|
name, args = match.groups()
|
||||||
|
|
@ -165,11 +165,8 @@ def find_app_by_string(script_info, module, app_name):
|
||||||
if args:
|
if args:
|
||||||
try:
|
try:
|
||||||
args = ast.literal_eval(f"({args},)")
|
args = ast.literal_eval(f"({args},)")
|
||||||
except (ValueError, SyntaxError) as e:
|
except (ValueError, SyntaxError):
|
||||||
raise NoAppException(
|
raise NoAppException(f"Could not parse the arguments in {app_name!r}.")
|
||||||
"Could not parse the arguments in "
|
|
||||||
'"{app_name}".'.format(e=e, app_name=app_name)
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
args = ()
|
args = ()
|
||||||
|
|
||||||
|
|
@ -180,10 +177,9 @@ def find_app_by_string(script_info, module, app_name):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
raise NoAppException(
|
raise NoAppException(
|
||||||
'{e}\nThe factory "{app_name}" in module "{module}" could not '
|
f"{e}\nThe factory {app_name!r} in module"
|
||||||
"be called with the specified arguments.".format(
|
f" {module.__name__!r} could not be called with the"
|
||||||
e=e, app_name=app_name, module=module.__name__
|
" specified arguments."
|
||||||
)
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
app = attr
|
app = attr
|
||||||
|
|
@ -192,8 +188,8 @@ def find_app_by_string(script_info, module, app_name):
|
||||||
return app
|
return app
|
||||||
|
|
||||||
raise NoAppException(
|
raise NoAppException(
|
||||||
"A valid Flask application was not obtained from "
|
"A valid Flask application was not obtained from"
|
||||||
'"{module}:{app_name}".'.format(module=module.__name__, app_name=app_name)
|
f" '{module.__name__}:{app_name}'."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -236,11 +232,11 @@ def locate_app(script_info, module_name, app_name, raise_if_not_found=True):
|
||||||
# Determine this by checking whether the trace has a depth > 1.
|
# Determine this by checking whether the trace has a depth > 1.
|
||||||
if sys.exc_info()[2].tb_next:
|
if sys.exc_info()[2].tb_next:
|
||||||
raise NoAppException(
|
raise NoAppException(
|
||||||
'While importing "{name}", an ImportError was raised:'
|
f"While importing {module_name!r}, an ImportError was"
|
||||||
"\n\n{tb}".format(name=module_name, tb=traceback.format_exc())
|
f" raised:\n\n{traceback.format_exc()}"
|
||||||
)
|
)
|
||||||
elif raise_if_not_found:
|
elif raise_if_not_found:
|
||||||
raise NoAppException(f'Could not import "{module_name}".')
|
raise NoAppException(f"Could not import {module_name!r}.")
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -259,14 +255,10 @@ def get_version(ctx, param, value):
|
||||||
import werkzeug
|
import werkzeug
|
||||||
from . import __version__
|
from . import __version__
|
||||||
|
|
||||||
message = "Python %(python)s\nFlask %(flask)s\nWerkzeug %(werkzeug)s"
|
|
||||||
click.echo(
|
click.echo(
|
||||||
message
|
f"Python {platform.python_version()}\n"
|
||||||
% {
|
f"Flask {__version__}\n"
|
||||||
"python": platform.python_version(),
|
f"Werkzeug {werkzeug.__version__}",
|
||||||
"flask": __version__,
|
|
||||||
"werkzeug": werkzeug.__version__,
|
|
||||||
},
|
|
||||||
color=ctx.color,
|
color=ctx.color,
|
||||||
)
|
)
|
||||||
ctx.exit()
|
ctx.exit()
|
||||||
|
|
@ -659,7 +651,7 @@ def show_server_banner(env, debug, app_import_path, eager_loading):
|
||||||
return
|
return
|
||||||
|
|
||||||
if app_import_path is not None:
|
if app_import_path is not None:
|
||||||
message = f' * Serving Flask app "{app_import_path}"'
|
message = f" * Serving Flask app {app_import_path!r}"
|
||||||
|
|
||||||
if not eager_loading:
|
if not eager_loading:
|
||||||
message += " (lazy loading)"
|
message += " (lazy loading)"
|
||||||
|
|
@ -670,14 +662,14 @@ def show_server_banner(env, debug, app_import_path, eager_loading):
|
||||||
|
|
||||||
if env == "production":
|
if env == "production":
|
||||||
click.secho(
|
click.secho(
|
||||||
" WARNING: This is a development server. "
|
" WARNING: This is a development server. Do not use it in"
|
||||||
"Do not use it in a production deployment.",
|
" a production deployment.",
|
||||||
fg="red",
|
fg="red",
|
||||||
)
|
)
|
||||||
click.secho(" Use a production WSGI server instead.", dim=True)
|
click.secho(" Use a production WSGI server instead.", dim=True)
|
||||||
|
|
||||||
if debug is not None:
|
if debug is not None:
|
||||||
click.echo(" * Debug mode: {}".format("on" if debug else "off"))
|
click.echo(f" * Debug mode: {'on' if debug else 'off'}")
|
||||||
|
|
||||||
|
|
||||||
class CertParamType(click.ParamType):
|
class CertParamType(click.ParamType):
|
||||||
|
|
@ -809,7 +801,7 @@ class SeparatedPathType(click.Path):
|
||||||
type=SeparatedPathType(),
|
type=SeparatedPathType(),
|
||||||
help=(
|
help=(
|
||||||
"Extra files that trigger a reload on change. Multiple paths"
|
"Extra files that trigger a reload on change. Multiple paths"
|
||||||
" are separated by '{}'.".format(os.path.pathsep)
|
f" are separated by {os.path.pathsep!r}."
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@pass_script_info
|
@pass_script_info
|
||||||
|
|
@ -863,8 +855,10 @@ def shell_command():
|
||||||
from .globals import _app_ctx_stack
|
from .globals import _app_ctx_stack
|
||||||
|
|
||||||
app = _app_ctx_stack.top.app
|
app = _app_ctx_stack.top.app
|
||||||
banner = "Python {} on {}\nApp: {} [{}]\nInstance: {}".format(
|
banner = (
|
||||||
sys.version, sys.platform, app.import_name, app.env, app.instance_path,
|
f"Python {sys.version} on {sys.platform}\n"
|
||||||
|
f"App: {app.import_name} [{app.env}]\n"
|
||||||
|
f"Instance: {app.instance_path}"
|
||||||
)
|
)
|
||||||
ctx = {}
|
ctx = {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -98,10 +98,10 @@ class Config(dict):
|
||||||
if silent:
|
if silent:
|
||||||
return False
|
return False
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"The environment variable %r is not set "
|
f"The environment variable {variable_name!r} is not set"
|
||||||
"and as such configuration could not be "
|
" and as such configuration could not be loaded. Set"
|
||||||
"loaded. Set this variable and make it "
|
" this variable and make it point to a configuration"
|
||||||
"point to a configuration file" % variable_name
|
" file"
|
||||||
)
|
)
|
||||||
return self.from_pyfile(rv, silent=silent)
|
return self.from_pyfile(rv, silent=silent)
|
||||||
|
|
||||||
|
|
@ -128,7 +128,7 @@ class Config(dict):
|
||||||
except OSError as e:
|
except OSError 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
|
return False
|
||||||
e.strerror = "Unable to load configuration file (%s)" % e.strerror
|
e.strerror = f"Unable to load configuration file ({e.strerror})"
|
||||||
raise
|
raise
|
||||||
self.from_object(d)
|
self.from_object(d)
|
||||||
return True
|
return True
|
||||||
|
|
@ -200,7 +200,7 @@ class Config(dict):
|
||||||
if silent and e.errno in (errno.ENOENT, errno.EISDIR):
|
if silent and e.errno in (errno.ENOENT, errno.EISDIR):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
e.strerror = "Unable to load configuration file (%s)" % e.strerror
|
e.strerror = f"Unable to load configuration file ({e.strerror})"
|
||||||
raise
|
raise
|
||||||
|
|
||||||
return self.from_mapping(obj)
|
return self.from_mapping(obj)
|
||||||
|
|
@ -219,7 +219,7 @@ class Config(dict):
|
||||||
mappings.append(mapping[0])
|
mappings.append(mapping[0])
|
||||||
elif len(mapping) > 1:
|
elif len(mapping) > 1:
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
"expected at most 1 positional argument, got %d" % len(mapping)
|
f"expected at most 1 positional argument, got {len(mapping)}"
|
||||||
)
|
)
|
||||||
mappings.append(kwargs.items())
|
mappings.append(kwargs.items())
|
||||||
for mapping in mappings:
|
for mapping in mappings:
|
||||||
|
|
@ -270,4 +270,4 @@ class Config(dict):
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<{} {}>".format(self.__class__.__name__, dict.__repr__(self))
|
return f"<{type(self).__name__} {dict.__repr__(self)}>"
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ class _AppCtxGlobals:
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
top = _app_ctx_stack.top
|
top = _app_ctx_stack.top
|
||||||
if top is not None:
|
if top is not None:
|
||||||
return "<flask.g of %r>" % top.app.name
|
return f"<flask.g of {top.app.name!r}>"
|
||||||
return object.__repr__(self)
|
return object.__repr__(self)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -445,9 +445,7 @@ class RequestContext:
|
||||||
self.auto_pop(exc_value)
|
self.auto_pop(exc_value)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<{} '{}' [{}] of {}>".format(
|
return (
|
||||||
self.__class__.__name__,
|
f"<{type(self).__name__} {self.request.url!r}"
|
||||||
self.request.url,
|
f" [{self.request.method}] of {self.app.name}>"
|
||||||
self.request.method,
|
|
||||||
self.app.name,
|
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -29,17 +29,18 @@ class DebugFilesKeyError(KeyError, AssertionError):
|
||||||
def __init__(self, request, key):
|
def __init__(self, request, key):
|
||||||
form_matches = request.form.getlist(key)
|
form_matches = request.form.getlist(key)
|
||||||
buf = [
|
buf = [
|
||||||
'You tried to access the file "%s" in the request.files '
|
f"You tried to access the file {key!r} in the request.files"
|
||||||
"dictionary but it does not exist. The mimetype for the request "
|
" dictionary but it does not exist. The mimetype for the"
|
||||||
'is "%s" instead of "multipart/form-data" which means that no '
|
f" request is {request.mimetype!r} instead of"
|
||||||
"file contents were transmitted. To fix this error you should "
|
" 'multipart/form-data' which means that no file contents"
|
||||||
'provide enctype="multipart/form-data" in your form.'
|
" were transmitted. To fix this error you should provide"
|
||||||
% (key, request.mimetype)
|
' enctype="multipart/form-data" in your form.'
|
||||||
]
|
]
|
||||||
if form_matches:
|
if form_matches:
|
||||||
|
names = ", ".join(repr(x) for x in form_matches)
|
||||||
buf.append(
|
buf.append(
|
||||||
"\n\nThe browser instead transmitted some file names. "
|
"\n\nThe browser instead transmitted some file names. "
|
||||||
"This was submitted: %s" % ", ".join('"%s"' % x for x in form_matches)
|
f"This was submitted: {names}"
|
||||||
)
|
)
|
||||||
self.msg = "".join(buf)
|
self.msg = "".join(buf)
|
||||||
|
|
||||||
|
|
@ -56,24 +57,24 @@ class FormDataRoutingRedirect(AssertionError):
|
||||||
def __init__(self, request):
|
def __init__(self, request):
|
||||||
exc = request.routing_exception
|
exc = request.routing_exception
|
||||||
buf = [
|
buf = [
|
||||||
"A request was sent to this URL (%s) but a redirect was "
|
f"A request was sent to this URL ({request.url}) but a"
|
||||||
'issued automatically by the routing system to "%s".'
|
" redirect was issued automatically by the routing system"
|
||||||
% (request.url, exc.new_url)
|
f" to {exc.new_url!r}."
|
||||||
]
|
]
|
||||||
|
|
||||||
# In case just a slash was appended we can be extra helpful
|
# In case just a slash was appended we can be extra helpful
|
||||||
if request.base_url + "/" == exc.new_url.split("?")[0]:
|
if f"{request.base_url}/" == exc.new_url.split("?")[0]:
|
||||||
buf.append(
|
buf.append(
|
||||||
" The URL was defined with a trailing slash so "
|
" The URL was defined with a trailing slash so Flask"
|
||||||
"Flask will automatically redirect to the URL "
|
" will automatically redirect to the URL with the"
|
||||||
"with the trailing slash if it was accessed "
|
" trailing slash if it was accessed without one."
|
||||||
"without one."
|
|
||||||
)
|
)
|
||||||
|
|
||||||
buf.append(
|
buf.append(
|
||||||
" Make sure to directly send your %s-request to this URL "
|
" Make sure to directly send your"
|
||||||
"since we can't make browsers or HTTP clients redirect "
|
f" {request.method}-request to this URL since we can't make"
|
||||||
"with form data reliably or without user interaction." % request.method
|
" browsers or HTTP clients redirect with form data reliably"
|
||||||
|
" or without user interaction."
|
||||||
)
|
)
|
||||||
buf.append("\n\nNote: this exception is only raised in debug mode")
|
buf.append("\n\nNote: this exception is only raised in debug mode")
|
||||||
AssertionError.__init__(self, "".join(buf).encode("utf-8"))
|
AssertionError.__init__(self, "".join(buf).encode("utf-8"))
|
||||||
|
|
@ -101,16 +102,16 @@ def attach_enctype_error_multidict(request):
|
||||||
|
|
||||||
|
|
||||||
def _dump_loader_info(loader):
|
def _dump_loader_info(loader):
|
||||||
yield "class: {}.{}".format(type(loader).__module__, type(loader).__name__)
|
yield f"class: {type(loader).__module__}.{type(loader).__name__}"
|
||||||
for key, value in sorted(loader.__dict__.items()):
|
for key, value in sorted(loader.__dict__.items()):
|
||||||
if key.startswith("_"):
|
if key.startswith("_"):
|
||||||
continue
|
continue
|
||||||
if isinstance(value, (tuple, list)):
|
if isinstance(value, (tuple, list)):
|
||||||
if not all(isinstance(x, str) for x in value):
|
if not all(isinstance(x, str) for x in value):
|
||||||
continue
|
continue
|
||||||
yield "%s:" % key
|
yield f"{key}:"
|
||||||
for item in value:
|
for item in value:
|
||||||
yield " - %s" % item
|
yield f" - {item}"
|
||||||
continue
|
continue
|
||||||
elif not isinstance(value, (str, int, float, bool)):
|
elif not isinstance(value, (str, int, float, bool)):
|
||||||
continue
|
continue
|
||||||
|
|
@ -119,7 +120,7 @@ def _dump_loader_info(loader):
|
||||||
|
|
||||||
def explain_template_loading_attempts(app, template, attempts):
|
def explain_template_loading_attempts(app, template, attempts):
|
||||||
"""This should help developers understand what failed"""
|
"""This should help developers understand what failed"""
|
||||||
info = ['Locating template "%s":' % template]
|
info = [f"Locating template {template!r}:"]
|
||||||
total_found = 0
|
total_found = 0
|
||||||
blueprint = None
|
blueprint = None
|
||||||
reqctx = _request_ctx_stack.top
|
reqctx = _request_ctx_stack.top
|
||||||
|
|
@ -128,23 +129,23 @@ def explain_template_loading_attempts(app, template, attempts):
|
||||||
|
|
||||||
for idx, (loader, srcobj, triple) in enumerate(attempts):
|
for idx, (loader, srcobj, triple) in enumerate(attempts):
|
||||||
if isinstance(srcobj, Flask):
|
if isinstance(srcobj, Flask):
|
||||||
src_info = 'application "%s"' % srcobj.import_name
|
src_info = f"application {srcobj.import_name!r}"
|
||||||
elif isinstance(srcobj, Blueprint):
|
elif isinstance(srcobj, Blueprint):
|
||||||
src_info = f'blueprint "{srcobj.name}" ({srcobj.import_name})'
|
src_info = f"blueprint {srcobj.name!r} ({srcobj.import_name})"
|
||||||
else:
|
else:
|
||||||
src_info = repr(srcobj)
|
src_info = repr(srcobj)
|
||||||
|
|
||||||
info.append("% 5d: trying loader of %s" % (idx + 1, src_info))
|
info.append(f"{idx + 1:5}: trying loader of {src_info}")
|
||||||
|
|
||||||
for line in _dump_loader_info(loader):
|
for line in _dump_loader_info(loader):
|
||||||
info.append(" %s" % line)
|
info.append(f" {line}")
|
||||||
|
|
||||||
if triple is None:
|
if triple is None:
|
||||||
detail = "no match"
|
detail = "no match"
|
||||||
else:
|
else:
|
||||||
detail = "found (%r)" % (triple[1] or "<string>")
|
detail = f"found ({triple[1] or '<string>'!r})"
|
||||||
total_found += 1
|
total_found += 1
|
||||||
info.append(" -> %s" % detail)
|
info.append(f" -> {detail}")
|
||||||
|
|
||||||
seems_fishy = False
|
seems_fishy = False
|
||||||
if total_found == 0:
|
if total_found == 0:
|
||||||
|
|
@ -156,8 +157,8 @@ def explain_template_loading_attempts(app, template, attempts):
|
||||||
|
|
||||||
if blueprint is not None and seems_fishy:
|
if blueprint is not None and seems_fishy:
|
||||||
info.append(
|
info.append(
|
||||||
" The template was looked up from an endpoint that "
|
" The template was looked up from an endpoint that belongs"
|
||||||
'belongs to the blueprint "%s".' % blueprint
|
f" to the blueprint {blueprint!r}."
|
||||||
)
|
)
|
||||||
info.append(" Maybe you did not place a template in the right folder?")
|
info.append(" Maybe you did not place a template in the right folder?")
|
||||||
info.append(" See https://flask.palletsprojects.com/blueprints/#templates")
|
info.append(" See https://flask.palletsprojects.com/blueprints/#templates")
|
||||||
|
|
@ -169,11 +170,10 @@ def explain_ignored_app_run():
|
||||||
if os.environ.get("WERKZEUG_RUN_MAIN") != "true":
|
if os.environ.get("WERKZEUG_RUN_MAIN") != "true":
|
||||||
warn(
|
warn(
|
||||||
Warning(
|
Warning(
|
||||||
"Silently ignoring app.run() because the "
|
"Silently ignoring app.run() because the application is"
|
||||||
"application is run from the flask command line "
|
" run from the flask command line executable. Consider"
|
||||||
"executable. Consider putting app.run() behind an "
|
' putting app.run() behind an if __name__ == "__main__"'
|
||||||
'if __name__ == "__main__" guard to silence this '
|
" guard to silence this warning."
|
||||||
"warning."
|
|
||||||
),
|
),
|
||||||
stacklevel=3,
|
stacklevel=3,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -311,7 +311,7 @@ def url_for(endpoint, **values):
|
||||||
|
|
||||||
if endpoint[:1] == ".":
|
if endpoint[:1] == ".":
|
||||||
if blueprint_name is not None:
|
if blueprint_name is not None:
|
||||||
endpoint = blueprint_name + endpoint
|
endpoint = f"{blueprint_name}{endpoint}"
|
||||||
else:
|
else:
|
||||||
endpoint = endpoint[1:]
|
endpoint = endpoint[1:]
|
||||||
|
|
||||||
|
|
@ -364,7 +364,7 @@ def url_for(endpoint, **values):
|
||||||
return appctx.app.handle_url_build_error(error, endpoint, values)
|
return appctx.app.handle_url_build_error(error, endpoint, values)
|
||||||
|
|
||||||
if anchor is not None:
|
if anchor is not None:
|
||||||
rv += "#" + url_quote(anchor)
|
rv += f"#{url_quote(anchor)}"
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -608,11 +608,12 @@ def send_file(
|
||||||
try:
|
try:
|
||||||
attachment_filename = attachment_filename.encode("ascii")
|
attachment_filename = attachment_filename.encode("ascii")
|
||||||
except UnicodeEncodeError:
|
except UnicodeEncodeError:
|
||||||
|
quoted = url_quote(attachment_filename, safe="")
|
||||||
filenames = {
|
filenames = {
|
||||||
"filename": unicodedata.normalize("NFKD", attachment_filename).encode(
|
"filename": unicodedata.normalize("NFKD", attachment_filename).encode(
|
||||||
"ascii", "ignore"
|
"ascii", "ignore"
|
||||||
),
|
),
|
||||||
"filename*": "UTF-8''%s" % url_quote(attachment_filename, safe=""),
|
"filename*": f"UTF-8''{quoted}",
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
filenames = {"filename": attachment_filename}
|
filenames = {"filename": attachment_filename}
|
||||||
|
|
@ -661,23 +662,19 @@ def send_file(
|
||||||
from warnings import warn
|
from warnings import warn
|
||||||
|
|
||||||
try:
|
try:
|
||||||
rv.set_etag(
|
check = (
|
||||||
"%s-%s-%s"
|
adler32(
|
||||||
% (
|
filename.encode("utf-8") if isinstance(filename, str) else filename
|
||||||
os.path.getmtime(filename),
|
|
||||||
os.path.getsize(filename),
|
|
||||||
adler32(
|
|
||||||
filename.encode("utf-8")
|
|
||||||
if isinstance(filename, str)
|
|
||||||
else filename
|
|
||||||
)
|
|
||||||
& 0xFFFFFFFF,
|
|
||||||
)
|
)
|
||||||
|
& 0xFFFFFFFF
|
||||||
|
)
|
||||||
|
rv.set_etag(
|
||||||
|
f"{os.path.getmtime(filename)}-{os.path.getsize(filename)}-{check}"
|
||||||
)
|
)
|
||||||
except OSError:
|
except OSError:
|
||||||
warn(
|
warn(
|
||||||
"Access %s failed, maybe it does not exist, so ignore etags in "
|
f"Access {filename} failed, maybe it does not exist, so"
|
||||||
"headers" % filename,
|
" ignore etags in headers",
|
||||||
stacklevel=2,
|
stacklevel=2,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -806,13 +803,12 @@ def get_root_path(import_name):
|
||||||
# first module that is contained in our package.
|
# first module that is contained in our package.
|
||||||
if filepath is None:
|
if filepath is None:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"No root path can be found for the provided "
|
"No root path can be found for the provided module"
|
||||||
'module "%s". This can happen because the '
|
f" {import_name!r}. This can happen because the module"
|
||||||
"module came from an import hook that does "
|
" came from an import hook that does not provide file"
|
||||||
"not provide file name information or because "
|
" name information or because it's a namespace package."
|
||||||
"it's a namespace package. In this case "
|
" In this case the root path needs to be explicitly"
|
||||||
"the root path needs to be explicitly "
|
" provided."
|
||||||
"provided." % import_name
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# filepath is import_name.py for a module, or __init__.py for a package.
|
# filepath is import_name.py for a module, or __init__.py for a package.
|
||||||
|
|
@ -823,6 +819,7 @@ def _matching_loader_thinks_module_is_package(loader, mod_name):
|
||||||
"""Given the loader that loaded a module and the module this function
|
"""Given the loader that loaded a module and the module this function
|
||||||
attempts to figure out if the given module is actually a package.
|
attempts to figure out if the given module is actually a package.
|
||||||
"""
|
"""
|
||||||
|
cls = type(loader)
|
||||||
# If the loader can tell us if something is a package, we can
|
# If the loader can tell us if something is a package, we can
|
||||||
# directly ask the loader.
|
# directly ask the loader.
|
||||||
if hasattr(loader, "is_package"):
|
if hasattr(loader, "is_package"):
|
||||||
|
|
@ -830,20 +827,13 @@ def _matching_loader_thinks_module_is_package(loader, mod_name):
|
||||||
# importlib's namespace loaders do not have this functionality but
|
# importlib's namespace loaders do not have this functionality but
|
||||||
# all the modules it loads are packages, so we can take advantage of
|
# all the modules it loads are packages, so we can take advantage of
|
||||||
# this information.
|
# this information.
|
||||||
elif (
|
elif cls.__module__ == "_frozen_importlib" and cls.__name__ == "NamespaceLoader":
|
||||||
loader.__class__.__module__ == "_frozen_importlib"
|
|
||||||
and loader.__class__.__name__ == "NamespaceLoader"
|
|
||||||
):
|
|
||||||
return True
|
return True
|
||||||
# Otherwise we need to fail with an error that explains what went
|
# Otherwise we need to fail with an error that explains what went
|
||||||
# wrong.
|
# wrong.
|
||||||
raise AttributeError(
|
raise AttributeError(
|
||||||
(
|
f"{cls.__name__}.is_package() method is missing but is required"
|
||||||
"%s.is_package() method is missing but is required by Flask of "
|
" for PEP 302 import hooks."
|
||||||
"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__
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1014,7 +1004,7 @@ class _PackageBoundObject:
|
||||||
|
|
||||||
if self.static_folder is not None:
|
if self.static_folder is not None:
|
||||||
basename = os.path.basename(self.static_folder)
|
basename = os.path.basename(self.static_folder)
|
||||||
return ("/" + basename).rstrip("/")
|
return f"/{basename}".rstrip("/")
|
||||||
|
|
||||||
@static_url_path.setter
|
@static_url_path.setter
|
||||||
def static_url_path(self, value):
|
def static_url_path(self, value):
|
||||||
|
|
|
||||||
|
|
@ -364,7 +364,7 @@ def jsonify(*args, **kwargs):
|
||||||
data = args or kwargs
|
data = args or kwargs
|
||||||
|
|
||||||
return current_app.response_class(
|
return current_app.response_class(
|
||||||
dumps(data, indent=indent, separators=separators) + "\n",
|
f"{dumps(data, indent=indent, separators=separators)}\n",
|
||||||
mimetype=current_app.config["JSONIFY_MIMETYPE"],
|
mimetype=current_app.config["JSONIFY_MIMETYPE"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ class TagDict(JSONTag):
|
||||||
|
|
||||||
def to_json(self, value):
|
def to_json(self, value):
|
||||||
key = next(iter(value))
|
key = next(iter(value))
|
||||||
return {key + "__": self.serializer.tag(value[key])}
|
return {f"{key}__": self.serializer.tag(value[key])}
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
key = next(iter(value))
|
key = next(iter(value))
|
||||||
|
|
|
||||||
|
|
@ -208,13 +208,13 @@ class SessionInterface:
|
||||||
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 '.'
|
# Chrome doesn't allow names without a '.'. This should only
|
||||||
# this should only come up with localhost
|
# come up with localhost. Hack around this by not setting
|
||||||
# hack around this by not setting the name, and show a warning
|
# the name, and show a warning.
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
'"{rv}" is not a valid cookie domain, it must contain a ".".'
|
f"{rv!r} is not a valid cookie domain, it must contain"
|
||||||
" Add an entry to your hosts file, for example"
|
" a '.'. Add an entry to your hosts file, for example"
|
||||||
' "{rv}.localdomain", and use that instead.'.format(rv=rv)
|
f" '{rv}.localdomain', and use that instead."
|
||||||
)
|
)
|
||||||
app.config["SESSION_COOKIE_DOMAIN"] = False
|
app.config["SESSION_COOKIE_DOMAIN"] = False
|
||||||
return None
|
return None
|
||||||
|
|
@ -232,7 +232,7 @@ class SessionInterface:
|
||||||
# if this is not an ip and app is mounted at the root, allow subdomain
|
# if this is not an ip and app is mounted at the root, allow subdomain
|
||||||
# matching by adding a '.' prefix
|
# matching by adding a '.' prefix
|
||||||
if self.get_cookie_path(app) == "/" and not ip:
|
if self.get_cookie_path(app) == "/" and not ip:
|
||||||
rv = "." + rv
|
rv = f".{rv}"
|
||||||
|
|
||||||
app.config["SESSION_COOKIE_DOMAIN"] = rv
|
app.config["SESSION_COOKIE_DOMAIN"] = rv
|
||||||
return rv
|
return rv
|
||||||
|
|
|
||||||
|
|
@ -69,10 +69,9 @@ class EnvironBuilder(werkzeug.test.EnvironBuilder):
|
||||||
url_scheme = app.config["PREFERRED_URL_SCHEME"]
|
url_scheme = app.config["PREFERRED_URL_SCHEME"]
|
||||||
|
|
||||||
url = url_parse(path)
|
url = url_parse(path)
|
||||||
base_url = "{scheme}://{netloc}/{path}".format(
|
base_url = (
|
||||||
scheme=url.scheme or url_scheme,
|
f"{url.scheme or url_scheme}://{url.netloc or http_host}"
|
||||||
netloc=url.netloc or http_host,
|
f"/{app_root.lstrip('/')}"
|
||||||
path=app_root.lstrip("/"),
|
|
||||||
)
|
)
|
||||||
path = url.path
|
path = url.path
|
||||||
|
|
||||||
|
|
@ -114,7 +113,7 @@ class FlaskClient(Client):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.environ_base = {
|
self.environ_base = {
|
||||||
"REMOTE_ADDR": "127.0.0.1",
|
"REMOTE_ADDR": "127.0.0.1",
|
||||||
"HTTP_USER_AGENT": "werkzeug/" + werkzeug.__version__,
|
"HTTP_USER_AGENT": f"werkzeug/{werkzeug.__version__}",
|
||||||
}
|
}
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ class View:
|
||||||
methods = ['GET']
|
methods = ['GET']
|
||||||
|
|
||||||
def dispatch_request(self, name):
|
def dispatch_request(self, name):
|
||||||
return 'Hello %s!' % name
|
return f"Hello {name}!"
|
||||||
|
|
||||||
app.add_url_rule('/hello/<name>', view_func=MyView.as_view('myview'))
|
app.add_url_rule('/hello/<name>', view_func=MyView.as_view('myview'))
|
||||||
|
|
||||||
|
|
@ -157,5 +157,5 @@ class MethodView(View, metaclass=MethodViewType):
|
||||||
if meth is None and request.method == "HEAD":
|
if meth is None and request.method == "HEAD":
|
||||||
meth = getattr(self, "get", None)
|
meth = getattr(self, "get", None)
|
||||||
|
|
||||||
assert meth is not None, "Unimplemented method %r" % request.method
|
assert meth is not None, f"Unimplemented method {request.method!r}"
|
||||||
return meth(*args, **kwargs)
|
return meth(*args, **kwargs)
|
||||||
|
|
|
||||||
|
|
@ -116,9 +116,8 @@ def limit_loader(request, monkeypatch):
|
||||||
self.loader = loader
|
self.loader = loader
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
if name in ("archive", "get_filename"):
|
if name in {"archive", "get_filename"}:
|
||||||
msg = "Mocking a loader which does not have `%s.`" % name
|
raise AttributeError(f"Mocking a loader which does not have {name!r}.")
|
||||||
raise AttributeError(msg)
|
|
||||||
return getattr(self.loader, name)
|
return getattr(self.loader, name)
|
||||||
|
|
||||||
old_get_loader = pkgutil.get_loader
|
old_get_loader = pkgutil.get_loader
|
||||||
|
|
@ -148,7 +147,7 @@ def site_packages(modules_tmpdir, monkeypatch):
|
||||||
"""Create a fake site-packages."""
|
"""Create a fake site-packages."""
|
||||||
rv = (
|
rv = (
|
||||||
modules_tmpdir.mkdir("lib")
|
modules_tmpdir.mkdir("lib")
|
||||||
.mkdir("python{x[0]}.{x[1]}".format(x=sys.version_info))
|
.mkdir(f"python{sys.version_info.major}.{sys.version_info.minor}")
|
||||||
.mkdir("site-packages")
|
.mkdir("site-packages")
|
||||||
)
|
)
|
||||||
monkeypatch.syspath_prepend(str(rv))
|
monkeypatch.syspath_prepend(str(rv))
|
||||||
|
|
@ -161,23 +160,21 @@ def install_egg(modules_tmpdir, monkeypatch):
|
||||||
sys.path."""
|
sys.path."""
|
||||||
|
|
||||||
def inner(name, base=modules_tmpdir):
|
def inner(name, base=modules_tmpdir):
|
||||||
if not isinstance(name, str):
|
|
||||||
raise ValueError(name)
|
|
||||||
base.join(name).ensure_dir()
|
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 = base.join("setup.py")
|
||||||
egg_setup.write(
|
egg_setup.write(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
"""
|
f"""
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
setup(name='{}',
|
setup(
|
||||||
version='1.0',
|
name="{name}",
|
||||||
packages=['site_egg'],
|
version="1.0",
|
||||||
zip_safe=True)
|
packages=["site_egg"],
|
||||||
""".format(
|
zip_safe=True,
|
||||||
name
|
|
||||||
)
|
)
|
||||||
|
"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -361,7 +361,7 @@ def test_session_localhost_warning(recwarn, app, client):
|
||||||
rv = client.get("/", "http://localhost:5000/")
|
rv = client.get("/", "http://localhost:5000/")
|
||||||
assert "domain" not in rv.headers["set-cookie"].lower()
|
assert "domain" not in rv.headers["set-cookie"].lower()
|
||||||
w = recwarn.pop(UserWarning)
|
w = recwarn.pop(UserWarning)
|
||||||
assert '"localhost" is not a valid cookie domain' in str(w.message)
|
assert "'localhost' is not a valid cookie domain" in str(w.message)
|
||||||
|
|
||||||
|
|
||||||
def test_session_ip_warning(recwarn, app, client):
|
def test_session_ip_warning(recwarn, app, client):
|
||||||
|
|
@ -1095,7 +1095,7 @@ def test_enctype_debug_helper(app, client):
|
||||||
with pytest.raises(DebugFilesKeyError) as e:
|
with pytest.raises(DebugFilesKeyError) as e:
|
||||||
client.post("/fail", data={"foo": "index.txt"})
|
client.post("/fail", data={"foo": "index.txt"})
|
||||||
assert "no file contents were transmitted" in str(e.value)
|
assert "no file contents were transmitted" in str(e.value)
|
||||||
assert 'This was submitted: "index.txt"' in str(e.value)
|
assert "This was submitted: 'index.txt'" in str(e.value)
|
||||||
|
|
||||||
|
|
||||||
def test_response_types(app, client):
|
def test_response_types(app, client):
|
||||||
|
|
@ -1821,7 +1821,7 @@ def test_subdomain_matching():
|
||||||
|
|
||||||
@app.route("/", subdomain="<user>")
|
@app.route("/", subdomain="<user>")
|
||||||
def index(user):
|
def index(user):
|
||||||
return "index for %s" % user
|
return f"index for {user}"
|
||||||
|
|
||||||
rv = client.get("/", "http://mitsuhiko.localhost.localdomain/")
|
rv = client.get("/", "http://mitsuhiko.localhost.localdomain/")
|
||||||
assert rv.data == b"index for mitsuhiko"
|
assert rv.data == b"index for mitsuhiko"
|
||||||
|
|
@ -1834,7 +1834,7 @@ def test_subdomain_matching_with_ports():
|
||||||
|
|
||||||
@app.route("/", subdomain="<user>")
|
@app.route("/", subdomain="<user>")
|
||||||
def index(user):
|
def index(user):
|
||||||
return "index for %s" % user
|
return f"index for {user}"
|
||||||
|
|
||||||
rv = client.get("/", "http://mitsuhiko.localhost.localdomain:3000/")
|
rv = client.get("/", "http://mitsuhiko.localhost.localdomain:3000/")
|
||||||
assert rv.data == b"index for mitsuhiko"
|
assert rv.data == b"index for mitsuhiko"
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ def test_blueprint_url_defaults(app, client):
|
||||||
|
|
||||||
@bp.route("/foo", defaults={"baz": 42})
|
@bp.route("/foo", defaults={"baz": 42})
|
||||||
def foo(bar, baz):
|
def foo(bar, baz):
|
||||||
return "%s/%d" % (bar, baz)
|
return f"{bar}/{baz:d}"
|
||||||
|
|
||||||
@bp.route("/bar")
|
@bp.route("/bar")
|
||||||
def bar(bar):
|
def bar(bar):
|
||||||
|
|
|
||||||
|
|
@ -268,9 +268,9 @@ def test_get_version(test_apps, capsys):
|
||||||
ctx = MockCtx()
|
ctx = MockCtx()
|
||||||
get_version(ctx, None, "test")
|
get_version(ctx, None, "test")
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert "Python " + python_version() in out
|
assert f"Python {python_version()}" in out
|
||||||
assert "Flask " + flask_version in out
|
assert f"Flask {flask_version}" in out
|
||||||
assert "Werkzeug " + werkzeug_version in out
|
assert f"Werkzeug {werkzeug_version}" in out
|
||||||
|
|
||||||
|
|
||||||
def test_scriptinfo(test_apps, monkeypatch):
|
def test_scriptinfo(test_apps, monkeypatch):
|
||||||
|
|
@ -288,7 +288,7 @@ def test_scriptinfo(test_apps, monkeypatch):
|
||||||
app = obj.load_app()
|
app = obj.load_app()
|
||||||
assert app.name == "testapp"
|
assert app.name == "testapp"
|
||||||
assert obj.load_app() is app
|
assert obj.load_app() is app
|
||||||
obj = ScriptInfo(app_import_path=cli_app_path + ":testapp")
|
obj = ScriptInfo(app_import_path=f"{cli_app_path}:testapp")
|
||||||
app = obj.load_app()
|
app = obj.load_app()
|
||||||
assert app.name == "testapp"
|
assert app.name == "testapp"
|
||||||
assert obj.load_app() is app
|
assert obj.load_app() is app
|
||||||
|
|
@ -406,7 +406,7 @@ def test_flaskgroup_debug(runner, set_debug_flag):
|
||||||
|
|
||||||
result = runner.invoke(cli, ["test"])
|
result = runner.invoke(cli, ["test"])
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
assert result.output == "%s\n" % str(not set_debug_flag)
|
assert result.output == f"{not set_debug_flag}\n"
|
||||||
|
|
||||||
|
|
||||||
def test_print_exceptions(runner):
|
def test_print_exceptions(runner):
|
||||||
|
|
@ -656,4 +656,4 @@ def test_cli_empty(app):
|
||||||
app.register_blueprint(bp)
|
app.register_blueprint(bp)
|
||||||
|
|
||||||
result = app.test_cli_runner().invoke(args=["blue", "--help"])
|
result = app.test_cli_runner().invoke(args=["blue", "--help"])
|
||||||
assert result.exit_code == 2, "Unexpected success:\n\n" + result.output
|
assert result.exit_code == 2, f"Unexpected success:\n\n{result.output}"
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ def common_object_test(app):
|
||||||
|
|
||||||
def test_config_from_pyfile():
|
def test_config_from_pyfile():
|
||||||
app = flask.Flask(__name__)
|
app = flask.Flask(__name__)
|
||||||
app.config.from_pyfile(__file__.rsplit(".", 1)[0] + ".py")
|
app.config.from_pyfile(f"{__file__.rsplit('.', 1)[0]}.py")
|
||||||
common_object_test(app)
|
common_object_test(app)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -84,7 +84,7 @@ def test_config_from_envvar(monkeypatch):
|
||||||
assert not app.config.from_envvar("FOO_SETTINGS", silent=True)
|
assert not app.config.from_envvar("FOO_SETTINGS", silent=True)
|
||||||
|
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
"os.environ", {"FOO_SETTINGS": __file__.rsplit(".", 1)[0] + ".py"}
|
"os.environ", {"FOO_SETTINGS": f"{__file__.rsplit('.', 1)[0]}.py"}
|
||||||
)
|
)
|
||||||
assert app.config.from_envvar("FOO_SETTINGS")
|
assert app.config.from_envvar("FOO_SETTINGS")
|
||||||
common_object_test(app)
|
common_object_test(app)
|
||||||
|
|
@ -185,12 +185,10 @@ def test_from_pyfile_weird_encoding(tmpdir, encoding):
|
||||||
f = tmpdir.join("my_config.py")
|
f = tmpdir.join("my_config.py")
|
||||||
f.write_binary(
|
f.write_binary(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
|
f"""
|
||||||
|
# -*- coding: {encoding} -*-
|
||||||
|
TEST_VALUE = "föö"
|
||||||
"""
|
"""
|
||||||
# -*- coding: {} -*-
|
|
||||||
TEST_VALUE = "föö"
|
|
||||||
""".format(
|
|
||||||
encoding
|
|
||||||
)
|
|
||||||
).encode(encoding)
|
).encode(encoding)
|
||||||
)
|
)
|
||||||
app = flask.Flask(__name__)
|
app = flask.Flask(__name__)
|
||||||
|
|
|
||||||
|
|
@ -286,7 +286,7 @@ class TestJSON:
|
||||||
class MyEncoder(flask.json.JSONEncoder):
|
class MyEncoder(flask.json.JSONEncoder):
|
||||||
def default(self, o):
|
def default(self, o):
|
||||||
if isinstance(o, X):
|
if isinstance(o, X):
|
||||||
return "<%d>" % o.val
|
return f"<{o.val}>"
|
||||||
return flask.json.JSONEncoder.default(self, o)
|
return flask.json.JSONEncoder.default(self, o)
|
||||||
|
|
||||||
class MyDecoder(flask.json.JSONDecoder):
|
class MyDecoder(flask.json.JSONDecoder):
|
||||||
|
|
@ -314,14 +314,16 @@ class TestJSON:
|
||||||
assert rv.data == b'"<42>"'
|
assert rv.data == b'"<42>"'
|
||||||
|
|
||||||
def test_blueprint_json_customization(self, app, client):
|
def test_blueprint_json_customization(self, app, client):
|
||||||
class X: # noqa: B903, for Python2 compatibility
|
class X:
|
||||||
|
__slots__ = ("val",)
|
||||||
|
|
||||||
def __init__(self, val):
|
def __init__(self, val):
|
||||||
self.val = val
|
self.val = val
|
||||||
|
|
||||||
class MyEncoder(flask.json.JSONEncoder):
|
class MyEncoder(flask.json.JSONEncoder):
|
||||||
def default(self, o):
|
def default(self, o):
|
||||||
if isinstance(o, X):
|
if isinstance(o, X):
|
||||||
return "<%d>" % o.val
|
return f"<{o.val}>"
|
||||||
|
|
||||||
return flask.json.JSONEncoder.default(self, o)
|
return flask.json.JSONEncoder.default(self, o)
|
||||||
|
|
||||||
|
|
@ -687,9 +689,9 @@ class TestSendfile:
|
||||||
)
|
)
|
||||||
rv.close()
|
rv.close()
|
||||||
content_disposition = rv.headers["Content-Disposition"]
|
content_disposition = rv.headers["Content-Disposition"]
|
||||||
assert "filename=%s" % ascii in content_disposition
|
assert f"filename={ascii}" in content_disposition
|
||||||
if utf8:
|
if utf8:
|
||||||
assert "filename*=UTF-8''" + utf8 in content_disposition
|
assert f"filename*=UTF-8''{utf8}" in content_disposition
|
||||||
else:
|
else:
|
||||||
assert "filename*=UTF-8''" not in content_disposition
|
assert "filename*=UTF-8''" not in content_disposition
|
||||||
|
|
||||||
|
|
@ -818,7 +820,7 @@ class TestUrlFor:
|
||||||
def get(self, id=None):
|
def get(self, id=None):
|
||||||
if id is None:
|
if id is None:
|
||||||
return "List"
|
return "List"
|
||||||
return "Get %d" % id
|
return f"Get {id:d}"
|
||||||
|
|
||||||
def post(self):
|
def post(self):
|
||||||
return "Create"
|
return "Create"
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ def test_proper_test_request_context(app):
|
||||||
def test_context_binding(app):
|
def test_context_binding(app):
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
def index():
|
def index():
|
||||||
return "Hello %s!" % flask.request.args["name"]
|
return f"Hello {flask.request.args['name']}!"
|
||||||
|
|
||||||
@app.route("/meh")
|
@app.route("/meh")
|
||||||
def meh():
|
def meh():
|
||||||
|
|
@ -138,7 +138,7 @@ def test_context_test(app):
|
||||||
def test_manual_context_binding(app):
|
def test_manual_context_binding(app):
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
def index():
|
def index():
|
||||||
return "Hello %s!" % flask.request.args["name"]
|
return f"Hello {flask.request.args['name']}!"
|
||||||
|
|
||||||
ctx = app.test_request_context("/?name=World")
|
ctx = app.test_request_context("/?name=World")
|
||||||
ctx.push()
|
ctx.push()
|
||||||
|
|
|
||||||
|
|
@ -414,16 +414,16 @@ def test_template_loader_debugging(test_apps, monkeypatch):
|
||||||
def handle(self, record):
|
def handle(self, record):
|
||||||
called.append(True)
|
called.append(True)
|
||||||
text = str(record.msg)
|
text = str(record.msg)
|
||||||
assert '1: trying loader of application "blueprintapp"' in text
|
assert "1: trying loader of application 'blueprintapp'" in text
|
||||||
assert (
|
assert (
|
||||||
'2: trying loader of blueprint "admin" (blueprintapp.apps.admin)'
|
"2: trying loader of blueprint 'admin' (blueprintapp.apps.admin)"
|
||||||
) in text
|
) in text
|
||||||
assert (
|
assert (
|
||||||
'trying loader of blueprint "frontend" (blueprintapp.apps.frontend)'
|
"trying loader of blueprint 'frontend' (blueprintapp.apps.frontend)"
|
||||||
) in text
|
) in text
|
||||||
assert "Error: the template could not be found" in text
|
assert "Error: the template could not be found" in text
|
||||||
assert (
|
assert (
|
||||||
'looked up from an endpoint that belongs to the blueprint "frontend"'
|
"looked up from an endpoint that belongs to the blueprint 'frontend'"
|
||||||
) in text
|
) in text
|
||||||
assert "See https://flask.palletsprojects.com/blueprints/#templates" in text
|
assert "See https://flask.palletsprojects.com/blueprints/#templates" in text
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ def test_environ_base_default(app, client, app_ctx):
|
||||||
|
|
||||||
rv = client.get("/")
|
rv = client.get("/")
|
||||||
assert rv.data == b"127.0.0.1"
|
assert rv.data == b"127.0.0.1"
|
||||||
assert flask.g.user_agent == "werkzeug/" + werkzeug.__version__
|
assert flask.g.user_agent == f"werkzeug/{werkzeug.__version__}"
|
||||||
|
|
||||||
|
|
||||||
def test_environ_base_modified(app, client, app_ctx):
|
def test_environ_base_modified(app, client, app_ctx):
|
||||||
|
|
@ -321,7 +321,7 @@ def test_json_request_and_response(app, client):
|
||||||
def test_client_json_no_app_context(app, client):
|
def test_client_json_no_app_context(app, client):
|
||||||
@app.route("/hello", methods=["POST"])
|
@app.route("/hello", methods=["POST"])
|
||||||
def hello():
|
def hello():
|
||||||
return "Hello, {}!".format(flask.request.json["name"])
|
return f"Hello, {flask.request.json['name']}!"
|
||||||
|
|
||||||
class Namespace:
|
class Namespace:
|
||||||
count = 0
|
count = 0
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ def test_error_handler_no_match(app, client):
|
||||||
original = getattr(e, "original_exception", None)
|
original = getattr(e, "original_exception", None)
|
||||||
|
|
||||||
if original is not None:
|
if original is not None:
|
||||||
return "wrapped " + type(original).__name__
|
return f"wrapped {type(original).__name__}"
|
||||||
|
|
||||||
return "direct"
|
return "direct"
|
||||||
|
|
||||||
|
|
@ -238,9 +238,9 @@ class TestGenericHandlers:
|
||||||
original = getattr(e, "original_exception", None)
|
original = getattr(e, "original_exception", None)
|
||||||
|
|
||||||
if original is not None:
|
if original is not None:
|
||||||
return "wrapped " + type(original).__name__
|
return f"wrapped {type(original).__name__}"
|
||||||
|
|
||||||
return "direct " + type(e).__name__
|
return f"direct {type(e).__name__}"
|
||||||
|
|
||||||
@pytest.mark.parametrize("to_handle", (InternalServerError, 500))
|
@pytest.mark.parametrize("to_handle", (InternalServerError, 500))
|
||||||
def test_handle_class_or_code(self, app, client, to_handle):
|
def test_handle_class_or_code(self, app, client, to_handle):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue