diff options
-rw-r--r-- | pecan/configuration.py | 23 | ||||
-rw-r--r-- | pecan/core.py | 15 | ||||
-rw-r--r-- | pecan/middleware/static.py | 1 | ||||
-rw-r--r-- | pecan/tests/test_conf.py | 16 |
4 files changed, 46 insertions, 9 deletions
diff --git a/pecan/configuration.py b/pecan/configuration.py index b4109a8..42a9b50 100644 --- a/pecan/configuration.py +++ b/pecan/configuration.py @@ -4,6 +4,11 @@ import os import six +if six.PY3: + from importlib.machinery import SourceFileLoader +else: + import imp + IDENTIFIER = re.compile(r'[a-z_](\w)*$', re.IGNORECASE) @@ -152,8 +157,24 @@ def conf_from_file(filepath): if not os.path.isfile(abspath): raise RuntimeError('`%s` is not a file.' % abspath) + # First, make sure the code will actually compile (and has no SyntaxErrors) with open(abspath, 'rb') as f: - exec(compile(f.read(), abspath, 'exec'), globals(), conf_dict) + compiled = compile(f.read(), abspath, 'exec') + + # Next, attempt to actually import the file as a module. + # This provides more verbose import-related error reporting than exec() + absname, _ = os.path.splitext(abspath) + basepath, module_name = absname.rsplit(os.sep, 1) + if six.PY3: + SourceFileLoader(module_name, abspath).load_module(module_name) + else: + imp.load_module( + module_name, + *imp.find_module(module_name, [basepath]) + ) + + # If we were able to import as a module, actually exec the compiled code + exec(compiled, globals(), conf_dict) conf_dict['__file__'] = abspath return conf_from_dict(conf_dict) diff --git a/pecan/core.py b/pecan/core.py index 173210b..af5d943 100644 --- a/pecan/core.py +++ b/pecan/core.py @@ -390,18 +390,19 @@ class PecanBase(object): return args, varargs, kwargs def render(self, template, namespace): - renderer = self.renderers.get( - self.default_renderer, - self.template_path - ) if template == 'json': renderer = self.renderers.get('json', self.template_path) - if ':' in template: + elif ':' in template: + renderer_name, template = template.split(':', 1) + renderer = self.renderers.get( + renderer_name, + self.template_path + ) + else: renderer = self.renderers.get( - template.split(':')[0], + self.default_renderer, self.template_path ) - template = template.split(':')[1] return renderer.render(template, namespace) def find_controller(self, state): diff --git a/pecan/middleware/static.py b/pecan/middleware/static.py index a05a7f8..ab6dd4e 100644 --- a/pecan/middleware/static.py +++ b/pecan/middleware/static.py @@ -115,7 +115,6 @@ class StaticFileMiddleware(object): def __init__(self, app, directory, fallback_mimetype='text/plain'): self.app = app - self.directory = directory self.loader = self.get_directory_loader(directory) self.fallback_mimetype = fallback_mimetype diff --git a/pecan/tests/test_conf.py b/pecan/tests/test_conf.py index c273b6f..e682885 100644 --- a/pecan/tests/test_conf.py +++ b/pecan/tests/test_conf.py @@ -144,6 +144,22 @@ class TestConf(PecanTestCase): f.name ) + def test_config_with_non_package_relative_import(self): + from pecan import configuration + with tempfile.NamedTemporaryFile('wb', suffix='.py') as f: + f.write(b_('\n'.join(['from . import variables']))) + f.flush() + configuration.Config({}) + + try: + configuration.conf_from_file(f.name) + except (ValueError, SystemError) as e: + assert 'relative import' in str(e) + else: + raise AssertionError( + "A relative import-related error should have been raised" + ) + def test_config_with_bad_import(self): from pecan import configuration path = ('bad', 'importerror.py') |