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

Commit 689ab78

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

File tree

3 files changed

+61
-25
lines changed

3 files changed

+61
-25
lines changed

gitlint/linters.py

Lines changed: 3 additions & 2 deletions
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

Lines changed: 28 additions & 7 deletions
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

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -91,60 +91,74 @@ 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' %
98+
dummy_hash,
99+
utils._get_cache_filename('linter1', dummy_hash,
100+
'bar/file.txt'))
98101

99102
self.assertEqual(
100-
'/home/user/.git-lint/cache/linter2/abspath/file.txt',
101-
utils._get_cache_filename('linter2', 'file.txt'))
103+
'/home/user/.git-lint/cache/linter2.%s/abspath/file.txt' %
104+
dummy_hash,
105+
utils._get_cache_filename('linter2', dummy_hash, 'file.txt'))
102106

103107
self.assertEqual(
104-
'/home/user/.git-lint/cache/linter3/bar/file.txt',
105-
utils._get_cache_filename('linter3', '/bar/file.txt'))
108+
'/home/user/.git-lint/cache/linter3.%s/bar/file.txt' %
109+
dummy_hash,
110+
utils._get_cache_filename('linter3', dummy_hash,
111+
'/bar/file.txt'))
106112

107113
@unittest.skipUnless(sys.version_info >= (3, 5),
108114
'pyfakefs does not support pathlib2. See'
109115
'https://github.com/jmcgeheeiv/pyfakefs/issues/408')
110116
def test_save_output_in_cache(self):
117+
dummy_hash = utils.calculate_hash("some_tool", [])
111118
output = 'Some content'
112119
with mock.patch(
113120
'gitlint.utils._get_cache_filename',
114-
return_value='/cache/filename.txt'):
115-
utils.save_output_in_cache('linter', 'filename', output)
121+
return_value='/cache/linter.%s/filename.txt' % dummy_hash):
122+
utils.save_output_in_cache('linter', dummy_hash, 'filename',
123+
output)
116124

117-
with open(utils._get_cache_filename('linter', 'filename')) as f:
125+
with open(
126+
utils._get_cache_filename('linter', dummy_hash,
127+
'filename')) as f:
118128
self.assertEqual(output, f.read())
119129

120130
def test_get_output_from_cache_no_cache(self):
121-
cache_filename = '/cache/filename.txt'
131+
dummy_hash = utils.calculate_hash("some_tool", [])
132+
cache_filename = '/cache/linter.%s/filename.txt' % dummy_hash
122133
with mock.patch(
123134
'gitlint.utils._get_cache_filename',
124135
return_value=cache_filename):
125136
self.assertIsNone(
126-
utils.get_output_from_cache('linter', 'filename'))
137+
utils.get_output_from_cache('linter', dummy_hash, 'filename'))
127138

128139
def test_get_output_from_cache_cache_is_expired(self):
129-
cache_filename = '/cache/filename.txt'
140+
dummy_hash = utils.calculate_hash("some_tool", [])
141+
cache_filename = '/cache/linter.%s/filename.txt' % dummy_hash
130142
self.fs.create_file(cache_filename)
131143
self.fs.create_file('filename')
132144
with mock.patch(
133145
'gitlint.utils._get_cache_filename',
134146
return_value=cache_filename):
135147
self.assertIsNone(
136-
utils.get_output_from_cache('linter', 'filename'))
148+
utils.get_output_from_cache('linter', dummy_hash, 'filename'))
137149

138150
def test_get_output_from_cache_cache_is_valid(self):
139-
cache_filename = '/cache/filename.txt'
151+
dummy_hash = utils.calculate_hash("some_tool", [])
152+
cache_filename = '/cache/linter.%s/filename.txt' % dummy_hash
140153
content = 'some_content'
141154
self.fs.create_file('filename')
142155
self.fs.create_file(cache_filename, contents=content)
143156
with mock.patch(
144157
'gitlint.utils._get_cache_filename',
145158
return_value=cache_filename):
146-
self.assertEqual(content,
147-
utils.get_output_from_cache('linter', 'filename'))
159+
self.assertEqual(
160+
content,
161+
utils.get_output_from_cache('linter', dummy_hash, 'filename'))
148162

149163
def test_which_absolute_path(self):
150164
filename = '/foo/bar.sh'

0 commit comments

Comments
 (0)