velocity-python 0.0.193__tar.gz → 0.0.195__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.193 → velocity_python-0.0.195}/PKG-INFO +1 -1
  2. {velocity_python-0.0.193 → velocity_python-0.0.195}/pyproject.toml +1 -1
  3. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/__init__.py +1 -1
  4. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/aws/handlers/base_handler.py +39 -7
  5. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/aws/handlers/context.py +11 -0
  6. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/aws/handlers/lambda_handler.py +16 -9
  7. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/aws/handlers/mixins/web_handler.py +64 -1
  8. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity_python.egg-info/PKG-INFO +1 -1
  9. {velocity_python-0.0.193 → velocity_python-0.0.195}/LICENSE +0 -0
  10. {velocity_python-0.0.193 → velocity_python-0.0.195}/README.md +0 -0
  11. {velocity_python-0.0.193 → velocity_python-0.0.195}/setup.cfg +0 -0
  12. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/app/__init__.py +0 -0
  13. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/app/invoices.py +0 -0
  14. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/app/orders.py +0 -0
  15. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/app/payments.py +0 -0
  16. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/app/purchase_orders.py +0 -0
  17. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/app/tests/__init__.py +0 -0
  18. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/app/tests/test_email_processing.py +0 -0
  19. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/app/tests/test_payment_profile_sorting.py +0 -0
  20. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/app/tests/test_spreadsheet_functions.py +0 -0
  21. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/aws/__init__.py +0 -0
  22. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/aws/amplify.py +0 -0
  23. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/aws/handlers/__init__.py +0 -0
  24. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/aws/handlers/exceptions.py +0 -0
  25. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/aws/handlers/mixins/__init__.py +0 -0
  26. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/aws/handlers/mixins/data_service.py +0 -0
  27. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/aws/handlers/response.py +0 -0
  28. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/aws/handlers/sqs_handler.py +0 -0
  29. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/aws/tests/__init__.py +0 -0
  30. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/aws/tests/test_lambda_handler_json_serialization.py +0 -0
  31. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/aws/tests/test_response.py +0 -0
  32. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/__init__.py +0 -0
  33. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/core/__init__.py +0 -0
  34. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/core/column.py +0 -0
  35. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/core/database.py +0 -0
  36. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/core/decorators.py +0 -0
  37. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/core/engine.py +0 -0
  38. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/core/result.py +0 -0
  39. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/core/row.py +0 -0
  40. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/core/sequence.py +0 -0
  41. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/core/table.py +0 -0
  42. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/core/transaction.py +0 -0
  43. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/exceptions.py +0 -0
  44. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/__init__.py +0 -0
  45. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/base/__init__.py +0 -0
  46. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/base/initializer.py +0 -0
  47. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/base/operators.py +0 -0
  48. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/base/sql.py +0 -0
  49. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/base/types.py +0 -0
  50. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/mysql/__init__.py +0 -0
  51. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/mysql/operators.py +0 -0
  52. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/mysql/reserved.py +0 -0
  53. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/mysql/sql.py +0 -0
  54. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/mysql/types.py +0 -0
  55. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/postgres/__init__.py +0 -0
  56. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/postgres/operators.py +0 -0
  57. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/postgres/reserved.py +0 -0
  58. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/postgres/sql.py +0 -0
  59. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/postgres/types.py +0 -0
  60. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/sqlite/__init__.py +0 -0
  61. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/sqlite/operators.py +0 -0
  62. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/sqlite/reserved.py +0 -0
  63. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/sqlite/sql.py +0 -0
  64. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/sqlite/types.py +0 -0
  65. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/sqlserver/__init__.py +0 -0
  66. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/sqlserver/operators.py +0 -0
  67. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/sqlserver/reserved.py +0 -0
  68. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/sqlserver/sql.py +0 -0
  69. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/sqlserver/types.py +0 -0
  70. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/servers/tablehelper.py +0 -0
  71. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/__init__.py +0 -0
  72. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/common_db_test.py +0 -0
  73. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/postgres/__init__.py +0 -0
  74. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/postgres/common.py +0 -0
  75. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/postgres/test_column.py +0 -0
  76. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/postgres/test_connections.py +0 -0
  77. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/postgres/test_database.py +0 -0
  78. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/postgres/test_engine.py +0 -0
  79. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/postgres/test_general_usage.py +0 -0
  80. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/postgres/test_imports.py +0 -0
  81. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/postgres/test_result.py +0 -0
  82. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/postgres/test_row.py +0 -0
  83. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/postgres/test_row_comprehensive.py +0 -0
  84. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/postgres/test_schema_locking.py +0 -0
  85. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/postgres/test_schema_locking_unit.py +0 -0
  86. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/postgres/test_sequence.py +0 -0
  87. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/postgres/test_sql_comprehensive.py +0 -0
  88. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/postgres/test_table.py +0 -0
  89. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/postgres/test_table_comprehensive.py +0 -0
  90. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/postgres/test_transaction.py +0 -0
  91. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/sql/__init__.py +0 -0
  92. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/sql/common.py +0 -0
  93. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/sql/test_postgres_select_advanced.py +0 -0
  94. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/sql/test_postgres_select_variances.py +0 -0
  95. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/test_cursor_rowcount_fix.py +0 -0
  96. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/test_db_utils.py +0 -0
  97. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/test_postgres.py +0 -0
  98. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/test_postgres_unchanged.py +0 -0
  99. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/test_process_error_robustness.py +0 -0
  100. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/test_result_caching.py +0 -0
  101. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/test_result_sql_aware.py +0 -0
  102. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/test_row_get_missing_column.py +0 -0
  103. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/test_schema_locking_initializers.py +0 -0
  104. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/test_schema_locking_simple.py +0 -0
  105. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/test_sql_builder.py +0 -0
  106. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/tests/test_tablehelper.py +0 -0
  107. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/db/utils.py +0 -0
  108. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/logging.py +0 -0
  109. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/misc/__init__.py +0 -0
  110. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/misc/conv/__init__.py +0 -0
  111. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/misc/conv/iconv.py +0 -0
  112. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/misc/conv/oconv.py +0 -0
  113. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/misc/db.py +0 -0
  114. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/misc/export.py +0 -0
  115. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/misc/format.py +0 -0
  116. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/misc/mail.py +0 -0
  117. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/misc/merge.py +0 -0
  118. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/misc/tests/__init__.py +0 -0
  119. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/misc/tests/test_db.py +0 -0
  120. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/misc/tests/test_fix.py +0 -0
  121. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/misc/tests/test_format.py +0 -0
  122. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/misc/tests/test_iconv.py +0 -0
  123. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/misc/tests/test_merge.py +0 -0
  124. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/misc/tests/test_oconv.py +0 -0
  125. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/misc/tests/test_original_error.py +0 -0
  126. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/misc/tests/test_timer.py +0 -0
  127. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/misc/timer.py +0 -0
  128. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/misc/tools.py +0 -0
  129. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/payment/__init__.py +0 -0
  130. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/payment/base_adapter.py +0 -0
  131. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/payment/braintree_adapter.py +0 -0
  132. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/payment/router.py +0 -0
  133. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity/payment/stripe_adapter.py +0 -0
  134. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity_python.egg-info/SOURCES.txt +0 -0
  135. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity_python.egg-info/dependency_links.txt +0 -0
  136. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity_python.egg-info/requires.txt +0 -0
  137. {velocity_python-0.0.193 → velocity_python-0.0.195}/src/velocity_python.egg-info/top_level.txt +0 -0
  138. {velocity_python-0.0.193 → velocity_python-0.0.195}/tests/test_decorators.py +0 -0
  139. {velocity_python-0.0.193 → velocity_python-0.0.195}/tests/test_lambda_handler.py +0 -0
  140. {velocity_python-0.0.193 → velocity_python-0.0.195}/tests/test_mixins_import.py +0 -0
  141. {velocity_python-0.0.193 → velocity_python-0.0.195}/tests/test_sys_modified_count_postgres_demo.py +0 -0
  142. {velocity_python-0.0.193 → velocity_python-0.0.195}/tests/test_table_alter.py +0 -0
  143. {velocity_python-0.0.193 → velocity_python-0.0.195}/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.193
3
+ Version: 0.0.195
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.193"
7
+ version = "0.0.195"
8
8
  authors = [
9
9
  { name="Velocity Team", email="info@codeclubs.org" },
10
10
  ]
@@ -1,4 +1,4 @@
1
- __version__ = version = "0.0.193"
1
+ __version__ = version = "0.0.195"
2
2
 
3
3
  from . import aws
4
4
  from . import db
@@ -5,13 +5,15 @@ This module provides a base class for handling AWS Lambda events.
5
5
  It includes common functionality shared between LambdaHandler and SqsHandler.
6
6
  """
7
7
 
8
+ import logging
8
9
  import os
9
10
  import sys
11
+ import time
10
12
  import traceback
11
13
  from typing import Any, Dict, List, Optional
12
14
 
13
15
  from velocity.aws.handlers import context as VelocityContext
14
- from velocity.logging import get_logger
16
+ logger = logging.getLogger(__name__)
15
17
 
16
18
 
17
19
  class BaseHandler:
@@ -22,8 +24,6 @@ class BaseHandler:
22
24
  and error handling hooks that can be shared across different handler types.
23
25
  """
24
26
 
25
- logger = get_logger("velocity.aws.handlers.base")
26
-
27
27
  def __init__(
28
28
  self,
29
29
  aws_event: Dict[str, Any],
@@ -131,9 +131,18 @@ class BaseHandler:
131
131
  Returns:
132
132
  True if an action was executed, False otherwise
133
133
  """
134
+ total_start = time.perf_counter()
135
+
134
136
  # Execute beforeAction hook if available
135
137
  if hasattr(self, "beforeAction"):
138
+ before_start = time.perf_counter()
136
139
  self.beforeAction(tx, local_context)
140
+ before_elapsed = (time.perf_counter() - before_start) * 1000
141
+ logger.info(
142
+ "Timing: beforeAction %.2f ms",
143
+ before_elapsed,
144
+ extra={"action": getattr(local_context, "action", lambda: None)()},
145
+ )
137
146
 
138
147
  action_executed = False
139
148
 
@@ -143,7 +152,15 @@ class BaseHandler:
143
152
  break
144
153
 
145
154
  if hasattr(self, action):
155
+ action_start = time.perf_counter()
146
156
  result = getattr(self, action)(tx, local_context)
157
+ action_elapsed = (time.perf_counter() - action_start) * 1000
158
+ logger.info(
159
+ "Timing: %s %.2f ms",
160
+ action,
161
+ action_elapsed,
162
+ extra={"action": getattr(local_context, "action", lambda: None)()},
163
+ )
147
164
  if result is not None:
148
165
  # Gather diagnostic information
149
166
  diagnostic_info = {
@@ -162,8 +179,9 @@ class BaseHandler:
162
179
  diagnostic_info["dataset"] = local_context.dataset()
163
180
  except Exception as e:
164
181
  diagnostic_info["diagnostic_error"] = str(e)
165
-
166
- self.logger.warning(
182
+ """
183
+ Get the list of actions to execute.
184
+ logger.warning(
167
185
  "Action %s returned an unexpected non-None result (%r).",
168
186
  action,
169
187
  result,
@@ -174,7 +192,21 @@ class BaseHandler:
174
192
 
175
193
  # Execute afterAction hook if available
176
194
  if hasattr(self, "afterAction"):
195
+ after_start = time.perf_counter()
177
196
  self.afterAction(tx, local_context)
197
+ after_elapsed = (time.perf_counter() - after_start) * 1000
198
+ logger.info(
199
+ "Timing: afterAction %.2f ms",
200
+ after_elapsed,
201
+ extra={"action": getattr(local_context, "action", lambda: None)()},
202
+ )
203
+
204
+ total_elapsed = (time.perf_counter() - total_start) * 1000
205
+ logger.info(
206
+ "Timing: execute_actions total %.2f ms",
207
+ total_elapsed,
208
+ extra={"action": getattr(local_context, "action", lambda: None)()},
209
+ )
178
210
 
179
211
  return action_executed
180
212
 
@@ -210,7 +242,7 @@ class BaseHandler:
210
242
  }
211
243
  self._extend_log_data(log_data)
212
244
 
213
- log_method = getattr(self.logger, level, self.logger.info)
245
+ log_method = getattr(logger, level, logger.info)
214
246
  log_method(
215
247
  "%s %s",
216
248
  function,
@@ -249,7 +281,7 @@ class BaseHandler:
249
281
  f" - action: {action}\n"
250
282
  f" - handler: {self.__class__.__name__}"
251
283
  )
252
- self.logger.warning(warning_message)
284
+ logger.warning(warning_message)
253
285
  local_context.response().set_body(
254
286
  {"event": self.aws_event, "postdata": local_context.postdata()}
255
287
  )
@@ -1,6 +1,7 @@
1
1
  import json
2
2
  import os
3
3
  import re
4
+ import time
4
5
  import boto3
5
6
  import uuid
6
7
  from velocity.misc.format import to_json
@@ -477,6 +478,7 @@ class Context:
477
478
  return None
478
479
 
479
480
  def get_cognito_user(self, aws_event):
481
+ overall_start = time.perf_counter()
480
482
  provider = (
481
483
  aws_event.get("requestContext", {})
482
484
  .get("identity", {})
@@ -502,18 +504,24 @@ class Context:
502
504
  raise AlertError("Incomplete Cognito identity provider data") from None
503
505
 
504
506
  try:
507
+ admin_start = time.perf_counter()
505
508
  response = cognito_client.admin_get_user(
506
509
  UserPoolId=user_pool_id,
507
510
  Username=user_sub,
508
511
  )
512
+ admin_elapsed = (time.perf_counter() - admin_start) * 1000
513
+ logger.info("Timing: cognito admin_get_user %.2f ms", admin_elapsed)
509
514
  resolved_username = response.get("Username")
510
515
  except cognito_client.exceptions.UserNotFoundException:
511
516
  try:
517
+ list_start = time.perf_counter()
512
518
  list_response = cognito_client.list_users(
513
519
  UserPoolId=user_pool_id,
514
520
  Filter=f'sub = "{user_sub}"',
515
521
  Limit=1,
516
522
  )
523
+ list_elapsed = (time.perf_counter() - list_start) * 1000
524
+ logger.info("Timing: cognito list_users %.2f ms", list_elapsed)
517
525
  except ClientError as exc:
518
526
  message = exc.response.get("Error", {}).get("Message", "Unknown error")
519
527
  raise AlertError(
@@ -552,6 +560,9 @@ class Context:
552
560
  "sub": attributes.get("sub", user_sub),
553
561
  }
554
562
 
563
+ overall_elapsed = (time.perf_counter() - overall_start) * 1000
564
+ logger.info("Timing: get_cognito_user total %.2f ms", overall_elapsed)
565
+
555
566
  return user
556
567
 
557
568
  def get_cognito_user_optional(self, aws_event):
@@ -1,5 +1,6 @@
1
1
  import copy
2
2
  import json
3
+ import logging
3
4
  import os
4
5
  import pprint
5
6
  import time
@@ -7,11 +8,11 @@ from typing import Optional, Type
7
8
 
8
9
  from velocity.aws.handlers.base_handler import BaseHandler
9
10
  from velocity.aws.handlers.response import Response
10
- from velocity.logging import configure_logging, get_logger
11
+ from velocity.logging import configure_logging
11
12
  from . import context
12
13
 
13
14
  configure_logging()
14
- logger = get_logger("velocity.aws.handlers.lambda")
15
+ logger = logging.getLogger(__name__)
15
16
 
16
17
 
17
18
  class LambdaHandler(BaseHandler):
@@ -68,7 +69,10 @@ class LambdaHandler(BaseHandler):
68
69
  logger.debug("starting LamdaHandler.beforeAction")
69
70
 
70
71
 
72
+ cognito_start = time.perf_counter()
71
73
  self.cognito_user = context.get_cognito_user(self.aws_event)
74
+ cognito_elapsed = (time.perf_counter() - cognito_start) * 1000
75
+ logger.info("Timing: get_cognito_user %.2f ms", cognito_elapsed)
72
76
  self.current_user = {}
73
77
 
74
78
  logger.debug("DEBUG: !!! cognito_user %s", self.cognito_user)
@@ -76,9 +80,9 @@ class LambdaHandler(BaseHandler):
76
80
  email_address = self.cognito_user["attributes"]["email"]
77
81
  self.session["email_address"] = email_address
78
82
  except Exception:
79
- self.logger.warning("Unable to read email from Cognito user", exc_info=True)
83
+ logger.warning("Unable to read email from Cognito user", exc_info=True)
80
84
 
81
- self.logger.info(
85
+ logger.info(
82
86
  "Starting action",
83
87
  extra={
84
88
  "action": context.action(),
@@ -89,16 +93,19 @@ class LambdaHandler(BaseHandler):
89
93
  )
90
94
 
91
95
  if not self.user_table:
92
- self.logger.warning(
96
+ logger.warning(
93
97
  "user_table not configured; skipping DB lookup for %s",
94
98
  self.session.get("email_address"),
95
99
  )
96
100
  raise Exception(
97
101
  "User table not configured; cannot validate user [Config]"
98
102
  )
103
+ lookup_start = time.perf_counter()
99
104
  row = tx.table(self.user_table).find(
100
105
  {"email_address": self.session.get("email_address")}
101
106
  )
107
+ lookup_elapsed = (time.perf_counter() - lookup_start) * 1000
108
+ logger.info("Timing: user lookup %.2f ms", lookup_elapsed)
102
109
  if not row:
103
110
  raise Exception(
104
111
  "A valid user with permission is required to access this function [DB]"
@@ -107,7 +114,7 @@ class LambdaHandler(BaseHandler):
107
114
 
108
115
  temp = copy.deepcopy(context.postdata())
109
116
  temp["payload"].pop("cognito_user", None)
110
- self.logger.debug(
117
+ logger.debug(
111
118
  "Events.OnAction %s",
112
119
  temp.get("action"),
113
120
  extra={"payload": pprint.pformat(temp)},
@@ -121,7 +128,7 @@ class LambdaHandler(BaseHandler):
121
128
  logger.debug("starting LamdaHandler.afterAction")
122
129
 
123
130
  self.end = time.time()
124
- self.logger.info(
131
+ logger.info(
125
132
  "Completed action",
126
133
  extra={
127
134
  "action": context.action(),
@@ -135,7 +142,7 @@ class LambdaHandler(BaseHandler):
135
142
  return
136
143
  logger.debug("starting LamdaHandler.aws_api_activity")
137
144
  request_id = getattr(self.aws_context, "aws_request_id", None)
138
- self.logger.error(
145
+ logger.error(
139
146
  "Unhandled exception for action %s",
140
147
  context.action(),
141
148
  exc_info=exc,
@@ -147,7 +154,7 @@ class LambdaHandler(BaseHandler):
147
154
  temp = copy.deepcopy(context.postdata())
148
155
  logger.debug("starting BackOfficeEvents.log")
149
156
  temp["payload"].pop("cognito_user", None)
150
- self.logger.error(
157
+ logger.error(
151
158
  "Events.OnError %s",
152
159
  temp.get("action"),
153
160
  extra={"payload": pprint.pformat(temp), "traceback": tb},
@@ -36,6 +36,8 @@ class WebHandler(ABC):
36
36
  self.end_time = None
37
37
  self.activity_log_key = None
38
38
  self.activity_data = {}
39
+ self._perf_timing_enabled = False
40
+ self._perf_timing_start = None
39
41
 
40
42
  # ------------------------------------------------------------------
41
43
  # Activity helpers
@@ -43,6 +45,7 @@ class WebHandler(ABC):
43
45
 
44
46
  def track_activity_start(self, tx, context):
45
47
  """Start tracking activity for the current request"""
48
+ perf_start = time.perf_counter()
46
49
  self.start_time = time.time()
47
50
 
48
51
  postdata = context.postdata()
@@ -73,7 +76,14 @@ class WebHandler(ABC):
73
76
  }
74
77
 
75
78
  try:
79
+ insert_start = time.perf_counter()
76
80
  self.activity_log_key = tx.table("aws_api_activity").new(self.activity_data).pk
81
+ insert_elapsed = (time.perf_counter() - insert_start) * 1000
82
+ logger.info(
83
+ "Timing: aws_api_activity insert %.2f ms",
84
+ insert_elapsed,
85
+ extra={"action": context.action(), "handler": self.__class__.__name__},
86
+ )
77
87
  except Exception as exc:
78
88
  logger.error(
79
89
  "WebHandler.track_activity_start failed",
@@ -84,6 +94,13 @@ class WebHandler(ABC):
84
94
  )
85
95
  raise
86
96
 
97
+ total_elapsed = (time.perf_counter() - perf_start) * 1000
98
+ logger.info(
99
+ "Timing: track_activity_start total %.2f ms",
100
+ total_elapsed,
101
+ extra={"action": context.action(), "handler": self.__class__.__name__},
102
+ )
103
+
87
104
  return self.activity_log_key
88
105
 
89
106
  def track_activity_success(self, tx, context):
@@ -132,6 +149,29 @@ class WebHandler(ABC):
132
149
 
133
150
  return json.dumps(sanitized)
134
151
 
152
+ def _is_performance_timing_enabled(self, context) -> bool:
153
+ """Check postdata/payload for log_performance_timing=true"""
154
+ try:
155
+ postdata = context.postdata() or {}
156
+ except Exception:
157
+ return False
158
+
159
+ def normalize(val):
160
+ if isinstance(val, bool):
161
+ return val
162
+ if val is None:
163
+ return False
164
+ return str(val).strip().lower() in {"true", "1", "yes", "y"}
165
+
166
+ if normalize(postdata.get("log_performance_timing")):
167
+ return True
168
+
169
+ payload = postdata.get("payload") if isinstance(postdata, dict) else None
170
+ if isinstance(payload, dict) and normalize(payload.get("log_performance_timing")):
171
+ return True
172
+
173
+ return False
174
+
135
175
  def _recursive_sanitize(self, data: Any, sensitive_fields: List[str]):
136
176
  """Recursively remove sensitive fields from nested data structures"""
137
177
  if isinstance(data, dict):
@@ -372,15 +412,38 @@ Request Details:
372
412
 
373
413
  def _enhanced_before_action(self, tx, context):
374
414
  """Enhanced beforeAction that adds activity tracking"""
415
+ self._perf_timing_enabled = self._is_performance_timing_enabled(context)
416
+ if self._perf_timing_enabled:
417
+ self._perf_timing_start = time.perf_counter()
418
+ logger.info(
419
+ "Performance timing enabled: start action=%s handler=%s",
420
+ context.action(),
421
+ self.__class__.__name__,
422
+ )
375
423
  self.track_activity_start(tx, context)
376
424
 
377
425
  def _enhanced_after_action(self, tx, context):
378
426
  """Enhanced afterAction that adds activity tracking"""
379
427
  self.track_activity_success(tx, context)
428
+ if self._perf_timing_enabled and self._perf_timing_start is not None:
429
+ elapsed_ms = (time.perf_counter() - self._perf_timing_start) * 1000
430
+ logger.info(
431
+ "Performance timing end: action=%s handler=%s duration=%.2f ms",
432
+ context.action(),
433
+ self.__class__.__name__,
434
+ elapsed_ms,
435
+ )
380
436
 
381
437
  def _enhanced_error_handler(self, tx, context, exc, tb):
382
438
  """Enhanced onError that adds standardized error handling"""
383
-
439
+ if self._perf_timing_enabled and self._perf_timing_start is not None:
440
+ elapsed_ms = (time.perf_counter() - self._perf_timing_start) * 1000
441
+ logger.info(
442
+ "Performance timing error: action=%s handler=%s duration=%.2f ms",
443
+ context.action(),
444
+ self.__class__.__name__,
445
+ elapsed_ms,
446
+ )
384
447
 
385
448
  self.track_activity_error(tx, context, exc, tb)
386
449
  self.log_error_to_system(tx, context, exc, tb)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.0.193
3
+ Version: 0.0.195
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