Git Product home page Git Product logo

postmarker's Introduction

Postmarker

Build Coverage Version Python versions Documentation Status Gitter License

Python client library for Postmark API.

Gitter: https://gitter.im/Stranger6667/postmarker

Installation

Postmarker can be obtained with pip:

$ pip install postmarker

Usage example

NOTE:

The attributes of all classes are provided as is, without transformation to snake case. We don't want to introduce new names for existing entities.

Send single email:

>>> from postmarker.core import PostmarkClient
>>> postmark = PostmarkClient(server_token='API_TOKEN')
>>> postmark.emails.send(
    From='[email protected]',
    To='[email protected]',
    Subject='Postmark test',
    HtmlBody='<html><body><strong>Hello</strong> dear Postmark user.</body></html>'
)

Send batch:

>>> postmark.emails.send_batch(
    {
        'From': '[email protected]',
        'To': '[email protected]',
        'Subject': 'Postmark test',
        'HtmlBody': '<html><body><strong>Hello</strong> dear Postmark user.</body></html>',
    },
    {
        'From': '[email protected]',
        'To': '[email protected]',
        'Subject': 'Postmark test 2',
        'HtmlBody': '<html><body><strong>Hello</strong> dear Postmark user.</body></html>',
    }
)

Setup an email:

>>> email = postmark.emails.Email(
    From='[email protected]',
    To='[email protected]',
    Subject='Postmark test',
    HtmlBody='<html><body><strong>Hello</strong> dear Postmark user.</body></html>'
)
>>> email['X-Accept-Language'] = 'en-us, en'
>>> email.attach('/home/user/readme.txt')
>>> email.attach_binary(content=b'content', filename='readme.txt')
>>> email.send()

There are a lot of features available. Check it out in our documentation! Here's just a few of them:

  • Support for sending Python email instances.
  • Bounces, Domains, Messages, Templates, Sender signatures, Status, Stats & Server API.
  • Django email backend.
  • Tornado helper.
  • Spam check API.
  • Wrappers for Bounce, Inbound, Open and Delivery webhooks.

Documentation

You can view the documentation online at:

Or you can look at the docs/ directory in the repository.

Python support

Postmarker supports CPython 3.6 - 3.10 and PyPy 3.7 & 3.8.

Thanks

Many thanks to Shmele and lobziik for their reviews and advice :)

postmarker's People

Contributors

avityk avatar cmabastar avatar colinhoernig avatar gregsadetsky avatar joveice avatar micthiesen avatar pyup-bot avatar robshep avatar sileht avatar stranger6667 avatar suriya avatar z33dd avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

postmarker's Issues

Send Tag information via the Django interface

I can't figure out a way to send Tag information to Postmark when using the Django EmailBackend. I did dive deep into the code but unfortunately had difficulty figuring out where the right place even to patch it would be.

I know I could use the regular PostmarkClient, but it's important for us to continue using the built-in Django mechanism so that we can stay vendor agnostic.

Can you point me in the right direction on where a patch would go?

Library incorrectly quotes long lines when sent via API

If large HTML files (particularly those with large lines, for example anything produced using MJML with the --min minification flag enabled) are sent using Postmarker, the HTML is escaped using quoted-printable and sent to Postmark's API that way, when it should actually be sent unquoted.

The reason this happens is under the hood, Postmarker calls the Django email backend's message.message() within EmailBackend.prepare_message(). This has the message turn itself into MIME, which then is later deconstructed back to a dict using Email.from_mime(). Since Django thinks it's serializing the email for an SMTP send, it automatically quotes the email or alternative attachment under certain circumstances (utf-8 + long lines, see django.core.mail.message.py:220) and sets the Content-Transfer-Encoding to quoted-printable. When Postmarker turns that back into a dict, it loses the Content-Transfer-Encoding header and does not unquote, leading to the quoted version being sent directly via Postmark's API and completely incorrect emails being sent, particularly if they include any Outlook-specific markup that uses XML namespaces and colons.

One way to fix this is when converting the MIME email back to a dictionary, to detect if it has been quoted and then to unquote using email.quoprimime.body_decode(...). Alternatively, the better way to fix this is to refactor simply not to let Django's mailing backend create the MIME email at all, and simply send the body and headers to Postmark via the API without doing any unnecessary processing. This is how djrill for Mandrill works, and emails I sent with Postmarker that got mangled were not mangled with djrill.

Bounce manager

This feature allows users to access to Bounce API in this way:

server_client.bounces.deliverystats

or

bounce = server_client.bounces.get(1)  # Get bounce with ID=1

Refactor clients

It will be better to use something simple instead of 2 client classes (and another one for status api)

E.g.

from postmarker.core import PostmarkClient

postmark = PostmarkClient(token='API_TOKEN')
postmark.emails.send(...)
postmark.bounces.all()
postmark.status
postmark.last_incident

Extension points

E.g. signals for some events like email sending. It could be useful

Bounce model

Instance returned by Bounce manager:

>>> bounce = server_client.bounces.get(1)

Should provide the following:

>>> bounce.dump  # /bounces/{bounceid}/dump
>>> bounce.activate()  # /bounces/{bounceid}/activate

along with basic fields like Type, TypeCode, etc.

The methods above should also be available for Bounce manager:

>>> server_client.bounces.activate(1)
>>> server_client.bounces.get_dump(1)

Testing feature

Another useful feature - use Postmark test API key - POSTMARK_API_TEST.

Unable to handle Unicode in message body using API

Sorry to be leaving you so many issues!

Looks like there's now an issue when sending messages at least with unicode characters in the body. The message I am sending usings django.template.Template to render a template to a string and then submit it as the message HTML body.

This problem goes away if you temporarily set sys.setdefaultencoding('utf8') (which you obviously shouldn't be doing).

Here is the traceback:

/Users/../utils/emails.pyc in send_html_email(subject, from_email, to_emails, reply_to, text_body, html_body, headers, tag, metadata, subaccount, attachments, send_in, extra_params, bcc)
    128             setattr(msg, key, value)
    129
--> 130     msg.send()
    131
    132

/Users/../.virtualenvs/TCJ/lib/python2.7/site-packages/django/core/mail/message.pyc in send(self, fail_silently)
    340             # send to.
    341             return 0
--> 342         return self.get_connection(fail_silently).send_messages([self])
    343
    344     def attach(self, filename=None, content=None, mimetype=None):

/Users/../.virtualenvs/TCJ/lib/python2.7/site-packages/postmarker/django.pyc in send_messages(self, email_messages)
     55             client_created = self.open()
     56             prepared_messages = [self.prepare_message(message) for message in email_messages]
---> 57             response = self.client.emails.send_batch(*prepared_messages, TrackOpens=self.get_option('TRACK_OPENS'))
     58             msg_count = len(response)
     59             if client_created:

/Users/../.virtualenvs/TCJ/lib/python2.7/site-packages/postmarker/models/emails.pyc in send_batch(self, *emails, **extra)
    322         :param extra: dictionary with extra arguments for every message in the batch.
    323         """
--> 324         return self.EmailBatch(*emails).send(**extra)
    325
    326     # NOTE. The following methods are included here to expose better interface without need to import relevant classes.

/Users/../.virtualenvs/TCJ/lib/python2.7/site-packages/postmarker/models/emails.pyc in send(self, **extra)
    242         :rtype: `list`
    243         """
--> 244         emails = self.as_dict(**extra)
    245         responses = [self._manager._send_batch(*batch) for batch in chunks(emails, self.MAX_SIZE)]
    246         return sum(responses, [])

/Users/../.virtualenvs/TCJ/lib/python2.7/site-packages/postmarker/models/emails.pyc in as_dict(self, **extra)
    220         :return: List of dictionaries.
    221         """
--> 222         return [self._construct_email(email, **extra) for email in self.emails]
    223
    224     def _construct_email(self, email, **extra):

/Users/../.virtualenvs/TCJ/lib/python2.7/site-packages/postmarker/models/emails.pyc in _construct_email(self, email, **extra)
    229             email = Email(manager=self._manager, **email)
    230         elif isinstance(email, (MIMEText, MIMEMultipart)):
--> 231             email = Email.from_mime(email, self._manager)
    232         elif not isinstance(email, Email):
    233             raise ValueError

/Users/../.virtualenvs/TCJ/lib/python2.7/site-packages/postmarker/models/emails.pyc in from_mime(cls, message, manager)
    178         """
    179         if isinstance(message, MIMEMultipart):
--> 180             text, html, attachments = deconstruct_multipart(message)
    181         else:
    182             text, html, attachments = message.get_payload(decode=True).decode(), None, []

/Users/../.virtualenvs/TCJ/lib/python2.7/site-packages/postmarker/models/emails.pyc in deconstruct_multipart(message)
     85             text = part.get_payload(decode=True).decode()
     86         elif content_type == 'text/html':
---> 87             html = part.get_payload(decode=True).decode()
     88         elif content_type != 'multipart/alternative':
     89             attachments.append(part)

UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 3495: ordinal not in range(128)

I'm currently getting around this with the following context manager, but obviously this cannot be the case long-term:

@contextlib.contextmanager
def set_default_encoding(encoding='ascii'):
    """\
    Temporarily set the default encoding within the span of the context.

    NOTE: Using `sys.setdefaultencoding()` is HIGHLY DISCOURAGED and this context
    manager should only be used when working with third party libraries that incorrectly
    handle unicode/strings/bytestrings and cannot easily be patched, updated, or worked around.
    """
    import sys
    reload(sys)
    original_encoding = sys.getdefaultencoding()
    sys.setdefaultencoding(encoding)
    yield
    sys.setdefaultencoding(original_encoding)

Server pools

It could be useful feature:

>>> server_pool = ServerPool(tokens=['token1', 'token2'])
>>> server_pool.bounces.all()
[<Bounce: 1>, <Bounce: 2>]

It allows users to work with multiple server clients in the same way as with single client

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.