velocity-python 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of velocity-python might be problematic. Click here for more details.
- velocity/__init__.py +3 -0
- velocity/aws/__init__.py +24 -0
- velocity/aws/context.py +0 -0
- velocity/aws/handlers/__init__.py +2 -0
- velocity/aws/handlers/lambda_handler.py +123 -0
- velocity/aws/handlers/sqs_handler.py +48 -0
- velocity/db/__init__.py +5 -0
- velocity/db/core/__init__.py +0 -0
- velocity/db/core/column.py +202 -0
- velocity/db/core/database.py +65 -0
- velocity/db/core/decorators.py +81 -0
- velocity/db/core/engine.py +367 -0
- velocity/db/core/exceptions.py +31 -0
- velocity/db/core/result.py +137 -0
- velocity/db/core/row.py +191 -0
- velocity/db/core/sequence.py +33 -0
- velocity/db/core/table.py +448 -0
- velocity/db/core/transaction.py +178 -0
- velocity/db/servers/__init__.py +0 -0
- velocity/db/servers/mysql.py +575 -0
- velocity/db/servers/postgres.py +1275 -0
- velocity/db/servers/sql.py +558 -0
- velocity/db/servers/sqlite.py +899 -0
- velocity/db/servers/sqlserver.py +821 -0
- velocity/misc/__init__.py +0 -0
- velocity/misc/conv.py +420 -0
- velocity/misc/db.py +85 -0
- velocity/misc/export.py +147 -0
- velocity/misc/format.py +81 -0
- velocity/misc/mail.py +67 -0
- velocity/misc/timer.py +27 -0
- velocity_python-0.0.1.dist-info/LICENSE +19 -0
- velocity_python-0.0.1.dist-info/METADATA +181 -0
- velocity_python-0.0.1.dist-info/RECORD +36 -0
- velocity_python-0.0.1.dist-info/WHEEL +5 -0
- velocity_python-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
from velocity.db import exceptions
|
|
2
|
+
from velocity.db.core.transaction import Transaction
|
|
3
|
+
|
|
4
|
+
from functools import wraps
|
|
5
|
+
import inspect, sys, re, traceback
|
|
6
|
+
|
|
7
|
+
class Engine(object):
|
|
8
|
+
MAX_RETRIES = 100
|
|
9
|
+
|
|
10
|
+
def __init__(self, driver, config, sql):
|
|
11
|
+
self.__config = config
|
|
12
|
+
self.__sql = sql
|
|
13
|
+
self.__driver = driver
|
|
14
|
+
|
|
15
|
+
def __str__(self):
|
|
16
|
+
return """[{}] engine({})""".format(self.sql.server, self.config)
|
|
17
|
+
|
|
18
|
+
def connect(self):
|
|
19
|
+
"""
|
|
20
|
+
Connects to the database and returns the connection object.
|
|
21
|
+
|
|
22
|
+
If the database is missing, it creates the database and then connects to it.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
conn: The connection object to the database.
|
|
26
|
+
"""
|
|
27
|
+
try:
|
|
28
|
+
conn = self.__connect()
|
|
29
|
+
except exceptions.DbDatabaseMissingError:
|
|
30
|
+
self.create_database()
|
|
31
|
+
conn = self.__connect()
|
|
32
|
+
if self.sql.server == 'SQLite3':
|
|
33
|
+
conn.isolation_level = None
|
|
34
|
+
return conn
|
|
35
|
+
|
|
36
|
+
def __connect(self):
|
|
37
|
+
"""
|
|
38
|
+
Connects to the database using the provided configuration.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
A connection object representing the connection to the database.
|
|
42
|
+
|
|
43
|
+
Raises:
|
|
44
|
+
Exception: If the configuration parameter is not handled properly.
|
|
45
|
+
ProcessError is called to handle other exceptions.
|
|
46
|
+
"""
|
|
47
|
+
try:
|
|
48
|
+
if isinstance(self.config, dict):
|
|
49
|
+
return self.driver.connect(**self.config)
|
|
50
|
+
elif isinstance(self.config, (tuple, list)):
|
|
51
|
+
return self.driver.connect(*self.config)
|
|
52
|
+
elif isinstance(self.config, str):
|
|
53
|
+
return self.driver.connect(self.config)
|
|
54
|
+
else:
|
|
55
|
+
raise Exception("Unhandled configuration parameter")
|
|
56
|
+
except:
|
|
57
|
+
self.ProcessError()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def transaction(self, func_or_cls=None):
|
|
61
|
+
"""
|
|
62
|
+
Decorator for defining a transaction. Use this to wrap a function, method, or class to automatically
|
|
63
|
+
start a transaction if necessary. If the function, method or class is called with a `tx` keyword argument,
|
|
64
|
+
it will use that transaction object instead of creating a new one. If the function, method or class
|
|
65
|
+
is called with a `tx` positional argument, it will use that transaction object instead of creating a new one.
|
|
66
|
+
If the function, method or class is called with a `tx` positional argument and a `tx` keyword argument, it will use the positional
|
|
67
|
+
argument and ignore the keyword argument. If the function, method or class is called without a `tx` argument,
|
|
68
|
+
it will create a new transaction object and use that.
|
|
69
|
+
Args:
|
|
70
|
+
func_or_cls: The function or class to be decorated.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
If `func_or_cls` is a function or method, returns a wrapped version of the function or method that
|
|
74
|
+
automatically starts a transaction if necessary. If `func_or_cls` is a class, returns a subclass of
|
|
75
|
+
`func_or_cls` that wraps all its methods with the transaction decorator.
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
If `func_or_cls` is not provided, returns a new `Transaction` object associated with the engine.
|
|
80
|
+
"""
|
|
81
|
+
# If you are having trouble passing TWO transaction objects, for
|
|
82
|
+
# example as a source database to draw data from, pass the second as
|
|
83
|
+
# a keyword. For example:
|
|
84
|
+
# @engine.transaction
|
|
85
|
+
# def function(tx, src=src) <-- pass second as a kwd and tx will populate correctly.
|
|
86
|
+
# ...
|
|
87
|
+
#
|
|
88
|
+
engine = self
|
|
89
|
+
if inspect.isfunction(func_or_cls) \
|
|
90
|
+
or inspect.ismethod(func_or_cls):
|
|
91
|
+
@wraps(func_or_cls)
|
|
92
|
+
def NewFunction(*args, **kwds):
|
|
93
|
+
tx = None
|
|
94
|
+
names = list(inspect.signature(func_or_cls).parameters.keys())
|
|
95
|
+
if '_tx' in names:
|
|
96
|
+
raise NameError(f'In function named `{func_or_cls.__name__}` You may not name a paramater `_tx`')
|
|
97
|
+
if 'tx' not in names:
|
|
98
|
+
return func_or_cls(*args, **kwds)
|
|
99
|
+
elif 'tx' in kwds:
|
|
100
|
+
if isinstance(kwds['tx'], Transaction):
|
|
101
|
+
tx = kwds['tx']
|
|
102
|
+
else:
|
|
103
|
+
raise TypeError(f'In function named `{func_or_cls.__name__}` keyword `tx` must be a Transaction object')
|
|
104
|
+
elif 'tx' in names:
|
|
105
|
+
pos = names.index('tx')
|
|
106
|
+
if len(args) > pos \
|
|
107
|
+
and isinstance(args[pos], Transaction):
|
|
108
|
+
tx = args[pos]
|
|
109
|
+
if tx:
|
|
110
|
+
return engine.exec_function(func_or_cls, tx, *args, **kwds)
|
|
111
|
+
else:
|
|
112
|
+
with Transaction(engine) as tx:
|
|
113
|
+
pos = names.index('tx')
|
|
114
|
+
args = list(args)
|
|
115
|
+
args.insert(pos, tx)
|
|
116
|
+
args = tuple(args)
|
|
117
|
+
return engine.exec_function(func_or_cls, tx, *args, **kwds)
|
|
118
|
+
return NewFunction
|
|
119
|
+
elif inspect.isclass(func_or_cls):
|
|
120
|
+
class NewCls(func_or_cls):
|
|
121
|
+
def __getattribute__(self, key):
|
|
122
|
+
attr = super(NewCls, self).__getattribute__(key)
|
|
123
|
+
if key in ['start_response']:
|
|
124
|
+
return attr
|
|
125
|
+
if inspect.ismethod(attr):
|
|
126
|
+
return engine.transaction(attr)
|
|
127
|
+
return attr
|
|
128
|
+
return NewCls
|
|
129
|
+
|
|
130
|
+
return Transaction(engine)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def exec_function(self, function, _tx, *args, **kwds):
|
|
134
|
+
"""
|
|
135
|
+
Executes the given function with the provided arguments and keyword arguments.
|
|
136
|
+
If there is no transaction object, it executes the function without a transaction.
|
|
137
|
+
|
|
138
|
+
If there is a transaction object, it executes the function within the transaction.
|
|
139
|
+
If the function raises a `DbRetryTransaction` exception, it rolls back the transaction and retries.
|
|
140
|
+
If the function raises a `DbLockTimeoutError` exception, it rolls back the transaction and retries.
|
|
141
|
+
If any other exception occurs, it raises the exception.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
function: The function to be executed.
|
|
145
|
+
tx: The transaction object to be passed to the function.
|
|
146
|
+
*args: Positional arguments to be passed to the function.
|
|
147
|
+
**kwds: Keyword arguments to be passed to the function.
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
The result of the function execution.
|
|
151
|
+
|
|
152
|
+
Raises:
|
|
153
|
+
DbRetryTransaction: If the maximum number of retries is exceeded.
|
|
154
|
+
DbLockTimeoutError: If the maximum number of retries is exceeded.
|
|
155
|
+
Any other exception raised by the function.
|
|
156
|
+
"""
|
|
157
|
+
retry_count = 0
|
|
158
|
+
tmout_count = 0
|
|
159
|
+
if _tx is None:
|
|
160
|
+
return function(*args, **kwds)
|
|
161
|
+
else:
|
|
162
|
+
while True:
|
|
163
|
+
try:
|
|
164
|
+
return function(*args, **kwds)
|
|
165
|
+
except exceptions.DbRetryTransaction as e:
|
|
166
|
+
if e.args and e.args[0]:
|
|
167
|
+
_tx.rollback()
|
|
168
|
+
continue
|
|
169
|
+
retry_count += 1
|
|
170
|
+
if retry_count > self.MAX_RETRIES:
|
|
171
|
+
raise
|
|
172
|
+
print('**Retry Transaction. Rollback and start over')
|
|
173
|
+
_tx.rollback()
|
|
174
|
+
continue
|
|
175
|
+
except exceptions.DbLockTimeoutError:
|
|
176
|
+
tmout_count += 1
|
|
177
|
+
if tmout_count > self.MAX_RETRIES:
|
|
178
|
+
raise
|
|
179
|
+
print('**DbLockTimeoutError. Rollback and start over')
|
|
180
|
+
_tx.rollback()
|
|
181
|
+
continue
|
|
182
|
+
except:
|
|
183
|
+
raise
|
|
184
|
+
|
|
185
|
+
@property
|
|
186
|
+
def driver(self):
|
|
187
|
+
return self.__driver
|
|
188
|
+
|
|
189
|
+
@property
|
|
190
|
+
def config(self):
|
|
191
|
+
return self.__config
|
|
192
|
+
|
|
193
|
+
@property
|
|
194
|
+
def sql(self):
|
|
195
|
+
return self.__sql
|
|
196
|
+
|
|
197
|
+
@property
|
|
198
|
+
def version(self):
|
|
199
|
+
with Transaction(self) as tx:
|
|
200
|
+
sql, vals = self.sql.version()
|
|
201
|
+
return tx.execute(sql, vals).scalar()
|
|
202
|
+
|
|
203
|
+
@property
|
|
204
|
+
def timestamp(self):
|
|
205
|
+
with Transaction(self) as tx:
|
|
206
|
+
sql, vals = self.sql.timestamp()
|
|
207
|
+
return tx.execute(sql, vals).scalar()
|
|
208
|
+
|
|
209
|
+
@property
|
|
210
|
+
def user(self):
|
|
211
|
+
with Transaction(self) as tx:
|
|
212
|
+
sql, vals = self.sql.user()
|
|
213
|
+
return tx.execute(sql, vals).scalar()
|
|
214
|
+
|
|
215
|
+
@property
|
|
216
|
+
def databases(self):
|
|
217
|
+
with Transaction(self) as tx:
|
|
218
|
+
sql, vals = self.sql.databases()
|
|
219
|
+
result = tx.execute(sql, vals)
|
|
220
|
+
return [x[0] for x in result.as_tuple()]
|
|
221
|
+
|
|
222
|
+
@property
|
|
223
|
+
def current_database(self):
|
|
224
|
+
with Transaction(self) as tx:
|
|
225
|
+
sql, vals = self.sql.current_database()
|
|
226
|
+
return tx.execute(sql, vals).scalar()
|
|
227
|
+
|
|
228
|
+
def create_database(self, name=None):
|
|
229
|
+
old = None
|
|
230
|
+
if name == None:
|
|
231
|
+
old = self.config['database']
|
|
232
|
+
self.set_config({'database':'postgres'})
|
|
233
|
+
name = old
|
|
234
|
+
with Transaction(self) as tx:
|
|
235
|
+
sql, vals = self.sql.create_database(name)
|
|
236
|
+
tx.execute(sql, vals, single=True)
|
|
237
|
+
if old:
|
|
238
|
+
self.set_config({'database':old})
|
|
239
|
+
return self
|
|
240
|
+
|
|
241
|
+
def switch_to_database(self, database):
|
|
242
|
+
conf = self.config
|
|
243
|
+
if 'database' in conf:
|
|
244
|
+
conf['database'] = database
|
|
245
|
+
if 'dbname' in conf:
|
|
246
|
+
conf['dbname'] = database
|
|
247
|
+
return self
|
|
248
|
+
|
|
249
|
+
def set_config(self, config):
|
|
250
|
+
self.config.update(config)
|
|
251
|
+
|
|
252
|
+
@property
|
|
253
|
+
def schemas(self):
|
|
254
|
+
with Transaction(self) as tx:
|
|
255
|
+
sql, vals = self.sql.schemas()
|
|
256
|
+
result = tx.execute(sql, vals)
|
|
257
|
+
return [x[0] for x in result.as_tuple()]
|
|
258
|
+
|
|
259
|
+
@property
|
|
260
|
+
def current_schema(self):
|
|
261
|
+
with Transaction(self) as tx:
|
|
262
|
+
sql, vals = self.sql.current_schema()
|
|
263
|
+
return tx.execute(sql, vals).scalar()
|
|
264
|
+
|
|
265
|
+
@property
|
|
266
|
+
def tables(self):
|
|
267
|
+
with Transaction(self) as tx:
|
|
268
|
+
sql, vals = self.sql.tables()
|
|
269
|
+
result = tx.execute(sql, vals)
|
|
270
|
+
return ["%s.%s" % x for x in result.as_tuple()]
|
|
271
|
+
|
|
272
|
+
@property
|
|
273
|
+
def views(self):
|
|
274
|
+
with Transaction(self) as tx:
|
|
275
|
+
sql, vals = self.sql.views()
|
|
276
|
+
result = tx.execute(sql, vals)
|
|
277
|
+
return ["%s.%s" % x for x in result.as_tuple()]
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def ProcessError(self, sql_stmt=None, sql_params=None):
|
|
281
|
+
sql = self.sql
|
|
282
|
+
e = sys.exc_info()[1]
|
|
283
|
+
msg = str(e).strip().lower()
|
|
284
|
+
if isinstance(e,exceptions.DbException):
|
|
285
|
+
raise
|
|
286
|
+
if hasattr(e,'pgcode'):
|
|
287
|
+
error_code = e.pgcode
|
|
288
|
+
error_mesg = e.pgerror
|
|
289
|
+
elif hasattr(e,'args') \
|
|
290
|
+
and isinstance(e.args,(tuple,list)) \
|
|
291
|
+
and len(e.args) > 1:
|
|
292
|
+
error_code = e[0]
|
|
293
|
+
error_mesg = e[1]
|
|
294
|
+
elif hasattr(e,'number') \
|
|
295
|
+
and hasattr(e,'text'):
|
|
296
|
+
error_code = e.number
|
|
297
|
+
error_mesg = e.text
|
|
298
|
+
elif hasattr(e,'args') \
|
|
299
|
+
and hasattr(e,'message'):
|
|
300
|
+
# SQLite3
|
|
301
|
+
error_code = None
|
|
302
|
+
error_mesg = e.message
|
|
303
|
+
else:
|
|
304
|
+
raise
|
|
305
|
+
if error_code in sql.ApplicationErrorCodes:
|
|
306
|
+
raise exceptions.DbApplicationError(e)
|
|
307
|
+
elif error_code in sql.ColumnMissingErrorCodes:
|
|
308
|
+
raise exceptions.DbColumnMissingError(e)
|
|
309
|
+
elif error_code in sql.TableMissingErrorCodes:
|
|
310
|
+
raise exceptions.DbTableMissingError(e)
|
|
311
|
+
elif error_code in sql.DatabaseMissingErrorCodes:
|
|
312
|
+
raise exceptions.DbDatabaseMissingError(e)
|
|
313
|
+
elif error_code in sql.ForeignKeyMissingErrorCodes:
|
|
314
|
+
raise exceptions.DbForeignKeyMissingError(e)
|
|
315
|
+
elif error_code in sql.TruncationErrorCodes:
|
|
316
|
+
raise exceptions.DbTruncationError(e)
|
|
317
|
+
elif error_code in sql.DataIntegrityErrorCodes:
|
|
318
|
+
raise exceptions.DbDataIntegrityError(e)
|
|
319
|
+
elif error_code in sql.ConnectionErrorCodes:
|
|
320
|
+
raise exceptions.DbConnectionError(e)
|
|
321
|
+
elif error_code in sql.DuplicateKeyErrorCodes:
|
|
322
|
+
raise exceptions.DbDuplicateKeyError(e)
|
|
323
|
+
elif re.search('key \(sys_id\)=\(\d+\) already exists.', msg, re.M):
|
|
324
|
+
raise exceptions.DbDuplicateKeyError(e)
|
|
325
|
+
elif error_code in sql.DatabaseObjectExistsErrorCodes:
|
|
326
|
+
raise exceptions.DbObjectExistsError(e)
|
|
327
|
+
elif error_code in sql.LockTimeoutErrorCodes:
|
|
328
|
+
raise exceptions.DbLockTimeoutError(e)
|
|
329
|
+
elif error_code in sql.RetryTransactionCodes:
|
|
330
|
+
raise exceptions.DbRetryTransaction(e)
|
|
331
|
+
elif re.findall('database.*does not exist', msg, re.M):
|
|
332
|
+
raise exceptions.DbDatabaseMissingError(e)
|
|
333
|
+
elif re.findall('no such database', msg, re.M):
|
|
334
|
+
raise exceptions.DbDatabaseMissingError(e)
|
|
335
|
+
elif re.findall('already exists', msg, re.M):
|
|
336
|
+
raise exceptions.DbObjectExistsError(e)
|
|
337
|
+
elif re.findall('server closed the connection unexpectedly', msg, re.M):
|
|
338
|
+
raise exceptions.DbConnectionError(e)
|
|
339
|
+
elif re.findall('no connection to the server', msg, re.M):
|
|
340
|
+
raise exceptions.DbConnectionError(e)
|
|
341
|
+
elif re.findall('connection timed out', msg, re.M):
|
|
342
|
+
raise exceptions.DbConnectionError(e)
|
|
343
|
+
elif re.findall('could not connect to server', msg, re.M):
|
|
344
|
+
raise exceptions.DbConnectionError(e)
|
|
345
|
+
elif re.findall('cannot connect to server', msg, re.M):
|
|
346
|
+
raise exceptions.DbConnectionError(e)
|
|
347
|
+
elif re.findall('connection already closed', msg, re.M):
|
|
348
|
+
raise exceptions.DbConnectionError(e)
|
|
349
|
+
elif re.findall('cursor already closed', msg, re.M):
|
|
350
|
+
raise exceptions.DbConnectionError(e)
|
|
351
|
+
# SQLite3 errors
|
|
352
|
+
elif 'no such table:' in msg:
|
|
353
|
+
raise exceptions.DbTableMissingError(e)
|
|
354
|
+
print("Unhandled/Unknown Error in connection.ProcessError")
|
|
355
|
+
print('EXC_TYPE = {}'.format(type(e)))
|
|
356
|
+
print('EXC_MSG = {}'.format(str(e).strip()))
|
|
357
|
+
print('ERROR_CODE = {}'.format(error_code))
|
|
358
|
+
print('ERROR_MSG = {}'.format(error_mesg))
|
|
359
|
+
if sql_stmt:
|
|
360
|
+
print("\n")
|
|
361
|
+
print("sql_stmt [velocity.db.engine]: {}".format(sql_stmt))
|
|
362
|
+
print("\n")
|
|
363
|
+
if sql_params:
|
|
364
|
+
print(sql_params)
|
|
365
|
+
print("\n")
|
|
366
|
+
traceback.print_exc()
|
|
367
|
+
raise
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
class DbException(Exception):
|
|
2
|
+
pass
|
|
3
|
+
|
|
4
|
+
class DbApplicationError(DbException):
|
|
5
|
+
pass
|
|
6
|
+
|
|
7
|
+
class DbForeignKeyMissingError(DbException):
|
|
8
|
+
pass
|
|
9
|
+
class DbDatabaseMissingError(DbException):
|
|
10
|
+
pass
|
|
11
|
+
class DbTableMissingError(DbException):
|
|
12
|
+
pass
|
|
13
|
+
class DbColumnMissingError(DbException):
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
class DbTruncationError(DbException):
|
|
17
|
+
pass
|
|
18
|
+
class DbConnectionError(DbException):
|
|
19
|
+
pass
|
|
20
|
+
class DbDuplicateKeyError(DbException):
|
|
21
|
+
pass
|
|
22
|
+
class DbObjectExistsError(DbException):
|
|
23
|
+
pass
|
|
24
|
+
class DbLockTimeoutError(DbException):
|
|
25
|
+
pass
|
|
26
|
+
class DbRetryTransaction(DbException):
|
|
27
|
+
pass
|
|
28
|
+
class DbDataIntegrityError(DbException):
|
|
29
|
+
pass
|
|
30
|
+
class DuplicateRowsFoundError(Exception):
|
|
31
|
+
pass
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
from velocity.misc.format import to_json
|
|
2
|
+
|
|
3
|
+
class Result(object):
|
|
4
|
+
def __init__(self, cursor=None):
|
|
5
|
+
self._cursor = cursor
|
|
6
|
+
if hasattr(cursor,'description') and cursor.description:
|
|
7
|
+
self._headers = [x[0].lower() for x in cursor.description]
|
|
8
|
+
else:
|
|
9
|
+
self._headers = []
|
|
10
|
+
self.as_dict()
|
|
11
|
+
self.__as_strings = False
|
|
12
|
+
self.__enumerate = False
|
|
13
|
+
self.__count = -1
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
def headers(self):
|
|
17
|
+
if not self._headers:
|
|
18
|
+
if self._cursor and hasattr(self._cursor, 'description'):
|
|
19
|
+
self._headers = [x[0].lower() for x in self._cursor.description]
|
|
20
|
+
return self._headers
|
|
21
|
+
|
|
22
|
+
def __str__(self):
|
|
23
|
+
return repr(self.all())
|
|
24
|
+
|
|
25
|
+
def __enter__(self):
|
|
26
|
+
return self
|
|
27
|
+
|
|
28
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
29
|
+
if not exc_type:
|
|
30
|
+
self.close()
|
|
31
|
+
|
|
32
|
+
def __next__(self):
|
|
33
|
+
if self._cursor:
|
|
34
|
+
row = self._cursor.fetchone()
|
|
35
|
+
if row:
|
|
36
|
+
if self.__as_strings:
|
|
37
|
+
row = ['' if x is None else str(x) for x in row]
|
|
38
|
+
if self.__enumerate:
|
|
39
|
+
self.__count += 1
|
|
40
|
+
return (self.__count, self.transform(row))
|
|
41
|
+
else:
|
|
42
|
+
return self.transform(row)
|
|
43
|
+
raise StopIteration
|
|
44
|
+
|
|
45
|
+
def batch(self, qty=1):
|
|
46
|
+
results = []
|
|
47
|
+
while True:
|
|
48
|
+
try:
|
|
49
|
+
results.append(next(self))
|
|
50
|
+
except StopIteration:
|
|
51
|
+
if results:
|
|
52
|
+
yield results
|
|
53
|
+
results = []
|
|
54
|
+
continue
|
|
55
|
+
raise
|
|
56
|
+
if len(results) == qty:
|
|
57
|
+
yield results
|
|
58
|
+
results = []
|
|
59
|
+
|
|
60
|
+
def all(self):
|
|
61
|
+
results = []
|
|
62
|
+
while True:
|
|
63
|
+
try:
|
|
64
|
+
results.append(next(self))
|
|
65
|
+
except StopIteration:
|
|
66
|
+
break
|
|
67
|
+
return results
|
|
68
|
+
|
|
69
|
+
def __iter__(self):
|
|
70
|
+
return self
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def cursor(self):
|
|
74
|
+
return self._cursor
|
|
75
|
+
|
|
76
|
+
def close(self):
|
|
77
|
+
self._cursor.close()
|
|
78
|
+
|
|
79
|
+
def as_dict(self):
|
|
80
|
+
self.transform = lambda row: dict(list(zip(self.headers,row)))
|
|
81
|
+
return self
|
|
82
|
+
|
|
83
|
+
def as_json(self):
|
|
84
|
+
self.transform = lambda row: to_json(dict(list(zip(self.headers,row))))
|
|
85
|
+
return self
|
|
86
|
+
|
|
87
|
+
def as_named_tuple(self):
|
|
88
|
+
self.transform = lambda row: list(zip(self.headers,row))
|
|
89
|
+
return self
|
|
90
|
+
|
|
91
|
+
def as_list(self):
|
|
92
|
+
self.transform = lambda row: list(row)
|
|
93
|
+
return self
|
|
94
|
+
|
|
95
|
+
def as_tuple(self):
|
|
96
|
+
self.transform = lambda row: row
|
|
97
|
+
return self
|
|
98
|
+
|
|
99
|
+
def as_simple_list(self, pos=0):
|
|
100
|
+
self.transform = lambda row: row[pos]
|
|
101
|
+
return self
|
|
102
|
+
|
|
103
|
+
def strings(self, as_strings=True):
|
|
104
|
+
self.__as_strings = as_strings
|
|
105
|
+
return self
|
|
106
|
+
|
|
107
|
+
def scalar(self, default=None):
|
|
108
|
+
if not self._cursor:
|
|
109
|
+
return None
|
|
110
|
+
val = self._cursor.fetchone()
|
|
111
|
+
self._cursor.fetchall()
|
|
112
|
+
return val[0] if val else default
|
|
113
|
+
|
|
114
|
+
def one(self,default=None):
|
|
115
|
+
try:
|
|
116
|
+
return next(self)
|
|
117
|
+
except StopIteration:
|
|
118
|
+
return default
|
|
119
|
+
finally:
|
|
120
|
+
if self._cursor:
|
|
121
|
+
self._cursor.fetchall()
|
|
122
|
+
|
|
123
|
+
def get_table_data(self, headers=True, strings=True):
|
|
124
|
+
self.as_list()
|
|
125
|
+
rows = []
|
|
126
|
+
for row in self:
|
|
127
|
+
rows.append(['' if x is None else str(x) for x in row])
|
|
128
|
+
if isinstance(headers,list):
|
|
129
|
+
rows.insert(0,[x.replace('_',' ').title() for x in headers])
|
|
130
|
+
elif headers:
|
|
131
|
+
rows.insert(0,[x.replace('_',' ').title() for x in self.headers])
|
|
132
|
+
return rows
|
|
133
|
+
|
|
134
|
+
def enum(self):
|
|
135
|
+
self.__enumerate = True
|
|
136
|
+
return self
|
|
137
|
+
enumerate = enum
|