mailerbot 0.1.0__py3-none-any.whl

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.
mailerbot/__init__.py ADDED
@@ -0,0 +1,50 @@
1
+ from .client import MailerBot
2
+ from .async_client import AsyncMailerBot
3
+ from .exceptions import (
4
+ MailerBotError,
5
+ AuthenticationError,
6
+ PermissionError,
7
+ NotFoundError,
8
+ ValidationError,
9
+ RateLimitError,
10
+ ServerError,
11
+ )
12
+ from .models import (
13
+ Page,
14
+ Contact, ContactList, AddressValidation, CsvImportResult,
15
+ Document,
16
+ Postcard, PostcardTemplate,
17
+ Mailing, CostCalculation, MailingItemTracking, MailingItemScan,
18
+ Campaign,
19
+ DashboardStats, ReportingData,
20
+ PaymentIntent, SavedCard,
21
+ QrLink, QrAnalytics,
22
+ CouponList, CouponCode, CouponImportResult, CouponAvailability,
23
+ Asset,
24
+ MergeTag,
25
+ )
26
+
27
+ __version__ = "0.1.0"
28
+ __all__ = [
29
+ "MailerBot",
30
+ "AsyncMailerBot",
31
+ "MailerBotError",
32
+ "AuthenticationError",
33
+ "PermissionError",
34
+ "NotFoundError",
35
+ "ValidationError",
36
+ "RateLimitError",
37
+ "ServerError",
38
+ "Page",
39
+ "Contact", "ContactList", "AddressValidation", "CsvImportResult",
40
+ "Document",
41
+ "Postcard", "PostcardTemplate",
42
+ "Mailing", "CostCalculation", "MailingItemTracking", "MailingItemScan",
43
+ "Campaign",
44
+ "DashboardStats", "ReportingData",
45
+ "PaymentIntent", "SavedCard",
46
+ "QrLink", "QrAnalytics",
47
+ "CouponList", "CouponCode", "CouponImportResult", "CouponAvailability",
48
+ "Asset",
49
+ "MergeTag",
50
+ ]
mailerbot/_http.py ADDED
@@ -0,0 +1,53 @@
1
+ from __future__ import annotations
2
+ import httpx
3
+ from .exceptions import (
4
+ MailerBotError, AuthenticationError, PermissionError,
5
+ NotFoundError, ValidationError, RateLimitError, ServerError,
6
+ )
7
+
8
+ DEFAULT_BASE_URL = "https://api.mailerbot.com/api/v1"
9
+ DEFAULT_TIMEOUT = 30.0
10
+
11
+
12
+ def _raise_for_status(response: httpx.Response) -> None:
13
+ if response.is_success:
14
+ return
15
+ try:
16
+ body = response.json()
17
+ except Exception:
18
+ body = {"detail": response.text}
19
+
20
+ detail = body.get("detail", str(body))
21
+ status = response.status_code
22
+
23
+ if status == 401:
24
+ raise AuthenticationError(detail, status_code=status, response=body)
25
+ if status == 403:
26
+ raise PermissionError(detail, status_code=status, response=body)
27
+ if status == 404:
28
+ raise NotFoundError(detail, status_code=status, response=body)
29
+ if status == 422:
30
+ raise ValidationError(detail, status_code=status, response=body)
31
+ if status == 429:
32
+ raise RateLimitError(detail, status_code=status, response=body)
33
+ if status >= 500:
34
+ raise ServerError(detail, status_code=status, response=body)
35
+ raise MailerBotError(detail, status_code=status, response=body)
36
+
37
+
38
+ def _build_headers(
39
+ *,
40
+ api_key: str | None = None,
41
+ bearer_token: str | None = None,
42
+ ) -> dict[str, str]:
43
+ headers: dict[str, str] = {
44
+ "Content-Type": "application/json",
45
+ "Accept": "application/json",
46
+ }
47
+ if api_key:
48
+ headers["X-API-Key"] = api_key
49
+ elif bearer_token:
50
+ headers["Authorization"] = f"Bearer {bearer_token}"
51
+ else:
52
+ raise ValueError("Either api_key or bearer_token must be provided")
53
+ return headers
@@ -0,0 +1,43 @@
1
+ from __future__ import annotations
2
+ from typing import Generic, TypeVar, Callable, Iterator, AsyncIterator
3
+ from .models.common import Page
4
+
5
+ T = TypeVar("T")
6
+
7
+
8
+ class PageIterator(Generic[T]):
9
+ """Sync auto-paginating iterator. Yields individual items across all pages."""
10
+
11
+ def __init__(self, fetch: Callable[[int, int], Page[T]], page_size: int = 100):
12
+ self._fetch = fetch
13
+ self._page_size = page_size
14
+
15
+ def __iter__(self) -> Iterator[T]:
16
+ page = 1
17
+ while True:
18
+ result = self._fetch(page, self._page_size)
19
+ yield from result.items
20
+ if page >= result.pages:
21
+ break
22
+ page += 1
23
+
24
+
25
+ class AsyncPageIterator(Generic[T]):
26
+ """Async auto-paginating iterator. Yields individual items across all pages."""
27
+
28
+ def __init__(self, fetch: Callable[[int, int], object], page_size: int = 100):
29
+ self._fetch = fetch
30
+ self._page_size = page_size
31
+
32
+ def __aiter__(self) -> AsyncIterator[T]:
33
+ return self._iterate()
34
+
35
+ async def _iterate(self) -> AsyncIterator[T]:
36
+ page = 1
37
+ while True:
38
+ result = await self._fetch(page, self._page_size)
39
+ for item in result.items:
40
+ yield item
41
+ if page >= result.pages:
42
+ break
43
+ page += 1