|
2 | 2 | """Linter plugin for pylint."""
|
3 | 3 | import collections
|
4 | 4 | import logging
|
| 5 | +import os |
| 6 | +import shlex |
| 7 | +import subprocess |
5 | 8 | import sys
|
6 | 9 |
|
7 |
| -from pylint.epylint import py_run |
8 | 10 | from pyls import hookimpl, lsp
|
9 | 11 |
|
10 | 12 | try:
|
11 | 13 | import ujson as json
|
12 | 14 | except Exception: # pylint: disable=broad-except
|
13 | 15 | import json
|
14 | 16 |
|
| 17 | +if sys.version_info.major == 2: |
| 18 | + from StringIO import StringIO |
| 19 | +else: |
| 20 | + from io import StringIO |
| 21 | + |
15 | 22 | log = logging.getLogger(__name__)
|
16 | 23 |
|
17 | 24 |
|
| 25 | +def spawn_pylint(document, flags): |
| 26 | + """Spawn pylint process as same as pylint.epylint.py_run |
| 27 | + """ |
| 28 | + path = document.path |
| 29 | + if sys.platform.startswith('win'): |
| 30 | + # Now, shlex.split is not applied on path, and Windows path |
| 31 | + # (including '\\') is safe enough. But turn backslashes into |
| 32 | + # forward slashes in order to keep previous behavior. |
| 33 | + path = path.replace('\\', '/') |
| 34 | + |
| 35 | + env = dict(os.environ) |
| 36 | + env["PYTHONPATH"] = os.pathsep.join(sys.path) |
| 37 | + |
| 38 | + # Detect if we use Python as executable or not, else default to `python` |
| 39 | + executable = sys.executable if "python" in sys.executable else "python" |
| 40 | + |
| 41 | + # Create command line to call pylint |
| 42 | + epylint_part = [executable, "-c", "from pylint import epylint;epylint.Run()"] |
| 43 | + options = shlex.split(flags, posix=not sys.platform.startswith("win")) |
| 44 | + |
| 45 | + pylint_call = [path, '-f', 'json'] + options |
| 46 | + log.debug("Calling pylint with %r", pylint_call) |
| 47 | + |
| 48 | + cli = epylint_part + pylint_call |
| 49 | + |
| 50 | + stdout = stderr = subprocess.PIPE |
| 51 | + |
| 52 | + # Call pylint in a subprocess |
| 53 | + process = subprocess.Popen( |
| 54 | + cli, |
| 55 | + shell=False, |
| 56 | + stdout=stdout, |
| 57 | + stderr=stderr, |
| 58 | + env=env, |
| 59 | + universal_newlines=True, |
| 60 | + ) |
| 61 | + proc_stdout, proc_stderr = process.communicate() |
| 62 | + # Return standard output and error |
| 63 | + return StringIO(proc_stdout), StringIO(proc_stderr) |
| 64 | + |
| 65 | + |
18 | 66 | class PylintLinter(object):
|
19 | 67 | last_diags = collections.defaultdict(list)
|
20 | 68 |
|
@@ -56,16 +104,7 @@ def lint(cls, document, is_saved, flags=''):
|
56 | 104 | # save.
|
57 | 105 | return cls.last_diags[document.path]
|
58 | 106 |
|
59 |
| - # py_run will call shlex.split on its arguments, and shlex.split does |
60 |
| - # not handle Windows paths (it will try to perform escaping). Turn |
61 |
| - # backslashes into forward slashes first to avoid this issue. |
62 |
| - path = document.path |
63 |
| - if sys.platform.startswith('win'): |
64 |
| - path = path.replace('\\', '/') |
65 |
| - |
66 |
| - pylint_call = '{} -f json {}'.format(path, flags) |
67 |
| - log.debug("Calling pylint with '%s'", pylint_call) |
68 |
| - json_out, err = py_run(pylint_call, return_std=True) |
| 107 | + json_out, err = spawn_pylint(document, flags) |
69 | 108 |
|
70 | 109 | # Get strings
|
71 | 110 | json_out = json_out.getvalue()
|
|
0 commit comments