0.2.0: --verbose and --quiet command-line args, removing missing entries
This commit is contained in:
parent
6d56beaacc
commit
de52daceaa
10
README.rst
10
README.rst
@ -21,6 +21,9 @@ files with a changed modification date. Most importantly however, it will
|
|||||||
report all errors, e.g. files that changed on the hard drive but still have the
|
report all errors, e.g. files that changed on the hard drive but still have the
|
||||||
same modification date.
|
same modification date.
|
||||||
|
|
||||||
|
All paths stored in ``.bitrot.db`` are relative so it's safe to rescan a folder
|
||||||
|
after moving it to another drive.
|
||||||
|
|
||||||
Performance
|
Performance
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
@ -33,6 +36,13 @@ under 10 minutes. Both tests on HFS+.
|
|||||||
Change Log
|
Change Log
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
0.2.0
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
* ``--verbose`` and ``--quiet`` command-line arguments
|
||||||
|
|
||||||
|
* if a file is no longer there, its entry is removed from the database
|
||||||
|
|
||||||
0.1.0
|
0.1.0
|
||||||
~~~~~
|
~~~~~
|
||||||
|
|
||||||
|
@ -26,5 +26,5 @@ from __future__ import division
|
|||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from bitrot import run
|
from bitrot import run_from_command_line
|
||||||
run()
|
run_from_command_line()
|
||||||
|
9
setup.py
9
setup.py
@ -28,7 +28,8 @@ from setuptools import setup, find_packages
|
|||||||
reload(sys)
|
reload(sys)
|
||||||
sys.setdefaultencoding('utf8')
|
sys.setdefaultencoding('utf8')
|
||||||
|
|
||||||
ld_file = open(os.path.join(os.path.dirname(__file__), 'README.rst'))
|
current_dir = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
ld_file = open(os.path.join(current_dir, 'README.rst'))
|
||||||
try:
|
try:
|
||||||
long_description = ld_file.read()
|
long_description = ld_file.read()
|
||||||
finally:
|
finally:
|
||||||
@ -36,9 +37,13 @@ finally:
|
|||||||
# We let it die a horrible tracebacking death if reading the file fails.
|
# We let it die a horrible tracebacking death if reading the file fails.
|
||||||
# We couldn't sensibly recover anyway: we need the long description.
|
# We couldn't sensibly recover anyway: we need the long description.
|
||||||
|
|
||||||
|
sys.path.insert(0, current_dir + os.sep + 'src')
|
||||||
|
from bitrot import VERSION
|
||||||
|
release = ".".join(str(num) for num in VERSION)
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name = 'bitrot',
|
name = 'bitrot',
|
||||||
version = '0.1.0',
|
version = release,
|
||||||
author = 'Łukasz Langa',
|
author = 'Łukasz Langa',
|
||||||
author_email = 'lukasz@langa.pl',
|
author_email = 'lukasz@langa.pl',
|
||||||
description = ("Detects bit rotten files on the hard drive to save your "
|
description = ("Detects bit rotten files on the hard drive to save your "
|
||||||
|
@ -26,6 +26,7 @@ from __future__ import division
|
|||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import argparse
|
||||||
import atexit
|
import atexit
|
||||||
import datetime
|
import datetime
|
||||||
import hashlib
|
import hashlib
|
||||||
@ -36,7 +37,7 @@ import sys
|
|||||||
|
|
||||||
CHUNK_SIZE = 16384
|
CHUNK_SIZE = 16384
|
||||||
DOT_THRESHOLD = 200
|
DOT_THRESHOLD = 200
|
||||||
VERSION = (0, 1, 0)
|
VERSION = (0, 2, 0)
|
||||||
|
|
||||||
|
|
||||||
def sha1(path):
|
def sha1(path):
|
||||||
@ -62,21 +63,27 @@ def get_sqlite3_cursor(path):
|
|||||||
return conn
|
return conn
|
||||||
|
|
||||||
|
|
||||||
def run():
|
def run(verbosity=1):
|
||||||
current_dir = b'.' # sic, relative path
|
current_dir = b'.' # sic, relative path
|
||||||
bitrot_db = os.path.join(current_dir, b'.bitrot.db')
|
bitrot_db = os.path.join(current_dir, b'.bitrot.db')
|
||||||
conn = get_sqlite3_cursor(bitrot_db)
|
conn = get_sqlite3_cursor(bitrot_db)
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
new_count = 0
|
new_paths = []
|
||||||
update_count = 0
|
updated_paths = []
|
||||||
error_count = 0
|
error_count = 0
|
||||||
dot_count = 0
|
dot_count = 0
|
||||||
|
missing_paths = set()
|
||||||
|
cur.execute('SELECT path FROM bitrot')
|
||||||
|
row = cur.fetchone()
|
||||||
|
while row:
|
||||||
|
missing_paths.add(row[0])
|
||||||
|
row = cur.fetchone()
|
||||||
for path, _, files in os.walk(current_dir):
|
for path, _, files in os.walk(current_dir):
|
||||||
for f in files:
|
for f in files:
|
||||||
dot_count = (dot_count + 1) % DOT_THRESHOLD
|
if verbosity and not dot_count:
|
||||||
if not dot_count:
|
|
||||||
sys.stdout.write('.')
|
sys.stdout.write('.')
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
dot_count = (dot_count + 1) % DOT_THRESHOLD
|
||||||
p = os.path.join(path, f)
|
p = os.path.join(path, f)
|
||||||
if p == bitrot_db:
|
if p == bitrot_db:
|
||||||
continue
|
continue
|
||||||
@ -86,18 +93,19 @@ def run():
|
|||||||
"%Y-%m-%d %H:%M:%S%z"
|
"%Y-%m-%d %H:%M:%S%z"
|
||||||
)
|
)
|
||||||
p_uni = p.decode('utf8')
|
p_uni = p.decode('utf8')
|
||||||
|
missing_paths.remove(p_uni)
|
||||||
cur.execute('SELECT mtime, hash, timestamp FROM bitrot WHERE '
|
cur.execute('SELECT mtime, hash, timestamp FROM bitrot WHERE '
|
||||||
'path=?', (p_uni,))
|
'path=?', (p_uni,))
|
||||||
row = cur.fetchone()
|
row = cur.fetchone()
|
||||||
if not row:
|
if not row:
|
||||||
new_count += 1
|
new_paths.append(p)
|
||||||
cur.execute('INSERT INTO bitrot VALUES (?, ?, ?, ?)',
|
cur.execute('INSERT INTO bitrot VALUES (?, ?, ?, ?)',
|
||||||
(p_uni, new_mtime, new_sha1, update_ts))
|
(p_uni, new_mtime, new_sha1, update_ts))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
continue
|
continue
|
||||||
stored_mtime, stored_sha1, update_ts = row
|
stored_mtime, stored_sha1, update_ts = row
|
||||||
if int(stored_mtime) != new_mtime:
|
if int(stored_mtime) != new_mtime:
|
||||||
update_count += 1
|
updated_paths.append(p)
|
||||||
cur.execute('UPDATE bitrot SET mtime=?, hash=?, timestamp=? '
|
cur.execute('UPDATE bitrot SET mtime=?, hash=?, timestamp=? '
|
||||||
'WHERE path=?',
|
'WHERE path=?',
|
||||||
(new_mtime, new_sha1, update_ts, p_uni))
|
(new_mtime, new_sha1, update_ts, p_uni))
|
||||||
@ -110,15 +118,56 @@ def run():
|
|||||||
),
|
),
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
|
for path in missing_paths:
|
||||||
|
cur.execute('DELETE FROM bitrot WHERE path=?', (path,))
|
||||||
|
conn.commit()
|
||||||
cur.execute('SELECT COUNT(path) FROM bitrot')
|
cur.execute('SELECT COUNT(path) FROM bitrot')
|
||||||
all_count = cur.fetchone()[0]
|
all_count = cur.fetchone()[0]
|
||||||
print("\nFinished. {} errors found.".format(error_count))
|
if verbosity:
|
||||||
print("{} entries in the database, {} new, {} updated.".format(
|
print("\rFinished. {} errors found.".format(error_count))
|
||||||
all_count, new_count, update_count
|
if verbosity == 1:
|
||||||
))
|
print("{} entries in the database, {} new, {} updated, {} missing."
|
||||||
|
"".format(all_count, len(new_paths), len(updated_paths),
|
||||||
|
len(missing_paths)))
|
||||||
|
elif verbosity > 1:
|
||||||
|
print("{} entries in the database.".format(all_count), end=' ')
|
||||||
|
if new_paths:
|
||||||
|
print("{} entries new:".format(len(new_paths)))
|
||||||
|
new_paths.sort()
|
||||||
|
for path in new_paths:
|
||||||
|
print(" ", path)
|
||||||
|
if updated_paths:
|
||||||
|
print("{} entries updated:".format(len(updated_paths)))
|
||||||
|
updated_paths.sort()
|
||||||
|
for path in updated_paths:
|
||||||
|
print(" ", path)
|
||||||
|
if missing_paths:
|
||||||
|
print("{} entries missing:".format(len(missing_paths)))
|
||||||
|
missing_paths = sorted(missing_paths)
|
||||||
|
for path in missing_paths:
|
||||||
|
print(" ", path)
|
||||||
|
if not any((new_paths, updated_paths, missing_paths)):
|
||||||
|
print()
|
||||||
if error_count:
|
if error_count:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def run_from_command_line():
|
||||||
|
parser = argparse.ArgumentParser(prog='bitrot')
|
||||||
|
parser.add_argument('-q', '--quiet', action='store_true',
|
||||||
|
help='don\'t print anything besides checksum errors')
|
||||||
|
parser.add_argument('-v', '--verbose', action='store_true',
|
||||||
|
help='list new, updated and missing entries')
|
||||||
|
parser.add_argument('--version', action='version',
|
||||||
|
version='%(prog)s {}.{}.{}'.format(*VERSION))
|
||||||
|
args = parser.parse_args()
|
||||||
|
verbosity = 1
|
||||||
|
if args.quiet:
|
||||||
|
verbosity = 0
|
||||||
|
elif args.verbose:
|
||||||
|
verbosity = 2
|
||||||
|
run(verbosity=verbosity)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
run()
|
run_from_command_line()
|
||||||
|
Reference in New Issue
Block a user