Skip to content

Commit

Permalink
Extract py_run of pylint.epylint as spawn_pylint
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
flying-foozy committed Dec 12, 2019
1 parent 6d05cc3 commit 2cc8080
Showing 1 changed file with 50 additions and 11 deletions.
61 changes: 50 additions & 11 deletions pyls/plugins/pylint_lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,67 @@
"""Linter plugin for pylint."""
import collections
import logging
import os
import shlex
import subprocess
import sys

from pylint.epylint import py_run
from pyls import hookimpl, lsp

try:
import ujson as json
except Exception: # pylint: disable=broad-except
import json

if sys.version_info.major == 2:
from StringIO import StringIO
else:
from io import StringIO

log = logging.getLogger(__name__)


def spawn_pylint(document, flags):
"""Spawn pylint process as same as pylint.epylint.py_run
"""
path = document.path
if sys.platform.startswith('win'):
# Now, shlex.split is not applied on path, and Windows path
# (including '\\') is safe enough. But turn backslashes into
# forward slashes in order to keep previous behavior.
path = path.replace('\\', '/')

env = dict(os.environ)
env["PYTHONPATH"] = os.pathsep.join(sys.path)

# Detect if we use Python as executable or not, else default to `python`
executable = sys.executable if "python" in sys.executable else "python"

# Create command line to call pylint
epylint_part = [executable, "-c", "from pylint import epylint;epylint.Run()"]
options = shlex.split(flags, posix=not sys.platform.startswith("win"))

pylint_call = [path, '-f', 'json'] + options
log.debug("Calling pylint with %r", pylint_call)

cli = epylint_part + pylint_call

stdout = stderr = subprocess.PIPE

# Call pylint in a subprocess
process = subprocess.Popen(
cli,
shell=False,
stdout=stdout,
stderr=stderr,
env=env,
universal_newlines=True,
)
proc_stdout, proc_stderr = process.communicate()
# Return standard output and error
return StringIO(proc_stdout), StringIO(proc_stderr)


class PylintLinter(object):
last_diags = collections.defaultdict(list)

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

# py_run will call shlex.split on its arguments, and shlex.split does
# not handle Windows paths (it will try to perform escaping). Turn
# backslashes into forward slashes first to avoid this issue.
path = document.path
if sys.platform.startswith('win'):
path = path.replace('\\', '/')

pylint_call = '{} -f json {}'.format(path, flags)
log.debug("Calling pylint with '%s'", pylint_call)
json_out, err = py_run(pylint_call, return_std=True)
json_out, err = spawn_pylint(document, flags)

# Get strings
json_out = json_out.getvalue()
Expand Down

0 comments on commit 2cc8080

Please sign in to comment.