-
Notifications
You must be signed in to change notification settings - Fork 5
/
utils.py
190 lines (136 loc) · 5.9 KB
/
utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
# Andre Anjos <andre.anjos@idiap.ch>
# Thu 12 May 08:33:24 2011
"""Some utilities shared by many of the databases.
"""
import os
class null(object):
"""A look-alike stream that discards the input"""
def write(self, s):
"""Writes contents of string ``s`` on this stream"""
pass
def flush(self):
"""Flushes the stream"""
pass
def apsw_is_available():
"""Checks lock-ability for SQLite on the current file system"""
try:
import apsw #another python sqlite wrapper (maybe supports URIs)
except ImportError:
return False
# if you got here, apsw is available, check we have matching versions w.r.t
# the sqlit3 module
import sqlite3
if apsw.sqlitelibversion() != sqlite3.sqlite_version:
return False
# if you get to this point, all seems OK
return True
class SQLiteConnector(object):
'''An object that handles the connection to SQLite databases.'''
@staticmethod
def filesystem_is_lockable(database):
"""Checks if the filesystem is lockable"""
from sqlite3 import connect
old = os.path.exists(database) #memorize if the database was already there
conn = connect(database)
retval = True
try:
conn.execute('PRAGMA synchronous = OFF')
except Exception:
retval = False
finally:
if not old and os.path.exists(database): os.unlink(database)
return retval
APSW_IS_AVAILABLE = apsw_is_available()
def __init__(self, filename, readonly=False, lock=None):
"""Initializes the connector
Keyword arguments
filename
The name of the file containing the SQLite database
readonly
Should I try and open the database in read-only mode?
lock
Any vfs name as output by apsw.vfsnames()
"""
self.readonly = readonly
self.vfs = lock
self.filename = filename
self.lockable = SQLiteConnector.filesystem_is_lockable(self.filename)
if (self.readonly or (self.vfs is not None)) and \
not self.APSW_IS_AVAILABLE and not self.lockable:
import warnings
warnings.warn('Got a request for an SQLite connection using APSW, but I cannot find an sqlite3-compatible installed version of that module (or the module is not installed at all). Furthermore, the place where the database is sitting ("%s") is on a filesystem that does **not** seem to support locks. I\'m returning a stock connection and hopping for the best.' % (filename,))
def __call__(self):
from sqlite3 import connect
if (self.readonly or (self.vfs is not None)) and self.APSW_IS_AVAILABLE:
# and not self.lockable
import apsw
if self.readonly: flags = apsw.SQLITE_OPEN_READONLY #1
else: flags = apsw.SQLITE_OPEN_READWRITE | apsw.SQLITE_OPEN_CREATE #2|4
apsw_con = apsw.Connection(self.filename, vfs=self.vfs, flags=flags)
return connect(apsw_con)
return connect(self.filename)
def create_engine(self, echo=False):
"""Returns an SQLAlchemy engine"""
from sqlalchemy import create_engine
return create_engine('sqlite://', creator=self, echo=echo)
def session(self, echo=False):
"""Returns an SQLAlchemy session"""
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind=self.create_engine(echo))
return Session()
def session(dbtype, dbfile, echo=False):
"""Creates a session to an SQLite database"""
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
url = connection_string(dbtype, dbfile)
engine = create_engine(url, echo=echo)
Session = sessionmaker(bind=engine)
return Session()
def session_try_readonly(dbtype, dbfile, echo=False):
"""Creates a read-only session to an SQLite database. If read-only sessions
are not supported by the underlying sqlite3 python DB driver, then a normal
session is returned. A warning is emitted in case the underlying filesystem
does not support locking properly.
Raises a NotImplementedError if the dbtype is not supported.
"""
if dbtype != 'sqlite':
raise NotImplementedError("Read-only sessions are only currently supported for SQLite databases")
connector = SQLiteConnector(dbfile, readonly=True, lock='unix-none')
return connector.session(echo=echo)
def create_engine_try_nolock(dbtype, dbfile, echo=False):
"""Creates an engine connected to an SQLite database with no locks. If
engines without locks are not supported by the underlying sqlite3 python DB
driver, then a normal engine is returned. A warning is emitted if the
underlying filesystem does not support locking properly in this case.
Raises a NotImplementedError if the dbtype is not supported.
"""
if dbtype != 'sqlite':
raise NotImplementedError("Unlocked engines are only currently supported for SQLite databases")
connector = SQLiteConnector(dbfile, lock='unix-none')
return connector.create_engine(echo=echo)
def session_try_nolock(dbtype, dbfile, echo=False):
"""Creates a session to an SQLite database with no locks. If sessions without
locks are not supported by the underlying sqlite3 python DB driver, then a
normal session is returned. A warning is emitted if the underlying filesystem
does not support locking properly in this case.
Raises a NotImplementedError if the dbtype is not supported.
"""
if dbtype != 'sqlite':
raise NotImplementedError("Unlocked sessions are only currently supported for SQLite databases")
connector = SQLiteConnector(dbfile, lock='unix-none')
return connector.session(echo=echo)
def connection_string(dbtype, dbfile, opts={}):
"""Returns a connection string for supported platforms
Keyword parameters
dbtype
The type of database (only 'sqlite' is supported for the time being)
dbfile
The location of the file to be used
"""
from sqlalchemy.engine.url import URL
return URL(dbtype, database=dbfile)
# import the create_directories_save function from bob.io
# (it was moved there and renamed)
from ..io import create_directories_save as makedirs_safe