prfilter 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. #!/usr/bin/env python3
  2. import collections
  3. import json
  4. import subprocess
  5. import sys
  6. DEFAULT_REF = 'origin/master'
  7. def get_diff_ranges(ref=DEFAULT_REF):
  8. '''Get chunks affected by a merge request
  9. Args:
  10. ref (str): a reference to diff the HEAD against (default: origin/master)
  11. Returns:
  12. dict: a dict with filenames in keys and list of `(start, end)` ranges in
  13. values (start is inclusive, end is not, wrt :py:func:`range`)
  14. '''
  15. files = collections.defaultdict(list)
  16. data = subprocess.check_output(['git', 'diff', '-U0', ref, 'HEAD']).decode()
  17. path = None
  18. for line in data.split('\n'):
  19. if line.startswith('+++ '):
  20. path = (None if line == '+++ /dev/null'
  21. else line.split('/', maxsplit=1)[-1])
  22. continue
  23. if line.startswith('@@ '):
  24. # @@ -8,0 +9 @@ [class name or previous line or whatever]
  25. if path is None: # /dev/null
  26. continue
  27. _, _, plus, *_ = line.split()
  28. start, length, *_ = *(int(i) for i in plus[1:].split(',')), 1
  29. if not length:
  30. # remove-only chunk
  31. continue
  32. files[path].append((start, start + length))
  33. return files
  34. class Diff:
  35. '''A quick and dirty diff evaluator
  36. >>> diff = Diff()
  37. >>> (message['file'], message['line']) in diff
  38. True # or False
  39. The default diff is to the ``origin/master`` ref.
  40. '''
  41. def __init__(self, ref=DEFAULT_REF):
  42. self._files = get_diff_ranges(ref)
  43. def __contains__(self, pathline):
  44. path, line = pathline
  45. try:
  46. return any(start <= line < end for start, end in self._files[path])
  47. except KeyError:
  48. return False
  49. def main():
  50. diff = Diff()
  51. with sys.stdin:
  52. pylint = json.load(sys.stdin)
  53. ret = 0
  54. for message in pylint:
  55. # shellcheck
  56. if 'path' not in message:
  57. message['path'] = message['file']
  58. if 'symbol' not in message:
  59. message['symbol'] = message['code']
  60. if (message['path'], message['line']) in diff:
  61. if not ret:
  62. print('MESSAGES AFFECTING THIS PR:')
  63. print('{path} +{line}:{column}: {symbol}: {message}'.format(
  64. **message))
  65. ret += 1
  66. return min(ret, 255)
  67. if __name__ == '__main__':
  68. sys.exit(main())