On 08/27/14 at 01:18pm, Kyle Keen wrote: > Signed-off-by: Kyle Keen <keen...@gmail.com> > --- > Namcap/rules/__init__.py | 1 + > Namcap/rules/py_mtime.py | 129 > +++++++++++++++++++++++++++++++++++++++++++++++ > Namcap/util.py | 28 ++++++++++ > namcap-tags | 3 ++ > 4 files changed, 161 insertions(+) > create mode 100644 Namcap/rules/py_mtime.py > > diff --git a/Namcap/rules/__init__.py b/Namcap/rules/__init__.py > index f7780d2..8dc4e68 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..aaff238 > --- /dev/null > +++ b/Namcap/rules/py_mtime.py > @@ -0,0 +1,129 @@ > +# > +# namcap rules - py_mtime > +# Copyright (C) 2013 Kyle Keen <keen...@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.util 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())
Since pyalpm is a Python 3 library, we can use dictionary comprehension here and in the other cases where you use dict(). > + > +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/util.py b/Namcap/util.py > index 21d7163..0613202 100644 > --- a/Namcap/util.py > +++ b/Namcap/util.py > @@ -20,6 +20,7 @@ > import os > import re > import stat > +import gzip > > def _read_carefully(path, readcall): > if not os.path.isfile(path): > @@ -77,4 +78,31 @@ def script_type(path): > > clean_filename = lambda s: re.search(r"/tmp/namcap\.[0-9]*/(.*)", s).group(1) > > +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-tags b/namcap-tags > index d638478..8b67330 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 newer (sub-second) 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. > -- > 2.0.1 Apart from the one comment, the code looks fine to me. -- Jelle van der Waa
signature.asc
Description: Digital signature