diff options
author | Helmut Grohne <helmut@subdivi.de> | 2014-07-22 08:56:42 +0200 |
---|---|---|
committer | Helmut Grohne <helmut@subdivi.de> | 2014-07-22 08:56:42 +0200 |
commit | 04597f25729740406775a3dff528c9774c84efd5 (patch) | |
tree | fe905fc94afbdcfad60d5aaf88886a1f10f92a8c /webapp.py | |
parent | ba9ae116e0bbb25e2df327ba48c82472ccfa2690 (diff) | |
parent | d48c3c208ee6ba54225b3eb68ce5c9f3c894bfa4 (diff) | |
download | debian-dedup-04597f25729740406775a3dff528c9774c84efd5.tar.gz |
Merge branch master into multiarch
Resolve accumulated conflicts. In particular webapp.py gained a few
non-trivial ones, such as changes in InternalRedirect or usage of
contextlib.closing.
Conflicts:
schema.sql
webapp.py
Diffstat (limited to 'webapp.py')
-rwxr-xr-x | webapp.py | 125 |
1 files changed, 70 insertions, 55 deletions
@@ -1,5 +1,6 @@ #!/usr/bin/python +import contextlib import datetime import optparse import sqlite3 @@ -7,7 +8,8 @@ from wsgiref.simple_server import make_server import jinja2 from werkzeug.exceptions import HTTPException, NotFound -from werkzeug.routing import Map, Rule, RequestRedirect +from werkzeug.routing import Map, Rule +from werkzeug.utils import redirect from werkzeug.wrappers import Request, Response from werkzeug.wsgi import SharedDataMiddleware @@ -61,9 +63,10 @@ def html_response(unicode_iterator, max_age=24 * 60 * 60): return resp class InternalRedirect(Exception): - def __init__(self, target): + def __init__(self, target, code=301): Exception.__init__(self) self.target = target + self.code = code class Application(object): def __init__(self, db): @@ -93,33 +96,33 @@ class Application(object): return self.show_hash(args["function"], args["hashvalue"]) elif endpoint == "index": if not request.environ["PATH_INFO"]: - raise RequestRedirect(request.environ["SCRIPT_NAME"] + "/") + raise InternalRedirect("/") return html_response(index_template.render(dict(urlroot=""))) elif endpoint == "source": return self.show_source(args["package"]) raise NotFound() except InternalRedirect as r: - return RequestRedirect(request.environ["SCRIPT_NAME"] + r.target) + return redirect(request.environ["SCRIPT_NAME"] + r.target, r.code) except HTTPException as e: return e def guess_package(self, package): - cur = self.db.cursor() - cur.execute("SELECT architecture, id FROM package WHERE name = ?;", - (package,)) - ret = dict(cur.fetchall()) + with contextlib.closing(self.db.cursor()) as cur: + cur.execute("SELECT architecture, id FROM package WHERE name = ?;", + (package,)) + ret = dict(cur.fetchall()) if not ret: raise NotFound() return ret def get_details(self, package, architecture): - cur = self.db.cursor() - cur.execute("SELECT package.id, package.version, package.architecture, count(content.filename), sum(content.size) FROM package JOIN content ON package.id = content.pid WHERE name = ? AND architecture = ? GROUP BY package.id;", + with contextlib.closing(self.db.cursor()) as cur: + cur.execute("SELECT package.id, package.version, count(content.filename), sum(content.size) FROM package JOIN content ON package.id = content.pid WHERE name = ? AND architecture = ? GROUP BY package.id;", (package, architecture)) - row = cur.fetchone() + row = cur.fetchone() if not row: raise NotFound() - pid, version, architecture, num_files, total_size = row + pid, version, num_files, total_size = row if total_size is None: total_size = 0 return dict(pid=pid, package=package, version=version, @@ -127,23 +130,25 @@ class Application(object): total_size=total_size) def get_dependencies(self, pid): - cur = self.db.cursor() - cur.execute("SELECT required FROM dependency WHERE pid = ?;", - (pid,)) - return set(row[0] for row in fetchiter(cur)) + with contextlib.closing(self.db.cursor()) as cur: + cur.execute("SELECT required FROM dependency WHERE pid = ?;", + (pid,)) + return set(row[0] for row in fetchiter(cur)) def cached_sharedstats(self, pid): - cur = self.db.cursor() sharedstats = {} - cur.execute("SELECT pid2, package.name, package.architecture, f1.name, f2.name, files, size FROM sharing JOIN package ON sharing.pid2 = package.id JOIN function AS f1 ON sharing.fid1 = f1.id JOIN function AS f2 ON sharing.fid2 = f2.id WHERE pid1 = ? AND f1.eqclass = f2.eqclass;", - (pid,)) - for pid2, package2, architecture2, func1, func2, files, size in fetchiter(cur): - curstats = sharedstats.setdefault( - function_combination(func1, func2), list()) - if pid2 == pid: - package2 = None - architecture2 = None - curstats.append(dict(package=package2, architecture=architecture2, duplicate=files, savable=size)) + with contextlib.closing(self.db.cursor()) as cur: + cur.execute("SELECT pid2, package.name, package.architecture, f1.name, f2.name, files, size FROM sharing JOIN package ON sharing.pid2 = package.id JOIN function AS f1 ON sharing.fid1 = f1.id JOIN function AS f2 ON sharing.fid2 = f2.id WHERE pid1 = ? AND f1.eqclass = f2.eqclass;", + (pid,)) + for pid2, package2, architecture2, func1, func2, files, size in fetchiter(cur): + curstats = sharedstats.setdefault( + function_combination(func1, func2), list()) + if pid2 == pid: + package2 = None + architecture2 = None + curstats.append(dict(package=package2, + architecture=architecture2, + duplicate=files, savable=size)) return sharedstats def show_package(self, package): @@ -151,7 +156,8 @@ class Application(object): package, architecture = package.split(':', 1) else: architecture = min(self.guess_package(package)) - raise InternalRedirect("/binary/%s:%s" % (package, architecture)) + raise InternalRedirect("/binary/%s:%s" % (package, architecture), + code=302) params = self.get_details(package, architecture) params["dependencies"] = self.get_dependencies(params["pid"]) params["shared"] = self.cached_sharedstats(params["pid"]) @@ -164,7 +170,7 @@ class Application(object): return html_response(package_template.render(params)) def compute_comparison(self, pid1, pid2): - """Compute a sequence of comparison objects ordery by the size of the + """Compute a sequence of comparison objects ordered by the size of the object in the first package. Each element of the sequence is a dict defining the following keys: * filenames: A set of filenames in package 1 (pid1) all referring to @@ -179,6 +185,7 @@ class Application(object): cursize = -1 files = dict() minmatch = 2 if pid1 == pid2 else 1 + cur2 = self.db.cursor() for cid, filename, size, hashvalue in fetchiter(cur): if cursize != size: for entry in files.values(): @@ -194,13 +201,12 @@ class Application(object): entry = dict(filenames=set((filename,)), size=size, matches={}) files[hashvalue] = entry - cur2 = self.db.cursor() - cur2.execute("SELECT fa.name, ha.hash, fb.name, filename FROM hash AS ha JOIN hash AS hb ON ha.hash = hb.hash JOIN content ON hb.cid = content.id JOIN function AS fa ON ha.fid = fa.id JOIN function AS fb ON hb.fid = fb.id WHERE ha.cid = ? AND pid = ?;", + cur2.execute("SELECT fa.name, ha.hash, fb.name, filename FROM hash AS ha JOIN hash AS hb ON ha.hash = hb.hash JOIN content ON hb.cid = content.id JOIN function AS fa ON ha.fid = fa.id JOIN function AS fb ON hb.fid = fb.id WHERE ha.cid = ? AND pid = ? AND fa.eqclass = fb.eqclass;", (cid, pid2)) for func1, hashvalue, func2, filename in fetchiter(cur2): entry["matches"].setdefault(filename, {})[func1, func2] = \ hashvalue - cur2.close() + cur2.close() cur.close() for entry in files.values(): @@ -222,7 +228,7 @@ class Application(object): if guessed: raise InternalRedirect("/compare/%s:%s/%s:%s" % (package1, architecture1, package2, - architecture2)) + architecture2), code=302) details1 = details2 = self.get_details(package1, architecture1) if package1 != package2 or architecture1 != architecture2: details2 = self.get_details(package2, architecture2) @@ -236,34 +242,43 @@ class Application(object): return html_response(detail_template.stream(params)) def show_hash(self, function, hashvalue): - cur = self.db.cursor() - cur.execute("SELECT package.name, package.architecture, content.filename, content.size, f2.name FROM hash JOIN content ON hash.cid = content.id JOIN package ON content.pid = package.id JOIN function AS f2 ON hash.fid = f2.id JOIN function AS f1 ON f2.eqclass = f1.eqclass WHERE f1.name = ? AND hash = ?;", - (function, hashvalue,)) - entries = [dict(package=package, architecture=architecture, - filename=filename, size=size, function=otherfunc) - for package, architecture, filename, size, otherfunc in fetchiter(cur)] - if not entries: - raise NotFound() + with contextlib.closing(self.db.cursor()) as cur: + cur.execute("SELECT package.name, package.architecture, content.filename, content.size, f2.name FROM hash JOIN content ON hash.cid = content.id JOIN package ON content.pid = package.id JOIN function AS f2 ON hash.fid = f2.id JOIN function AS f1 ON f2.eqclass = f1.eqclass WHERE f1.name = ? AND hash = ?;", + (function, hashvalue,)) + entries = [dict(package=package, architecture=architecture, + filename=filename, size=size, function=otherfunc) + for package, architecture, filename, size, otherfunc + in fetchiter(cur)] + if not entries: + # Assumption: '~' serves as an infinite character larger than + # any other character in the hash column. + cur.execute("SELECT DISTINCT hash.hash FROM hash JOIN function ON hash.fid = function.id WHERE function.name = ? AND hash.hash >= ? AND hash.hash <= ? LIMIT 2;", + (function, hashvalue, hashvalue + '~')) + values = cur.fetchall() + if len(values) == 1: + raise InternalRedirect("/hash/%s/%s" % + (function, values[0][0]), 302) + raise NotFound() params = dict(function=function, hashvalue=hashvalue, entries=entries, urlroot="../..") return html_response(hash_template.render(params)) def show_source(self, package): - cur = self.db.cursor() - cur.execute("SELECT name FROM package WHERE source = ?;", - (package,)) - binpkgs = dict.fromkeys(pkg for pkg, in fetchiter(cur)) - if not binpkgs: - raise NotFound - cur.execute("SELECT p1.name, p2.name, f1.name, f2.name, sharing.files, sharing.size FROM sharing JOIN package AS p1 ON sharing.pid1 = p1.id JOIN package AS p2 ON sharing.pid2 = p2.id JOIN function AS f1 ON sharing.fid1 = f1.id JOIN function AS f2 ON sharing.fid2 = f2.id WHERE p1.source = ?;", - (package,)) - for binary, otherbin, func1, func2, files, size in fetchiter(cur): - entry = dict(package=otherbin, - funccomb=function_combination(func1, func2), - duplicate=files, savable=size) - oldentry = binpkgs.get(binary) - if not (oldentry and oldentry["savable"] >= size): - binpkgs[binary] = entry + with contextlib.closing(self.db.cursor()) as cur: + cur.execute("SELECT name FROM package WHERE source = ?;", + (package,)) + binpkgs = dict.fromkeys(pkg for pkg, in fetchiter(cur)) + if not binpkgs: + raise NotFound + cur.execute("SELECT p1.name, p2.name, f1.name, f2.name, sharing.files, sharing.size FROM sharing JOIN package AS p1 ON sharing.pid1 = p1.id JOIN package AS p2 ON sharing.pid2 = p2.id JOIN function AS f1 ON sharing.fid1 = f1.id JOIN function AS f2 ON sharing.fid2 = f2.id WHERE p1.source = ?;", + (package,)) + for binary, otherbin, func1, func2, files, size in fetchiter(cur): + entry = dict(package=otherbin, + funccomb=function_combination(func1, func2), + duplicate=files, savable=size) + oldentry = binpkgs.get(binary) + if not (oldentry and oldentry["savable"] >= size): + binpkgs[binary] = entry params = dict(source=package, packages=binpkgs, urlroot="..") return html_response(source_template.render(params)) |