|
3 | 3 | from .environment import env |
4 | 4 | from .utils import silence_stderr |
5 | 5 |
|
| 6 | +import os |
6 | 7 | import os.path |
| 8 | +import re |
| 9 | +import subprocess |
| 10 | +import tempfile |
7 | 11 |
|
8 | | - |
| 12 | +from pylama.errors import Error as LintError |
9 | 13 | from pylama.lint.extensions import LINTERS |
10 | 14 |
|
11 | 15 | try: |
|
15 | 19 | pass |
16 | 20 |
|
17 | 21 |
|
| 22 | +def __maybe_list_to_str(x): |
| 23 | + """Convert list or string to str.""" |
| 24 | + if isinstance(x, str): |
| 25 | + return x |
| 26 | + else: |
| 27 | + return ','.join(x) |
| 28 | + |
| 29 | +__PARSE_PYLAMA_MESSSAGE_RE = re.compile( |
| 30 | + '^(?P<filename>.*?):(?P<lnum>\d*):(((?P<col>\d*):)| (\[(?P<type>.*?)\])) (?P<text>.*)$') |
| 31 | +__PARSE_PYLAMA_TEXT_RE = re.compile( |
| 32 | + '^(?P<text>.*) \[(?P<linter>.*?)\]$') |
| 33 | + |
| 34 | + |
| 35 | +def _external_python_code_check(python_binary, linters, ignore, select, linter_options): |
| 36 | + path = os.path.relpath(env.curbuf.name, env.curdir) |
| 37 | + env.debug("Start code check: ", path) |
| 38 | + |
| 39 | + check_path_command_args = [ |
| 40 | + python_binary, '-m', 'pylama', path, |
| 41 | + '--linters', __maybe_list_to_str(linters), |
| 42 | + '--force', |
| 43 | + '--ignore', __maybe_list_to_str(ignore), |
| 44 | + '--select', __maybe_list_to_str(select), |
| 45 | + ] |
| 46 | + env.debug("Linter options: ", linter_options) |
| 47 | + |
| 48 | + options_file = None |
| 49 | + try: |
| 50 | + if linter_options: |
| 51 | + with tempfile.NamedTemporaryFile('w', delete=False) as f: |
| 52 | + options_file = f.name |
| 53 | + for linter, opts in linter_options.items(): |
| 54 | + f.write('[pylama:{linter!s}]\n'.format(linter=linter)) |
| 55 | + for param, value in opts.items(): |
| 56 | + f.write('{param!s}={value!s}\n'.format( |
| 57 | + param=param, value=value)) |
| 58 | + |
| 59 | + check_path_command_args += ['--options', options_file] |
| 60 | + |
| 61 | + env.debug("Check path args: ", check_path_command_args) |
| 62 | + check_path_process = subprocess.Popen( |
| 63 | + check_path_command_args, |
| 64 | + stdout=subprocess.PIPE, |
| 65 | + stderr=subprocess.PIPE) |
| 66 | + |
| 67 | + stdout, stderr = check_path_process.communicate() |
| 68 | + finally: |
| 69 | + if options_file is not None: |
| 70 | + os.remove(options_file) |
| 71 | + |
| 72 | + errors = [] |
| 73 | + for line in stdout.splitlines(): |
| 74 | + message_match = __PARSE_PYLAMA_MESSSAGE_RE.match(line) |
| 75 | + if message_match is not None: |
| 76 | + full_message_text = message_match.group('text') |
| 77 | + error_kwargs = { |
| 78 | + 'filename': message_match.group('filename'), |
| 79 | + } |
| 80 | + if message_match.group('lnum'): |
| 81 | + error_kwargs['lnum'] = int(message_match.group('lnum')) |
| 82 | + if message_match.group('col'): |
| 83 | + error_kwargs['col'] = int(message_match.group('col')) |
| 84 | + if message_match.group('type') is not None: |
| 85 | + error_kwargs['type'] = message_match.group('type') |
| 86 | + |
| 87 | + linter_match = __PARSE_PYLAMA_TEXT_RE.match(full_message_text) |
| 88 | + if linter_match is None: |
| 89 | + error_kwargs['text'] = full_message_text |
| 90 | + else: |
| 91 | + error_kwargs['text'] = linter_match.group('text') |
| 92 | + error_kwargs['linter'] = linter_match.group('linter') |
| 93 | + |
| 94 | + errors.append(LintError(**error_kwargs)) |
| 95 | + return errors |
| 96 | + |
| 97 | + |
| 98 | +def _internal_python_code_check(linters, ignore, select, linter_options): |
| 99 | + from pylama.core import run |
| 100 | + from pylama.main import parse_options |
| 101 | + |
| 102 | + if not env.curbuf.name: |
| 103 | + return env.stop() |
| 104 | + |
| 105 | + options = parse_options( |
| 106 | + linters=linters, force=1, |
| 107 | + ignore=ignore, |
| 108 | + select=select, |
| 109 | + ) |
| 110 | + |
| 111 | + for linter, opts in linter_options.items(): |
| 112 | + if opts: |
| 113 | + options.linters_params[linter] = options.linters_params.get(linter, {}) |
| 114 | + options.linters_params[linter].update(opts) |
| 115 | + |
| 116 | + env.debug(options) |
| 117 | + |
| 118 | + path = os.path.relpath(env.curbuf.name, env.curdir) |
| 119 | + env.debug("Start code check: ", path) |
| 120 | + |
| 121 | + if getattr(options, 'skip', None) and any(p.match(path) for p in options.skip): # noqa |
| 122 | + env.message('Skip code checking.') |
| 123 | + env.debug("Skipped") |
| 124 | + return env.stop() |
| 125 | + |
| 126 | + if env.options.get('debug'): |
| 127 | + from pylama.core import LOGGER, logging |
| 128 | + LOGGER.setLevel(logging.DEBUG) |
| 129 | + |
| 130 | + errors = run(path, code='\n'.join(env.curbuf) + '\n', options=options) |
| 131 | + return errors |
| 132 | + |
| 133 | + |
18 | 134 | def code_check(): |
19 | 135 | """Run pylama and check current file. |
20 | 136 |
|
21 | 137 | :return bool: |
22 | 138 |
|
23 | 139 | """ |
24 | 140 | with silence_stderr(): |
25 | | - |
26 | | - from pylama.core import run |
27 | | - from pylama.main import parse_options |
28 | | - |
29 | 141 | if not env.curbuf.name: |
30 | 142 | return env.stop() |
31 | 143 |
|
32 | 144 | linters = env.var('g:pymode_lint_checkers') |
33 | 145 | env.debug(linters) |
| 146 | + ignore = env.var('g:pymode_lint_ignore') |
| 147 | + select = env.var('g:pymode_lint_select') |
34 | 148 |
|
35 | | - options = parse_options( |
36 | | - linters=linters, force=1, |
37 | | - ignore=env.var('g:pymode_lint_ignore'), |
38 | | - select=env.var('g:pymode_lint_select'), |
39 | | - ) |
40 | | - |
| 149 | + linter_options = {} |
41 | 150 | for linter in linters: |
42 | 151 | opts = env.var('g:pymode_lint_options_%s' % linter, silence=True) |
43 | 152 | if opts: |
44 | | - options.linters_params[linter] = options.linters_params.get(linter, {}) |
45 | | - options.linters_params[linter].update(opts) |
46 | | - |
47 | | - env.debug(options) |
48 | | - |
49 | | - path = os.path.relpath(env.curbuf.name, env.curdir) |
50 | | - env.debug("Start code check: ", path) |
51 | | - |
52 | | - if getattr(options, 'skip', None) and any(p.match(path) for p in options.skip): # noqa |
53 | | - env.message('Skip code checking.') |
54 | | - env.debug("Skipped") |
55 | | - return env.stop() |
56 | | - |
57 | | - if env.options.get('debug'): |
58 | | - from pylama.core import LOGGER, logging |
59 | | - LOGGER.setLevel(logging.DEBUG) |
60 | | - |
61 | | - errors = run(path, code='\n'.join(env.curbuf) + '\n', options=options) |
| 153 | + linter_options[linter] = opts |
| 154 | + |
| 155 | + python_binary = env.var('g:pymode_lint_external_python', silence=True) |
| 156 | + if python_binary: |
| 157 | + errors = _external_python_code_check( |
| 158 | + python_binary=python_binary, |
| 159 | + linters=linters, |
| 160 | + ignore=ignore, |
| 161 | + select=select, |
| 162 | + linter_options=linter_options) |
| 163 | + else: |
| 164 | + errors = _internal_python_code_check( |
| 165 | + linters=linters, |
| 166 | + ignore=ignore, |
| 167 | + select=select, |
| 168 | + linter_options=linter_options) |
62 | 169 |
|
63 | 170 | env.debug("Find errors: ", len(errors)) |
64 | 171 | sort_rules = env.var('g:pymode_lint_sort') |
|
0 commit comments