diff -Nru dupeguru-4.0.1~trusty/debian/changelog dupeguru-4.0.3~trusty/debian/changelog --- dupeguru-4.0.1~trusty/debian/changelog 2016-08-26 01:09:03.000000000 +0000 +++ dupeguru-4.0.3~trusty/debian/changelog 2016-11-27 01:48:51.000000000 +0000 @@ -1,3 +1,18 @@ +dupeguru (4.0.3~trusty) trusty; urgency=low + + * Add new picture cache backend: shelve + * Make shelve picture cache backend the active one on MacOS to fix #394 more elegantly. [cocoa] + * Remove Sparkle (auto-updates) due to technical limitations. [cocoa] + + -- Virgil Dupras Thu, 24 Nov 2016 00:00:00 +0000 + +dupeguru (4.0.2~trusty) trusty; urgency=low + + * Fix systematic crash in Picture Mode under MacOS Sierra. (#394) + * No change for Linux. Just keeping version in sync. + + -- Virgil Dupras Sun, 09 Oct 2016 00:00:00 +0000 + dupeguru (4.0.1~trusty) trusty; urgency=low * Add Greek localization, by Gabriel Koutilellis. (#382) diff -Nru dupeguru-4.0.1~trusty/src/core/app.py dupeguru-4.0.3~trusty/src/core/app.py --- dupeguru-4.0.1~trusty/src/core/app.py 2016-06-29 00:51:58.000000000 +0000 +++ dupeguru-4.0.3~trusty/src/core/app.py 2016-11-27 01:32:24.000000000 +0000 @@ -116,6 +116,8 @@ NAME = PROMPT_NAME = "dupeGuru" + PICTURE_CACHE_TYPE = 'sqlite' # set to 'shelve' for a ShelveCache + def __init__(self, view): if view.get_default(DEBUG_MODE_PREFERENCE): logging.getLogger().setLevel(logging.DEBUG) @@ -138,7 +140,7 @@ 'clean_empty_dirs': False, 'ignore_hardlink_matches': False, 'copymove_dest_type': DestType.Relative, - 'cache_path': op.join(self.appdata, 'cached_pictures.db'), + 'picture_cache_type': self.PICTURE_CACHE_TYPE } self.selected_dupes = [] self.details_panel = DetailsPanel(self) @@ -166,6 +168,11 @@ self.result_table.connect() self.view.create_results_window() + def _get_picture_cache_path(self): + cache_type = self.options['picture_cache_type'] + cache_name = 'cached_pictures.shelve' if cache_type == 'shelve' else 'cached_pictures.db' + return op.join(self.appdata, cache_name) + def _get_dupe_sort_key(self, dupe, get_group, key, delta): if self.app_mode in (AppMode.Music, AppMode.Picture): if key == 'folder_path': @@ -405,9 +412,10 @@ path = path.parent() def clear_picture_cache(self): - cache = pe.cache.Cache(self.options['cache_path']) - cache.clear() - cache.close() + try: + os.remove(self._get_picture_cache_path()) + except FileNotFoundError: + pass # we don't care def copy_or_move(self, dupe, copy: bool, destination: str, dest_type: DestType): source_path = dupe.path @@ -754,6 +762,8 @@ for k, v in self.options.items(): if hasattr(scanner, k): setattr(scanner, k, v) + if self.app_mode == AppMode.Picture: + scanner.cache_path = self._get_picture_cache_path() self.results.groups = [] self._recreate_result_table() self._results_changed() Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/gui/__pycache__/base.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/gui/__pycache__/base.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/gui/__pycache__/base.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/gui/__pycache__/base.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/gui/__pycache__/deletion_options.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/gui/__pycache__/deletion_options.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/gui/__pycache__/deletion_options.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/gui/__pycache__/deletion_options.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/gui/__pycache__/details_panel.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/gui/__pycache__/details_panel.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/gui/__pycache__/details_panel.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/gui/__pycache__/details_panel.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/gui/__pycache__/directory_tree.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/gui/__pycache__/directory_tree.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/gui/__pycache__/directory_tree.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/gui/__pycache__/directory_tree.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/gui/__pycache__/ignore_list_dialog.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/gui/__pycache__/ignore_list_dialog.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/gui/__pycache__/ignore_list_dialog.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/gui/__pycache__/ignore_list_dialog.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/gui/__pycache__/ignore_list_table.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/gui/__pycache__/ignore_list_table.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/gui/__pycache__/ignore_list_table.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/gui/__pycache__/ignore_list_table.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/gui/__pycache__/__init__.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/gui/__pycache__/__init__.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/gui/__pycache__/__init__.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/gui/__pycache__/__init__.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/gui/__pycache__/prioritize_dialog.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/gui/__pycache__/prioritize_dialog.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/gui/__pycache__/prioritize_dialog.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/gui/__pycache__/prioritize_dialog.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/gui/__pycache__/problem_dialog.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/gui/__pycache__/problem_dialog.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/gui/__pycache__/problem_dialog.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/gui/__pycache__/problem_dialog.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/gui/__pycache__/problem_table.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/gui/__pycache__/problem_table.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/gui/__pycache__/problem_table.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/gui/__pycache__/problem_table.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/gui/__pycache__/result_table.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/gui/__pycache__/result_table.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/gui/__pycache__/result_table.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/gui/__pycache__/result_table.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/gui/__pycache__/stats_label.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/gui/__pycache__/stats_label.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/gui/__pycache__/stats_label.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/gui/__pycache__/stats_label.cpython-35.pyc differ diff -Nru dupeguru-4.0.1~trusty/src/core/__init__.py dupeguru-4.0.3~trusty/src/core/__init__.py --- dupeguru-4.0.1~trusty/src/core/__init__.py 2016-08-26 01:04:45.000000000 +0000 +++ dupeguru-4.0.3~trusty/src/core/__init__.py 2016-11-27 01:32:24.000000000 +0000 @@ -1,3 +1,3 @@ -__version__ = '4.0.1' +__version__ = '4.0.3' __appname__ = 'dupeGuru' Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/me/__pycache__/fs.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/me/__pycache__/fs.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/me/__pycache__/fs.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/me/__pycache__/fs.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/me/__pycache__/__init__.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/me/__pycache__/__init__.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/me/__pycache__/__init__.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/me/__pycache__/__init__.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/me/__pycache__/prioritize.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/me/__pycache__/prioritize.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/me/__pycache__/prioritize.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/me/__pycache__/prioritize.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/me/__pycache__/result_table.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/me/__pycache__/result_table.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/me/__pycache__/result_table.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/me/__pycache__/result_table.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/me/__pycache__/scanner.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/me/__pycache__/scanner.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/me/__pycache__/scanner.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/me/__pycache__/scanner.cpython-35.pyc differ diff -Nru dupeguru-4.0.1~trusty/src/core/pe/cache.py dupeguru-4.0.3~trusty/src/core/pe/cache.py --- dupeguru-4.0.1~trusty/src/core/pe/cache.py 2016-06-29 00:51:58.000000000 +0000 +++ dupeguru-4.0.3~trusty/src/core/pe/cache.py 2016-11-27 01:32:24.000000000 +0000 @@ -1,17 +1,10 @@ -# Created By: Virgil Dupras -# Created On: 2006/09/14 -# Copyright 2015 Hardcoded Software (http://www.hardcoded.net) +# Copyright 2016 Virgil Dupras # # This software is licensed under the "GPLv3" License as described in the "LICENSE" file, # which should be included with this package. The terms are also available at # http://www.gnu.org/licenses/gpl-3.0.html -import os -import os.path as op -import logging -import sqlite3 as sqlite - -from ._cache import string_to_colors +from ._cache import string_to_colors # noqa def colors_to_string(colors): """Transform the 3 sized tuples 'colors' into a hex string. @@ -19,7 +12,7 @@ [(0,100,255)] --> 0064ff [(1,2,3),(4,5,6)] --> 010203040506 """ - return ''.join(['%02x%02x%02x' % (r, g, b) for r, g, b in colors]) + return ''.join('%02x%02x%02x' % (r, g, b) for r, g, b in colors) # This function is an important bottleneck of dupeGuru PE. It has been converted to C. # def string_to_colors(s): @@ -31,132 +24,3 @@ # result.append((number >> 16, (number >> 8) & 0xff, number & 0xff)) # return result -class Cache: - """A class to cache picture blocks. - """ - def __init__(self, db=':memory:'): - self.dbname = db - self.con = None - self._create_con() - - def __contains__(self, key): - sql = "select count(*) from pictures where path = ?" - result = self.con.execute(sql, [key]).fetchall() - return result[0][0] > 0 - - def __delitem__(self, key): - if key not in self: - raise KeyError(key) - sql = "delete from pictures where path = ?" - self.con.execute(sql, [key]) - - # Optimized - def __getitem__(self, key): - if isinstance(key, int): - sql = "select blocks from pictures where rowid = ?" - else: - sql = "select blocks from pictures where path = ?" - result = self.con.execute(sql, [key]).fetchone() - if result: - result = string_to_colors(result[0]) - return result - else: - raise KeyError(key) - - def __iter__(self): - sql = "select path from pictures" - result = self.con.execute(sql) - return (row[0] for row in result) - - def __len__(self): - sql = "select count(*) from pictures" - result = self.con.execute(sql).fetchall() - return result[0][0] - - def __setitem__(self, path_str, blocks): - blocks = colors_to_string(blocks) - if op.exists(path_str): - mtime = int(os.stat(path_str).st_mtime) - else: - mtime = 0 - if path_str in self: - sql = "update pictures set blocks = ?, mtime = ? where path = ?" - else: - sql = "insert into pictures(blocks,mtime,path) values(?,?,?)" - try: - self.con.execute(sql, [blocks, mtime, path_str]) - except sqlite.OperationalError: - logging.warning('Picture cache could not set value for key %r', path_str) - except sqlite.DatabaseError as e: - logging.warning('DatabaseError while setting value for key %r: %s', path_str, str(e)) - - def _create_con(self, second_try=False): - def create_tables(): - logging.debug("Creating picture cache tables.") - self.con.execute("drop table if exists pictures") - self.con.execute("drop index if exists idx_path") - self.con.execute("create table pictures(path TEXT, mtime INTEGER, blocks TEXT)") - self.con.execute("create index idx_path on pictures (path)") - - self.con = sqlite.connect(self.dbname, isolation_level=None) - try: - self.con.execute("select path, mtime, blocks from pictures where 1=2") - except sqlite.OperationalError: # new db - create_tables() - except sqlite.DatabaseError as e: # corrupted db - if second_try: - raise # Something really strange is happening - logging.warning('Could not create picture cache because of an error: %s', str(e)) - self.con.close() - os.remove(self.dbname) - self._create_con(second_try=True) - - def clear(self): - self.close() - if self.dbname != ':memory:': - os.remove(self.dbname) - self._create_con() - - def close(self): - if self.con is not None: - self.con.close() - self.con = None - - def filter(self, func): - to_delete = [key for key in self if not func(key)] - for key in to_delete: - del self[key] - - def get_id(self, path): - sql = "select rowid from pictures where path = ?" - result = self.con.execute(sql, [path]).fetchone() - if result: - return result[0] - else: - raise ValueError(path) - - def get_multiple(self, rowids): - sql = "select rowid, blocks from pictures where rowid in (%s)" % ','.join(map(str, rowids)) - cur = self.con.execute(sql) - return ((rowid, string_to_colors(blocks)) for rowid, blocks in cur) - - def purge_outdated(self): - """Go through the cache and purge outdated records. - - A record is outdated if the picture doesn't exist or if its mtime is greater than the one in - the db. - """ - todelete = [] - sql = "select rowid, path, mtime from pictures" - cur = self.con.execute(sql) - for rowid, path_str, mtime in cur: - if mtime and op.exists(path_str): - picture_mtime = os.stat(path_str).st_mtime - if int(picture_mtime) <= mtime: - # not outdated - continue - todelete.append(rowid) - if todelete: - sql = "delete from pictures where rowid in (%s)" % ','.join(map(str, todelete)) - self.con.execute(sql) - diff -Nru dupeguru-4.0.1~trusty/src/core/pe/cache_shelve.py dupeguru-4.0.3~trusty/src/core/pe/cache_shelve.py --- dupeguru-4.0.1~trusty/src/core/pe/cache_shelve.py 1970-01-01 00:00:00.000000000 +0000 +++ dupeguru-4.0.3~trusty/src/core/pe/cache_shelve.py 2016-11-27 01:32:24.000000000 +0000 @@ -0,0 +1,131 @@ +# Copyright 2016 Virgil Dupras +# +# This software is licensed under the "GPLv3" License as described in the "LICENSE" file, +# which should be included with this package. The terms are also available at +# http://www.gnu.org/licenses/gpl-3.0.html + +import os +import os.path as op +import shelve +import tempfile +from collections import namedtuple + +from .cache import string_to_colors, colors_to_string + +def wrap_path(path): + return 'path:{}'.format(path) + +def unwrap_path(key): + return key[5:] + +def wrap_id(path): + return 'id:{}'.format(path) + +def unwrap_id(key): + return int(key[3:]) + +CacheRow = namedtuple('CacheRow', 'id path blocks mtime') + +class ShelveCache: + """A class to cache picture blocks in a shelve backend. + """ + def __init__(self, db=None, readonly=False): + self.istmp = db is None + if self.istmp: + self.dtmp = tempfile.mkdtemp() + self.ftmp = db = op.join(self.dtmp, 'tmpdb') + flag = 'r' if readonly else 'c' + self.shelve = shelve.open(db, flag) + self.maxid = self._compute_maxid() + + def __contains__(self, key): + return wrap_path(key) in self.shelve + + def __delitem__(self, key): + row = self.shelve[wrap_path(key)] + del self.shelve[wrap_path(key)] + del self.shelve[wrap_id(row.id)] + + def __getitem__(self, key): + if isinstance(key, int): + skey = self.shelve[wrap_id(key)] + else: + skey = wrap_path(key) + return string_to_colors(self.shelve[skey].blocks) + + def __iter__(self): + return (unwrap_path(k) for k in self.shelve if k.startswith('path:')) + + def __len__(self): + return sum(1 for k in self.shelve if k.startswith('path:')) + + def __setitem__(self, path_str, blocks): + blocks = colors_to_string(blocks) + if op.exists(path_str): + mtime = int(os.stat(path_str).st_mtime) + else: + mtime = 0 + if path_str in self: + rowid = self.shelve[wrap_path(path_str)].id + else: + rowid = self._get_new_id() + row = CacheRow(rowid, path_str, blocks, mtime) + self.shelve[wrap_path(path_str)] = row + self.shelve[wrap_id(rowid)] = wrap_path(path_str) + + def _compute_maxid(self): + return max((unwrap_id(k) for k in self.shelve if k.startswith('id:')), default=1) + + def _get_new_id(self): + self.maxid += 1 + return self.maxid + + def clear(self): + self.shelve.clear() + + def close(self): + if self.shelve is not None: + self.shelve.close() + if self.istmp: + os.remove(self.ftmp) + os.rmdir(self.dtmp) + self.shelve = None + + def filter(self, func): + to_delete = [key for key in self if not func(key)] + for key in to_delete: + del self[key] + + def get_id(self, path): + if path in self: + return self.shelve[wrap_path(path)].id + else: + raise ValueError(path) + + def get_multiple(self, rowids): + for rowid in rowids: + try: + skey = self.shelve[wrap_id(rowid)] + except KeyError: + continue + yield (rowid, string_to_colors(self.shelve[skey].blocks)) + + def purge_outdated(self): + """Go through the cache and purge outdated records. + + A record is outdated if the picture doesn't exist or if its mtime is greater than the one in + the db. + """ + todelete = [] + for path in self: + row = self.shelve[wrap_path(path)] + if row.mtime and op.exists(path): + picture_mtime = os.stat(path).st_mtime + if int(picture_mtime) <= row.mtime: + # not outdated + continue + todelete.append(path) + for path in todelete: + del self[path] + + diff -Nru dupeguru-4.0.1~trusty/src/core/pe/cache_sqlite.py dupeguru-4.0.3~trusty/src/core/pe/cache_sqlite.py --- dupeguru-4.0.1~trusty/src/core/pe/cache_sqlite.py 1970-01-01 00:00:00.000000000 +0000 +++ dupeguru-4.0.3~trusty/src/core/pe/cache_sqlite.py 2016-11-27 01:32:24.000000000 +0000 @@ -0,0 +1,143 @@ +# Copyright 2016 Virgil Dupras +# +# This software is licensed under the "GPLv3" License as described in the "LICENSE" file, +# which should be included with this package. The terms are also available at +# http://www.gnu.org/licenses/gpl-3.0.html + +import os +import os.path as op +import logging +import sqlite3 as sqlite + +from .cache import string_to_colors, colors_to_string + +class SqliteCache: + """A class to cache picture blocks in a sqlite backend. + """ + def __init__(self, db=':memory:', readonly=False): + # readonly is not used in the sqlite version of the cache + self.dbname = db + self.con = None + self._create_con() + + def __contains__(self, key): + sql = "select count(*) from pictures where path = ?" + result = self.con.execute(sql, [key]).fetchall() + return result[0][0] > 0 + + def __delitem__(self, key): + if key not in self: + raise KeyError(key) + sql = "delete from pictures where path = ?" + self.con.execute(sql, [key]) + + # Optimized + def __getitem__(self, key): + if isinstance(key, int): + sql = "select blocks from pictures where rowid = ?" + else: + sql = "select blocks from pictures where path = ?" + result = self.con.execute(sql, [key]).fetchone() + if result: + result = string_to_colors(result[0]) + return result + else: + raise KeyError(key) + + def __iter__(self): + sql = "select path from pictures" + result = self.con.execute(sql) + return (row[0] for row in result) + + def __len__(self): + sql = "select count(*) from pictures" + result = self.con.execute(sql).fetchall() + return result[0][0] + + def __setitem__(self, path_str, blocks): + blocks = colors_to_string(blocks) + if op.exists(path_str): + mtime = int(os.stat(path_str).st_mtime) + else: + mtime = 0 + if path_str in self: + sql = "update pictures set blocks = ?, mtime = ? where path = ?" + else: + sql = "insert into pictures(blocks,mtime,path) values(?,?,?)" + try: + self.con.execute(sql, [blocks, mtime, path_str]) + except sqlite.OperationalError: + logging.warning('Picture cache could not set value for key %r', path_str) + except sqlite.DatabaseError as e: + logging.warning('DatabaseError while setting value for key %r: %s', path_str, str(e)) + + def _create_con(self, second_try=False): + def create_tables(): + logging.debug("Creating picture cache tables.") + self.con.execute("drop table if exists pictures") + self.con.execute("drop index if exists idx_path") + self.con.execute("create table pictures(path TEXT, mtime INTEGER, blocks TEXT)") + self.con.execute("create index idx_path on pictures (path)") + + self.con = sqlite.connect(self.dbname, isolation_level=None) + try: + self.con.execute("select path, mtime, blocks from pictures where 1=2") + except sqlite.OperationalError: # new db + create_tables() + except sqlite.DatabaseError as e: # corrupted db + if second_try: + raise # Something really strange is happening + logging.warning('Could not create picture cache because of an error: %s', str(e)) + self.con.close() + os.remove(self.dbname) + self._create_con(second_try=True) + + def clear(self): + self.close() + if self.dbname != ':memory:': + os.remove(self.dbname) + self._create_con() + + def close(self): + if self.con is not None: + self.con.close() + self.con = None + + def filter(self, func): + to_delete = [key for key in self if not func(key)] + for key in to_delete: + del self[key] + + def get_id(self, path): + sql = "select rowid from pictures where path = ?" + result = self.con.execute(sql, [path]).fetchone() + if result: + return result[0] + else: + raise ValueError(path) + + def get_multiple(self, rowids): + sql = "select rowid, blocks from pictures where rowid in (%s)" % ','.join(map(str, rowids)) + cur = self.con.execute(sql) + return ((rowid, string_to_colors(blocks)) for rowid, blocks in cur) + + def purge_outdated(self): + """Go through the cache and purge outdated records. + + A record is outdated if the picture doesn't exist or if its mtime is greater than the one in + the db. + """ + todelete = [] + sql = "select rowid, path, mtime from pictures" + cur = self.con.execute(sql) + for rowid, path_str, mtime in cur: + if mtime and op.exists(path_str): + picture_mtime = os.stat(path_str).st_mtime + if int(picture_mtime) <= mtime: + # not outdated + continue + todelete.append(rowid) + if todelete: + sql = "delete from pictures where rowid in (%s)" % ','.join(map(str, todelete)) + self.con.execute(sql) + diff -Nru dupeguru-4.0.1~trusty/src/core/pe/matchblock.py dupeguru-4.0.3~trusty/src/core/pe/matchblock.py --- dupeguru-4.0.1~trusty/src/core/pe/matchblock.py 2016-08-26 01:04:45.000000000 +0000 +++ dupeguru-4.0.3~trusty/src/core/pe/matchblock.py 2016-11-27 01:32:24.000000000 +0000 @@ -16,7 +16,6 @@ from core.engine import Match from .block import avgdiff, DifferentBlockCountError, NoBlocksError -from .cache import Cache # OPTIMIZATION NOTES: # The bottleneck of the matching phase is CPU, which is why we use multiprocessing. However, another @@ -49,12 +48,20 @@ logging.warning("Had problems to determine cpu count on launch.") RESULTS_QUEUE_LIMIT = 8 +def get_cache(cache_path, readonly=False): + if cache_path.endswith('shelve'): + from .cache_shelve import ShelveCache + return ShelveCache(cache_path, readonly=readonly) + else: + from .cache_sqlite import SqliteCache + return SqliteCache(cache_path, readonly=readonly) + def prepare_pictures(pictures, cache_path, with_dimensions, j=job.nulljob): # The MemoryError handlers in there use logging without first caring about whether or not # there is enough memory left to carry on the operation because it is assumed that the # MemoryError happens when trying to read an image file, which is freed from memory by the # time that MemoryError is raised. - cache = Cache(cache_path) + cache = get_cache(cache_path) cache.purge_outdated() prepared = [] # only pictures for which there was no error getting blocks try: @@ -109,7 +116,7 @@ # The list of ids in ref_ids have to be compared to the list of ids in other_ids. other_ids # can be None. In this case, ref_ids has to be compared with itself # picinfo is a dictionary {pic_id: (dimensions, is_ref)} - cache = Cache(dbname) + cache = get_cache(dbname, readonly=True) limit = 100 - threshold ref_pairs = list(cache.get_multiple(ref_ids)) if other_ids is not None: @@ -159,7 +166,7 @@ j = j.start_subjob([3, 7]) pictures = prepare_pictures(pictures, cache_path, with_dimensions=not match_scaled, j=j) j = j.start_subjob([9, 1], tr("Preparing for matching")) - cache = Cache(cache_path) + cache = get_cache(cache_path) id2picture = {} for picture in pictures: try: Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/pe/__pycache__/block.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/pe/__pycache__/block.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/pe/__pycache__/block.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/pe/__pycache__/block.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/pe/__pycache__/cache.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/pe/__pycache__/cache.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/pe/__pycache__/cache.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/pe/__pycache__/cache.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/pe/__pycache__/cache_shelve.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/pe/__pycache__/cache_shelve.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/pe/__pycache__/cache_sqlite.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/pe/__pycache__/cache_sqlite.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/pe/__pycache__/exif.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/pe/__pycache__/exif.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/pe/__pycache__/exif.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/pe/__pycache__/exif.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/pe/__pycache__/__init__.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/pe/__pycache__/__init__.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/pe/__pycache__/__init__.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/pe/__pycache__/__init__.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/pe/__pycache__/iphoto_plist.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/pe/__pycache__/iphoto_plist.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/pe/__pycache__/iphoto_plist.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/pe/__pycache__/iphoto_plist.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/pe/__pycache__/matchblock.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/pe/__pycache__/matchblock.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/pe/__pycache__/matchblock.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/pe/__pycache__/matchblock.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/pe/__pycache__/matchexif.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/pe/__pycache__/matchexif.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/pe/__pycache__/matchexif.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/pe/__pycache__/matchexif.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/pe/__pycache__/photo.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/pe/__pycache__/photo.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/pe/__pycache__/photo.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/pe/__pycache__/photo.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/pe/__pycache__/prioritize.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/pe/__pycache__/prioritize.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/pe/__pycache__/prioritize.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/pe/__pycache__/prioritize.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/pe/__pycache__/result_table.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/pe/__pycache__/result_table.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/pe/__pycache__/result_table.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/pe/__pycache__/result_table.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/pe/__pycache__/scanner.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/pe/__pycache__/scanner.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/pe/__pycache__/scanner.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/pe/__pycache__/scanner.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/__pycache__/app.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/__pycache__/app.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/__pycache__/app.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/__pycache__/app.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/__pycache__/directories.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/__pycache__/directories.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/__pycache__/directories.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/__pycache__/directories.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/__pycache__/engine.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/__pycache__/engine.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/__pycache__/engine.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/__pycache__/engine.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/__pycache__/export.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/__pycache__/export.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/__pycache__/export.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/__pycache__/export.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/__pycache__/fs.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/__pycache__/fs.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/__pycache__/fs.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/__pycache__/fs.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/__pycache__/ignore.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/__pycache__/ignore.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/__pycache__/ignore.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/__pycache__/ignore.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/__pycache__/__init__.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/__pycache__/__init__.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/__pycache__/__init__.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/__pycache__/__init__.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/__pycache__/markable.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/__pycache__/markable.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/__pycache__/markable.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/__pycache__/markable.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/__pycache__/prioritize.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/__pycache__/prioritize.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/__pycache__/prioritize.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/__pycache__/prioritize.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/__pycache__/results.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/__pycache__/results.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/__pycache__/results.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/__pycache__/results.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/__pycache__/scanner.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/__pycache__/scanner.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/__pycache__/scanner.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/__pycache__/scanner.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/__pycache__/util.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/__pycache__/util.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/__pycache__/util.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/__pycache__/util.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/se/__pycache__/fs.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/se/__pycache__/fs.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/se/__pycache__/fs.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/se/__pycache__/fs.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/se/__pycache__/__init__.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/se/__pycache__/__init__.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/se/__pycache__/__init__.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/se/__pycache__/__init__.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/se/__pycache__/result_table.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/se/__pycache__/result_table.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/se/__pycache__/result_table.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/se/__pycache__/result_table.cpython-35.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/se/__pycache__/scanner.cpython-34.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/se/__pycache__/scanner.cpython-34.pyc differ Binary files /tmp/tmpw3d7LR/7jBakahHB9/dupeguru-4.0.1~trusty/src/core/se/__pycache__/scanner.cpython-35.pyc and /tmp/tmpw3d7LR/cWyfu7BCX9/dupeguru-4.0.3~trusty/src/core/se/__pycache__/scanner.cpython-35.pyc differ diff -Nru dupeguru-4.0.1~trusty/src/help/.buildinfo dupeguru-4.0.3~trusty/src/help/.buildinfo --- dupeguru-4.0.1~trusty/src/help/.buildinfo 2016-08-26 01:08:52.000000000 +0000 +++ dupeguru-4.0.3~trusty/src/help/.buildinfo 2016-11-27 01:48:42.000000000 +0000 @@ -1,4 +1,4 @@ # Sphinx build info version 1 # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: a271aadfe8437f4500261d609d52e55d +config: ea58cd3d71203d83ebd7fd295c7a73cc tags: 645f666f9bcd5a90fca523b33c5a78b7 diff -Nru dupeguru-4.0.1~trusty/src/help/changelog.html dupeguru-4.0.3~trusty/src/help/changelog.html --- dupeguru-4.0.1~trusty/src/help/changelog.html 2016-08-26 01:08:45.000000000 +0000 +++ dupeguru-4.0.3~trusty/src/help/changelog.html 2016-11-27 01:48:41.000000000 +0000 @@ -6,7 +6,7 @@ - Changelog — dupeGuru 4.0.1 documentation + Changelog — dupeGuru 4.0.3 documentation @@ -14,7 +14,7 @@ - + + +