velocity-python 0.0.187__tar.gz → 0.0.189__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.
Files changed (143) hide show
  1. {velocity_python-0.0.187/src/velocity_python.egg-info → velocity_python-0.0.189}/PKG-INFO +1 -1
  2. {velocity_python-0.0.187 → velocity_python-0.0.189}/pyproject.toml +1 -1
  3. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/__init__.py +1 -1
  4. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/aws/handlers/mixins/__init__.py +2 -2
  5. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/aws/handlers/mixins/data_service.py +27 -138
  6. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/aws/handlers/mixins/web_handler.py +13 -7
  7. {velocity_python-0.0.187 → velocity_python-0.0.189/src/velocity_python.egg-info}/PKG-INFO +1 -1
  8. {velocity_python-0.0.187 → velocity_python-0.0.189}/LICENSE +0 -0
  9. {velocity_python-0.0.187 → velocity_python-0.0.189}/README.md +0 -0
  10. {velocity_python-0.0.187 → velocity_python-0.0.189}/setup.cfg +0 -0
  11. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/app/__init__.py +0 -0
  12. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/app/invoices.py +0 -0
  13. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/app/orders.py +0 -0
  14. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/app/payments.py +0 -0
  15. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/app/purchase_orders.py +0 -0
  16. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/app/tests/__init__.py +0 -0
  17. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/app/tests/test_email_processing.py +0 -0
  18. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/app/tests/test_payment_profile_sorting.py +0 -0
  19. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/app/tests/test_spreadsheet_functions.py +0 -0
  20. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/aws/__init__.py +0 -0
  21. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/aws/amplify.py +0 -0
  22. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/aws/handlers/__init__.py +0 -0
  23. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/aws/handlers/base_handler.py +0 -0
  24. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/aws/handlers/context.py +0 -0
  25. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/aws/handlers/exceptions.py +0 -0
  26. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/aws/handlers/lambda_handler.py +0 -0
  27. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/aws/handlers/response.py +0 -0
  28. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/aws/handlers/sqs_handler.py +0 -0
  29. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/aws/tests/__init__.py +0 -0
  30. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/aws/tests/test_lambda_handler_json_serialization.py +0 -0
  31. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/aws/tests/test_response.py +0 -0
  32. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/__init__.py +0 -0
  33. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/core/__init__.py +0 -0
  34. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/core/column.py +0 -0
  35. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/core/database.py +0 -0
  36. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/core/decorators.py +0 -0
  37. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/core/engine.py +0 -0
  38. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/core/result.py +0 -0
  39. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/core/row.py +0 -0
  40. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/core/sequence.py +0 -0
  41. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/core/table.py +0 -0
  42. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/core/transaction.py +0 -0
  43. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/exceptions.py +0 -0
  44. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/__init__.py +0 -0
  45. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/base/__init__.py +0 -0
  46. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/base/initializer.py +0 -0
  47. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/base/operators.py +0 -0
  48. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/base/sql.py +0 -0
  49. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/base/types.py +0 -0
  50. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/mysql/__init__.py +0 -0
  51. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/mysql/operators.py +0 -0
  52. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/mysql/reserved.py +0 -0
  53. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/mysql/sql.py +0 -0
  54. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/mysql/types.py +0 -0
  55. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/postgres/__init__.py +0 -0
  56. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/postgres/operators.py +0 -0
  57. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/postgres/reserved.py +0 -0
  58. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/postgres/sql.py +0 -0
  59. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/postgres/types.py +0 -0
  60. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/sqlite/__init__.py +0 -0
  61. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/sqlite/operators.py +0 -0
  62. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/sqlite/reserved.py +0 -0
  63. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/sqlite/sql.py +0 -0
  64. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/sqlite/types.py +0 -0
  65. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/sqlserver/__init__.py +0 -0
  66. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/sqlserver/operators.py +0 -0
  67. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/sqlserver/reserved.py +0 -0
  68. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/sqlserver/sql.py +0 -0
  69. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/sqlserver/types.py +0 -0
  70. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/servers/tablehelper.py +0 -0
  71. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/__init__.py +0 -0
  72. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/common_db_test.py +0 -0
  73. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/postgres/__init__.py +0 -0
  74. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/postgres/common.py +0 -0
  75. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/postgres/test_column.py +0 -0
  76. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/postgres/test_connections.py +0 -0
  77. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/postgres/test_database.py +0 -0
  78. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/postgres/test_engine.py +0 -0
  79. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/postgres/test_general_usage.py +0 -0
  80. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/postgres/test_imports.py +0 -0
  81. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/postgres/test_result.py +0 -0
  82. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/postgres/test_row.py +0 -0
  83. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/postgres/test_row_comprehensive.py +0 -0
  84. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/postgres/test_schema_locking.py +0 -0
  85. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/postgres/test_schema_locking_unit.py +0 -0
  86. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/postgres/test_sequence.py +0 -0
  87. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/postgres/test_sql_comprehensive.py +0 -0
  88. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/postgres/test_table.py +0 -0
  89. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/postgres/test_table_comprehensive.py +0 -0
  90. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/postgres/test_transaction.py +0 -0
  91. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/sql/__init__.py +0 -0
  92. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/sql/common.py +0 -0
  93. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/sql/test_postgres_select_advanced.py +0 -0
  94. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/sql/test_postgres_select_variances.py +0 -0
  95. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/test_cursor_rowcount_fix.py +0 -0
  96. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/test_db_utils.py +0 -0
  97. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/test_postgres.py +0 -0
  98. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/test_postgres_unchanged.py +0 -0
  99. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/test_process_error_robustness.py +0 -0
  100. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/test_result_caching.py +0 -0
  101. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/test_result_sql_aware.py +0 -0
  102. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/test_row_get_missing_column.py +0 -0
  103. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/test_schema_locking_initializers.py +0 -0
  104. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/test_schema_locking_simple.py +0 -0
  105. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/test_sql_builder.py +0 -0
  106. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/tests/test_tablehelper.py +0 -0
  107. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/db/utils.py +0 -0
  108. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/logging.py +0 -0
  109. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/misc/__init__.py +0 -0
  110. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/misc/conv/__init__.py +0 -0
  111. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/misc/conv/iconv.py +0 -0
  112. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/misc/conv/oconv.py +0 -0
  113. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/misc/db.py +0 -0
  114. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/misc/export.py +0 -0
  115. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/misc/format.py +0 -0
  116. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/misc/mail.py +0 -0
  117. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/misc/merge.py +0 -0
  118. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/misc/tests/__init__.py +0 -0
  119. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/misc/tests/test_db.py +0 -0
  120. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/misc/tests/test_fix.py +0 -0
  121. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/misc/tests/test_format.py +0 -0
  122. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/misc/tests/test_iconv.py +0 -0
  123. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/misc/tests/test_merge.py +0 -0
  124. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/misc/tests/test_oconv.py +0 -0
  125. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/misc/tests/test_original_error.py +0 -0
  126. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/misc/tests/test_timer.py +0 -0
  127. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/misc/timer.py +0 -0
  128. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/misc/tools.py +0 -0
  129. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/payment/__init__.py +0 -0
  130. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/payment/base_adapter.py +0 -0
  131. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/payment/braintree_adapter.py +0 -0
  132. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/payment/router.py +0 -0
  133. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity/payment/stripe_adapter.py +0 -0
  134. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity_python.egg-info/SOURCES.txt +0 -0
  135. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity_python.egg-info/dependency_links.txt +0 -0
  136. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity_python.egg-info/requires.txt +0 -0
  137. {velocity_python-0.0.187 → velocity_python-0.0.189}/src/velocity_python.egg-info/top_level.txt +0 -0
  138. {velocity_python-0.0.187 → velocity_python-0.0.189}/tests/test_decorators.py +0 -0
  139. {velocity_python-0.0.187 → velocity_python-0.0.189}/tests/test_lambda_handler.py +0 -0
  140. {velocity_python-0.0.187 → velocity_python-0.0.189}/tests/test_mixins_import.py +0 -0
  141. {velocity_python-0.0.187 → velocity_python-0.0.189}/tests/test_sys_modified_count_postgres_demo.py +0 -0
  142. {velocity_python-0.0.187 → velocity_python-0.0.189}/tests/test_table_alter.py +0 -0
  143. {velocity_python-0.0.187 → velocity_python-0.0.189}/tests/test_where_clause_validation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.0.187
3
+ Version: 0.0.189
4
4
  Summary: A rapid application development library for interfacing with data storage
5
5
  Author-email: Velocity Team <info@codeclubs.org>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "velocity-python"
7
- version = "0.0.187"
7
+ version = "0.0.189"
8
8
  authors = [
9
9
  { name="Velocity Team", email="info@codeclubs.org" },
10
10
  ]
@@ -1,4 +1,4 @@
1
- __version__ = version = "0.0.187"
1
+ __version__ = version = "0.0.189"
2
2
 
3
3
  from . import aws
4
4
  from . import db
@@ -8,6 +8,6 @@ This package provides mixins for common Lambda handler patterns:
8
8
  """
9
9
 
10
10
  from .web_handler import WebHandler, ButtonHandler
11
- from .data_service import DataServiceMixin, RWXHookSystem, apply_sys_modified_by
11
+ from .data_service import DataServiceMixin
12
12
 
13
- __all__ = ['WebHandler', 'ButtonHandler', 'DataServiceMixin', 'RWXHookSystem', 'apply_sys_modified_by']
13
+ __all__ = ['WebHandler', 'ButtonHandler', 'DataServiceMixin',]
@@ -8,10 +8,13 @@ that uses velocity.db for database access.
8
8
  import base64
9
9
  import datetime
10
10
  import importlib
11
+ import logging
11
12
  from io import BytesIO
12
13
 
13
14
  from velocity.misc import export
14
15
 
16
+ logger = logging.getLogger(__name__)
17
+
15
18
 
16
19
  class DataServiceMixin:
17
20
  """
@@ -71,6 +74,7 @@ class DataServiceMixin:
71
74
  *args: Arguments to pass to the hook
72
75
  **kwargs: Keyword arguments to pass to the hook
73
76
  """
77
+
74
78
  try:
75
79
  m = importlib.import_module(f".{table}", "rwx")
76
80
  if hasattr(m, hook_name):
@@ -275,10 +279,14 @@ class DataServiceMixin:
275
279
  if not any(value is not None for value in incoming.values()):
276
280
  raise ValueError("Parameter 'object' cannot contain only None values")
277
281
 
278
- print(
279
- f"WRITE_LOG: Writing to table {table_name} with sys_id {incoming.get('sys_id')}"
282
+ logger.debug(
283
+ "Writing to table",
284
+ extra={
285
+ "table_name": table_name,
286
+ "sys_id": incoming.get("sys_id"),
287
+ "object_keys": list(incoming.keys()),
288
+ },
280
289
  )
281
- print(f"WRITE_LOG: Object keys: {list(incoming.keys())}")
282
290
 
283
291
  try:
284
292
  row = self.write_hook(
@@ -290,7 +298,10 @@ class DataServiceMixin:
290
298
  )
291
299
 
292
300
  if not row:
293
- print(f"WARNING: self.write_hook returned empty row for table {table_name}")
301
+ logger.warning(
302
+ "write_hook returned empty row",
303
+ extra={"table_name": table_name},
304
+ )
294
305
  row = {}
295
306
 
296
307
  context.response().set_body(
@@ -299,11 +310,20 @@ class DataServiceMixin:
299
310
  "lastFetch": datetime.datetime.now(),
300
311
  }
301
312
  )
302
- print(f"WRITE_LOG: Successfully wrote to {table_name}")
313
+ logger.debug(
314
+ "Successfully wrote to table",
315
+ extra={"table_name": table_name},
316
+ )
303
317
 
304
318
  except Exception as e:
305
- print(f"ERROR in OnActionWriteObject: {e}")
306
- print(f"table_name={table_name}, incoming_keys={list(incoming.keys())}")
319
+ logger.error(
320
+ "Error in OnActionWriteObject",
321
+ extra={
322
+ "exception": str(e),
323
+ "table_name": table_name,
324
+ "incoming_keys": list(incoming.keys()),
325
+ },
326
+ )
307
327
  raise
308
328
  context.response().load_object(row)
309
329
 
@@ -532,134 +552,3 @@ class DataServiceMixin:
532
552
  )
533
553
  raise Exception(f"Failed to retrieve table schema: {str(e)}")
534
554
 
535
-
536
- class RWXHookSystem:
537
- """
538
- RWX (Read/Write/eXecute) hook system for table-specific business logic.
539
-
540
- This system allows you to create table-specific hook modules in an 'rwx' package
541
- and have them automatically called during CRUD operations. This is useful for
542
- adding custom business logic (validation, permissions, data enrichment) for
543
- specific tables without modifying the generic DataServiceMixin.
544
-
545
- How it works:
546
- 1. Create a Python package called 'rwx' in your Lambda handler
547
- 2. For each table that needs custom logic, create a module (e.g., rwx/admin_users.py)
548
- 3. Define hook functions in the module (before_read, after_write, before_delete, etc.)
549
- 4. Subclass RWXHookSystem and set hook_module_name = 'rwx'
550
- 5. Use it from your handler class
551
-
552
- Example structure:
553
- src/
554
- index.py
555
- rwx/
556
- __init__.py
557
- admin_users.py # hooks for admin_users table
558
- donors.py # hooks for donors table
559
-
560
- Example hook in rwx/admin_users.py:
561
- def before_write(tx, table, sys_id, incoming, context):
562
- # Validate incoming data
563
- if not incoming.get('email_address'):
564
- raise ValueError("Email is required")
565
- # Modify data
566
- incoming['description'] = f"{incoming['full_name']} ({incoming['email_address']})"
567
-
568
- def after_read(tx, table, sys_id, row, context):
569
- # Enrich response data
570
- context.response().set_body({'extra_data': 'value'})
571
-
572
- Hook signatures:
573
- - on_new(tx, table, row, context) - When creating new empty object
574
- - before_read(tx, table, sys_id, context) - Before reading an object
575
- - after_read(tx, table, sys_id, row, context) - After reading an object
576
- - before_find(tx, table, query, context) - Before finding by query
577
- - after_find(tx, table, query, row, context) - After finding by query
578
- - before_new(tx, table, sys_id, incoming, context) - Before creating new object
579
- - after_new(tx, table, sys_id, row, context) - After creating new object
580
- - before_write(tx, table, sys_id, incoming, context) - Before write (insert/update)
581
- - after_write(tx, table, sys_id, row, context) - After write (insert/update)
582
- - before_delete(tx, table, sys_id, context) - Before deleting an object
583
- - after_delete(tx, table, sys_id, context) - After deleting an object
584
- - before_query(tx, table, payload, context) - Before querying objects
585
- - after_query(tx, table, data, payload, context) - After querying objects
586
-
587
- Usage in a Lambda handler:
588
- from velocity.aws.handlers.mixins import DataServiceMixin, RWXHookSystem
589
-
590
- class MyRWXSystem(RWXHookSystem):
591
- hook_module_name = 'rwx' # Name of your rwx package
592
-
593
- @engine.transaction
594
- class HttpEventHandler(DataServiceMixin, LambdaHandler):
595
- def __init__(self, aws_event, aws_context):
596
- super().__init__(aws_event, aws_context)
597
- """
598
-
599
- hook_module_name = None # Override in subclass (e.g., 'rwx')
600
-
601
- @classmethod
602
- def _get_table_module(cls, table):
603
- """Load table-specific hook module if it exists"""
604
- if not cls.hook_module_name:
605
- return None
606
- try:
607
- return importlib.import_module(f".{table}", cls.hook_module_name)
608
- except ImportError:
609
- return None
610
-
611
- @classmethod
612
- def _call_hook(cls, hook_name, table, *args, **kwargs):
613
- """Call a hook on table-specific module if it exists"""
614
- module = cls._get_table_module(table)
615
- if module and hasattr(module, hook_name):
616
- return getattr(module, hook_name)(*args, **kwargs)
617
- return None
618
-
619
-
620
- def apply_sys_modified_by(incoming, context):
621
- """
622
- Strip sys_* fields and set sys_modified_by from context.
623
-
624
- Common pattern for applications that track who modified records.
625
- Call this from your write_hook implementation.
626
-
627
- Args:
628
- incoming: Dictionary of data being written
629
- context: Request context with session/payload
630
- """
631
- payload = context.payload()
632
-
633
- # Strip system fields from incoming data
634
- for key in list(incoming.keys()):
635
- if "sys_" in key:
636
- incoming.pop(key)
637
-
638
- # Extract email from session
639
- session = context.session() if hasattr(context, "session") else None
640
- email_address = None
641
-
642
- if isinstance(session, dict):
643
- email_address = session.get("cognito_user_email")
644
- if not email_address:
645
- cognito_user = session.get("cognito_user")
646
- if isinstance(cognito_user, dict):
647
- email_address = cognito_user.get("email") or (
648
- cognito_user.get("attributes") or {}
649
- ).get("email")
650
- if not email_address:
651
- email_address = session.get("email_address")
652
-
653
- if not email_address and isinstance(payload, dict):
654
- payload_user = payload.get("cognito_user")
655
- if isinstance(payload_user, dict):
656
- email_address = payload_user.get("email") or (
657
- payload_user.get("attributes") or {}
658
- ).get("email")
659
-
660
- incoming["sys_modified_by"] = (
661
- email_address.lower()
662
- if isinstance(email_address, str)
663
- else incoming.get("sys_modified_by", "unknown")
664
- )
665
- incoming["sys_dirty"] = True
@@ -9,6 +9,7 @@ onError implementations in handlers.
9
9
  import copy
10
10
  import importlib
11
11
  import json
12
+ import logging
12
13
  import os
13
14
  import pprint
14
15
  import time
@@ -18,6 +19,8 @@ from typing import Any, Dict, List
18
19
 
19
20
  from velocity.aws.handlers.exceptions import AlertError
20
21
 
22
+ logger = logging.getLogger(__name__)
23
+
21
24
  class WebHandler(ABC):
22
25
  """
23
26
  Mixin providing unified activity tracking plus standardized error handling.
@@ -72,9 +75,12 @@ class WebHandler(ABC):
72
75
  try:
73
76
  self.activity_log_key = tx.table("aws_api_activity").new(self.activity_data).pk
74
77
  except Exception as exc:
75
- context.log(
76
- f"WebHandler.track_activity_start failed: {exc}; keys={list(self.activity_data.keys())}",
77
- "WebHandler.track_activity_start",
78
+ logger.error(
79
+ "WebHandler.track_activity_start failed",
80
+ extra={
81
+ "exception": str(exc),
82
+ "activity_data_keys": list(self.activity_data.keys()),
83
+ },
78
84
  )
79
85
  raise
80
86
 
@@ -254,7 +260,7 @@ Request Details:
254
260
  html = html.replace("\n", "<br>").replace(" ", "&nbsp;")
255
261
  recipients = self._resolve_error_notification_recipients(tx, context)
256
262
  if not recipients:
257
- print("No error notification recipients configured; skipping notification.")
263
+ logger.debug("No error notification recipients configured; skipping notification.")
258
264
  return
259
265
 
260
266
  sender = self._resolve_error_notification_sender(tx, context)
@@ -271,7 +277,7 @@ Request Details:
271
277
  email_settings_id=1001,
272
278
  )
273
279
  except Exception as email_error: # pragma: no cover
274
- print(f"Failed to send error notification email: {email_error}")
280
+ logger.error("Failed to send error notification email", extra={"error": str(email_error)})
275
281
 
276
282
  def _resolve_error_notification_recipients(self, tx, context) -> List[str]:
277
283
  recipients: List[str] = []
@@ -280,7 +286,7 @@ Request Details:
280
286
  try:
281
287
  recipients = context_method(tx) or []
282
288
  except Exception as exc:
283
- print(f"Failed to load recipients from context: {exc}")
289
+ logger.warning("Failed to load recipients from context", extra={"error": str(exc)})
284
290
  if recipients:
285
291
  return recipients
286
292
  return self._get_error_notification_recipients()
@@ -292,7 +298,7 @@ Request Details:
292
298
  try:
293
299
  sender = context_method(tx)
294
300
  except Exception as exc:
295
- print(f"Failed to load sender from context: {exc}")
301
+ logger.warning("Failed to load sender from context", extra={"error": str(exc)})
296
302
  if sender:
297
303
  return sender
298
304
  return self._get_error_notification_sender()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.0.187
3
+ Version: 0.0.189
4
4
  Summary: A rapid application development library for interfacing with data storage
5
5
  Author-email: Velocity Team <info@codeclubs.org>
6
6
  License-Expression: MIT