Skip to content

Commit 21dc875

Browse files
committed
Refactor file checking for a simpler parallel API
This change prepares the code for enabling Prospector to take advantage of running PyLint parallel. Iterating files is moved into generator (_iterate_file_descrs) so that parallel checking can use the same implementation (_check_file) just by providing different kind of generator that reads the files from parent process. The refactoring removes code duplication that existed in PyLinter._do_check method; checking module content from stdin had identical implementation to checking content from a source file. Made PyLinter.expand_files a private method. The previous implementation of parallel linting created new PyLinter objects in the worker (child) process causing failure when running under Prospector because Prospector uses a custom PyLinter class (a class inherited from PyLinter) and PyLint naturally just creates PyLinter object. This caused linting to fail because there is options for Prospector's IndentChecker which was not created in the worker process. The new implementation passes the original PyLinter object into workers when the workers are created. See https://docs.python.org/3/library/multiprocessing.html#contexts-and-start-methods Note that as Windows uses spawn method by default, PyLinter object (and its) members need to be pickleable from now on with the exception being PyLinter.reporter which is not passed to child processes. The performance has remained about the same based on quick tests done with Django project containing about 30 000 lines of code; with the old implementation linting took 26-28 seconds with 8 jobs on quad core i7 and 24-27 seconds with the new implementation.
1 parent 662c9e4 commit 21dc875

File tree

8 files changed

+290
-256
lines changed

8 files changed

+290
-256
lines changed

CONTRIBUTORS.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,3 +339,4 @@ contributors:
339339

340340
* laike9m: contributor
341341

342+
* Janne Rönkkö: contributor

ChangeLog

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,21 @@ Release date: TBA
3131

3232
Closes #2729
3333

34+
* Allow parallel linting when run under Prospector
35+
3436

3537
What's New in Pylint 2.4.3?
3638
===========================
3739

38-
Release date: TBA
40+
Pass the actual PyLinter object to sub processes to allow using custom
41+
PyLinter classes.
42+
43+
PyLinter object (and all its members except reporter) needs to support
44+
pickling so the PyLinter object can be passed to worker processes.
45+
46+
* Refactor file checking
47+
48+
Remove code duplication from file checking.
3949

4050
* Fix an issue with ``unnecessary-comprehension`` in comprehensions with additional repacking of elements.
4151

pylint/checkers/classes.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -634,11 +634,15 @@ def _has_same_layout_slots(slots, assigned_value):
634634
}
635635

636636

637+
def _scope_default():
638+
return collections.defaultdict(list)
639+
640+
637641
class ScopeAccessMap:
638642
"""Store the accessed variables per scope."""
639643

640644
def __init__(self):
641-
self._scopes = collections.defaultdict(lambda: collections.defaultdict(list))
645+
self._scopes = collections.defaultdict(_scope_default)
642646

643647
def set_accessed(self, node):
644648
"""Set the given node as accessed."""

pylint/config.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import configparser
3838
import contextlib
3939
import copy
40+
import functools
4041
import io
4142
import optparse
4243
import os
@@ -719,10 +720,8 @@ def read_config_file(self, config_file=None, verbose=None):
719720
opt = "-".join(["long"] * helplevel) + "-help"
720721
if opt in self._all_options:
721722
break # already processed
722-
# pylint: disable=unused-argument
723-
def helpfunc(option, opt, val, p, level=helplevel):
724-
print(self.help(level))
725-
sys.exit(0)
723+
724+
helpfunc = functools.partial(self.helpfunc, level=helplevel)
726725

727726
helpmsg = "%s verbose help." % " ".join(["more"] * helplevel)
728727
optdict = {"action": "callback", "callback": helpfunc, "help": helpmsg}
@@ -830,6 +829,10 @@ def help(self, level=0):
830829
with _patch_optparse():
831830
return self.cmdline_parser.format_help()
832831

832+
def helpfunc(self, option, opt, val, p, level): # pylint: disable=unused-argument
833+
print(self.help(level))
834+
sys.exit(0)
835+
833836

834837
class OptionsProviderMixIn:
835838
"""Mixin to provide options to an OptionsManager"""

0 commit comments

Comments
 (0)