Advertisement
Guest User

Untitled

a guest
Jul 15th, 2016
139
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 3.78 KB | None | 0 0
  1. from sqlalchemy.ext.automap import automap_base
  2. from sqlalchemy.orm import Session
  3. from sqlalchemy import create_engine, MetaData
  4.  
  5. from collections import namedtuple
  6.  
  7. import hashlib
  8. import os
  9. import pickle
  10.  
  11. __ALL__ = ['describe', 'Connection', 'Database']
  12.  
  13.  
  14. Connection = namedtuple('Connection', ['host', 'port', 'db', 'user', 'password'])
  15.  
  16.  
  17. def describe(obj, cols=None):
  18. ret = {}
  19. reserved = ['metadata', 'prepare']
  20. if cols is None:
  21. for prop in dir(obj):
  22. if prop.startswith('_') or prop in reserved:
  23. continue
  24. ret[prop] = getattr(obj, prop)
  25. return ret
  26.  
  27.  
  28. class Models:
  29. def __init__(self, classes):
  30. self._klasses = []
  31. for name, klass in classes:
  32. self._klasses.append(klass)
  33. setattr(self, name, klass)
  34.  
  35. def __iter__(self):
  36. yield from self._klasses
  37.  
  38.  
  39. class Database:
  40. __VERSION__ = '0.1'
  41. _CACHE_DIR = os.path.expanduser('~/.dbconnect')
  42.  
  43. @classmethod
  44. def auto_discover(cls, index, **kwargs):
  45. return cls(*cls._get_connections()[index], **kwargs)
  46.  
  47. @classmethod
  48. def _get_connections(cls):
  49. connections = []
  50. with open(os.path.expanduser('~/.pgpass')) as pgpassfile:
  51. for line in pgpassfile:
  52. cleaned = line.strip()
  53. connections.append(Connection(*cleaned.split(':')))
  54. return connections
  55.  
  56. @classmethod
  57. def _get_hash(cls, s):
  58. return hashlib.sha1(''.join((s, cls.__VERSION__)).encode('utf-8')).hexdigest()
  59.  
  60. def _get_cached_tables(self, connection_hash):
  61. try:
  62. with open(os.path.join(self._CACHE_DIR, connection_hash), 'rb') as f:
  63. return pickle.load(f)
  64. except (FileNotFoundError, EOFError):
  65. return None
  66.  
  67. def _cache_tables(self, connection_hash, tables):
  68. with open(os.path.join(self._CACHE_DIR, connection_hash), 'wb') as f:
  69. f.write(pickle.dumps(tables))
  70.  
  71. def __init__(self, host, port, db, user, password, debug=False, cached=True):
  72. # pre-connection stuff
  73. self._setup_cache_dir()
  74. self.debug = debug
  75.  
  76. self.db = 'postgres://{user}:{password}@{host}:{port}/{db}'.format(
  77. user=user, password=password, host=host, port=port, db=db)
  78.  
  79. connection_hash = self._get_hash(self.db)
  80. if cached is True:
  81. cached_tables = self._get_cached_tables(connection_hash)
  82. if cached_tables:
  83. session, models = self._init_existing(cached_tables)
  84. else:
  85. # duplicate code
  86. session, models = self._init_automap()
  87. self._cache_tables(connection_hash, [m.__table__ for m in models])
  88. else:
  89. # duplicate code
  90. session, models = self._init_automap()
  91. self._cache_tables(connection_hash, [m.__table__ for m in models])
  92. self.s, self.m = session, models
  93.  
  94. def _init_automap(self):
  95. """Use sqlalchemy automap to reflect tables
  96. """
  97. engine = create_engine(self.db, isolation_level="AUTOCOMMIT", echo=self.debug)
  98. base = automap_base()
  99. base.prepare(engine, reflect=True)
  100. return Session(engine), Models(base.classes.items())
  101.  
  102. def _init_existing(self, tables):
  103. """Use pickled model tables for sqlalchemy
  104. """
  105. engine = create_engine(self.db, isolation_level="AUTOCOMMIT", echo=self.debug)
  106. metadata = MetaData()
  107. _tables = [t.tometadata(metadata) for t in tables] # noqa
  108. base = automap_base(metadata=metadata)
  109. base.prepare()
  110. return Session(engine), Models(base.classes.items())
  111.  
  112. def _setup_cache_dir(self):
  113. """Creates table cache directory if it doesn't already exist
  114. """
  115. try:
  116. os.mkdir(self._CACHE_DIR)
  117. except FileExistsError:
  118. pass
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement