nui-python-shared-utils 2.3__tar.gz → 2.4__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.
- nui_python_shared_utils-2.4/CHANGELOG.md +251 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/PKG-INFO +1 -1
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/guides/powertools-integration.md +46 -1
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_python_shared_utils.egg-info/SOURCES.txt +1 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/powertools_helpers.py +18 -9
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/test_powertools_helpers.py +75 -1
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/.editorconfig +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/.github/workflows/ci.yml +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/.github/workflows/publish.yml +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/.github/workflows/test.yml +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/.markdownlint-cli2.yaml +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/AGENTS.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/CLAUDE.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/CONTRIBUTING.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/LICENSE +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/MANIFEST.in +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/README.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/README.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/decisions/ADR-001-structured-logging-field-vocabulary.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/decisions/ADR-002-keep-shared-utilities-generic.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/decisions/ADR-003-lazy-load-integrations.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/decisions/ADR-004-credential-resolution-precedence.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/decisions/ADR-005-explicit-secrets-manager-region.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/decisions/ADR-006-snowflake-sql-api-client.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/decisions/ADR-007-elasticsearch-client-version-range.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/decisions/ADR-008-stdlib-zoneinfo-over-pytz.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/decisions/README.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/development/testing.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/getting-started/configuration.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/getting-started/installation.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/getting-started/quickstart.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/guides/cli-tools.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/guides/elasticsearch-integration.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/guides/jwt-authentication.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/guides/lambda-utilities.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/guides/llm-integration.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/guides/log-processing.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/guides/shared-types.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/guides/slack-integration.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/guides/snowflake-integration.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/guides/structured-logging-contract.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/mypy.ini +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/__init__.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/base_client.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/cli.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/cloudwatch_metrics.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/config.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/db_client.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/error_handler.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/es_client.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/es_query_builder.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/jwt_auth.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/lambda_helpers.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/llm.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/log_processors.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/logging/__init__.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/logging/binding.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/logging/fields.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/logging/redaction.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/secrets_helper.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/slack_client.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/slack_formatter.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/slack_setup/__init__.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/slack_setup/channel_creator.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/slack_setup/channel_definitions.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/slack_setup/setup_helpers.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/snowflake_client.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/timezone.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/utils.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/pyproject.toml +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/pytest.ini +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/redirect/README.md +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/redirect/pyproject.toml +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/redirect/setup.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/requirements-test.txt +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/scripts/bench_imports.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/setup.cfg +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/setup.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/__init__.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/test_aws_utils.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/test_base_client.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/test_cloudwatch_metrics.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/test_config.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/test_db_client.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/test_error_handler.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/test_es_client.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/test_es_client_compat.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/test_es_query_builder.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/test_jwt_auth.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/test_lambda_helpers.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/test_lazy_imports.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/test_llm.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/test_log_processors.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/test_logging_binding.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/test_logging_fields.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/test_redaction.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/test_secrets_helper.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/test_slack_client.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/test_slack_formatter.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/test_snowflake_client.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/test_timezone.py +0 -0
- {nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/test_utils.py +0 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and breaking changes are called out explicitly below, including historical minor releases
|
|
7
|
+
that contained them.
|
|
8
|
+
|
|
9
|
+
## [Unreleased]
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Add an opt-in `reraise_exceptions` parameter to `powertools_handler`. When enabled,
|
|
14
|
+
handler failures are logged and alerted before the original exception is re-raised so
|
|
15
|
+
stream and asynchronous event sources can apply their configured failure handling.
|
|
16
|
+
The default remains the existing 500 response, while SQS events continue to re-raise
|
|
17
|
+
unconditionally.
|
|
18
|
+
|
|
19
|
+
## [2.3] - 2026-10-12
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- `powertools_handler` re-raises exceptions for SQS event-source mappings after logging and
|
|
24
|
+
alerting, so failed batches are redelivered and can reach a configured DLQ instead of
|
|
25
|
+
being acknowledged by the wrapper's 500 response.
|
|
26
|
+
|
|
27
|
+
## [2.2] - 2026-09-14
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
|
|
31
|
+
- Add response-returning Slack write methods (`post_message`, `post_thread_reply`,
|
|
32
|
+
`post_update`, and `post_file`) for callers that need message timestamps, channel IDs,
|
|
33
|
+
or the raw upload response. The existing bool-returning `send_*` methods retain their
|
|
34
|
+
error-swallowing behavior.
|
|
35
|
+
- Add `SentMessage` as the result type for posted messages and thread replies.
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- `send_file` and `post_file` accept bytes and can upload into a thread with `thread_ts`.
|
|
40
|
+
|
|
41
|
+
## [2.1] - 2026-08-03
|
|
42
|
+
|
|
43
|
+
### Changed
|
|
44
|
+
|
|
45
|
+
- **BREAKING**: replace `pytz` with the standard-library `zoneinfo` API and remove `pytz`
|
|
46
|
+
from the base dependencies. Code importing timezone constants must replace `.localize()`
|
|
47
|
+
with `tzinfo=...`, passing `fold=1` for repeated wall times where the old `is_dst=False`
|
|
48
|
+
default mattered, and replace `.zone` with `.key`. Declare `pytz` directly if application
|
|
49
|
+
code still imports it. The `[timezone]` extra supplies `tzdata` for minimal images without
|
|
50
|
+
a system IANA database.
|
|
51
|
+
- Allow Elasticsearch client majors 7.17, 8.x, and 9.x through the `[elasticsearch]` and
|
|
52
|
+
`all` extras. See
|
|
53
|
+
[ADR-007](docs/decisions/ADR-007-elasticsearch-client-version-range.md).
|
|
54
|
+
|
|
55
|
+
### Fixed
|
|
56
|
+
|
|
57
|
+
- Send Elasticsearch search `size` inside a copied request body, avoiding caller-dict
|
|
58
|
+
mutation and duplicate-value errors on client majors 8.x and 9.x.
|
|
59
|
+
- Return a plain `list` from `ElasticsearchClient.get_indices_info()` on every supported
|
|
60
|
+
Elasticsearch client major.
|
|
61
|
+
- Make no-argument `slack_formatter.format_nz_time()` independent of the host timezone.
|
|
62
|
+
|
|
63
|
+
### Added
|
|
64
|
+
|
|
65
|
+
- Add an Elasticsearch compatibility test matrix for client majors 7.17, 8.x, and 9.x.
|
|
66
|
+
- Add ADRs covering Elasticsearch version support and the move to `zoneinfo`.
|
|
67
|
+
|
|
68
|
+
## [2.0] - 2026-07-13
|
|
69
|
+
|
|
70
|
+
### Removed
|
|
71
|
+
|
|
72
|
+
- **BREAKING**: the `nui_lambda_shared_utils` import shim. Import from `nui_shared_utils` instead (`from nui_shared_utils import ...`). The shim forwarded to `nui_shared_utils` with a `DeprecationWarning` since the 1.3.1 import-root rename and was always slated for removal in 2.0.0.
|
|
73
|
+
- The old distribution name still installs: `pip install nui-lambda-shared-utils` continues to resolve via the redirect package (which depends on `nui-python-shared-utils`). Only the old import root is gone.
|
|
74
|
+
|
|
75
|
+
### Added
|
|
76
|
+
|
|
77
|
+
- Add ADRs for the package's generic scope, lazy integrations, credential precedence,
|
|
78
|
+
explicit Secrets Manager region resolution, and Snowflake SQL API choice.
|
|
79
|
+
|
|
80
|
+
## [1.6.0] - 2026-07-13
|
|
81
|
+
|
|
82
|
+
### Added
|
|
83
|
+
|
|
84
|
+
- Add a backend-neutral structured logging field registry, request-scoped Powertools and
|
|
85
|
+
stdlib binders, binding helpers, field validation, and recursive payload redaction.
|
|
86
|
+
- Add the structured logging contract guide and ADR-001.
|
|
87
|
+
|
|
88
|
+
## [1.5.0] - 2026-07-02
|
|
89
|
+
|
|
90
|
+
### Added
|
|
91
|
+
|
|
92
|
+
- Add optional `unfurl_links` and `unfurl_media` controls to `SlackClient.send_message`.
|
|
93
|
+
Unset values preserve Slack's existing defaults.
|
|
94
|
+
|
|
95
|
+
## [1.4.1] - 2026-06-29
|
|
96
|
+
|
|
97
|
+
### Fixed
|
|
98
|
+
|
|
99
|
+
- Replace the Snowflake extra's direct Git dependency with the published
|
|
100
|
+
`snowflake-sql-api>=0.1.1,<0.2.0` range so PyPI accepts the package metadata.
|
|
101
|
+
|
|
102
|
+
## [1.4.0] - 2026-06-29
|
|
103
|
+
|
|
104
|
+
### Added
|
|
105
|
+
|
|
106
|
+
- Add the pure-Python Snowflake SQL API client behind the optional `[snowflake]` extra,
|
|
107
|
+
including sync and async factories, key-pair credential resolution, query logging, and
|
|
108
|
+
offline tests.
|
|
109
|
+
- Add a generic Anthropic client helper behind the optional `[llm]` extra, with API-key and
|
|
110
|
+
Bedrock authentication, forced tool-use calls, and text calls.
|
|
111
|
+
- Add Snowflake and LLM integration guides.
|
|
112
|
+
|
|
113
|
+
### Changed
|
|
114
|
+
|
|
115
|
+
- **BREAKING**: resolve the Secrets Manager region from the boto3 session, `AWS_REGION`, or
|
|
116
|
+
`AWS_DEFAULT_REGION`, and fail explicitly instead of silently defaulting to
|
|
117
|
+
`ap-southeast-2`.
|
|
118
|
+
|
|
119
|
+
## [1.3.3] - 2026-05-06
|
|
120
|
+
|
|
121
|
+
### Performance
|
|
122
|
+
|
|
123
|
+
- Cold-start init for Lambda consumers reduced by ~400ms.
|
|
124
|
+
- `boto3` and `botocore` are no longer imported at module load. They are deferred to first use inside `secrets_helper.get_secret`, `utils.create_aws_client`/`resolve_aws_region`, and `cloudwatch_metrics.MetricsPublisher` (now lazy-constructs its boto3 client on first metric publish).
|
|
125
|
+
- The package `__init__` uses PEP 562 `__getattr__` so submodules (`slack_client`, `es_client`, `db_client`, `jwt_auth`, `powertools_helpers`, etc.) only load when their attributes are first accessed. `import nui_shared_utils` is ~10ms, down from ~700ms+.
|
|
126
|
+
- Public API is unchanged. Optional integrations still resolve to `None` when their backing dependency is not installed (matching the previous behaviour).
|
|
127
|
+
|
|
128
|
+
### Added
|
|
129
|
+
|
|
130
|
+
- `scripts/bench_imports.py` for measuring `python -X importtime` cumulative cost across common entry points.
|
|
131
|
+
- `tests/test_lazy_imports.py` regression tests (boto3 must not appear in `sys.modules` after a bare package import or after importing `jwt_auth`/`powertools_helpers`).
|
|
132
|
+
|
|
133
|
+
## [1.3.2] - 2026-04-10
|
|
134
|
+
|
|
135
|
+
### Fixed
|
|
136
|
+
|
|
137
|
+
- `get_elasticsearch_credentials()` now checks env vars (`ES_PASSWORD`/`ES_PASS`) before Secrets Manager, aligning with `BaseServiceClient` credential precedence
|
|
138
|
+
- `ESClient._resolve_credentials_from_env()` accepts both `ES_PASSWORD`/`ES_USERNAME` and `ES_PASS`/`ES_USER` conventions
|
|
139
|
+
|
|
140
|
+
## [1.3.1] - 2026-04-05
|
|
141
|
+
|
|
142
|
+
### Changed
|
|
143
|
+
|
|
144
|
+
- Renamed internal import from `nui_lambda_shared_utils` to `nui_shared_utils`
|
|
145
|
+
|
|
146
|
+
## [1.3.0] - 2026-04-05
|
|
147
|
+
|
|
148
|
+
### Changed
|
|
149
|
+
|
|
150
|
+
- Renamed PyPI package from `nui-lambda-shared-utils` to `nui-python-shared-utils`
|
|
151
|
+
- Added explicit license field for twine 6.x compatibility
|
|
152
|
+
- Updated CI publish workflow for new package name and tag pattern
|
|
153
|
+
|
|
154
|
+
## [1.0.0 - 1.2.3] - 2025-11 to 2026-03
|
|
155
|
+
|
|
156
|
+
### Added
|
|
157
|
+
|
|
158
|
+
- **Log Processors Module** - `log_processors` for Kinesis/CloudWatch log extraction
|
|
159
|
+
- **AWS Powertools Integration** - `powertools_helpers` module with `get_powertools_logger()` and `powertools_handler()`
|
|
160
|
+
- **JWT Authentication** - RS256 token validation for API Gateway Lambdas
|
|
161
|
+
- **Slack Block Kit improvements** - Enhanced block builder patterns
|
|
162
|
+
- **`slack-channel-setup` CLI tool** for Slack workspace automation
|
|
163
|
+
- `service_name` parameter to `SlackClient` for custom display names
|
|
164
|
+
- `[powertools]` optional dependency group
|
|
165
|
+
- Streaming bulk operations for Elasticsearch
|
|
166
|
+
- Environment-based credential resolution in `BaseServiceClient`
|
|
167
|
+
- Pass-through account names support
|
|
168
|
+
|
|
169
|
+
### Changed
|
|
170
|
+
|
|
171
|
+
- Rebranded as generic production-ready package (not NUI-specific)
|
|
172
|
+
- Improved Slack Lambda header formatting (cleaner 2-line layout)
|
|
173
|
+
- Documentation reorganization and expansion
|
|
174
|
+
|
|
175
|
+
### Breaking Changes
|
|
176
|
+
|
|
177
|
+
- Removed `SERVICE_EMOJI` from public API exports
|
|
178
|
+
- Removed `get_entity_stats()` and `get_record_stats()` from `DatabaseClient`
|
|
179
|
+
- `format_currency()` now requires explicit currency parameter
|
|
180
|
+
|
|
181
|
+
## [0.0.6] - 2025-11-16
|
|
182
|
+
|
|
183
|
+
### Added
|
|
184
|
+
|
|
185
|
+
- Configuration YAML support for account info
|
|
186
|
+
- RabbitMQ integration utilities
|
|
187
|
+
|
|
188
|
+
### Changed
|
|
189
|
+
|
|
190
|
+
- Enhanced configuration system
|
|
191
|
+
|
|
192
|
+
## [0.0.5] - 2025-09-29
|
|
193
|
+
|
|
194
|
+
### Added
|
|
195
|
+
|
|
196
|
+
- Comprehensive test improvements
|
|
197
|
+
- Better test coverage across modules
|
|
198
|
+
|
|
199
|
+
### Fixed
|
|
200
|
+
|
|
201
|
+
- Various test fixes and improvements
|
|
202
|
+
|
|
203
|
+
## [0.0.4] - 2025-09-05
|
|
204
|
+
|
|
205
|
+
### Changed
|
|
206
|
+
|
|
207
|
+
- Minor improvements and fixes
|
|
208
|
+
|
|
209
|
+
## [0.0.3] - 2025-09-05
|
|
210
|
+
|
|
211
|
+
### Changed
|
|
212
|
+
|
|
213
|
+
- Initial improvements
|
|
214
|
+
|
|
215
|
+
## [0.0.2] - 2025-09-05
|
|
216
|
+
|
|
217
|
+
### Added
|
|
218
|
+
|
|
219
|
+
- Initial package structure
|
|
220
|
+
- Core client implementations (Slack, Elasticsearch, Database)
|
|
221
|
+
- Metrics publishing utilities
|
|
222
|
+
- Error handling decorators
|
|
223
|
+
- Timezone utilities for NZ timezone handling
|
|
224
|
+
|
|
225
|
+
## [0.0.1] - 2025-09-05
|
|
226
|
+
|
|
227
|
+
### Added
|
|
228
|
+
|
|
229
|
+
- Initial release
|
|
230
|
+
- Basic project structure
|
|
231
|
+
- Python package configuration
|
|
232
|
+
|
|
233
|
+
[Unreleased]: https://github.com/nuimarkets/nui-python-shared-utils/compare/2.3...HEAD
|
|
234
|
+
[2.3]: https://github.com/nuimarkets/nui-python-shared-utils/compare/2.2...2.3
|
|
235
|
+
[2.2]: https://github.com/nuimarkets/nui-python-shared-utils/compare/2.1...2.2
|
|
236
|
+
[2.1]: https://github.com/nuimarkets/nui-python-shared-utils/compare/2.0...2.1
|
|
237
|
+
[2.0]: https://github.com/nuimarkets/nui-python-shared-utils/compare/1.6.0...2.0
|
|
238
|
+
[1.6.0]: https://github.com/nuimarkets/nui-python-shared-utils/compare/1.5.0...1.6.0
|
|
239
|
+
[1.5.0]: https://github.com/nuimarkets/nui-python-shared-utils/compare/1.4.1...1.5.0
|
|
240
|
+
[1.4.1]: https://github.com/nuimarkets/nui-python-shared-utils/compare/1.4.0...1.4.1
|
|
241
|
+
[1.4.0]: https://github.com/nuimarkets/nui-python-shared-utils/compare/1.3.3...1.4.0
|
|
242
|
+
[1.3.3]: https://github.com/nuimarkets/nui-python-shared-utils/compare/1.3.2...1.3.3
|
|
243
|
+
[1.3.2]: https://github.com/nuimarkets/nui-python-shared-utils/compare/1.3.1...1.3.2
|
|
244
|
+
[1.3.1]: https://github.com/nuimarkets/nui-python-shared-utils/compare/1.3.0...1.3.1
|
|
245
|
+
[1.3.0]: https://github.com/nuimarkets/nui-python-shared-utils/compare/0.0.6...1.3.0
|
|
246
|
+
[0.0.6]: https://github.com/nuimarkets/nui-python-shared-utils/compare/0.0.5...0.0.6
|
|
247
|
+
[0.0.5]: https://github.com/nuimarkets/nui-python-shared-utils/compare/0.0.4...0.0.5
|
|
248
|
+
[0.0.4]: https://github.com/nuimarkets/nui-python-shared-utils/compare/0.0.3...0.0.4
|
|
249
|
+
[0.0.3]: https://github.com/nuimarkets/nui-python-shared-utils/compare/0.0.2...0.0.3
|
|
250
|
+
[0.0.2]: https://github.com/nuimarkets/nui-python-shared-utils/compare/0.0.1...0.0.2
|
|
251
|
+
[0.0.1]: https://github.com/nuimarkets/nui-python-shared-utils/releases/tag/0.0.1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nui-python-shared-utils
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4
|
|
4
4
|
Summary: Shared Python utilities for AWS Lambda, CLI tools, and agents with Slack, Elasticsearch, and monitoring integrations
|
|
5
5
|
Home-page: https://github.com/nuimarkets/nui-python-shared-utils
|
|
6
6
|
Author: NUI Markets
|
{nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/guides/powertools-integration.md
RENAMED
|
@@ -168,6 +168,9 @@ def powertools_handler(
|
|
|
168
168
|
service_name: str,
|
|
169
169
|
metrics_namespace: Optional[str] = None,
|
|
170
170
|
slack_alert_channel: Optional[str] = None,
|
|
171
|
+
slack_account_names: Optional[Dict[str, str]] = None,
|
|
172
|
+
slack_account_names_config: Optional[str] = None,
|
|
173
|
+
reraise_exceptions: bool = False,
|
|
171
174
|
):
|
|
172
175
|
"""
|
|
173
176
|
Decorator for Lambda handlers with logging, metrics, and error handling.
|
|
@@ -176,6 +179,9 @@ def powertools_handler(
|
|
|
176
179
|
service_name: Service identifier for logging and metrics dimensions
|
|
177
180
|
metrics_namespace: CloudWatch namespace (e.g., "NUI/TenderAnalyser")
|
|
178
181
|
slack_alert_channel: Slack channel for error alerts (e.g., "#alerts")
|
|
182
|
+
slack_account_names: AWS account ID to display-name mapping for alerts
|
|
183
|
+
slack_account_names_config: YAML file containing account-name defaults
|
|
184
|
+
reraise_exceptions: Re-raise after logging and alerting. Defaults to False.
|
|
179
185
|
"""
|
|
180
186
|
```
|
|
181
187
|
|
|
@@ -193,7 +199,46 @@ The decorator logs the exception and sends the Slack alert (if configured), then
|
|
|
193
199
|
group waits until the batch succeeds or redrives, and the Slack alert fires on every
|
|
194
200
|
receive. For per-record retries, enable `ReportBatchItemFailures` on the mapping and
|
|
195
201
|
return `{"batchItemFailures": [...]}` from the handler; the decorator passes it through.
|
|
196
|
-
- **Every other event
|
|
202
|
+
- **Every other event, by default**: returns
|
|
203
|
+
`{"statusCode": 500, "body": "Internal Server Error"}` to preserve existing behavior.
|
|
204
|
+
- **Every other event with `reraise_exceptions=True`**: re-raises after logging the
|
|
205
|
+
exception and sending the configured Slack alert. Use this for Kinesis and DynamoDB
|
|
206
|
+
Streams, where a raised exception prevents the checkpoint from advancing, and for
|
|
207
|
+
asynchronous sources such as SNS, S3, and EventBridge, where Lambda failure handling
|
|
208
|
+
depends on the invocation failing.
|
|
209
|
+
|
|
210
|
+
The option is handler-wide rather than an event-source list because the handler's trigger
|
|
211
|
+
configuration determines whether Lambda failure semantics are appropriate. SQS does not
|
|
212
|
+
require the option and continues to re-raise unconditionally.
|
|
213
|
+
|
|
214
|
+
For synchronous triggers such as API Gateway, enabling this option changes failures from
|
|
215
|
+
the decorator's 500 response into an unhandled Lambda invocation error. Enable it only when
|
|
216
|
+
that caller-visible behavior is intended.
|
|
217
|
+
|
|
218
|
+
### Re-raise Failures for Stream and Async Sources
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
from nui_shared_utils import powertools_handler
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
@powertools_handler(
|
|
225
|
+
service_name="event-processor",
|
|
226
|
+
slack_alert_channel="#production-alerts",
|
|
227
|
+
reraise_exceptions=True,
|
|
228
|
+
)
|
|
229
|
+
def handler(event, context):
|
|
230
|
+
records = event.get("Records", [])
|
|
231
|
+
for record in records:
|
|
232
|
+
print(record)
|
|
233
|
+
return {"processed": len(records)}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Logging and Slack alerting run before the original exception is re-raised. Configure the
|
|
237
|
+
trigger's retry, bisect, failure destination, or dead-letter behavior separately in AWS.
|
|
238
|
+
Every retry invokes the decorator again, so a configured Slack alert can be sent once per
|
|
239
|
+
attempt. For Kinesis and DynamoDB Streams, maximum retry attempts and maximum record age
|
|
240
|
+
bound retries. Bisect-on-error isolates a failing record by adding invocations, which do not
|
|
241
|
+
consume the retry quota and can produce additional alerts.
|
|
197
242
|
|
|
198
243
|
### Basic Usage (Logging Only)
|
|
199
244
|
|
{nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/powertools_helpers.py
RENAMED
|
@@ -162,6 +162,7 @@ def powertools_handler(
|
|
|
162
162
|
slack_alert_channel: Optional[str] = None,
|
|
163
163
|
slack_account_names: Optional[Dict[str, str]] = None,
|
|
164
164
|
slack_account_names_config: Optional[str] = None,
|
|
165
|
+
reraise_exceptions: bool = False,
|
|
165
166
|
):
|
|
166
167
|
"""
|
|
167
168
|
Decorator for Lambda handlers with logging, metrics, and error handling.
|
|
@@ -175,6 +176,7 @@ def powertools_handler(
|
|
|
175
176
|
- Optional metrics.log_metrics integration (if metrics_namespace provided)
|
|
176
177
|
- Structured exception logging with traceback
|
|
177
178
|
- Optional Slack alerts on failures (if slack_alert_channel provided)
|
|
179
|
+
- Optional exception re-raising for event sources that use Lambda failure semantics
|
|
178
180
|
- Graceful degradation if Slack client unavailable
|
|
179
181
|
- Proper Lambda error response formatting
|
|
180
182
|
|
|
@@ -190,6 +192,11 @@ def powertools_handler(
|
|
|
190
192
|
Values loaded from this file are used as defaults, but any keys
|
|
191
193
|
provided in ``slack_account_names`` take precedence and override
|
|
192
194
|
the YAML-loaded values.
|
|
195
|
+
reraise_exceptions: Re-raise handler exceptions after logging and Slack
|
|
196
|
+
alerting. Enable this for event sources such as Kinesis, DynamoDB
|
|
197
|
+
Streams, SNS, S3, and EventBridge that rely on a raised exception to
|
|
198
|
+
record a failed invocation and apply their retry or failure handling.
|
|
199
|
+
Defaults to False to preserve the existing 500 response behavior.
|
|
193
200
|
|
|
194
201
|
Returns:
|
|
195
202
|
Decorator function for Lambda handlers
|
|
@@ -214,13 +221,14 @@ def powertools_handler(
|
|
|
214
221
|
for API Gateway, ``{"batchItemFailures": [...]}`` for an SQS mapping with
|
|
215
222
|
``ReportBatchItemFailures``.
|
|
216
223
|
|
|
217
|
-
On exception, returns ``{"statusCode": 500, "body": "Internal Server Error"}
|
|
218
|
-
|
|
219
|
-
alerting
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
+
On exception, returns ``{"statusCode": 500, "body": "Internal Server Error"}``
|
|
225
|
+
by default. Set ``reraise_exceptions=True`` to re-raise after logging and
|
|
226
|
+
alerting when the event source relies on Lambda failure semantics. SQS events
|
|
227
|
+
are always re-raised, regardless of this option: a returned value tells the
|
|
228
|
+
SQS mapping the batch succeeded (with ``ReportBatchItemFailures``, a response
|
|
229
|
+
without ``batchItemFailures`` means every record did), so the 500 dict would
|
|
230
|
+
delete the batch. Re-raising leaves it on the queue for redelivery, and in the
|
|
231
|
+
DLQ after ``maxReceiveCount`` when the queue has a redrive policy.
|
|
224
232
|
"""
|
|
225
233
|
|
|
226
234
|
def decorator(func: Callable) -> Callable:
|
|
@@ -297,8 +305,9 @@ def powertools_handler(
|
|
|
297
305
|
except Exception as slack_error:
|
|
298
306
|
logger.warning("Failed to send Slack alert: %s", slack_error)
|
|
299
307
|
|
|
300
|
-
# Re-raise for SQS so the batch stays on the queue
|
|
301
|
-
|
|
308
|
+
# Re-raise for SQS so the batch stays on the queue, or when the caller
|
|
309
|
+
# opts into Lambda failure semantics; see the Note above.
|
|
310
|
+
if is_sqs or reraise_exceptions:
|
|
302
311
|
raise
|
|
303
312
|
|
|
304
313
|
# Return proper Lambda error response
|
{nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/test_powertools_helpers.py
RENAMED
|
@@ -7,7 +7,6 @@ from unittest.mock import MagicMock, patch
|
|
|
7
7
|
|
|
8
8
|
import pytest
|
|
9
9
|
|
|
10
|
-
|
|
11
10
|
# Test markers
|
|
12
11
|
pytestmark = pytest.mark.unit
|
|
13
12
|
|
|
@@ -338,6 +337,18 @@ def _sqs_event(*message_ids):
|
|
|
338
337
|
return {"Records": [{"messageId": mid, "eventSource": "aws:sqs", "body": "{}"} for mid in message_ids]}
|
|
339
338
|
|
|
340
339
|
|
|
340
|
+
def _kinesis_event():
|
|
341
|
+
return {
|
|
342
|
+
"Records": [
|
|
343
|
+
{
|
|
344
|
+
"eventID": "shardId-000000000000:1",
|
|
345
|
+
"eventSource": "aws:kinesis",
|
|
346
|
+
"kinesis": {"sequenceNumber": "1", "data": "e30="},
|
|
347
|
+
}
|
|
348
|
+
]
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
|
|
341
352
|
class TestPowertoolsHandlerSqs:
|
|
342
353
|
"""An SQS batch must stay on the queue when the handler raises.
|
|
343
354
|
|
|
@@ -481,6 +492,69 @@ class TestPowertoolsHandlerSqs:
|
|
|
481
492
|
assert result == {"statusCode": 500, "body": "Internal Server Error"}
|
|
482
493
|
|
|
483
494
|
|
|
495
|
+
class TestPowertoolsHandlerReraise:
|
|
496
|
+
def test_kinesis_exception_is_reraised_when_enabled(self, monkeypatch):
|
|
497
|
+
monkeypatch.delenv("AWS_LAMBDA_RUNTIME_API", raising=False)
|
|
498
|
+
|
|
499
|
+
from nui_shared_utils.powertools_helpers import powertools_handler
|
|
500
|
+
|
|
501
|
+
error = RuntimeError("Kinesis record failed")
|
|
502
|
+
|
|
503
|
+
@powertools_handler(service_name="test-service", reraise_exceptions=True)
|
|
504
|
+
def handler(event, context):
|
|
505
|
+
raise error
|
|
506
|
+
|
|
507
|
+
with pytest.raises(RuntimeError) as excinfo:
|
|
508
|
+
handler(_kinesis_event(), MagicMock())
|
|
509
|
+
|
|
510
|
+
assert excinfo.value is error
|
|
511
|
+
|
|
512
|
+
def test_kinesis_exception_keeps_500_by_default(self, monkeypatch):
|
|
513
|
+
monkeypatch.delenv("AWS_LAMBDA_RUNTIME_API", raising=False)
|
|
514
|
+
|
|
515
|
+
from nui_shared_utils.powertools_helpers import powertools_handler
|
|
516
|
+
|
|
517
|
+
@powertools_handler(service_name="test-service", reraise_exceptions=False)
|
|
518
|
+
def handler(event, context):
|
|
519
|
+
raise RuntimeError("Kinesis record failed")
|
|
520
|
+
|
|
521
|
+
result = handler(_kinesis_event(), MagicMock())
|
|
522
|
+
|
|
523
|
+
assert result == {"statusCode": 500, "body": "Internal Server Error"}
|
|
524
|
+
|
|
525
|
+
def test_eventbridge_exception_alerts_before_reraising(self, monkeypatch):
|
|
526
|
+
monkeypatch.delenv("AWS_LAMBDA_RUNTIME_API", raising=False)
|
|
527
|
+
|
|
528
|
+
from nui_shared_utils.powertools_helpers import powertools_handler
|
|
529
|
+
|
|
530
|
+
calls = []
|
|
531
|
+
mock_logger = MagicMock()
|
|
532
|
+
mock_logger.inject_lambda_context.side_effect = lambda func: func
|
|
533
|
+
mock_logger.exception.side_effect = lambda *args, **kwargs: calls.append("log")
|
|
534
|
+
mock_slack_client = MagicMock()
|
|
535
|
+
mock_slack_client.send_message.side_effect = lambda **kwargs: calls.append("slack")
|
|
536
|
+
|
|
537
|
+
with patch("nui_shared_utils.powertools_helpers.get_powertools_logger", return_value=mock_logger):
|
|
538
|
+
with patch("nui_shared_utils.powertools_helpers.SLACK_CLIENT_AVAILABLE", True):
|
|
539
|
+
with patch("nui_shared_utils.powertools_helpers.SlackClient", return_value=mock_slack_client):
|
|
540
|
+
|
|
541
|
+
@powertools_handler(
|
|
542
|
+
service_name="test-service",
|
|
543
|
+
slack_alert_channel="#errors",
|
|
544
|
+
reraise_exceptions=True,
|
|
545
|
+
)
|
|
546
|
+
def handler(event, context):
|
|
547
|
+
raise ValueError("EventBridge event failed")
|
|
548
|
+
|
|
549
|
+
with pytest.raises(ValueError, match="EventBridge event failed"):
|
|
550
|
+
handler(
|
|
551
|
+
{"source": "example.orders", "detail-type": "Order Created", "detail": {}},
|
|
552
|
+
MagicMock(),
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
assert calls == ["log", "slack"]
|
|
556
|
+
|
|
557
|
+
|
|
484
558
|
class TestOptionalImports:
|
|
485
559
|
"""Tests for graceful degradation of optional dependencies"""
|
|
486
560
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/getting-started/configuration.md
RENAMED
|
File without changes
|
{nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/getting-started/installation.md
RENAMED
|
File without changes
|
{nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/getting-started/quickstart.md
RENAMED
|
File without changes
|
|
File without changes
|
{nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/guides/elasticsearch-integration.md
RENAMED
|
File without changes
|
{nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/guides/jwt-authentication.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/guides/slack-integration.md
RENAMED
|
File without changes
|
{nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/docs/guides/snowflake-integration.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/cloudwatch_metrics.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/error_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
{nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/es_query_builder.py
RENAMED
|
File without changes
|
|
File without changes
|
{nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/lambda_helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
{nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/log_processors.py
RENAMED
|
File without changes
|
{nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/logging/__init__.py
RENAMED
|
File without changes
|
{nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/logging/binding.py
RENAMED
|
File without changes
|
{nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/logging/fields.py
RENAMED
|
File without changes
|
{nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/logging/redaction.py
RENAMED
|
File without changes
|
{nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/secrets_helper.py
RENAMED
|
File without changes
|
{nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/slack_client.py
RENAMED
|
File without changes
|
{nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/slack_formatter.py
RENAMED
|
File without changes
|
{nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/slack_setup/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/nui_shared_utils/snowflake_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nui_python_shared_utils-2.3 → nui_python_shared_utils-2.4}/tests/test_cloudwatch_metrics.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|