Simplify model initialization shortcut

This avoids multiple paths to setting values. It also moves the buffer check
into PathType.normalize, where it belongs. This is slightly faster than the
previous iteration: about 8 vs. 9 seconds to list about 13k songs on my
machine.
This commit is contained in:
Adrian Sampson 2014-05-10 14:02:45 -07:00
parent 505add7e5c
commit d48c604b2f
2 changed files with 30 additions and 46 deletions

View file

@ -95,7 +95,7 @@ class Model(object):
# Basic operation.
def __init__(self, db=None, fixed=None, flexattr=None, **values):
def __init__(self, db=None, **values):
"""Create a new object with an optional Database association and
initial field values.
"""
@ -105,10 +105,24 @@ class Model(object):
self._values_flex = {}
# Initial contents.
self._bulk_update(fixed, flexattr)
self.update(values)
self.clear_dirty()
@classmethod
def _awaken(cls, db=None, fixed_values=None, flex_values=None):
"""Create an object with values drawn from the database.
This is a performance optimization: the checks involved with
ordinary construction are bypassed.
"""
obj = cls(db)
if fixed_values:
for key, value in fixed_values.items():
obj._values_fixed[key] = cls._fields[key].normalize(value)
if flex_values:
obj._values_flex.update(flex_values)
return obj
def __repr__(self):
return '{0}({1})'.format(
type(self).__name__,
@ -196,24 +210,6 @@ class Model(object):
for key, value in values.items():
self[key] = value
def _bulk_update(self, fixed, flexattr):
"""Assign all values in the fixed and flex dicts.
Using _bulk_update() bypasses many tests made by update() and
should only be used when loading data from the db.
"""
if fixed:
for (key, value) in fixed.items():
self._set_fixed_attr(key, value)
if flexattr:
for (key, value) in flexattr.items():
self._set_flex_attr(key, value)
def _set_fixed_attr(self, key, value):
self._values_fixed[key] = self._fields[key].normalize(value)
def _set_flex_attr(self, key, value):
self._values_flex[key] = value
def items(self):
"""Iterate over (key, value) pairs that this object contains.
Computed fields are not included.
@ -515,7 +511,7 @@ class Results(object):
# Construct the Python object and yield it if it passes the
# predicate.
obj = self.model_class(self.db, values, flex_values)
obj = self.model_class._awaken(self.db, values, flex_values)
if not self.query or self.query.match(obj):
yield obj

View file

@ -94,6 +94,19 @@ class PathType(types.Type):
def parse(self, string):
return normpath(bytestring_path(string))
def normalize(self, value):
if isinstance(value, unicode):
# Paths stored internally as encoded bytes.
return bytestring_path(value)
elif isinstance(value, buffer):
# SQLite must store bytestings as buffers to avoid decoding.
# We unwrap buffers to bytes.
return bytes(value)
else:
return value
# Special path format key.
PF_KEY_DEFAULT = 'default'
@ -295,14 +308,6 @@ class Item(LibModel):
if self.mtime == 0 and 'mtime' in values:
self.mtime = values['mtime']
def _set_fixed_attr(self, key, value):
if key == 'path':
if isinstance(value, unicode):
value = bytestring_path(value)
elif isinstance(value, buffer):
value = str(value)
super(Item, self)._set_fixed_attr(key, value)
def get_album(self):
"""Get the Album object that this item belongs to, if any, or
None if the item is a singleton or is not associated with a
@ -695,23 +700,6 @@ class Album(LibModel):
getters['path'] = Album.item_dir
return getters
def _set_fixed_attr(self, key, value):
if key == 'artpath':
if isinstance(value, unicode):
value = bytestring_path(value)
elif isinstance(value, buffer):
value = bytes(value)
super(Album, self)._set_fixed_attr(key, value)
def __setitem__(self, key, value):
"""Set the value of an album attribute."""
if key == 'artpath':
if isinstance(value, unicode):
value = bytestring_path(value)
elif isinstance(value, buffer):
value = bytes(value)
super(Album, self).__setitem__(key, value)
def items(self):
"""Returns an iterable over the items associated with this
album.