django-lambda-tasks 0.1.0__tar.gz → 0.1.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.
Files changed (76) hide show
  1. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/steering/product.md +2 -2
  2. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/steering/structure.md +2 -2
  3. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/PKG-INFO +7 -7
  4. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/README.md +6 -6
  5. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/lambda_tasks/handler.py +6 -4
  6. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/lambda_tasks/secret_loader.py +7 -7
  7. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/pyproject.toml +1 -1
  8. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/tests/test_handler.py +2 -3
  9. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/tests/test_secret_loader.py +29 -29
  10. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.github/workflows/ci.yml +0 -0
  11. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.github/workflows/release.yml +0 -0
  12. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.gitignore +0 -0
  13. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/deferred-task-enqueue/.config.kiro +0 -0
  14. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/deferred-task-enqueue/design.md +0 -0
  15. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/deferred-task-enqueue/requirements.md +0 -0
  16. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/deferred-task-enqueue/tasks.md +0 -0
  17. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/eager-mode-example-app/.config.kiro +0 -0
  18. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/eager-mode-example-app/design.md +0 -0
  19. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/eager-mode-example-app/requirements.md +0 -0
  20. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/eager-mode-example-app/tasks.md +0 -0
  21. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/ignore-errors-decorator-option/.config.kiro +0 -0
  22. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/ignore-errors-decorator-option/design.md +0 -0
  23. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/ignore-errors-decorator-option/requirements.md +0 -0
  24. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/ignore-errors-decorator-option/tasks.md +0 -0
  25. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/import-string-task-resolution/.config.kiro +0 -0
  26. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/import-string-task-resolution/design.md +0 -0
  27. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/import-string-task-resolution/requirements.md +0 -0
  28. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/import-string-task-resolution/tasks.md +0 -0
  29. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/rse-background-tasks/.config.kiro +0 -0
  30. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/rse-background-tasks/design.md +0 -0
  31. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/rse-background-tasks/requirements.md +0 -0
  32. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/rse-background-tasks/tasks.md +0 -0
  33. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/rse-background-tasks-bugfix/.config.kiro +0 -0
  34. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/rse-background-tasks-bugfix/bugfix.md +0 -0
  35. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/rse-background-tasks-bugfix/design.md +0 -0
  36. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/rse-background-tasks-bugfix/tasks.md +0 -0
  37. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/task-retry/.config.kiro +0 -0
  38. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/task-retry/design.md +0 -0
  39. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/task-retry/requirements.md +0 -0
  40. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/specs/task-retry/tasks.md +0 -0
  41. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.kiro/steering/tech.md +0 -0
  42. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.pre-commit-config.yaml +0 -0
  43. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/.vscode/settings.json +0 -0
  44. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/example/README.md +0 -0
  45. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/example/example_app/__init__.py +0 -0
  46. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/example/example_app/apps.py +0 -0
  47. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/example/example_app/tasks.py +0 -0
  48. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/example/example_app/urls.py +0 -0
  49. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/example/example_app/views.py +0 -0
  50. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/example/example_project/__init__.py +0 -0
  51. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/example/example_project/settings.py +0 -0
  52. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/example/example_project/urls.py +0 -0
  53. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/example/example_project/wsgi.py +0 -0
  54. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/example/manage.py +0 -0
  55. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/lambda_tasks/__init__.py +0 -0
  56. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/lambda_tasks/admin.py +0 -0
  57. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/lambda_tasks/apps.py +0 -0
  58. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/lambda_tasks/decorators.py +0 -0
  59. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/lambda_tasks/logging.py +0 -0
  60. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/lambda_tasks/migrations/0001_initial.py +0 -0
  61. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/lambda_tasks/migrations/__init__.py +0 -0
  62. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/lambda_tasks/models.py +0 -0
  63. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/lambda_tasks/settings.py +0 -0
  64. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/lambda_tasks/timeouts.py +0 -0
  65. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/tests/conftest.py +0 -0
  66. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/tests/settings.py +0 -0
  67. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/tests/test_admin.py +0 -0
  68. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/tests/test_decorator.py +0 -0
  69. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/tests/test_decorators.py +0 -0
  70. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/tests/test_deferred_enqueue.py +0 -0
  71. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/tests/test_kwargs_only.py +0 -0
  72. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/tests/test_logging.py +0 -0
  73. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/tests/test_models.py +0 -0
  74. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/tests/test_serializer.py +0 -0
  75. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/tests/test_settings.py +0 -0
  76. {django_lambda_tasks-0.1.0 → django_lambda_tasks-0.1.1}/tests/test_timeouts.py +0 -0
@@ -121,13 +121,13 @@ class SQSLambdaTaskMessage(BaseModel):
121
121
 
122
122
  `resolve_secrets_into_env()` in `secret_loader.py` runs once at Lambda cold start, before `django.setup()`.
123
123
 
124
- Any env var prefixed `AWS_SECRETS_MANAGER_` is treated as a Secrets Manager reference. The unprefixed name is the target env var.
124
+ Any env var prefixed `LAMBDA_TASKS_SECRET_` is treated as a Secrets Manager reference. The unprefixed name is the target env var.
125
125
 
126
126
  Required format: `<arn>:<json-key>:<version-stage>:<version-id>` (10 colon-separated segments, all fields non-empty).
127
127
 
128
128
  Behaviour:
129
129
  - All references are validated before any AWS call — malformed references raise `ValueError` immediately
130
- - Setting both `AWS_SECRETS_MANAGER_FOO` and `FOO` is a configuration error and raises `ValueError`
130
+ - Setting both `LAMBDA_TASKS_SECRET_FOO` and `FOO` is a configuration error and raises `ValueError`
131
131
  - Calls are batched by `(ARN, version-stage, version-id)` — one `GetSecretValue` per unique combination
132
132
  - Fetched secrets are cached in-process; warm invocations pay no extra cost
133
133
 
@@ -17,7 +17,7 @@ django-lambda-tasks/
17
17
  │ ├── logging.py # task_logger — invocation-scoped LoggerAdapter
18
18
  │ ├── models.py # TaskRecord, SQSLambdaTaskMessage, SQSLambdaTask
19
19
  │ ├── settings.py # LambdaTasksSettings (lazy Django settings reader)
20
- │ ├── secret_loader.py # Resolves AWS_SECRETS_MANAGER_* env vars at cold start
20
+ │ ├── secret_loader.py # Resolves LAMBDA_TASKS_SECRET_* env vars at cold start
21
21
  │ ├── timeouts.py # TimeoutContext implementation
22
22
  │ └── migrations/ # Django migrations for TaskRecord
23
23
  ├── tests/ # pytest test suite
@@ -39,7 +39,7 @@ django-lambda-tasks/
39
39
  - `decorators.py` — defines `@lambda_task`; enforces kwargs-only at decoration time
40
40
  - `models.py` — `TaskRecord` (Django ORM), `SQSLambdaTaskMessage` (Pydantic, SQS schema + execution logic), `SQSLambdaTask` (Pydantic, holds message + routing; `_execute()` publishes to SQS or executes eagerly; `execute_on_commit()` registers `_execute` with `transaction.on_commit`)
41
41
  - `handler.py` — Lambda entry point; calls `resolve_secrets_into_env()` then `django.setup()` at cold start; processes SQS records independently; returns `batchItemFailures`
42
- - `secret_loader.py` — resolves `AWS_SECRETS_MANAGER_*` env vars from Secrets Manager before Django starts; validates format, detects conflicts, batches API calls, caches results in-process
42
+ - `secret_loader.py` — resolves `LAMBDA_TASKS_SECRET_*` env vars from Secrets Manager before Django starts; validates format, detects conflicts, batches API calls, caches results in-process
43
43
  - `logging.py` — `task_logger` singleton; `invocation_id` set/cleared around each task execution
44
44
  - `settings.py` — `LambdaTasksSettings` instantiated fresh per use (reads live Django settings)
45
45
  - `admin.py` — Django admin registration for `TaskRecord`
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-lambda-tasks
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Run async tasks in a lambda function
5
5
  Requires-Python: >=3.10
6
6
  Requires-Dist: boto3
@@ -403,15 +403,15 @@ Ensure the Lambda execution environment has `DJANGO_SETTINGS_MODULE` set and tha
403
403
 
404
404
  The Lambda handler supports loading secret values from AWS Secrets Manager into the environment before Django starts. This lets your Django settings file read from `os.environ` as normal while keeping secrets out of plaintext environment variables.
405
405
 
406
- Set any env var with the prefix `AWS_SECRETS_MANAGER_` to a full Secrets Manager dynamic reference. The unprefixed name becomes the target env var:
406
+ Set any env var with the prefix `LAMBDA_TASKS_SECRET_` to a full Secrets Manager dynamic reference. The unprefixed name becomes the target env var:
407
407
 
408
408
  ```
409
- AWS_SECRETS_MANAGER_DATABASE_URL=arn:aws:secretsmanager:eu-west-1:123456789012:secret:myapp/prod:DATABASE_URL:AWSCURRENT:v1
409
+ LAMBDA_TASKS_SECRET_DATABASE_URL=arn:aws:secretsmanager:eu-west-1:123456789012:secret:myapp/prod:DATABASE_URL:AWSCURRENT:v1
410
410
  ```
411
411
 
412
412
  At cold start, before `django.setup()` is called, the handler calls `resolve_secrets_into_env()` which:
413
413
 
414
- 1. Scans all env vars for the `AWS_SECRETS_MANAGER_` prefix
414
+ 1. Scans all env vars for the `LAMBDA_TASKS_SECRET_` prefix
415
415
  2. Validates every reference — malformed references raise immediately so the container fails to start rather than misconfiguring Django silently
416
416
  3. Groups references by `(ARN, version-stage, version-id)` and makes one `GetSecretValue` call per unique combination
417
417
  4. Extracts the named JSON key from the secret and writes it into `os.environ`
@@ -435,8 +435,8 @@ arn:aws:secretsmanager:eu-west-1:123456789012:secret:myapp/prod:DATABASE_URL:AWS
435
435
  Multiple env vars can reference different keys from the same secret — only one `GetSecretValue` call is made for that `(ARN, version-stage, version-id)` combination:
436
436
 
437
437
  ```
438
- AWS_SECRETS_MANAGER_DATABASE_URL=arn:...:myapp/prod:DATABASE_URL:AWSCURRENT:v1
439
- AWS_SECRETS_MANAGER_SECRET_KEY=arn:...:myapp/prod:SECRET_KEY:AWSCURRENT:v1
438
+ LAMBDA_TASKS_SECRET_DATABASE_URL=arn:...:myapp/prod:DATABASE_URL:AWSCURRENT:v1
439
+ LAMBDA_TASKS_SECRET_SECRET_KEY=arn:...:myapp/prod:SECRET_KEY:AWSCURRENT:v1
440
440
  ```
441
441
 
442
442
  #### Validation errors
@@ -445,7 +445,7 @@ The following all raise `ValueError` at cold start, preventing the Lambda contai
445
445
 
446
446
  - Wrong number of colon-separated segments (must be exactly 10)
447
447
  - Empty `json-key`, `version-stage`, or `version-id`
448
- - Both `AWS_SECRETS_MANAGER_FOO` and `FOO` are set — use one or the other
448
+ - Both `LAMBDA_TASKS_SECRET_FOO` and `FOO` are set — use one or the other
449
449
  - The named JSON key does not exist in the fetched secret
450
450
  - The secret value is not valid JSON
451
451
 
@@ -393,15 +393,15 @@ Ensure the Lambda execution environment has `DJANGO_SETTINGS_MODULE` set and tha
393
393
 
394
394
  The Lambda handler supports loading secret values from AWS Secrets Manager into the environment before Django starts. This lets your Django settings file read from `os.environ` as normal while keeping secrets out of plaintext environment variables.
395
395
 
396
- Set any env var with the prefix `AWS_SECRETS_MANAGER_` to a full Secrets Manager dynamic reference. The unprefixed name becomes the target env var:
396
+ Set any env var with the prefix `LAMBDA_TASKS_SECRET_` to a full Secrets Manager dynamic reference. The unprefixed name becomes the target env var:
397
397
 
398
398
  ```
399
- AWS_SECRETS_MANAGER_DATABASE_URL=arn:aws:secretsmanager:eu-west-1:123456789012:secret:myapp/prod:DATABASE_URL:AWSCURRENT:v1
399
+ LAMBDA_TASKS_SECRET_DATABASE_URL=arn:aws:secretsmanager:eu-west-1:123456789012:secret:myapp/prod:DATABASE_URL:AWSCURRENT:v1
400
400
  ```
401
401
 
402
402
  At cold start, before `django.setup()` is called, the handler calls `resolve_secrets_into_env()` which:
403
403
 
404
- 1. Scans all env vars for the `AWS_SECRETS_MANAGER_` prefix
404
+ 1. Scans all env vars for the `LAMBDA_TASKS_SECRET_` prefix
405
405
  2. Validates every reference — malformed references raise immediately so the container fails to start rather than misconfiguring Django silently
406
406
  3. Groups references by `(ARN, version-stage, version-id)` and makes one `GetSecretValue` call per unique combination
407
407
  4. Extracts the named JSON key from the secret and writes it into `os.environ`
@@ -425,8 +425,8 @@ arn:aws:secretsmanager:eu-west-1:123456789012:secret:myapp/prod:DATABASE_URL:AWS
425
425
  Multiple env vars can reference different keys from the same secret — only one `GetSecretValue` call is made for that `(ARN, version-stage, version-id)` combination:
426
426
 
427
427
  ```
428
- AWS_SECRETS_MANAGER_DATABASE_URL=arn:...:myapp/prod:DATABASE_URL:AWSCURRENT:v1
429
- AWS_SECRETS_MANAGER_SECRET_KEY=arn:...:myapp/prod:SECRET_KEY:AWSCURRENT:v1
428
+ LAMBDA_TASKS_SECRET_DATABASE_URL=arn:...:myapp/prod:DATABASE_URL:AWSCURRENT:v1
429
+ LAMBDA_TASKS_SECRET_SECRET_KEY=arn:...:myapp/prod:SECRET_KEY:AWSCURRENT:v1
430
430
  ```
431
431
 
432
432
  #### Validation errors
@@ -435,7 +435,7 @@ The following all raise `ValueError` at cold start, preventing the Lambda contai
435
435
 
436
436
  - Wrong number of colon-separated segments (must be exactly 10)
437
437
  - Empty `json-key`, `version-stage`, or `version-id`
438
- - Both `AWS_SECRETS_MANAGER_FOO` and `FOO` are set — use one or the other
438
+ - Both `LAMBDA_TASKS_SECRET_FOO` and `FOO` are set — use one or the other
439
439
  - The named JSON key does not exist in the fetched secret
440
440
  - The secret value is not valid JSON
441
441
 
@@ -10,16 +10,15 @@ import logging
10
10
  import os
11
11
 
12
12
  import django
13
- from django.apps import apps as django_apps
13
+ from django.apps import apps
14
14
 
15
- from lambda_tasks.models import SQSLambdaTaskMessage
16
15
  from lambda_tasks.secret_loader import resolve_secrets_into_env
17
16
 
18
17
  # Cold-start Django setup — runs once per Lambda container.
19
18
  # Secrets are resolved first so Django settings can reference the populated
20
- # env vars. resolve_secrets_into_env() is idempotent and caches fetched
19
+ # env vars. resolve_secrets_into_env() is idempotent and caches fetched
21
20
  # secrets in-process, so subsequent invocations pay no extra cost.
22
- if os.environ.get("DJANGO_SETTINGS_MODULE") and not django_apps.ready:
21
+ if os.environ.get("DJANGO_SETTINGS_MODULE") and not apps.ready:
23
22
  resolve_secrets_into_env()
24
23
  django.setup()
25
24
 
@@ -32,6 +31,9 @@ def handler(*, event: dict, context: object) -> dict:
32
31
 
33
32
  Returns a partial-batch failure report so AWS only re-drives failed records.
34
33
  """
34
+ # Local import due to AppRegistryNotReady
35
+ from lambda_tasks.models import SQSLambdaTaskMessage
36
+
35
37
  batch_item_failures: list[dict] = []
36
38
 
37
39
  for record in event["Records"]:
@@ -1,14 +1,14 @@
1
1
  """
2
2
  Resolves environment variables that reference AWS Secrets Manager ARNs.
3
3
 
4
- Any env var prefixed with ``AWS_SECRETS_MANAGER_`` is treated as a pointer
4
+ Any env var prefixed with ``LAMBDA_TASKS_SECRET_`` is treated as a pointer
5
5
  to a secret value. The unprefixed name is the target env var to populate.
6
6
 
7
7
  Required value format
8
8
  ---------------------
9
9
  Every reference must follow the full dynamic reference syntax::
10
10
 
11
- AWS_SECRETS_MANAGER_DJANGO_ADMIN_URL=arn:aws:secretsmanager:eu-west-1:123:secret:my-secret:DJANGO_ADMIN_URL:AWSCURRENT:v1
11
+ LAMBDA_TASKS_SECRET_DJANGO_ADMIN_URL=arn:aws:secretsmanager:eu-west-1:123:secret:my-secret:DJANGO_ADMIN_URL:AWSCURRENT:v1
12
12
 
13
13
  That is: ``<arn>:<json-key>:<version-stage>:<version-id>``
14
14
 
@@ -16,7 +16,7 @@ All four suffix segments must be present and non-empty.
16
16
  A malformed reference raises ``ValueError`` immediately so the Lambda
17
17
  container fails at cold start rather than silently misconfiguring Django.
18
18
 
19
- It is a configuration error to set both ``AWS_SECRETS_MANAGER_FOO`` and
19
+ It is a configuration error to set both ``LAMBDA_TASKS_SECRET_FOO`` and
20
20
  ``FOO`` — use one or the other. Having both raises ``ValueError`` at cold
21
21
  start so the misconfiguration is caught immediately.
22
22
 
@@ -35,7 +35,7 @@ import boto3
35
35
 
36
36
  logger = logging.getLogger(__name__)
37
37
 
38
- _PREFIX = "AWS_SECRETS_MANAGER_"
38
+ _PREFIX = "LAMBDA_TASKS_SECRET_"
39
39
 
40
40
  # Module-level cache: (arn, version_stage, version_id) → raw secret string.
41
41
  # Populated on first call; reused for the lifetime of the Lambda container.
@@ -128,14 +128,14 @@ def _fetch_secret(*, client: object, ref: _SecretReference) -> dict[str, str]:
128
128
 
129
129
 
130
130
  def resolve_secrets_into_env() -> None:
131
- """Scan env vars for ``AWS_SECRETS_MANAGER_*`` references and resolve them.
131
+ """Scan env vars for ``LAMBDA_TASKS_SECRET_*`` references and resolve them.
132
132
 
133
133
  For each matching env var the resolved value is written back into
134
134
  ``os.environ`` under the unprefixed name.
135
135
 
136
136
  Raises ``ValueError`` at cold start if:
137
137
  - A reference is malformed (wrong segment count, any empty field)
138
- - The target env var is already set — use ``AWS_SECRETS_MANAGER_FOO`` or
138
+ - The target env var is already set — use ``LAMBDA_TASKS_SECRET_FOO`` or
139
139
  ``FOO``, not both
140
140
 
141
141
  This function is idempotent — calling it multiple times is safe and cheap
@@ -157,7 +157,7 @@ def resolve_secrets_into_env() -> None:
157
157
  if conflicts:
158
158
  raise ValueError(
159
159
  "The following environment variables are set both directly and via "
160
- f"AWS_SECRETS_MANAGER_*: {', '.join(sorted(conflicts))}. "
160
+ f"LAMBDA_TASKS_SECRET_*: {', '.join(sorted(conflicts))}. "
161
161
  "Use one or the other, not both."
162
162
  )
163
163
 
@@ -7,7 +7,7 @@ packages = ["lambda_tasks"]
7
7
 
8
8
  [project]
9
9
  name = "django-lambda-tasks"
10
- version = "0.1.0"
10
+ version = "0.1.1"
11
11
  description = "Run async tasks in a lambda function"
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.10"
@@ -46,7 +46,7 @@ def _valid_body(task_name: str = "my_module.my_task", **kwargs) -> str:
46
46
  def _patch_model_validate(side_effect):
47
47
  """Patch SQSLambdaTaskMessage.model_validate_json in the handler module."""
48
48
  return patch(
49
- "lambda_tasks.handler.SQSLambdaTaskMessage.model_validate_json",
49
+ "lambda_tasks.models.SQSLambdaTaskMessage.model_validate_json",
50
50
  side_effect=side_effect,
51
51
  )
52
52
 
@@ -252,7 +252,6 @@ def test_property_11_batch_records_processed_independently(flags):
252
252
  def test_property_4_django_setup_before_execute_task(monkeypatch):
253
253
  import importlib
254
254
 
255
- import django
256
255
  import django.apps
257
256
 
258
257
  import lambda_tasks.handler as handler_module
@@ -272,7 +271,7 @@ def test_property_4_django_setup_before_execute_task(monkeypatch):
272
271
  return MagicMock()
273
272
 
274
273
  monkeypatch.setattr(
275
- "lambda_tasks.handler.SQSLambdaTaskMessage.model_validate_json",
274
+ "lambda_tasks.models.SQSLambdaTaskMessage.model_validate_json",
276
275
  spy_model_validate,
277
276
  )
278
277
 
@@ -43,7 +43,7 @@ def clear_cache():
43
43
 
44
44
  class TestParseReferenceValid:
45
45
  def test_returns_named_tuple_with_arn_and_key(self):
46
- ref = _parse_reference(env_var="AWS_SECRETS_MANAGER_X", value=VALID_REF)
46
+ ref = _parse_reference(env_var="LAMBDA_TASKS_SECRET_X", value=VALID_REF)
47
47
  assert ref.arn == ARN
48
48
  assert ref.json_key == "MY_KEY"
49
49
  assert ref.version_stage == "AWSCURRENT"
@@ -51,7 +51,7 @@ class TestParseReferenceValid:
51
51
 
52
52
  def test_version_id_populated(self):
53
53
  value = f"{ARN}:DJANGO_ADMIN_URL:AWSCURRENT:abc123"
54
- ref = _parse_reference(env_var="AWS_SECRETS_MANAGER_X", value=value)
54
+ ref = _parse_reference(env_var="LAMBDA_TASKS_SECRET_X", value=value)
55
55
  assert ref.arn == ARN
56
56
  assert ref.json_key == "DJANGO_ADMIN_URL"
57
57
  assert ref.version_stage == "AWSCURRENT"
@@ -66,43 +66,43 @@ class TestParseReferenceValid:
66
66
  class TestParseReferenceInvalid:
67
67
  def test_plain_arn_rejected(self):
68
68
  with pytest.raises(ValueError, match="10 colon-separated segments"):
69
- _parse_reference(env_var="AWS_SECRETS_MANAGER_X", value=ARN)
69
+ _parse_reference(env_var="LAMBDA_TASKS_SECRET_X", value=ARN)
70
70
 
71
71
  def test_empty_json_key_rejected(self):
72
72
  # 10 segments but key is empty
73
73
  value = f"{ARN}::AWSCURRENT:abc123"
74
74
  with pytest.raises(ValueError, match="missing the json-key"):
75
- _parse_reference(env_var="AWS_SECRETS_MANAGER_X", value=value)
75
+ _parse_reference(env_var="LAMBDA_TASKS_SECRET_X", value=value)
76
76
 
77
77
  def test_empty_version_stage_rejected(self):
78
78
  value = f"{ARN}:MY_KEY::abc123"
79
79
  with pytest.raises(ValueError, match="missing the version-stage"):
80
- _parse_reference(env_var="AWS_SECRETS_MANAGER_X", value=value)
80
+ _parse_reference(env_var="LAMBDA_TASKS_SECRET_X", value=value)
81
81
 
82
82
  def test_empty_version_id_rejected(self):
83
83
  value = f"{ARN}:MY_KEY:AWSCURRENT:"
84
84
  with pytest.raises(ValueError, match="missing the version-id"):
85
- _parse_reference(env_var="AWS_SECRETS_MANAGER_X", value=value)
85
+ _parse_reference(env_var="LAMBDA_TASKS_SECRET_X", value=value)
86
86
 
87
87
  def test_too_few_segments_rejected(self):
88
88
  with pytest.raises(ValueError, match="10 colon-separated segments"):
89
- _parse_reference(env_var="AWS_SECRETS_MANAGER_X", value=f"{ARN}:KEY:")
89
+ _parse_reference(env_var="LAMBDA_TASKS_SECRET_X", value=f"{ARN}:KEY:")
90
90
 
91
91
  def test_too_many_segments_rejected(self):
92
92
  with pytest.raises(ValueError, match="10 colon-separated segments"):
93
93
  _parse_reference(
94
- env_var="AWS_SECRETS_MANAGER_X",
94
+ env_var="LAMBDA_TASKS_SECRET_X",
95
95
  value=f"{ARN}:KEY:STAGE:VER:EXTRA",
96
96
  )
97
97
 
98
98
  def test_error_message_includes_env_var_name(self):
99
- with pytest.raises(ValueError, match="AWS_SECRETS_MANAGER_MY_VAR"):
100
- _parse_reference(env_var="AWS_SECRETS_MANAGER_MY_VAR", value=ARN)
99
+ with pytest.raises(ValueError, match="LAMBDA_TASKS_SECRET_MY_VAR"):
100
+ _parse_reference(env_var="LAMBDA_TASKS_SECRET_MY_VAR", value=ARN)
101
101
 
102
102
  @given(st.text(min_size=1).filter(lambda s: s.count(":") != 9))
103
103
  def test_any_non_10_segment_value_rejected(self, value: str):
104
104
  with pytest.raises(ValueError):
105
- _parse_reference(env_var="AWS_SECRETS_MANAGER_X", value=value)
105
+ _parse_reference(env_var="LAMBDA_TASKS_SECRET_X", value=value)
106
106
 
107
107
 
108
108
  # ---------------------------------------------------------------------------
@@ -112,13 +112,13 @@ class TestParseReferenceInvalid:
112
112
 
113
113
  class TestNoReferences:
114
114
  def test_no_prefixed_vars_makes_no_boto3_call(self, monkeypatch):
115
- monkeypatch.delenv("AWS_SECRETS_MANAGER_ANYTHING", raising=False)
115
+ monkeypatch.delenv("LAMBDA_TASKS_SECRET_ANYTHING", raising=False)
116
116
  with patch("lambda_tasks.secret_loader.boto3") as mock_boto3:
117
117
  resolve_secrets_into_env()
118
118
  mock_boto3.client.assert_not_called()
119
119
 
120
120
  def test_no_prefixed_vars_leaves_env_unchanged(self, monkeypatch):
121
- monkeypatch.delenv("AWS_SECRETS_MANAGER_ANYTHING", raising=False)
121
+ monkeypatch.delenv("LAMBDA_TASKS_SECRET_ANYTHING", raising=False)
122
122
  before = dict(os.environ)
123
123
  resolve_secrets_into_env()
124
124
  assert dict(os.environ) == before
@@ -131,14 +131,14 @@ class TestNoReferences:
131
131
 
132
132
  class TestMalformedReference:
133
133
  def test_plain_arn_raises_before_any_boto3_call(self, monkeypatch):
134
- monkeypatch.setenv("AWS_SECRETS_MANAGER_MY_VAR", ARN)
134
+ monkeypatch.setenv("LAMBDA_TASKS_SECRET_MY_VAR", ARN)
135
135
  with patch("lambda_tasks.secret_loader.boto3") as mock_boto3:
136
- with pytest.raises(ValueError, match="AWS_SECRETS_MANAGER_MY_VAR"):
136
+ with pytest.raises(ValueError, match="LAMBDA_TASKS_SECRET_MY_VAR"):
137
137
  resolve_secrets_into_env()
138
138
  mock_boto3.client.assert_not_called()
139
139
 
140
140
  def test_empty_key_raises_before_any_boto3_call(self, monkeypatch):
141
- monkeypatch.setenv("AWS_SECRETS_MANAGER_MY_VAR", f"{ARN}::AWSCURRENT:abc123")
141
+ monkeypatch.setenv("LAMBDA_TASKS_SECRET_MY_VAR", f"{ARN}::AWSCURRENT:abc123")
142
142
  with patch("lambda_tasks.secret_loader.boto3") as mock_boto3:
143
143
  with pytest.raises(ValueError, match="missing the json-key"):
144
144
  resolve_secrets_into_env()
@@ -152,7 +152,7 @@ class TestMalformedReference:
152
152
 
153
153
  class TestConflict:
154
154
  def test_raises_when_target_already_set(self, monkeypatch):
155
- monkeypatch.setenv("AWS_SECRETS_MANAGER_MY_VAR", VALID_REF)
155
+ monkeypatch.setenv("LAMBDA_TASKS_SECRET_MY_VAR", VALID_REF)
156
156
  monkeypatch.setenv("MY_VAR", "already-set")
157
157
 
158
158
  with patch("lambda_tasks.secret_loader.boto3"):
@@ -160,8 +160,8 @@ class TestConflict:
160
160
  resolve_secrets_into_env()
161
161
 
162
162
  def test_error_message_lists_all_conflicts(self, monkeypatch):
163
- monkeypatch.setenv("AWS_SECRETS_MANAGER_VAR_A", VALID_REF)
164
- monkeypatch.setenv("AWS_SECRETS_MANAGER_VAR_B", VALID_REF2)
163
+ monkeypatch.setenv("LAMBDA_TASKS_SECRET_VAR_A", VALID_REF)
164
+ monkeypatch.setenv("LAMBDA_TASKS_SECRET_VAR_B", VALID_REF2)
165
165
  monkeypatch.setenv("VAR_A", "x")
166
166
  monkeypatch.setenv("VAR_B", "y")
167
167
 
@@ -174,7 +174,7 @@ class TestConflict:
174
174
  assert "VAR_B" in msg
175
175
 
176
176
  def test_conflict_raises_before_any_boto3_call(self, monkeypatch):
177
- monkeypatch.setenv("AWS_SECRETS_MANAGER_MY_VAR", VALID_REF)
177
+ monkeypatch.setenv("LAMBDA_TASKS_SECRET_MY_VAR", VALID_REF)
178
178
  monkeypatch.setenv("MY_VAR", "already-set")
179
179
 
180
180
  with patch("lambda_tasks.secret_loader.boto3") as mock_boto3:
@@ -190,7 +190,7 @@ class TestConflict:
190
190
 
191
191
  class TestResolution:
192
192
  def test_sets_target_env_var(self, monkeypatch):
193
- monkeypatch.setenv("AWS_SECRETS_MANAGER_MY_VAR", VALID_REF)
193
+ monkeypatch.setenv("LAMBDA_TASKS_SECRET_MY_VAR", VALID_REF)
194
194
  monkeypatch.delenv("MY_VAR", raising=False)
195
195
 
196
196
  payload = json.dumps({"MY_KEY": "supersecret"})
@@ -204,7 +204,7 @@ class TestResolution:
204
204
  assert os.environ["MY_VAR"] == "supersecret"
205
205
 
206
206
  def test_missing_key_in_secret_raises(self, monkeypatch):
207
- monkeypatch.setenv("AWS_SECRETS_MANAGER_SOME_VAR", VALID_REF)
207
+ monkeypatch.setenv("LAMBDA_TASKS_SECRET_SOME_VAR", VALID_REF)
208
208
  monkeypatch.delenv("SOME_VAR", raising=False)
209
209
 
210
210
  client = MagicMock()
@@ -218,7 +218,7 @@ class TestResolution:
218
218
  resolve_secrets_into_env()
219
219
 
220
220
  def test_invalid_json_in_secret_raises(self, monkeypatch):
221
- monkeypatch.setenv("AWS_SECRETS_MANAGER_SOME_VAR", VALID_REF)
221
+ monkeypatch.setenv("LAMBDA_TASKS_SECRET_SOME_VAR", VALID_REF)
222
222
  monkeypatch.delenv("SOME_VAR", raising=False)
223
223
 
224
224
  client = MagicMock()
@@ -239,8 +239,8 @@ class TestBatching:
239
239
  def test_single_api_call_for_same_arn(self, monkeypatch):
240
240
  ref_a = f"{ARN}:KEY_A:AWSCURRENT:AWSCURRENT"
241
241
  ref_b = f"{ARN}:KEY_B:AWSCURRENT:AWSCURRENT"
242
- monkeypatch.setenv("AWS_SECRETS_MANAGER_VAR_A", ref_a)
243
- monkeypatch.setenv("AWS_SECRETS_MANAGER_VAR_B", ref_b)
242
+ monkeypatch.setenv("LAMBDA_TASKS_SECRET_VAR_A", ref_a)
243
+ monkeypatch.setenv("LAMBDA_TASKS_SECRET_VAR_B", ref_b)
244
244
  monkeypatch.delenv("VAR_A", raising=False)
245
245
  monkeypatch.delenv("VAR_B", raising=False)
246
246
 
@@ -261,8 +261,8 @@ class TestBatching:
261
261
  assert os.environ["VAR_B"] == "val-b"
262
262
 
263
263
  def test_separate_api_calls_for_different_arns(self, monkeypatch):
264
- monkeypatch.setenv("AWS_SECRETS_MANAGER_VAR_A", VALID_REF)
265
- monkeypatch.setenv("AWS_SECRETS_MANAGER_VAR_B", VALID_REF2)
264
+ monkeypatch.setenv("LAMBDA_TASKS_SECRET_VAR_A", VALID_REF)
265
+ monkeypatch.setenv("LAMBDA_TASKS_SECRET_VAR_B", VALID_REF2)
266
266
  monkeypatch.delenv("VAR_A", raising=False)
267
267
  monkeypatch.delenv("VAR_B", raising=False)
268
268
 
@@ -288,7 +288,7 @@ class TestBatching:
288
288
 
289
289
  class TestCaching:
290
290
  def test_second_call_makes_no_api_call(self, monkeypatch):
291
- monkeypatch.setenv("AWS_SECRETS_MANAGER_MY_VAR", VALID_REF)
291
+ monkeypatch.setenv("LAMBDA_TASKS_SECRET_MY_VAR", VALID_REF)
292
292
  monkeypatch.delenv("MY_VAR", raising=False)
293
293
 
294
294
  payload = json.dumps({"MY_KEY": "cached-value"})
@@ -307,7 +307,7 @@ class TestCaching:
307
307
  secret_loader._secret_cache[(ARN, "AWSCURRENT", "AWSCURRENT")] = {
308
308
  "MY_KEY": "pre-cached"
309
309
  }
310
- monkeypatch.setenv("AWS_SECRETS_MANAGER_MY_VAR", VALID_REF)
310
+ monkeypatch.setenv("LAMBDA_TASKS_SECRET_MY_VAR", VALID_REF)
311
311
  monkeypatch.delenv("MY_VAR", raising=False)
312
312
 
313
313
  with patch("lambda_tasks.secret_loader.boto3") as mock_boto3: