Merge branch 'master' of git://github.com/mitsuhiko/flask
This commit is contained in:
commit
048b3008ba
11 changed files with 141 additions and 30 deletions
13
CHANGES
13
CHANGES
|
|
@ -23,11 +23,15 @@ Release date to be announced, codename to be selected
|
|||
1.0 the old behaviour will continue to work but issue dependency
|
||||
warnings.
|
||||
- fixed a problem for Flask to run on jython.
|
||||
- added a `PROPAGATE_EXCEPTIONS` configuration variable that can be
|
||||
used to flip the setting of exception propagation which previously
|
||||
was linked to `DEBUG` alone and is now linked to either `DEBUG` or
|
||||
`TESTING`.
|
||||
|
||||
Version 0.6.1
|
||||
-------------
|
||||
|
||||
Bugfix release, release date to be announced.
|
||||
Bugfix release, released on December 31st 2010
|
||||
|
||||
- Fixed an issue where the default `OPTIONS` response was
|
||||
not exposing all valid methods in the `Allow` header.
|
||||
|
|
@ -36,6 +40,9 @@ Bugfix release, release date to be announced.
|
|||
module setups.
|
||||
- Fixed an issue where the subdomain setting for modules was
|
||||
ignored for the static folder.
|
||||
- Fixed a security problem that allowed clients to download arbitrary files
|
||||
if the host server was a windows based operating system and the client
|
||||
uses backslashes to escape the directory the files where exposed from.
|
||||
|
||||
Version 0.6
|
||||
-----------
|
||||
|
|
@ -45,7 +52,7 @@ Released on July 27th 2010, codename Whisky
|
|||
- after request functions are now called in reverse order of
|
||||
registration.
|
||||
- OPTIONS is now automatically implemented by Flask unless the
|
||||
application explictly adds 'OPTIONS' as method to the URL rule.
|
||||
application explicitly adds 'OPTIONS' as method to the URL rule.
|
||||
In this case no automatic OPTIONS handling kicks in.
|
||||
- static rules are now even in place if there is no static folder
|
||||
for the module. This was implemented to aid GAE which will
|
||||
|
|
@ -65,7 +72,7 @@ Released on July 27th 2010, codename Whisky
|
|||
- added signalling support based on blinker. This feature is currently
|
||||
optional and supposed to be used by extensions and applications. If
|
||||
you want to use it, make sure to have `blinker`_ installed.
|
||||
- refactored the way url adapters are created. This process is now
|
||||
- refactored the way URL adapters are created. This process is now
|
||||
fully customizable with the :meth:`~flask.Flask.create_url_adapter`
|
||||
method.
|
||||
- modules can now register for a subdomain instead of just an URL
|
||||
|
|
|
|||
|
|
@ -54,6 +54,11 @@ The following configuration values are used internally by Flask:
|
|||
=============================== =========================================
|
||||
``DEBUG`` enable/disable debug mode
|
||||
``TESTING`` enable/disable testing mode
|
||||
``PROPAGATE_EXCEPTIONS`` explicitly enable or disable the
|
||||
propagation of exceptions. If not set or
|
||||
explicitly set to `None` this is
|
||||
implicitly true if either `TESTING` or
|
||||
`DEBUG` is true.
|
||||
``SECRET_KEY`` the secret key
|
||||
``SESSION_COOKIE_NAME`` the name of the session cookie
|
||||
``PERMANENT_SESSION_LIFETIME`` the lifetime of a permanent session as
|
||||
|
|
@ -96,6 +101,9 @@ The following configuration values are used internally by Flask:
|
|||
.. versionadded:: 0.6
|
||||
``MAX_CONTENT_LENGTH``
|
||||
|
||||
.. versionadded:: 0.7
|
||||
``PROPAGATE_EXCEPTIONS``
|
||||
|
||||
Configuring from Files
|
||||
----------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -106,4 +106,4 @@ Werkzeug and Flask will be ported to Python 3 as soon as a solution for
|
|||
WSGI is found, and we will provide helpful tips how to upgrade existing
|
||||
applications to Python 3. Until then, we strongly recommend using Python
|
||||
2.6 and 2.7 with activated Python 3 warnings during development, as well
|
||||
as the unicode literals `__future__` feature.
|
||||
as the Unicode literals `__future__` feature.
|
||||
|
|
|
|||
|
|
@ -81,16 +81,23 @@ context are appended to it.
|
|||
Additionally there is a convenient helper method
|
||||
(:meth:`~blinker.base.Signal.connected_to`). that allows you to
|
||||
temporarily subscribe a function to a signal with is a context manager on
|
||||
its own which simplifies the example above::
|
||||
its own. Because the return value of the context manager cannot be
|
||||
specified that way one has to pass the list in as argument::
|
||||
|
||||
from flask import template_rendered
|
||||
|
||||
def captured_templates(app):
|
||||
recorded = []
|
||||
def captured_templates(app, recorded):
|
||||
def record(sender, template, context):
|
||||
recorded.append((template, context))
|
||||
return template_rendered.connected_to(record, app)
|
||||
|
||||
The example above would then look like this::
|
||||
|
||||
templates = []
|
||||
with captured_templates(app, templates):
|
||||
...
|
||||
template, context = templates[0]
|
||||
|
||||
.. admonition:: Blinker API Changes
|
||||
|
||||
The :meth:`~blinker.base.Signal.connected_to` method arrived in Blinker
|
||||
|
|
|
|||
|
|
@ -1,23 +1,23 @@
|
|||
Unicode in Flask
|
||||
================
|
||||
|
||||
Flask like Jinja2 and Werkzeug is totally unicode based when it comes to
|
||||
Flask like Jinja2 and Werkzeug is totally Unicode based when it comes to
|
||||
text. Not only these libraries, also the majority of web related Python
|
||||
libraries that deal with text. If you don't know unicode so far, you
|
||||
libraries that deal with text. If you don't know Unicode so far, you
|
||||
should probably read `The Absolute Minimum Every Software Developer
|
||||
Absolutely, Positively Must Know About Unicode and Character Sets
|
||||
<http://www.joelonsoftware.com/articles/Unicode.html>`_. This part of the
|
||||
documentation just tries to cover the very basics so that you have a
|
||||
pleasant experience with unicode related things.
|
||||
pleasant experience with Unicode related things.
|
||||
|
||||
Automatic Conversion
|
||||
--------------------
|
||||
|
||||
Flask has a few assumptions about your application (which you can change
|
||||
of course) that give you basic and painless unicode support:
|
||||
of course) that give you basic and painless Unicode support:
|
||||
|
||||
- the encoding for text on your website is UTF-8
|
||||
- internally you will always use unicode exclusively for text except
|
||||
- internally you will always use Unicode exclusively for text except
|
||||
for literal strings with only ASCII character points.
|
||||
- encoding and decoding happens whenever you are talking over a protocol
|
||||
that requires bytes to be transmitted.
|
||||
|
|
@ -29,27 +29,27 @@ address documents on servers (so called URIs or URLs). However HTML which
|
|||
is usually transmitted on top of HTTP supports a large variety of
|
||||
character sets and which ones are used, are transmitted in an HTTP header.
|
||||
To not make this too complex Flask just assumes that if you are sending
|
||||
unicode out you want it to be UTF-8 encoded. Flask will do the encoding
|
||||
Unicode out you want it to be UTF-8 encoded. Flask will do the encoding
|
||||
and setting of the appropriate headers for you.
|
||||
|
||||
The same is true if you are talking to databases with the help of
|
||||
SQLAlchemy or a similar ORM system. Some databases have a protocol that
|
||||
already transmits unicode and if they do not, SQLAlchemy or your other ORM
|
||||
already transmits Unicode and if they do not, SQLAlchemy or your other ORM
|
||||
should take care of that.
|
||||
|
||||
The Golden Rule
|
||||
---------------
|
||||
|
||||
So the rule of thumb: if you are not dealing with binary data, work with
|
||||
unicode. What does working with unicode in Python 2.x mean?
|
||||
Unicode. What does working with Unicode in Python 2.x mean?
|
||||
|
||||
- as long as you are using ASCII charpoints only (basically numbers,
|
||||
some special characters of latin letters without umlauts or anything
|
||||
fancy) you can use regular string literals (``'Hello World'``).
|
||||
- if you need anything else than ASCII in a string you have to mark
|
||||
this string as unicode string by prefixing it with a lowercase `u`.
|
||||
this string as Unicode string by prefixing it with a lowercase `u`.
|
||||
(like ``u'Hänsel und Gretel'``)
|
||||
- if you are using non-unicode characters in your Python files you have
|
||||
- if you are using non-Unicode characters in your Python files you have
|
||||
to tell Python which encoding your file uses. Again, I recommend
|
||||
UTF-8 for this purpose. To tell the interpreter your encoding you can
|
||||
put the ``# -*- coding: utf-8 -*-`` into the first or second line of
|
||||
|
|
@ -61,21 +61,21 @@ Encoding and Decoding Yourself
|
|||
------------------------------
|
||||
|
||||
If you are talking with a filesystem or something that is not really based
|
||||
on unicode you will have to ensure that you decode properly when working
|
||||
with unicode interface. So for example if you want to load a file on the
|
||||
on Unicode you will have to ensure that you decode properly when working
|
||||
with Unicode interface. So for example if you want to load a file on the
|
||||
filesystem and embed it into a Jinja2 template you will have to decode it
|
||||
from the encoding of that file. Here the old problem that text files do
|
||||
not specify their encoding comes into play. So do yourself a favour and
|
||||
limit yourself to UTF-8 for text files as well.
|
||||
|
||||
Anyways. To load such a file with unicode you can use the built-in
|
||||
Anyways. To load such a file with Unicode you can use the built-in
|
||||
:meth:`str.decode` method::
|
||||
|
||||
def read_file(filename, charset='utf-8'):
|
||||
with open(filename, 'r') as f:
|
||||
return f.read().decode(charset)
|
||||
|
||||
To go from unicode into a specific charset such as UTF-8 you can use the
|
||||
To go from Unicode into a specific charset such as UTF-8 you can use the
|
||||
:meth:`unicode.encode` method::
|
||||
|
||||
def write_file(filename, contents, charset='utf-8'):
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
from __future__ import with_statement
|
||||
import sqlite3
|
||||
from sqlite3 import dbapi2 as sqlite3
|
||||
from contextlib import closing
|
||||
from flask import Flask, request, session, g, redirect, url_for, abort, \
|
||||
render_template, flash
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
"""
|
||||
from __future__ import with_statement
|
||||
import time
|
||||
import sqlite3
|
||||
from sqlite3 import dbapi2 as sqlite3
|
||||
from hashlib import md5
|
||||
from datetime import datetime
|
||||
from contextlib import closing
|
||||
|
|
|
|||
33
flask/app.py
33
flask/app.py
|
|
@ -189,6 +189,7 @@ class Flask(_PackageBoundObject):
|
|||
default_config = ImmutableDict({
|
||||
'DEBUG': False,
|
||||
'TESTING': False,
|
||||
'PROPAGATE_EXCEPTIONS': None,
|
||||
'SECRET_KEY': None,
|
||||
'SESSION_COOKIE_NAME': 'session',
|
||||
'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
|
||||
|
|
@ -198,6 +199,11 @@ class Flask(_PackageBoundObject):
|
|||
'MAX_CONTENT_LENGTH': None
|
||||
})
|
||||
|
||||
#: the test client that is used with when `test_client` is used.
|
||||
#:
|
||||
#: .. versionadded:: 0.7
|
||||
test_client_class = None
|
||||
|
||||
def __init__(self, import_name, static_path=None):
|
||||
_PackageBoundObject.__init__(self, import_name)
|
||||
if static_path is not None:
|
||||
|
|
@ -303,6 +309,18 @@ class Flask(_PackageBoundObject):
|
|||
self.jinja_env = self.create_jinja_environment()
|
||||
self.init_jinja_globals()
|
||||
|
||||
@property
|
||||
def propagate_exceptions(self):
|
||||
"""Returns the value of the `PROPAGATE_EXCEPTIONS` configuration
|
||||
value in case it's set, otherwise a sensible default is returned.
|
||||
|
||||
.. versionadded:: 0.7
|
||||
"""
|
||||
rv = self.config['PROPAGATE_EXCEPTIONS']
|
||||
if rv is not None:
|
||||
return rv
|
||||
return self.testing or self.debug
|
||||
|
||||
@property
|
||||
def logger(self):
|
||||
"""A :class:`logging.Logger` object for this application. The
|
||||
|
|
@ -416,7 +434,7 @@ class Flask(_PackageBoundObject):
|
|||
options.setdefault('use_debugger', self.debug)
|
||||
return run_simple(host, port, self, **options)
|
||||
|
||||
def test_client(self):
|
||||
def test_client(self, use_cookies=True):
|
||||
"""Creates a test client for this application. For information
|
||||
about unit testing head over to :ref:`testing`.
|
||||
|
||||
|
|
@ -430,9 +448,16 @@ class Flask(_PackageBoundObject):
|
|||
|
||||
.. versionchanged:: 0.4
|
||||
added support for `with` block usage for the client.
|
||||
|
||||
.. versionadded:: 0.7
|
||||
The `use_cookies` parameter was added as well as the ability
|
||||
to override the client to be used by setting the
|
||||
:attr:`test_client_class` attribute.
|
||||
"""
|
||||
from flask.testing import FlaskClient
|
||||
return FlaskClient(self, self.response_class, use_cookies=True)
|
||||
cls = self.test_client_class
|
||||
if cls is None:
|
||||
from flask.testing import FlaskClient as cls
|
||||
return cls(self, self.response_class, use_cookies=use_cookies)
|
||||
|
||||
def open_session(self, request):
|
||||
"""Creates or opens a new session. Default implementation stores all
|
||||
|
|
@ -682,7 +707,7 @@ class Flask(_PackageBoundObject):
|
|||
"""
|
||||
got_request_exception.send(self, exception=e)
|
||||
handler = self.error_handlers.get(500)
|
||||
if self.debug:
|
||||
if self.propagate_exceptions:
|
||||
raise
|
||||
self.logger.exception('Exception on %s [%s]' % (
|
||||
request.path,
|
||||
|
|
|
|||
|
|
@ -58,6 +58,13 @@ else:
|
|||
_tojson_filter = json.dumps
|
||||
|
||||
|
||||
# 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, '/'))
|
||||
|
||||
|
||||
def _endpoint_from_view_func(view_func):
|
||||
"""Internal helper that returns the default endpoint for a given
|
||||
function. This always is the function name.
|
||||
|
|
@ -413,7 +420,10 @@ def send_from_directory(directory, filename, **options):
|
|||
forwarded to :func:`send_file`.
|
||||
"""
|
||||
filename = posixpath.normpath(filename)
|
||||
if filename.startswith(('/', '../')):
|
||||
for sep in _os_alt_seps:
|
||||
if sep in filename:
|
||||
raise NotFound()
|
||||
if os.path.isabs(filename) or filename.startswith('../'):
|
||||
raise NotFound()
|
||||
filename = os.path.join(directory, filename)
|
||||
if not os.path.isfile(filename):
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ def _register_module(module, static_path):
|
|||
state.app.add_url_rule(path + '/<path:filename>',
|
||||
endpoint='%s.static' % module.name,
|
||||
view_func=module.send_static_file,
|
||||
subdomain=module.subdomain)
|
||||
subdomain=state.subdomain)
|
||||
return _register
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import re
|
|||
import sys
|
||||
import flask
|
||||
import unittest
|
||||
from threading import Thread
|
||||
from logging import StreamHandler
|
||||
from contextlib import contextmanager
|
||||
from datetime import datetime
|
||||
|
|
@ -28,6 +29,15 @@ sys.path.append(os.path.join(example_path, 'flaskr'))
|
|||
sys.path.append(os.path.join(example_path, 'minitwit'))
|
||||
|
||||
|
||||
def has_encoding(name):
|
||||
try:
|
||||
import codecs
|
||||
codecs.lookup(name)
|
||||
return True
|
||||
except LookupError:
|
||||
return False
|
||||
|
||||
|
||||
# config keys used for the ConfigTestCase
|
||||
TEST_KEY = 'foo'
|
||||
SECRET_KEY = 'devkey'
|
||||
|
|
@ -547,7 +557,6 @@ class BasicFunctionalityTestCase(unittest.TestCase):
|
|||
"No ValueError exception should have been raised \"%s\"" % e
|
||||
)
|
||||
|
||||
|
||||
def test_test_app_proper_environ(self):
|
||||
app = flask.Flask(__name__)
|
||||
app.config.update(
|
||||
|
|
@ -619,6 +628,33 @@ class BasicFunctionalityTestCase(unittest.TestCase):
|
|||
"No ValueError exception should have been raised \"%s\"" % e
|
||||
)
|
||||
|
||||
def test_exception_propagation(self):
|
||||
def apprunner(configkey):
|
||||
app = flask.Flask(__name__)
|
||||
@app.route('/')
|
||||
def index():
|
||||
1/0
|
||||
c = app.test_client()
|
||||
if config_key is not None:
|
||||
app.config[config_key] = True
|
||||
try:
|
||||
resp = c.get('/')
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
self.fail('expected exception')
|
||||
else:
|
||||
assert c.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:
|
||||
t = Thread(target=apprunner, args=(config_key,))
|
||||
t.start()
|
||||
t.join()
|
||||
|
||||
|
||||
class JSONTestCase(unittest.TestCase):
|
||||
|
||||
|
|
@ -671,6 +707,9 @@ class JSONTestCase(unittest.TestCase):
|
|||
assert rv.status_code == 200
|
||||
assert rv.data == u'정상처리'.encode('utf-8')
|
||||
|
||||
if not has_encoding('euc-kr'):
|
||||
test_modified_url_encoding = None
|
||||
|
||||
|
||||
class TemplatingTestCase(unittest.TestCase):
|
||||
|
||||
|
|
@ -957,6 +996,21 @@ class ModuleTestCase(unittest.TestCase):
|
|||
else:
|
||||
assert 0, 'expected exception'
|
||||
|
||||
# testcase for a security issue that may exist on windows systems
|
||||
import os
|
||||
import ntpath
|
||||
old_path = os.path
|
||||
os.path = ntpath
|
||||
try:
|
||||
try:
|
||||
f('..\\__init__.py')
|
||||
except NotFound:
|
||||
pass
|
||||
else:
|
||||
assert 0, 'expected exception'
|
||||
finally:
|
||||
os.path = old_path
|
||||
|
||||
|
||||
class SendfileTestCase(unittest.TestCase):
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue