diff options
author | Kyle Keen <keenerd@gmail.com> | 2016-01-31 17:06:49 -0500 |
---|---|---|
committer | Kyle Keen <keenerd@gmail.com> | 2016-01-31 17:06:49 -0500 |
commit | 805ed84f6c476333973e91d9f8059434297ce28f (patch) | |
tree | 588d8e90dd9a16ba5ca80f8d0f15041862a57af0 | |
parent | 2546d2edb1c57b8554e70e6aedb84b1636d01542 (diff) | |
download | namcap-805ed84f6c476333973e91d9f8059434297ce28f.tar.gz namcap-805ed84f6c476333973e91d9f8059434297ce28f.zip |
Add py_mtime rule
Signed-off-by: Kyle Keen <keenerd@gmail.com>
-rw-r--r-- | Namcap/package.py | 28 | ||||
-rw-r--r-- | Namcap/rules/__init__.py | 1 | ||||
-rw-r--r-- | Namcap/rules/py_mtime.py | 129 | ||||
-rw-r--r-- | namcap-tags | 3 |
4 files changed, 161 insertions, 0 deletions
diff --git a/Namcap/package.py b/Namcap/package.py index cb5da94..308d0ce 100644 --- a/Namcap/package.py +++ b/Namcap/package.py @@ -24,6 +24,7 @@ import sys import subprocess import re import collections +import gzip import pyalpm _pyalpm_version_tuple = tuple(int(n) for n in pyalpm.version().split('.')) @@ -261,4 +262,31 @@ def lookup_provider(pkgname, db): if pkgname in pkg.provides: return pkg +def mtree_line(line): + "returns head, {key:value}" + # todo, un-hex the escaped chars + head,_,kvs = line.partition(' ') + kvs = dict(kv.split('=') for kv in kvs.split(' ')) + return head, kvs + +def load_mtree(tar): + "takes a tar object, returns (path, {attributes})" + if '.MTREE' not in tar.getnames(): + raise StopIteration + zfile = tar.extractfile('.MTREE') + text = gzip.open(zfile).read().decode("utf-8") + defaults = {} + for line in text.split('\n'): + if not line: + continue + if line.startswith('#'): + continue + head, kvs = mtree_line(line) + if head == '/set': + defaults = kvs + attr = {} + attr.update(defaults) + attr.update(kvs) + yield head, attr + # vim: set ts=4 sw=4 noet: diff --git a/Namcap/rules/__init__.py b/Namcap/rules/__init__.py index a1b6775..6c8c708 100644 --- a/Namcap/rules/__init__.py +++ b/Namcap/rules/__init__.py @@ -42,6 +42,7 @@ from . import ( missingbackups, perllocal, permissions, + py_mtime, rpath, scrollkeeper, shebangdepends, diff --git a/Namcap/rules/py_mtime.py b/Namcap/rules/py_mtime.py new file mode 100644 index 0000000..ee91b32 --- /dev/null +++ b/Namcap/rules/py_mtime.py @@ -0,0 +1,129 @@ +# +# namcap rules - py_mtime +# Copyright (C) 2013 Kyle Keen <keener@gmail.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +""" +Check for py timestamps that are ahead of pyc/pyo timestamps +""" + +import os +from Namcap.package import load_mtree +from Namcap.ruleclass import * + +def _quick_filter(names): + "can this package be skipped outright" + if not names: + return True + found_py = any(n.endswith('.py') for n in names) + found_pyc = any(n.endswith('.pyc') for n in names) + found_pyo = any(n.endswith('.pyo') for n in names) + if found_py and found_pyc: + return False + if found_py and found_pyo: + return False + return True + +def _tar_timestamps(tar): + "takes a tar object" + return dict((m.name, m.mtime) for m in tar.getmembers()) + +def _mtree_timestamps(tar): + "takes a tar object" + return dict((h, a['time']) for h,a in load_mtree(tar) if 'time' in a) + +def _generic_timestamps(tar): + "works for mtree and tar" + if '.MTREE' in tar.getnames(): + return _mtree_timestamps(tar) + return _tar_timestamps(tar) + +def _try_mtree(tar): + "returns True if good, False if bad, None if N/A" + if '.MTREE' not in tar.getnames(): + return None + stamps = _mtree_timestamps(tar) + if _quick_filter(stamps.keys()): + return True + return not _mtime_filter(stamps) + +def _try_tar(tar): + "returns True if good, False if bad" + names = tar.getnames() + if _quick_filter(names): + return True + mtimes = _tar_timestamps(tar) + return not _mtime_filter(mtimes) + +def _split_all(path): + "like os.path.split but splits every directory" + p2 = path + dirs = [] + while p2 and p2 != '/': + p2,p3 = os.path.split(p2) + dirs.insert(0, p3) + #dirs.insert(0, '/') + return dirs + +def _source_py(path): + "given a pyc/pyo, return the source path" + if not path.endswith('.pyc') and not path.endswith('.pyo'): + return None + path = path[:-1] + # handle py2 + if '__pycache__' not in path: + return path + # handle py3 + splitup = _split_all(path) + if splitup[-2] != '__pycache__': + return None + splitup.pop(-2) + f = splitup[-1] + f = f.split('.') + f.pop(-2) + splitup[-1] = '.'.join(f) + return os.path.join(*splitup) + +def _mtime_filter(mtimes): + "return list of bad py file names" + bad = [] + for name, mt2 in mtimes.items(): + if not name.endswith('.pyc') and not name.endswith('.pyo'): + continue + source_name = _source_py(name) + if source_name not in mtimes: + continue + mt1 = mtimes[source_name] + if mt1 > mt2: + bad.append(source_name) + return bad + +class package(TarballRule): + name = "py_mtime" + description = "Check for py timestamps that are ahead of pyc/pyo timestamps" + def analyze(self, pkginfo, tar): + mtree_status = _try_mtree(tar) + tar_status = _try_tar(tar) + if mtree_status == False and tar_status: + # mtree only + self.warning = [('py-mtime-mtree-warning', ())] + elif not tar_status: + # tar or both + self.errors = [('py-mtime-tar-error', ())] + self.infos = [('py-mtime-file-name %s', f[1:]) for f in _mtime_filter(_generic_timestamps(tar))] + +# vim: set ts=4 sw=4 noet: diff --git a/namcap-tags b/namcap-tags index e5656f4..a5c348c 100644 --- a/namcap-tags +++ b/namcap-tags @@ -67,6 +67,9 @@ perllocal-pod-present %s :: perllocal.pod found in %s. pkgname-in-description :: Description should not contain the package name. potential-non-fhs-info-page %s :: Potential non-FHS info page (%s) found. potential-non-fhs-man-page %s :: Potential non-FHS man page (%s) found. +py-mtime-mtree-warning :: Found .py file unoticeably newer than associated .pyc/pyo. +py-mtime-tar-error :: Found .py file newer than associated .pyc/pyo. +py-mtime-file-name %s :: Python script (%s) is newer than associated .pyc/pyo. script-link-detected %s in %s :: Script link detected (%s) in file %s scrollkeeper-dir-exists %s :: Scrollkeeper directory exists (%s). Remember to not run scrollkeeper till post_{install,upgrade,remove}. site-ruby :: Found usr/lib/ruby/site_ruby in package, usr/lib/ruby/vendor_ruby should be used instead. |