diff --git a/docs/_themes b/docs/_themes index f87d00ee..0d8f3d85 160000 --- a/docs/_themes +++ b/docs/_themes @@ -1 +1 @@ -Subproject commit f87d00eee80e4a555e94ed124a94ffea483dc329 +Subproject commit 0d8f3d85558168647632c768bdea7d58cf6f8e42 diff --git a/docs/becomingbig.rst b/docs/becomingbig.rst index fcffe7c2..8ad125de 100644 --- a/docs/becomingbig.rst +++ b/docs/becomingbig.rst @@ -27,7 +27,6 @@ endpoints. Here are some suggestions for how Flask can be modified to better accommodate large-scale applications: -- implement dotted names for URL endpoints - get rid of the decorator function registering which causes a lot of troubles for applications that have circular dependencies. It also requires that the whole application is imported when the system diff --git a/docs/config.rst b/docs/config.rst index 24bc8b04..6a2900a1 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -1,3 +1,5 @@ +.. _config: + Configuration Handling ====================== @@ -47,6 +49,8 @@ Builtin Configuration Values The following configuration values are used internally by Flask: +.. tabularcolumns:: |p{6.5cm}|p{8.5cm}| + =============================== ========================================= ``DEBUG`` enable/disable debug mode ``SECRET_KEY`` the secret key diff --git a/docs/contents.rst.inc b/docs/contents.rst.inc index 5fda5931..918ad804 100644 --- a/docs/contents.rst.inc +++ b/docs/contents.rst.inc @@ -41,4 +41,5 @@ Design notes, legal information and changelog are here for the interested. design license + upgrading changelog diff --git a/docs/foreword.rst b/docs/foreword.rst index 34992a18..fe466dab 100644 --- a/docs/foreword.rst +++ b/docs/foreword.rst @@ -84,11 +84,6 @@ widespread usage. Recent versions of Flask scale nicely within reasonable bounds and if you grow larger, you won't have any troubles adjusting Flask for your new application size. -Flask serves two purposes: it's an example of how to create a minimal and -opinionated framework on top of Werkzeug to show how this can be done, and -to provide people with a simple tool to prototype larger applications or -to implement small and medium sized applications. - If you suddenly discover that your application grows larger than originally intended, head over to the :ref:`becomingbig` section to see some possible solutions for larger applications. diff --git a/docs/patterns/appfactories.rst b/docs/patterns/appfactories.rst index 27c53a75..448dba71 100644 --- a/docs/patterns/appfactories.rst +++ b/docs/patterns/appfactories.rst @@ -1,3 +1,5 @@ +.. _app-factories: + Application Factories ===================== diff --git a/docs/patterns/jquery.rst b/docs/patterns/jquery.rst index f087e6f0..e9c9f956 100644 --- a/docs/patterns/jquery.rst +++ b/docs/patterns/jquery.rst @@ -77,7 +77,7 @@ inside a `script` block here where different rules apply. will not be parsed. Everything until ```` is handled as script. This also means that there must never be any ``"|tojson|safe }`` is rendered as + escape slashes for you (``{{ ""|tojson|safe }}`` is rendered as ``"<\/script>"``). diff --git a/docs/testing.rst b/docs/testing.rst index be72e746..2a58efd3 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -94,7 +94,7 @@ this:: class FlaskrTestCase(unittest.TestCase): def setUp(self): - self.db_fd, flaskr.DATABASE = tempfile.mkstemp() + self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp() self.app = flaskr.app.test_client() flaskr.init_db() @@ -151,13 +151,13 @@ Now we can easily test if logging in and out works and that it fails with invalid credentials. Add this new test to the class:: def test_login_logout(self): - rv = self.login(flaskr.USERNAME, flaskr.PASSWORD) + rv = self.login('admin', 'default') assert 'You were logged in' in rv.data rv = self.logout() assert 'You were logged out' in rv.data - rv = self.login(flaskr.USERNAME + 'x', flaskr.PASSWORD) + rv = self.login('adminx', 'default') assert 'Invalid username' in rv.data - rv = self.login(flaskr.USERNAME, flaskr.PASSWORD + 'x') + rv = self.login('admin', 'defaultx') assert 'Invalid password' in rv.data Test Adding Messages @@ -167,7 +167,7 @@ Now we can also test that adding messages works. Add a new test method like this:: def test_messages(self): - self.login(flaskr.USERNAME, flaskr.PASSWORD) + self.login('admin', 'default') rv = self.app.post('/add', data=dict( title='', text='HTML allowed here' @@ -214,3 +214,7 @@ functions. Here a full example that showcases this:: assert flask.request.args['name'] == 'Peter' All the other objects that are context bound can be used the same. + +If you want to test your application with different configurations and +there does not seem to be a good way to do that, consider switching to +application factories (see :ref:`app-factories`). diff --git a/docs/tutorial/setup.rst b/docs/tutorial/setup.rst index f9a0b302..790cc41e 100644 --- a/docs/tutorial/setup.rst +++ b/docs/tutorial/setup.rst @@ -26,12 +26,27 @@ the values from there. PASSWORD = 'default' Next we can create our actual application and initialize it with the -config:: +config from the same file:: # create our little application :) app = Flask(__name__) - app.secret_key = SECRET_KEY - app.debug = DEBUG + app.config.from_object(__name__) + +:meth:`~flask.Config.from_object` will look at the given object (if it's a +string it will import it) and then look for all uppercase variables +defined there. In our case, the configuration we just wrote a few lines +of code above. You can also move that into a separate file. + +It is also a good idea to be able to load a configuration from a +configurable file. This is what :meth:`~flask.Config.from_envvar` can +do:: + + app.config.from_envvar('FLASKR_SETTINGS', silent=True) + +That way someone can set an environment variable called +:envvar:`FLASKR_SETTINGS` to specify a config file to be loaded which will +then override the default values. The silent switch just tells Flask to +not complain if no such environment key is set. The `secret_key` is needed to keep the client-side sessions secure. Choose that key wisely and as hard to guess and complex as possible. The @@ -46,7 +61,7 @@ Python shell or a script. This will come in handy later :: def connect_db(): - return sqlite3.connect(DATABASE) + return sqlite3.connect(app.config['DATABASE']) Finally we just add a line to the bottom of the file that fires up the server if we run that file as standalone application:: diff --git a/docs/tutorial/views.rst b/docs/tutorial/views.rst index 03c7709b..c7cc1ed4 100644 --- a/docs/tutorial/views.rst +++ b/docs/tutorial/views.rst @@ -63,9 +63,9 @@ notified about that and the user asked again:: def login(): error = None if request.method == 'POST': - if request.form['username'] != USERNAME: + if request.form['username'] != app.config['USERNAME']: error = 'Invalid username' - elif request.form['password'] != PASSWORD: + elif request.form['password'] != app.config['PASSWORD']: error = 'Invalid password' else: session['logged_in'] = True diff --git a/docs/upgrading.rst b/docs/upgrading.rst new file mode 100644 index 00000000..e0f210db --- /dev/null +++ b/docs/upgrading.rst @@ -0,0 +1,56 @@ +Upgrading to Newer Releases +=========================== + +Flask itself is changing like any software is changing over time. Most of +the changes are the nice kind, the kind where you don't have th change +anything in your code to profit from a new release. + +However every once in a while there are changes that do require some +changes in your code or there are changes that make it possible for you to +improve your own code quality by taking advantage of new features in +Flask. + +This section of the documentation enumerates all the changes in Flask from +release to release and how you can change your code to have a painless +updating experience. + +Version 0.5 +----------- + +Flask 0.5 introduces configuration support and logging as well as +categories for flashing messages. All these are features that are 100% +backwards compatible but you might want to take advantage of them. + +Configuration Support +````````````````````` + +The configuration support makes it easier to write any kind of application +that requires some sort of configuration. (Which most likely is the case +for any application out there). + +If you previously had code like this:: + + app.debug = DEBUG + app.secret_key = SECRET_KEY + +You no longer have to do that, instead you can just load a configuration +into the config object. How this works is outlined in :ref:`config`. + +Logging Integration +``````````````````` + +Flask now configures a logger for you with some basic and useful defaults. +If you run your application in production and want to profit from +automatic error logging, you might be interested in attaching a proper log +handler. Also you can start logging warnings and errors into the logger +when appropriately. For more information on that, read +:ref:`application-errors`. + +Categories for Flash Messages +````````````````````````````` + +Flash messages can now have categories attached. This makes it possible +to render errors, warnings or regular messages differently for example. +This is an opt-in feature because it requires some rethinking in the code. + +Read all about that in the :ref:`message-flashing-pattern` pattern. diff --git a/examples/flaskr/README b/examples/flaskr/README index 4a9a02c6..9ab20589 100644 --- a/examples/flaskr/README +++ b/examples/flaskr/README @@ -10,7 +10,9 @@ ~ How do I use it? - 1. edit the configuration in the flaskr.py file + 1. edit the configuration in the flaskr.py file or + export an FLASKR_SETTINGS environment variable + pointing to a configuration file. 2. fire up a python shell and run this: diff --git a/examples/flaskr/flaskr.py b/examples/flaskr/flaskr.py index f2a8b341..1df24293 100644 --- a/examples/flaskr/flaskr.py +++ b/examples/flaskr/flaskr.py @@ -24,13 +24,13 @@ PASSWORD = 'default' # create our little application :) app = Flask(__name__) -app.secret_key = SECRET_KEY -app.debug = DEBUG +app.config.from_object(__name__) +app.config.from_envvar('FLASKR_SETTINGS', silent=True) def connect_db(): """Returns a new connection to the database.""" - return sqlite3.connect(DATABASE) + return sqlite3.connect(app.config['DATABASE']) def init_db(): @@ -76,9 +76,9 @@ def add_entry(): def login(): error = None if request.method == 'POST': - if request.form['username'] != USERNAME: + if request.form['username'] != app.config['USERNAME']: error = 'Invalid username' - elif request.form['password'] != PASSWORD: + elif request.form['password'] != app.config['PASSWORD']: error = 'Invalid password' else: session['logged_in'] = True diff --git a/examples/flaskr/flaskr_tests.py b/examples/flaskr/flaskr_tests.py index 9421cca6..07e702c4 100644 --- a/examples/flaskr/flaskr_tests.py +++ b/examples/flaskr/flaskr_tests.py @@ -18,14 +18,14 @@ class FlaskrTestCase(unittest.TestCase): def setUp(self): """Before each test, set up a blank database""" - self.db_fd, flaskr.DATABASE = tempfile.mkstemp() + self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp() self.app = flaskr.app.test_client() flaskr.init_db() def tearDown(self): """Get rid of the database again after each test.""" os.close(self.db_fd) - os.unlink(flaskr.DATABASE) + os.unlink(flaskr.app.config['DATABASE']) def login(self, username, password): return self.app.post('/login', data=dict( @@ -45,18 +45,22 @@ class FlaskrTestCase(unittest.TestCase): def test_login_logout(self): """Make sure login and logout works""" - rv = self.login(flaskr.USERNAME, flaskr.PASSWORD) + rv = self.login(flaskr.app.config['USERNAME'], + flaskr.app.config['PASSWORD']) assert 'You were logged in' in rv.data rv = self.logout() assert 'You were logged out' in rv.data - rv = self.login(flaskr.USERNAME + 'x', flaskr.PASSWORD) + rv = self.login(flaskr.app.config['USERNAME'] + 'x', + flaskr.app.config['PASSWORD']) assert 'Invalid username' in rv.data - rv = self.login(flaskr.USERNAME, flaskr.PASSWORD + 'x') + rv = self.login(flaskr.app.config['USERNAME'], + flaskr.app.config['PASSWORD'] + 'x') assert 'Invalid password' in rv.data def test_messages(self): """Test that messages work""" - self.login(flaskr.USERNAME, flaskr.PASSWORD) + self.login(flaskr.app.config['USERNAME'], + flaskr.app.config['PASSWORD']) rv = self.app.post('/add', data=dict( title='', text='HTML allowed here' diff --git a/examples/minitwit/README b/examples/minitwit/README index 065674a9..ab946295 100644 --- a/examples/minitwit/README +++ b/examples/minitwit/README @@ -10,7 +10,9 @@ ~ How do I use it? - 1. edit the configuration in the minitwit.py file + 1. edit the configuration in the minitwit.py file or + export an MINITWIT_SETTINGS environment variable + pointing to a configuration file. 2. fire up a python shell and run this: diff --git a/examples/minitwit/minitwit.py b/examples/minitwit/minitwit.py index 07ffe4c7..b740bc25 100644 --- a/examples/minitwit/minitwit.py +++ b/examples/minitwit/minitwit.py @@ -27,11 +27,13 @@ SECRET_KEY = 'development key' # create our little application :) app = Flask(__name__) +app.config.from_object(__name__) +app.config.from_envvar('MINITWIT_SETTINGS', silent=True) def connect_db(): """Returns a new connection to the database.""" - return sqlite3.connect(DATABASE) + return sqlite3.connect(app.config['DATABASE']) def init_db(): @@ -237,12 +239,9 @@ def logout(): return redirect(url_for('public_timeline')) -# add some filters to jinja and set the secret key and debug mode -# from the configuration. +# add some filters to jinja app.jinja_env.filters['datetimeformat'] = format_datetime app.jinja_env.filters['gravatar'] = gravatar_url -app.secret_key = SECRET_KEY -app.debug = DEBUG if __name__ == '__main__': diff --git a/examples/minitwit/minitwit_tests.py b/examples/minitwit/minitwit_tests.py index 10962142..87741165 100644 --- a/examples/minitwit/minitwit_tests.py +++ b/examples/minitwit/minitwit_tests.py @@ -18,14 +18,14 @@ class MiniTwitTestCase(unittest.TestCase): def setUp(self): """Before each test, set up a blank database""" - self.db_fd, minitwit.DATABASE = tempfile.mkstemp() + self.db_fd, minitwit.app.config['DATABASE'] = tempfile.mkstemp() self.app = minitwit.app.test_client() minitwit.init_db() def tearDown(self): """Get rid of the database again after each test.""" os.close(self.db_fd) - os.unlink(minitwit.DATABASE) + os.unlink(minitwit.app.config['DATABASE']) # helper functions