summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Petrello <lists@ryanpetrello.com>2015-01-06 11:16:45 -0500
committerRyan Petrello <lists@ryanpetrello.com>2015-01-06 13:47:29 -0500
commit868ab86e7666eea619c5371fe2b32f88d442c9a1 (patch)
tree5fc93ff9180f59a71d73312fbf030b4b7729b448
parent97306eda458f7f54d3eac7df0bda4165ea9fd1e7 (diff)
downloadpecan-868ab86e7666eea619c5371fe2b32f88d442c9a1.tar.gz
Improve ImportError verbosity for configuration files.
Fixes bug: 1408008 Change-Id: Iac73b6b0017794de9dea4180d043c18d3fb6d942
-rw-r--r--pecan/configuration.py23
-rw-r--r--pecan/tests/test_conf.py16
2 files changed, 38 insertions, 1 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/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')