root / guide / pre_commit.py

Revision 144:60ec3971f3f6, 4.5 kB (checked in by Tarek Ziad?? <tarek@…>, 11 months ago)

Added scripts from the book

Line 
1#!/usr/bin/python
2# -*- coding: UTF-8 -*-
3#
4# Copyright (c) 2007 Tarek Ziadé
5#
6# Authors:
7#   Tarek Ziadé <tarek@ziade.org>
8#
9# This program is free software; you can redistribute it and/or
10# modify it under the terms of the GNU General Public License
11# as published by the Free Software Foundation; either version 2
12# of the License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program; if not, write to the Free Software
21# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22from subprocess import Popen
23from subprocess import PIPE
24import re
25import os
26import smtplib
27
28re_options = re.IGNORECASE | re.MULTILINE | re.DOTALL
29
30class EOF(object):
31    def findall(self, content):
32        if content.endswith('\n'):
33            return []
34        return ['\n']
35
36tab_catcher = re.compile(r'^\t', re_options)
37windows_catcher = re.compile(r'\r\n$', re_options)
38
39testers = (('found <TAB>', tab_catcher),
40           ('found <CR/LF>', windows_catcher),
41           ('no new line at the end', EOF()))
42
43
44def command_output(cmd):
45    """ Capture a command's standard output."""
46    return Popen(cmd.split(), stdout=PIPE).communicate()[0]
47
48def pylint_output(cmd):
49    """ Capture a command's standard output."""
50    def _getLine(line):
51        if line == '':
52            return False
53        if 'Wrong encoding specified' in line:
54            return False
55        return line[0] in ('E', 'C', 'W', 'F')
56
57    return [line for line in
58            Popen(cmd.split(), stdout=PIPE).communicate()[0].split('\n')
59            if _getLine(line)]
60
61def checkFileQuality(repos, path):
62    """Checks a file"""
63    file = os.path.join(repos, path)
64    try:
65        return command_output('pylint %s' % file).split('\n')
66    except OSError:
67        return []
68
69def sendResults(file, results):
70    """Sends QA result"""
71    fromaddr = 'tarek@emencia.com'
72    toaddrs  = ['tarek@emencia.com']
73    subject = '[ECS QA Check] %s' % file
74    msg = ("From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n"
75        % (fromaddr, ", ".join(toaddrs), subject))
76    msg = '%s%s\n' % (msg, results)
77    server = smtplib.SMTP('localhost')
78    server.sendmail(fromaddr, toaddrs, msg)
79    server.quit()
80
81def files_changed(look_cmd):
82    """ List the files added or updated by this transaction."""
83    def filename(line):
84        return line[4:]
85
86    def added_or_updated(line):
87        return line and line[0] in ("A", "U")
88
89    return [filename(line) for line in
90            command_output(look_cmd % "changed").split("\n")
91            if added_or_updated(line)]
92
93def file_contents(filename, look_cmd):
94    """Return a file's contents for this transaction"""
95    return command_output("%s %s" % (look_cmd % "cat", filename))
96
97def test_expression(expr, filename, look_cmd):
98    """test regexpr over file"""
99    return len(expr.findall(file_contents(filename, look_cmd))) > 0
100
101def check_file(look_cmd, repos):
102    """checks Python files in this transaction"""
103    def is_python_file(fname):
104        return os.path.splitext(fname)[1] in ".py".split()
105
106    erroneous_files = []
107
108    checked = []
109
110    for file in files_changed(look_cmd):
111        if not is_python_file(file):
112            continue
113
114        errors = 0
115
116        for error_type, tester in testers:
117            if test_expression(tester, file, look_cmd):
118                erroneous_files.append((error_type, file))
119                errors += 1
120
121    num_failures = len(erroneous_files)
122
123    if num_failures > 0:
124        sys.stderr.write("[ERROR] please check your files:\n")
125        for error_type, file in erroneous_files:
126            sys.stderr.write("[ERROR] %s in %s\n" % (error_type, file))
127
128    return num_failures
129
130def main():
131    from optparse import OptionParser
132    parser = OptionParser()
133    parser.add_option("-r", "--revision",
134                        help="Test mode. TXN actually refers to a revision.",
135                        action="store_true", default=False)
136    errors = 0
137    (opts, (repos, txn_or_rvn)) = parser.parse_args()
138    look_opt = ("--transaction", "--revision")[opts.revision]
139    look_cmd = "svnlook %s %s %s %s" % (
140        "%s", repos, look_opt, txn_or_rvn)
141    errors += check_file(look_cmd, repos)
142
143    return errors
144
145if __name__ == "__main__":
146    import sys
147    sys.exit(main())
Note: See TracBrowser for help on using the browser.