pennypost 0.1.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.
- pennypost-0.1.0/PKG-INFO +17 -0
- pennypost-0.1.0/README.md +7 -0
- pennypost-0.1.0/pennypost/__init__.py +86 -0
- pennypost-0.1.0/pennypost/py.typed +0 -0
- pennypost-0.1.0/pennypost.egg-info/PKG-INFO +17 -0
- pennypost-0.1.0/pennypost.egg-info/SOURCES.txt +8 -0
- pennypost-0.1.0/pennypost.egg-info/dependency_links.txt +1 -0
- pennypost-0.1.0/pennypost.egg-info/top_level.txt +1 -0
- pennypost-0.1.0/pyproject.toml +14 -0
- pennypost-0.1.0/setup.cfg +4 -0
pennypost-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pennypost
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official Python SDK for PennyPost, the affordable email API. Zero dependencies.
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Homepage, https://pennypost.io/docs
|
|
7
|
+
Keywords: email,transactional-email,email-api,pennypost
|
|
8
|
+
Requires-Python: >=3.9
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
|
|
11
|
+
pennypost — official Python SDK for [PennyPost](https://pennypost.io).
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from pennypost import PennyPost
|
|
15
|
+
pp = PennyPost("pp_live_...")
|
|
16
|
+
pp.send_email({"from": "Receipts <r@yourdomain.com>", "to": ["a@b.com"], "subject": "Hi", "text": "Hello"})
|
|
17
|
+
```
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Generated from api/openapi.json (sha 7c58f5917c4b) by sdk/generate.ts — do not edit by hand.
|
|
2
|
+
# PennyPost: the affordable email API. https://pennypost.io/docs
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
import json
|
|
6
|
+
import urllib.error
|
|
7
|
+
import urllib.parse
|
|
8
|
+
import urllib.request
|
|
9
|
+
|
|
10
|
+
SPEC_SHA = "7c58f5917c4b"
|
|
11
|
+
_DEFAULT_BASE = "https://api.pennypost.io"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _q(v):
|
|
15
|
+
return urllib.parse.quote(str(v), safe="")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class PennyPostError(Exception):
|
|
19
|
+
"""Typed API error: .status, .type, .code, .param, .retryable, .detail."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, status: int, body: dict):
|
|
22
|
+
err = body.get("error", {}) if isinstance(body, dict) else {}
|
|
23
|
+
super().__init__(err.get("message") or f"HTTP {status}")
|
|
24
|
+
self.status = status
|
|
25
|
+
self.type = err.get("type", "provider")
|
|
26
|
+
self.code = err.get("code", "unknown_error")
|
|
27
|
+
self.param = err.get("param")
|
|
28
|
+
self.retryable = bool(err.get("retryable", status >= 500))
|
|
29
|
+
self.detail = err.get("detail")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class PennyPost:
|
|
33
|
+
"""PennyPost client. Zero dependencies (stdlib only).
|
|
34
|
+
|
|
35
|
+
pp = PennyPost("pp_live_...")
|
|
36
|
+
pp.send_email({"from": "Receipts <r@yourdomain.com>", "to": ["a@b.com"],
|
|
37
|
+
"subject": "Hi", "text": "Hello"})
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(self, api_key: str, base_url: str = _DEFAULT_BASE):
|
|
41
|
+
if not api_key.startswith("pp_"):
|
|
42
|
+
raise ValueError("pass your API key (pp_live_... or pp_test_...)")
|
|
43
|
+
self._key = api_key
|
|
44
|
+
self._base = base_url.rstrip("/")
|
|
45
|
+
|
|
46
|
+
def _request(self, method: str, path: str, body, query, idempotency_key) -> dict:
|
|
47
|
+
url = self._base + path
|
|
48
|
+
if query:
|
|
49
|
+
qs = urllib.parse.urlencode({k: v for k, v in query.items() if v is not None})
|
|
50
|
+
if qs:
|
|
51
|
+
url += "?" + qs
|
|
52
|
+
headers = {"Authorization": f"Bearer {self._key}"}
|
|
53
|
+
data = None
|
|
54
|
+
if body is not None:
|
|
55
|
+
headers["Content-Type"] = "application/json"
|
|
56
|
+
data = json.dumps(body).encode()
|
|
57
|
+
if idempotency_key:
|
|
58
|
+
headers["Idempotency-Key"] = idempotency_key
|
|
59
|
+
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
|
60
|
+
try:
|
|
61
|
+
with urllib.request.urlopen(req) as res:
|
|
62
|
+
return json.loads(res.read() or b"{}")
|
|
63
|
+
except urllib.error.HTTPError as e:
|
|
64
|
+
try:
|
|
65
|
+
parsed = json.loads(e.read() or b"{}")
|
|
66
|
+
except json.JSONDecodeError:
|
|
67
|
+
parsed = {}
|
|
68
|
+
raise PennyPostError(e.code, parsed) from None
|
|
69
|
+
|
|
70
|
+
def send_email(self, req: dict, idempotency_key: str | None = None) -> dict:
|
|
71
|
+
return self._request("POST", f"/v1/emails", req, None, idempotency_key)
|
|
72
|
+
|
|
73
|
+
def list_emails(self, to: str | int | None = None, limit: str | int | None = None, cursor: str | int | None = None) -> dict:
|
|
74
|
+
return self._request("GET", f"/v1/emails", None, {"to": to, "limit": limit, "cursor": cursor}, None)
|
|
75
|
+
|
|
76
|
+
def get_email(self, id: str) -> dict:
|
|
77
|
+
return self._request("GET", f"/v1/emails/{_q(id)}", None, None, None)
|
|
78
|
+
|
|
79
|
+
def list_suppressions(self, limit: str | int | None = None, cursor: str | int | None = None) -> dict:
|
|
80
|
+
return self._request("GET", f"/v1/suppressions", None, {"limit": limit, "cursor": cursor}, None)
|
|
81
|
+
|
|
82
|
+
def add_suppression(self, email: str) -> dict:
|
|
83
|
+
return self._request("POST", f"/v1/suppressions", {"email": email}, None, None)
|
|
84
|
+
|
|
85
|
+
def remove_suppression(self, email: str) -> dict:
|
|
86
|
+
return self._request("DELETE", f"/v1/suppressions/{_q(email)}", None, None, None)
|
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pennypost
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official Python SDK for PennyPost, the affordable email API. Zero dependencies.
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Homepage, https://pennypost.io/docs
|
|
7
|
+
Keywords: email,transactional-email,email-api,pennypost
|
|
8
|
+
Requires-Python: >=3.9
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
|
|
11
|
+
pennypost — official Python SDK for [PennyPost](https://pennypost.io).
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from pennypost import PennyPost
|
|
15
|
+
pp = PennyPost("pp_live_...")
|
|
16
|
+
pp.send_email({"from": "Receipts <r@yourdomain.com>", "to": ["a@b.com"], "subject": "Hi", "text": "Hello"})
|
|
17
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pennypost
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pennypost"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Official Python SDK for PennyPost, the affordable email API. Zero dependencies."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
keywords = ["email", "transactional-email", "email-api", "pennypost"]
|
|
13
|
+
[project.urls]
|
|
14
|
+
Homepage = "https://pennypost.io/docs"
|