Skip to content

Commit e178c06

Browse files
committed
Initial import
0 parents  commit e178c06

2 files changed

Lines changed: 323 additions & 0 deletions

File tree

src/postmark/__init__.py

Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
__version__ = '0.1.1'
2+
__author__ = "David Martorana ([email protected])"
3+
__date__ = '2009-December-2'
4+
__url__ = 'http://postmarkapp.com'
5+
__copyright__ = "(C) 2009 David Martorana, Wildbit LLC, Python Software Foundation."
6+
7+
__doc__ = '''
8+
9+
PMMail object for Postmark (http://postmarkapp.com)
10+
11+
Version: ''' + __version__ + '''
12+
Author: ''' + __author__ + '''
13+
Date: ''' + __date__ + '''
14+
15+
CHANGE LOG:
16+
17+
Version 0.1.1:
18+
Initial release
19+
20+
USEAGE:
21+
Make sure you have a Postmark account. Visit
22+
http://postmarkapp.com to sign up for an account.
23+
Requires a Postmark API key.
24+
25+
Import postmark.PMMail to use Postmark. Check
26+
class documentation on PMMail object for more
27+
information.
28+
29+
DJANGO:
30+
The library can be used stand-alone with Django. You can also
31+
add the setting
32+
33+
POSTMARK_API_KEY = 'your-key'
34+
35+
to your settings.py file, and when you create a new PMMail object,
36+
it will grab the API key automatically.
37+
38+
39+
EXCEPTIONS:
40+
PMMailMissingValueException(Exception):
41+
One of the required values for attempting a send request is missing
42+
43+
PMMailSendException(Exception):
44+
Base Postmark send exception
45+
46+
PMMailUnauthorizedException(PMMailSendException):
47+
401: Unathorized sending due to bad API key
48+
49+
PMMailUnprocessableEntityException(PMMailSendException):
50+
422: Unprocessable Entity - usually an exception with either the sender
51+
not having a matching Sender Signature in Postmark. Read the message
52+
details for further information
53+
54+
PMMailServerErrorException(PMMailSendException):
55+
500: Internal error - this is on the Postmark server side. Errors are
56+
logged and recorded at Postmark.
57+
58+
PMMailURLException(PMMailSendException):
59+
A URLError was caught - usually has to do with connectivity
60+
and the ability to reach the server. The inner_exception will
61+
have the base URLError object.
62+
63+
TODO:
64+
Add automatic multipart emails via regex stripping of HTML tags from html_body
65+
if the .multipart property is set to True
66+
67+
'''
68+
69+
#
70+
# Imports (JSON library based on import try)
71+
72+
import sys
73+
import urllib
74+
import urllib2
75+
76+
try:
77+
import json
78+
except ImportError:
79+
try:
80+
import simplejson as json
81+
except ImportError:
82+
raise Exception('Cannot use PMMail without Python 2.6 or greater, or Python 2.4 or 2.5 and the "simplejson" library')
83+
84+
#
85+
#
86+
__POSTMARK_URL__ = 'http://api.postmarkapp.com/email'
87+
88+
class PMMail(object):
89+
'''
90+
The Postmark Mail object.
91+
'''
92+
def __init__(self, **kwargs):
93+
'''
94+
Keyword arguments are:
95+
api_key: Your Postmark server API key
96+
sender: Who the email is coming from, in either
97+
"[email protected]" or "First Last <[email protected]>" format
98+
recipient: Who to send the email to, in either
99+
"[email protected]" or "First Last <[email protected]>" format
100+
subject: Subject of the email
101+
html_body: Email message in HTML
102+
text_body: Email message in plain text
103+
'''
104+
# initiate properties
105+
self.__api_key = None
106+
self.__sender = None
107+
self.__recipient = None
108+
self.__subject = None
109+
self.__html_body = None
110+
self.__text_body = None
111+
#self.__multipart = False
112+
113+
acceptable_keys = (
114+
'api_key',
115+
'sender',
116+
'recipient',
117+
'subject',
118+
'html_body',
119+
'text_body',
120+
#'multipart'
121+
)
122+
123+
for key in kwargs:
124+
if key in acceptable_keys:
125+
setattr(self, '_PMMail__%s' % key, kwargs[key])
126+
127+
# Set up the user-agent
128+
self.__user_agent = 'Python/%s (Postmark PMMail Library version %s)' % ('_'.join([str(var) for var in sys.version_info]), __version__)
129+
130+
# Try to pull in the API key from Django
131+
try:
132+
import django
133+
from django.conf import settings as django_settings
134+
self.__api_key = django_settings.POSTMARK_API_KEY
135+
self.__user_agent = '%s (Django %s)' % (self.__user_agent, '_'.join([str(var) for var in django.VERSION]))
136+
except ImportError:
137+
pass
138+
139+
#
140+
# Properties
141+
142+
api_key = property(
143+
lambda self: self.__api_key,
144+
lambda self, value: setattr(self, '_PMMail__api_key', value),
145+
"The API Key for your server on Postmark"
146+
)
147+
148+
sender = property(
149+
lambda self: self.__sender,
150+
lambda self, value: setattr(self, '_PMMail__sender', value),
151+
'''
152+
The sender, in either "[email protected]" or "First Last <[email protected]>" formats.
153+
The address should match one of your Sender Signatures in Postmark.
154+
'''
155+
)
156+
157+
recipient = property(
158+
lambda self: self.__recipient,
159+
lambda self, value: setattr(self, '_PMMail__recipient', value),
160+
'The recipient, in either "[email protected]" or "First Last <[email protected]>" formats'
161+
)
162+
163+
subject = property(
164+
lambda self: self.__subject,
165+
lambda self, value: setattr(self, '_PMMail__subject', value),
166+
'The subject of your email message'
167+
)
168+
169+
html_body = property(
170+
lambda self: self.__html_body,
171+
lambda self, value: setattr(self, '_PMMail__html_body', value),
172+
'The email message body, in html format'
173+
)
174+
175+
text_body = property(
176+
lambda self: self.__text_body,
177+
lambda self, value: setattr(self, '_PMMail__text_body', value),
178+
'The email message body, in text format'
179+
)
180+
181+
# multipart = property(
182+
# lambda self: self.__multipart,
183+
# lambda self, value: setattr(self, '_PMMail__multipart', value),
184+
# 'The API Key for one of your servers on Postmark'
185+
# )
186+
187+
def _check_values(self):
188+
'''
189+
Make sure all values are of the appropriate
190+
type and are not missing.
191+
'''
192+
if not self.__api_key:
193+
raise PMMailMissingValueException('Cannot send an e-mail without an Postmark API Key')
194+
elif not self.__sender:
195+
raise PMMailMissingValueException('Cannot send an e-mail without a sender')
196+
elif not self.__recipient:
197+
raise PMMailMissingValueException('Cannot send an e-mail without a recipient')
198+
elif not self.__subject:
199+
raise PMMailMissingValueException('Cannot send an e-mail without a subject')
200+
elif not self.__html_body and not self.__text_body:
201+
raise PMMailMissingValueException('Cannot send an e-mail without either an HTML or text version of your e-mail body')
202+
203+
204+
def send(self):
205+
'''
206+
Send the email through the Postmark system
207+
'''
208+
self._check_values()
209+
210+
# Set up message dictionary
211+
json_message = {
212+
'From': self.__sender,
213+
'To': self.__recipient,
214+
'Subject': self.__subject,
215+
}
216+
217+
if self.__html_body:
218+
has_html = True
219+
json_message['HtmlBody'] = self.__html_body
220+
221+
if self.__text_body:
222+
has_text = True
223+
json_message['TextBody'] = self.__text_body
224+
225+
# if (self.__html_body and not self.__text_body) and self.__multipart:
226+
# # TODO: Set up regex to strip html
227+
# pass
228+
229+
# Set up the url Request
230+
req = urllib2.Request(
231+
__POSTMARK_URL__,
232+
json.dumps(json_message),
233+
{
234+
'Accept': 'application/json',
235+
'Content-Type': 'application/json',
236+
'X-Postmark-Server-Token': self.__api_key,
237+
'User-agent': self.__user_agent
238+
}
239+
)
240+
241+
# Attempt send
242+
try:
243+
result = urllib2.urlopen(req)
244+
result.close()
245+
if result.code == 200:
246+
return True
247+
else:
248+
raise PMMainSendException('Return code %d: %s' % (result.code, result.msg))
249+
except urllib2.HTTPError, err:
250+
if err.code == 401:
251+
raise PMMailUnauthorizedException('Sending Unauthorized - incorrect API key.', err)
252+
elif err.code == 422:
253+
try:
254+
jsontxt = err.read()
255+
jsonobj = json.loads(jsontxt)
256+
desc = jsonobj['Message']
257+
except:
258+
desc = 'Description not given'
259+
raise PMMailUnprocessableEntityException('Unprocessable Entity: %s' % desc)
260+
elif err.code == 500:
261+
raise PMMailServerErrorException('Internal server error at Postmark. Admins have been alerted.', err)
262+
except urllib2.URLError, err:
263+
if hasattr(err, 'reason'):
264+
raise PMMailURLException('URLError: Failed to reach the server: %s (See "inner_exception" for details)' % err.reason, err)
265+
elif hasattr(err, 'code'):
266+
raise PMMailURLException('URLError: %d: The server couldn\'t fufill the request. (See "inner_exception" for details)' % err.code, err)
267+
else:
268+
raise PMMailURLException('URLError: The server couldn\'t fufill the request. (See "inner_exception" for details)', err)
269+
270+
271+
#
272+
# Exceptions
273+
274+
class PMMailMissingValueException(Exception):
275+
def __init__(self, value):
276+
self.parameter = value
277+
def __str__(self):
278+
return repr(self.parameter)
279+
280+
class PMMailSendException(Exception):
281+
'''
282+
Base Postmark send exception
283+
'''
284+
def __init__(self, value, inner_exception=None):
285+
self.parameter = value
286+
self.inner_exception = inner_exception
287+
def __str__(self):
288+
return repr(self.parameter)
289+
290+
class PMMailUnauthorizedException(PMMailSendException):
291+
'''
292+
401: Unathorized sending due to bad API key
293+
'''
294+
pass
295+
296+
class PMMailUnprocessableEntityException(PMMailSendException):
297+
'''
298+
422: Unprocessable Entity - usually an exception with either the sender
299+
not having a matching Sender Signature in Postmark. Read the message
300+
details for further information
301+
'''
302+
pass
303+
304+
class PMMailServerErrorException(PMMailSendException):
305+
'''
306+
500: Internal error - this is on the Postmark server side. Errors are
307+
logged and recorded at Postmark.
308+
'''
309+
pass
310+
311+
class PMMailURLException(PMMailSendException):
312+
'''
313+
A URLError was caught - usually has to do with connectivity
314+
and the ability to reach the server. The inner_exception will
315+
have the base URLError object.
316+
'''
317+
pass
318+
319+
320+
321+
322+
323+

src/postmark/__init__.pyc

12.1 KB
Binary file not shown.

0 commit comments

Comments
 (0)