On 08/27/14 at 01:18pm, Kyle Keen wrote: > Signed-off-by: Kyle Keen <[email protected]> > --- > 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 <[email protected]> > +# > +# 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
