nui-python-shared-utils 2.4__tar.gz → 2.4.1__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 → nui_python_shared_utils-2.4.1}/.github/workflows/ci.yml +1 -1
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/.github/workflows/publish.yml +1 -1
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/.github/workflows/test.yml +1 -1
- nui_python_shared_utils-2.4.1/.markdownlint-base.yaml +66 -0
- nui_python_shared_utils-2.4.1/.markdownlint-cli2.yaml +17 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/CHANGELOG.md +15 -1
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/CONTRIBUTING.md +1 -1
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/PKG-INFO +3 -4
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/README.md +1 -1
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/decisions/ADR-008-stdlib-zoneinfo-over-pytz.md +4 -3
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/getting-started/installation.md +3 -3
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/guides/shared-types.md +15 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/mypy.ini +1 -1
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_python_shared_utils.egg-info/SOURCES.txt +1 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/logging/binding.py +0 -1
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/timezone.py +4 -4
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/pyproject.toml +3 -4
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/redirect/pyproject.toml +1 -1
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/setup.py +1 -2
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_slack_client.py +66 -51
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_timezone.py +37 -0
- nui_python_shared_utils-2.4/.markdownlint-cli2.yaml +0 -47
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/.editorconfig +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/AGENTS.md +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/CLAUDE.md +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/LICENSE +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/MANIFEST.in +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/README.md +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/decisions/ADR-001-structured-logging-field-vocabulary.md +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/decisions/ADR-002-keep-shared-utilities-generic.md +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/decisions/ADR-003-lazy-load-integrations.md +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/decisions/ADR-004-credential-resolution-precedence.md +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/decisions/ADR-005-explicit-secrets-manager-region.md +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/decisions/ADR-006-snowflake-sql-api-client.md +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/decisions/ADR-007-elasticsearch-client-version-range.md +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/decisions/README.md +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/development/testing.md +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/getting-started/configuration.md +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/getting-started/quickstart.md +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/guides/cli-tools.md +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/guides/elasticsearch-integration.md +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/guides/jwt-authentication.md +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/guides/lambda-utilities.md +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/guides/llm-integration.md +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/guides/log-processing.md +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/guides/powertools-integration.md +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/guides/slack-integration.md +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/guides/snowflake-integration.md +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/guides/structured-logging-contract.md +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/__init__.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/base_client.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/cli.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/cloudwatch_metrics.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/config.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/db_client.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/error_handler.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/es_client.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/es_query_builder.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/jwt_auth.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/lambda_helpers.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/llm.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/log_processors.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/logging/__init__.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/logging/fields.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/logging/redaction.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/powertools_helpers.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/secrets_helper.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/slack_client.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/slack_formatter.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/slack_setup/__init__.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/slack_setup/channel_creator.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/slack_setup/channel_definitions.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/slack_setup/setup_helpers.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/snowflake_client.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/utils.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/pytest.ini +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/redirect/README.md +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/redirect/setup.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/requirements-test.txt +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/scripts/bench_imports.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/setup.cfg +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/__init__.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_aws_utils.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_base_client.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_cloudwatch_metrics.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_config.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_db_client.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_error_handler.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_es_client.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_es_client_compat.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_es_query_builder.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_jwt_auth.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_lambda_helpers.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_lazy_imports.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_llm.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_log_processors.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_logging_binding.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_logging_fields.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_powertools_helpers.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_redaction.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_secrets_helper.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_slack_formatter.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_snowflake_client.py +0 -0
- {nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_utils.py +0 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# === managed by sync-markdownlint.sh — DO NOT EDIT ===
|
|
2
|
+
# Source of truth: shared tooling config/markdownlint-base.yaml
|
|
3
|
+
# Change rules in the source, then run: sync-markdownlint.sh --update
|
|
4
|
+
# stamp: e9f1a3abeab6
|
|
5
|
+
# === end managed header (sync-markdownlint.sh) ===
|
|
6
|
+
# Shared markdownlint ruleset (FLAT format).
|
|
7
|
+
# See: https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md
|
|
8
|
+
#
|
|
9
|
+
# SOURCE OF TRUTH. Repos adopt this by vendoring a stamped copy as
|
|
10
|
+
# `.markdownlint-base.yaml` (via sync-markdownlint.sh) and pointing their own
|
|
11
|
+
# `.markdownlint-cli2.yaml` at it with `config: { extends: .markdownlint-base.yaml }`,
|
|
12
|
+
# then adding repo-specific `globs`/`ignores` (and any rare repo-only rule tweaks).
|
|
13
|
+
#
|
|
14
|
+
# This file is FLAT (rules at top level, no `config:` wrapper) because that is
|
|
15
|
+
# the format markdownlint's `extends` requires. Keep it rules-only: file
|
|
16
|
+
# selection (globs) and exclusions (ignores) are per-repo.
|
|
17
|
+
|
|
18
|
+
# Enable all rules by default; disables/customisations follow.
|
|
19
|
+
default: true
|
|
20
|
+
|
|
21
|
+
# MD013: Line length - off. Code blocks, tables, and URLs routinely exceed any
|
|
22
|
+
# sane width; wrapping prose to a column is not a standard we enforce.
|
|
23
|
+
MD013: false
|
|
24
|
+
|
|
25
|
+
# MD033: Inline HTML - allowed. Needed for details/summary, anchors, and the
|
|
26
|
+
# HTML comment markers some docs use (companions metadata, stay markers).
|
|
27
|
+
MD033: false
|
|
28
|
+
|
|
29
|
+
# MD034: Bare URLs - off. Bare URLs in reference docs are intentional.
|
|
30
|
+
MD034: false
|
|
31
|
+
|
|
32
|
+
# MD041: First line in file should be a top-level heading - off. Many docs open
|
|
33
|
+
# with YAML frontmatter or a metadata block, not an H1.
|
|
34
|
+
MD041: false
|
|
35
|
+
|
|
36
|
+
# MD024: Multiple headings with the same content - allowed when not siblings
|
|
37
|
+
# (e.g. a repeated "Example" under different sections).
|
|
38
|
+
MD024:
|
|
39
|
+
siblings_only: true
|
|
40
|
+
|
|
41
|
+
# MD046: Code block style - require fenced (not indented) blocks.
|
|
42
|
+
MD046:
|
|
43
|
+
style: "fenced"
|
|
44
|
+
|
|
45
|
+
# MD026: Trailing punctuation in headings - off. "Note:" / "Warning!" are fine.
|
|
46
|
+
MD026: false
|
|
47
|
+
|
|
48
|
+
# MD029: Ordered list item prefix - off. Docs mix "1. 1. 1." and "1. 2. 3." and
|
|
49
|
+
# both render correctly; not worth churning.
|
|
50
|
+
MD029: false
|
|
51
|
+
|
|
52
|
+
# MD036: Emphasis used instead of a heading - off. Bold-as-label
|
|
53
|
+
# ("**Root cause:**", date stamps) is an accepted convention here.
|
|
54
|
+
MD036: false
|
|
55
|
+
|
|
56
|
+
# MD040: Fenced code language - off. Not every code block has (or needs) a
|
|
57
|
+
# language tag; forcing one produces noise on output samples and snippets.
|
|
58
|
+
MD040: false
|
|
59
|
+
|
|
60
|
+
# MD051: Link fragments should be valid - off. Cross-doc anchor validation is
|
|
61
|
+
# unreliable across the workspace's mixed heading styles and generated pages.
|
|
62
|
+
MD051: false
|
|
63
|
+
|
|
64
|
+
# MD060: Table column style - off. Minor pipe-alignment preference; the corpus
|
|
65
|
+
# has always mixed styles.
|
|
66
|
+
MD060: false
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Markdownlint configuration for nui-python-shared-utils
|
|
2
|
+
# Shared rules are vendored by sync-markdownlint.sh.
|
|
3
|
+
|
|
4
|
+
config:
|
|
5
|
+
extends: .markdownlint-base.yaml
|
|
6
|
+
|
|
7
|
+
# File patterns to check
|
|
8
|
+
globs:
|
|
9
|
+
- "**/*.md"
|
|
10
|
+
|
|
11
|
+
# Exclusions
|
|
12
|
+
ignores:
|
|
13
|
+
- "node_modules/**"
|
|
14
|
+
- "vendor/**"
|
|
15
|
+
- ".git/**"
|
|
16
|
+
- "venv/**"
|
|
17
|
+
- ".venv/**"
|
|
@@ -8,6 +8,19 @@ that contained them.
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- **BREAKING**: require Python 3.10 or newer in both the main package and the legacy
|
|
14
|
+
redirect package. Python 3.9 is no longer supported or included in CI test matrices.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Use canonical IANA names `America/New_York` and `America/Los_Angeles` for US timezone
|
|
19
|
+
constants and option labels, so importing timezone utilities and Slack formatting works
|
|
20
|
+
on systems without legacy timezone links.
|
|
21
|
+
|
|
22
|
+
## [2.4] - 2026-10-20
|
|
23
|
+
|
|
11
24
|
### Added
|
|
12
25
|
|
|
13
26
|
- Add an opt-in `reraise_exceptions` parameter to `powertools_handler`. When enabled,
|
|
@@ -230,7 +243,8 @@ that contained them.
|
|
|
230
243
|
- Basic project structure
|
|
231
244
|
- Python package configuration
|
|
232
245
|
|
|
233
|
-
[Unreleased]: https://github.com/nuimarkets/nui-python-shared-utils/compare/2.
|
|
246
|
+
[Unreleased]: https://github.com/nuimarkets/nui-python-shared-utils/compare/2.4...HEAD
|
|
247
|
+
[2.4]: https://github.com/nuimarkets/nui-python-shared-utils/compare/2.3...2.4
|
|
234
248
|
[2.3]: https://github.com/nuimarkets/nui-python-shared-utils/compare/2.2...2.3
|
|
235
249
|
[2.2]: https://github.com/nuimarkets/nui-python-shared-utils/compare/2.1...2.2
|
|
236
250
|
[2.1]: https://github.com/nuimarkets/nui-python-shared-utils/compare/2.0...2.1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nui-python-shared-utils
|
|
3
|
-
Version: 2.4
|
|
3
|
+
Version: 2.4.1
|
|
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
|
|
@@ -36,7 +36,6 @@ Classifier: Intended Audience :: Developers
|
|
|
36
36
|
Classifier: License :: OSI Approved :: MIT License
|
|
37
37
|
Classifier: Operating System :: OS Independent
|
|
38
38
|
Classifier: Programming Language :: Python :: 3
|
|
39
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
40
39
|
Classifier: Programming Language :: Python :: 3.10
|
|
41
40
|
Classifier: Programming Language :: Python :: 3.11
|
|
42
41
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -45,7 +44,7 @@ Classifier: Topic :: System :: Monitoring
|
|
|
45
44
|
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
46
45
|
Classifier: Environment :: Console
|
|
47
46
|
Classifier: Framework :: AWS CDK
|
|
48
|
-
Requires-Python: >=3.
|
|
47
|
+
Requires-Python: >=3.10
|
|
49
48
|
Description-Content-Type: text/markdown
|
|
50
49
|
License-File: LICENSE
|
|
51
50
|
Requires-Dist: boto3>=1.20.0
|
|
@@ -102,7 +101,7 @@ Dynamic: requires-python
|
|
|
102
101
|
# NUI Python Shared Utilities
|
|
103
102
|
|
|
104
103
|
[](https://badge.fury.io/py/nui-python-shared-utils)
|
|
105
|
-
[](https://www.python.org/downloads/)
|
|
106
105
|
[](https://opensource.org/licenses/MIT)
|
|
107
106
|
|
|
108
107
|
Production-ready shared Python utilities for AWS Lambda functions, CLI tools, and agents. Provides standardized integrations for Slack, Elasticsearch, database, and monitoring. Built and battle-tested on the NUI platform with sensible defaults configurable for any environment.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# NUI Python Shared Utilities
|
|
2
2
|
|
|
3
3
|
[](https://badge.fury.io/py/nui-python-shared-utils)
|
|
4
|
-
[](https://www.python.org/downloads/)
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|
|
|
7
7
|
Production-ready shared Python utilities for AWS Lambda functions, CLI tools, and agents. Provides standardized integrations for Slack, Elasticsearch, database, and monitoring. Built and battle-tested on the NUI platform with sensible defaults configurable for any environment.
|
|
@@ -11,7 +11,7 @@ that wanted only the logging contract or the metrics helper still shipped it.
|
|
|
11
11
|
|
|
12
12
|
The cost is not the code. `pytz` bundles its own copy of the Olson/IANA database (0.86 MB of a
|
|
13
13
|
1.0 MB installed package, measured on `pytz==2025.2`), and that copy is redundant on
|
|
14
|
-
every interpreter this package supports: `requires-python = ">=3.
|
|
14
|
+
every interpreter this package supports: `requires-python = ">=3.10"`, and `zoneinfo` has been in
|
|
15
15
|
the standard library since 3.9, reading the system database that Linux, macOS, and the AWS Lambda
|
|
16
16
|
runtimes all ship.
|
|
17
17
|
|
|
@@ -82,8 +82,9 @@ a database to everyone:
|
|
|
82
82
|
|
|
83
83
|
## Load-bearing premises (supersede triggers)
|
|
84
84
|
|
|
85
|
-
- **
|
|
86
|
-
would need `backports.zoneinfo`, at
|
|
85
|
+
- **All supported interpreters include stdlib `zoneinfo`.** The package requires Python 3.10
|
|
86
|
+
or higher. Supporting an interpreter older than 3.9 would need `backports.zoneinfo`, at
|
|
87
|
+
which point the calculus changes.
|
|
87
88
|
- **Target runtimes ship a system IANA database.** True of Linux, macOS, and the AWS Lambda
|
|
88
89
|
runtimes today. If the primary deployment target became one that does not, `tzdata` belongs in
|
|
89
90
|
the base dependencies unconditionally and this ADR's marker split is obsolete.
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/getting-started/installation.md
RENAMED
|
@@ -4,7 +4,7 @@ This guide covers installation, dependency management, and environment setup for
|
|
|
4
4
|
|
|
5
5
|
## Requirements
|
|
6
6
|
|
|
7
|
-
- Python 3.
|
|
7
|
+
- Python 3.10 or higher
|
|
8
8
|
- AWS credentials configured for Secrets Manager access
|
|
9
9
|
- Optional: AWS CLI for credential management
|
|
10
10
|
|
|
@@ -139,7 +139,7 @@ pip install nui-python-shared-utils[all]
|
|
|
139
139
|
|
|
140
140
|
```bash
|
|
141
141
|
# Create environment
|
|
142
|
-
conda create -n nui-utils python=3.
|
|
142
|
+
conda create -n nui-utils python=3.10
|
|
143
143
|
|
|
144
144
|
# Activate environment
|
|
145
145
|
conda activate nui-utils
|
|
@@ -209,7 +209,7 @@ aws lambda publish-layer-version \
|
|
|
209
209
|
--layer-name nui-python-shared-utils \
|
|
210
210
|
--description "NUI Lambda Shared Utilities" \
|
|
211
211
|
--zip-file fileb://nui-utils-layer.zip \
|
|
212
|
-
--compatible-runtimes python3.
|
|
212
|
+
--compatible-runtimes python3.10 python3.11
|
|
213
213
|
```
|
|
214
214
|
|
|
215
215
|
### Using in Lambda Function
|
|
@@ -103,6 +103,7 @@ client.get_connection_info() -> Dict
|
|
|
103
103
|
```
|
|
104
104
|
|
|
105
105
|
**Connection Pool Entry Structure:**
|
|
106
|
+
|
|
106
107
|
```python
|
|
107
108
|
{
|
|
108
109
|
"connection": pymysql.Connection,
|
|
@@ -111,6 +112,7 @@ client.get_connection_info() -> Dict
|
|
|
111
112
|
```
|
|
112
113
|
|
|
113
114
|
**Connection Info Response:**
|
|
115
|
+
|
|
114
116
|
```python
|
|
115
117
|
{
|
|
116
118
|
"host": str,
|
|
@@ -169,6 +171,7 @@ client.get_indices_info(pattern="*") -> List[Dict]
|
|
|
169
171
|
```
|
|
170
172
|
|
|
171
173
|
**Service Stats Response:**
|
|
174
|
+
|
|
172
175
|
```python
|
|
173
176
|
{
|
|
174
177
|
"service": str,
|
|
@@ -183,6 +186,7 @@ client.get_indices_info(pattern="*") -> List[Dict]
|
|
|
183
186
|
```
|
|
184
187
|
|
|
185
188
|
**Cluster Info Response:**
|
|
189
|
+
|
|
186
190
|
```python
|
|
187
191
|
{
|
|
188
192
|
"version": str,
|
|
@@ -222,6 +226,7 @@ client.get_bot_info() -> Dict
|
|
|
222
226
|
```
|
|
223
227
|
|
|
224
228
|
**Lambda Context Structure:**
|
|
229
|
+
|
|
225
230
|
```python
|
|
226
231
|
{
|
|
227
232
|
"function_name": str,
|
|
@@ -240,6 +245,7 @@ client.get_bot_info() -> Dict
|
|
|
240
245
|
```
|
|
241
246
|
|
|
242
247
|
**Bot Info Response:**
|
|
248
|
+
|
|
243
249
|
```python
|
|
244
250
|
{
|
|
245
251
|
"bot_id": str,
|
|
@@ -274,6 +280,7 @@ result = matcher.categorize_error(exception)
|
|
|
274
280
|
```
|
|
275
281
|
|
|
276
282
|
**Categorized Error Response:**
|
|
283
|
+
|
|
277
284
|
```python
|
|
278
285
|
{
|
|
279
286
|
"error": str,
|
|
@@ -286,6 +293,7 @@ result = matcher.categorize_error(exception)
|
|
|
286
293
|
```
|
|
287
294
|
|
|
288
295
|
**Built-in Error Categories:**
|
|
296
|
+
|
|
289
297
|
- `data_format` - JSON parsing errors
|
|
290
298
|
- `authentication` - Auth failures
|
|
291
299
|
- `database` - DB timeouts
|
|
@@ -310,6 +318,7 @@ summary = aggregator.get_summary()
|
|
|
310
318
|
```
|
|
311
319
|
|
|
312
320
|
**Error Summary Response:**
|
|
321
|
+
|
|
313
322
|
```python
|
|
314
323
|
{
|
|
315
324
|
"total_errors": int,
|
|
@@ -360,6 +369,7 @@ publisher.flush() -> bool
|
|
|
360
369
|
```
|
|
361
370
|
|
|
362
371
|
**Metric Data Structure (internal buffer):**
|
|
372
|
+
|
|
363
373
|
```python
|
|
364
374
|
{
|
|
365
375
|
"MetricName": str,
|
|
@@ -429,6 +439,7 @@ with TimedMetric(publisher, "DatabaseQuery", unit="Milliseconds", dimensions=Non
|
|
|
429
439
|
### Credential Structures
|
|
430
440
|
|
|
431
441
|
**Database Credentials:**
|
|
442
|
+
|
|
432
443
|
```python
|
|
433
444
|
{
|
|
434
445
|
"host": str,
|
|
@@ -440,6 +451,7 @@ with TimedMetric(publisher, "DatabaseQuery", unit="Milliseconds", dimensions=Non
|
|
|
440
451
|
```
|
|
441
452
|
|
|
442
453
|
**Elasticsearch Credentials:**
|
|
454
|
+
|
|
443
455
|
```python
|
|
444
456
|
{
|
|
445
457
|
"host": str, # With port (e.g., "elastic:9200")
|
|
@@ -449,6 +461,7 @@ with TimedMetric(publisher, "DatabaseQuery", unit="Milliseconds", dimensions=Non
|
|
|
449
461
|
```
|
|
450
462
|
|
|
451
463
|
**Slack Credentials:**
|
|
464
|
+
|
|
452
465
|
```python
|
|
453
466
|
{
|
|
454
467
|
"bot_token": str,
|
|
@@ -492,6 +505,7 @@ stats = get_pool_stats()
|
|
|
492
505
|
```
|
|
493
506
|
|
|
494
507
|
**Pool Stats Response:**
|
|
508
|
+
|
|
495
509
|
```python
|
|
496
510
|
{
|
|
497
511
|
"total_pools": int,
|
|
@@ -514,6 +528,7 @@ response = handle_lambda_error(error, context)
|
|
|
514
528
|
```
|
|
515
529
|
|
|
516
530
|
**Lambda Error Response:**
|
|
531
|
+
|
|
517
532
|
```python
|
|
518
533
|
{
|
|
519
534
|
"statusCode": 500,
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/logging/binding.py
RENAMED
|
@@ -306,7 +306,6 @@ def log_exception(binder: Any, exc: BaseException, message: str) -> None:
|
|
|
306
306
|
extra={
|
|
307
307
|
F.ERROR_TYPE: type(exc).__name__,
|
|
308
308
|
F.ERROR_MESSAGE: str(exc),
|
|
309
|
-
# 3-arg form for Python 3.9 compatibility (single-arg added in 3.10).
|
|
310
309
|
F.ERROR_STACK: "".join(traceback.format_exception(type(exc), exc, exc.__traceback__)),
|
|
311
310
|
},
|
|
312
311
|
)
|
|
@@ -13,8 +13,8 @@ from zoneinfo import ZoneInfo
|
|
|
13
13
|
# Supported timezones
|
|
14
14
|
NZ_TZ = ZoneInfo("Pacific/Auckland") # Primary timezone
|
|
15
15
|
AUS_TZ = ZoneInfo("Australia/Sydney")
|
|
16
|
-
US_EAST_TZ = ZoneInfo("
|
|
17
|
-
US_WEST_TZ = ZoneInfo("
|
|
16
|
+
US_EAST_TZ = ZoneInfo("America/New_York")
|
|
17
|
+
US_WEST_TZ = ZoneInfo("America/Los_Angeles")
|
|
18
18
|
EUR_TZ = ZoneInfo("Europe/London")
|
|
19
19
|
|
|
20
20
|
|
|
@@ -115,7 +115,7 @@ def get_timezone_options() -> List[Tuple[str, str]]:
|
|
|
115
115
|
return [
|
|
116
116
|
("nz", "New Zealand (Pacific/Auckland)"),
|
|
117
117
|
("aus", "Australia (Australia/Sydney)"),
|
|
118
|
-
("us_east", "US Eastern (
|
|
119
|
-
("us_west", "US Pacific (
|
|
118
|
+
("us_east", "US Eastern (America/New_York)"),
|
|
119
|
+
("us_west", "US Pacific (America/Los_Angeles)"),
|
|
120
120
|
("eur", "Europe (Europe/London)"),
|
|
121
121
|
]
|
|
@@ -11,14 +11,13 @@ authors = [
|
|
|
11
11
|
description = "Shared Python utilities for AWS Lambda, CLI tools, and agents with Slack, Elasticsearch, and monitoring integrations"
|
|
12
12
|
readme = "README.md"
|
|
13
13
|
license = {file = "LICENSE"}
|
|
14
|
-
requires-python = ">=3.
|
|
14
|
+
requires-python = ">=3.10"
|
|
15
15
|
classifiers = [
|
|
16
16
|
"Development Status :: 4 - Beta",
|
|
17
17
|
"Intended Audience :: Developers",
|
|
18
18
|
"License :: OSI Approved :: MIT License",
|
|
19
19
|
"Operating System :: OS Independent",
|
|
20
20
|
"Programming Language :: Python :: 3",
|
|
21
|
-
"Programming Language :: Python :: 3.9",
|
|
22
21
|
"Programming Language :: Python :: 3.10",
|
|
23
22
|
"Programming Language :: Python :: 3.11",
|
|
24
23
|
"Programming Language :: Python :: 3.12",
|
|
@@ -110,7 +109,7 @@ include = ["nui_shared_utils*"]
|
|
|
110
109
|
|
|
111
110
|
[tool.black]
|
|
112
111
|
line-length = 120
|
|
113
|
-
target-version = ['
|
|
112
|
+
target-version = ['py310']
|
|
114
113
|
include = '\.pyi?$'
|
|
115
114
|
extend-exclude = '''
|
|
116
115
|
/(
|
|
@@ -127,7 +126,7 @@ extend-exclude = '''
|
|
|
127
126
|
'''
|
|
128
127
|
|
|
129
128
|
[tool.mypy]
|
|
130
|
-
python_version = "3.
|
|
129
|
+
python_version = "3.10"
|
|
131
130
|
warn_return_any = false
|
|
132
131
|
warn_unused_configs = true
|
|
133
132
|
ignore_missing_imports = true
|
|
@@ -10,7 +10,7 @@ authors = [
|
|
|
10
10
|
]
|
|
11
11
|
description = "Redirect package: use nui-python-shared-utils instead"
|
|
12
12
|
readme = "README.md"
|
|
13
|
-
requires-python = ">=3.
|
|
13
|
+
requires-python = ">=3.10"
|
|
14
14
|
classifiers = [
|
|
15
15
|
"Development Status :: 7 - Inactive",
|
|
16
16
|
"Intended Audience :: Developers",
|
|
@@ -28,7 +28,6 @@ setup(
|
|
|
28
28
|
"License :: OSI Approved :: MIT License",
|
|
29
29
|
"Operating System :: OS Independent",
|
|
30
30
|
"Programming Language :: Python :: 3",
|
|
31
|
-
"Programming Language :: Python :: 3.9",
|
|
32
31
|
"Programming Language :: Python :: 3.10",
|
|
33
32
|
"Programming Language :: Python :: 3.11",
|
|
34
33
|
"Programming Language :: Python :: 3.12",
|
|
@@ -103,6 +102,6 @@ setup(
|
|
|
103
102
|
"anthropic[bedrock]>=0.45.0,<1.0.0",
|
|
104
103
|
],
|
|
105
104
|
},
|
|
106
|
-
python_requires=">=3.
|
|
105
|
+
python_requires=">=3.10",
|
|
107
106
|
keywords="aws lambda utilities slack elasticsearch monitoring serverless python shared",
|
|
108
107
|
)
|
|
@@ -51,12 +51,12 @@ class TestSlackClient:
|
|
|
51
51
|
def test_init_with_default_secret_name(self, mock_webclient, mock_get_secret):
|
|
52
52
|
"""Test initialization with default secret name."""
|
|
53
53
|
mock_get_secret.return_value = {"bot_token": "xoxb-default-token"}
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
with patch("nui_shared_utils.base_client.resolve_config_value") as mock_resolve:
|
|
56
56
|
mock_resolve.return_value = "slack-credentials"
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
client = SlackClient()
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
mock_get_secret.assert_called_once_with("slack-credentials")
|
|
61
61
|
mock_webclient.assert_called_once_with(token="xoxb-default-token")
|
|
62
62
|
|
|
@@ -77,7 +77,9 @@ class TestSendMessage:
|
|
|
77
77
|
result = slack.send_message("C123", "Test message", include_lambda_header=False)
|
|
78
78
|
|
|
79
79
|
assert result is True
|
|
80
|
-
mock_client.chat_postMessage.assert_called_once_with(
|
|
80
|
+
mock_client.chat_postMessage.assert_called_once_with(
|
|
81
|
+
channel="C123", text="Test message", blocks=None, attachments=None
|
|
82
|
+
)
|
|
81
83
|
|
|
82
84
|
@patch("nui_shared_utils.base_client.get_secret")
|
|
83
85
|
@patch("nui_shared_utils.slack_client.WebClient")
|
|
@@ -94,7 +96,9 @@ class TestSendMessage:
|
|
|
94
96
|
result = slack.send_message("C123", "Fallback text", blocks=blocks, include_lambda_header=False)
|
|
95
97
|
|
|
96
98
|
assert result is True
|
|
97
|
-
mock_client.chat_postMessage.assert_called_once_with(
|
|
99
|
+
mock_client.chat_postMessage.assert_called_once_with(
|
|
100
|
+
channel="C123", text="Fallback text", blocks=blocks, attachments=None
|
|
101
|
+
)
|
|
98
102
|
|
|
99
103
|
@patch("nui_shared_utils.base_client.get_secret")
|
|
100
104
|
@patch("nui_shared_utils.slack_client.WebClient")
|
|
@@ -954,7 +958,7 @@ class TestLambdaContextHeader:
|
|
|
954
958
|
"AWS_LAMBDA_LOG_STREAM_NAME": "2023/11/15/[$LATEST]abc123",
|
|
955
959
|
"AWS_REGION": "eu-west-1",
|
|
956
960
|
"STAGE": "prod",
|
|
957
|
-
"AWS_EXECUTION_ENV": "AWS_Lambda_python3.
|
|
961
|
+
"AWS_EXECUTION_ENV": "AWS_Lambda_python3.10",
|
|
958
962
|
},
|
|
959
963
|
)
|
|
960
964
|
def test_lambda_header_connect_production(self, mock_webclient, mock_get_secret, mock_boto3):
|
|
@@ -996,15 +1000,18 @@ class TestLambdaContextHeader:
|
|
|
996
1000
|
header_text = header_block["elements"][0]["text"]
|
|
997
1001
|
|
|
998
1002
|
# Use helper to check all expected parts
|
|
999
|
-
self.assert_header_format(
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1003
|
+
self.assert_header_format(
|
|
1004
|
+
header_text,
|
|
1005
|
+
{
|
|
1006
|
+
"robot emoji": "🤖",
|
|
1007
|
+
"function name": "test-function",
|
|
1008
|
+
"account name": "Production",
|
|
1009
|
+
"account ID": "(123456789012)",
|
|
1010
|
+
"region": "eu-west-1",
|
|
1011
|
+
"log emoji": "📋",
|
|
1012
|
+
"log group": "`/aws/lambda/test-function`",
|
|
1013
|
+
},
|
|
1014
|
+
)
|
|
1008
1015
|
|
|
1009
1016
|
@patch("nui_shared_utils.slack_client.create_aws_client")
|
|
1010
1017
|
@patch("nui_shared_utils.base_client.get_secret")
|
|
@@ -1055,14 +1062,17 @@ class TestLambdaContextHeader:
|
|
|
1055
1062
|
header_text = header_block["elements"][0]["text"]
|
|
1056
1063
|
|
|
1057
1064
|
# Use helper to check all expected parts
|
|
1058
|
-
self.assert_header_format(
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1065
|
+
self.assert_header_format(
|
|
1066
|
+
header_text,
|
|
1067
|
+
{
|
|
1068
|
+
"robot emoji": "🤖",
|
|
1069
|
+
"function name": "nui-service",
|
|
1070
|
+
"account name": "Development",
|
|
1071
|
+
"account ID": "(234567890123)",
|
|
1072
|
+
"region": "ap-southeast-2",
|
|
1073
|
+
"log emoji": "📋",
|
|
1074
|
+
},
|
|
1075
|
+
)
|
|
1066
1076
|
|
|
1067
1077
|
@patch("nui_shared_utils.slack_client.create_aws_client")
|
|
1068
1078
|
@patch("nui_shared_utils.base_client.get_secret")
|
|
@@ -1096,14 +1106,14 @@ class TestLambdaContextHeader:
|
|
|
1096
1106
|
|
|
1097
1107
|
# Create client with custom service name
|
|
1098
1108
|
slack = SlackClient(
|
|
1099
|
-
secret_name="test-secret",
|
|
1100
|
-
service_name="my-service",
|
|
1101
|
-
account_names={"123456789012": "Production"}
|
|
1109
|
+
secret_name="test-secret", service_name="my-service", account_names={"123456789012": "Production"}
|
|
1102
1110
|
)
|
|
1103
1111
|
|
|
1104
1112
|
# Simulate Lambda handler providing context with ARN
|
|
1105
1113
|
mock_context = Mock()
|
|
1106
|
-
mock_context.invoked_function_arn =
|
|
1114
|
+
mock_context.invoked_function_arn = (
|
|
1115
|
+
"arn:aws:lambda:eu-west-1:123456789012:function:connect-prod-long-service-name-12345"
|
|
1116
|
+
)
|
|
1107
1117
|
slack.set_handler_context(mock_context)
|
|
1108
1118
|
|
|
1109
1119
|
result = slack.send_message("C123", "Test message")
|
|
@@ -1122,14 +1132,17 @@ class TestLambdaContextHeader:
|
|
|
1122
1132
|
assert "connect-prod-long-service-name-12345" not in header_text
|
|
1123
1133
|
|
|
1124
1134
|
# Other header elements should still be present
|
|
1125
|
-
self.assert_header_format(
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1135
|
+
self.assert_header_format(
|
|
1136
|
+
header_text,
|
|
1137
|
+
{
|
|
1138
|
+
"robot emoji": "🤖",
|
|
1139
|
+
"function name": "my-service", # Custom name
|
|
1140
|
+
"account name": "Production",
|
|
1141
|
+
"account ID": "(123456789012)",
|
|
1142
|
+
"region": "eu-west-1",
|
|
1143
|
+
"log emoji": "📋",
|
|
1144
|
+
},
|
|
1145
|
+
)
|
|
1133
1146
|
|
|
1134
1147
|
@patch("nui_shared_utils.slack_client.create_aws_client")
|
|
1135
1148
|
@patch("nui_shared_utils.base_client.get_secret")
|
|
@@ -1153,19 +1166,14 @@ class TestLambdaContextHeader:
|
|
|
1153
1166
|
# Mock Lambda client
|
|
1154
1167
|
mock_lambda = Mock()
|
|
1155
1168
|
mock_boto3.return_value = mock_lambda
|
|
1156
|
-
mock_lambda.get_function.return_value = {
|
|
1157
|
-
"Configuration": {"FunctionName": "test-function"}
|
|
1158
|
-
}
|
|
1169
|
+
mock_lambda.get_function.return_value = {"Configuration": {"FunctionName": "test-function"}}
|
|
1159
1170
|
|
|
1160
1171
|
# Mock config file check
|
|
1161
1172
|
with patch("os.path.exists") as mock_exists:
|
|
1162
1173
|
mock_exists.return_value = False
|
|
1163
1174
|
|
|
1164
1175
|
# Create client WITHOUT custom service name
|
|
1165
|
-
slack = SlackClient(
|
|
1166
|
-
secret_name="test-secret",
|
|
1167
|
-
account_names={"123456789012": "Production"}
|
|
1168
|
-
)
|
|
1176
|
+
slack = SlackClient(secret_name="test-secret", account_names={"123456789012": "Production"})
|
|
1169
1177
|
|
|
1170
1178
|
# Simulate Lambda handler providing context with ARN
|
|
1171
1179
|
mock_context = Mock()
|
|
@@ -1187,14 +1195,17 @@ class TestLambdaContextHeader:
|
|
|
1187
1195
|
assert "test-function" in header_text
|
|
1188
1196
|
|
|
1189
1197
|
# All header elements should be present
|
|
1190
|
-
self.assert_header_format(
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
+
self.assert_header_format(
|
|
1199
|
+
header_text,
|
|
1200
|
+
{
|
|
1201
|
+
"robot emoji": "🤖",
|
|
1202
|
+
"function name": "test-function", # Original function name
|
|
1203
|
+
"account name": "Production",
|
|
1204
|
+
"account ID": "(123456789012)",
|
|
1205
|
+
"region": "eu-west-1",
|
|
1206
|
+
"log emoji": "📋",
|
|
1207
|
+
},
|
|
1208
|
+
)
|
|
1198
1209
|
|
|
1199
1210
|
@patch("nui_shared_utils.slack_client.create_aws_client")
|
|
1200
1211
|
@patch("nui_shared_utils.base_client.get_secret")
|
|
@@ -1632,7 +1643,11 @@ class TestSlackCredentialResolution:
|
|
|
1632
1643
|
|
|
1633
1644
|
@patch("nui_shared_utils.base_client.get_secret")
|
|
1634
1645
|
@patch("nui_shared_utils.slack_client.WebClient")
|
|
1635
|
-
@patch.dict(
|
|
1646
|
+
@patch.dict(
|
|
1647
|
+
"os.environ",
|
|
1648
|
+
{"SLACK_BOT_TOKEN": "xoxb-env-token", "SLACK_WEBHOOK_URL": "https://hooks.slack.com/xxx"},
|
|
1649
|
+
clear=False,
|
|
1650
|
+
)
|
|
1636
1651
|
def test_env_var_includes_optional_webhook(self, _mock_webclient, _mock_get_secret):
|
|
1637
1652
|
"""SLACK_WEBHOOK_URL is included when present."""
|
|
1638
1653
|
client = SlackClient()
|
|
@@ -8,9 +8,11 @@ import sys
|
|
|
8
8
|
import textwrap
|
|
9
9
|
from datetime import datetime, timedelta, timezone
|
|
10
10
|
from unittest.mock import patch
|
|
11
|
+
from zoneinfo import ZoneInfo
|
|
11
12
|
|
|
12
13
|
import pytest
|
|
13
14
|
|
|
15
|
+
import nui_shared_utils.timezone as timezone_utils
|
|
14
16
|
from nui_shared_utils.timezone import nz_time, format_nz_time, NZ_TZ
|
|
15
17
|
|
|
16
18
|
|
|
@@ -259,6 +261,41 @@ class TestAmbiguousWallTimes:
|
|
|
259
261
|
class TestTimezoneConstants:
|
|
260
262
|
"""Tests for timezone constants."""
|
|
261
263
|
|
|
264
|
+
def test_all_zone_keys_are_canonical(self):
|
|
265
|
+
# Canonical names for the supported regions, from IANA's zone1970.tab:
|
|
266
|
+
# https://data.iana.org/time-zones/tzdb/zone1970.tab
|
|
267
|
+
# Keep this list independent of the host database: available_timezones()
|
|
268
|
+
# also includes optional backward links on many systems.
|
|
269
|
+
canonical_keys = {
|
|
270
|
+
"Pacific/Auckland",
|
|
271
|
+
"Australia/Sydney",
|
|
272
|
+
"America/New_York",
|
|
273
|
+
"America/Los_Angeles",
|
|
274
|
+
"Europe/London",
|
|
275
|
+
}
|
|
276
|
+
zone_keys = {value.key for value in vars(timezone_utils).values() if isinstance(value, ZoneInfo)}
|
|
277
|
+
assert zone_keys, "No timezone constants were checked"
|
|
278
|
+
assert zone_keys <= canonical_keys, f"Non-canonical or unreviewed zone keys: {zone_keys - canonical_keys}"
|
|
279
|
+
|
|
280
|
+
def test_option_labels_use_canonical_zone_keys(self):
|
|
281
|
+
labels = dict(timezone_utils.get_timezone_options())
|
|
282
|
+
assert labels["us_east"] == "US Eastern (America/New_York)"
|
|
283
|
+
assert labels["us_west"] == "US Pacific (America/Los_Angeles)"
|
|
284
|
+
|
|
285
|
+
@pytest.mark.parametrize(
|
|
286
|
+
"zone, month, offset, abbreviation",
|
|
287
|
+
[
|
|
288
|
+
(timezone_utils.US_EAST_TZ, 1, -5, "EST"),
|
|
289
|
+
(timezone_utils.US_EAST_TZ, 7, -4, "EDT"),
|
|
290
|
+
(timezone_utils.US_WEST_TZ, 1, -8, "PST"),
|
|
291
|
+
(timezone_utils.US_WEST_TZ, 7, -7, "PDT"),
|
|
292
|
+
],
|
|
293
|
+
)
|
|
294
|
+
def test_us_offsets_and_dst(self, zone, month, offset, abbreviation):
|
|
295
|
+
local = datetime(2024, month, 15, 12, tzinfo=timezone.utc).astimezone(zone)
|
|
296
|
+
assert local.utcoffset() == timedelta(hours=offset)
|
|
297
|
+
assert local.tzname() == abbreviation
|
|
298
|
+
|
|
262
299
|
def test_nz_tz_constant(self):
|
|
263
300
|
"""Test that NZ_TZ is properly configured."""
|
|
264
301
|
assert NZ_TZ.key == "Pacific/Auckland"
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# Markdownlint configuration for nui-python-shared-utils
|
|
2
|
-
# Extends workspace config with project-specific overrides
|
|
3
|
-
|
|
4
|
-
config:
|
|
5
|
-
# Enable all rules by default
|
|
6
|
-
default: true
|
|
7
|
-
|
|
8
|
-
# MD013: Line length - disabled (technical docs need long lines for URLs/commands)
|
|
9
|
-
MD013: false
|
|
10
|
-
|
|
11
|
-
# MD033: Inline HTML - allowed (needed for complex formatting)
|
|
12
|
-
MD033: false
|
|
13
|
-
|
|
14
|
-
# MD036: No emphasis as heading - disabled (acceptable in planning docs)
|
|
15
|
-
MD036: false
|
|
16
|
-
|
|
17
|
-
# MD040: Fenced code language - disabled (not all code blocks need language tags)
|
|
18
|
-
MD040: false
|
|
19
|
-
|
|
20
|
-
# MD041: First line heading - disabled (docs may start with metadata)
|
|
21
|
-
MD041: false
|
|
22
|
-
|
|
23
|
-
# MD024: Multiple headings with same content - allowed with different levels
|
|
24
|
-
MD024:
|
|
25
|
-
siblings_only: true
|
|
26
|
-
|
|
27
|
-
# MD034: Bare URLs - allowed (sometimes intentional)
|
|
28
|
-
MD034: false
|
|
29
|
-
|
|
30
|
-
# MD046: Code block style - prefer fenced
|
|
31
|
-
MD046:
|
|
32
|
-
style: "fenced"
|
|
33
|
-
|
|
34
|
-
# MD060: Table column style - disabled (minor formatting preference)
|
|
35
|
-
MD060: false
|
|
36
|
-
|
|
37
|
-
# File patterns to check
|
|
38
|
-
globs:
|
|
39
|
-
- "**/*.md"
|
|
40
|
-
|
|
41
|
-
# Exclusions
|
|
42
|
-
ignores:
|
|
43
|
-
- "node_modules/**"
|
|
44
|
-
- "vendor/**"
|
|
45
|
-
- ".git/**"
|
|
46
|
-
- "venv/**"
|
|
47
|
-
- ".venv/**"
|
|
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.4 → nui_python_shared_utils-2.4.1}/docs/getting-started/configuration.md
RENAMED
|
File without changes
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/getting-started/quickstart.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/guides/jwt-authentication.md
RENAMED
|
File without changes
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/guides/lambda-utilities.md
RENAMED
|
File without changes
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/guides/llm-integration.md
RENAMED
|
File without changes
|
|
File without changes
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/guides/powertools-integration.md
RENAMED
|
File without changes
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/guides/slack-integration.md
RENAMED
|
File without changes
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/docs/guides/snowflake-integration.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/base_client.py
RENAMED
|
File without changes
|
|
File without changes
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/cloudwatch_metrics.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/error_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/es_query_builder.py
RENAMED
|
File without changes
|
|
File without changes
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/lambda_helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/log_processors.py
RENAMED
|
File without changes
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/logging/__init__.py
RENAMED
|
File without changes
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/logging/fields.py
RENAMED
|
File without changes
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/logging/redaction.py
RENAMED
|
File without changes
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/powertools_helpers.py
RENAMED
|
File without changes
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/secrets_helper.py
RENAMED
|
File without changes
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/slack_client.py
RENAMED
|
File without changes
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/nui_shared_utils/slack_formatter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/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
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_cloudwatch_metrics.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_es_client_compat.py
RENAMED
|
File without changes
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_es_query_builder.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
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_powertools_helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nui_python_shared_utils-2.4 → nui_python_shared_utils-2.4.1}/tests/test_snowflake_client.py
RENAMED
|
File without changes
|
|
File without changes
|