summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaudiu Popa <pcmanticore@gmail.com>2015-12-06 18:00:01 +0200
committerClaudiu Popa <pcmanticore@gmail.com>2015-12-06 18:00:01 +0200
commit4b4cdeb07b670ef2366c159ef675c336f3c2fb6f (patch)
tree124c8c98c493eb71e64826fb4f14ebf4a7f32dcf
parente4ecd3d2805abfbd6f6b457939d4026fa19446ce (diff)
downloadastroid-4b4cdeb07b670ef2366c159ef675c336f3c2fb6f.tar.gz
relative_to_absolute_name will now raise TooManyLevelsError when a relative import is trying to access something beyond the top-level package.
-rw-r--r--ChangeLog3
-rw-r--r--astroid/exceptions.py15
-rw-r--r--astroid/scoped_nodes.py4
-rw-r--r--astroid/tests/unittest_scoped_nodes.py13
4 files changed, 35 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
index 55a4602..c064fef 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,6 +2,9 @@ Change log for the astroid package (used to be astng)
=====================================================
--
+ * relative_to_absolute_name or methods calling it will now raise
+ TooManyLevelsError when a relative import was trying to
+ access something beyond the top-level package.
* AstroidBuildingException is now AstroidBuildingError. The first
name will exist until astroid 2.0.
diff --git a/astroid/exceptions.py b/astroid/exceptions.py
index 993e4b1..3cfadbd 100644
--- a/astroid/exceptions.py
+++ b/astroid/exceptions.py
@@ -58,6 +58,21 @@ class AstroidImportError(AstroidBuildingError):
"""Exception class used when a module can't be imported by astroid."""
+class TooManyLevelsError(AstroidImportError):
+ """Exception class which is raised when a relative import was beyond the top-level.
+
+ Standard attributes:
+ level: The level which was attempted.
+ name: the name of the module on which the relative import was attempted.
+ """
+ level = None
+ name = None
+
+ def __init__(self, message='Relative import with too many levels '
+ '({level}) for module {name!r}', **kws):
+ super(TooManyLevelsError, self).__init__(message, **kws)
+
+
class AstroidSyntaxError(AstroidBuildingError):
"""Exception class used when a module can't be parsed."""
diff --git a/astroid/scoped_nodes.py b/astroid/scoped_nodes.py
index de4b394..25d28ae 100644
--- a/astroid/scoped_nodes.py
+++ b/astroid/scoped_nodes.py
@@ -442,6 +442,10 @@ class Module(LocalsDictNodeNG):
if level:
if self.package:
level = level - 1
+ if level and self.name.count('.') < level:
+ raise exceptions.TooManyLevelsError(
+ level=level, name=self.name)
+
package_name = self.name.rsplit('.', level)[0]
elif self.package:
package_name = self.name
diff --git a/astroid/tests/unittest_scoped_nodes.py b/astroid/tests/unittest_scoped_nodes.py
index 6f215e4..d49154d 100644
--- a/astroid/tests/unittest_scoped_nodes.py
+++ b/astroid/tests/unittest_scoped_nodes.py
@@ -32,6 +32,7 @@ from astroid.exceptions import (
InferenceError, AttributeInferenceError,
NoDefault, ResolveError, MroError,
InconsistentMroError, DuplicateBasesError,
+ TooManyLevelsError,
)
from astroid.bases import (
BUILTINS, Instance,
@@ -160,6 +161,18 @@ class ModuleNodeTest(ModuleLoader, unittest.TestCase):
modname = mod.relative_to_absolute_name('', 1)
self.assertEqual(modname, 'very.multi')
+ def test_relative_to_absolute_name_beyond_top_level(self):
+ mod = nodes.Module('a.b.c', '')
+ mod.package = True
+ for level in (5, 4):
+ with self.assertRaises(TooManyLevelsError) as cm:
+ mod.relative_to_absolute_name('test', level)
+
+ expected = ("Relative import with too many levels "
+ "({level}) for module {name!r}".format(
+ level=level - 1, name=mod.name))
+ self.assertEqual(expected, str(cm.exception))
+
def test_import_1(self):
data = '''from . import subpackage'''
sys.path.insert(0, resources.find('data'))