Skip to content

Commit 2cc8080

Browse files
committed
Extract py_run of pylint.epylint as spawn_pylint
This is the preparation for spawning pylint process with pyls specific customization in subsequent change. Almost all of spawn_pylint is cited from pylint/epylint.py in 6d818e3b84b853fe06f4199eb8408fe6dfe033be (the latest version on master branch at 2019-12-04) of pylint. BTW, this commit imports StringIO manually for compatibility between Python 2.x and 3.x instead of using "six" like recent pylint 1.9.x (supporting python 2.x), because requiring "six" only for StringIO seems over-kill.
1 parent 6d05cc3 commit 2cc8080

File tree

1 file changed

+50
-11
lines changed

1 file changed

+50
-11
lines changed

pyls/plugins/pylint_lint.py

+50-11
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,67 @@
22
"""Linter plugin for pylint."""
33
import collections
44
import logging
5+
import os
6+
import shlex
7+
import subprocess
58
import sys
69

7-
from pylint.epylint import py_run
810
from pyls import hookimpl, lsp
911

1012
try:
1113
import ujson as json
1214
except Exception: # pylint: disable=broad-except
1315
import json
1416

17+
if sys.version_info.major == 2:
18+
from StringIO import StringIO
19+
else:
20+
from io import StringIO
21+
1522
log = logging.getLogger(__name__)
1623

1724

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+
1866
class PylintLinter(object):
1967
last_diags = collections.defaultdict(list)
2068

@@ -56,16 +104,7 @@ def lint(cls, document, is_saved, flags=''):
56104
# save.
57105
return cls.last_diags[document.path]
58106

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)
69108

70109
# Get strings
71110
json_out = json_out.getvalue()

0 commit comments

Comments
 (0)