<s> """<STR_LIT>""" <EOL> from __future__ import division , absolute_import , print_function <EOL> import time <EOL> import os <EOL> from collections import defaultdict <EOL> import threading <EOL> import sqlite3 <EOL> import contextlib <EOL> import collections <EOL> import beets <EOL> from beets . util . functemplate import Template <EOL> from beets . dbcore import types <EOL> from . query import MatchQuery , NullSort , TrueQuery <EOL> class FormattedMapping ( collections . Mapping ) : <EOL> """<STR_LIT>""" <EOL> def __init__ ( self , model , for_path = False ) : <EOL> self . for_path = for_path <EOL> self . model = model <EOL> self . model_keys = model . keys ( True ) <EOL> def __getitem__ ( self , key ) : <EOL> if key in self . model_keys : <EOL> return self . _get_formatted ( self . model , key ) <EOL> else : <EOL> raise KeyError ( key ) <EOL> def __iter__ ( self ) : <EOL> return iter ( self . model_keys ) <EOL> def __len__ ( self ) : <EOL> return len ( self . model_keys ) <EOL> def get ( self , key , default = None ) : <EOL> if default is None : <EOL> default = self . model . _type ( key ) . format ( None ) <EOL> return super ( FormattedMapping , self ) . get ( key , default ) <EOL> def _get_formatted ( self , model , key ) : <EOL> value = model . _type ( key ) . format ( model . get ( key ) ) <EOL> if isinstance ( value , bytes ) : <EOL> value = value . decode ( '<STR_LIT:utf8>' , '<STR_LIT:ignore>' ) <EOL> if self . for_path : <EOL> sep_repl = beets . config [ '<STR_LIT>' ] . get ( unicode ) <EOL> for sep in ( os . path . sep , os . path . altsep ) : <EOL> if sep : <EOL> value = value . replace ( sep , sep_repl ) <EOL> return value <EOL> class Model ( object ) : <EOL> """<STR_LIT>""" <EOL> _table = None <EOL> """<STR_LIT>""" <EOL> _flex_table = None <EOL> """<STR_LIT>""" <EOL> _fields = { } <EOL> """<STR_LIT>""" <EOL> _search_fields = ( ) <EOL> """<STR_LIT>""" <EOL> _types = { } <EOL> """<STR_LIT>""" <EOL> _sorts = { } <EOL> """<STR_LIT>""" <EOL> _always_dirty = False <EOL> """<STR_LIT>""" <EOL> @ classmethod <EOL> def _getters ( cls ) : <EOL> """<STR_LIT>""" <EOL> raise NotImplementedError ( ) <EOL> def _template_funcs ( self ) : <EOL> """<STR_LIT>""" <EOL> raise NotImplementedError ( ) <EOL> def __init__ ( self , db = None , ** values ) : <EOL> """<STR_LIT>""" <EOL> self . _db = db <EOL> self . _dirty = set ( ) <EOL> self . _values_fixed = { } <EOL> self . _values_flex = { } <EOL> self . update ( values ) <EOL> self . clear_dirty ( ) <EOL> @ classmethod <EOL> def _awaken ( cls , db = None , fixed_values = { } , flex_values = { } ) : <EOL> """<STR_LIT>""" <EOL> obj = cls ( db ) <EOL> for key , value in fixed_values . iteritems ( ) : <EOL> obj . _values_fixed [ key ] = cls . _type ( key ) . from_sql ( value ) <EOL> for key , value in flex_values . iteritems ( ) : <EOL> obj . _values_flex [ key ] = cls . _type ( key ) . from_sql ( value ) <EOL> return obj <EOL> def __repr__ ( self ) : <EOL> return '<STR_LIT>' . format ( <EOL> type ( self ) . __name__ , <EOL> '<STR_LIT:U+002CU+0020>' . join ( '<STR_LIT>' . format ( k , v ) for k , v in dict ( self ) . items ( ) ) , <EOL> ) <EOL> def clear_dirty ( self ) : <EOL> """<STR_LIT>""" <EOL> self . _dirty = set ( ) <EOL> def _check_db ( self , need_id = True ) : <EOL> """<STR_LIT>""" <EOL> if not self . _db : <EOL> raise ValueError ( <EOL> u'<STR_LIT>' . format ( type ( self ) . __name__ ) <EOL> ) <EOL> if need_id and not self . id : <EOL> raise ValueError ( u'<STR_LIT>' . format ( type ( self ) . __name__ ) ) <EOL> @ classmethod <EOL> def _type ( cls , key ) : <EOL> """<STR_LIT>""" <EOL> return cls . _fields . get ( key ) or cls . _types . get ( key ) or types . DEFAULT <EOL> def __getitem__ ( self , key ) : <EOL> """<STR_LIT>""" <EOL> getters = self . _getters ( ) <EOL> if key in getters : <EOL> return getters [ key ] ( self ) <EOL> elif key in self . _fields : <EOL> return self . _values_fixed . get ( key ) <EOL> elif key in self . _values_flex : <EOL> return self . _values_flex [ key ] <EOL> else : <EOL> raise KeyError ( key ) <EOL> def __setitem__ ( self , key , value ) : <EOL> """<STR_LIT>""" <EOL> if key in self . _fields : <EOL> source = self . _values_fixed <EOL> else : <EOL> source = self . _values_flex <EOL> value = self . _type ( key ) . normalize ( value ) <EOL> old_value = source . get ( key ) <EOL> source [ key ] = value <EOL> if self . _always_dirty or old_value != value : <EOL> self . _dirty . add ( key ) <EOL> def __delitem__ ( self , key ) : <EOL> """<STR_LIT>""" <EOL> if key in self . _values_flex : <EOL> del self . _values_flex [ key ] <EOL> self . _dirty . add ( key ) <EOL> elif key in self . _getters ( ) : <EOL> raise KeyError ( u'<STR_LIT>' . format ( key ) ) <EOL> elif key in self . _fields : <EOL> raise KeyError ( u'<STR_LIT>' . format ( key ) ) <EOL> else : <EOL> raise KeyError ( u'<STR_LIT>' . format ( key ) ) <EOL> def keys ( self , computed = False ) : <EOL> """<STR_LIT>""" <EOL> base_keys = list ( self . _fields ) + self . _values_flex . keys ( ) <EOL> if computed : <EOL> return base_keys + self . _getters ( ) . keys ( ) <EOL> else : <EOL> return base_keys <EOL> @ classmethod <EOL> def all_keys ( cls ) : <EOL> """<STR_LIT>""" <EOL> return list ( cls . _fields ) + cls . _getters ( ) . keys ( ) <EOL> def update ( self , values ) : <EOL> """<STR_LIT>""" <EOL> for key , value in values . items ( ) : <EOL> self [ key ] = value <EOL> def items ( self ) : <EOL> """<STR_LIT>""" <EOL> for key in self : <EOL> yield key , self [ key ] <EOL> def get ( self , key , default = None ) : <EOL> """<STR_LIT>""" <EOL> if key in self : <EOL> return self [ key ] <EOL> else : <EOL> return default <EOL> def __contains__ ( self , key ) : <EOL> """<STR_LIT>""" <EOL> return key in self . keys ( True ) <EOL> def __iter__ ( self ) : <EOL> """<STR_LIT>""" <EOL> return iter ( self . keys ( ) ) <EOL> def __getattr__ ( self , key ) : <EOL> if key . startswith ( '<STR_LIT:_>' ) : <EOL> raise AttributeError ( u'<STR_LIT>' . format ( key ) ) <EOL> else : <EOL> try : <EOL> return self [ key ] <EOL> except KeyError : <EOL> raise AttributeError ( u'<STR_LIT>' . format ( key ) ) <EOL> def __setattr__ ( self , key , value ) : <EOL> if key . startswith ( '<STR_LIT:_>' ) : <EOL> super ( Model , self ) . __setattr__ ( key , value ) <EOL> else : <EOL> self [ key ] = value <EOL> def __delattr__ ( self , key ) : <EOL> if key . startswith ( '<STR_LIT:_>' ) : <EOL> super ( Model , self ) . __delattr__ ( key ) <EOL> else : <EOL> del self [ key ] <EOL> def store ( self ) : <EOL> """<STR_LIT>""" <EOL> self . _check_db ( ) <EOL> assignments = [ ] <EOL> subvars = [ ] <EOL> for key in self . _fields : <EOL> if key != '<STR_LIT:id>' and key in self . _dirty : <EOL> self . _dirty . remove ( key ) <EOL> assignments . append ( key + '<STR_LIT>' ) <EOL> value = self . _type ( key ) . to_sql ( self [ key ] ) <EOL> subvars . append ( value ) <EOL> assignments = '<STR_LIT:U+002C>' . join ( assignments ) <EOL> with self . _db . transaction ( ) as tx : <EOL> if assignments : <EOL> query = '<STR_LIT>' . format ( <EOL> self . _table , assignments <EOL> ) <EOL> subvars . append ( self . id ) <EOL> tx . mutate ( query , subvars ) <EOL> for key , value in self . _values_flex . items ( ) : <EOL> if key in self . _dirty : <EOL> self . _dirty . remove ( key ) <EOL> tx . mutate ( <EOL> '<STR_LIT>' <EOL> '<STR_LIT>' <EOL> '<STR_LIT>' . format ( self . _flex_table ) , <EOL> ( self . id , key , value ) , <EOL> ) <EOL> for key in self . _dirty : <EOL> tx . mutate ( <EOL> '<STR_LIT>' <EOL> '<STR_LIT>' . format ( self . _flex_table ) , <EOL> ( self . id , key ) <EOL> ) <EOL> self . clear_dirty ( ) <EOL> def load ( self ) : <EOL> """<STR_LIT>""" <EOL> self . _check_db ( ) <EOL> stored_obj = self . _db . _get ( type ( self ) , self . id ) <EOL> assert stored_obj is not None , u"<STR_LIT>" . format ( self . id ) <EOL> self . _values_fixed = { } <EOL> self . _values_flex = { } <EOL> self . update ( dict ( stored_obj ) ) <EOL> self . clear_dirty ( ) <EOL> def remove ( self ) : <EOL> """<STR_LIT>""" <EOL> self . _check_db ( ) <EOL> with self . _db . transaction ( ) as tx : <EOL> tx . mutate ( <EOL> '<STR_LIT>' . format ( self . _table ) , <EOL> ( self . id , ) <EOL> ) <EOL> tx . mutate ( <EOL> '<STR_LIT>' . format ( self . _flex_table ) , <EOL> ( self . id , ) <EOL> ) <EOL> def add ( self , db = None ) : <EOL> """<STR_LIT>""" <EOL> if db : <EOL> self . _db = db <EOL> self . _check_db ( False ) <EOL> with self . _db . transaction ( ) as tx : <EOL> new_id = tx . mutate ( <EOL> '<STR_LIT>' . format ( self . _table ) <EOL> ) <EOL> self . id = new_id <EOL> self . added = time . time ( ) <EOL> for key in self : <EOL> if self [ key ] is not None : <EOL> self . _dirty . add ( key ) <EOL> self . store ( ) <EOL> _formatter = FormattedMapping <EOL> def formatted ( self , for_path = False ) : <EOL> """<STR_LIT>""" <EOL> return self . _formatter ( self , for_path ) <EOL> def evaluate_template ( self , template , for_path = False ) : <EOL> """<STR_LIT>""" <EOL> if isinstance ( template , basestring ) : <EOL> template = Template ( template ) <EOL> return template . substitute ( self . formatted ( for_path ) , <EOL> self . _template_funcs ( ) ) <EOL> @ classmethod <EOL> def _parse ( cls , key , string ) : <EOL> """<STR_LIT>""" <EOL> if not isinstance ( string , basestring ) : <EOL> raise TypeError ( u"<STR_LIT>" ) <EOL> return cls . _type ( key ) . parse ( string ) <EOL> def set_parse ( self , key , string ) : <EOL> """<STR_LIT>""" <EOL> self [ key ] = self . _parse ( key , string ) <EOL> class Results ( object ) : <EOL> """<STR_LIT>""" <EOL> def __init__ ( self , model_class , rows , db , query = None , sort = None ) : <EOL> """<STR_LIT>""" <EOL> self . model_class = model_class <EOL> self . rows = rows <EOL> self . db = db <EOL> self . query = query <EOL> self . sort = sort <EOL> self . _rows = rows <EOL> self . _row_count = len ( rows ) <EOL> self . _objects = [ ] <EOL> def _get_objects ( self ) : <EOL> """<STR_LIT>""" <EOL> index = <NUM_LIT:0> <EOL> while index < len ( self . _objects ) or self . _rows : <EOL> if index < len ( self . _objects ) : <EOL> yield self . _objects [ index ] <EOL> index += <NUM_LIT:1> <EOL> else : <EOL> while self . _rows : <EOL> row = self . _rows . pop ( <NUM_LIT:0> ) <EOL> obj = self . _make_model ( row ) <EOL> if not self . query or self . query . match ( obj ) : <EOL> self . _objects . append ( obj ) <EOL> index += <NUM_LIT:1> <EOL> yield obj <EOL> break <EOL> def __iter__ ( self ) : <EOL> """<STR_LIT>""" <EOL> if self . sort : <EOL> objects = self . sort . sort ( list ( self . _get_objects ( ) ) ) <EOL> return iter ( objects ) <EOL> else : <EOL> return self . _get_objects ( ) <EOL> def _make_model ( self , row ) : <EOL> with self . db . transaction ( ) as tx : <EOL> flex_rows = tx . query ( <EOL> '<STR_LIT>' . format ( <EOL> self . model_class . _flex_table <EOL> ) , <EOL> ( row [ b'<STR_LIT:id>' ] , ) <EOL> ) <EOL> cols = dict ( row ) <EOL> values = dict ( ( k , v ) for ( k , v ) in cols . items ( ) <EOL> if not k [ : <NUM_LIT:4> ] == '<STR_LIT>' ) <EOL> flex_values = dict ( ( row [ b'<STR_LIT:key>' ] , row [ b'<STR_LIT:value>' ] ) for row in flex_rows ) <EOL> obj = self . model_class . _awaken ( self . db , values , flex_values ) <EOL> return obj <EOL> def __len__ ( self ) : <EOL> """<STR_LIT>""" <EOL> if not self . _rows : <EOL> return len ( self . _objects ) <EOL> elif self . query : <EOL> count = <NUM_LIT:0> <EOL> for obj in self : <EOL> count += <NUM_LIT:1> <EOL> return count <EOL> else : <EOL> return self . _row_count <EOL> def __nonzero__ ( self ) : <EOL> """<STR_LIT>""" <EOL> return bool ( len ( self ) ) <EOL> def __getitem__ ( self , n ) : <EOL> """<STR_LIT>""" <EOL> if not self . _rows and not self . sort : <EOL> return self . _objects [ n ] <EOL> it = iter ( self ) <EOL> try : <EOL> for i in range ( n ) : <EOL> it . next ( ) <EOL> return it . next ( ) <EOL> except StopIteration : <EOL> raise IndexError ( u'<STR_LIT>' . format ( n ) ) <EOL> def get ( self ) : <EOL> """<STR_LIT>""" <EOL> it = iter ( self ) <EOL> try : <EOL> return it . next ( ) <EOL> except StopIteration : <EOL> return None <EOL> class Transaction ( object ) : <EOL> """<STR_LIT>""" <EOL> def __init__ ( self , db ) : <EOL> self . db = db <EOL> def __enter__ ( self ) : <EOL> """<STR_LIT>""" <EOL> with self . db . _tx_stack ( ) as stack : <EOL> first = not stack <EOL> stack . append ( self ) <EOL> if first : <EOL> self . db . _db_lock . acquire ( ) <EOL> return self <EOL> def __exit__ ( self , exc_type , exc_value , traceback ) : <EOL> """<STR_LIT>""" <EOL> with self . db . _tx_stack ( ) as stack : <EOL> assert stack . pop ( ) is self <EOL> empty = not stack <EOL> if empty : <EOL> self . db . _connection ( ) . commit ( ) <EOL> self . db . _db_lock . release ( ) <EOL> def query ( self , statement , subvals = ( ) ) : <EOL> """<STR_LIT>""" <EOL> cursor = self . db . _connection ( ) . execute ( statement , subvals ) <EOL> return cursor . fetchall ( ) <EOL> def mutate ( self , statement , subvals = ( ) ) : <EOL> """<STR_LIT>""" <EOL> cursor = self . db . _connection ( ) . execute ( statement , subvals ) <EOL> return cursor . lastrowid <EOL> def script ( self , statements ) : <EOL> """<STR_LIT>""" <EOL> self . db . _connection ( ) . executescript ( statements ) <EOL> class Database ( object ) : <EOL> """<STR_LIT>""" <EOL> _models = ( ) <EOL> """<STR_LIT>""" <EOL> def __init__ ( self , path ) : <EOL> self . path = path <EOL> self . _connections = { } <EOL> self . _tx_stacks = defaultdict ( list ) <EOL> self . _shared_map_lock = threading . Lock ( ) <EOL> self . _db_lock = threading . Lock ( ) <EOL> for model_cls in self . _models : <EOL> self . _make_table ( model_cls . _table , model_cls . _fields ) <EOL> self . _make_attribute_table ( model_cls . _flex_table ) <EOL> def _connection ( self ) : <EOL> """<STR_LIT>""" <EOL> thread_id = threading . current_thread ( ) . ident <EOL> with self . _shared_map_lock : <EOL> if thread_id in self . _connections : <EOL> return self . _connections [ thread_id ] <EOL> else : <EOL> conn = sqlite3 . connect ( <EOL> self . path , <EOL> timeout = beets . config [ '<STR_LIT>' ] . as_number ( ) , <EOL> ) <EOL> conn . row_factory = sqlite3 . Row <EOL> self . _connections [ thread_id ] = conn <EOL> return conn <EOL> @ contextlib . contextmanager <EOL> def _tx_stack ( self ) : <EOL> """<STR_LIT>""" <EOL> thread_id = threading . current_thread ( ) . ident <EOL> with self . _shared_map_lock : <EOL> yield self . _tx_stacks [ thread_id ] <EOL> def transaction ( self ) : <EOL> """<STR_LIT>""" <EOL> return Transaction ( self ) <EOL> def _make_table ( self , table , fields ) : <EOL> """<STR_LIT>""" <EOL> with self . transaction ( ) as tx : <EOL> rows = tx . query ( '<STR_LIT>' % table ) <EOL> current_fields = set ( [ row [ <NUM_LIT:1> ] for row in rows ] ) <EOL> field_names = set ( fields . keys ( ) ) <EOL> if current_fields . issuperset ( field_names ) : <EOL> return <EOL> if not current_fields : <EOL> columns = [ ] <EOL> for name , typ in fields . items ( ) : <EOL> columns . append ( '<STR_LIT>' . format ( name , typ . sql ) ) <EOL> setup_sql = '<STR_LIT>' . format ( table , <EOL> '<STR_LIT:U+002CU+0020>' . join ( columns ) ) <EOL> else : <EOL> setup_sql = '<STR_LIT>' <EOL> for name , typ in fields . items ( ) : <EOL> if name in current_fields : <EOL> continue <EOL> setup_sql += '<STR_LIT>' . format ( <EOL> table , name , typ . sql <EOL> ) <EOL> with self . transaction ( ) as tx : <EOL> tx . script ( setup_sql ) <EOL> def _make_attribute_table ( self , flex_table ) : <EOL> """<STR_LIT>""" <EOL> with self . transaction ( ) as tx : <EOL> tx . script ( """<STR_LIT>""" . format ( flex_table ) ) <EOL> def _fetch ( self , model_cls , query = None , sort = None ) : <EOL> """<STR_LIT>""" <EOL> query = query or TrueQuery ( ) <EOL> sort = sort or NullSort ( ) <EOL> where , subvals = query . clause ( ) <EOL> order_by = sort . order_clause ( ) <EOL> sql = ( "<STR_LIT>" ) . format ( <EOL> model_cls . _table , <EOL> where or '<STR_LIT:1>' , <EOL> "<STR_LIT>" . format ( order_by ) if order_by else '<STR_LIT>' , <EOL> ) <EOL> with self . transaction ( ) as tx : <EOL> rows = tx . query ( sql , subvals ) <EOL> return Results ( <EOL> model_cls , rows , self , <EOL> None if where else query , <EOL> sort if sort . is_slow ( ) else None , <EOL> ) <EOL> def _get ( self , model_cls , id ) : <EOL> """<STR_LIT>""" <EOL> return self . _fetch ( model_cls , MatchQuery ( '<STR_LIT:id>' , id ) ) . get ( ) </s>
