version based on SQLite 3

This commit is contained in:
Łukasz Langa 2013-01-17 15:59:29 +01:00
parent b244bdd4d9
commit 6d56beaacc
2 changed files with 49 additions and 27 deletions

View File

@ -13,8 +13,8 @@ Go to the desired directory and simply invoke::
$ bitrot $ bitrot
This will start digging through your directory structure recursively indexing This will start digging through your directory structure recursively indexing
all files found. The index is stored in a ``.bitrot.db`` file which is a DBM all files found. The index is stored in a ``.bitrot.db`` file which is a SQLite
database. 3 database.
Next time you run ``bitrot`` it will add new files and update the index for Next time you run ``bitrot`` it will add new files and update the index for
files with a changed modification date. Most importantly however, it will files with a changed modification date. Most importantly however, it will

View File

@ -28,9 +28,9 @@ from __future__ import unicode_literals
import atexit import atexit
import datetime import datetime
import dbm
import hashlib import hashlib
import os import os
import sqlite3
import sys import sys
@ -38,6 +38,7 @@ CHUNK_SIZE = 16384
DOT_THRESHOLD = 200 DOT_THRESHOLD = 200
VERSION = (0, 1, 0) VERSION = (0, 1, 0)
def sha1(path): def sha1(path):
digest = hashlib.sha1() digest = hashlib.sha1()
with open(path) as f: with open(path) as f:
@ -48,12 +49,24 @@ def sha1(path):
return digest.hexdigest() return digest.hexdigest()
def get_sqlite3_cursor(path):
conn = sqlite3.connect(path)
atexit.register(conn.close)
cur = conn.cursor()
for name, in cur.execute('SELECT name FROM sqlite_master'):
if name == 'bitrot':
break
else:
cur.execute('CREATE TABLE bitrot (path TEXT PRIMARY KEY, '
'mtime INTEGER, hash TEXT, timestamp TEXT)')
return conn
def run(): def run():
current_dir = b'.' # sic, relative path current_dir = b'.' # sic, relative path
bitrot_db = os.path.join(current_dir, b'.bitrot') bitrot_db = os.path.join(current_dir, b'.bitrot.db')
db = dbm.open(bitrot_db, 'c') conn = get_sqlite3_cursor(bitrot_db)
bitrot_db += b'.db' cur = conn.cursor()
atexit.register(db.close)
new_count = 0 new_count = 0
update_count = 0 update_count = 0
error_count = 0 error_count = 0
@ -69,30 +82,39 @@ def run():
continue continue
new_mtime = int(os.stat(p).st_mtime) new_mtime = int(os.stat(p).st_mtime)
new_sha1 = sha1(p) new_sha1 = sha1(p)
try: update_ts = datetime.datetime.utcnow().strftime(
stored_mtime, stored_sha1, update_ts = db[p].split(b' ') "%Y-%m-%d %H:%M:%S%z"
if int(stored_mtime) != new_mtime: )
new_count -= 1 p_uni = p.decode('utf8')
update_count += 1 cur.execute('SELECT mtime, hash, timestamp FROM bitrot WHERE '
raise KeyError("out of date") 'path=?', (p_uni,))
except (KeyError, ValueError): row = cur.fetchone()
if not row:
new_count += 1 new_count += 1
update_ts = datetime.datetime.utcnow().strftime( cur.execute('INSERT INTO bitrot VALUES (?, ?, ?, ?)',
"%Y-%m-%d\u00a0%H:%M:%S%z".encode('utf8') (p_uni, new_mtime, new_sha1, update_ts))
conn.commit()
continue
stored_mtime, stored_sha1, update_ts = row
if int(stored_mtime) != new_mtime:
update_count += 1
cur.execute('UPDATE bitrot SET mtime=?, hash=?, timestamp=? '
'WHERE path=?',
(new_mtime, new_sha1, update_ts, p_uni))
conn.commit()
elif stored_sha1 != new_sha1:
error_count += 1
print("\rerror: SHA1 mismatch for {}: expected {}, got {}."
" Original info from {}.".format(
p, stored_sha1, new_sha1, update_ts
),
file=sys.stderr,
) )
db[p] = b'{} {} {}'.format(new_mtime, new_sha1, update_ts) cur.execute('SELECT COUNT(path) FROM bitrot')
else: all_count = cur.fetchone()[0]
if stored_sha1 != new_sha1:
error_count += 1
print("\rerror: SHA1 mismatch for {}: expected {}, got {}."
" Original info from {}.".format(
p, stored_sha1, new_sha1, update_ts
),
file=sys.stderr,
)
print("\nFinished. {} errors found.".format(error_count)) print("\nFinished. {} errors found.".format(error_count))
print("{} entries in the database, {} new, {} updated.".format( print("{} entries in the database, {} new, {} updated.".format(
len(db), new_count, update_count all_count, new_count, update_count
)) ))
if error_count: if error_count:
sys.exit(1) sys.exit(1)