Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/numberoverzero/snails
https://github.com/numberoverzero/snails
Last synced: 18 days ago
JSON representation
- Host: GitHub
- URL: https://github.com/numberoverzero/snails
- Owner: numberoverzero
- License: mit
- Created: 2019-04-26T18:43:23.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2024-06-21T15:54:43.000Z (5 months ago)
- Last Synced: 2024-10-07T09:06:41.037Z (about 1 month ago)
- Language: Python
- Size: 8.79 KB
- Stars: 0
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.rst
- License: LICENSE
Awesome Lists containing this project
README
.. image:: https://img.shields.io/pypi/v/snails.svg?style=flat-square
:target: https://pypi.python.org/pypi/snailsSometimes you want to write a dumb email handler.
Good for: low volume, minimal parsing, interacting with legacy email-based systems
Bad for: high volume, production use, 100% RFC compliance
Requires Python 3.7+
::
pip install snails
=======
Usage
=======.. code-block:: python
import snails
def handle(msg: snails.Message) -> None:
print(f"To: {msg['to']}")
print(f"From: {msg['from']}")
print("Subject: {msg['subject']}")
for p in msg.get_payload():
print(p.get_payload(decode=True))# run and block until ctrl + c
snails.serve(handle, "0.0.0.0", 8025).. code-block:: python
# or, call start/stop yourself
mailbox = snails.Mailbox(handle, "0.0.0.0", 8025)
mailbox.start()============
Enable TLS
============.. code-block:: python
import ssl
import snailsdef handle(msg: bytes) -> None:
... # TODOssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ssl_context.load_cert_chain("cert.pem", "key.pem")mailbox = snails.Mailbox(handle, "::", 25, ssl_context=ssl_context)
=================
Message Parsing
=================When a new request arrives, ``snails`` will pass the envelope to a parser function. You can either provide this
parser yourself, or let snails infer the parser based on your handler's type annotations.Snails provides parsers for the following types:
* ``bytes``
* ``aiosmtpd.smtp.Envelope`` (aliased to ``snails.Envelope``)
* ``email.message.Message`` (aliased to ``snails.Message``)Most of the time it's enough to use an annotation:
.. code-block:: python
def handle(x: bytes):
with open("out.log", "wb") as f:
f.write(x)def handle(x: snails.Envelope):
with open("out.log", "wb") as f:
f.write(x.content)def handle(x: snails.Message):
with open("out.log", "wb") as f:
f.write(x.as_bytes())You can also define your own parser:
.. code-block:: python
def parse(e: snails.Envelope) -> dict:
as_str = e.content.decode()
return {} # TODO your parsingdef handle(x: dict):
... # TODO use the dict parsed abovemailbox = snails.Mailbox(handle, "::", 25, parser=parse)
===============
Async Mailbox
===============Your handler and parser can both be async functions; by default ``snails`` wraps all synchronous functions.
.. code-block:: python
import snails
async def parse(e: snails.Envelope) -> dict:
as_str = e.content.decode()
return {} # TODO your parsingasync def handle(x: dict):
res = await some_db_call(...)mailbox = snails.Mailbox(handle, "::", 25, parser=parse)
=======
Other
=======* You can return a string from your handler such as ``"250 OK"`` or the built-in ``snails.SMTP_250``.
* Instead of ``snails.serve`` use ``Mailbox.start`` and ``Mailbox.stop``
* Call ``snails.serve`` with ``cleanup_at_exit=True`` to ensure ``Mailbox.stop`` is called
when the interpreter is shutting down (enabled by default)
* Call ``snails.serve`` with ``block=True`` to block execution after calling ``Mailbox.start`` (enabled by default).
You can stop the server by sending SIGINT or Ctrl + C.