No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

sqlwrapper.py 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. # -*- coding: utf-8 -*-
  2. import os
  3. import logging as logger
  4. import sqlalchemy as sqla
  5. from django.conf import settings
  6. #Logger config
  7. logger.getLogger().setLevel('DEBUG')
  8. #To be able to use dango confs
  9. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Lodel.settings")
  10. class SqlWrapper(object):
  11. """ A wrapper class to sqlalchemy
  12. Usefull to provide a standart API
  13. """
  14. ##Read Engine
  15. rengine = None
  16. ##Write Engine
  17. wengine = None
  18. ##Read connection
  19. rconn = None
  20. ##Write connection
  21. wconn = None
  22. ##Configuration dict alias for class access
  23. config=settings.LODEL2SQLWRAPPER
  24. ##SqlAlchemy logging
  25. sqla_logging = False
  26. def __init__(self, alchemy_logs=None):
  27. """ Instanciate a new SqlWrapper
  28. @param alchemy_logs bool: If true activate sqlalchemy logger
  29. """
  30. if (alchemy_logs != None and bool(alchemy_logs) != self.__class__.sqla_logging):
  31. #logging config changed for sqlalchemy
  32. self.__class__.restart()
  33. if not self.__class__.started():
  34. self.__class__.start()
  35. pass
  36. @classmethod
  37. def table(c, tname):
  38. """ Return a SqlAlchemy Table object
  39. @param o str: Table name
  40. @return a SqlAlchemy Table instance
  41. """
  42. if not isinstance(tname, str):
  43. raise TypeError("Excepting a str but got a "+str(type(name)))
  44. return sqla.Table(o, sqla.MetaData())
  45. @classmethod
  46. def connect(c,read = None):
  47. if read == None:
  48. return c.connect(True) and c.coonect(False)
  49. elif read:
  50. c.rconn = c.rengine.connect()
  51. else:
  52. c.wconn = c.wengine.connect()
  53. return True #TODO attention c'est pas checké...
  54. @classmethod
  55. def conn(c, read=True):
  56. if read:
  57. res = c.rconn
  58. else:
  59. res = c.wconn
  60. if res == None:
  61. if not c.connect(read):
  62. raise RuntimeError('Unable to connect to Db')
  63. return c.conn(read)
  64. return c.rconn
  65. @classmethod
  66. def rc(c): return c.conn(True)
  67. @classmethod
  68. def wc(c): return c.conn(False)
  69. @classmethod
  70. def start(c, sqlalogging = None):
  71. """ Load engines
  72. Open connections to databases
  73. @param sqlalogging bool: overwrite class parameter about sqlalchemy logging
  74. @return False if already started
  75. """
  76. c.checkConf()
  77. if c.started():
  78. logger.warning('Starting SqlWrapper but it is allready started')
  79. return False
  80. c.rengine = c._getEngine(read=True, sqlalogging=None)
  81. c.wengine = c._getEngine(read=False, sqlalogging=None)
  82. return True
  83. @classmethod
  84. def stop(c): c.rengine = c.wengine = None; pass
  85. @classmethod
  86. def restart(c): c.stop(); c.start(); pass
  87. @classmethod
  88. def started(c): return (c.rengine != None and c.rengine != None)
  89. @classmethod
  90. def _sqllog(c,sqlalogging = None):
  91. return bool(sqlalogging) if sqlalogging != None else c.sqla_logging
  92. @classmethod
  93. def _getEngine(c, read=True, sqlalogging = None):
  94. """ Return a sqlalchemy engine
  95. @param read bool: If True return the read engine, else
  96. return the write one
  97. @return a sqlachemy engine instance
  98. @todo Put the check on db config in SqlWrapper.checkConf()
  99. """
  100. #Loading confs
  101. connconf = 'dbread' if read else 'dbwrite'
  102. dbconf = connconf if connconf in c.config['db'] else 'default'
  103. if dbconf not in c.config['db']: #This check should not be here
  104. raise NameError('Configuration error no db "'+dbconf+'" in configuration files')
  105. cfg = c.config['db'][dbconf] #Database config
  106. edata = c.config['engines'][cfg['ENGINE']] #engine infos
  107. conn_str = cfg['ENGINE']+'+'+edata['driver']+'://'
  108. if cfg['ENGINE'] == 'sqlite':
  109. #Sqlite connection string
  110. conn_str = '%s+%s:///%s'%( cfg['ENGINE'],
  111. edata['driver'],
  112. cfg['NAME'])
  113. else:
  114. #Mysql and Postgres connection string
  115. user = cfg['USER']
  116. user += (':'+cfg['PASSWORD'] if 'PASSWORD' in cfg else '')
  117. if 'HOST' not in cfg:
  118. logger.info('Not HOST in configuration, using localhost')
  119. host = 'localhost'
  120. else:
  121. host = cfg['HOST']
  122. host += (':'+cfg['PORT'] if 'PORT' in cfg else '')
  123. conn_str = '%s+%s://'%(cfg['ENGINE'], edata['driver'])
  124. conn_str += '%s@%s/%s'%(user,host,cfg['NAME'])
  125. return sqla.create_engine(conn_str, encoding=edata['encoding'], echo=c._sqllog(sqlalogging))
  126. @classmethod
  127. def checkConf(c):
  128. """ Class method that check the configuration
  129. Configuration looks like
  130. - db (mandatory)
  131. - ENGINE (mandatory)
  132. - NAME (mandatory)
  133. - USER
  134. - PASSWORD
  135. - engines (mandatory)
  136. - driver (mandatory)
  137. - encoding (mandatory)
  138. - dbread (mandatory if no default db)
  139. - dbwrite (mandatory if no default db)
  140. """
  141. err = []
  142. if 'db' not in c.config:
  143. err.append('Missing "db" in configuration')
  144. else:
  145. if 'default' not in c.config['db']:
  146. if 'dbread' not in c.config:
  147. err.append('Missing "dbread" in configuration and no "default" db declared')
  148. if 'dbwrite' not in c.config:
  149. err.append('Missing "dbwrite" in configuration and no "default" db declared')
  150. for dbname in c.config['db']:
  151. db = c.config['db'][dbname]
  152. if 'ENGINE' not in db:
  153. err.append('Missing "ENGINE" in database "'+db+'"')
  154. else:
  155. if db['ENGINE'] != 'sqlite' and 'USER' not in db:
  156. err.append('Missing "USER" in database "'+db+'"')
  157. if 'NAME' not in db:
  158. err.append('Missing "NAME" in database "'+db+'"')
  159. if len(c.config['engines']) == 0:
  160. err.append('Missing "engines" in configuration')
  161. for ename in c.config['engines']:
  162. engine = c.config['engines'][ename]
  163. if 'driver' not in engine:
  164. err.append('Missing "driver" in database engine "'+ename+'"')
  165. if 'encoding' not in engine:
  166. err.append('Missing "encoding" in database engine "'+ename+'"')
  167. if len(err)>0:
  168. err_str = "\n"
  169. for e in err:
  170. err_str += "\t\t"+e+"\n"
  171. raise NameError('Configuration errors in LODEL2SQLWRAPPER:'+err_str)
  172. @property
  173. def cfg(self):
  174. """ Get the dict of options for the wrapper
  175. Its an alias to the classes property SqlWrapper.config
  176. @return a dict containing the Db settings"""
  177. return self.__class__.config