diff options
-rw-r--r-- | lint.py | 67 | ||||
-rw-r--r-- | test/unittest_lint.py | 161 |
2 files changed, 158 insertions, 70 deletions
@@ -33,6 +33,7 @@ from pylint.checkers import utils #pylint: disable=unused-import import sys import os import tokenize +from contextlib import contextmanager from operator import attrgetter from warnings import warn @@ -71,8 +72,9 @@ import six def _get_python_path(filepath): - dirname = os.path.dirname(os.path.realpath( - os.path.expanduser(filepath))) + dirname = os.path.realpath(os.path.expanduser(filepath)) + if not os.path.isdir(dirname): + dirname = os.path.dirname(dirname) while True: if not os.path.exists(os.path.join(dirname, "__init__.py")): return dirname @@ -411,17 +413,6 @@ class PyLinter(OptionsManagerMixIn, MessagesHandlerMixIn, ReportsHandlerMixIn, if not self.reporter: self._load_reporter() - def prepare_import_path(self, args): - """Prepare sys.path for running the linter checks.""" - if len(args) == 1: - sys.path.insert(0, _get_python_path(args[0])) - else: - sys.path.insert(0, os.getcwd()) - - def cleanup_import_path(self): - """Revert any changes made to sys.path in prepare_import_path.""" - sys.path.pop(0) - def load_plugin_modules(self, modnames): """take a list of module names which are pylint plugins and load and register them @@ -1015,6 +1006,31 @@ def preprocess_options(args, search_for): else: i += 1 + +@contextmanager +def fix_import_path(args): + """Prepare sys.path for running the linter checks. + + Within this context, each of the given arguments is importable. + Paths are added to sys.path in corresponding order to the arguments. + We avoid adding duplicate directories to sys.path. + `sys.path` is reset to its original value upon exitign this context. + """ + orig = list(sys.path) + changes = [] + for arg in args: + path = _get_python_path(arg) + if path in changes: + continue + else: + changes.append(path) + sys.path[:] = changes + sys.path + try: + yield + finally: + sys.path[:] = orig + + class Run(object): """helper class to use as main for pylint : @@ -1184,20 +1200,19 @@ group are mutually exclusive.'), # insert current working directory to the python path to have a correct # behaviour - linter.prepare_import_path(args) - if self.linter.config.profile: - print('** profiled run', file=sys.stderr) - import cProfile, pstats - cProfile.runctx('linter.check(%r)' % args, globals(), locals(), - 'stones.prof') - data = pstats.Stats('stones.prof') - data.strip_dirs() - data.sort_stats('time', 'calls') - data.print_stats(30) - else: - linter.check(args) + with fix_import_path(args): + if self.linter.config.profile: + print('** profiled run', file=sys.stderr) + import cProfile, pstats + cProfile.runctx('linter.check(%r)' % args, globals(), locals(), + 'stones.prof') + data = pstats.Stats('stones.prof') + data.strip_dirs() + data.sort_stats('time', 'calls') + data.print_stats(30) + else: + linter.check(args) linter.generate_reports() - linter.cleanup_import_path() if exit: sys.exit(self.linter.msg_status) diff --git a/test/unittest_lint.py b/test/unittest_lint.py index bef9c14..c006406 100644 --- a/test/unittest_lint.py +++ b/test/unittest_lint.py @@ -12,18 +12,19 @@ # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +from contextlib import contextmanager import cStringIO import sys import os import tempfile from shutil import rmtree from os import getcwd, chdir -from os.path import join, basename, dirname, isdir, abspath +from os.path import join, basename, dirname, isdir, abspath, sep import unittest from logilab.common.compat import reload -from pylint import config +from pylint import config, lint from pylint.lint import PyLinter, Run, UnknownMessage, preprocess_options, \ ArgumentPreprocessingError from pylint.utils import MSG_STATE_SCOPE_CONFIG, MSG_STATE_SCOPE_MODULE, MSG_STATE_CONFIDENCE, \ @@ -57,7 +58,28 @@ class GetNoteMessageTC(unittest.TestCase): HERE = abspath(dirname(__file__)) INPUTDIR = join(HERE, 'input') -def create_files(paths, chroot): + +@contextmanager +def tempdir(): + """Create a temp directory and change the current location to it. + + This is supposed to be used with a *with* statement. + """ + tmp = tempfile.mkdtemp() + + # Get real path of tempfile, otherwise test fail on mac os x + current_dir = getcwd() + chdir(tmp) + abs_tmp = abspath('.') + + try: + yield abs_tmp + finally: + chdir(current_dir) + rmtree(abs_tmp) + + +def create_files(paths, chroot='.'): """Creates directories and files found in <path>. :param paths: list of relative paths to files or directories @@ -94,6 +116,78 @@ def create_files(paths, chroot): open(filepath, 'w').close() +class SysPathFixupTC(unittest.TestCase): + def setUp(self): + self.orig = list(sys.path) + self.fake = [1, 2, 3] + sys.path[:] = self.fake + + def tearDown(self): + sys.path[:] = self.orig + + def test_no_args(self): + with lint.fix_import_path([]): + self.assertEqual(sys.path, self.fake) + self.assertEqual(sys.path, self.fake) + + def test_one_arg(self): + with tempdir() as chroot: + create_files(['a/b/__init__.py']) + expected = [join(chroot, 'a')] + self.fake + + cases = ( + ['a/b/'], + ['a/b'], + ['a/b/__init__.py'], + ['a/'], + ['a'], + ) + + self.assertEqual(sys.path, self.fake) + for case in cases: + with lint.fix_import_path(case): + self.assertEqual(sys.path, expected) + self.assertEqual(sys.path, self.fake) + + def test_two_similar_args(self): + with tempdir() as chroot: + create_files(['a/b/__init__.py', 'a/c/__init__.py']) + expected = [join(chroot, 'a')] + self.fake + + cases = ( + ['a/b', 'a/c'], + ['a/c/', 'a/b/'], + ['a/b/__init__.py', 'a/c/__init__.py'], + ['a', 'a/c/__init__.py'], + ) + + self.assertEqual(sys.path, self.fake) + for case in cases: + with lint.fix_import_path(case): + self.assertEqual(sys.path, expected) + self.assertEqual(sys.path, self.fake) + + def test_more_args(self): + with tempdir() as chroot: + create_files(['a/b/c/__init__.py', 'a/d/__init__.py', 'a/e/f.py']) + expected = [ + join(chroot, suffix) + for suffix in [sep.join(('a', 'b')), 'a', sep.join(('a', 'e'))] + ] + self.fake + + cases = ( + ['a/b/c/__init__.py', 'a/d/__init__.py', 'a/e/f.py'], + ['a/b/c', 'a', 'a/e'], + ['a/b/c', 'a', 'a/b/c', 'a/e', 'a'], + ) + + self.assertEqual(sys.path, self.fake) + for case in cases: + with lint.fix_import_path(case): + self.assertEqual(sys.path, expected) + self.assertEqual(sys.path, self.fake) + + class PyLinterTC(unittest.TestCase): def setUp(self): @@ -428,18 +522,10 @@ class ConfigTC(unittest.TestCase): reload(config) def test_pylintrc_parentdir(self): - chroot = tempfile.mkdtemp() - - # Get real path of tempfile, otherwise test fail on mac os x - cdir = getcwd() - chdir(chroot) - chroot = abspath('.') - chdir(cdir) + with tempdir() as chroot: - try: create_files(['a/pylintrc', 'a/b/__init__.py', 'a/b/pylintrc', - 'a/b/c/__init__.py', 'a/b/c/d/__init__.py'], chroot) - os.chdir(chroot) + 'a/b/c/__init__.py', 'a/b/c/d/__init__.py']) fake_home = tempfile.mkdtemp('fake-home') home = os.environ[HOME] try: @@ -456,39 +542,26 @@ class ConfigTC(unittest.TestCase): for basedir, expected in results.items(): os.chdir(join(chroot, basedir)) self.assertEqual(config.find_pylintrc(), expected) - finally: - os.chdir(HERE) - rmtree(chroot) def test_pylintrc_parentdir_no_package(self): - chroot = tempfile.mkdtemp() - - # Get real path of tempfile, otherwise test fail on mac os x - cdir = getcwd() - chdir(chroot) - chroot = abspath('.') - chdir(cdir) - - fake_home = tempfile.mkdtemp('fake-home') - home = os.environ[HOME] - os.environ[HOME] = fake_home - try: - create_files(['a/pylintrc', 'a/b/pylintrc', 'a/b/c/d/__init__.py'], chroot) - os.chdir(chroot) - self.assertEqual(config.find_pylintrc(), None) - results = {'a' : join(chroot, 'a', 'pylintrc'), - 'a/b' : join(chroot, 'a', 'b', 'pylintrc'), - 'a/b/c' : None, - 'a/b/c/d' : None, - } - for basedir, expected in results.items(): - os.chdir(join(chroot, basedir)) - self.assertEqual(config.find_pylintrc(), expected) - finally: - os.environ[HOME] = home - rmtree(fake_home, ignore_errors=True) - os.chdir(HERE) - rmtree(chroot) + with tempdir() as chroot: + fake_home = tempfile.mkdtemp('fake-home') + home = os.environ[HOME] + os.environ[HOME] = fake_home + try: + create_files(['a/pylintrc', 'a/b/pylintrc', 'a/b/c/d/__init__.py']) + self.assertEqual(config.find_pylintrc(), None) + results = {'a' : join(chroot, 'a', 'pylintrc'), + 'a/b' : join(chroot, 'a', 'b', 'pylintrc'), + 'a/b/c' : None, + 'a/b/c/d' : None, + } + for basedir, expected in results.items(): + os.chdir(join(chroot, basedir)) + self.assertEqual(config.find_pylintrc(), expected) + finally: + os.environ[HOME] = home + rmtree(fake_home, ignore_errors=True) class PreprocessOptionsTC(unittest.TestCase): |