labyrinthe-connect 1.2.0__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 (49) hide show
  1. labyrinthe_connect-1.2.0/.gitignore +10 -0
  2. labyrinthe_connect-1.2.0/CHANGELOG.md +102 -0
  3. labyrinthe_connect-1.2.0/LICENSE +21 -0
  4. labyrinthe_connect-1.2.0/PKG-INFO +720 -0
  5. labyrinthe_connect-1.2.0/README.fr.md +690 -0
  6. labyrinthe_connect-1.2.0/README.md +679 -0
  7. labyrinthe_connect-1.2.0/examples/async_send.py +37 -0
  8. labyrinthe_connect-1.2.0/examples/otp_login.py +51 -0
  9. labyrinthe_connect-1.2.0/examples/send_sms.py +37 -0
  10. labyrinthe_connect-1.2.0/examples/webhook_fastapi.py +41 -0
  11. labyrinthe_connect-1.2.0/examples/webhook_flask.py +41 -0
  12. labyrinthe_connect-1.2.0/pyproject.toml +122 -0
  13. labyrinthe_connect-1.2.0/requirements-dev.in +18 -0
  14. labyrinthe_connect-1.2.0/requirements-dev.txt +129 -0
  15. labyrinthe_connect-1.2.0/src/labyrinthe_connect/__init__.py +145 -0
  16. labyrinthe_connect-1.2.0/src/labyrinthe_connect/_client.py +208 -0
  17. labyrinthe_connect-1.2.0/src/labyrinthe_connect/_data.py +187 -0
  18. labyrinthe_connect-1.2.0/src/labyrinthe_connect/_redact.py +39 -0
  19. labyrinthe_connect-1.2.0/src/labyrinthe_connect/_transport.py +524 -0
  20. labyrinthe_connect-1.2.0/src/labyrinthe_connect/_version.py +1 -0
  21. labyrinthe_connect-1.2.0/src/labyrinthe_connect/contrib/__init__.py +29 -0
  22. labyrinthe_connect-1.2.0/src/labyrinthe_connect/contrib/django.py +114 -0
  23. labyrinthe_connect-1.2.0/src/labyrinthe_connect/contrib/fastapi.py +86 -0
  24. labyrinthe_connect-1.2.0/src/labyrinthe_connect/contrib/flask.py +73 -0
  25. labyrinthe_connect-1.2.0/src/labyrinthe_connect/errors.py +459 -0
  26. labyrinthe_connect-1.2.0/src/labyrinthe_connect/pagination.py +151 -0
  27. labyrinthe_connect-1.2.0/src/labyrinthe_connect/phone.py +93 -0
  28. labyrinthe_connect-1.2.0/src/labyrinthe_connect/py.typed +0 -0
  29. labyrinthe_connect-1.2.0/src/labyrinthe_connect/resources/__init__.py +16 -0
  30. labyrinthe_connect-1.2.0/src/labyrinthe_connect/resources/account.py +76 -0
  31. labyrinthe_connect-1.2.0/src/labyrinthe_connect/resources/otp.py +217 -0
  32. labyrinthe_connect-1.2.0/src/labyrinthe_connect/resources/sms.py +467 -0
  33. labyrinthe_connect-1.2.0/src/labyrinthe_connect/types.py +476 -0
  34. labyrinthe_connect-1.2.0/src/labyrinthe_connect/webhooks.py +327 -0
  35. labyrinthe_connect-1.2.0/tests/__init__.py +0 -0
  36. labyrinthe_connect-1.2.0/tests/conftest.py +25 -0
  37. labyrinthe_connect-1.2.0/tests/helpers.py +190 -0
  38. labyrinthe_connect-1.2.0/tests/test_api_1_2.py +512 -0
  39. labyrinthe_connect-1.2.0/tests/test_client.py +175 -0
  40. labyrinthe_connect-1.2.0/tests/test_contract.py +528 -0
  41. labyrinthe_connect-1.2.0/tests/test_contrib.py +384 -0
  42. labyrinthe_connect-1.2.0/tests/test_errors.py +284 -0
  43. labyrinthe_connect-1.2.0/tests/test_integration.py +267 -0
  44. labyrinthe_connect-1.2.0/tests/test_otp.py +117 -0
  45. labyrinthe_connect-1.2.0/tests/test_phone.py +93 -0
  46. labyrinthe_connect-1.2.0/tests/test_retry.py +408 -0
  47. labyrinthe_connect-1.2.0/tests/test_security.py +170 -0
  48. labyrinthe_connect-1.2.0/tests/test_sms.py +326 -0
  49. labyrinthe_connect-1.2.0/tests/test_webhooks.py +219 -0
@@ -0,0 +1,10 @@
1
+ # Environnements et caches Python
2
+ .venv/
3
+ __pycache__/
4
+ *.py[cod]
5
+ .mypy_cache/
6
+ .ruff_cache/
7
+ .pytest_cache/
8
+ .coverage
9
+ *.egg-info/
10
+ dist/
@@ -0,0 +1,102 @@
1
+ # Changelog
2
+
3
+ All notable changes to `labyrinthe-connect` (Python) are documented here. This project follows
4
+ [Semantic Versioning](https://semver.org/) and the format of [Keep a Changelog](https://keepachangelog.com/).
5
+
6
+ ## [Unreleased]
7
+
8
+ ## [1.2.0] - 2026-11-06
9
+
10
+ Built against the API specification 1.3.0. Backwards compatible with 1.1.x.
11
+
12
+ ### Added
13
+ - `FeatureDisabledError` (503 `FEATURE_DISABLED`, any operation), a subclass of `ServiceUnavailableError`: the
14
+ platform switched off the whole API (`blocked_by == "api"`) or one function (`api.sms_send`, `api.otp_send`,
15
+ `api.otp_verify`); nothing was done. Properties `feature`, `blocked_by`, `admin_message` (`details.message`),
16
+ `until` (aware `datetime`, `None` when unknown or unreadable) and `retry_after` (`Retry-After` seconds, `None` when
17
+ no re-enable is scheduled). Exported from `labyrinthe_connect`.
18
+
19
+ ### Changed
20
+ - 503 `OTP_SERVICE_DISABLED` (`otp.send`) is now raised as `FeatureDisabledError` (still a `ServiceUnavailableError`),
21
+ with the same details (`feature` and `blocked_by` = `otp`).
22
+ - Retries: `FEATURE_DISABLED` and `OTP_SERVICE_DISABLED` are never retried with an exponential backoff. They are
23
+ retried for every operation, `otp.verify` included (refused before processing), only when `Retry-After` is present
24
+ and not longer than `max_retry_wait`, after exactly that delay; otherwise the error is raised at once. Other 5xx
25
+ are unchanged.
26
+
27
+ ## [1.1.1] - 2026-11-06
28
+
29
+ ### Added
30
+ - `IdempotencyKeyUnresolvedError` (409 `IDEMPOTENCY_KEY_UNRESOLVED`, `sms.send`, `sms.send_many` and `otp.send`): the
31
+ original request with this `Idempotency-Key` failed unexpectedly and its outcome is unknown (an SMS may have been
32
+ sent). Never retried: check (`sms.list(reference=...)` or with the user), then send again with a new key.
33
+
34
+ ### Fixed
35
+ - Phone numbers are now sent to the API in E.164 with their `+` (`recipient`, `recipients`, the OTP `phone` and the
36
+ `recipient` filter of `sms.list()`). Without it, the API read a short foreign number (10 digits or fewer, e.g.
37
+ `+2481234567`, Seychelles) as a local DR Congo number and sent it to `2432481234567`. `phone.normalize()` still
38
+ returns digits without `+` (the format of the API responses).
39
+
40
+ ## [1.1.0] - 2026-11-06
41
+
42
+ Built against the API specification 1.2.0. Backwards compatible with 1.0.0.
43
+
44
+ ### Added
45
+ - `request_id` (`X-Request-Id`) on every error (`LabyrintheError.request_id`; for a network error, the id you sent)
46
+ and on every returned object (`Message`, `MessageBatch`, `Estimate`, `Balance`, `SenderId`, `OtpSendResult`,
47
+ `OtpVerifyResult`, pages), left out of equality and `repr()`. `ApiError.api_version` (`X-Api-Version`).
48
+ - `request_id=` on every method: sent as `X-Request-Id` on every attempt (checked locally against the spec pattern).
49
+ - `IdempotencyRequestInProgressError` (409 `IDEMPOTENCY_REQUEST_IN_PROGRESS`, `retry_after`): retried automatically
50
+ after `Retry-After` with the same `Idempotency-Key` for `sms.send`, `sms.send_many` and `otp.send`.
51
+ - `AccountSuspendedError` (403 `ACCOUNT_SUSPENDED`), a subclass of `PermissionError`.
52
+ - `otp.send()`: automatic `Idempotency-Key` (`uuid4`, or `idempotency_key=`) reused across retries; `web_otp_domain`
53
+ and `strip_accents` parameters; `OtpSendResult.code_length`, `code_type` (`OtpCodeType`: `numeric`,
54
+ `alphanumeric`, `unknown`), `resend_after_seconds`, `replayed`, `idempotency_key`.
55
+ - `OtpVerifyResult.attempts_remaining`.
56
+ - `strip_accents` on `sms.send()`, `sms.send_many()` and `sms.estimate()`.
57
+ - Webhooks: `MessageStatusChanged.data.reference`, `error_code`, `error_message`; new events `OtpVerified`
58
+ (`otp.verified`) and `OtpFailed` (`otp.failed`) with `OtpWebhookData` and `OtpVerificationStatus`; Django signals
59
+ `otp_verified` and `otp_failed`; FastAPI and Flask examples updated.
60
+
61
+ ### Changed
62
+ - `otp.send()` is now retried, with the same `Idempotency-Key`, after a network error, a 5xx, a 429
63
+ `TOO_MANY_REQUESTS` or a 409 `IDEMPOTENCY_REQUEST_IN_PROGRESS` (never after `OTP_RESEND_TOO_SOON` or
64
+ `OTP_RATE_LIMITED`). `otp.verify()` is still retried only on 429 `TOO_MANY_REQUESTS`.
65
+ - Phone normalisation aligned with the API: without `+`, 11 digits or more not starting with 0 are already
66
+ international (`33612345678` is no longer prefixed with 243). DR Congo formats are unchanged.
67
+ - `OtpVerifyResult` equality now includes `attempts_remaining`.
68
+ - `retry_after` of `OtpRateLimitedError` and `OtpResendTooSoonError` reads the `Retry-After` header now sent by the API.
69
+ - Fields added by the API 1.2.0 are read leniently (`None` / `UNKNOWN` when an older backend omits them); `[]`
70
+ error details and `Z` / fractional dates are still accepted.
71
+
72
+ ## [1.0.0] - 2026-11-06
73
+
74
+ First stable release, built against the API specification 1.1.0.
75
+
76
+ ### Added
77
+ - `Labyrinthe` (synchronous) and `AsyncLabyrinthe` (asyncio) clients with the same surface:
78
+ `api_key`, `base_url`, `timeout`, `max_retries`, `max_retry_wait`, `default_country_code`, `default_sender`,
79
+ `http_client`; `LABYRINTHE_API_KEY` / `LABYRINTHE_BASE_URL` read from the environment; context managers;
80
+ `is_test_mode` / `is_live_mode`.
81
+ - SMS: `send()`, `send_many()` (all or nothing, `replayed`), `estimate()`, `get()`, `list()` with filters and
82
+ pagination (`auto_paging_iter()`, `for` / `async for`).
83
+ - Balance and approved Sender IDs.
84
+ - OTP: `send()` (with `test_code` in test mode, hidden from `repr()` and dropped when pickled) and `verify()`.
85
+ - Automatic `Idempotency-Key` (`uuid4`) on every SMS send, reused across retries.
86
+ - Retries with exponential backoff and jitter, only when safe (GET, estimate, SMS send; OTP only on
87
+ 429 `TOO_MANY_REQUESTS`); `Retry-After` support capped by `max_retry_wait`; no redirect following.
88
+ - Typed errors for every documented error code (`LabyrintheError` → `ApiError` → …), `APIConnectionError`,
89
+ `APITimeoutError`, `InvalidArgumentError`; the original `httpx` exception is never chained.
90
+ - `webhooks.construct_event()`: constant-time HMAC-SHA256 verification on the raw body, timestamp tolerance, typed
91
+ frozen events (`MessageStatusChanged`, `CampaignCompleted`, `BalanceLow`, `TestPing`, `UnknownEvent`);
92
+ `sign()` and `verify_signature()`.
93
+ - Optional integrations: `contrib.django` (view + signals), `contrib.fastapi` (dependency + router),
94
+ `contrib.flask` (blueprint + signal); extras `[django]`, `[fastapi]`, `[flask]`.
95
+ - `phone` helpers (normalisation identical to the API, validation, masking); `logging` logger
96
+ `labyrinthe_connect` (no key, code, body or full number); fully typed (`py.typed`); only dependency: `httpx`.
97
+
98
+ [Unreleased]: https://github.com/Labyrinthe-Team/labyrinthe-connect-sdks/compare/python-v1.2.0...HEAD
99
+ [1.2.0]: https://github.com/Labyrinthe-Team/labyrinthe-connect-sdks/compare/python-v1.1.1...python-v1.2.0
100
+ [1.1.1]: https://github.com/Labyrinthe-Team/labyrinthe-connect-sdks/compare/python-v1.1.0...python-v1.1.1
101
+ [1.1.0]: https://github.com/Labyrinthe-Team/labyrinthe-connect-sdks/compare/python-v1.0.0...python-v1.1.0
102
+ [1.0.0]: https://github.com/Labyrinthe-Team/labyrinthe-connect-sdks/releases/tag/python-v1.0.0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Labyrinthe Connect
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.