Add decorators.@transaction to rollback sql upon exception

master
voussoir 2017-05-01 20:23:58 -07:00
parent c71a45191e
commit af6785cead
3 changed files with 53 additions and 0 deletions

View File

@ -55,3 +55,16 @@ def time_me(function):
print('%s: %0.8f' % (function.__name__, end-start)) print('%s: %0.8f' % (function.__name__, end-start))
return result return result
return timed_function return timed_function
def transaction(method):
@functools.wraps(method)
def wrapped(self, *args, **kwargs):
try:
ret = method(self, *args, **kwargs)
return ret
except Exception as e:
self.log.debug('Rolling back')
print(e)
self.sql.rollback()
raise
return wrapped

View File

@ -17,6 +17,14 @@ class ObjectBase:
super().__init__() super().__init__()
self.photodb = photodb self.photodb = photodb
@property
def log(self):
return self.photodb.log
@property
def sql(self):
return self.photodb.sql
def __eq__(self, other): def __eq__(self, other):
return ( return (
isinstance(other, type(self)) and isinstance(other, type(self)) and
@ -39,6 +47,7 @@ class GroupableMixin:
group_sql_index = None group_sql_index = None
group_table = None group_table = None
@decorators.transaction
def add(self, member, *, commit=True): def add(self, member, *, commit=True):
''' '''
Add a child object to this group. Add a child object to this group.
@ -99,6 +108,7 @@ class GroupableMixin:
results.sort(key=lambda x: x.id) results.sort(key=lambda x: x.id)
return results return results
@decorators.transaction
def delete(self, *, delete_children=False, commit=True): def delete(self, *, delete_children=False, commit=True):
''' '''
Delete this object's relationships to other groupables. Delete this object's relationships to other groupables.
@ -160,6 +170,7 @@ class GroupableMixin:
parentid = fetch[self.group_sql_index['parentid']] parentid = fetch[self.group_sql_index['parentid']]
return self.group_getter(id=parentid) return self.group_getter(id=parentid)
@decorators.transaction
def join_group(self, group, *, commit=True): def join_group(self, group, *, commit=True):
''' '''
Leave the current group, then call `group.add(self)`. Leave the current group, then call `group.add(self)`.
@ -175,6 +186,7 @@ class GroupableMixin:
self.leave_group(commit=commit) self.leave_group(commit=commit)
group.add(self, commit=commit) group.add(self, commit=commit)
@decorators.transaction
def leave_group(self, *, commit=True): def leave_group(self, *, commit=True):
''' '''
Leave the current group and become independent. Leave the current group and become independent.
@ -228,6 +240,8 @@ class Album(ObjectBase, GroupableMixin):
self._sum_bytes_photos = None self._sum_bytes_photos = None
self._sum_bytes_albums = None self._sum_bytes_albums = None
@decorators.transaction
@decorators.transaction
def add_photo(self, photo, *, commit=True): def add_photo(self, photo, *, commit=True):
if self.photodb != photo.photodb: if self.photodb != photo.photodb:
raise ValueError('Not the same PhotoDB') raise ValueError('Not the same PhotoDB')
@ -241,6 +255,7 @@ class Album(ObjectBase, GroupableMixin):
self.photodb.log.debug('Committing - add photo to album') self.photodb.log.debug('Committing - add photo to album')
self.photodb.commit() self.photodb.commit()
@decorators.transaction
def add_tag_to_all(self, tag, *, nested_children=True, commit=True): def add_tag_to_all(self, tag, *, nested_children=True, commit=True):
''' '''
Add this tag to every photo in the album. Saves you from having to Add this tag to every photo in the album. Saves you from having to
@ -272,6 +287,7 @@ class Album(ObjectBase, GroupableMixin):
directories = [pathclass.Path(x) for x in directories] directories = [pathclass.Path(x) for x in directories]
return directories return directories
@decorators.transaction
def delete(self, *, delete_children=False, commit=True): def delete(self, *, delete_children=False, commit=True):
self.photodb.log.debug('Deleting album {album:r}'.format(album=self)) self.photodb.log.debug('Deleting album {album:r}'.format(album=self))
GroupableMixin.delete(self, delete_children=delete_children, commit=False) GroupableMixin.delete(self, delete_children=delete_children, commit=False)
@ -291,6 +307,7 @@ class Album(ObjectBase, GroupableMixin):
else: else:
return self.id return self.id
@decorators.transaction
def edit(self, title=None, description=None, *, commit=True): def edit(self, title=None, description=None, *, commit=True):
''' '''
Change the title or description. Leave None to keep current value. Change the title or description. Leave None to keep current value.
@ -335,6 +352,7 @@ class Album(ObjectBase, GroupableMixin):
photos.sort(key=lambda x: x.basename.lower()) photos.sort(key=lambda x: x.basename.lower())
return photos return photos
@decorators.transaction
def remove_photo(self, photo, *, commit=True): def remove_photo(self, photo, *, commit=True):
if not self.has_photo(photo): if not self.has_photo(photo):
return return
@ -386,12 +404,14 @@ class Bookmark(ObjectBase):
def __repr__(self): def __repr__(self):
return 'Bookmark:{id}'.format(id=self.id) return 'Bookmark:{id}'.format(id=self.id)
@decorators.transaction
def delete(self, *, commit=True): def delete(self, *, commit=True):
cur = self.photodb.sql.cursor() cur = self.photodb.sql.cursor()
cur.execute('DELETE FROM bookmarks WHERE id == ?', [self.id]) cur.execute('DELETE FROM bookmarks WHERE id == ?', [self.id])
if commit: if commit:
self.photodb.commit() self.photodb.commit()
@decorators.transaction
def edit(self, title=None, url=None, *, commit=True): def edit(self, title=None, url=None, *, commit=True):
if title is None and url is None: if title is None and url is None:
return return
@ -468,6 +488,7 @@ class Photo(ObjectBase):
def _uncache(self): def _uncache(self):
self.photodb.caches['photo'].remove(self.id) self.photodb.caches['photo'].remove(self.id)
@decorators.transaction
def add_tag(self, tag, *, commit=True): def add_tag(self, tag, *, commit=True):
if not self.photodb.config['enable_photo_add_remove_tag']: if not self.photodb.config['enable_photo_add_remove_tag']:
raise exceptions.FeatureDisabled('photo.add_tag, photo.remove_tag') raise exceptions.FeatureDisabled('photo.add_tag, photo.remove_tag')
@ -526,6 +547,7 @@ class Photo(ObjectBase):
for tag in other_photo.tags(): for tag in other_photo.tags():
self.add_tag(tag) self.add_tag(tag)
@decorators.transaction
def delete(self, *, delete_file=False, commit=True): def delete(self, *, delete_file=False, commit=True):
''' '''
Delete the Photo and its relation to any tags and albums. Delete the Photo and its relation to any tags and albums.
@ -555,6 +577,7 @@ class Photo(ObjectBase):
return helpers.seconds_to_hms(self.duration) return helpers.seconds_to_hms(self.duration)
#@decorators.time_me #@decorators.time_me
@decorators.transaction
def generate_thumbnail(self, *, commit=True, **special): def generate_thumbnail(self, *, commit=True, **special):
''' '''
special: special:
@ -691,6 +714,7 @@ class Photo(ObjectBase):
return hopeful_filepath return hopeful_filepath
#@decorators.time_me #@decorators.time_me
@decorators.transaction
def reload_metadata(self, *, commit=True): def reload_metadata(self, *, commit=True):
''' '''
Load the file's height, width, etc as appropriate for this type of file. Load the file's height, width, etc as appropriate for this type of file.
@ -749,6 +773,7 @@ class Photo(ObjectBase):
self.photodb.log.debug('Committing - reload metadata') self.photodb.log.debug('Committing - reload metadata')
self.photodb.commit() self.photodb.commit()
@decorators.transaction
def relocate(self, new_filepath, *, allow_duplicates=False, commit=True): def relocate(self, new_filepath, *, allow_duplicates=False, commit=True):
''' '''
Point the Photo object to a different filepath. Point the Photo object to a different filepath.
@ -781,6 +806,7 @@ class Photo(ObjectBase):
self.photodb.log.debug('Commit - relocate photo') self.photodb.log.debug('Commit - relocate photo')
self.photodb.commit() self.photodb.commit()
@decorators.transaction
def remove_tag(self, tag, *, commit=True): def remove_tag(self, tag, *, commit=True):
if not self.photodb.config['enable_photo_add_remove_tag']: if not self.photodb.config['enable_photo_add_remove_tag']:
raise exceptions.FeatureDisabled('photo.add_tag, photo.remove_tag') raise exceptions.FeatureDisabled('photo.add_tag, photo.remove_tag')
@ -802,6 +828,7 @@ class Photo(ObjectBase):
self.photodb.log.debug('Committing - remove photo tag') self.photodb.log.debug('Committing - remove photo tag')
self.photodb.commit() self.photodb.commit()
@decorators.transaction
def rename_file(self, new_filename, *, move=False, commit=True): def rename_file(self, new_filename, *, move=False, commit=True):
''' '''
Rename the file on the disk as well as in the database. Rename the file on the disk as well as in the database.
@ -921,6 +948,7 @@ class Tag(ObjectBase, GroupableMixin):
def _uncache(self): def _uncache(self):
self.photodb.caches['tag'].remove(self.id) self.photodb.caches['tag'].remove(self.id)
@decorators.transaction
def add_synonym(self, synname, *, commit=True): def add_synonym(self, synname, *, commit=True):
synname = self.photodb.normalize_tagname(synname) synname = self.photodb.normalize_tagname(synname)
@ -945,6 +973,7 @@ class Tag(ObjectBase, GroupableMixin):
return synname return synname
@decorators.transaction
def convert_to_synonym(self, mastertag, *, commit=True): def convert_to_synonym(self, mastertag, *, commit=True):
''' '''
Convert this tag into a synonym for a different tag. Convert this tag into a synonym for a different tag.
@ -993,6 +1022,7 @@ class Tag(ObjectBase, GroupableMixin):
self.photodb.log.debug('Committing - convert to synonym') self.photodb.log.debug('Committing - convert to synonym')
self.photodb.commit() self.photodb.commit()
@decorators.transaction
def delete(self, *, delete_children=False, commit=True): def delete(self, *, delete_children=False, commit=True):
self.photodb.log.debug('Deleting tag {tag:r}'.format(tag=self)) self.photodb.log.debug('Deleting tag {tag:r}'.format(tag=self))
self.photodb._cached_frozen_children = None self.photodb._cached_frozen_children = None
@ -1018,6 +1048,7 @@ class Tag(ObjectBase, GroupableMixin):
self._cached_qualified_name = qualname self._cached_qualified_name = qualname
return qualname return qualname
@decorators.transaction
def remove_synonym(self, synname, *, commit=True): def remove_synonym(self, synname, *, commit=True):
''' '''
Delete a synonym. Delete a synonym.
@ -1043,6 +1074,7 @@ class Tag(ObjectBase, GroupableMixin):
self.photodb.log.debug('Committing - remove synonym') self.photodb.log.debug('Committing - remove synonym')
self.photodb.commit() self.photodb.commit()
@decorators.transaction
def rename(self, new_name, *, apply_to_synonyms=True, commit=True): def rename(self, new_name, *, apply_to_synonyms=True, commit=True):
''' '''
Rename the tag. Does not affect its relation to Photos or tag groups. Rename the tag. Does not affect its relation to Photos or tag groups.

View File

@ -280,6 +280,7 @@ class PDBAlbumMixin:
if album.parent() is None: if album.parent() is None:
yield album yield album
@decorators.transaction
def new_album( def new_album(
self, self,
title=None, title=None,
@ -364,6 +365,7 @@ class PDBBookmarkMixin:
def get_bookmarks(self): def get_bookmarks(self):
yield from self.get_things(thing_type='bookmark') yield from self.get_things(thing_type='bookmark')
@decorators.transaction
def new_bookmark(self, url, title=None, *, author=None, commit=True): def new_bookmark(self, url, title=None, *, author=None, commit=True):
if not self.config['enable_new_bookmark']: if not self.config['enable_new_bookmark']:
raise exceptions.FeatureDisabled('new_bookmark') raise exceptions.FeatureDisabled('new_bookmark')
@ -438,6 +440,7 @@ class PDBPhotoMixin:
if count <= 0: if count <= 0:
break break
@decorators.transaction
def new_photo( def new_photo(
self, self,
filepath, filepath,
@ -518,6 +521,7 @@ class PDBPhotoMixin:
self.commit() self.commit()
return photo return photo
@decorators.transaction
def purge_deleted_files(self, photos=None, *, commit=True): def purge_deleted_files(self, photos=None, *, commit=True):
''' '''
Remove Photo entries if their corresponding file is no longer found. Remove Photo entries if their corresponding file is no longer found.
@ -536,6 +540,7 @@ class PDBPhotoMixin:
self.log.debug('Committing - purge deleted photos') self.log.debug('Committing - purge deleted photos')
self.commit() self.commit()
@decorators.transaction
def purge_empty_albums(self, *, commit=True): def purge_empty_albums(self, *, commit=True):
albums = self.get_albums() albums = self.get_albums()
for album in albums: for album in albums:
@ -949,6 +954,7 @@ class PDBTagMixin:
def get_tags(self): def get_tags(self):
yield from self.get_things(thing_type='tag') yield from self.get_things(thing_type='tag')
@decorators.transaction
def new_tag(self, tagname, *, commit=True): def new_tag(self, tagname, *, commit=True):
''' '''
Register a new tag and return the Tag object. Register a new tag and return the Tag object.
@ -1074,6 +1080,7 @@ class PDBUserMixin:
return objects.User(self, fetch) return objects.User(self, fetch)
@decorators.transaction
def register_user(self, username, password, commit=True): def register_user(self, username, password, commit=True):
if not self.config['enable_new_user']: if not self.config['enable_new_user']:
raise exceptions.FeatureDisabled('new_user') raise exceptions.FeatureDisabled('new_user')
@ -1216,6 +1223,7 @@ class PhotoDB(PDBAlbumMixin, PDBBookmarkMixin, PDBPhotoMixin, PDBTagMixin, PDBUs
task['action'](*args, **kwargs) task['action'](*args, **kwargs)
self.sql.commit() self.sql.commit()
@decorators.transaction
def digest_directory( def digest_directory(
self, self,
directory, directory,