summaryrefslogtreecommitdiff
path: root/bin/warnings_in_scope
blob: 2a854211a2bf6bc2f157d2e92c51d53af483f650 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#!/usr/bin/env python3
import os
import subprocess
from pathlib import Path
import optparse
import sys
import re

def run(command, cwd=None):
    try:
        return subprocess.Popen(
            command, shell=True, cwd=cwd,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE)
    except OSError as err:
        raise OSError("Error in command '{0}': {1}".format(command, err))

def parse_location(line):
    # take substring between @@
    # take second part of it
    location = line.split(b'@@')[1].strip().split(b' ')[1]
    tokens = location.split(b',')
    if len(tokens) == 1:
        return (int(tokens[0][1:]), 1)
    elif len(tokens) == 2:
        return (int(tokens[0][1:]), int(tokens[1]))

def changed_files(directory, scope):
    result = {}
    proc = run('git diff --no-prefix --unified={0}'.format(scope), cwd=str(directory))
    file_path = None
    for line in iter(proc.stdout.readline, b''):
        if line.startswith(b'diff --git '):
            # this would be problematic if directory has space in the name
            file_name = line.split(b' ')[3].strip()
            file_path = str(directory.joinpath(str(file_name, 'utf-8')))
            result[file_path] = set()
            continue
        if line.startswith(b'@@'):
            start_pos, number_of_lines = parse_location(line)
            for line_number in range(start_pos, start_pos + number_of_lines):
                result[file_path].add(line_number)
    return result

def print_changed(file_name, line_number):
    print('{0}:{1}'.format(str(file_name), str(line_number)))

def changes(dirs, scope):
    result = {}
    for directory in dirs:
        result.update(changed_files(directory, scope))
    return result

def repositories(root):
    for directory in Path(root).rglob('.git'):
        if not directory.is_dir():
            continue
        yield directory.parent

def setup_argparse():
    parser = optparse.OptionParser(description="Filter output to remove unrelated warning")
    parser.add_option(
        "-r",
        "--regexp",
        dest="regexp",
        default='(?P<file_name>[^:]+):(?P<line>\d+).*',
        help="Regexp used to extract file_name and line number",
    )
    parser.add_option(
        "-s",
        "--scope",
        dest="scope",
        default=0,
        help="Number of lines surrounding the change we consider relevant",
    )
    parser.add_option(
        "-p",
        "--print-only",
        action="store_true",
        dest="print_only",
        default=False,
        help="Print changed lines only",
    )
    return parser.parse_args()

def filter_stdin(regexp, changes):
    any_matches = False
    for line in iter(sys.stdin.readline, ''):
        matches = re.match(regexp, line)
        if matches:
            file_name = matches.group('file_name')
            line_number = int(matches.group('line'))
            if file_name in changes and line_number in changes[file_name]:
                print(line, end='')
                any_matches = True
    return any_matches

def validate_regexp(regexp):
    index = regexp.groupindex
    if 'file_name' in index and 'line' in index:
        return True
    else:
        raise TypeError("Regexp must define following groups:\n  - file_name\n  - line")

def main():
    opts, args = setup_argparse()
    if opts.print_only:
        for file_name, changed_lines in changes(repositories('.'), opts.scope).items():
            for line_number in changed_lines:
                print_changed(file_name, line_number)
        return 0
    else:
        regexp = re.compile(opts.regexp)
        validate_regexp(regexp)
        if filter_stdin(regexp, changes(repositories('.'), opts.scope)):
            return 1
        else:
            return 0

if __name__ == "__main__":
    try:
        sys.exit(main())
    except KeyboardInterrupt:
        pass