Imported Upstream version 1.7 upstream/1.7
authorDongHun Kwak <dh0128.kwak@samsung.com>
Tue, 23 Feb 2021 00:26:05 +0000 (09:26 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Tue, 23 Feb 2021 00:26:05 +0000 (09:26 +0900)
19 files changed:
AbstractCheck.py
AppDataCheck.py
BinariesCheck.py
FilesCheck.py
Filter.py
INSTALL
InitScriptCheck.py
Makefile
MenuCheck.py
Pkg.py
PostCheck.py
SCLCheck.py
SourceCheck.py
SpecCheck.py
TagsCheck.py
ZipCheck.py
config
rpmlint
test.sh

index 64d279e..1c2bae0 100644 (file)
@@ -57,7 +57,7 @@ class AbstractCheck:
     def check_binary(self, pkg):
         return
 
-    def check_spec(self, pkg, spec_file, spec_lines=[]):
+    def check_spec(self, pkg, spec_file):
         return
 
     def check_url(self, pkg, tag, url):
index 03fc655..57c0fe9 100644 (file)
@@ -9,8 +9,12 @@
 from Filter import addDetails, printError
 from Pkg import getstatusoutput
 import AbstractCheck
+import Config
 
 STANDARD_BIN_DIRS = ['/bin/', '/sbin/', '/usr/bin/', '/usr/sbin/']
+DEFAULT_APPDATA_CHECKER = ('appstream-util', 'validate-relax')
+
+appdata_checker = Config.getOption("AppDataChecker", DEFAULT_APPDATA_CHECKER)
 
 
 class AppDataCheck(AbstractCheck.AbstractFilesCheck):
@@ -25,21 +29,19 @@ class AppDataCheck(AbstractCheck.AbstractFilesCheck):
         root = pkg.dirName()
         f = root + filename
         try:
-            st = getstatusoutput(('appdata-validate', f), True)
+            st = getstatusoutput(appdata_checker + (f,))
         except OSError:
-            # ignore the check if appdata-validate is not install
+            # ignore if the checker is not installed
             return
         if st[0]:
-            for line in st[1].splitlines():
-                if 'problems detected' in line:
-                    printError(pkg, 'invalid-appdata-file', filename)
+            printError(pkg, 'invalid-appdata-file', filename)
 
 
 check = AppDataCheck()
 
 addDetails(
 'invalid-appdata-file',
-'''appdata file is not valid, check with appdata-validate''',
+'''appdata file is not valid, check with %s''' % (" ".join(appdata_checker)),
 )
 
 # Local variables:
index 4634eed..b80a3fa 100644 (file)
@@ -278,7 +278,7 @@ class BinariesCheck(AbstractCheck.AbstractCheck):
         multi_pkg = False
         srpm = pkg[rpm.RPMTAG_SOURCERPM]
         if srpm:
-            res = srcname_regex.search(Pkg.b2s(srpm))
+            res = srcname_regex.search(srpm)
             if res:
                 multi_pkg = (pkg.name != res.group(1))
 
index 1ad548b..1e43bf3 100644 (file)
@@ -17,7 +17,7 @@ import sys
 import rpm
 
 from Filter import addDetails, printError, printWarning
-from Pkg import b2s, catcmd, getstatusoutput, is_utf8, is_utf8_str
+from Pkg import b2s, catcmd, getstatusoutput, is_utf8, is_utf8_bytestr
 import AbstractCheck
 import Config
 
@@ -194,7 +194,7 @@ buildconfig_rpath_regex = re.compile('(?:-rpath|Wl,-R)\\b')
 sofile_regex = re.compile('/lib(64)?/(.+/)?lib[^/]+\.so$')
 devel_regex = re.compile('(.*)-(debug(info)?|devel|headers|source|static)$')
 debuginfo_package_regex = re.compile('-debug(info)?$')
-lib_regex = re.compile('lib(64)?/lib[^/]*\.so\..*')
+lib_regex = re.compile('lib(64)?/lib[^/]*(\.so\..*|-[0-9.]+\.so)')
 ldconfig_regex = re.compile('^[^#]*ldconfig', re.MULTILINE)
 depmod_regex = re.compile('^[^#]*depmod', re.MULTILINE)
 install_info_regex = re.compile('^[^#]*install-info', re.MULTILINE)
@@ -312,7 +312,7 @@ def peek(filename, pkg, length=1024):
     return (chunk, istext)
 
 # See Python sources for a full list of the values here.
-# http://hg.python.org/cpython/file/tip/Lib/importlib/_bootstrap.py
+# http://hg.python.org/cpython/file/tip/Lib/importlib/_bootstrap_external.py
 # http://hg.python.org/cpython/file/2.7/Python/import.c
 _python_magic_values = {
     '2.2': 60717,
@@ -326,6 +326,7 @@ _python_magic_values = {
     '3.2': 3180,
     '3.3': 3230,
     '3.4': 3310,
+    '3.5': 3350,
     }
 
 
@@ -394,17 +395,17 @@ class FilesCheck(AbstractCheck.AbstractCheck):
 
     def check(self, pkg):
 
-        files = pkg.files()
-
         if use_utf8:
-            for filename in files:
-                if not is_utf8_str(filename):
-                    printError(pkg, 'filename-not-utf8', filename)
+            for filename in pkg.header[rpm.RPMTAG_FILENAMES] or ():
+                if not is_utf8_bytestr(filename):
+                    printError(pkg, 'filename-not-utf8', b2s(filename))
 
         # Rest of the checks are for binary packages only
         if pkg.isSource():
             return
 
+        files = pkg.files()
+
         # Check if the package is a development package
         devel_pkg = devel_regex.search(pkg.name)
 
@@ -421,7 +422,7 @@ class FilesCheck(AbstractCheck.AbstractCheck):
         python_dep_error = False
         lib_file = False
         non_lib_file = None
-        log_file = None
+        log_files = []
         logrotate_file = False
         debuginfo_srcs = False
         debuginfo_debugs = False
@@ -512,13 +513,13 @@ class FilesCheck(AbstractCheck.AbstractCheck):
             mode_is_exec = mode & int("111", 8)
 
             if log_regex.search(f):
-                log_file = f
+                log_files.append(f)
 
             # Hardlink check
-            hardlink = hardlinks.get((rdev, inode))
-            if hardlink and os.path.dirname(hardlink) != os.path.dirname(f):
-                printWarning(pkg, 'cross-directory-hard-link', f, hardlink)
-            hardlinks[(rdev, inode)] = f
+            for hardlink in hardlinks.get((rdev, inode), ()):
+                if os.path.dirname(hardlink) != os.path.dirname(f):
+                    printWarning(pkg, 'cross-directory-hard-link', f, hardlink)
+            hardlinks.setdefault((rdev, inode), []).append(f)
 
             # normal file check
             if stat.S_ISREG(mode):
@@ -526,24 +527,24 @@ class FilesCheck(AbstractCheck.AbstractCheck):
                 # set[ug]id bit check
                 if stat.S_ISGID & mode or stat.S_ISUID & mode:
                     if stat.S_ISUID & mode:
-                        printError(pkg, 'setuid-binary', f, user, oct(perm))
+                        printError(pkg, 'setuid-binary', f, user, "%o" % perm)
                     if stat.S_ISGID & mode:
                         if not (group == 'games' and
                                 (games_path_regex.search(f) or
                                  games_group_regex.search(
                                     pkg[rpm.RPMTAG_GROUP]))):
                             printError(pkg, 'setgid-binary', f, group,
-                                       oct(perm))
+                                       "%o" % perm)
                     if mode & int("777", 8) != int("755", 8):
                         printError(pkg, 'non-standard-executable-perm', f,
-                                   oct(perm))
+                                   "%o" % perm)
 
                 # Prefetch scriptlets, strip quotes from them (#169)
-                postin = b2s(pkg[rpm.RPMTAG_POSTIN]) or \
+                postin = pkg[rpm.RPMTAG_POSTIN] or \
                          pkg.scriptprog(rpm.RPMTAG_POSTINPROG)
                 if postin:
                     postin = quotes_regex.sub('', postin)
-                postun = b2s(pkg[rpm.RPMTAG_POSTUN]) or \
+                postun = pkg[rpm.RPMTAG_POSTUN] or \
                          pkg.scriptprog(rpm.RPMTAG_POSTUNPROG)
                 if postun:
                     postun = quotes_regex.sub('', postun)
@@ -582,6 +583,8 @@ class FilesCheck(AbstractCheck.AbstractCheck):
 
                 # check ldconfig call in %post and %postun
                 if lib_regex.search(f):
+                    if devel_pkg:
+                        printError(pkg, 'non-devel-file-in-devel-package', f)
                     if not postin:
                         printError(pkg, 'library-without-ldconfig-postin', f)
                     else:
@@ -624,7 +627,7 @@ class FilesCheck(AbstractCheck.AbstractCheck):
                     elif not install_info_regex.search(postin):
                         printError(pkg, 'postin-without-install-info', f)
 
-                    preun = b2s(pkg[rpm.RPMTAG_PREUN]) or \
+                    preun = pkg[rpm.RPMTAG_PREUN] or \
                             pkg.scriptprog(rpm.RPMTAG_PREUNPROG)
                     if not postun and not preun:
                         printError(pkg,
@@ -638,7 +641,7 @@ class FilesCheck(AbstractCheck.AbstractCheck):
                 if perl_temp_file_regex.search(f):
                     printWarning(pkg, 'perl-temp-file', f)
 
-                is_buildconfig = buildconfigfile_regex.search(f) and True
+                is_buildconfig = istext and buildconfigfile_regex.search(f)
 
                 # check rpaths in buildconfig files
                 if is_buildconfig:
@@ -650,7 +653,7 @@ class FilesCheck(AbstractCheck.AbstractCheck):
                 if res:
                     if not mode_is_exec:
                         printWarning(pkg, 'non-executable-in-bin', f,
-                                     oct(perm))
+                                     "%o" % perm)
                     else:
                         exe = res.group(1)
                         if "/" not in exe:
@@ -668,13 +671,13 @@ class FilesCheck(AbstractCheck.AbstractCheck):
                             ok_nonreadable = True
                             break
                     if not ok_nonreadable:
-                        printError(pkg, 'non-readable', f, oct(perm))
+                        printError(pkg, 'non-readable', f, "%o" % perm)
                 if size == 0 and not normal_zero_length_regex.search(f) and \
                         f not in ghost_files:
                     printError(pkg, 'zero-length', f)
 
                 if mode & stat.S_IWOTH:
-                    printError(pkg, 'world-writable', f, oct(perm))
+                    printError(pkg, 'world-writable', f, "%o" % perm)
 
                 if not perl_dep_error:
                     res = perl_regex.search(f)
@@ -748,7 +751,7 @@ class FilesCheck(AbstractCheck.AbstractCheck):
                 # normal executable check
                 if mode & stat.S_IXUSR and perm != int("755", 8):
                     printError(pkg, 'non-standard-executable-perm',
-                               f, oct(perm))
+                               f, "%o" % perm)
                 if mode_is_exec:
                     if f in config_files:
                         printError(pkg, 'executable-marked-as-config-file', f)
@@ -818,7 +821,7 @@ class FilesCheck(AbstractCheck.AbstractCheck):
                                        interpreter)
                         if mode_is_exec:
                             printError(pkg, 'executable-sourced-script',
-                                       f, oct(perm))
+                                       f, "%o" % perm)
                     # ...but executed ones should
                     elif interpreter or mode_is_exec or script_regex.search(f):
                         if interpreter:
@@ -832,7 +835,7 @@ class FilesCheck(AbstractCheck.AbstractCheck):
 
                         if not mode_is_exec and not is_doc:
                             printError(pkg, 'non-executable-script', f,
-                                       oct(perm), interpreter)
+                                       "%o" % perm, interpreter)
                         if b'\r' in chunk:
                             printError(
                                 pkg, 'wrong-script-end-of-line-encoding', f)
@@ -859,9 +862,9 @@ class FilesCheck(AbstractCheck.AbstractCheck):
             # normal dir check
             elif stat.S_ISDIR(mode):
                 if mode & int("1002", 8) == 2:  # world writable w/o sticky bit
-                    printError(pkg, 'world-writable', f, oct(perm))
+                    printError(pkg, 'world-writable', f, "%o" % perm)
                 if perm != int("755", 8):
-                    printError(pkg, 'non-standard-dir-perm', f, oct(perm))
+                    printError(pkg, 'non-standard-dir-perm', f, "%o" % perm)
                 if pkg.name not in filesys_packages and f in STANDARD_DIRS:
                     printError(pkg, 'standard-dir-owned-by-package', f)
                 if hidden_file_regex.search(f):
@@ -975,8 +978,8 @@ class FilesCheck(AbstractCheck.AbstractCheck):
                 if stat.S_IWGRP & mode or stat.S_IWOTH & mode:
                     printError(pkg, 'non-owner-writeable-only-crontab-file', f)
 
-        if log_file and not logrotate_file:
-            printWarning(pkg, 'log-files-without-logrotate', log_file)
+        if len(log_files) and not logrotate_file:
+            printWarning(pkg, 'log-files-without-logrotate', sorted(log_files))
 
         if lib_package and lib_file and non_lib_file:
             printError(pkg, 'outside-libdir-files', non_lib_file)
@@ -1106,6 +1109,9 @@ that the file is an executable or that it has executable permissions.''',
 package. If you want to include source code in your package, be sure to
 create a development package.''',
 
+'non-devel-file-in-devel-package',
+'''A non-development file is located in a devel package.''',
+
 'non-standard-dir-perm',
 '''A standard directory should have permission set to 0755. If you get this
 message, it means that you have wrong directory permissions in some dirs
@@ -1362,7 +1368,6 @@ for i in disallowed_dirs:
 for packages to install files in this directory.''' % i)
 
 
-
 # FilesCheck.py ends here
 
 # Local variables:
index eaa8384..5ce6219 100644 (file)
--- a/Filter.py
+++ b/Filter.py
@@ -7,6 +7,7 @@
 # Purpose       : filter the output of rpmlint to allow exceptions.
 #############################################################################
 
+from __future__ import print_function
 import locale
 import sys
 import textwrap
@@ -27,10 +28,16 @@ if sys.stdout.isatty():
     def __print(s):
         print(s)
 else:
+    __stdout = sys.stdout
+    if not __stdout.encoding:  # Python < 3 only?
+        import codecs
+        if hasattr(__stdout, "buffer"):
+            __stdout = __stdout.buffer
+        __stdout = codecs.getwriter(
+            locale.getpreferredencoding())(__stdout, "replace")
+
     def __print(s):
-        if isinstance(s, unicode):
-            s = s.encode(locale.getpreferredencoding(), "replace")
-        print(s)
+        print(s, file=__stdout)
 
 
 def printInfo(pkg, reason, *details):
@@ -75,7 +82,8 @@ def _print(msgtype, pkg, reason, details):
         Testing.addOutput(s)
     else:
         if _rawout:
-            print >>_rawout, s.encode(locale.getpreferredencoding(), "replace")
+            print(s.encode(locale.getpreferredencoding(), "replace"),
+                  file=_rawout)
         if not Config.isFiltered(s):
             printed_messages[msgtype] += 1
             _badness_score += badness
diff --git a/INSTALL b/INSTALL
index c53ae86..34d5da0 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -1,6 +1,6 @@
 You need the following utilities to run rpmlint:
 
-       o python 2.x (x >= 6)
+       o python 2.6 or newer
        o rpm 4.4.2.2 or newer and its python bindings
        o libmagic and its python bindings (optional)
        o readelf
@@ -9,11 +9,11 @@ You need the following utilities to run rpmlint:
        o gzip, bzip2 and xz
        o groff and gtbl (optional)
        o enchant and its python bindings (optional)
-       o appdata-validate (optional)
+       o appstream-util, part of appstream-glib (optional)
 
 and the following programs to install it:
 
-       o python 2.x (x >= 6)
+       o python 2.6 or newer
        o rpm python bindings 4.4 or newer
        o sed
        o pytest (for running the test suite)
index 5d851d4..0559405 100644 (file)
@@ -59,14 +59,14 @@ class InitScriptCheck(AbstractCheck.AbstractCheck):
                 printError(pkg, 'init-script-name-with-dot', fname)
 
             # check chkconfig call in %post and %preun
-            postin = Pkg.b2s(pkg[rpm.RPMTAG_POSTIN]) or \
+            postin = pkg[rpm.RPMTAG_POSTIN] or \
                      pkg.scriptprog(rpm.RPMTAG_POSTINPROG)
             if not postin:
                 printError(pkg, 'init-script-without-chkconfig-postin', fname)
             elif not chkconfig_regex.search(postin):
                 printError(pkg, 'postin-without-chkconfig', fname)
 
-            preun = Pkg.b2s(pkg[rpm.RPMTAG_PREUN]) or \
+            preun = pkg[rpm.RPMTAG_PREUN] or \
                     pkg.scriptprog(rpm.RPMTAG_PREUNPROG)
             if not preun:
                 printError(pkg, 'init-script-without-chkconfig-preun', fname)
@@ -84,7 +84,7 @@ class InitScriptCheck(AbstractCheck.AbstractCheck):
             # check common error in file content
             content = None
             try:
-                content = Pkg.readlines(pkgfile.path)
+                content = [x for x in Pkg.readlines(pkgfile.path)]
             except Exception:
                 e = sys.exc_info()[1]
                 printWarning(pkg, 'read-error', e)
index e6e2a36..1efe7b4 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -17,29 +17,35 @@ FILES = rpmlint *.py INSTALL README README.devel COPYING tools/*.py \
 GENERATED = ChangeLog __version__.py
 
 PACKAGE = rpmlint
-PYTHON = python
+PYTHON = /usr/bin/python
 
 # update this variable to create a new release
-VERSION := 1.6
+VERSION := 1.7
 
 # for the [A-Z]* part
 LC_ALL:=C
 export LC_ALL
 
 all: __version__.py __isocodes__.py
-       if [ "x${COMPILE_PYC}" = "x1" ] ; then \
-               $(PYTHON) -m py_compile [A-Z]*.py __*__.py ; \
-       fi
-       $(PYTHON) -O -m py_compile [A-Z]*.py __*__.py
 
 clean:
-       rm -f *~ *.pyc *.pyo $(GENERATED)
+       rm -rf *~ *.py[co] */*.py[co] __pycache__ */__pycache__ $(GENERATED)
 
 install: all
        mkdir -p $(DESTDIR)$(LIBDIR) $(DESTDIR)$(BINDIR) $(DESTDIR)$(ETCDIR)/$(PACKAGE) $(DESTDIR)$(MANDIR)/man1
-       -cp -p *.pyc $(DESTDIR)$(LIBDIR)
-       cp -p *.py *.pyo $(DESTDIR)$(LIBDIR)
-       cp -p rpmlint rpmdiff $(DESTDIR)$(BINDIR)
+       cp -p *.py $(DESTDIR)$(LIBDIR)
+       if [ "x${COMPILE_PYC}" = "x1" ] ; then \
+               $(PYTHON) -m py_compile \
+                       $(DESTDIR)$(LIBDIR)/[A-Z]*.py \
+                       $(DESTDIR)$(LIBDIR)/__*__.py ; \
+       fi
+       $(PYTHON) -O -m py_compile \
+               $(DESTDIR)$(LIBDIR)/[A-Z]*.py \
+               $(DESTDIR)$(LIBDIR)/__*__.py ; \
+       for file in rpmlint rpmdiff ; do \
+               sed -e "s,#!/usr/bin/python ,#!$(PYTHON) ," $$file > $(DESTDIR)$(BINDIR)/$$file ; \
+               chmod +x $(DESTDIR)$(BINDIR)/$$file ; \
+       done
        cp -p config $(DESTDIR)$(ETCDIR)/$(PACKAGE)
        compdir=`pkg-config --variable=completionsdir bash-completion 2>/dev/null` ; \
        if [ "x$$compdir" = "x" ] ; then \
@@ -48,7 +54,7 @@ install: all
        else \
                mkdir -p $(DESTDIR)$$compdir ; \
                cp -p rpmlint.bash-completion $(DESTDIR)$$compdir/rpmlint ; \
-               ln -s rpmlint $(DESTDIR)$$compdir/rpmdiff ; \
+               ln -sf rpmlint $(DESTDIR)$$compdir/rpmdiff ; \
        fi
        cp -p rpmdiff.1 rpmlint.1 $(DESTDIR)$(MANDIR)/man1
 
index c137ab9..7751133 100644 (file)
@@ -198,14 +198,14 @@ class MenuCheck(AbstractCheck.AbstractCheck):
                     printError(pkg, 'menu-in-wrong-dir', fname)
 
         if menus:
-            postin = Pkg.b2s(pkg[rpm.RPMTAG_POSTIN]) or \
+            postin = pkg[rpm.RPMTAG_POSTIN] or \
                      pkg.scriptprog(rpm.RPMTAG_POSTINPROG)
             if not postin:
                 printError(pkg, 'menu-without-postin')
             elif not update_menus_regex.search(postin):
                 printError(pkg, 'postin-without-update-menus')
 
-            postun = Pkg.b2s(pkg[rpm.RPMTAG_POSTUN]) or \
+            postun = pkg[rpm.RPMTAG_POSTUN] or \
                      pkg.scriptprog(rpm.RPMTAG_POSTUNPROG)
             if not postun:
                 printError(pkg, 'menu-without-postun')
diff --git a/Pkg.py b/Pkg.py
index e97d7ef..7439a3f 100644 (file)
--- a/Pkg.py
+++ b/Pkg.py
@@ -13,7 +13,6 @@ import re
 import subprocess
 import sys
 import tempfile
-import unicodedata
 try:
     from urlparse import urljoin
 except:
@@ -37,6 +36,7 @@ if sys.version_info[0] > 2:
     # Blows up with Python < 3 without the exec() hack
     exec('def warn(s): print (s, file=sys.stderr)')
     long = int
+    unicode = str
 
     def b2s(b):
         if b is None:
@@ -62,6 +62,17 @@ PREREQ_FLAG = (rpm.RPMSENSE_PREREQ or 64) | \
               rpm.RPMSENSE_SCRIPT_PREUN | \
               rpm.RPMSENSE_SCRIPT_POSTUN
 
+SCRIPT_TAGS = [
+    (rpm.RPMTAG_PREIN,          rpm.RPMTAG_PREINPROG,         '%pre'),
+    (rpm.RPMTAG_POSTIN,         rpm.RPMTAG_POSTINPROG,        '%post'),
+    (rpm.RPMTAG_PREUN,          rpm.RPMTAG_PREUNPROG,         '%preun'),
+    (rpm.RPMTAG_POSTUN,         rpm.RPMTAG_POSTUNPROG,        '%postun'),
+    (rpm.RPMTAG_TRIGGERSCRIPTS, rpm.RPMTAG_TRIGGERSCRIPTPROG, '%trigger'),
+    (rpm.RPMTAG_PRETRANS,       rpm.RPMTAG_PRETRANSPROG,      '%pretrans'),
+    (rpm.RPMTAG_POSTTRANS,      rpm.RPMTAG_POSTTRANSPROG,     '%posttrans'),
+    (rpm.RPMTAG_VERIFYSCRIPT,   rpm.RPMTAG_VERIFYSCRIPTPROG,  '%verifyscript'),
+    ]
+
 var_regex = re.compile('^(.*)\${?(\w+)}?(.*)$')
 
 
@@ -91,7 +102,7 @@ def substitute_shell_vars(val, script):
         return val
 
 
-def getstatusoutput(cmd, stdoutonly=False, shell=False):
+def getstatusoutput(cmd, stdoutonly=False, shell=False, raw=False):
     '''A version of commands.getstatusoutput() which can take cmd as a
        sequence, thus making it potentially more secure.'''
     if stdoutonly:
@@ -102,12 +113,15 @@ def getstatusoutput(cmd, stdoutonly=False, shell=False):
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.STDOUT, close_fds=True)
     proc.stdin.close()
-    text = b2s(proc.stdout.read())
+    with proc.stdout:
+        text = proc.stdout.read()
     sts = proc.wait()
+    if not raw:
+        text = b2s(text)
+        if text.endswith('\n'):
+            text = text[:-1]
     if sts is None:
         sts = 0
-    if text.endswith('\n'):
-        text = text[:-1]
     return sts, text
 
 bz2_regex = re.compile('\.t?bz2?$')
@@ -125,59 +139,38 @@ def catcmd(fname):
 
 
 def is_utf8(fname):
-    (sts, text) = getstatusoutput(catcmd(fname).split() + [fname])
-    return not sts and is_utf8_str(text)
-
-REPLACEMENT_CHAR = unicodedata.lookup('REPLACEMENT CHARACTER')
+    (sts, output) = getstatusoutput(catcmd(fname).split() + [fname], raw=True)
+    return not sts and is_utf8_bytestr(output)
 
 
-def is_utf8_str(s):
-    if hasattr(s, 'decode'):
-        # byte string
-        try:
-            s.decode('UTF-8')
-        except:
-            return False
-        return True
-    # unicode string
-    return REPLACEMENT_CHAR not in s
+def is_utf8_bytestr(s):
+    try:
+        s.decode('UTF-8')
+    except:
+        return False
+    return True
 
 
-# TODO: PY3
-def to_utf8(string):
+def to_unicode(string):
     if string is None:
-        return ''
+        return unicode('')
     elif isinstance(string, unicode):
         return string
-    try:
-        x = unicode(string, 'ascii')
-        return string
-    except UnicodeError:
-        encodings = ['utf-8', 'iso-8859-1', 'iso-8859-15', 'iso-8859-2']
-        for enc in encodings:
-            try:
-                x = unicode(string, enc)
-            except UnicodeError:
-                pass
-            else:
-                if x.encode(enc) == string:
-                    return x.encode('utf-8')
-    newstring = ''
-    for char in string:
-        if ord(char) > 127:
-            newstring = newstring + '?'
+    for enc in ('utf-8', 'iso-8859-1', 'iso-8859-15', 'iso-8859-2'):
+        try:
+            x = unicode(string, enc)
+        except UnicodeError:
+            pass
         else:
-            newstring = newstring + char
-    return newstring
+            if x.encode(enc) == string:
+                return x
+    return unicode(string, "ascii", errors="replace")
 
 
 def readlines(path):
-    fobj = open(path, 'rb')
-    try:
+    with open(path, 'rb') as fobj:
         for line in fobj:
             yield b2s(line)
-    finally:
-        fobj.close()
 
 
 def mktemp():
@@ -204,20 +197,13 @@ def get_default_valid_rpmgroups(filename=None):
     the rpm package (if installed) if no filename is given"""
     groups = []
     if not filename:
-        try:
-            p = InstalledPkg('rpm')
-        except:
-            pass
-        else:
+        with InstalledPkg("rpm") as p:
             groupsfiles = [x for x in p.files() if x.endswith('/GROUPS')]
             if groupsfiles:
                 filename = groupsfiles[0]
     if filename and os.path.exists(filename):
-        fobj = open(filename)
-        try:
+        with open(filename) as fobj:
             groups = fobj.read().strip().splitlines()
-        finally:
-            fobj.close()
         if 'Development/Debug' not in groups:
             groups.append('Development/Debug')
         if 'Unspecified' not in groups:  # auto-added by rpm >= 4.6.0
@@ -450,7 +436,19 @@ def parse_deps(line):
 
 # classes representing package
 
-class Pkg:
+class AbstractPkg:
+
+    def cleanup(self):
+        pass
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        self.cleanup()
+
+
+class Pkg(AbstractPkg):
 
     _magic_from_compressed_re = re.compile('\([^)]+\s+compressed\s+data\\b')
 
@@ -483,7 +481,7 @@ class Pkg:
                 os.close(fd)
             self.is_source = not self.header[rpm.RPMTAG_SOURCERPM]
 
-        self.name = b2s(self.header[rpm.RPMTAG_NAME])
+        self.name = self[rpm.RPMTAG_NAME]
         if self.isNoSource():
             self.arch = 'nosrc'
         elif self.isSource():
@@ -509,9 +507,14 @@ class Pkg:
         if val == []:
             return None
         else:
-            if key in (rpm.RPMTAG_VERSION, rpm.RPMTAG_RELEASE, rpm.RPMTAG_ARCH,
-                       rpm.RPMTAG_GROUP, rpm.RPMTAG_BUILDHOST,
-                       rpm.RPMTAG_LICENSE):
+            # Note that text tags we want to try decoding for real in TagsCheck
+            # such as summary, description and changelog are not here.
+            if key in (rpm.RPMTAG_NAME, rpm.RPMTAG_VERSION, rpm.RPMTAG_RELEASE,
+                       rpm.RPMTAG_ARCH, rpm.RPMTAG_GROUP, rpm.RPMTAG_BUILDHOST,
+                       rpm.RPMTAG_LICENSE, rpm.RPMTAG_HEADERI18NTABLE,
+                       rpm.RPMTAG_PACKAGER, rpm.RPMTAG_SOURCERPM) \
+            or key in (x[0] for x in SCRIPT_TAGS) \
+            or key in (x[1] for x in SCRIPT_TAGS):
                 val = b2s(val)
             return val
 
@@ -573,7 +576,7 @@ class Pkg:
         # LANGUAGE trumps other env vars per GNU gettext docs, see also #166
         orig = os.environ.get('LANGUAGE')
         os.environ['LANGUAGE'] = lang
-        ret = b2s(self[tag])
+        ret = self[tag]
         if orig is not None:
             os.environ['LANGUAGE'] = orig
         return ret
@@ -666,8 +669,8 @@ class Pkg:
                     self.dirName() or '/', pkgfile.name.lstrip('/')))
                 pkgfile.flags = flags[idx]
                 pkgfile.mode = modes[idx]
-                pkgfile.user = users[idx]
-                pkgfile.group = groups[idx]
+                pkgfile.user = b2s(users[idx])
+                pkgfile.group = b2s(groups[idx])
                 pkgfile.linkto = links[idx] and safe_normpath(links[idx])
                 pkgfile.size = sizes[idx]
                 pkgfile.md5 = md5s[idx]
@@ -676,7 +679,7 @@ class Pkg:
                 pkgfile.inode = inodes[idx]
                 pkgfile.requires = parse_deps(requires[idx])
                 pkgfile.provides = parse_deps(provides[idx])
-                pkgfile.lang = langs[idx]
+                pkgfile.lang = b2s(langs[idx])
                 pkgfile.magic = magics[idx]
                 if not pkgfile.magic and _magic:
                     pkgfile.magic = _magic.file(pkgfile.path)
@@ -750,6 +753,30 @@ class Pkg:
         self._gatherDepInfo()
         return self._provides
 
+    def recommends(self):
+        """Get package Recommends as list of
+           (name, flags, (epoch, version, release)) tuples."""
+        self._gatherDepInfo()
+        return self._recommends
+
+    def suggests(self):
+        """Get package Suggests as list of
+           (name, flags, (epoch, version, release)) tuples."""
+        self._gatherDepInfo()
+        return self._suggests
+
+    def enhances(self):
+        """Get package Enhances as list of
+           (name, flags, (epoch, version, release)) tuples."""
+        self._gatherDepInfo()
+        return self._enhances
+
+    def supplements(self):
+        """Get package Supplements as list of
+           (name, flags, (epoch, version, release)) tuples."""
+        self._gatherDepInfo()
+        return self._supplements
+
     # internal function to gather dependency info used by the above ones
     def _gather_aux(self, header, list, nametag, flagstag, versiontag,
                     prereq=None):
@@ -773,6 +800,10 @@ class Pkg:
             self._provides = []
             self._conflicts = []
             self._obsoletes = []
+            self._recommends = []
+            self._suggests = []
+            self._enhances = []
+            self._supplements = []
 
             self._gather_aux(self.header, self._requires,
                              rpm.RPMTAG_REQUIRENAME,
@@ -791,6 +822,26 @@ class Pkg:
                              rpm.RPMTAG_OBSOLETENAME,
                              rpm.RPMTAG_OBSOLETEFLAGS,
                              rpm.RPMTAG_OBSOLETEVERSION)
+            if hasattr(rpm, "RPMTAG_RECOMMENDNAME"):  # rpm >= 4.12
+                self._gather_aux(self.header, self._recommends,
+                                 rpm.RPMTAG_RECOMMENDNAME,
+                                 rpm.RPMTAG_RECOMMENDFLAGS,
+                                 rpm.RPMTAG_RECOMMENDVERSION)
+            if hasattr(rpm, "RPMTAG_SUGGESTNAME"):  # rpm >= 4.12
+                self._gather_aux(self.header, self._suggests,
+                                 rpm.RPMTAG_SUGGESTNAME,
+                                 rpm.RPMTAG_SUGGESTFLAGS,
+                                 rpm.RPMTAG_SUGGESTVERSION)
+            if hasattr(rpm, "RPMTAG_ENHANCENAME"):  # rpm >= 4.12
+                self._gather_aux(self.header, self._enhances,
+                                 rpm.RPMTAG_ENHANCENAME,
+                                 rpm.RPMTAG_ENHANCEFLAGS,
+                                 rpm.RPMTAG_ENHANCEVERSION)
+            if hasattr(rpm, "RPMTAG_SUPPLEMENTNAME"):  # rpm >= 4.12
+                self._gather_aux(self.header, self._supplements,
+                                 rpm.RPMTAG_SUPPLEMENTNAME,
+                                 rpm.RPMTAG_SUPPLEMENTFLAGS,
+                                 rpm.RPMTAG_SUPPLEMENTVERSION)
 
     def scriptprog(self, which):
         """Get the specified script interpreter as a string.
@@ -798,11 +849,11 @@ class Pkg:
            interpreter arguments, if any."""
         prog = self[which]
         if prog is None:
-            prog = b''
+            prog = ""
         elif isinstance(prog, (list, tuple)):
             # http://rpm.org/ticket/847#comment:2
-            prog = b' '.join(prog)
-        return b2s(prog)
+            prog = "".join(prog)
+        return prog
 
 
 def getInstalledPkgs(name):
@@ -831,7 +882,7 @@ class InstalledPkg(Pkg):
             if not mi:
                 raise KeyError(name)
             try:
-                hdr = mi.next()
+                hdr = next(mi)
             except StopIteration:
                 raise KeyError(name)
 
@@ -851,15 +902,12 @@ class InstalledPkg(Pkg):
 
 
 # Class to provide an API to a "fake" package, eg. for specfile-only checks
-class FakePkg:
+class FakePkg(AbstractPkg):
     def __init__(self, name):
         self.name = name
         self.arch = None
         self.current_linenum = None
 
-    def cleanup(self):
-        pass
-
 
 # Class for files in packages
 class PkgFile(object):
@@ -895,13 +943,12 @@ class PkgFile(object):
 
 if __name__ == '__main__':
     for p in sys.argv[1:]:
-        pkg = Pkg(sys.argv[1], tempfile.gettempdir())
-        print ('Requires: %s' % pkg.requires())
-        print ('Prereq: %s' % pkg.prereq())
-        print ('Conflicts: %s' % pkg.conflicts())
-        print ('Provides: %s' % pkg.provides())
-        print ('Obsoletes: %s' % pkg.obsoletes())
-        pkg.cleanup()
+        with Pkg(p, tempfile.gettempdir()) as pkg:
+            print('Requires: %s' % pkg.requires())
+            print('Prereq: %s' % pkg.prereq())
+            print('Conflicts: %s' % pkg.conflicts())
+            print('Provides: %s' % pkg.provides())
+            print('Obsoletes: %s' % pkg.obsoletes())
 
 # Pkg.py ends here
 
index a87964a..71337fa 100644 (file)
@@ -61,17 +61,6 @@ for p in prereq_assoc:
 # pychecker fix
 del p
 
-script_tags = [
-    (rpm.RPMTAG_PREIN,          rpm.RPMTAG_PREINPROG,         '%pre'),
-    (rpm.RPMTAG_POSTIN,         rpm.RPMTAG_POSTINPROG,        '%post'),
-    (rpm.RPMTAG_PREUN,          rpm.RPMTAG_PREUNPROG,         '%preun'),
-    (rpm.RPMTAG_POSTUN,         rpm.RPMTAG_POSTUNPROG,        '%postun'),
-    (rpm.RPMTAG_TRIGGERSCRIPTS, rpm.RPMTAG_TRIGGERSCRIPTPROG, '%trigger'),
-    (rpm.RPMTAG_PRETRANS,       rpm.RPMTAG_PRETRANSPROG,      '%pretrans'),
-    (rpm.RPMTAG_POSTTRANS,      rpm.RPMTAG_POSTTRANSPROG,     '%posttrans'),
-    (rpm.RPMTAG_VERIFYSCRIPT,   rpm.RPMTAG_VERIFYSCRIPTPROG,  '%verifyscript'),
-    ]
-
 
 def incorrect_shell_script(prog, shellscript):
     return check_syntax_script(prog, '-n', shellscript)
@@ -105,7 +94,7 @@ class PostCheck(AbstractCheck.AbstractCheck):
         prereq = [x[0] for x in pkg.prereq()]
         files = pkg.files()
 
-        for tag in script_tags:
+        for tag in Pkg.SCRIPT_TAGS:
             script = pkg[tag[0]]
 
             if not isinstance(script, list):
@@ -123,15 +112,15 @@ class PostCheck(AbstractCheck.AbstractCheck):
         if ghost_files:
             postin = pkg[rpm.RPMTAG_POSTIN]
             prein = pkg[rpm.RPMTAG_PREIN]
-            if not postin and not prein:
-                printWarning(pkg, 'ghost-files-without-postin')
-            else:
-                for f in ghost_files:
-                    if (not postin or f not in postin) and \
-                       (not prein or f not in prein) and \
-                       f not in pkg.missingOkFiles():
-                        printWarning(pkg,
-                                     'postin-without-ghost-file-creation', f)
+            for f in ghost_files:
+                if f in pkg.missingOkFiles():
+                    continue
+                if not postin and not prein:
+                    printWarning(pkg, 'ghost-files-without-postin')
+                if (not postin or f not in postin) and \
+                        (not prein or f not in prein):
+                    printWarning(pkg,
+                                 'postin-without-ghost-file-creation', f)
 
     def check_aux(self, pkg, files, prog, script, tag, prereq):
         if script:
index a9f099d..d0f783c 100644 (file)
@@ -67,7 +67,7 @@ class SCLCheck(AbstractCheck.AbstractCheck):
                 self._spec_file = pkgfile.path
                 self.check_spec(pkg, self._spec_file)
 
-    def check_spec(self, pkg, spec_file, spec_lines=[]):
+    def check_spec(self, pkg, spec_file):
         '''SCL spec file checks'''
         spec = '\n'.join(Pkg.readlines(spec_file))
         if global_scl_definition.search(spec):
index 83c5d17..fcf796c 100644 (file)
@@ -41,7 +41,7 @@ class SourceCheck(AbstractCheck.AbstractCheck):
                              compress_ext, fname)
             perm = pkgfile.mode & int("7777", 8)
             if perm not in valid_src_perms:
-                printWarning(pkg, 'strange-permission', fname, oct(perm))
+                printWarning(pkg, 'strange-permission', fname, "%o" % perm)
 
 check = SourceCheck()
 
index 2b24b43..b69bead 100644 (file)
@@ -8,6 +8,7 @@
 #############################################################################
 
 import re
+import sys
 import unicodedata
 try:
     from urlparse import urlparse
@@ -96,6 +97,7 @@ conflicts_regex = re.compile('^(?:Build)?Conflicts:\s*(.*)', re.IGNORECASE)
 
 compop_regex = re.compile('[<>=]')
 
+setup_regex = re.compile(r'%setup\b')  # intentionally no whitespace before!
 setup_q_regex = re.compile(' -[A-Za-z]*q')
 setup_t_regex = re.compile(' -[A-Za-z]*T')
 setup_ab_regex = re.compile(' -[A-Za-z]*[ab]')
@@ -154,11 +156,9 @@ class SpecCheck(AbstractCheck.AbstractCheck):
             # check content of spec file
             self.check_spec(pkg, self._spec_file)
 
-    def check_spec(self, pkg, spec_file, spec_lines=[]):
+    def check_spec(self, pkg, spec_file):
         self._spec_file = spec_file
         spec_only = isinstance(pkg, Pkg.FakePkg)
-        if not spec_lines:
-            spec_lines = Pkg.readlines(spec_file)
         patches = {}
         applied_patches = []
         applied_patches_ifarch = []
@@ -198,12 +198,13 @@ class SpecCheck(AbstractCheck.AbstractCheck):
         nbsp = chr(0xA0)
         if is_utf8:
             nbsp = UNICODE_NBSP
+        do_unicode = is_utf8 and sys.version_info[0] <= 2
 
-        for line in spec_lines:
+        for line in Pkg.readlines(spec_file):
 
             pkg.current_linenum += 1
 
-            if is_utf8:
+            if do_unicode:
                 line = unicode(line, "utf-8", "replace")
 
             char = line.find(nbsp)
@@ -259,7 +260,7 @@ class SpecCheck(AbstractCheck.AbstractCheck):
             if if_regex.search(line):
                 if_depth = if_depth + 1
 
-            if line.startswith('%setup'):
+            if setup_regex.match(line):
                 if not setup_q_regex.search(line):
                     # Don't warn if there's a -T without -a or -b
                     if setup_t_regex.search(line):
index fa38a07..b08b0d5 100644 (file)
@@ -466,9 +466,10 @@ def spell_check(pkg, str, fmt, lang, ignored):
         if checker:
             # squeeze whitespace to ease leading context check
             checker.set_text(re.sub(r'\s+', ' ', str))
-            uppername = pkg.name.upper()
             if use_utf8:
-                uppername = Pkg.to_utf8(uppername).decode('utf-8')
+                uppername = Pkg.to_unicode(pkg.header[rpm.RPMTAG_NAME]).upper()
+            else:
+                uppername = pkg.name.upper()
             upperparts = uppername.split('-')
             if lang.startswith('en'):
                 ups = [x + "'S" for x in upperparts]
@@ -538,16 +539,18 @@ class TagsCheck(AbstractCheck.AbstractCheck):
     def _unexpanded_macros(self, pkg, tagname, value, is_url=False):
         if not value:
             return
-        # str(value) because value might be a list
-        for match in AbstractCheck.macro_regex.findall(str(value)):
-            # Do not warn about %XX URL escapes
-            if is_url and re.match('^%[0-9A-F][0-9A-F]$', match, re.I):
-                continue
-            printWarning(pkg, 'unexpanded-macro', tagname, match)
+        if not isinstance(value, (list, tuple)):
+            value = [value]
+        for val in value:
+            for match in AbstractCheck.macro_regex.findall(val):
+                # Do not warn about %XX URL escapes
+                if is_url and re.match('^%[0-9A-F][0-9A-F]$', match, re.I):
+                    continue
+                printWarning(pkg, 'unexpanded-macro', tagname, match)
 
     def check(self, pkg):
 
-        packager = Pkg.b2s(pkg[rpm.RPMTAG_PACKAGER])
+        packager = pkg[rpm.RPMTAG_PACKAGER]
         if packager:
             self._unexpanded_macros(pkg, 'Packager', packager)
             if Config.getOption('Packager') and \
@@ -684,23 +687,22 @@ class TagsCheck(AbstractCheck.AbstractCheck):
         ignored_words.update((x[0] for x in pkg.obsoletes()))
 
         langs = pkg[rpm.RPMTAG_HEADERI18NTABLE]
-        if langs:
-            langs = [Pkg.b2s(x) for x in langs]
 
-        summary = Pkg.b2s(pkg[rpm.RPMTAG_SUMMARY])
+        summary = pkg[rpm.RPMTAG_SUMMARY]
         if summary:
             if not langs:
-                self._unexpanded_macros(pkg, 'Summary', summary)
+                self._unexpanded_macros(pkg, 'Summary', Pkg.b2s(summary))
             else:
                 for lang in langs:
                     self.check_summary(pkg, lang, ignored_words)
         else:
             printError(pkg, 'no-summary-tag')
 
-        description = Pkg.b2s(pkg[rpm.RPMTAG_DESCRIPTION])
+        description = pkg[rpm.RPMTAG_DESCRIPTION]
         if description:
             if not langs:
-                self._unexpanded_macros(pkg, '%description', description)
+                self._unexpanded_macros(pkg, '%description',
+                                        Pkg.b2s(description))
             else:
                 for lang in langs:
                     self.check_description(pkg, lang, ignored_words)
@@ -726,14 +728,13 @@ class TagsCheck(AbstractCheck.AbstractCheck):
         if not changelog:
             printError(pkg, 'no-changelogname-tag')
         else:
-            changelog = [Pkg.b2s(x) for x in changelog]
-            clt = [Pkg.b2s(x) for x in pkg[rpm.RPMTAG_CHANGELOGTEXT]]
+            clt = pkg[rpm.RPMTAG_CHANGELOGTEXT]
             if use_version_in_changelog:
-                ret = changelog_version_regex.search(changelog[0])
+                ret = changelog_version_regex.search(Pkg.b2s(changelog[0]))
                 if not ret and clt:
                     # we also allow the version specified as the first
                     # thing on the first line of the text
-                    ret = changelog_text_version_regex.search(clt[0])
+                    ret = changelog_text_version_regex.search(Pkg.b2s(clt[0]))
                 if not ret:
                     printWarning(pkg, 'no-version-in-last-changelog')
                 elif version and release:
@@ -754,10 +755,13 @@ class TagsCheck(AbstractCheck.AbstractCheck):
                             printWarning(pkg, 'incoherent-version-in-changelog',
                                          ret.group(1), expected)
 
-            if clt:
-                changelog = changelog + clt
-            if use_utf8 and not Pkg.is_utf8_str(' '.join(changelog)):
-                printError(pkg, 'tag-not-utf8', '%changelog')
+            if use_utf8:
+                if clt:
+                    changelog = changelog + clt
+                for s in changelog:
+                    if not Pkg.is_utf8_bytestr(s):
+                        printError(pkg, 'tag-not-utf8', '%changelog')
+                        break
 
             clt = pkg[rpm.RPMTAG_CHANGELOGTIME][0]
             if clt:
@@ -873,12 +877,16 @@ class TagsCheck(AbstractCheck.AbstractCheck):
 
     def check_description(self, pkg, lang, ignored_words):
         description = pkg.langtag(rpm.RPMTAG_DESCRIPTION, lang)
-        self._unexpanded_macros(pkg, '%%description -l %s' % lang, description)
-        utf8desc = description
         if use_utf8:
-            utf8desc = Pkg.to_utf8(description).decode('utf-8')
-        spell_check(pkg, utf8desc, '%%description -l %s', lang, ignored_words)
-        for l in utf8desc.splitlines():
+            if not Pkg.is_utf8_bytestr(description):
+                printError(pkg, 'tag-not-utf8', '%description', lang)
+            description = Pkg.to_unicode(description)
+        else:
+            description = Pkg.b2s(description)
+        self._unexpanded_macros(pkg, '%%description -l %s' % lang, description)
+        spell_check(pkg, description, '%%description -l %s', lang,
+                    ignored_words)
+        for l in description.splitlines():
             if len(l) > max_line_len:
                 printError(pkg, 'description-line-too-long', lang, l)
             res = forbidden_words_regex.search(l)
@@ -888,23 +896,24 @@ class TagsCheck(AbstractCheck.AbstractCheck):
             res = tag_regex.search(l)
             if res:
                 printWarning(pkg, 'tag-in-description', lang, res.group(1))
-        if use_utf8 and not Pkg.is_utf8_str(description):
-            printError(pkg, 'tag-not-utf8', '%description', lang)
 
     def check_summary(self, pkg, lang, ignored_words):
         summary = pkg.langtag(rpm.RPMTAG_SUMMARY, lang)
-        self._unexpanded_macros(pkg, 'Summary(%s)' % lang, summary)
-        utf8summary = summary
         if use_utf8:
-            utf8summary = Pkg.to_utf8(summary).decode('utf-8')
-        spell_check(pkg, utf8summary, 'Summary(%s)', lang, ignored_words)
+            if not Pkg.is_utf8_bytestr(summary):
+                printError(pkg, 'tag-not-utf8', 'Summary', lang)
+            summary = Pkg.to_unicode(summary)
+        else:
+            summary = Pkg.b2s(summary)
+        self._unexpanded_macros(pkg, 'Summary(%s)' % lang, summary)
+        spell_check(pkg, summary, 'Summary(%s)', lang, ignored_words)
         if '\n' in summary:
             printError(pkg, 'summary-on-multiple-lines', lang)
         if summary[0] != summary[0].upper():
             printWarning(pkg, 'summary-not-capitalized', lang, summary)
         if summary[-1] == '.':
             printWarning(pkg, 'summary-ended-with-dot', lang, summary)
-        if len(utf8summary) > max_line_len:
+        if len(summary) > max_line_len:
             printError(pkg, 'summary-too-long', lang, summary)
         if leading_space_regex.search(summary):
             printError(pkg, 'summary-has-leading-spaces', lang, summary)
@@ -919,8 +928,6 @@ class TagsCheck(AbstractCheck.AbstractCheck):
             if res:
                 printWarning(pkg, 'name-repeated-in-summary', lang,
                              res.group(1))
-        if use_utf8 and not Pkg.is_utf8_str(summary):
-            printError(pkg, 'tag-not-utf8', 'Summary', lang)
 
 
 # Create an object to enable the auto registration of the test
index 097fc45..8a7b975 100644 (file)
@@ -16,6 +16,7 @@ import zipfile
 from Filter import addDetails, printError, printWarning
 import AbstractCheck
 import Config
+import Pkg
 
 
 zip_regex = re.compile('\.(zip|[ewj]ar)$')
@@ -53,7 +54,7 @@ class ZipCheck(AbstractCheck.AbstractCheck):
                     # additional jar checks
                     if jar_regex.search(fname):
                         try:
-                            mf = z.read('META-INF/MANIFEST.MF')
+                            mf = Pkg.b2s(z.read('META-INF/MANIFEST.MF'))
                             if classpath_regex.search(mf):
                                 printWarning(pkg,
                                              'class-path-in-manifest', fname)
diff --git a/config b/config
index f66f7a3..f1319a3 100644 (file)
--- a/config
+++ b/config
@@ -212,6 +212,10 @@ from Config import *
 # Type: string, default: 'mac'
 #SetOption("ManWarningCategory", 'mac')
 
+# Command and arguments to validate appdata files.
+# Type: tuple of strings, default: see DEFAULT_APPDATA_CHECKER in AppDataCheck
+#setOption("AppDataChecker", ('appstream-util', 'validate-relax'))
+
 # Output filters.
 # ---------------
 
diff --git a/rpmlint b/rpmlint
index aa37c3a..acbda29 100755 (executable)
--- a/rpmlint
+++ b/rpmlint
@@ -108,8 +108,8 @@ def main():
                     stdin = sys.stdin.readlines()
                     if not stdin:
                         continue
-                    pkg = Pkg.FakePkg(arg)
-                    runSpecChecks(pkg, arg)
+                    with Pkg.FakePkg(arg) as pkg:
+                        runSpecChecks(pkg, arg)
                     specfiles_checked += 1
                     continue
 
@@ -119,8 +119,8 @@ def main():
                     if stat.S_ISREG(st[stat.ST_MODE]):
                         if arg.endswith(".spec"):
                             # Short-circuit spec file checks
-                            pkg = Pkg.FakePkg(arg)
-                            runSpecChecks(pkg, arg)
+                            with Pkg.FakePkg(arg) as pkg:
+                                runSpecChecks(pkg, arg)
                             specfiles_checked += 1
                         elif "/" in arg or arg.endswith(".rpm") or \
                                 arg.endswith(".spm"):
@@ -157,7 +157,8 @@ def main():
                 continue
 
             for pkg in pkgs:
-                runChecks(pkg)
+                with pkg:
+                    runChecks(pkg)
                 packages_checked += 1
 
         for dname in dirs:
@@ -168,13 +169,13 @@ def main():
                         try:
                             if fname.endswith('.rpm') or \
                                fname.endswith('.spm'):
-                                pkg = Pkg.Pkg(fname, extract_dir)
-                                runChecks(pkg)
+                                with Pkg.Pkg(fname, extract_dir) as pkg:
+                                    runChecks(pkg)
                                 packages_checked += 1
 
                             elif fname.endswith('.spec'):
-                                pkg = Pkg.FakePkg(fname)
-                                runSpecChecks(pkg, fname)
+                                with Pkg.FakePkg(fname) as pkg:
+                                    runSpecChecks(pkg, fname)
                                 specfiles_checked += 1
 
                         except KeyboardInterrupt:
@@ -209,36 +210,30 @@ def main():
 
 def runChecks(pkg):
 
-    try:
-        if verbose:
-            printInfo(pkg, 'checking')
-
-        for name in Config.allChecks():
-            check = AbstractCheck.AbstractCheck.known_checks.get(name)
-            if check:
-                check.verbose = verbose
-                check.check(pkg)
-            else:
-                Pkg.warn('(none): W: unknown check %s, skipping' % name)
-    finally:
-        pkg.cleanup()
+    if verbose:
+        printInfo(pkg, 'checking')
+
+    for name in Config.allChecks():
+        check = AbstractCheck.AbstractCheck.known_checks.get(name)
+        if check:
+            check.verbose = verbose
+            check.check(pkg)
+        else:
+            Pkg.warn('(none): W: unknown check %s, skipping' % name)
 
 
 def runSpecChecks(pkg, fname):
 
-    try:
-        if verbose:
-            printInfo(pkg, 'checking')
-
-        for name in Config.allChecks():
-            check = AbstractCheck.AbstractCheck.known_checks.get(name)
-            if check:
-                check.verbose = verbose
-                check.check_spec(pkg, fname)
-            else:
-                Pkg.warn('(none): W: unknown check %s, skipping' % name)
-    finally:
-        pkg.cleanup()
+    if verbose:
+        printInfo(pkg, 'checking')
+
+    for name in Config.allChecks():
+        check = AbstractCheck.AbstractCheck.known_checks.get(name)
+        if check:
+            check.verbose = verbose
+            check.check_spec(pkg, fname)
+        else:
+            Pkg.warn('(none): W: unknown check %s, skipping' % name)
 
 #############################################################################
 #
diff --git a/test.sh b/test.sh
index 1e071c5..1e18b97 100755 (executable)
--- a/test.sh
+++ b/test.sh
@@ -2,6 +2,7 @@
 
 export PYTHONPATH=$(pwd)/tools:$(pwd)
 export TESTPATH="$(pwd)/test/"
+: ${PYTHON:=python} ${PYTEST:=py.test}
 
 echo
 echo "Please ignore the possibly occurring output like this:"
@@ -9,7 +10,7 @@ echo "    .../Patch*.patch: No such file or directory"
 echo
 
 for i in $TESTPATH/test.*.py; do
-    python $i
+    $PYTHON $i
     RET=$?
     if [ $RET -ne 0 ]; then
         exit $RET
@@ -17,9 +18,9 @@ for i in $TESTPATH/test.*.py; do
 done
 
 echo "Check that rpmlint executes with no unexpected errors"
-python ./rpmlint -C $(pwd) test/*/*.rpm test/spec/*.spec >/dev/null
+$PYTHON ./rpmlint -C $(pwd) test/*/*.rpm test/spec/*.spec >/dev/null
 rc=$?
 test $rc -eq 0 -o $rc -eq 64
 
 # SCLCheck tests
-py.test -v
+$PYTEST -v