{ "info": { "author": "Joe Cross", "author_email": "joe.mcross@gmail.com", "bugtrack_url": null, "classifiers": [], "description": ".. image:: https://img.shields.io/pypi/v/snails.svg?style=flat-square\n :target: https://pypi.python.org/pypi/snails\n\nSometimes you want to write a dumb email handler.\n\nGood for: low volume, minimal parsing, interacting with legacy email-based systems\n\nBad for: high volume, production use, 100% RFC compliance\n\n\nRequires Python 3.7+\n\n::\n\n pip install snails\n\n=======\n Usage\n=======\n\n.. code-block:: python\n\n import snails\n\n\n def handle(msg: snails.Message) -> None:\n print(f\"To: {msg['to']}\")\n print(f\"From: {msg['from']}\")\n print(\"Subject: {msg['subject']}\")\n for p in msg.get_payload():\n print(p.get_payload(decode=True))\n\n # run and block until ctrl + c\n snails.serve(handle, \"127.0.0.1\", 10025)\n\n # or, call start/stop yourself\n mailbox = snails.Mailbox(handle, \"127.0.0.1\", 10025)\n mailbox.start()\n\n============\n Enable TLS\n============\n\n.. code-block:: python\n\n import ssl\n import snails\n\n\n def handle(msg: bytes) -> None:\n ... # TODO\n\n\n ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)\n ssl_context.load_cert_chain(\"cert.pem\", \"key.pem\")\n\n mailbox = snails.Mailbox(handle, \"::\", 25, ssl_context=ssl_context)\n\n=================\n Message Parsing\n=================\n\nWhen a new request arrives, ``snails`` will pass the envelope to a parser function. You can either provide this\nparser yourself, or let snails infer the parser based on your handler's type annotations.\n\nSnails provides parsers for the following types:\n\n* ``bytes``\n* ``aiosmtpd.smtp.Envelope`` (aliased to ``snails.Envelope``)\n* ``email.message.Message`` (aliased to ``snails.Message``)\n\n\nMost of the time it's enough to use an annotation:\n\n.. code-block:: python\n\n def handle(x: bytes):\n with open(\"out.log\", \"wb\") as f:\n f.write(x)\n\n def handle(x: snails.Envelope):\n with open(\"out.log\", \"wb\") as f:\n f.write(x.content)\n\n def handle(x: snails.Message):\n with open(\"out.log\", \"wb\") as f:\n f.write(x.as_bytes())\n\n\nYou can also define your own parser:\n\n.. code-block:: python\n\n def parse(e: snails.Envelope) -> dict:\n as_str = e.content.decode()\n return {} # TODO your parsing\n\n\n def handle(x: dict):\n ... # TODO use the dict parsed above\n\n\n mailbox = snails.Mailbox(handle, \"::\", 25, parser=parse)\n\n===============\n Async Mailbox\n===============\n\nYour handler and parser can both be async functions; by default ``snails`` wraps all synchronous functions.\n\n.. code-block:: python\n\n import snails\n\n async def parse(e: snails.Envelope) -> dict:\n as_str = e.content.decode()\n return {} # TODO your parsing\n\n\n async def handle(x: dict):\n res = await some_db_call(...)\n\n\n mailbox = snails.Mailbox(handle, \"::\", 25, parser=parse)\n\n=======\n Other\n=======\n\n* You can return a string from your handler such as ``\"250 OK\"`` or the built-in ``snails.SMTP_250``.\n* Instead of ``snails.serve`` use ``Mailbox.start`` and ``Mailbox.stop``\n* Call ``snails.serve`` with ``cleanup_at_exit=True`` to ensure ``Mailbox.stop`` is called\n when the interpreter is shutting down (enabled by default)\n* Call ``snails.serve`` with ``block=True`` to block execution after calling ``Mailbox.start`` (enabled by default).\n You can stop the server by sending SIGINT or Ctrl + C.\n\n\n", "description_content_type": "text/x-rst", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/numberoverzero/snails", "keywords": "", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "snails", "package_url": "https://pypi.org/project/snails/", "platform": "any", "project_url": "https://pypi.org/project/snails/", "project_urls": { "Homepage": "https://github.com/numberoverzero/snails" }, "release_url": "https://pypi.org/project/snails/2.0/", "requires_dist": [ "aiosmtpd" ], "requires_python": "", "summary": "minimal smtpd handler", "version": "2.0" }, "last_serial": 5206995, "releases": { "1.0": [ { "comment_text": "", "digests": { "md5": "3179ecec6273922050e3b4900abff21c", "sha256": "61f741155338305c17031bc09c51c985a9a20a0301f64405ca4e364481cdc2b3" }, "downloads": -1, "filename": "snails-1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "3179ecec6273922050e3b4900abff21c", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 3802, "upload_time": "2019-04-27T20:29:51", "url": "https://files.pythonhosted.org/packages/70/82/77de965f3d8eb73a3cab9e48a53b2f21b3b2c397c5590d322eec307dd7a3/snails-1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "b380a47d76e4e2bc198bdc3594f8092a", "sha256": "eab3d5da549c2896872d3757eada2d456d22a6734b7d4adcf9435c842d356a22" }, "downloads": -1, "filename": "snails-1.0.tar.gz", "has_sig": false, "md5_digest": "b380a47d76e4e2bc198bdc3594f8092a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 2898, "upload_time": "2019-04-27T20:30:01", "url": "https://files.pythonhosted.org/packages/22/6a/6a987e25faaf94e88725d43d015ea44ff81fd2bf3f31111a9df9a0490d63/snails-1.0.tar.gz" } ], "2.0": [ { "comment_text": "", "digests": { "md5": "b1f80576f0b704dcb0931407530c846c", "sha256": "0eb811d6ceec10a8820bfe7cd02b07ebf89876501e036a69e4f7325d6886d7ca" }, "downloads": -1, "filename": "snails-2.0-py3-none-any.whl", "has_sig": false, "md5_digest": "b1f80576f0b704dcb0931407530c846c", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 4909, "upload_time": "2019-04-30T08:12:05", "url": "https://files.pythonhosted.org/packages/71/c2/e732fb738f57b5f443320f6608198d005839eb5c2721f846e7aaf3d68e8d/snails-2.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "ef62d1dd8ffe7fe941988f59114db4f9", "sha256": "091e126f1af9c715fa986372e9ed0ee927a69455bb3f30927b50ec83b2409f8b" }, "downloads": -1, "filename": "snails-2.0.tar.gz", "has_sig": false, "md5_digest": "ef62d1dd8ffe7fe941988f59114db4f9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4089, "upload_time": "2019-04-30T08:12:10", "url": "https://files.pythonhosted.org/packages/f8/cf/fd831b93da54156c8117ed2a2611b4c730ab07a9d8a4cc4017fa12d0eebe/snails-2.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "b1f80576f0b704dcb0931407530c846c", "sha256": "0eb811d6ceec10a8820bfe7cd02b07ebf89876501e036a69e4f7325d6886d7ca" }, "downloads": -1, "filename": "snails-2.0-py3-none-any.whl", "has_sig": false, "md5_digest": "b1f80576f0b704dcb0931407530c846c", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 4909, "upload_time": "2019-04-30T08:12:05", "url": "https://files.pythonhosted.org/packages/71/c2/e732fb738f57b5f443320f6608198d005839eb5c2721f846e7aaf3d68e8d/snails-2.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "ef62d1dd8ffe7fe941988f59114db4f9", "sha256": "091e126f1af9c715fa986372e9ed0ee927a69455bb3f30927b50ec83b2409f8b" }, "downloads": -1, "filename": "snails-2.0.tar.gz", "has_sig": false, "md5_digest": "ef62d1dd8ffe7fe941988f59114db4f9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4089, "upload_time": "2019-04-30T08:12:10", "url": "https://files.pythonhosted.org/packages/f8/cf/fd831b93da54156c8117ed2a2611b4c730ab07a9d8a4cc4017fa12d0eebe/snails-2.0.tar.gz" } ] }