Add new repoman check classes using StringIO; possibly need some testing, obviously...
authorAlec Warner <antarus@gentoo.org>
Mon, 1 Oct 2007 10:19:21 +0000 (10:19 -0000)
committerAlec Warner <antarus@gentoo.org>
Mon, 1 Oct 2007 10:19:21 +0000 (10:19 -0000)
svn path=/main/trunk/; revision=7894

pym/repoman/__init__.py [new file with mode: 0644]
pym/repoman/checks.py [new file with mode: 0644]
pym/repoman/errors.py [new file with mode: 0644]

diff --git a/pym/repoman/__init__.py b/pym/repoman/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/pym/repoman/checks.py b/pym/repoman/checks.py
new file mode 100644 (file)
index 0000000..89f041d
--- /dev/null
@@ -0,0 +1,177 @@
+# repoman: Checks
+# Copyright 2007 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+
+import time
+import re
+import os
+
+from repoman.errors import COPYRIGHT_ERROR, LICENSE_ERROR, CVS_HEADER_ERROR, \
+       LEADING_SPACES_ERROR, READONLY_ASSIGNMENT_ERROR, TRAILING_WHITESPACE_ERROR, \
+       MISSING_QUOTES_ERROR
+
+
+class ContentCheckException(Exception):
+       """Parent class for exceptions relating to invalid content"""
+       
+       def __init__(self, str):
+               Exception.__init__(self, str)
+
+
+class ContentCheck(object):
+       """Given a file-like object, run checks over it's content
+       
+       Args:
+         contents - A file-like object, preferably a StringIO instance
+       
+       Raises:
+               ContentCheckException or a subclass
+       """
+       repoman_check_name = None
+       
+       def __init__(self, contents):
+               self.contents = contents
+               pass
+       
+       def Run(self):
+               """Run the check against the contents, return a sequence of errors
+                  of the form ((line, error),...)
+               """
+               pass
+
+
+class EbuildHeaderCheck(ContentCheck):
+       """Ensure ebuilds have proper headers
+       
+       Args:
+               modification_year - Year the ebuild was last modified
+       """
+
+       repoman_check_name = 'ebuild.badheader'
+
+       gentoo_copyright = r'^# Copyright ((1999|200\d)-)?%s Gentoo Foundation$'
+       # Why a regex here, use a string match
+       # gentoo_license = re.compile(r'^# Distributed under the terms of the GNU General Public License v2$')
+       gentoo_license = r'# Distributed under the terms of the GNU General Public License v2'
+       cvs_header = re.compile(r'^#\s*\$Header.*\$$')
+
+       def __init__(self, contents, modification_year):
+               ContentCheck.__init__(self, contents)
+               self.modification_year = modification_year
+               self.gentoo_copyright_re = re.compile(self.gentoo_copyright % self.modification_year)
+
+       def Run(self):
+               """Locate simple header mistakes in an ebuild
+               Copyright header errors
+               CVS header errors
+               License header errors
+               """
+               errors = []
+               for num, line in enumerate(self.contents):
+                       if num == 0:
+                               match = self.gentoo_copyright_re.match(line)
+                               if not match:
+                                       errors.append((num + 1, COPYRIGHT_ERROR))
+                       if num == 1 and line.strip() != self.gentoo_license:
+                               errors.append((num + 1, LICENSE_ERROR))
+                       if num == 2:
+                               match = self.cvs_header.match(line)
+                               if not match:
+                                       errors.append((num + 1, CVS_HEADER_ERROR))
+                       if num > 2:
+                               return errors
+               return errors
+
+
+class EbuildWhitespaceCheck(ContentCheck):
+       """Ensure ebuilds have proper whitespacing"""
+
+       repoman_check_name = 'ebuild.minorsyn'
+
+       ignore_line = re.compile(r'(^$)|(^(\t)*#)')
+       leading_spaces = re.compile(r'^[\S\t]')
+       trailing_whitespace = re.compile(r'.*([\S]$)')  
+
+       def __init__(self, contents):
+               ContentCheck.__init__(self, contents)
+
+       def Run(self):
+               """Locate simple whitespace errors
+               Lines with leading spaces or trailing whitespace
+               """
+               errors = []
+               for num, line in enumerate(self.contents):
+                       match = self.ignore_line.match(line)
+                       if match:
+                               continue
+                       match = self.leading_spaces.match(line)
+                       if not match:
+                               errors.append((num + 1, LEADING_SPACES_ERROR))
+                       match = self.trailing_whitespace.match(line)
+                       if not match:
+                               errors.append((num + 1, TRAILING_WHITESPACE_ERROR))
+               return errors
+
+
+class EbuildQuoteCheck(ContentCheck):
+       """Ensure ebuilds have valid quoting around things like D,FILESDIR, etc..."""
+
+       repoman_check_name = 'ebuild.minorsyn'
+
+       missing_quotes = re.compile(r'[^"]\${?(D|S|T|ROOT|FILESDIR|WORKDIR)}?\W')
+       missing_quotes_exclude = re.compile(r'\[\[.*[^"]\${?(D|S|T|ROOT|FILESDIR|WORKDIR)}?\W.*\]\]')
+       ignore_line = re.compile(r'(^$)|(^(\t)*#)')
+       
+       def __init__(self, contents):
+               ContentCheck.__init__(self, contents)
+
+       def Run(self):
+               """Locate simple errors in ebuilds:
+               Missing quotes around variables that may contain spaces
+               """
+
+               errors = []
+               for num, line in enumerate(self.contents):
+                       match = self.ignore_line.match(line)
+                       if match:
+                               continue
+                       missing_quotes_line = self.missing_quotes.search(line)
+                       if missing_quotes_line:
+                               for group in missing_quotes_line.group():
+                                       match = self.missing_quotes_exclude.search(group)
+                                       if not match:
+                                               errors.append((num + 1, MISSING_QUOTES_ERROR))
+                                               break
+               return errors
+
+
+class EbuildAssignmentCheck(ContentCheck):
+       """Ensure ebuilds don't assign to readonly variables."""
+
+       repoman_check_name = 'ebuild.majorsyn'
+
+       readonly_assignment = re.compile(r'^\s*(export\s+)?(A|CATEGORY|P|PV|PN|PR|PVR|PF|D|WORKDIR|FILESDIR|FEATURES|USE)=')
+       line_continuation = re.compile(r'([^#]*\S)(\s+|\t)\\$')
+       ignore_line = re.compile(r'(^$)|(^(\t)*#)')
+
+       def __init__(self, contents):
+               ContentCheck.__init__(self, contents)
+
+       def Run(self):
+               """Locate simple errors in ebuilds:
+               Assigning to read-only variables.
+               """
+
+               errors = []
+               previous_line = None
+               # enumerate is 0 indexed, so add one when necesseary
+               for num, line in enumerate(self.contents):
+                       match = self.ignore_line.match(line)
+                       if match:
+                               continue
+                       match = self.readonly_assignment.match(line)
+                       if match and (not previous_line or not self.line_continuation.match(previous_line)):
+                               errors.append((num + 1, READONLY_ASSIGNMENT_ERROR))
+                       previous_line = line
+               return errors
diff --git a/pym/repoman/errors.py b/pym/repoman/errors.py
new file mode 100644 (file)
index 0000000..c8734ff
--- /dev/null
@@ -0,0 +1,12 @@
+# repoman: Error Messages
+# Copyright 2007 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+
+COPYRIGHT_ERROR = 'Invalid Gentoo Copyright on line: %d'
+LICENSE_ERROR = 'Invalid Gentoo/GPL License on line: %d'
+CVS_HEADER_ERROR = 'Malformed CVS Header on line: %d'
+LEADING_SPACES_ERROR = 'Ebuild contains leading spaces on line: %d'
+TRAILING_WHITESPACE_ERROR = 'Trailing whitespace error on line: %d'
+READONLY_ASSIGNMENT_ERROR = 'Ebuild contains assignment to read-only variable on line: %d'
+MISSING_QUOTES_ERROR = 'Unquoted Variable on line: %d'
\ No newline at end of file