Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Implemented instance paths

  • Loading branch information...
commit 153ecbc9202f249c4037602115ed2c2b5785550d 1 parent 6387661
@mitsuhiko authored
Showing with 94 additions and 8 deletions.
  1. +5 −0 CHANGES
  2. +71 −8 flask/app.py
  3. +18 −0 tests/flask_tests.py
View
5 CHANGES
@@ -26,6 +26,11 @@ Relase date to be decided, codename to be chosen.
- Malformed JSON data will now trigger a bad request HTTP exception instead
of a value error which usually would result in a 500 internal server
error if not handled. This is a backwards incompatible change.
+- Applications now not only have a root path where the resources and modules
+ are located but also an instane path which is the designated place to
+ drop files that are modified at runtime (uploads etc.). Also this is
+ conceptionally only instance depending and outside version control so it's
+ the perfect place to put configuration files etc.
Version 0.7.3
-------------
View
79 flask/app.py
@@ -11,6 +11,7 @@
from __future__ import with_statement
+import os
import sys
from threading import Lock
from datetime import timedelta
@@ -103,14 +104,33 @@ class Flask(_PackageBoundObject):
pick up SQL queries in `yourapplication.app` and not
`yourapplication.views.frontend`)
- .. versionadded:: 0.5
- The `static_path` parameter was added.
+ .. versionadded:: 0.7
+ The `static_url_path`, `static_folder`, and `template_folder`
+ parameters were added.
+
+ .. versionadded:: 0.8
+ The `instance_path` and `instance_relative_config` parameters were
+ added.
:param import_name: the name of the application package
- :param static_path: can be used to specify a different path for the
- static files on the web. Defaults to ``/static``.
- This does not affect the folder the files are served
- *from*.
+ :param static_url_path: can be used to specify a different path for the
+ static files on the web. Defaults to the name
+ of the `static_folder` folder.
+ :param static_folder: the folder with static files that should be served
+ at `static_url_path`. Defaults to the ``'static'``
+ folder in the root path of the application.
+ :param template_folder: the folder that contains the templates that should
+ be used by the application. Defaults to
+ ``'templates'`` folder in the root path of the
+ application.
+ :param instance_path: An alternative instance path for the application.
+ By default the folder ``'instance'`` next to the
+ package or module is assumed to be the instance
+ path.
+ :param instance_relative_config: if set to `True` relative filenames
+ for loading the config are assumed to
+ be relative to the instance path instead
+ of the application root.
"""
#: The class that is used for request objects. See :class:`~flask.Request`
@@ -238,7 +258,8 @@ class Flask(_PackageBoundObject):
session_interface = SecureCookieSessionInterface()
def __init__(self, import_name, static_path=None, static_url_path=None,
- static_folder='static', template_folder='templates'):
+ static_folder='static', template_folder='templates',
+ instance_path=None, instance_relative_config=False):
_PackageBoundObject.__init__(self, import_name,
template_folder=template_folder)
if static_path is not None:
@@ -251,11 +272,21 @@ def __init__(self, import_name, static_path=None, static_url_path=None,
self.static_url_path = static_url_path
if static_folder is not None:
self.static_folder = static_folder
+ if instance_path is None:
+ instance_path = self.auto_find_instance_path()
+ elif not os.path.isabs(instance_path):
+ raise ValueError('If an instance path is provided it must be '
+ 'absolute. A relative path was given instead.')
+
+ #: Holds the path to the instance folder.
+ #:
+ #: .. versionadded:: 0.8
+ self.instance_path = instance_path
#: The configuration dictionary as :class:`Config`. This behaves
#: exactly like a regular dictionary but supports additional methods
#: to load a config from files.
- self.config = Config(self.root_path, self.default_config)
+ self.config = self.make_config(instance_relative_config)
# Prepare the deferred setup of the logger.
self._logger = None
@@ -491,6 +522,38 @@ def got_first_request(self):
"""
return self._got_first_request
+ def make_config(self, instance_relative=False):
+ """Used to create the config attribute by the Flask constructor.
+ The `instance_relative` parameter is passed in from the constructor
+ of Flask (there named `instance_relative_config`) and indicates if
+ the config should be relative to the instance path or the root path
+ of the application.
+
+ .. versionadded:: 0.8
+ """
+ root_path = self.root_path
+ if instance_relative:
+ root_path = self.instance_path
+ return Config(root_path, self.default_config)
+
+ def auto_find_instance_path(self):
+ """Tries to locate the instance path if it was not provided to the
+ constructor of the application class. It will basically calculate
+ the path to a folder named ``instance`` next to your main file or
+ the package.
+
+ .. versionadded:: 0.8
+ """
+ root_mod = sys.modules[self.import_name.split('.')[0]]
+ instance_path = None
+ if hasattr(root_mod, '__path__'):
+ package_dir = os.path.dirname(root_mod.__file__)
+ instance_path = os.path.join(package_dir, os.path.pardir)
+ else:
+ instance_path = os.path.dirname(root_mod.__file__)
+ basedir = os.path.normpath(os.path.abspath(instance_path))
+ return os.path.join(basedir, 'instance')
+
def create_jinja_environment(self):
"""Creates the Jinja2 environment based on :attr:`jinja_options`
and :meth:`select_jinja_autoescape`. Since 0.7 this also adds
View
18 tests/flask_tests.py
@@ -1005,6 +1005,24 @@ def foo():
rv = c.post('/foo', data={}, follow_redirects=True)
self.assertEqual(rv.data, 'success')
+ def test_basic_instance_paths(self):
+ here = os.path.abspath(os.path.dirname(__file__))
+ app = flask.Flask(__name__)
+ self.assertEqual(app.instance_path, os.path.join(here, 'instance'))
+
+ app = flask.Flask(__name__, instance_path=here)
+ self.assertEqual(app.instance_path, here)
+
+ try:
+ flask.Flask(__name__, instance_path='instance')
+ except ValueError, e:
+ self.assert_('must be absolute' in str(e))
+ else:
+ self.fail('Expected value error')
+
+ from blueprintapp import app
+ self.assertEqual(app.instance_path, os.path.join(here, 'instance'))
+
class JSONTestCase(unittest.TestCase):

1 comment on commit 153ecbc

@svieira

Chuckles I was just working on a wrapper for my application to enable just this sort of functionality -- and then I check and see that you've just checked this in ... awesome work @mitsuhiko! Thank you!

Please sign in to comment.
Something went wrong with that request. Please try again.