<s> __author__ = '<STR_LIT>' <EOL> '''<STR_LIT>''' <EOL> import os , re , sys , time , uuid , socket , datetime , functools , threading , logging , collections <EOL> from utils import Dict <EOL> def next_str ( t = None ) : <EOL> '''<STR_LIT>''' <EOL> if t is None : <EOL> t = time . time ( ) <EOL> return '<STR_LIT>' % ( int ( t * <NUM_LIT:1000> ) , uuid . uuid4 ( ) . hex ) <EOL> next_id = next_str <EOL> def _profiling ( start , sql = '<STR_LIT>' ) : <EOL> t = time . time ( ) - start <EOL> if t > <NUM_LIT:0.1> : <EOL> logging . warning ( '<STR_LIT>' % ( t , sql ) ) <EOL> else : <EOL> logging . info ( '<STR_LIT>' % ( t , sql ) ) <EOL> class DBError ( Exception ) : <EOL> pass <EOL> class MultiColumnsError ( DBError ) : <EOL> pass <EOL> def _log ( s ) : <EOL> logging . debug ( s ) <EOL> def _dummy_connect ( ) : <EOL> '''<STR_LIT>''' <EOL> raise DBError ( '<STR_LIT>' ) <EOL> _db_connect = _dummy_connect <EOL> _db_convert = '<STR_LIT:?>' <EOL> class _LasyConnection ( object ) : <EOL> def __init__ ( self ) : <EOL> self . connection = None <EOL> def cursor ( self ) : <EOL> if self . connection is None : <EOL> _log ( '<STR_LIT>' ) <EOL> self . connection = _db_connect ( ) <EOL> return self . connection . cursor ( ) <EOL> def commit ( self ) : <EOL> self . connection . commit ( ) <EOL> def rollback ( self ) : <EOL> self . connection . rollback ( ) <EOL> def cleanup ( self ) : <EOL> if self . connection : <EOL> connection = self . connection <EOL> self . connection = None <EOL> _log ( '<STR_LIT>' ) <EOL> connection . close ( ) <EOL> class _DbCtx ( threading . local ) : <EOL> '''<STR_LIT>''' <EOL> def __init__ ( self ) : <EOL> self . connection = None <EOL> self . transactions = <NUM_LIT:0> <EOL> def is_init ( self ) : <EOL> return not self . connection is None <EOL> def init ( self ) : <EOL> _log ( '<STR_LIT>' ) <EOL> self . connection = _LasyConnection ( ) <EOL> self . transactions = <NUM_LIT:0> <EOL> def cleanup ( self ) : <EOL> self . connection . cleanup ( ) <EOL> def cursor ( self ) : <EOL> '''<STR_LIT>''' <EOL> return self . connection . cursor ( ) <EOL> _db_ctx = _DbCtx ( ) <EOL> class _ConnectionCtx ( object ) : <EOL> '''<STR_LIT>''' <EOL> def __enter__ ( self ) : <EOL> global _db_ctx <EOL> self . should_cleanup = False <EOL> if not _db_ctx . is_init ( ) : <EOL> _db_ctx . init ( ) <EOL> self . should_cleanup = True <EOL> return self <EOL> def __exit__ ( self , exctype , excvalue , traceback ) : <EOL> global _db_ctx <EOL> if self . should_cleanup : <EOL> _db_ctx . cleanup ( ) <EOL> def connection ( ) : <EOL> '''<STR_LIT>''' <EOL> return _ConnectionCtx ( ) <EOL> def with_connection ( func ) : <EOL> '''<STR_LIT>''' <EOL> @ functools . wraps ( func ) <EOL> def _wrapper ( * args , ** kw ) : <EOL> with _ConnectionCtx ( ) : <EOL> return func ( * args , ** kw ) <EOL> return _wrapper <EOL> class _TransactionCtx ( object ) : <EOL> '''<STR_LIT>''' <EOL> def __enter__ ( self ) : <EOL> global _db_ctx <EOL> self . should_close_conn = False <EOL> if not _db_ctx . is_init ( ) : <EOL> _db_ctx . init ( ) <EOL> self . should_close_conn = True <EOL> _db_ctx . transactions = _db_ctx . transactions + <NUM_LIT:1> <EOL> _log ( '<STR_LIT>' if _db_ctx . transactions == <NUM_LIT:1> else '<STR_LIT>' ) <EOL> return self <EOL> def __exit__ ( self , exctype , excvalue , traceback ) : <EOL> global _db_ctx <EOL> _db_ctx . transactions = _db_ctx . transactions - <NUM_LIT:1> <EOL> try : <EOL> if _db_ctx . transactions == <NUM_LIT:0> : <EOL> if exctype is None : <EOL> self . commit ( ) <EOL> else : <EOL> self . rollback ( ) <EOL> finally : <EOL> if self . should_close_conn : <EOL> _db_ctx . cleanup ( ) <EOL> def commit ( self ) : <EOL> global _db_ctx <EOL> _log ( '<STR_LIT>' ) <EOL> try : <EOL> _db_ctx . connection . commit ( ) <EOL> _log ( '<STR_LIT>' ) <EOL> except : <EOL> logging . warning ( '<STR_LIT>' ) <EOL> _db_ctx . connection . rollback ( ) <EOL> logging . warning ( '<STR_LIT>' ) <EOL> raise <EOL> def rollback ( self ) : <EOL> global _db_ctx <EOL> _log ( '<STR_LIT>' ) <EOL> _db_ctx . connection . rollback ( ) <EOL> logging . info ( '<STR_LIT>' ) <EOL> def transaction ( ) : <EOL> '''<STR_LIT>''' <EOL> return _TransactionCtx ( ) <EOL> def with_transaction ( func ) : <EOL> '''<STR_LIT>''' <EOL> @ functools . wraps ( func ) <EOL> def _wrapper ( * args , ** kw ) : <EOL> _start = time . time ( ) <EOL> with _TransactionCtx ( ) : <EOL> return func ( * args , ** kw ) <EOL> _profiling ( _start ) <EOL> return _wrapper <EOL> def _select ( sql , first , * args ) : <EOL> '<STR_LIT>' <EOL> global _db_ctx , _db_convert <EOL> cursor = None <EOL> if _db_convert != '<STR_LIT:?>' : <EOL> sql = sql . replace ( '<STR_LIT:?>' , _db_convert ) <EOL> _log ( '<STR_LIT>' % ( sql , args ) ) <EOL> start = time . time ( ) <EOL> try : <EOL> cursor = _db_ctx . connection . cursor ( ) <EOL> cursor . execute ( sql , args ) <EOL> if cursor . description : <EOL> names = [ x [ <NUM_LIT:0> ] for x in cursor . description ] <EOL> if first : <EOL> values = cursor . fetchone ( ) <EOL> if not values : <EOL> return None <EOL> return Dict ( names , values ) <EOL> return [ Dict ( names , x ) for x in cursor . fetchall ( ) ] <EOL> finally : <EOL> if cursor : <EOL> cursor . close ( ) <EOL> _profiling ( start , sql ) <EOL> @ with_connection <EOL> def select_one ( sql , * args ) : <EOL> '''<STR_LIT>''' <EOL> return _select ( sql , True , * args ) <EOL> @ with_connection <EOL> def select_int ( sql , * args ) : <EOL> '''<STR_LIT>''' <EOL> d = _select ( sql , True , * args ) <EOL> if len ( d ) != <NUM_LIT:1> : <EOL> raise MultiColumnsError ( '<STR_LIT>' ) <EOL> return d . values ( ) [ <NUM_LIT:0> ] <EOL> @ with_connection <EOL> def select ( sql , * args ) : <EOL> '''<STR_LIT>''' <EOL> return _select ( sql , False , * args ) <EOL> @ with_connection <EOL> def _update ( sql , args , post_fn = None ) : <EOL> global _db_ctx , _db_convert <EOL> cursor = None <EOL> if _db_convert != '<STR_LIT:?>' : <EOL> sql = sql . replace ( '<STR_LIT:?>' , _db_convert ) <EOL> _log ( '<STR_LIT>' % ( sql , args ) ) <EOL> start = time . time ( ) <EOL> try : <EOL> cursor = _db_ctx . connection . cursor ( ) <EOL> cursor . execute ( sql , args ) <EOL> r = cursor . rowcount <EOL> if _db_ctx . transactions == <NUM_LIT:0> : <EOL> _log ( '<STR_LIT>' ) <EOL> _db_ctx . connection . commit ( ) <EOL> post_fn and post_fn ( ) <EOL> return r <EOL> finally : <EOL> if cursor : <EOL> cursor . close ( ) <EOL> _profiling ( start , sql ) <EOL> def insert ( table , ** kw ) : <EOL> '''<STR_LIT>''' <EOL> cols , args = zip ( * kw . iteritems ( ) ) <EOL> sql = '<STR_LIT>' % ( table , '<STR_LIT:U+002C>' . join ( cols ) , '<STR_LIT:U+002C>' . join ( [ _db_convert for i in range ( len ( cols ) ) ] ) ) <EOL> return _update ( sql , args ) <EOL> def update ( sql , * args ) : <EOL> '''<STR_LIT>''' <EOL> return _update ( sql , args ) <EOL> def update_kw ( table , where , * args , ** kw ) : <EOL> '''<STR_LIT>''' <EOL> if len ( kw ) == <NUM_LIT:0> : <EOL> raise ValueError ( '<STR_LIT>' ) <EOL> sqls = [ '<STR_LIT>' , table , '<STR_LIT>' ] <EOL> params = [ ] <EOL> updates = [ ] <EOL> for k , v in kw . iteritems ( ) : <EOL> updates . append ( '<STR_LIT>' % k ) <EOL> params . append ( v ) <EOL> sqls . append ( '<STR_LIT:U+002CU+0020>' . join ( updates ) ) <EOL> sqls . append ( '<STR_LIT>' ) <EOL> sqls . append ( where ) <EOL> sql = '<STR_LIT:U+0020>' . join ( sqls ) <EOL> params . extend ( args ) <EOL> return update ( sql , * params ) <EOL> def init_connector ( func_connect , convert_char = '<STR_LIT:%s>' ) : <EOL> global _db_connect , _db_convert <EOL> _log ( '<STR_LIT>' ) <EOL> _db_connect = func_connect <EOL> _db_convert = convert_char <EOL> def init ( db_type , db_schema , db_host , db_port = <NUM_LIT:0> , db_user = None , db_password = None , db_driver = None , ** db_args ) : <EOL> '''<STR_LIT>''' <EOL> global _db_connect , _db_convert <EOL> if db_type == '<STR_LIT>' : <EOL> _log ( '<STR_LIT>' ) <EOL> default_args = { <EOL> '<STR_LIT:host>' : '<STR_LIT:127.0.0.1>' , <EOL> '<STR_LIT:port>' : <NUM_LIT> , <EOL> '<STR_LIT:user>' : '<STR_LIT>' , <EOL> '<STR_LIT:password>' : '<STR_LIT>' , <EOL> '<STR_LIT>' : db_schema , <EOL> '<STR_LIT>' : True , <EOL> '<STR_LIT>' : '<STR_LIT:utf8>' , <EOL> '<STR_LIT>' : '<STR_LIT>' <EOL> } <EOL> import mysql . connector <EOL> for k , v in default_args . iteritems ( ) : <EOL> db_args [ k ] = db_args . get ( k , v ) <EOL> _db_connect = lambda : mysql . connector . connect ( ** db_args ) <EOL> _db_convert = '<STR_LIT:%s>' <EOL> elif db_type == '<STR_LIT>' : <EOL> _log ( '<STR_LIT>' ) <EOL> import sqlite3 <EOL> _db_connect = lambda : sqlite3 . connect ( db_schema ) <EOL> else : <EOL> raise DBError ( '<STR_LIT>' % db_type ) <EOL> class Field ( object ) : <EOL> _count = <NUM_LIT:0> <EOL> def __init__ ( self , ** kw ) : <EOL> self . name = kw . get ( '<STR_LIT:name>' , None ) <EOL> self . _default = kw . get ( '<STR_LIT:default>' , None ) <EOL> self . primary_key = kw . get ( '<STR_LIT:primary_key>' , False ) <EOL> self . nullable = kw . get ( '<STR_LIT>' , False ) <EOL> self . updatable = kw . get ( '<STR_LIT>' , True ) <EOL> self . insertable = kw . get ( '<STR_LIT>' , True ) <EOL> self . ddl = kw . get ( '<STR_LIT>' , '<STR_LIT>' ) <EOL> self . _order = Field . _count <EOL> Field . _count = Field . _count + <NUM_LIT:1> <EOL> @ property <EOL> def default ( self ) : <EOL> d = self . _default <EOL> return d ( ) if callable ( d ) else d <EOL> def __str__ ( self ) : <EOL> s = [ '<STR_LIT>' % ( self . __class__ . __name__ , self . name , self . ddl , self . default ) ] <EOL> self . nullable and s . append ( '<STR_LIT:N>' ) <EOL> self . updatable and s . append ( '<STR_LIT>' ) <EOL> self . insertable and s . append ( '<STR_LIT:I>' ) <EOL> s . append ( '<STR_LIT:>>' ) <EOL> return '<STR_LIT>' . join ( s ) <EOL> class StringField ( Field ) : <EOL> def __init__ ( self , ** kw ) : <EOL> kw [ '<STR_LIT:default>' ] = kw . get ( '<STR_LIT:default>' , '<STR_LIT>' ) <EOL> kw [ '<STR_LIT>' ] = kw . get ( '<STR_LIT>' , '<STR_LIT>' ) <EOL> super ( StringField , self ) . __init__ ( ** kw ) <EOL> class IntegerField ( Field ) : <EOL> def __init__ ( self , ** kw ) : <EOL> if not '<STR_LIT:default>' in kw : <EOL> kw [ '<STR_LIT:default>' ] = <NUM_LIT:0> <EOL> if not '<STR_LIT>' in kw : <EOL> kw [ '<STR_LIT>' ] = '<STR_LIT>' <EOL> super ( IntegerField , self ) . __init__ ( ** kw ) <EOL> class FloatField ( Field ) : <EOL> def __init__ ( self , ** kw ) : <EOL> kw [ '<STR_LIT:default>' ] = kw . get ( '<STR_LIT:default>' , <NUM_LIT:0.0> ) <EOL> kw [ '<STR_LIT>' ] = kw . get ( '<STR_LIT>' , '<STR_LIT>' ) <EOL> super ( FloatField , self ) . __init__ ( ** kw ) <EOL> class BooleanField ( Field ) : <EOL> def __init__ ( self , ** kw ) : <EOL> kw [ '<STR_LIT:default>' ] = kw . get ( '<STR_LIT:default>' , False ) <EOL> kw [ '<STR_LIT>' ] = kw . get ( '<STR_LIT>' , '<STR_LIT:bool>' ) <EOL> super ( BooleanField , self ) . __init__ ( ** kw ) <EOL> class DateTimeField ( Field ) : <EOL> pass <EOL> class TextField ( Field ) : <EOL> def __init__ ( self , ** kw ) : <EOL> kw [ '<STR_LIT:default>' ] = kw . get ( '<STR_LIT:default>' , '<STR_LIT>' ) <EOL> kw [ '<STR_LIT>' ] = kw . get ( '<STR_LIT>' , '<STR_LIT:text>' ) <EOL> super ( TextField , self ) . __init__ ( ** kw ) <EOL> class BlobField ( Field ) : <EOL> def __init__ ( self , ** kw ) : <EOL> kw [ '<STR_LIT:default>' ] = kw . get ( '<STR_LIT:default>' , '<STR_LIT>' ) <EOL> kw [ '<STR_LIT>' ] = kw . get ( '<STR_LIT>' , '<STR_LIT>' ) <EOL> super ( BlobField , self ) . __init__ ( ** kw ) <EOL> class VersionField ( Field ) : <EOL> def __init__ ( self , name = None ) : <EOL> super ( VersionField , self ) . __init__ ( name = name , default = <NUM_LIT:0> , ddl = '<STR_LIT>' ) <EOL> _triggers = ( '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' ) <EOL> def _gen_sql ( table_name , mappings ) : <EOL> pk = None <EOL> sql = [ '<STR_LIT>' % table_name , '<STR_LIT>' % table_name ] <EOL> for f in sorted ( mappings . values ( ) , lambda x , y : cmp ( x . _order , y . _order ) ) : <EOL> if not hasattr ( f , '<STR_LIT>' ) : <EOL> raise StandardError ( '<STR_LIT>' % n ) <EOL> ddl = f . ddl <EOL> nullable = f . nullable <EOL> if f . primary_key : <EOL> pk = f . name <EOL> sql . append ( nullable and '<STR_LIT>' % ( f . name , ddl ) or '<STR_LIT>' % ( f . name , ddl ) ) <EOL> sql . append ( '<STR_LIT>' % pk ) <EOL> sql . append ( '<STR_LIT>' ) <EOL> return '<STR_LIT:\n>' . join ( sql ) <EOL> class ModelMetaclass ( type ) : <EOL> '''<STR_LIT>''' <EOL> def __new__ ( cls , name , bases , attrs ) : <EOL> if name == '<STR_LIT>' : <EOL> return type . __new__ ( cls , name , bases , attrs ) <EOL> if not hasattr ( cls , '<STR_LIT>' ) : <EOL> cls . subclasses = { } <EOL> if not name in cls . subclasses : <EOL> cls . subclasses [ name ] = name <EOL> else : <EOL> logging . warning ( '<STR_LIT>' % name ) <EOL> logging . info ( '<STR_LIT>' % name ) <EOL> mappings = dict ( ) <EOL> primary_key = None <EOL> for k , v in attrs . iteritems ( ) : <EOL> if isinstance ( v , Field ) : <EOL> if not v . name : <EOL> v . name = k <EOL> logging . info ( '<STR_LIT>' % ( k , v ) ) <EOL> if v . primary_key : <EOL> if primary_key : <EOL> raise TypeError ( '<STR_LIT>' % name ) <EOL> if v . updatable : <EOL> logging . warning ( '<STR_LIT>' ) <EOL> v . updatable = False <EOL> if v . nullable : <EOL> logging . warning ( '<STR_LIT>' ) <EOL> v . nullable = False <EOL> primary_key = v <EOL> mappings [ k ] = v <EOL> if not primary_key : <EOL> raise TypeError ( '<STR_LIT>' % name ) <EOL> for k in mappings . iterkeys ( ) : <EOL> attrs . pop ( k ) <EOL> if not '<STR_LIT>' in attrs : <EOL> attrs [ '<STR_LIT>' ] = name . lower ( ) <EOL> attrs [ '<STR_LIT>' ] = mappings <EOL> attrs [ '<STR_LIT>' ] = primary_key <EOL> def _sql ( self ) : <EOL> return _gen_sql ( attrs [ '<STR_LIT>' ] , mappings ) <EOL> attrs [ '<STR_LIT>' ] = _sql <EOL> for trigger in _triggers : <EOL> if not trigger in attrs : <EOL> attrs [ trigger ] = None <EOL> return type . __new__ ( cls , name , bases , attrs ) <EOL> class Model ( dict ) : <EOL> '''<STR_LIT>''' <EOL> __metaclass__ = ModelMetaclass <EOL> def __init__ ( self , ** kw ) : <EOL> super ( Model , self ) . __init__ ( ** kw ) <EOL> def __getattr__ ( self , key ) : <EOL> try : <EOL> return self [ key ] <EOL> except KeyError : <EOL> raise AttributeError ( r"<STR_LIT>" % key ) <EOL> def __setattr__ ( self , key , value ) : <EOL> self [ key ] = value <EOL> @ classmethod <EOL> def get_by_id ( cls , pk ) : <EOL> d = select_one ( '<STR_LIT>' % ( cls . __table__ , cls . __primary_key__ . name ) , pk ) <EOL> return cls ( ** d ) if d else None <EOL> @ classmethod <EOL> def select_one ( cls , where , * args ) : <EOL> '''<STR_LIT>''' <EOL> d = select_one ( '<STR_LIT>' % ( cls . __table__ , where ) , * args ) if where else select_one ( '<STR_LIT>' % cls . __table__ ) <EOL> return cls ( ** d ) if d else None <EOL> @ classmethod <EOL> def select ( cls , where , * args ) : <EOL> '''<STR_LIT>''' <EOL> L = select ( '<STR_LIT>' % ( cls . __table__ , where ) , * args ) if where else select ( '<STR_LIT>' % cls . __table__ ) <EOL> return [ cls ( ** d ) for d in L ] <EOL> @ classmethod <EOL> def count ( cls , where , * args ) : <EOL> '''<STR_LIT>''' <EOL> return select_int ( '<STR_LIT>' % ( cls . __primary_key__ . name , cls . __table__ , where ) , * args ) if where else select_int ( '<STR_LIT>' % ( cls . __primary_key__ . name , cls . __table__ ) ) <EOL> def update ( self ) : <EOL> self . pre_update and self . pre_update ( ) <EOL> kw = { } <EOL> for k , v in self . __mappings__ . iteritems ( ) : <EOL> if v . updatable : <EOL> if hasattr ( self , k ) : <EOL> arg = getattr ( self , k ) <EOL> else : <EOL> arg = v . default <EOL> setattr ( self , k , arg ) <EOL> kw [ k ] = arg <EOL> pk = self . __primary_key__ . name <EOL> update_kw ( self . __table__ , '<STR_LIT>' % pk , getattr ( self , pk ) , ** kw ) <EOL> return self <EOL> def delete ( self ) : <EOL> self . pre_delete and self . pre_delete ( ) <EOL> pk = self . __primary_key__ . name <EOL> args = ( getattr ( self , pk ) , ) <EOL> _update ( '<STR_LIT>' % ( self . __table__ , pk ) , args ) <EOL> return self <EOL> def insert ( self ) : <EOL> self . pre_insert and self . pre_insert ( ) <EOL> fields = [ ] <EOL> params = [ ] <EOL> args = [ ] <EOL> for k , v in self . __mappings__ . iteritems ( ) : <EOL> if v . insertable : <EOL> fields . append ( v . name ) <EOL> params . append ( '<STR_LIT:?>' ) <EOL> arg = getattr ( self , k , None ) <EOL> if arg is None : <EOL> arg = v . default <EOL> setattr ( self , k , arg ) <EOL> args . append ( arg ) <EOL> _update ( '<STR_LIT>' % ( self . __table__ , '<STR_LIT:U+002C>' . join ( fields ) , '<STR_LIT:U+002C>' . join ( params ) ) , args ) <EOL> return self <EOL> if __name__ == '<STR_LIT:__main__>' : <EOL> logging . basicConfig ( level = logging . DEBUG ) <EOL> sys . path . append ( '<STR_LIT:.>' ) <EOL> dbpath = os . path . join ( os . path . dirname ( os . path . dirname ( os . path . abspath ( __file__ ) ) ) , '<STR_LIT>' ) <EOL> print ( dbpath ) <EOL> if os . path . isfile ( dbpath ) : <EOL> os . remove ( dbpath ) <EOL> init ( '<STR_LIT>' , dbpath , '<STR_LIT>' ) <EOL> update ( '<STR_LIT>' ) <EOL> import doctest <EOL> doctest . testmod ( ) <EOL> os . remove ( dbpath ) </s>
