postwing 0.3.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.
@@ -0,0 +1,528 @@
1
+ Metadata-Version: 2.4
2
+ Name: postwing
3
+ Version: 0.3.0
4
+ Summary: Python SDK for Postwing email service API (api.postwing.app)
5
+ Author-email: Andrey Fanyagin <skymanrm@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/skymanrm/postwing-python-sdk
8
+ Project-URL: Repository, https://github.com/skymanrm/postwing-python-sdk
9
+ Project-URL: Issues, https://github.com/skymanrm/postwing-python-sdk/issues
10
+ Keywords: email,postwing,sdk,api,email-service
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.7
15
+ Classifier: Programming Language :: Python :: 3.8
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Communications :: Email
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Requires-Python: >=3.7
23
+ Description-Content-Type: text/markdown
24
+ Requires-Dist: requests>=2.25.0
25
+ Provides-Extra: dev
26
+ Requires-Dist: faker>=8.0.0; extra == "dev"
27
+
28
+ # PostwingSDK
29
+
30
+ A Python SDK for the [Postwing](https://postwing.ru) email service API. Provides both synchronous and asynchronous methods for sending emails via templates or simple HTML content.
31
+
32
+ ## Features
33
+
34
+ - **Synchronous and Asynchronous API** - Choose between blocking and non-blocking email sending
35
+ - **Template Support** - Send emails using pre-configured templates with parameters
36
+ - **Simple HTML Emails** - Send plain HTML emails directly
37
+ - **Idempotency Keys** - Prevent duplicate email sends with unique keys
38
+ - **Multi-language Support** - Send templated emails in different languages
39
+ - **Thread Pool Execution** - Efficient concurrent email sending with configurable worker threads
40
+ - **Callback Support** - Handle async results with callbacks for fire-and-forget patterns
41
+ - **Fail Silently Mode** - Option to suppress exceptions for graceful degradation
42
+ - **Comprehensive Logging** - Configurable logging levels with detailed request/response information for debugging
43
+
44
+ ## Installation
45
+
46
+ Install the required dependencies:
47
+
48
+ ```bash
49
+ pip install -r requirements.txt
50
+ ```
51
+
52
+ ### Dependencies
53
+
54
+ - `requests` - For making HTTP API calls
55
+ - `faker` - For running tests (development only)
56
+
57
+ ## Quick Start
58
+
59
+ ```python
60
+ from postwing.sdk import PostwingSdk
61
+
62
+ # Initialize the SDK
63
+ sdk = PostwingSdk(
64
+ username="your-domain@example.com",
65
+ password="your-api-token"
66
+ )
67
+
68
+ # Send a simple HTML email
69
+ sdk.send_simple(
70
+ recipient="user@example.com",
71
+ sender="noreply@example.com",
72
+ subject="Welcome!",
73
+ body="<h1>Hello World</h1>"
74
+ )
75
+
76
+ # Send a templated email
77
+ sdk.send(
78
+ tpl="welcome-template",
79
+ recipient="user@example.com",
80
+ sender="noreply@example.com",
81
+ lang="en",
82
+ params={"name": "John", "code": "123456"}
83
+ )
84
+ ```
85
+
86
+ ## Usage
87
+
88
+ ### Synchronous Methods
89
+
90
+ #### Send Simple HTML Email
91
+
92
+ ```python
93
+ sdk.send_simple(
94
+ recipient="user@example.com",
95
+ sender="noreply@example.com",
96
+ subject="Important Notice",
97
+ body="<p>This is an important message.</p>",
98
+ idempotency_key="unique-key-123" # Optional: prevent duplicates
99
+ )
100
+ ```
101
+
102
+ #### Send Templated Email
103
+
104
+ ```python
105
+ sdk.send(
106
+ tpl="password-reset",
107
+ recipient="user@example.com",
108
+ sender="noreply@example.com",
109
+ lang="en", # Optional: language code
110
+ params={"reset_link": "https://example.com/reset/token"}, # Template variables
111
+ idempotency_key="reset-user-123" # Optional: prevent duplicates
112
+ )
113
+ ```
114
+
115
+ ### Asynchronous Methods
116
+
117
+ Async methods use a thread pool executor for non-blocking operation. They return `Future` objects that can be used in various ways:
118
+
119
+ #### Fire and Forget
120
+
121
+ Send emails without waiting for responses:
122
+
123
+ ```python
124
+ sdk.send_simple_async(
125
+ recipient="user@example.com",
126
+ sender="noreply@example.com",
127
+ subject="Newsletter",
128
+ body="<h1>Latest Updates</h1>"
129
+ )
130
+ # Continues immediately without blocking
131
+ ```
132
+
133
+ #### Using Callbacks
134
+
135
+ Handle results with callback functions:
136
+
137
+ ```python
138
+ def on_complete(success, error):
139
+ if error:
140
+ print(f"Failed to send email: {error}")
141
+ else:
142
+ print("Email sent successfully!")
143
+
144
+ sdk.send_async(
145
+ tpl="notification",
146
+ recipient="user@example.com",
147
+ sender="noreply@example.com",
148
+ params={"message": "You have a new notification"},
149
+ callback=on_complete
150
+ )
151
+ ```
152
+
153
+ #### Wait for Results
154
+
155
+ Send async but wait for completion when needed:
156
+
157
+ ```python
158
+ future = sdk.send_simple_async(
159
+ recipient="user@example.com",
160
+ sender="noreply@example.com",
161
+ subject="Confirmation",
162
+ body="<p>Please confirm your action</p>"
163
+ )
164
+
165
+ # Do other work...
166
+
167
+ # Wait for result (with timeout)
168
+ try:
169
+ result = future.result(timeout=10) # Wait up to 10 seconds
170
+ print(f"Email sent: {result}")
171
+ except Exception as e:
172
+ print(f"Email failed: {e}")
173
+ ```
174
+
175
+ #### Batch Sending
176
+
177
+ Send multiple emails concurrently:
178
+
179
+ ```python
180
+ recipients = ["user1@example.com", "user2@example.com", "user3@example.com"]
181
+ futures = []
182
+
183
+ for recipient in recipients:
184
+ future = sdk.send_simple_async(
185
+ recipient=recipient,
186
+ sender="noreply@example.com",
187
+ subject="Batch Email",
188
+ body="<p>Hello!</p>",
189
+ idempotency_key=f"batch-{recipient}"
190
+ )
191
+ futures.append(future)
192
+
193
+ # Wait for all to complete
194
+ for future in futures:
195
+ try:
196
+ future.result(timeout=30)
197
+ except Exception as e:
198
+ print(f"Failed: {e}")
199
+ ```
200
+
201
+ #### Check Status Without Blocking
202
+
203
+ ```python
204
+ future = sdk.send_simple_async(
205
+ recipient="user@example.com",
206
+ sender="noreply@example.com",
207
+ subject="Status Check",
208
+ body="<p>Testing</p>"
209
+ )
210
+
211
+ if future.done():
212
+ result = future.result()
213
+ print(f"Already completed: {result}")
214
+ else:
215
+ print("Still processing...")
216
+ ```
217
+
218
+ ## Configuration
219
+
220
+ ### SDK Options
221
+
222
+ ```python
223
+ import logging
224
+
225
+ sdk = PostwingSdk(
226
+ username="your-domain@example.com",
227
+ password="your-api-token",
228
+ fail_silently=False, # If True, suppresses exceptions
229
+ max_workers=5, # Number of concurrent threads for async operations
230
+ log_level=logging.INFO # Logging level (default: INFO)
231
+ )
232
+ ```
233
+
234
+ ### Logging
235
+
236
+ The SDK includes comprehensive logging capabilities to help with debugging and monitoring email operations.
237
+
238
+ #### Log Levels
239
+
240
+ The SDK supports standard Python logging levels:
241
+
242
+ - `logging.DEBUG` - Detailed information including request/response data (recommended for development)
243
+ - `logging.INFO` - General operational messages about SDK lifecycle and email sends (default)
244
+ - `logging.WARNING` - Warning messages
245
+ - `logging.ERROR` - Error messages only
246
+
247
+ #### Basic Logging Configuration
248
+
249
+ ```python
250
+ import logging
251
+ from postwing.sdk import PostwingSdk
252
+
253
+ # Enable DEBUG logging to see detailed request/response information
254
+ sdk = PostwingSdk(
255
+ username="your-domain@example.com",
256
+ password="your-api-token",
257
+ log_level=logging.DEBUG
258
+ )
259
+
260
+ # Send an email - you'll see detailed logs
261
+ sdk.send_simple(
262
+ recipient="user@example.com",
263
+ sender="noreply@example.com",
264
+ subject="Test Email",
265
+ body="<p>Testing with debug logs</p>"
266
+ )
267
+ ```
268
+
269
+ #### What Gets Logged
270
+
271
+ **INFO Level:**
272
+ - SDK initialization with configuration
273
+ - ThreadPoolExecutor creation and shutdown
274
+ - Successful email sends
275
+
276
+ **DEBUG Level:**
277
+ - All INFO level messages
278
+ - Full request details (URL, parameters, sanitized payload)
279
+ - Full response details (status codes, response body)
280
+ - Async task lifecycle (submission, start, completion)
281
+ - Callback execution
282
+
283
+ **ERROR Level:**
284
+ - API errors (non-2xx responses)
285
+ - Network/connection errors
286
+ - Async task failures
287
+
288
+ #### Example Output
289
+
290
+ ```
291
+ 2025-11-21 17:36:05,170 - postwing.sdk.4472389120 - INFO - PostwingSdk initialized with username=test, max_workers=5, log_level=INFO
292
+ 2025-11-21 17:36:05,171 - postwing.sdk.4472389120 - INFO - Initializing ThreadPoolExecutor with 5 workers
293
+ 2025-11-21 17:36:05,171 - postwing.sdk.4472389120 - DEBUG - Sending simple email - URL: https://api.postwing.app/external/send_email_simple/, recipient: user@example.com, sender: noreply@example.com, subject: Test, idempotency_key: None
294
+ 2025-11-21 17:36:05,171 - postwing.sdk.4472389120 - DEBUG - Request payload: {"recipient": "user@example.com", "sender": "noreply@example.com", "subject": "Test", "body": "<p>Test</p>", "auth": "***"}
295
+ 2025-11-21 17:36:05,171 - postwing.sdk.4472389120 - DEBUG - Response received - status_code: 200, response: {"success": true}
296
+ 2025-11-21 17:36:05,171 - postwing.sdk.4472389120 - INFO - Simple email sent successfully to user@example.com
297
+ ```
298
+
299
+ #### Disable Logging
300
+
301
+ To disable all logging output:
302
+
303
+ ```python
304
+ sdk = PostwingSdk(
305
+ username="your-domain@example.com",
306
+ password="your-api-token",
307
+ log_level=logging.CRITICAL # Only critical errors
308
+ )
309
+ ```
310
+
311
+ #### Production Recommendations
312
+
313
+ For production environments, we recommend:
314
+
315
+ 1. Use `logging.INFO` or `logging.WARNING` to avoid logging sensitive data
316
+ 2. Configure external log aggregation (e.g., CloudWatch, Datadog)
317
+ 3. Monitor ERROR level logs for operational issues
318
+ 4. Use DEBUG level only for troubleshooting specific issues
319
+
320
+ #### Custom Logging Configuration
321
+
322
+ If you need more control over logging format or handlers, you can configure Python's logging system before initializing the SDK:
323
+
324
+ ```python
325
+ import logging
326
+
327
+ # Configure global logging
328
+ logging.basicConfig(
329
+ level=logging.INFO,
330
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
331
+ handlers=[
332
+ logging.FileHandler('postwing.log'),
333
+ logging.StreamHandler()
334
+ ]
335
+ )
336
+
337
+ # SDK will use the configured logging system
338
+ sdk = PostwingSdk(
339
+ username="your-domain@example.com",
340
+ password="your-api-token",
341
+ log_level=logging.DEBUG
342
+ )
343
+ ```
344
+
345
+ ### Cleanup
346
+
347
+ Properly shutdown the thread pool when done:
348
+
349
+ ```python
350
+ # Wait for all pending emails to complete before shutdown
351
+ sdk.shutdown(wait=True)
352
+ ```
353
+
354
+ Or use a try-finally pattern:
355
+
356
+ ```python
357
+ try:
358
+ sdk.send_simple_async(...)
359
+ # ... more operations
360
+ finally:
361
+ sdk.shutdown(wait=True)
362
+ ```
363
+
364
+ ## API Reference
365
+
366
+ ### `PostwingSdk`
367
+
368
+ #### Constructor
369
+
370
+ ```python
371
+ PostwingSdk(username: str, password: str, fail_silently=False, max_workers=5, log_level=logging.INFO)
372
+ ```
373
+
374
+ - `username` - Your Postwing account username (typically your domain)
375
+ - `password` - Your Postwing API token
376
+ - `fail_silently` - If True, suppresses exceptions on errors
377
+ - `max_workers` - Number of threads for async operations (default: 5)
378
+ - `log_level` - Logging level using Python's logging constants (default: logging.INFO). Use logging.DEBUG for detailed request/response logs
379
+
380
+ #### Methods
381
+
382
+ ##### `send_simple(recipient, sender, subject, body, idempotency_key=None) -> bool`
383
+
384
+ Send a simple HTML email synchronously.
385
+
386
+ ##### `send(tpl, recipient, sender, lang=None, params=None, idempotency_key=None) -> bool`
387
+
388
+ Send a templated email synchronously.
389
+
390
+ ##### `send_simple_async(recipient, sender, subject, body, idempotency_key=None, callback=None) -> Future`
391
+
392
+ Send a simple HTML email asynchronously.
393
+
394
+ ##### `send_async(tpl, recipient, sender, lang=None, params=None, idempotency_key=None, callback=None) -> Future`
395
+
396
+ Send a templated email asynchronously.
397
+
398
+ ##### `shutdown(wait=True)`
399
+
400
+ Shutdown the thread pool executor.
401
+
402
+ ### Exceptions
403
+
404
+ #### `PostwingSdkException`
405
+
406
+ Raised when API requests fail or network errors occur. Can be suppressed with `fail_silently=True`.
407
+
408
+ ## Development
409
+
410
+ ### Using the Makefile
411
+
412
+ The project includes a Makefile for common development tasks:
413
+
414
+ ```bash
415
+ # View all available commands
416
+ make help
417
+
418
+ # Install dependencies
419
+ make install
420
+
421
+ # Run all tests
422
+ make test
423
+
424
+ # Run synchronous tests only
425
+ make test-sync
426
+
427
+ # Run asynchronous tests only
428
+ make test-async
429
+
430
+ # Run a specific test
431
+ make test-specific TEST=tests.PostwingAsyncTestUtils.test_send_simple_async_success
432
+
433
+ # Build the package
434
+ make build
435
+
436
+ # Clean build artifacts
437
+ make clean
438
+ ```
439
+
440
+ ### Testing
441
+
442
+ The SDK includes a comprehensive test suite covering both synchronous and asynchronous operations.
443
+
444
+ #### Quick Test Commands (Using Makefile)
445
+
446
+ ```bash
447
+ # Run all tests
448
+ make test
449
+
450
+ # Run specific test classes
451
+ make test-sync # Synchronous tests only
452
+ make test-async # Asynchronous tests only
453
+
454
+ # Run a specific test
455
+ make test-specific TEST=tests.PostwingAsyncTestUtils.test_send_simple_async_success
456
+ ```
457
+
458
+ #### Manual Test Commands (Without Makefile)
459
+
460
+ ```bash
461
+ # Run all tests
462
+ source .venv/bin/activate
463
+ PYTHONPATH=/Users/skyman/Documents/My/Python:$PYTHONPATH python -m unittest tests
464
+
465
+ # Run specific test class
466
+ PYTHONPATH=/Users/skyman/Documents/My/Python:$PYTHONPATH python -m unittest tests.PostwingTestUtils
467
+
468
+ # Run specific test
469
+ PYTHONPATH=/Users/skyman/Documents/My/Python:$PYTHONPATH python -m unittest tests.PostwingAsyncTestUtils.test_send_simple_async_success
470
+ ```
471
+
472
+ ### Building and Publishing
473
+
474
+ Build the package for distribution:
475
+
476
+ ```bash
477
+ # Build the package
478
+ make build
479
+
480
+ # Publish to TestPyPI (for testing)
481
+ make publish-test
482
+
483
+ # Publish to PyPI (production)
484
+ make publish
485
+ ```
486
+
487
+ Or manually:
488
+
489
+ ```bash
490
+ # Build
491
+ python -m build
492
+
493
+ # Check the distribution
494
+ twine check dist/*
495
+
496
+ # Upload to PyPI
497
+ twine upload dist/*
498
+ ```
499
+
500
+ ## Examples
501
+
502
+ See `async_example.py` for comprehensive examples of all async patterns including:
503
+ - Fire and forget
504
+ - Callback handling
505
+ - Waiting for results
506
+ - Batch sending
507
+ - Status checking
508
+ - Proper cleanup
509
+
510
+ ## API Endpoints
511
+
512
+ The SDK communicates with the following Postwing API endpoints:
513
+
514
+ - **Base URL**: `https://api.postwing.app/external/`
515
+ - **Simple Send**: `POST /external/send_email_simple/`
516
+ - **Template Send**: `POST /external/send_email_tpl/`
517
+
518
+ ## Contributing
519
+
520
+ Contributions are welcome! Please ensure all tests pass before submitting a pull request.
521
+
522
+ ## License
523
+
524
+ [Add your license information here]
525
+
526
+ ## Support
527
+
528
+ For issues, questions, or feature requests, please contact Postwing support or open an issue in this repository.