Skip to content
This repository was archived by the owner on Sep 9, 2020. It is now read-only.

Commit 3b6185e

Browse files
committed
Invalidate cache if linter path or arguments change (#19)
1 parent 79c9f5c commit 3b6185e

File tree

3 files changed

+51
-24
lines changed

3 files changed

+51
-24
lines changed

gitlint/linters.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ def lint_command(name, program, arguments, filter_regex, filename, lines):
7373
7474
Returns: dict: a dict with the extracted info from the message.
7575
"""
76-
output = utils.get_output_from_cache(name, filename)
76+
linter_hash = utils.calculate_hash(program, arguments)
77+
output = utils.get_output_from_cache(name, linter_hash, filename)
7778

7879
if output is None:
7980
call_arguments = [program] + arguments + [filename]
@@ -91,7 +92,7 @@ def lint_command(name, program, arguments, filter_regex, filename, lines):
9192
}
9293
}
9394
output = output.decode('utf-8')
94-
utils.save_output_in_cache(name, filename, output)
95+
utils.save_output_in_cache(name, linter_hash, filename, output)
9596

9697
output_lines = output.split(os.linesep)
9798

gitlint/utils.py

+28-7
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# limitations under the License.
1414
"""Common function used across modules."""
1515

16+
import hashlib
1617
import io
1718
import os
1819
import re
@@ -72,29 +73,48 @@ def _open_for_write(filename):
7273
return io.open(filename, 'w')
7374

7475

75-
def _get_cache_filename(name, filename):
76-
"""Returns the cache location for filename and linter name."""
76+
def _get_cache_filename(name, linter_hash, filename):
77+
"""Returns the cache location for filename and linter name and hash."""
7778
filename = os.path.abspath(filename)[1:]
79+
linter_dir = "%s.%s" % (name, linter_hash)
7880
home_folder = os.path.expanduser('~')
7981
base_cache_dir = os.path.join(home_folder, '.git-lint', 'cache')
8082

81-
return os.path.join(base_cache_dir, name, filename)
83+
return os.path.join(base_cache_dir, linter_dir, filename)
8284

8385

84-
def get_output_from_cache(name, filename):
86+
def calculate_hash(program, arguments):
87+
"""Calculate sha256 hash as hex string over program and arguments.
88+
89+
Args:
90+
program: string: lint program.
91+
arguments: list[string]: extra arguments for the program.
92+
93+
Returns: string: a string with the calculated sha256 hex value.
94+
"""
95+
algorithm = hashlib.sha256()
96+
algorithm.update(program)
97+
for argument in arguments:
98+
algorithm.update("|")
99+
algorithm.update(argument)
100+
return algorithm.hexdigest()
101+
102+
103+
def get_output_from_cache(name, linter_hash, filename):
85104
"""Returns the output from the cache if still valid.
86105
87106
It checks that the cache file is defined and that its modification time is
88107
after the modification time of the original file.
89108
90109
Args:
91110
name: string: name of the linter.
111+
linter_hash: string: hash representing linter binary and arguments.
92112
filename: string: path of the filename for which we are retrieving the
93113
output.
94114
95115
Returns: a string with the output, if it is still valid, or None otherwise.
96116
"""
97-
cache_filename = _get_cache_filename(name, filename)
117+
cache_filename = _get_cache_filename(name, linter_hash, filename)
98118
if (os.path.exists(cache_filename)
99119
and os.path.getmtime(filename) < os.path.getmtime(cache_filename)):
100120
with io.open(cache_filename) as f:
@@ -103,14 +123,15 @@ def get_output_from_cache(name, filename):
103123
return None
104124

105125

106-
def save_output_in_cache(name, filename, output):
126+
def save_output_in_cache(name, linter_hash, filename, output):
107127
"""Saves output in the cache location.
108128
109129
Args:
110130
name: string: name of the linter.
131+
linter_hash: string: hash representing linter binary and arguments.
111132
filename: string: path of the filename for which we are saving the output.
112133
output: string: full output (not yet filetered) of the lint command.
113134
"""
114-
cache_filename = _get_cache_filename(name, filename)
135+
cache_filename = _get_cache_filename(name, linter_hash, filename)
115136
with _open_for_write(cache_filename) as f:
116137
f.write(output)

test/unittest/test_utils.py

+20-15
Original file line numberDiff line numberDiff line change
@@ -91,60 +91,65 @@ def test_open_for_write(self):
9191
def test_get_cache_filename(self):
9292
self.fs.create_dir('/abspath')
9393
os.chdir('/abspath')
94+
dummy_hash = utils.calculate_hash("some_tool", [])
9495
with mock.patch('os.path.expanduser', return_value='/home/user'):
9596
self.assertEqual(
96-
'/home/user/.git-lint/cache/linter1/abspath/bar/file.txt',
97-
utils._get_cache_filename('linter1', 'bar/file.txt'))
97+
'/home/user/.git-lint/cache/linter1.%s/abspath/bar/file.txt' % dummy_hash,
98+
utils._get_cache_filename('linter1', dummy_hash, 'bar/file.txt'))
9899

99100
self.assertEqual(
100-
'/home/user/.git-lint/cache/linter2/abspath/file.txt',
101-
utils._get_cache_filename('linter2', 'file.txt'))
101+
'/home/user/.git-lint/cache/linter2.%s/abspath/file.txt' % dummy_hash,
102+
utils._get_cache_filename('linter2', dummy_hash, 'file.txt'))
102103

103104
self.assertEqual(
104-
'/home/user/.git-lint/cache/linter3/bar/file.txt',
105-
utils._get_cache_filename('linter3', '/bar/file.txt'))
105+
'/home/user/.git-lint/cache/linter3.%s/bar/file.txt' % dummy_hash,
106+
utils._get_cache_filename('linter3', dummy_hash, '/bar/file.txt'))
106107

107108
@unittest.skipUnless(sys.version_info >= (3, 5),
108109
'pyfakefs does not support pathlib2. See'
109110
'https://github.com/jmcgeheeiv/pyfakefs/issues/408')
110111
def test_save_output_in_cache(self):
112+
dummy_hash = utils.calculate_hash("some_tool", [])
111113
output = 'Some content'
112114
with mock.patch(
113115
'gitlint.utils._get_cache_filename',
114-
return_value='/cache/filename.txt'):
115-
utils.save_output_in_cache('linter', 'filename', output)
116+
return_value='/cache/linter.%s/filename.txt' % dummy_hash):
117+
utils.save_output_in_cache('linter', dummy_hash, 'filename', output)
116118

117-
with open(utils._get_cache_filename('linter', 'filename')) as f:
119+
with open(utils._get_cache_filename('linter', dummy_hash, 'filename')) as f:
118120
self.assertEqual(output, f.read())
119121

120122
def test_get_output_from_cache_no_cache(self):
121-
cache_filename = '/cache/filename.txt'
123+
dummy_hash = utils.calculate_hash("some_tool", [])
124+
cache_filename = '/cache/linter.%s/filename.txt' % dummy_hash
122125
with mock.patch(
123126
'gitlint.utils._get_cache_filename',
124127
return_value=cache_filename):
125128
self.assertIsNone(
126-
utils.get_output_from_cache('linter', 'filename'))
129+
utils.get_output_from_cache('linter', dummy_hash, 'filename'))
127130

128131
def test_get_output_from_cache_cache_is_expired(self):
129-
cache_filename = '/cache/filename.txt'
132+
dummy_hash = utils.calculate_hash("some_tool", [])
133+
cache_filename = '/cache/linter.%s/filename.txt' % dummy_hash
130134
self.fs.create_file(cache_filename)
131135
self.fs.create_file('filename')
132136
with mock.patch(
133137
'gitlint.utils._get_cache_filename',
134138
return_value=cache_filename):
135139
self.assertIsNone(
136-
utils.get_output_from_cache('linter', 'filename'))
140+
utils.get_output_from_cache('linter', dummy_hash, 'filename'))
137141

138142
def test_get_output_from_cache_cache_is_valid(self):
139-
cache_filename = '/cache/filename.txt'
143+
dummy_hash = utils.calculate_hash("some_tool", [])
144+
cache_filename = '/cache/linter.%s/filename.txt' % dummy_hash
140145
content = 'some_content'
141146
self.fs.create_file('filename')
142147
self.fs.create_file(cache_filename, contents=content)
143148
with mock.patch(
144149
'gitlint.utils._get_cache_filename',
145150
return_value=cache_filename):
146151
self.assertEqual(content,
147-
utils.get_output_from_cache('linter', 'filename'))
152+
utils.get_output_from_cache('linter', dummy_hash, 'filename'))
148153

149154
def test_which_absolute_path(self):
150155
filename = '/foo/bar.sh'

0 commit comments

Comments
 (0)