Skip to content

python 3.5/3.8 compatibilty fix: need to pass host and port at init time to the SMTP session object #92

Closed
digiturtle wants to merge 2 commits into
marrow:developfrom
digiturtle:develop
Closed

python 3.5/3.8 compatibilty fix: need to pass host and port at init time to the SMTP session object #92
digiturtle wants to merge 2 commits into
marrow:developfrom
digiturtle:develop

Conversation

@digiturtle

Copy link
Copy Markdown

The new version of smtplib requires an hostname at init time. Also if the mail provider uses a non-standard port (as an example, iCloud uses port 587) and the port is not passed at init time, marrow.mailer will fail with a "connection refused" error (errno 61)

@amcgregor amcgregor left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm unsure of the actual need for this change. Could evidence in the form of the traceback you are encountering (an MCVE would be even better!) and/or documentation as to the nature of the required change be provided? Additionally, there appears to be unnecessary duplication/branches that can be removed, and there is a subtle negative behavior difference (impacting diagnostic logging capability) as a result of this change. Thank you.

Comment on lines -71 to +78
connection = SMTP_SSL(local_hostname=self.local_hostname, keyfile=self.keyfile,
certfile=self.certfile, timeout=self.timeout)
if PY35_OR_LATER:
connection = SMTP_SSL(local_hostname=self.local_hostname, keyfile=self.keyfile,
certfile=self.certfile, timeout=self.timeout)
else:
connection = SMTP_SSL(local_hostname=self.local_hostname, keyfile=self.keyfile,
certfile=self.certfile, timeout=self.timeout)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is there a conditional branch here? The invocations are identical.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're right there is an error in the second commit, sorry about that, the second commit was added because the travis continuous integration "tricked" me in thinking the first one was breaking python 2 and python < 3.5, but then looking in detail I saw that what seems broken is the continuous integration for some versions of Python. So you should really only pull the first commit. Would you prefer I close this pull request and submit another one?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need; an additional commit is no worries. Squash merges are nice. 😉

Comment on lines -74 to +83
connection = SMTP(local_hostname=self.local_hostname, timeout=self.timeout)
if PY35_OR_LATER:
connection = SMTP(self.host, port=self.port, local_hostname=self.local_hostname, timeout=self.timeout)
else:
connection = SMTP(local_hostname=self.local_hostname, timeout=self.timeout)

@amcgregor amcgregor Sep 26, 2020

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

smtplib has supported passing in the host and port via the SMTP and SMTP_SSL classes as far back as currently available documentation will go, so there should be no need for this behavior to be conditional between versions, at all, unless there is some other deficit I'm not aware of. I would recommend, for some limited consistency, passing host and port positionally. The other arguments deserve more "explanation" (context).

If passed in here, the subsequent connect() call does not need to happen. The logic within SMTP's constructor states that if a host is provided, a connection should be immediately attempted. The reason this was not utilized previously is that it makes it impossible to set the debug level prior to the connection attempt.

@digiturtle digiturtle Sep 26, 2020

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right again, I have only now noticed that I was using a version that had been patched (probably because of #83) and was already providing a host to the SMTP constructor... in this case it is evident that also a port should be provided. So in the end really this pull request is about issue #83. I have verified that if I remove the patch marrow.mailer doesn't fail with non-standard ports (using python 2.7)

@digiturtle digiturtle Sep 26, 2020

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the traceback with the exception when using python 3.8 and marrow.mailer without the patch:

connect: ('smtp.mail.me.com', 587)
connect: to ('smtp.mail.me.com', 587) None
reply: b'220 iCloud SMTP - pv50p00im-tydg10011801.me.com 3.4.7 (2007B172-3902c1cbb426)\r\n'
reply: retcode (220); Msg: b'iCloud SMTP - pv50p00im-tydg10011801.me.com 3.4.7 (2007B172-3902c1cbb426)'
connect: b'iCloud SMTP - pv50p00im-tydg10011801.me.com 3.4.7 (2007B172-3902c1cbb426)'
send: 'ehlo ftoso-mbp.local\r\n'
reply: b'250-pv50p00im-tydg10011801.me.com\r\n'
reply: b'250-PIPELINING\r\n'
reply: b'250-SIZE 28311552\r\n'
reply: b'250-ETRN\r\n'
reply: b'250-STARTTLS\r\n'
reply: b'250-ENHANCEDSTATUSCODES\r\n'
reply: b'250-8BITMIME\r\n'
reply: b'250-DSN\r\n'
reply: b'250 CHUNKING\r\n'
reply: retcode (250); Msg: b'pv50p00im-tydg10011801.me.com\nPIPELINING\nSIZE 28311552\nETRN\nSTARTTLS\nENHANCEDSTATUSCODES\n8BITMIME\nDSN\nCHUNKING'
send: 'STARTTLS\r\n'
reply: b'220 2.0.0 Ready to start TLS\r\n'
reply: retcode (220); Msg: b'2.0.0 Ready to start TLS'
Delivery of message <160115213980.50096.5092007329413341917@ftoso-mbp.local> failed.
Traceback (most recent call last):
  File "/Users/ftoso/develop/konga/third_party/generated/python38/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/marrow.mailer-4.0.3-py3.8.egg/marrow/mailer/manager/util.py", line 50, in __enter__
    transport = pool.transports.get(False)
  File "/Users/ftoso/develop/konga/third_party/generated/python38/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/queue.py", line 167, in get
    raise Empty
_queue.Empty

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "send_email_message_mm.py", line 138, in <module>
    mailer.send(message)
  File "/Users/ftoso/develop/konga/third_party/generated/python38/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/marrow.mailer-4.0.3-py3.8.egg/marrow/mailer/__init__.py", line 149, in send
    result = self.manager.deliver(message)
  File "/Users/ftoso/develop/konga/third_party/generated/python38/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/marrow.mailer-4.0.3-py3.8.egg/marrow/mailer/manager/immediate.py", line 41, in deliver
    with self.transport() as transport:
  File "/Users/ftoso/develop/konga/third_party/generated/python38/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/marrow.mailer-4.0.3-py3.8.egg/marrow/mailer/manager/util.py", line 57, in __enter__
    transport.startup()
  File "/Users/ftoso/develop/konga/third_party/generated/python38/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/marrow.mailer-4.0.3-py3.8.egg/marrow/mailer/transport/smtp.py", line 50, in startup
    self.connect_to_server()
  File "/Users/ftoso/develop/konga/third_party/generated/python38/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/marrow.mailer-4.0.3-py3.8.egg/marrow/mailer/transport/smtp.py", line 84, in connect_to_server
    connection.starttls(self.keyfile, self.certfile)
  File "/Users/ftoso/develop/konga/third_party/generated/python38/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/smtplib.py", line 774, in starttls
    self.sock = context.wrap_socket(self.sock,
  File "/Users/ftoso/develop/konga/third_party/generated/python38/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/ssl.py", line 500, in wrap_socket
    return self.sslsocket_class._create(
  File "/Users/ftoso/develop/konga/third_party/generated/python38/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/ssl.py", line 1031, in _create
    self._sslobj = self._context._wrap_socket(
ValueError: server_hostname cannot be an empty string or start with a leading dot.


log = __import__('logging').getLogger(__name__)

PY35_OR_LATER = (sys.version_info >= (3,5))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@amcgregor amcgregor added meta:blocked This issue is blocked pending an external factor. need:example A minimal, or even better, concrete example needs to be provided. transport:smtp and removed meta:blocked This issue is blocked pending an external factor. need:example A minimal, or even better, concrete example needs to be provided. labels Sep 26, 2020
@amcgregor

Copy link
Copy Markdown
Member

As per #91 (comment) this is a regression in Python standard library behaviour and should be reported/corrected upstream.

@amcgregor amcgregor closed this Sep 28, 2020
@amcgregor amcgregor linked an issue Oct 1, 2020 that may be closed by this pull request
@amcgregor amcgregor added the meta:rejected This does not seem right and/or will not be worked on. label Oct 1, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

meta:rejected This does not seem right and/or will not be worked on. transport:smtp

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SMTP regression and failure during TLS negotiation under Python 3.7.

3 participants