diff --git a/CHANGES b/CHANGES index e551dbef..8fc7670b 100644 --- a/CHANGES +++ b/CHANGES @@ -24,6 +24,8 @@ Version 1.0 templates will be reloaded only if the application is running in debug mode. For higher performance it’s possible to disable that. - Added a workaround for a limitation in Python 3.3's namespace loader. +- Added support for explicit root paths when using Python 3.3's namespace + packages. Version 0.10.2 -------------- diff --git a/flask/app.py b/flask/app.py index 8d59fd30..6b6da433 100644 --- a/flask/app.py +++ b/flask/app.py @@ -118,6 +118,9 @@ class Flask(_PackageBoundObject): The `instance_path` and `instance_relative_config` parameters were added. + .. versionadded:: 1.0 + The `root_path` parameter was added. + :param import_name: the name of the application package :param static_url_path: can be used to specify a different path for the static files on the web. Defaults to the name @@ -137,6 +140,11 @@ class Flask(_PackageBoundObject): for loading the config are assumed to be relative to the instance path instead of the application root. + :param root_path: Flask by default will automatically calculate the path + to the root of the application. In certain situations + this cannot be achieved (for instance if the package + is a Python 3 namespace package) and needs to be + manually defined. """ #: The class that is used for request objects. See :class:`~flask.Request` @@ -327,9 +335,11 @@ class Flask(_PackageBoundObject): def __init__(self, import_name, static_path=None, static_url_path=None, static_folder='static', template_folder='templates', - instance_path=None, instance_relative_config=False): + instance_path=None, instance_relative_config=False, + root_path=None): _PackageBoundObject.__init__(self, import_name, - template_folder=template_folder) + template_folder=template_folder, + root_path=root_path) if static_path is not None: from warnings import warn warn(DeprecationWarning('static_path is now called ' diff --git a/flask/blueprints.py b/flask/blueprints.py index 4abc0ffa..924c5441 100644 --- a/flask/blueprints.py +++ b/flask/blueprints.py @@ -91,8 +91,10 @@ class Blueprint(_PackageBoundObject): def __init__(self, name, import_name, static_folder=None, static_url_path=None, template_folder=None, - url_prefix=None, subdomain=None, url_defaults=None): - _PackageBoundObject.__init__(self, import_name, template_folder) + url_prefix=None, subdomain=None, url_defaults=None, + root_path=None): + _PackageBoundObject.__init__(self, import_name, template_folder, + root_path=root_path) self.name = name self.url_prefix = url_prefix self.subdomain = subdomain diff --git a/flask/helpers.py b/flask/helpers.py index cd321e57..920c4c87 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -650,7 +650,20 @@ def get_root_path(import_name): else: # Fall back to imports. __import__(import_name) - filepath = sys.modules[import_name].__file__ + mod = sys.modules[import_name] + filepath = getattr(mod, '__file__', None) + + # If we don't have a filepath it might be because we are a + # namespace package. In this case we pick the root path from the + # first module that is contained in our package. + if filepath is None: + raise RuntimeError('No root path can be found for the provided ' + 'module "%s". This can happen because the ' + 'module came from an import hook that does ' + 'not provide file name information or because ' + 'it\'s a namespace package. In this case ' + 'the root path needs to be explictly ' + 'provided.' % import_name) # filepath is import_name.py for a module, or __init__.py for a package. return os.path.dirname(os.path.abspath(filepath)) @@ -762,7 +775,7 @@ class locked_cached_property(object): class _PackageBoundObject(object): - def __init__(self, import_name, template_folder=None): + def __init__(self, import_name, template_folder=None, root_path=None): #: The name of the package or module. Do not change this once #: it was set by the constructor. self.import_name = import_name @@ -771,8 +784,11 @@ class _PackageBoundObject(object): #: exposed. self.template_folder = template_folder + if root_path is None: + root_path = get_root_path(self.import_name) + #: Where is the app root located? - self.root_path = get_root_path(self.import_name) + self.root_path = root_path self._static_folder = None self._static_url_path = None