Skip to content

Commit

Permalink
Invalidate cache if linter path or arguments change (sk-#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
sieberst committed Aug 29, 2018
1 parent 6b6050a commit 842a3c6
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 15 deletions.
12 changes: 6 additions & 6 deletions gitlint/linters.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,14 @@ def missing_requirements_command(missing_programs, installation_string,


# TODO(skreft): add test case for result already in cache.
def lint_command(name, program, arguments, filter_regex, filename, lines):
def lint_command(program, arguments, filter_regex, filename, lines):
"""Executes a lint program and filter the output.
Executes the lint tool 'program' with arguments 'arguments' over the file
'filename' returning only those lines matching the regular expression
'filter_regex'.
Args:
name: string: the name of the linter.
program: string: lint program.
arguments: list[string]: extra arguments for the program.
filter_regex: string: regular expression to filter lines.
Expand All @@ -73,7 +72,8 @@ def lint_command(name, program, arguments, filter_regex, filename, lines):
Returns: dict: a dict with the extracted info from the message.
"""
output = utils.get_output_from_cache(name, filename)
linter_hash = utils.calculate_hash(program, arguments)
output = utils.get_output_from_cache(linter_hash, filename)

if output is None:
call_arguments = [program] + arguments + [filename]
Expand All @@ -91,7 +91,7 @@ def lint_command(name, program, arguments, filter_regex, filename, lines):
}
}
output = output.decode('utf-8')
utils.save_output_in_cache(name, filename, output)
utils.save_output_in_cache(linter_hash, filename, output)

output_lines = output.split(os.linesep)

Expand Down Expand Up @@ -137,7 +137,7 @@ def parse_yaml_config(yaml_config, repo_home):
'REPO_HOME': repo_home,
}

for name, data in yaml_config.items():
for _, data in yaml_config.items():
command = _replace_variables([data['command']], variables)[0]
requirements = _replace_variables(
data.get('requirements', []), variables)
Expand All @@ -149,7 +149,7 @@ def parse_yaml_config(yaml_config, repo_home):
linter_command = Partial(missing_requirements_command,
not_found_programs, data['installation'])
else:
linter_command = Partial(lint_command, name, command, arguments,
linter_command = Partial(lint_command, command, arguments,
data['filter'])
for extension in data['extensions']:
config[extension].append(linter_command)
Expand Down
35 changes: 26 additions & 9 deletions gitlint/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# limitations under the License.
"""Common function used across modules."""

import hashlib
import io
import os
import re
Expand Down Expand Up @@ -72,29 +73,45 @@ def _open_for_write(filename):
return io.open(filename, 'w')


def _get_cache_filename(name, filename):
"""Returns the cache location for filename and linter name."""
def _get_cache_filename(linter_hash, filename):
"""Returns the cache location for filename and linter hash."""
filename = os.path.abspath(filename)[1:]
home_folder = os.path.expanduser('~')
base_cache_dir = os.path.join(home_folder, '.git-lint', 'cache')

return os.path.join(base_cache_dir, name, filename)
return os.path.join(base_cache_dir, linter_hash, filename)


def get_output_from_cache(name, filename):
def calculate_hash(program, arguments):
"""Calculate sha256 hash as hex string over program and arguments.
Args:
program: string: lint program.
arguments: list[string]: extra arguments for the program.
Returns: string: a string with the calculated sha256 hex value.
"""
h = hashlib.sha256()
h.update(program)
for argument in arguments:
h.update(argument)
return h.hexdigest()


def get_output_from_cache(linter_hash, filename):
"""Returns the output from the cache if still valid.
It checks that the cache file is defined and that its modification time is
after the modification time of the original file.
Args:
name: string: name of the linter.
linter_hash: string: hash representing linter binary and arguments.
filename: string: path of the filename for which we are retrieving the
output.
Returns: a string with the output, if it is still valid, or None otherwise.
"""
cache_filename = _get_cache_filename(name, filename)
cache_filename = _get_cache_filename(linter_hash, filename)
if (os.path.exists(cache_filename)
and os.path.getmtime(filename) < os.path.getmtime(cache_filename)):
with io.open(cache_filename) as f:
Expand All @@ -103,14 +120,14 @@ def get_output_from_cache(name, filename):
return None


def save_output_in_cache(name, filename, output):
def save_output_in_cache(linter_hash, filename, output):
"""Saves output in the cache location.
Args:
name: string: name of the linter.
linter_hash: string: hash representing linter binary and arguments.
filename: string: path of the filename for which we are saving the output.
output: string: full output (not yet filetered) of the lint command.
"""
cache_filename = _get_cache_filename(name, filename)
cache_filename = _get_cache_filename(linter_hash, filename)
with _open_for_write(cache_filename) as f:
f.write(output)

0 comments on commit 842a3c6

Please sign in to comment.