Coder Social home page Coder Social logo

aiosmtplib's Introduction

aiosmtplib

"aiosmtplib CircleCI build status" "pre-commit.ci status" codecov "aiosmtplib on the Python Package Index" pypi-status "aiosmtplib on pypy.tech" pypi-python-versions pypi-license


aiosmtplib is an asynchronous SMTP client for use with asyncio.

For documentation, see Read The Docs.

Quickstart

import asyncio
from email.message import EmailMessage

import aiosmtplib

message = EmailMessage()
message["From"] = "root@localhost"
message["To"] = "[email protected]"
message["Subject"] = "Hello World!"
message.set_content("Sent via aiosmtplib")

asyncio.run(aiosmtplib.send(message, hostname="127.0.0.1", port=25))

Requirements

Python 3.8+ is required.

Bug Reporting

Bug reports (and feature requests) are welcome via Github issues.

aiosmtplib's People

Contributors

agronholm avatar bmwiedemann avatar cole avatar dependabot-preview[bot] avatar etodd avatar fabaff avatar gjcarneiro avatar ikrivosheev avatar jellezijlstra avatar montag451 avatar p-eb avatar pauloxnet avatar pre-commit-ci[bot] avatar pyup-bot avatar rafaelrds avatar retnikt avatar snyk-bot avatar suspiciousraccoon avatar thijstriemstra avatar voldemat avatar wombatonfire avatar zh0uquan 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  avatar  avatar  avatar  avatar  avatar  avatar

aiosmtplib's Issues

Reconnecting after a failed connect seems to hang indefinitely

Try the following snippet:

import aiosmtplib, asyncio

async def c():
    s = aiosmtplib.SMTP('asd.asd.com')
    try:
        await s.connect()
    except:
        print('error raised for the first time')
        try:
            await s.connect()
        except:
            print('error raised for the first time, exiting')
            return

asyncio.get_event_loop().run_until_complete(c())

I would expect for the same error to be raised twice, but the obtained result was that an error is raised only for the first connection and the second one hangs indefinitely.

I'm running aiosmtplib 1.0.2 under python 3.6.6.

Support for authentication and TLS

I'd like to use this with email services (e.g. mandrill, sendgrid, Amazon SES) and as a pre-requisite it would need to support authentication.

Ideally it should support TLS for security, which may be a pre-requisite of some email services.

Package license file

Could you please add the license file to MANIFEST.in and setup.cfg so that it will be included in sdists, whls, and other packages?

Upload new release to PyPI

Following the resolution of #42, is it possible to upload a new release to PyPI? I'm asking this because at my work place we rely on PyPI to deploy our softwares.

PS : I'm sorry to use the issue tracker to ask you that but I don't know a better way :(

Am I using auth correctly?

I'm trying to use a remote SMTP server that requires a email and password. Though the SMTP class doesn't appear to take a username and password as it arguments. I did find the SMTP.auth_login function in the documentation.

I've managed to establish what I think is a secure connection, though I'm unsure if I'm using the API correctly. Here's what I have:

smtp = aiosmtplib.SMTP(
    hostname=SMTP_HOSTNAME,
    port=SMTP_PORT,
    use_tls=False)
_ = await smtp.connect()
_ = await smtp.ehlo()
_ = await smtp.starttls()
_ = await smtp.ehlo()
_ = await smtp.auth_login(SMTP_USERNAME, SMTP_PASSWORD)

I feel like I may be calling internal API stuff or, if I'm meant to, I may be doing it incorrectly. My knowleage of SMTP servers/protocols is between zero and none.

Thanks for the library.

Usage as an asynchronous context manager

It would be nice if the SMTP class could be used as an async context manager. From digging around, it looks like it might be possible, but it isn't documented.

Unicode support in address lines

I've looked around in the tests for examples on how to properly send mails with non-ascii characters in address lines. I cannot get the following to work:

import asyncio
from email.message import EmailMessage
from aiosmtplib.email import formataddr

import aiosmtplib

PASSWORD = 'password'
USER = '[email protected]'


async def send_hello_world():
    message = EmailMessage()
    message["From"] = "[email protected]"
    message["To"] = formataddr(("æøå", "[email protected]"))
    message["Subject"] = "Hello World!"
    message.set_content("Sent via aiosmtplib")

    send = await aiosmtplib.send(message,
                          hostname="smtp.office365.com",
                          port=587,
                        start_tls=True,
                          password=PASSWORD,
                          username=USER)
    return send

event_loop = asyncio.get_event_loop()
event_loop.run_until_complete(send_hello_world())

The interesting line is this: message["To"] = formataddr(("æøå", "[email protected]")). Theæøå breaks the email somehow.

I do receive an email - however, there is no subject and the email body is the EmailMessage sort of converted to a string. I could succesfully send the email with smtplib with no issues. What do I nee

regarding the concurrency benenchmark

@cole Hello cole, thanks for the good library, learned a lot from this cool asyncio library.

As you said in the parallel execution, we can't gain a lot from asynchronous communication since SMTP is sequential protocol, thus even with async, we can only send email one by one, thus we can't solve I/O bound issue except creating a new SMTP client.
so this means we either have lots of emails to send to one smtp server, or emails belongs to different smtp server, thus we gain benefits? and have you done some benchmark some comparing to sync smtp client in threads?

SMTP AUTH extension not supported by server

async def send_hello_world():
message = EmailMessage()
client = SMTP(hostname="smtp.gmail.com", port=587,username='[email protected]',password='************', use_tls=False)
print("sandeep patel")
message["From"] = "[email protected]"
message["To"] = "[email protected]"
message["Subject"] = "Hello World!"
message.set_content("Sent via aiosmtplib")
async with client:
await client.smtp_client(message)

Getting Error
' raise SMTPException("SMTP AUTH extension not supported by server.")\n'
'aiosmtplib.errors.SMTPException: SMTP AUTH extension not supported by server.\n'

Default connect timeout doesn't work

I have a problem with connect operation not terminating after timeout. For example you can try to connect to wrong port:

import asyncio
import aiosmtplib
loop = asyncio.get_event_loop()
smtp = aiosmtplib.SMTP('smtp-mail.outlook.com', 5871, use_tls=True)
async def connect_and_print(smtp):
    await smtp.connect()
    print('connected')
loop.run_until_complete(connect_and_print(smtp))

This code hangs for a long-long time. I also tried to wrap connect into asyncio.wait_for but it also doesn't work.

Support specifying email domain other than hostname by automatically resolving MX record

Hello.

Sometimes it may be desirable to send emails directly to remote mail servers instead of localhost. In my case, I want to push books to Kindle devices by sending emails to @kindle.com.

After roughly looking through the source code, I suppose there is no way so far to specify an email domain (e.g. kindle.com in my case) instead of hostname. I think it would be great if this can be supported by aiosmtplib as it is such a common use scenario. It does take much effort to figure out how to properly resolve and maintain cache/TTL of MX records with asyncio.

Thanks for the project.

incompatible with uvloop because of access to private attribute _protocol

I'm using uvloop because of the significant speed up that it provides. However this seems to be incompatible with using the starttls method.

minimal example (put in actual connection information before running):

import asyncio
import uvloop
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())

import aiosmtplib

async def connect(loop):
    smtp = aiosmtplib.SMTP(
        loop=loop,
        hostname=localhost,
        port=port,
        use_tls=True)
    await smtp.connect(use_tls=False, port=port)
    await smtp.starttls(validate_certs=False)

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(connect(loop))

result

Traceback (most recent call last):
  File "minimal_example.py", line 19, in <module>
    loop.run_until_complete(connect(loop))
  File "uvloop/loop.pyx", line 1203, in uvloop.loop.Loop.run_until_complete (uvloop/loop.c:25632)
  File "minimal_example.py", line 15, in connect
    await smtp.starttls(validate_certs=False)
  File "/git/dnd/lib64/python3.6/site-packages/aiosmtplib/esmtp.py", line 193, in starttls
    tls_context, server_hostname=server_hostname, timeout=timeout)
  File "/git/dnd/lib64/python3.6/site-packages/aiosmtplib/protocol.py", line 209, in starttls
    tls_context, server_hostname=server_hostname, waiter=waiter)
  File "/git/dnd/lib64/python3.6/site-packages/aiosmtplib/protocol.py", line 82, in upgrade_transport
    self.reader._transport._protocol = tls_protocol  # type: ignore
AttributeError: 'uvloop.loop.TCPTransport' object has no attribute '_protocol'

support proxy

Hi,

in our production environment, we need a proxy to send email, is there any plan to support it?

thanks,

Support Unicode response message

Hi Cole,

I was reading the test and see the test_live.py::test_qq_login has been marked because of utf-8 unsupported. As far as I investigated, the reason is that qq smtp service return non-ascii (and even non utf-8 chars):

535 Error: \xc7\xeb\xca\xb9\xd3\xc3\xca\xda\xc8\xa8\xc2\xeb\xb5\xc7\xc2\xbc\xa1\xa3\xcf\xea\xc7\xe9\xc7\xeb\xbf\xb4: http://service.mail.qq.com/cgi-bin/help?subtype=1&&id=28&&no=1001256\r\n

I was thinking of 2 posibility to solve this problem:

  1. Using chardet to guess the response message encoding.
  2. Using re to eliminate non-utf8 (ascii) chars since it is the message, we still have response code

What do you think?

login needs elho every time

Hi,

thanks for the great lib, now I use it couple with tornado to send email async.

there is a problem with the check here
https://github.com/cole/aiosmtplib/blob/master/aiosmtplib/smtp.py#L464
it does nothing

I still need to do ehlo() every time to make it work, otherwise I will get 503

async with smtp:
  await smtp.ehlo()
  await smtp.login(...)
  ...
  await smtp.send_message()

I'm not very familiar with smtp protocol, also I wonder whether there's a plan to simplify the process to be like

smtp = SMTP(host, port, user, pwd)
async with smtp:
  ...
  await smtp.send_message(...)

so I don't need to worry about the login each time

Assertion error when calling send_message on a closed connection

When calling send_message on an aiosmtplib connection where the remote end has closed, I'd expect to get a SMTPServerDisconnected exception.

Instead, I get an assertion error, which is not the most ideal thing to handle:

  File "aiosmtplib/smtp.py", line 229, in send_message
    sender, recipients, flat_message, timeout=timeout)
  File "aiosmtplib/smtp.py", line 152, in sendmail
    await self.mail(sender, options=mail_options, timeout=timeout)
  File "aiosmtplib/esmtp.py", line 217, in mail
    b'MAIL', from_string, *options_bytes, timeout=timeout)
  File "aiosmtplib/connection.py", line 270, in execute_command
    *args, timeout=timeout)
  File "aiosmtplib/protocol.py", line 178, in execute_command
    await self.write_and_drain(command, timeout=timeout)
  File "aiosmtplib/protocol.py", line 146, in write_and_drain
    assert self._stream_writer is not None, 'Client not connected'
AssertionError: Client not connected

Python 3.5, aiosmtplib 1.0.2.

openSUSE i586 failure in test_send_message_smtputf8_sender

test_send_message_smtputf8_sender is failing only on i586

https://build.opensuse.org/package/live_build_log/devel:languages:python/python-aiosmtplib/openSUSE_Tumbleweed/i586

Does it look like it is a aiosmtplib issue, or the test runner aiosmtpd, or ...?

[  110s] ______________________ test_send_message_smtputf8_sender _______________________
[  110s] 
[  110s] smtp_client = <aiosmtplib.smtp.SMTP object at 0xb485426c>
[  110s] smtpd_server_smtputf8 = <Server sockets=[<socket.socket fd=15, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=6, laddr=('127.0.0.1', 52703)>]>
[  110s] message = <email.mime.multipart.MIMEMultipart object at 0xb4ac66cc>
[  110s] received_commands = [('EHLO', 'lamb56'), ('MAIL', 'séndër@exåmple.com', ['SIZE=345', 'SMTPUTF8', 'BODY=8BITMIME']), ('RCPT', '[email protected]', []), ('DATA',), ('QUIT',)]
[  110s] received_messages = [<email.message.Message object at 0xb64846cc>]
[  110s] 
[  110s]     async def test_send_message_smtputf8_sender(
[  110s]         smtp_client, smtpd_server_smtputf8, message, received_commands, received_messages
[  110s]     ):
[  110s]         del message["From"]
[  110s]         message["From"] = "séndër@exåmple.com"
[  110s]     
[  110s]         async with smtp_client:
[  110s]             errors, response = await smtp_client.send_message(message)
[  110s]     
[  110s]         assert not errors
[  110s]         assert response != ""
[  110s]     
[  110s]         assert received_commands[1][0] == "MAIL"
[  110s]         assert received_commands[1][1] == message["From"]
[  110s] >       assert received_commands[1][2] == ["SIZE=372", "SMTPUTF8", "BODY=8BITMIME"]
[  110s] E       AssertionError: assert ['SIZE=345', ...ODY=8BITMIME'] == ['SIZE=372', '...ODY=8BITMIME']
[  110s] E         At index 0 diff: 'SIZE=345' != 'SIZE=372'
[  110s] E         Full diff:
[  110s] E         - ['SIZE=345', 'SMTPUTF8', 'BODY=8BITMIME']
[  110s] E         ?         ^^
[  110s] E         + ['SIZE=372', 'SMTPUTF8', 'BODY=8BITMIME']
[  110s] E         ?         ^^
[  110s] 
[  110s] tests/test_sendmail.py:288: AssertionError

aiohttp

How to implement this with aiohttp server, example for postgres asyncpg i can create a pool but here duno how at all :(

Connection not closed when errors occurs

In the connect() method, if an error occurs (here or here) the connection is not closed since the transport attribute is only set at the end of the method (here). I think that as soon as the connection to the server is made, the transport attribute should be set otherwise the close() method does nothing.

How can i port from existing smtp

This is not the issue. I have email script in traditional smtp. How I can port to aiosmtplib? Is there any steps can you please help me with that?

#112

If I try something similar to the issue: 112, will it work?

Thanks..

SMTP.quit produces warning "Future exception was never retrieved"

I ported this code from my old implementation on standard smtplib module, and perhaps it is not correct for aiosmtplib.
But i think this shouldn't matter.

import asyncio
import aiosmtplib
from email.mime.text import MIMEText
from email.header import Header
async def send_mail(host, login, password, from_addr, to_addr, subject, body_text):
	msg=MIMEText(*body_text)
	msg['Subject']=Header(subject, 'utf-8')
	msg['From']=from_addr
	msg['To']=to_addr
	server=aiosmtplib.SMTP(host)
	await server.connect()
	await server.starttls()
	await server.login(login, password)
	try:
		await server.sendmail(from_addr, [to_addr], msg.as_string())
	except (aiosmtplib.SMTPRecipientsRefused, aiosmtplib.SMTPSenderRefused):
		return 1
	finally:
		await server.quit()
#code for testing
async def start():
	await send_mail('smtp.yandex.ru', '[email protected]', '1234', '[email protected]', '[email protected]', 'Testing', ['Hello', 'plain', 'utf-8'])
	print('success')
#emulating work of other asyncio tasks...
	await asyncio.sleep(3)
asyncio.run(start())```

```Python 3.7.2 (tags/v3.7.2:9a3ffc0492, Dec 23 2018, 23:09:28) [MSC v.1916 64 bit (AMD64)] on win32
>>> import mail_test
success
>>> exit()
Future exception was never retrieved
future: <Future finished exception=SMTPServerDisconnected('Unexpected EOF received')>
aiosmtplib.errors.SMTPServerDisconnected: Unexpected EOF received```

If i comment two last lines of my function, it works good.

```>>> import mail_test
success
>>> exit()```

AttributeError: 'NoneType' object has no attribute 'readuntil'

Hello,

I'm having trouble sending several emails in a loop. When I do so, I've got an error apparently spawning randomly. Do you have any ideas of what the issue is?

Please read the code below.

from time import sleep
import email
import asyncio
import aiosmtplib

loop = asyncio.get_event_loop()
for i in range(20):
    smtp = aiosmtplib.SMTP(hostname='gmail-smtp-in.l.google.com', port=25, loop=loop, use_tls=False)
    msg = email.message_from_string(my_eml)
    loop.run_until_complete(smtp.connect())
    loop.run_until_complete(smtp.starttls())
    loop.run_until_complete(smtp.send_message(msg))
    sleep(0.5)
    smtp.close()
loop.close()
Traceback (most recent call last):
  File "test.py", line 33, in <module>
    loop.run_until_complete(smtp.send_message(msg))
  File "/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncio/base_events.py", line 468, in run_until_complete
    return future.result()
  File "/Users/myuser/.local/share/virtualenvs/proxy-D8_I7f06/lib/python3.6/site-packages/aiosmtplib/smtp.py", line 229, in send_message
    sender, recipients, flat_message, timeout=timeout)
  File "/Users/myuser/.local/share/virtualenvs/proxy-D8_I7f06/lib/python3.6/site-packages/aiosmtplib/smtp.py", line 155, in sendmail
    response = await self.data(message, timeout=timeout)
  File "/Users/myuser/.local/share/virtualenvs/proxy-D8_I7f06/lib/python3.6/site-packages/aiosmtplib/esmtp.py", line 278, in data
    timeout=timeout)
  File "/Users/myuser/.local/share/virtualenvs/proxy-D8_I7f06/lib/python3.6/site-packages/aiosmtplib/protocol.py", line 120, in read_response
    line = await self._readline(timeout=timeout)
  File "/Users/myuser/.local/share/virtualenvs/proxy-D8_I7f06/lib/python3.6/site-packages/aiosmtplib/protocol.py", line 227, in _readline
    self._stream_reader.readuntil(separator=b'\n'), loop=self._loop)
AttributeError: 'NoneType' object has no attribute 'readuntil'

Python 3.6.5
aiosmtplib 1.0.2

Cheers,
Taslim42.

RuntimeError: This event loop is already running

Hey, I'm trying to run some code to send some emails, however, I am already using the event loop for another task. When I try running the line loop.run_until_complete(smtp.connect())
I get the following error " RuntimeError: This event loop is already running ".

InvalidStateError

I'm using the last revision (f5ce158) of the master branch and getting the following exception:

InvalidStateError: invalid state
  File "asyncio/events.py", line 88, in _run
    self._context.run(self._callback, *self._args)
  File "asyncio/selector_events.py", line 913, in _call_connection_lost
    super()._call_connection_lost(exc)
  File "asyncio/selector_events.py", line 687, in _call_connection_lost
    self._protocol.connection_lost(exc)
  File "aiosmtplib/protocol.py", line 79, in connection_lost
    self._connection_lost_waiter.set_result(None)

Not recognizing auth types for exchange server

I am unable to send emails from a Microsoft Exchange account hosted on Office365. My credentials are valid and I have succesfully tested sending emails with smtplib. For some reason SMTPAuth.server_auth_methods ends up being an empty list and then sending an email fails with

SMTP AUTH extension not supported by server.

You can see a minimal working example below (I have not tried to reproduce the error without an Office 365 account).

import asyncio
from email.message import EmailMessage

import aiosmtplib

PASSWORD = 'password'
USER = '[email protected]'

async def send_hello_world():
    message = EmailMessage()
    message["From"] = '[email protected]'
    message["To"] = "[email protected]"
    message["Subject"] = "Hello World!"
    message.set_content("Sent via aiosmtplib")

    send = await aiosmtplib.send(message,
                          hostname="smtp.office365.com",
                          port=587,
                          password=PASSWORD,
                          username=USER)
    return send

event_loop = asyncio.get_event_loop()
event_loop.run_until_complete(send_hello_world())

Installed docs

When aiosmtplib installed besides .../site-packages/aiosmtplib directory there is also .../site-packages/docs directory created with its docs.

Can this confusing behavior be changed?

aiosmtplib doesn't work, smtplib does

This script that sends using yahoo smtp works:

import smtplib
from email.mime.text import MIMEText
SMTP_SERVER = "smtp.mail.yahoo.com"
SMTP_PORT = 587
SMTP_USERNAME = "foo" # without @yahoo.com
SMTP_PASSWORD = "bar"
EMAIL_FROM = "[email protected]"
EMAIL_TO = "[email protected]"
EMAIL_SUBJECT = "hi from smtplib"
co_msg = """
Hello world!
"""
def send_email():
    msg = MIMEText(co_msg)
    msg['Subject'] = EMAIL_SUBJECT
    msg['From'] = EMAIL_FROM 
    msg['To'] = EMAIL_TO
    debuglevel = True
    mail = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
    mail.set_debuglevel(debuglevel)
    mail.starttls()
    mail.login(SMTP_USERNAME, SMTP_PASSWORD)
    mail.sendmail(EMAIL_FROM, EMAIL_TO, msg.as_string())
    mail.quit()

if __name__=='__main__':
    send_email()

But using aiosmtplib it always fails with:

  File "/home/thijs/.virtualenvs/foo/lib/python3.7/site-packages/aiosmtplib/api.py", line 397, in send
    message, sender=sender, recipients=recipients
  File "/home/thijs/.virtualenvs/foo/lib/python3.7/site-packages/aiosmtplib/smtp.py", line 299, in send_message
    timeout=timeout,
  File "/home/thijs/.virtualenvs/foo/lib/python3.7/site-packages/aiosmtplib/smtp.py", line 179, in sendmail
    raise exc
  File "/home/thijs/.virtualenvs/foo/lib/python3.7/site-packages/aiosmtplib/smtp.py", line 170, in sendmail
    response = await self.data(message, timeout=timeout)
  File "/home/thijs/.virtualenvs/foo/lib/python3.7/site-packages/aiosmtplib/esmtp.py", line 332, in data
    return await self.protocol.execute_data_command(message, timeout=timeout)
  File "/home/thijs/.virtualenvs/foo/lib/python3.7/site-packages/aiosmtplib/protocol.py", line 252, in execute_data_command
    raise SMTPDataError(response.code, response.message)
aiosmtplib.errors.SMTPDataError: (550, 'Request failed; Mailbox unavailable')
mail:
  host: smtp.mail.yahoo.com
  port: 587
  tls: True
  timeout: 300
  username: foo
  password: foo
  sender: [email protected]
  recipient: [email protected]
  validate_certs: False
await aiosmtplib.send(
            message=msg,
            hostname=mail_cfg.get('host'),
            port=mail_cfg.get('port'),
            username=mail_cfg.get('username'),
            password=mail_cfg.get('password'),
            start_tls=mail_cfg.get('tls'),
            timeout=mail_cfg.get('timeout'),
            validate_certs=mail_cfg.get('validate_certs')

Any idea? Is there any DEBUG flag I can enable, like mail.set_debuglevel(debuglevel) in example above?

When connecting to port 25 or 587 and using TLS, SMTP.connect() has to be plain text before issuing starttls.

When connecting to port 25 or 587 and using TLS, SMTP.connect() has to initiate a plaintext connection before issuing starttls.

Traceback (most recent call last):
  File "/git/dnd/lib64/python3.6/site-packages/aiosmtplib/connection.py", line 46, in create_connection
    connect_future, timeout=timeout, loop=loop)
  File "/usr/lib64/python3.6/asyncio/tasks.py", line 352, in wait_for
    return fut.result()
  File "/usr/lib64/python3.6/asyncio/base_events.py", line 802, in create_connection
    sock, protocol_factory, ssl, server_hostname)
  File "/usr/lib64/python3.6/asyncio/base_events.py", line 828, in _create_connection_transport
    yield from waiter
  File "/usr/lib64/python3.6/asyncio/sslproto.py", line 503, in data_received
    ssldata, appdata = self._sslpipe.feed_ssldata(data)
  File "/usr/lib64/python3.6/asyncio/sslproto.py", line 201, in feed_ssldata
    self._sslobj.do_handshake()
  File "/usr/lib64/python3.6/ssl.py", line 683, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [SSL: UNKNOWN_PROTOCOL] unknown protocol (_ssl.c:749)
async with aiosmtplib.SMTP(
        loop=cfg.APP.loop,
        hostname=cfg.SMTP_HOST,
        port=cfg.SMTP_PORT,
        use_tls=cfg.SMTP_TLS,
        ) as smtp:
    if cfg.SMTP_TLS:
        await smtp.starttls(validate_certs=False)
    if cfg.SMTP_USERNAME:
        await smtp.login(cfg.SMTP_USERNAME, cfg.SMTP_PASSWORD)
    msg = MIMEText(body, 'html')
    msg['Subject'] = subject
    msg['From'] = cfg.SMTP_SENDER
    msg['To'] = recipient
    await smtp.send_message(msg)

To be clear, cfg.SMTP_PORT was set to 587, and cfg.SMTP_TLS to True in this case.

Provide an API to stream messages, instead of loading them into memory

Today, the aiosmtplib only accepts email body as string or bytes. It means that the mail should be completly loaded in memory before begining to send it.

With asyncio, it is very easy to work with streams (generated on the fly, read from files, pipes, sockets, etc). Moreover, if we want to send more that a few emails, we should either send them sequentially or load all of them in memory. In either case, we loose one of the feature of async programmming.

It would be nice to support email bodies as something else than just static buffers (string or bytes). Have a look at the aiohttp multipart module (http://aiohttp.readthedocs.io/en/stable/multipart.html). It enables to build a MIME Multipart body by attaching string, bytes, but also HTTP Responses, streams, etc. The content is serialized on the fly when using the object, and can be streamed to a socket or the standard output.

It would be nice to allow a streamable object as a message body, allowing not to load the content completly into memory while sending it.

Unicode sender

I'm specifying "From" header at Message object but it's not enough, - aiosmtplib.errors.SMTPNotSupported: An address containing non-ASCII characters was provided, but SMTPUTF8 is not supported by this server is raised.

The code is as follows:

import asyncio
from email.header import Header
from email.message import EmailMessage

import aiosmtplib

async def main():
    message = EmailMessage()
    message['Subject'] = 'Subject'
    sender = Header('Pepé', 'utf-8').encode()
    message['From'] = sender
    message['To'] = ('[email protected]',)
    message.set_content('Body')
    # Working
    await aiosmtplib.send(message, sender, hostname='smtp.host')
    # Not working
    await aiosmtplib.send(message, hostname='smtp.host')

asyncio.run(main())

`send_message` raise `AttributeError`

send_message raise AttributeError

it works on 1.0.6, failing on 1.1.0

using official docker image python:3.7.4

Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/faust/agents/agent.py", line 632, in _execute_task
    await coro
  File "/usr/local/lib/python3.7/site-packages/faust/agents/agent.py", line 649, in _slurp
    async for value in it:
  File "/tmp/streams/email/__init__.py", line 120, in process_message
    await smtp.send_message(msg, recipients=message['recipients'])
  File "/usr/local/lib/python3.7/site-packages/aiosmtplib/smtp.py", line 291, in send_message
    flat_message = flatten_message(message, utf8=utf8_required, cte_type=cte_type)
  File "/usr/local/lib/python3.7/site-packages/aiosmtplib/email.py", line 84, in flatten_message
    generator.flatten(message_copy)
  File "/usr/local/lib/python3.7/email/generator.py", line 116, in flatten
    self._write(msg)
  File "/usr/local/lib/python3.7/email/generator.py", line 195, in _write
    self._write_headers(msg)
  File "/usr/local/lib/python3.7/email/generator.py", line 418, in _write_headers
    self._fp.write(self.policy.fold_binary(h, v))
  File "/usr/local/lib/python3.7/email/policy.py", line 200, in fold_binary
    folded = self._fold(name, value, refold_binary=self.cte_type=='7bit')
  File "/usr/local/lib/python3.7/email/policy.py", line 208, in _fold
    lines = value.splitlines()
AttributeError: 'Header' object has no attribute 'splitlines'

concurrency problem

I created a client

smtp = SMTP(...)

and the code of sending email

async def send_mails(smtp):
  async with smtp:
    await smtp.ehlo()
    await smtp.login()
    await smtp.send_message()

if it's called concurrently I will get errors:

aiosmtplib.errors.SMTPResponseException: (250, 'PIPELINING\nSTARTTLS\nAUTH=LOGIN\n8BITMIME')
aiosmtplib.errors.SMTPHeloError: (221, 'smtp.qq.com\nSIZE 73400320\nAUTH LOGIN PLAIN\nMAILCOMPRESS\nBye')
aiosmtplib.errors.SMTPResponseException: (-1, 'Malformed SMTP response: ')

Is there some form of locking implemented in a single instance?

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.