Skip to content

Commit a319ad4

Browse files
authored
Merge pull request #83 from yanqd0/patch
Skip unchanged files which is checked
2 parents 875d155 + e48f4d9 commit a319ad4

File tree

3 files changed

+80
-9
lines changed

3 files changed

+80
-9
lines changed

pylintrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
[TYPECHECK]
2-
ignored-classes=pytest
2+
ignored-classes=pytest
3+
disable=bad-option-value

pytest_pylint.py

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
from pylint.reporters import BaseReporter
1717
import pytest
1818

19+
HISTKEY = 'pylint/mtimes'
20+
1921

2022
class PyLintException(Exception):
2123
"""Exception to raise if a file has a specified pylint error"""
@@ -152,6 +154,40 @@ def include_file(path, ignore_list, ignore_patterns=None):
152154
return not set(parts) & set(ignore_list)
153155

154156

157+
def pytest_configure(config):
158+
"""
159+
Add a plugin to cache file mtimes.
160+
161+
:param _pytest.config.Config config: pytest config object
162+
"""
163+
if config.option.pylint:
164+
config.pylint = PylintPlugin(config)
165+
config.pluginmanager.register(config.pylint)
166+
config.addinivalue_line('markers', "pylint: Tests which run pylint.")
167+
168+
169+
# There will be an old-style-class error in Python 2.7,
170+
# or a useless-object-inheritance warning in Python 3.
171+
# If disable any, pylint will have a bad-option-value error in 2.7 or 3.
172+
# Finally I have to disable useless-object-inheritance locally
173+
# and bad-option-value globally.
174+
# pylint: disable=too-few-public-methods, useless-object-inheritance
175+
class PylintPlugin(object):
176+
"""
177+
A Plugin object for pylint, which loads and records file mtimes.
178+
"""
179+
def __init__(self, config):
180+
self.mtimes = config.cache.get(HISTKEY, {})
181+
182+
def pytest_sessionfinish(self, session):
183+
"""
184+
Save file mtimes to pytest cache.
185+
186+
:param _pytest.main.Session session: the pytest session object
187+
"""
188+
session.config.cache.set(HISTKEY, self.mtimes)
189+
190+
155191
def pytest_collect_file(path, parent):
156192
"""Collect files on which pylint should run"""
157193
config = parent.session.config
@@ -162,17 +198,18 @@ def pytest_collect_file(path, parent):
162198
rel_path = get_rel_path(path.strpath, parent.session.fspath.strpath)
163199
session = parent.session
164200
if session.pylint_config is None:
165-
session.pylint_files.add(rel_path)
166201
# No pylintrc, therefore no ignores, so return the item.
167-
return PyLintItem(path, parent)
168-
169-
if include_file(rel_path, session.pylint_ignore,
170-
session.pylint_ignore_patterns):
171-
session.pylint_files.add(rel_path)
172-
return PyLintItem(
202+
item = PyLintItem(path, parent)
203+
elif include_file(rel_path, session.pylint_ignore,
204+
session.pylint_ignore_patterns):
205+
item = PyLintItem(
173206
path, parent, session.pylint_msg_template, session.pylintrc_file
174207
)
175-
return None
208+
else:
209+
return None
210+
if not item.should_skip:
211+
session.pylint_files.add(rel_path)
212+
return item
176213

177214

178215
def pytest_collection_finish(session):
@@ -231,6 +268,14 @@ def __init__(self, fspath, parent, msg_format=None, pylintrc_file=None):
231268
self._msg_format = msg_format
232269

233270
self.pylintrc_file = pylintrc_file
271+
self.__mtime = self.fspath.mtime()
272+
prev_mtime = self.config.pylint.mtimes.get(self.nodeid, 0)
273+
self.should_skip = (prev_mtime == self.__mtime)
274+
275+
def setup(self):
276+
"""Mark unchanged files as SKIPPED."""
277+
if self.should_skip:
278+
pytest.skip("file(s) previously passed pylint checks")
234279

235280
def runtest(self):
236281
"""Check the pylint messages to see if any errors were reported."""
@@ -243,6 +288,9 @@ def runtest(self):
243288
if reported_errors:
244289
raise PyLintException('\n'.join(reported_errors))
245290

291+
# Update the cache if the item passed pylint.
292+
self.config.pylint.mtimes[self.nodeid] = self.__mtime
293+
246294
def repr_failure(self, excinfo):
247295
"""Handle any test failures by checkint that they were ours."""
248296
if excinfo.errisinstance(PyLintException):

test_pytest_pylint.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,25 @@ def test_pylint_ignore_patterns():
216216
assert include_file("part", [], ignore_patterns) is False
217217
assert include_file("1part2", [], ignore_patterns) is True
218218
assert include_file("base.py", [], ignore_patterns) is False
219+
220+
221+
def test_skip_checked_files(testdir):
222+
"""
223+
Test a file twice which can pass pylint.
224+
The 2nd time should be skipped.
225+
"""
226+
testdir.makepyfile(
227+
'#!/usr/bin/env python',
228+
'"""A hello world script."""',
229+
'',
230+
'from __future__ import print_function',
231+
'',
232+
'print("Hello world!") # pylint: disable=missing-final-newline',
233+
)
234+
# The 1st time should be passed
235+
result = testdir.runpytest('--pylint')
236+
assert '1 passed' in result.stdout.str()
237+
238+
# The 2nd time should be skipped
239+
result = testdir.runpytest('--pylint')
240+
assert '1 skipped' in result.stdout.str()

0 commit comments

Comments
 (0)