From 4b4cdeb07b670ef2366c159ef675c336f3c2fb6f Mon Sep 17 00:00:00 2001 From: Claudiu Popa Date: Sun, 6 Dec 2015 18:00:01 +0200 Subject: relative_to_absolute_name will now raise TooManyLevelsError when a relative import is trying to access something beyond the top-level package. --- ChangeLog | 3 +++ astroid/exceptions.py | 15 +++++++++++++++ astroid/scoped_nodes.py | 4 ++++ astroid/tests/unittest_scoped_nodes.py | 13 +++++++++++++ 4 files changed, 35 insertions(+) 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')) -- cgit v1.2.1