velocity-python 0.0.109__py3-none-any.whl → 0.0.161__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.
- velocity/__init__.py +3 -1
- velocity/app/orders.py +3 -4
- velocity/app/tests/__init__.py +1 -0
- velocity/app/tests/test_email_processing.py +112 -0
- velocity/app/tests/test_payment_profile_sorting.py +191 -0
- velocity/app/tests/test_spreadsheet_functions.py +124 -0
- velocity/aws/__init__.py +3 -0
- velocity/aws/amplify.py +10 -6
- velocity/aws/handlers/__init__.py +2 -0
- velocity/aws/handlers/base_handler.py +248 -0
- velocity/aws/handlers/context.py +251 -2
- velocity/aws/handlers/exceptions.py +16 -0
- velocity/aws/handlers/lambda_handler.py +24 -85
- velocity/aws/handlers/mixins/__init__.py +16 -0
- velocity/aws/handlers/mixins/activity_tracker.py +181 -0
- velocity/aws/handlers/mixins/aws_session_mixin.py +192 -0
- velocity/aws/handlers/mixins/error_handler.py +192 -0
- velocity/aws/handlers/mixins/legacy_mixin.py +53 -0
- velocity/aws/handlers/mixins/standard_mixin.py +73 -0
- velocity/aws/handlers/response.py +1 -1
- velocity/aws/handlers/sqs_handler.py +28 -143
- velocity/aws/tests/__init__.py +1 -0
- velocity/aws/tests/test_lambda_handler_json_serialization.py +120 -0
- velocity/aws/tests/test_response.py +163 -0
- velocity/db/__init__.py +16 -4
- velocity/db/core/decorators.py +48 -13
- velocity/db/core/engine.py +187 -840
- velocity/db/core/result.py +33 -25
- velocity/db/core/row.py +15 -3
- velocity/db/core/table.py +493 -50
- velocity/db/core/transaction.py +28 -15
- velocity/db/exceptions.py +42 -18
- velocity/db/servers/base/__init__.py +9 -0
- velocity/db/servers/base/initializer.py +70 -0
- velocity/db/servers/base/operators.py +98 -0
- velocity/db/servers/base/sql.py +503 -0
- velocity/db/servers/base/types.py +135 -0
- velocity/db/servers/mysql/__init__.py +73 -0
- velocity/db/servers/mysql/operators.py +54 -0
- velocity/db/servers/{mysql_reserved.py → mysql/reserved.py} +2 -14
- velocity/db/servers/mysql/sql.py +718 -0
- velocity/db/servers/mysql/types.py +107 -0
- velocity/db/servers/postgres/__init__.py +59 -11
- velocity/db/servers/postgres/operators.py +34 -0
- velocity/db/servers/postgres/sql.py +474 -120
- velocity/db/servers/postgres/types.py +88 -2
- velocity/db/servers/sqlite/__init__.py +61 -0
- velocity/db/servers/sqlite/operators.py +52 -0
- velocity/db/servers/sqlite/reserved.py +20 -0
- velocity/db/servers/sqlite/sql.py +677 -0
- velocity/db/servers/sqlite/types.py +92 -0
- velocity/db/servers/sqlserver/__init__.py +73 -0
- velocity/db/servers/sqlserver/operators.py +47 -0
- velocity/db/servers/sqlserver/reserved.py +32 -0
- velocity/db/servers/sqlserver/sql.py +805 -0
- velocity/db/servers/sqlserver/types.py +114 -0
- velocity/db/servers/tablehelper.py +117 -91
- velocity/db/tests/__init__.py +1 -0
- velocity/db/tests/common_db_test.py +0 -0
- velocity/db/tests/postgres/__init__.py +1 -0
- velocity/db/tests/postgres/common.py +49 -0
- velocity/db/tests/postgres/test_column.py +29 -0
- velocity/db/tests/postgres/test_connections.py +25 -0
- velocity/db/tests/postgres/test_database.py +21 -0
- velocity/db/tests/postgres/test_engine.py +205 -0
- velocity/db/tests/postgres/test_general_usage.py +88 -0
- velocity/db/tests/postgres/test_imports.py +8 -0
- velocity/db/tests/postgres/test_result.py +19 -0
- velocity/db/tests/postgres/test_row.py +137 -0
- velocity/db/tests/postgres/test_row_comprehensive.py +720 -0
- velocity/db/tests/postgres/test_schema_locking.py +335 -0
- velocity/db/tests/postgres/test_schema_locking_unit.py +115 -0
- velocity/db/tests/postgres/test_sequence.py +34 -0
- velocity/db/tests/postgres/test_sql_comprehensive.py +462 -0
- velocity/db/tests/postgres/test_table.py +101 -0
- velocity/db/tests/postgres/test_table_comprehensive.py +646 -0
- velocity/db/tests/postgres/test_transaction.py +106 -0
- velocity/db/tests/sql/__init__.py +1 -0
- velocity/db/tests/sql/common.py +177 -0
- velocity/db/tests/sql/test_postgres_select_advanced.py +285 -0
- velocity/db/tests/sql/test_postgres_select_variances.py +517 -0
- velocity/db/tests/test_cursor_rowcount_fix.py +150 -0
- velocity/db/tests/test_db_utils.py +270 -0
- velocity/db/tests/test_postgres.py +448 -0
- velocity/db/tests/test_postgres_unchanged.py +81 -0
- velocity/db/tests/test_process_error_robustness.py +292 -0
- velocity/db/tests/test_result_caching.py +279 -0
- velocity/db/tests/test_result_sql_aware.py +117 -0
- velocity/db/tests/test_row_get_missing_column.py +72 -0
- velocity/db/tests/test_schema_locking_initializers.py +226 -0
- velocity/db/tests/test_schema_locking_simple.py +97 -0
- velocity/db/tests/test_sql_builder.py +165 -0
- velocity/db/tests/test_tablehelper.py +486 -0
- velocity/db/utils.py +129 -51
- velocity/misc/conv/__init__.py +2 -0
- velocity/misc/conv/iconv.py +5 -4
- velocity/misc/export.py +1 -4
- velocity/misc/merge.py +1 -1
- velocity/misc/tests/__init__.py +1 -0
- velocity/misc/tests/test_db.py +90 -0
- velocity/misc/tests/test_fix.py +78 -0
- velocity/misc/tests/test_format.py +64 -0
- velocity/misc/tests/test_iconv.py +203 -0
- velocity/misc/tests/test_merge.py +82 -0
- velocity/misc/tests/test_oconv.py +144 -0
- velocity/misc/tests/test_original_error.py +52 -0
- velocity/misc/tests/test_timer.py +74 -0
- velocity/misc/tools.py +0 -1
- {velocity_python-0.0.109.dist-info → velocity_python-0.0.161.dist-info}/METADATA +2 -2
- velocity_python-0.0.161.dist-info/RECORD +129 -0
- velocity/db/core/exceptions.py +0 -70
- velocity/db/servers/mysql.py +0 -641
- velocity/db/servers/sqlite.py +0 -968
- velocity/db/servers/sqlite_reserved.py +0 -208
- velocity/db/servers/sqlserver.py +0 -921
- velocity/db/servers/sqlserver_reserved.py +0 -314
- velocity_python-0.0.109.dist-info/RECORD +0 -56
- {velocity_python-0.0.109.dist-info → velocity_python-0.0.161.dist-info}/WHEEL +0 -0
- {velocity_python-0.0.109.dist-info → velocity_python-0.0.161.dist-info}/licenses/LICENSE +0 -0
- {velocity_python-0.0.109.dist-info → velocity_python-0.0.161.dist-info}/top_level.txt +0 -0
velocity/db/core/result.py
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import datetime
|
|
2
|
-
import decimal
|
|
3
1
|
from velocity.misc.format import to_json
|
|
4
2
|
|
|
5
3
|
|
|
@@ -7,14 +5,14 @@ class Result:
|
|
|
7
5
|
"""
|
|
8
6
|
Wraps a database cursor to provide various convenience transformations
|
|
9
7
|
(dict, list, tuple, etc.) and helps iterate over query results.
|
|
10
|
-
|
|
8
|
+
|
|
11
9
|
Features:
|
|
12
10
|
- Pre-fetches first row for immediate boolean evaluation
|
|
13
11
|
- Boolean state changes as rows are consumed: bool(result) tells you if MORE rows are available
|
|
14
12
|
- Supports __bool__, is_empty(), has_results() for checking remaining results
|
|
15
13
|
- Efficient iteration without unnecessary fetchall() calls
|
|
16
14
|
- Caches next row to maintain accurate state without redundant database calls
|
|
17
|
-
|
|
15
|
+
|
|
18
16
|
Boolean Behavior:
|
|
19
17
|
- Initially: bool(result) = True if query returned any rows
|
|
20
18
|
- After each row: bool(result) = True if more rows are available to fetch
|
|
@@ -29,15 +27,15 @@ class Result:
|
|
|
29
27
|
description = getattr(cursor, "description", []) or []
|
|
30
28
|
self._headers = []
|
|
31
29
|
for col in description:
|
|
32
|
-
if hasattr(col,
|
|
30
|
+
if hasattr(col, "__getitem__"): # Tuple-like (col[0])
|
|
33
31
|
self._headers.append(col[0].lower())
|
|
34
|
-
elif hasattr(col,
|
|
32
|
+
elif hasattr(col, "name"): # Object with name attribute
|
|
35
33
|
self._headers.append(col.name.lower())
|
|
36
34
|
else:
|
|
37
|
-
self._headers.append(f
|
|
35
|
+
self._headers.append(f"column_{len(self._headers)}")
|
|
38
36
|
except (AttributeError, TypeError, IndexError):
|
|
39
37
|
self._headers = []
|
|
40
|
-
|
|
38
|
+
|
|
41
39
|
self.__as_strings = False
|
|
42
40
|
self.__enumerate = False
|
|
43
41
|
self.__count = -1
|
|
@@ -49,7 +47,7 @@ class Result:
|
|
|
49
47
|
self._cached_first_row = None
|
|
50
48
|
self._first_row_fetched = False
|
|
51
49
|
self._exhausted = False
|
|
52
|
-
|
|
50
|
+
|
|
53
51
|
# Pre-fetch the first row to enable immediate boolean evaluation
|
|
54
52
|
self._fetch_first_row()
|
|
55
53
|
|
|
@@ -60,14 +58,16 @@ class Result:
|
|
|
60
58
|
"""
|
|
61
59
|
if self._first_row_fetched or not self._cursor:
|
|
62
60
|
return
|
|
63
|
-
|
|
61
|
+
|
|
64
62
|
# Don't try to fetch from INSERT/UPDATE/DELETE operations
|
|
65
63
|
# These operations don't return rows, only rowcount
|
|
66
|
-
if self.__sql and self.__sql.strip().upper().startswith(
|
|
64
|
+
if self.__sql and self.__sql.strip().upper().startswith(
|
|
65
|
+
("INSERT", "UPDATE", "DELETE", "TRUNCATE")
|
|
66
|
+
):
|
|
67
67
|
self._exhausted = True
|
|
68
68
|
self._first_row_fetched = True
|
|
69
69
|
return
|
|
70
|
-
|
|
70
|
+
|
|
71
71
|
try:
|
|
72
72
|
raw_row = self._cursor.fetchone()
|
|
73
73
|
if raw_row:
|
|
@@ -109,7 +109,9 @@ class Result:
|
|
|
109
109
|
Return True if there are more rows available to fetch.
|
|
110
110
|
This is based on whether we have a cached row or the cursor isn't exhausted.
|
|
111
111
|
"""
|
|
112
|
-
return self._cached_first_row is not None or (
|
|
112
|
+
return self._cached_first_row is not None or (
|
|
113
|
+
not self._exhausted and self._cursor
|
|
114
|
+
)
|
|
113
115
|
|
|
114
116
|
def __next__(self):
|
|
115
117
|
"""
|
|
@@ -127,7 +129,7 @@ class Result:
|
|
|
127
129
|
if not row:
|
|
128
130
|
self._exhausted = True
|
|
129
131
|
raise StopIteration
|
|
130
|
-
# Try to pre-fetch the next row to update our state
|
|
132
|
+
# Try to pre-fetch the next row to update our state
|
|
131
133
|
self._try_cache_next_row()
|
|
132
134
|
except Exception as e:
|
|
133
135
|
# Handle cursor errors (e.g., closed cursor)
|
|
@@ -154,7 +156,7 @@ class Result:
|
|
|
154
156
|
"""
|
|
155
157
|
if not self._cursor or self._cached_first_row is not None:
|
|
156
158
|
return
|
|
157
|
-
|
|
159
|
+
|
|
158
160
|
try:
|
|
159
161
|
next_row = self._cursor.fetchone()
|
|
160
162
|
if next_row:
|
|
@@ -214,18 +216,22 @@ class Result:
|
|
|
214
216
|
"""
|
|
215
217
|
if not self.__columns and self._cursor and hasattr(self._cursor, "description"):
|
|
216
218
|
for column in self._cursor.description:
|
|
217
|
-
data = {
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
|
|
219
|
+
data = {"type_name": "unknown"} # Default value
|
|
220
|
+
|
|
221
221
|
# Try to get type information (PostgreSQL specific)
|
|
222
222
|
try:
|
|
223
|
-
if
|
|
224
|
-
|
|
223
|
+
if (
|
|
224
|
+
hasattr(column, "type_code")
|
|
225
|
+
and self.__tx
|
|
226
|
+
and hasattr(self.__tx, "pg_types")
|
|
227
|
+
):
|
|
228
|
+
data["type_name"] = self.__tx.pg_types.get(
|
|
229
|
+
column.type_code, "unknown"
|
|
230
|
+
)
|
|
225
231
|
except (AttributeError, KeyError):
|
|
226
232
|
# Keep default value
|
|
227
233
|
pass
|
|
228
|
-
|
|
234
|
+
|
|
229
235
|
# Get all other column attributes safely
|
|
230
236
|
for key in dir(column):
|
|
231
237
|
if not key.startswith("__"):
|
|
@@ -234,8 +240,8 @@ class Result:
|
|
|
234
240
|
except (AttributeError, TypeError):
|
|
235
241
|
# Skip attributes that can't be accessed
|
|
236
242
|
continue
|
|
237
|
-
|
|
238
|
-
column_name = getattr(column,
|
|
243
|
+
|
|
244
|
+
column_name = getattr(column, "name", f"column_{len(self.__columns)}")
|
|
239
245
|
self.__columns[column_name] = data
|
|
240
246
|
return self.__columns
|
|
241
247
|
|
|
@@ -343,7 +349,9 @@ class Result:
|
|
|
343
349
|
return row
|
|
344
350
|
except StopIteration:
|
|
345
351
|
return default
|
|
346
|
-
|
|
352
|
+
|
|
353
|
+
one_or_none = one
|
|
354
|
+
|
|
347
355
|
def get_table_data(self, headers=True):
|
|
348
356
|
"""
|
|
349
357
|
Builds a two-dimensional list: first row is column headers, subsequent rows are data.
|
velocity/db/core/row.py
CHANGED
|
@@ -44,7 +44,12 @@ class Row:
|
|
|
44
44
|
def __setitem__(self, key, val):
|
|
45
45
|
if key in self.pk:
|
|
46
46
|
raise Exception("Cannot update a primary key.")
|
|
47
|
-
self.table
|
|
47
|
+
if hasattr(self.table, "updins"):
|
|
48
|
+
self.table.updins({key: val}, pk=self.pk)
|
|
49
|
+
elif hasattr(self.table, "upsert"):
|
|
50
|
+
self.table.upsert({key: val}, pk=self.pk)
|
|
51
|
+
else:
|
|
52
|
+
self.table.update({key: val}, pk=self.pk)
|
|
48
53
|
|
|
49
54
|
def __delitem__(self, key):
|
|
50
55
|
if key in self.pk:
|
|
@@ -97,7 +102,9 @@ class Row:
|
|
|
97
102
|
except Exception as e:
|
|
98
103
|
# Check if the error message indicates a missing column
|
|
99
104
|
error_msg = str(e).lower()
|
|
100
|
-
if
|
|
105
|
+
if "column" in error_msg and (
|
|
106
|
+
"does not exist" in error_msg or "not found" in error_msg
|
|
107
|
+
):
|
|
101
108
|
return failobj
|
|
102
109
|
# Re-raise other exceptions
|
|
103
110
|
raise
|
|
@@ -119,7 +126,12 @@ class Row:
|
|
|
119
126
|
if kwds:
|
|
120
127
|
data.update(kwds)
|
|
121
128
|
if data:
|
|
122
|
-
self.table
|
|
129
|
+
if hasattr(self.table, "updins"):
|
|
130
|
+
self.table.updins(data, pk=self.pk)
|
|
131
|
+
elif hasattr(self.table, "upsert"):
|
|
132
|
+
self.table.upsert(data, pk=self.pk)
|
|
133
|
+
else:
|
|
134
|
+
self.table.update(data, pk=self.pk)
|
|
123
135
|
return self
|
|
124
136
|
|
|
125
137
|
def __cmp__(self, other):
|