summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaudiu Popa <pcmanticore@gmail.com>2015-12-06 17:06:11 +0200
committerClaudiu Popa <pcmanticore@gmail.com>2015-12-06 17:06:11 +0200
commit97c9092852d69f9b98b1276e3d10e9d29c7c0224 (patch)
tree7d52ab7622495bcf0961727d462ca6bb90e4c7e0
parentcf052851cab3176fc6442114cbdc8194b24b5b2e (diff)
downloadastroid-97c9092852d69f9b98b1276e3d10e9d29c7c0224.tar.gz
Add two new exceptions, AstroidImportError and AstroidSyntaxError.
They are subclasses of AstroidBuildingException and are raised when a module can't be imported from various reasons. Also do_import_module lets the errors to bubble up without converting them to InferenceError. This particular conversion happens only during the inference.
-rw-r--r--ChangeLog8
-rw-r--r--astroid/builder.py28
-rw-r--r--astroid/exceptions.py10
-rw-r--r--astroid/inference.py24
-rw-r--r--astroid/manager.py12
-rw-r--r--astroid/mixins.py20
-rw-r--r--astroid/tests/testdata/python2/data/invalid_encoding.py1
-rw-r--r--astroid/tests/testdata/python3/data/invalid_encoding.py1
-rw-r--r--astroid/tests/unittest_builder.py8
9 files changed, 63 insertions, 49 deletions
diff --git a/ChangeLog b/ChangeLog
index 5e2b17b..5003418 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,14 @@
Change log for the astroid package (used to be astng)
=====================================================
+--
+
+ * Add two new exceptions, AstroidImportError and AstroidSyntaxError.
+ They are subclasses of AstroidBuildingException and are raised when
+ a module can't be imported from various reasons.
+ Also do_import_module lets the errors to bubble up without converting
+ them to InferenceError. This particular conversion happens only
+ during the inference.
* Revert to using printf-style formatting in as_string, in order
to avoid a potential problem with encodings when using .format.
diff --git a/astroid/builder.py b/astroid/builder.py
index 9e06ef7..089cdd0 100644
--- a/astroid/builder.py
+++ b/astroid/builder.py
@@ -47,13 +47,7 @@ if sys.version_info >= (3, 0):
with open(filename, 'rb') as byte_stream:
encoding = detect_encoding(byte_stream.readline)[0]
stream = open(filename, 'r', newline=None, encoding=encoding)
- try:
- data = stream.read()
- except UnicodeError: # wrong encoding
- # detect_encoding returns utf-8 if no encoding specified
- util.reraise(exceptions.AstroidBuildingException(
- 'Wrong ({encoding}) or no encoding specified for {filename}.',
- encoding=encoding, filename=filename))
+ data = stream.read()
return stream, encoding, data
else:
@@ -128,9 +122,14 @@ class AstroidBuilder(raw_building.InspectBuilder):
'Unable to load file {path}:\n{error}',
modname=modname, path=path, error=exc))
except (SyntaxError, LookupError) as exc:
- util.reraise(exceptions.AstroidBuildingException(
+ util.reraise(exceptions.AstroidSyntaxError(
'Python 3 encoding specification error or unknown encoding:\n'
'{error}', modname=modname, path=path, error=exc))
+ except UnicodeError: # wrong encoding
+ # detect_encoding returns utf-8 if no encoding specified
+ util.reraise(exceptions.AstroidBuildingException(
+ 'Wrong ({encoding}) or no encoding specified for {filename}.',
+ encoding=encoding, filename=filename))
with stream:
# get module name if necessary
if modname is None:
@@ -171,17 +170,10 @@ class AstroidBuilder(raw_building.InspectBuilder):
"""Build tree node from data and add some informations"""
try:
node = _parse(data + '\n')
- except (TypeError, ValueError) as exc:
- util.reraise(exceptions.AstroidBuildingException(
+ except (TypeError, ValueError, SyntaxError) as exc:
+ util.reraise(exceptions.AstroidSyntaxError(
'Parsing Python code failed:\n{error}',
source=data, modname=modname, path=path, error=exc))
- except SyntaxError as exc:
- # Pass the entire exception object to AstroidBuildingException,
- # since pylint uses this as an introspection method,
- # in order to find what error happened.
- util.reraise(exceptions.AstroidBuildingException(
- 'Syntax error in Python source: {error}',
- source=data, modname=modname, path=path, error=exc))
if path is not None:
node_file = os.path.abspath(path)
else:
@@ -210,7 +202,7 @@ class AstroidBuilder(raw_building.InspectBuilder):
if name == '*':
try:
imported = node.do_import_module()
- except exceptions.InferenceError:
+ except exceptions.AstroidBuildingException:
continue
for name in imported.wildcard_import_names():
node.parent.set_local(name, node)
diff --git a/astroid/exceptions.py b/astroid/exceptions.py
index e92abc5..3437a1f 100644
--- a/astroid/exceptions.py
+++ b/astroid/exceptions.py
@@ -54,6 +54,14 @@ class AstroidBuildingException(AstroidError):
super(AstroidBuildingException, self).__init__(message, **kws)
+class AstroidImportError(AstroidBuildingException):
+ """Exception class used when a module can't be imported by astroid."""
+
+
+class AstroidSyntaxError(AstroidBuildingException):
+ """Exception class used when a module can't be parsed."""
+
+
class NoDefault(AstroidError):
"""raised by function's `default_value` method when an argument has
no default value
@@ -96,9 +104,11 @@ class MroError(ResolveError):
for m in self.mros)
return self.message.format(mros=mro_names, cls=self.cls)
+
class DuplicateBasesError(MroError):
"""Error raised when there are duplicate bases in the same class bases."""
+
class InconsistentMroError(MroError):
"""Error raised when a class's MRO is inconsistent."""
diff --git a/astroid/inference.py b/astroid/inference.py
index 45f31ff..7b2aa6b 100644
--- a/astroid/inference.py
+++ b/astroid/inference.py
@@ -130,10 +130,16 @@ def infer_import(self, context=None, asname=True):
name = context.lookupname
if name is None:
raise exceptions.InferenceError(node=self, context=context)
- if asname:
- yield self.do_import_module(self.real_name(name))
- else:
- yield self.do_import_module(name)
+
+ try:
+ if asname:
+ yield self.do_import_module(self.real_name(name))
+ else:
+ yield self.do_import_module(name)
+ except exceptions.AstroidBuildingException as exc:
+ util.reraise(exceptions.InferenceError(node=self, error=exc,
+ context=context))
+
nodes.Import._infer = infer_import
@@ -152,7 +158,13 @@ def infer_import_from(self, context=None, asname=True):
raise exceptions.InferenceError(node=self, context=context)
if asname:
name = self.real_name(name)
- module = self.do_import_module()
+
+ try:
+ module = self.do_import_module()
+ except exceptions.AstroidBuildingException as exc:
+ util.reraise(exceptions.InferenceError(node=self, error=exc,
+ context=context))
+
try:
context = contextmod.copy_context(context)
context.lookupname = name
@@ -277,7 +289,7 @@ def infer_subscript(self, context=None):
except (IndexError, TypeError, AttributeError) as exc:
util.reraise(exceptions.InferenceError(node=self, error=exc,
context=context))
-
+
# Prevent inferring if the inferred subscript
# is the same as the original subscripted object.
if self is assigned or assigned is util.Uninferable:
diff --git a/astroid/manager.py b/astroid/manager.py
index 55a09be..f2aa9d2 100644
--- a/astroid/manager.py
+++ b/astroid/manager.py
@@ -127,16 +127,16 @@ class AstroidManager(object):
try:
module = modutils.load_module_from_name(modname)
except Exception as ex: # pylint: disable=broad-except
- util.reraise(exceptions.AstroidBuildingException(
+ util.reraise(exceptions.AstroidImportError(
'Loading {modname} failed with:\n{error}',
modname=modname, path=filepath, error=ex))
return self.ast_from_module(module, modname)
elif mp_type == imp.PY_COMPILED:
- raise exceptions.AstroidBuildingException(
+ raise exceptions.AstroidImportError(
"Unable to load compiled module {modname}.",
modname=modname, path=filepath)
if filepath is None:
- raise exceptions.AstroidBuildingException(
+ raise exceptions.AstroidImportError(
"Can't find a file for module {modname}.",
modname=modname)
return self.ast_from_file(filepath, modname, fallback=False)
@@ -182,7 +182,7 @@ class AstroidManager(object):
modname.split('.'), context_file=contextfile)
traceback = sys.exc_info()[2]
except ImportError as ex:
- value = exceptions.AstroidBuildingException(
+ value = exceptions.AstroidImportError(
'Failed to import module {modname} with error:\n{error}.',
modname=modname, error=ex)
traceback = sys.exc_info()[2]
@@ -232,7 +232,7 @@ class AstroidManager(object):
'Unable to get module for {class_repr}.',
cls=klass, class_repr=safe_repr(klass)))
except Exception as ex: # pylint: disable=broad-except
- util.reraise(exceptions.AstroidBuildingException(
+ util.reraise(exceptions.AstroidImportError(
'Unexpected error while retrieving module for {class_repr}:\n'
'{error}', cls=klass, class_repr=safe_repr(klass), error=ex))
try:
@@ -242,7 +242,7 @@ class AstroidManager(object):
'Unable to get name for {class_repr}:\n',
cls=klass, class_repr=safe_repr(klass)))
except Exception as ex: # pylint: disable=broad-except
- util.reraise(exceptions.AstroidBuildingException(
+ util.reraise(exceptions.AstroidImportError(
'Unexpected error while retrieving name for {class_repr}:\n'
'{error}', cls=klass, class_repr=safe_repr(klass), error=ex))
# take care, on living object __module__ is regularly wrong :(
diff --git a/astroid/mixins.py b/astroid/mixins.py
index 6ee7b4d..e67c9af 100644
--- a/astroid/mixins.py
+++ b/astroid/mixins.py
@@ -125,23 +125,9 @@ class ImportFromMixin(FilterStmtsMixin):
if mymodule.relative_to_absolute_name(modname, level) == mymodule.name:
# FIXME: we used to raise InferenceError here, but why ?
return mymodule
- try:
- return mymodule.import_module(modname, level=level,
- relative_only=level and level >= 1)
- except exceptions.AstroidBuildingException as ex:
- if isinstance(getattr(ex, 'error', None), SyntaxError):
- util.reraise(exceptions.InferenceError(
- 'Could not import {modname} because of SyntaxError:\n'
- '{syntax_error}', modname=modname, syntax_error=ex.error,
- import_node=self))
- util.reraise(exceptions.InferenceError('Could not import {modname}.',
- modname=modname,
- import_node=self))
- except SyntaxError as ex:
- util.reraise(exceptions.InferenceError(
- 'Could not import {modname} because of SyntaxError:\n'
- '{syntax_error}', modname=modname, syntax_error=ex,
- import_node=self))
+
+ return mymodule.import_module(modname, level=level,
+ relative_only=level and level >= 1)
def real_name(self, asname):
"""get name from 'as' name"""
diff --git a/astroid/tests/testdata/python2/data/invalid_encoding.py b/astroid/tests/testdata/python2/data/invalid_encoding.py
new file mode 100644
index 0000000..dddd208
--- /dev/null
+++ b/astroid/tests/testdata/python2/data/invalid_encoding.py
@@ -0,0 +1 @@
+# -*- coding: lala -*- \ No newline at end of file
diff --git a/astroid/tests/testdata/python3/data/invalid_encoding.py b/astroid/tests/testdata/python3/data/invalid_encoding.py
new file mode 100644
index 0000000..dddd208
--- /dev/null
+++ b/astroid/tests/testdata/python3/data/invalid_encoding.py
@@ -0,0 +1 @@
+# -*- coding: lala -*- \ No newline at end of file
diff --git a/astroid/tests/unittest_builder.py b/astroid/tests/unittest_builder.py
index dc161d0..cb5bba9 100644
--- a/astroid/tests/unittest_builder.py
+++ b/astroid/tests/unittest_builder.py
@@ -255,11 +255,11 @@ class BuilderTest(unittest.TestCase):
self.builder = builder.AstroidBuilder()
def test_data_build_null_bytes(self):
- with self.assertRaises(exceptions.AstroidBuildingException):
+ with self.assertRaises(exceptions.AstroidSyntaxError):
self.builder.string_build('\x00')
def test_data_build_invalid_x_escape(self):
- with self.assertRaises(exceptions.AstroidBuildingException):
+ with self.assertRaises(exceptions.AstroidSyntaxError):
self.builder.string_build('"\\x1"')
def test_missing_newline(self):
@@ -705,6 +705,10 @@ class FileBuildTest(unittest.TestCase):
self.assertEqual(len(_locals), 3)
self.assertEqual(keys, ['autre', 'local', 'self'])
+ def test_unknown_encoding(self):
+ with self.assertRaises(exceptions.AstroidSyntaxError):
+ resources.build_file('data/invalid_encoding.py')
+
class ModuleBuildTest(resources.SysPathSetup, FileBuildTest):