Created
September 22, 2016 13:16
-
-
Save dopuskh3/71a5b2be2f502e829d7ef06626f92a32 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
from nltk.corpus import verbnet | |
import sys | |
class CommitException(Exception): | |
def __init__(self, msg, line, start, end, fatal=False): | |
Exception.__init__(self,msg) | |
self._line = line | |
self._start = start | |
self._end = end | |
self._fatal = fatal | |
def formatError(self): | |
return "%d:%d:%s" % (self._line, self._start, str(self)) | |
def isFatal(self): | |
return self._fatal | |
class NoDescriptionException(CommitException): | |
pass | |
class InvalidTitleException(CommitException): | |
pass | |
class TrailingSpaceException(CommitException): | |
pass | |
class LineTooLongException(CommitException): | |
pass | |
class InvalidMessageException(CommitException): | |
pass | |
class ContextInfo(object): | |
def __init__(self, line): | |
self.line = line | |
class Validator(object): | |
def __init__(self, content, contextInfo=None): | |
self._content = content | |
self._context = contextInfo | |
def context(self, lineNumber): | |
if not self._context: | |
return lineNumber | |
return lineNumber + self._context.line | |
def validate(self, *args, **kwargs): | |
try: | |
self.run(*args, **kwargs) | |
return True | |
except CommitException, e: | |
print >>sys.stderr, e.formatError() | |
if e.isFatal(): | |
raise e | |
return False | |
class TrailingSpaceValidator(Validator): | |
def run(self): | |
for line, l in enumerate(self._content): | |
if l.rstrip(' ') != l: | |
raise TrailingSpaceException("Tailing space: [%s]" % l, | |
self.context(line+1), | |
len(l.rstrip(' '))+1, | |
len(l)+1) | |
class LineLengthValidator(Validator): | |
def __init__(self, content, max_len, context=None): | |
Validator.__init__(self, content, context) | |
self._max_len = max_len | |
def run(self): | |
for line, l in enumerate(self._content): | |
if len(l) > self._max_len: | |
raise LineTooLongException("Line [%s] is too long (max=%d, length=%d)" % (l, self._max_len, len(l)), | |
self.context(line+1), | |
self._max_len, | |
len(l)) | |
class TitleValidator(Validator): | |
def run(self): | |
TrailingSpaceValidator(self._content, self._context).validate() | |
LineLengthValidator(self._content, 50, self._context).validate() | |
firstWord = self._content[0].split(' ')[0] | |
if not firstWord[0].isupper(): | |
raise InvalidTitleException("First title character must be a capital letter: [%s]" % self._content[0], | |
self.context(1), | |
1, 2) | |
vclass = verbnet.classids(firstWord.lower()) | |
if not vclass: | |
raise InvalidTitleException("First title work must begin with an infinitive verb [%s]" % self._content[0], | |
self.context(1), | |
1, len(firstWord)) | |
class EmptyLinesValidator(Validator): | |
def run(self): | |
if self._content and self._content[0]: | |
if not self._content[0].strip(' ').strip('\t'): | |
raise CommitException("Too much empty lines", self.context(1), 1, 2) | |
class DescriptionValidator(Validator): | |
def run(self): | |
EmptyLinesValidator(self._content, self._context).validate() | |
TrailingSpaceValidator(self._content, self._context).validate() | |
LineLengthValidator(self._content, 72, self._context).validate() | |
class CommitValidator(Validator): | |
def __init__(self, msg): | |
self._msg = msg | |
def run(self): | |
lines = self._msg.splitlines() | |
if len(lines) < 3: | |
raise NoDescriptionException("Commit message do not contains a description", 0, 1, | |
len(lines[0]), fatal=True) | |
if lines[1]: | |
raise InvalidMessageException("Second commit line must be empty", 1, 1, 2, fatal=True) | |
title, description = [lines[0]], lines[2:] | |
self._validateTitle(title) | |
self._validateDescription(description) | |
def _validateTitle(self, title): | |
TitleValidator(title).validate() | |
def _validateDescription(self, description): | |
DescriptionValidator(description, ContextInfo(2)).validate() | |
def validateFromFile(filename): | |
with open(filename, 'r') as fd: | |
msg = fd.read() | |
CommitValidator(msg).validate() | |
if __name__ == "__main__": | |
validateFromFile(sys.argv[1]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment