velocity-python 0.0.89__tar.gz → 0.0.90__tar.gz

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.

Files changed (71) hide show
  1. {velocity_python-0.0.89 → velocity_python-0.0.90}/PKG-INFO +1 -1
  2. {velocity_python-0.0.89 → velocity_python-0.0.90}/pyproject.toml +1 -1
  3. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/__init__.py +1 -1
  4. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/db/servers/postgres/sql.py +107 -34
  5. velocity_python-0.0.90/src/velocity/db/servers/tablehelper.py +577 -0
  6. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity_python.egg-info/PKG-INFO +1 -1
  7. velocity_python-0.0.89/src/velocity/db/servers/tablehelper.py +0 -323
  8. {velocity_python-0.0.89 → velocity_python-0.0.90}/LICENSE +0 -0
  9. {velocity_python-0.0.89 → velocity_python-0.0.90}/README.md +0 -0
  10. {velocity_python-0.0.89 → velocity_python-0.0.90}/setup.cfg +0 -0
  11. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/app/__init__.py +0 -0
  12. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/app/invoices.py +0 -0
  13. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/app/orders.py +0 -0
  14. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/app/payments.py +0 -0
  15. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/app/purchase_orders.py +0 -0
  16. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/aws/__init__.py +0 -0
  17. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/aws/amplify.py +0 -0
  18. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/aws/handlers/__init__.py +0 -0
  19. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/aws/handlers/context.py +0 -0
  20. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/aws/handlers/lambda_handler.py +0 -0
  21. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/aws/handlers/response.py +0 -0
  22. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/aws/handlers/sqs_handler.py +0 -0
  23. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/db/__init__.py +0 -0
  24. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/db/core/__init__.py +0 -0
  25. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/db/core/column.py +0 -0
  26. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/db/core/database.py +0 -0
  27. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/db/core/decorators.py +0 -0
  28. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/db/core/engine.py +0 -0
  29. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/db/core/exceptions.py +0 -0
  30. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/db/core/result.py +0 -0
  31. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/db/core/row.py +0 -0
  32. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/db/core/sequence.py +0 -0
  33. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/db/core/table.py +0 -0
  34. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/db/core/transaction.py +0 -0
  35. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/db/servers/__init__.py +0 -0
  36. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/db/servers/mysql.py +0 -0
  37. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/db/servers/mysql_reserved.py +0 -0
  38. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/db/servers/postgres/__init__.py +0 -0
  39. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/db/servers/postgres/operators.py +0 -0
  40. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/db/servers/postgres/reserved.py +0 -0
  41. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/db/servers/postgres/types.py +0 -0
  42. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/db/servers/sqlite.py +0 -0
  43. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/db/servers/sqlite_reserved.py +0 -0
  44. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/db/servers/sqlserver.py +0 -0
  45. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/db/servers/sqlserver_reserved.py +0 -0
  46. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/misc/__init__.py +0 -0
  47. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/misc/conv/__init__.py +0 -0
  48. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/misc/conv/iconv.py +0 -0
  49. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/misc/conv/oconv.py +0 -0
  50. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/misc/db.py +0 -0
  51. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/misc/export.py +0 -0
  52. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/misc/format.py +0 -0
  53. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/misc/mail.py +0 -0
  54. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/misc/merge.py +0 -0
  55. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/misc/timer.py +0 -0
  56. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity/misc/tools.py +0 -0
  57. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity_python.egg-info/SOURCES.txt +0 -0
  58. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity_python.egg-info/dependency_links.txt +0 -0
  59. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity_python.egg-info/requires.txt +0 -0
  60. {velocity_python-0.0.89 → velocity_python-0.0.90}/src/velocity_python.egg-info/top_level.txt +0 -0
  61. {velocity_python-0.0.89 → velocity_python-0.0.90}/tests/test_db.py +0 -0
  62. {velocity_python-0.0.89 → velocity_python-0.0.90}/tests/test_email_processing.py +0 -0
  63. {velocity_python-0.0.89 → velocity_python-0.0.90}/tests/test_format.py +0 -0
  64. {velocity_python-0.0.89 → velocity_python-0.0.90}/tests/test_iconv.py +0 -0
  65. {velocity_python-0.0.89 → velocity_python-0.0.90}/tests/test_merge.py +0 -0
  66. {velocity_python-0.0.89 → velocity_python-0.0.90}/tests/test_oconv.py +0 -0
  67. {velocity_python-0.0.89 → velocity_python-0.0.90}/tests/test_postgres.py +0 -0
  68. {velocity_python-0.0.89 → velocity_python-0.0.90}/tests/test_response.py +0 -0
  69. {velocity_python-0.0.89 → velocity_python-0.0.90}/tests/test_spreadsheet_functions.py +0 -0
  70. {velocity_python-0.0.89 → velocity_python-0.0.90}/tests/test_sql_builder.py +0 -0
  71. {velocity_python-0.0.89 → velocity_python-0.0.90}/tests/test_timer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.0.89
3
+ Version: 0.0.90
4
4
  Summary: A rapid application development library for interfacing with data storage
5
5
  Author-email: Paul Perez <pperez@codeclubs.org>
6
6
  Project-URL: Homepage, https://codeclubs.org/projects/velocity
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "velocity-python"
3
- version = "0.0.89"
3
+ version = "0.0.90"
4
4
  authors = [
5
5
  { name="Paul Perez", email="pperez@codeclubs.org" },
6
6
  ]
@@ -1,4 +1,4 @@
1
- __version__ = version = "0.0.89"
1
+ __version__ = version = "0.0.90"
2
2
 
3
3
  from . import aws
4
4
  from . import db
@@ -12,10 +12,44 @@ from ..tablehelper import TableHelper
12
12
  from collections.abc import Mapping, Sequence
13
13
 
14
14
 
15
+ # Configure TableHelper for PostgreSQL
15
16
  TableHelper.reserved = reserved_words
16
17
  TableHelper.operators = OPERATORS
17
18
 
18
19
 
20
+ def _get_table_helper(tx, table):
21
+ """
22
+ Utility function to create a TableHelper instance.
23
+ Ensures consistent configuration across all SQL methods.
24
+ """
25
+ return TableHelper(tx, table)
26
+
27
+
28
+ def _validate_table_name(table):
29
+ """Validate table name format."""
30
+ if not table or not isinstance(table, str):
31
+ raise ValueError("Table name must be a non-empty string")
32
+ # Add more validation as needed
33
+ return table.strip()
34
+
35
+
36
+ def _handle_predicate_errors(predicates, operation="WHERE"):
37
+ """Process a list of predicates with error handling."""
38
+ sql_parts = []
39
+ vals = []
40
+
41
+ for pred, val in predicates:
42
+ sql_parts.append(pred)
43
+ if val is None:
44
+ pass
45
+ elif isinstance(val, tuple):
46
+ vals.extend(val)
47
+ else:
48
+ vals.append(val)
49
+
50
+ return sql_parts, vals
51
+
52
+
19
53
  system_fields = [
20
54
  "sys_id",
21
55
  "sys_created",
@@ -72,9 +106,17 @@ class SQL:
72
106
  lock=None,
73
107
  skip_locked=None,
74
108
  ):
75
-
109
+ """
110
+ Generate a PostgreSQL SELECT statement with proper table helper integration.
111
+ """
76
112
  if not table:
77
113
  raise ValueError("Table name is required.")
114
+
115
+ # Validate pagination parameters
116
+ if start is not None and not isinstance(start, int):
117
+ raise ValueError("Start (OFFSET) must be an integer.")
118
+ if qty is not None and not isinstance(qty, int):
119
+ raise ValueError("Qty (FETCH) must be an integer.")
78
120
 
79
121
  sql_parts = {
80
122
  "SELECT": [],
@@ -88,8 +130,8 @@ class SQL:
88
130
  sql = []
89
131
  vals = []
90
132
 
91
- # Assume these helpers and functions exist externally
92
- th = TableHelper(tx, table)
133
+ # Create table helper instance
134
+ th = _get_table_helper(tx, table)
93
135
 
94
136
  # Handle columns and DISTINCT before aliasing
95
137
  if columns is None:
@@ -97,25 +139,28 @@ class SQL:
97
139
  columns = ["*"]
98
140
  elif isinstance(columns, str):
99
141
  columns = th.split_columns(columns)
100
-
101
- if not isinstance(columns, Sequence):
102
- raise Exception(
103
- f"variable `columns` must be a sequence, but {type(columns)} was found"
142
+ elif not isinstance(columns, Sequence):
143
+ raise TypeError(
144
+ f"Columns must be a string, sequence, or None, but {type(columns)} was found"
104
145
  )
105
146
 
106
- columns = [c.strip() for c in columns] # Preserve original case
147
+ # Clean and validate columns
148
+ columns = [c.strip() for c in columns if c.strip()] # Remove empty columns
149
+ if not columns:
150
+ raise ValueError("No valid columns specified")
151
+
107
152
  distinct = False
108
153
 
109
- if any(
110
- "distinct" in c.lower() for c in columns
111
- ): # Check if "distinct" exists in any entry
154
+ # Check for DISTINCT keyword in any column
155
+ if any("distinct" in c.lower() for c in columns):
112
156
  distinct = True
113
157
  columns = [re.sub(r"(?i)\bdistinct\b", "", c).strip() for c in columns]
114
158
 
159
+ # Process column references
115
160
  processed_columns = []
116
161
  for col in columns:
117
- processed_columns.append(
118
- th.resolve_references(
162
+ try:
163
+ processed_col = th.resolve_references(
119
164
  col,
120
165
  options={
121
166
  "alias_column": True,
@@ -123,40 +168,65 @@ class SQL:
123
168
  "bypass_on_error": True,
124
169
  },
125
170
  )
126
- )
171
+ processed_columns.append(processed_col)
172
+ except Exception as e:
173
+ raise ValueError(f"Error processing column '{col}': {e}")
127
174
 
128
175
  columns = processed_columns
129
176
 
130
- # Handle WHERE conditions
177
+ # Handle WHERE conditions with better error handling
131
178
  if isinstance(where, Mapping):
132
179
  new_where = []
133
180
  for key, val in where.items():
134
- new_where.append(th.make_predicate(key, val))
181
+ try:
182
+ new_where.append(th.make_predicate(key, val))
183
+ except Exception as e:
184
+ raise ValueError(f"Error processing WHERE condition '{key}': {e}")
135
185
  where = new_where
136
186
 
187
+ # Handle ORDER BY with improved validation
137
188
  new_orderby = []
138
189
  if isinstance(orderby, str):
139
190
  orderby = th.split_columns(orderby)
191
+
140
192
  # Handle orderby references
141
- if isinstance(orderby, (Sequence)):
193
+ if isinstance(orderby, Sequence):
142
194
  for column in orderby:
143
- if " " in column:
144
- col_name, direction = column.split(" ", 1)
145
- col_name = th.resolve_references(
146
- col_name, options={"alias_only": True}
147
- )
148
- new_orderby.append(f"{col_name} {direction}")
149
- else:
150
- new_orderby.append(
151
- th.resolve_references(
195
+ try:
196
+ if " " in column:
197
+ parts = column.split(" ", 1)
198
+ if len(parts) == 2:
199
+ col_name, direction = parts
200
+ # Validate direction
201
+ direction = direction.upper()
202
+ if direction not in ("ASC", "DESC"):
203
+ raise ValueError(f"Invalid ORDER BY direction: {direction}")
204
+ col_name = th.resolve_references(
205
+ col_name.strip(), options={"alias_only": True}
206
+ )
207
+ new_orderby.append(f"{col_name} {direction}")
208
+ else:
209
+ raise ValueError(f"Invalid ORDER BY format: {column}")
210
+ else:
211
+ resolved_col = th.resolve_references(
152
212
  column.strip(), options={"alias_only": True}
153
213
  )
154
- )
214
+ new_orderby.append(resolved_col)
215
+ except Exception as e:
216
+ raise ValueError(f"Error processing ORDER BY column '{column}': {e}")
155
217
 
156
- if isinstance(orderby, Mapping):
218
+ elif isinstance(orderby, Mapping):
157
219
  for key, val in orderby.items():
158
- parsed_key = th.resolve_references(key, options={"alias_only": True})
159
- new_orderby.append(f"{parsed_key} {val}")
220
+ try:
221
+ # Validate direction
222
+ direction = str(val).upper()
223
+ if direction not in ("ASC", "DESC"):
224
+ raise ValueError(f"Invalid ORDER BY direction: {direction}")
225
+ parsed_key = th.resolve_references(key, options={"alias_only": True})
226
+ new_orderby.append(f"{parsed_key} {direction}")
227
+ except Exception as e:
228
+ raise ValueError(f"Error processing ORDER BY key '{key}': {e}")
229
+
160
230
  orderby = new_orderby
161
231
 
162
232
  # Handle groupby
@@ -308,7 +378,7 @@ class SQL:
308
378
  if not isinstance(data, Mapping) or not data:
309
379
  raise ValueError("data must be a non-empty mapping of column-value pairs.")
310
380
 
311
- th = TableHelper(tx, table)
381
+ th = _get_table_helper(tx, table)
312
382
  set_clauses = []
313
383
  vals = []
314
384
 
@@ -390,12 +460,15 @@ class SQL:
390
460
  """
391
461
  Generate an INSERT statement.
392
462
  """
393
-
463
+ # Create a temporary TableHelper instance for quoting
464
+ # Note: We pass None for tx since we only need quoting functionality
465
+ temp_helper = TableHelper(None, table)
466
+
394
467
  keys = []
395
468
  vals_placeholders = []
396
469
  args = []
397
470
  for key, val in data.items():
398
- keys.append(TableHelper.quote(key.lower()))
471
+ keys.append(temp_helper.quote(key.lower()))
399
472
  if isinstance(val, str) and len(val) > 2 and val[:2] == "@@" and val[2:]:
400
473
  vals_placeholders.append(val[2:])
401
474
  else:
@@ -404,7 +477,7 @@ class SQL:
404
477
 
405
478
  sql_parts = []
406
479
  sql_parts.append("INSERT INTO")
407
- sql_parts.append(TableHelper.quote(table))
480
+ sql_parts.append(temp_helper.quote(table))
408
481
  sql_parts.append("(")
409
482
  sql_parts.append(",".join(keys))
410
483
  sql_parts.append(")")