Sorry if the question isn't spot on, I'm having difficulty phrasing this question properly.
I'm trying to use the email module to create a simple plain text email and send it when certain conditions are met. I'm running in to all sorts of abnormal behavior and would like to understand it. I started with a simple test pulled from the official examples (https://docs.python.org/3.4/library/email-examples.html) and it worked fine. When I started trying to implement this in my project I started getting all sorts of "'module' object has no attribute 'something'"
. I can run something like this and it works fine
import email
import smtplib
# Create message object
msg = email.message.EmailMessage()
# Create the from and to Addresses
from_address = email.headerregistry.Address("Stack of Pancakes", "pancakes@gmail.com")
to_address = email.headerregistry.Address("Joe", "pythontest@mailinator.com")
# Create email headers
msg['Subject'] = "subject of email"
msg['From'] = from_address
msg['To'] = to_address
#
email_content = "This is a plain text email"
msg.set_content(email_content)
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login("pancakes@gmail.com", "password")
server.send_message(msg)
server.quit()
This works perfectly. However if I order things differently things start breaking and I don't understand why. For example if I place the from_address
and to_address
lines above where the EmailMessage is called like so
import email
import smtplib
# Create the from and to Addresses
from_address = email.headerregistry.Address("Stack of Pancakes", "pancakes@gmail.com")
to_address = email.headerregistry.Address("Joe", "pythontest@mailinator.com")
# Create message object
msg = email.message.EmailMessage()
... other code
It fails with 'module' object has no attribute 'headerregistry'
. Why does the EmailMessage creation allow the other code to function properly?
In fact if I have a file that contains only this
import email
to_address = email.headerregistry.Address("joe", "joe@joe.com")
it fails with the same error.
To get that small snippet to run I had to do this
from email.headerregistry import Address
to_address = Address("joe", "joe@joe.com")
Alternatively, and this is really weird, I can get this to run
import email
import smtplib
email.message.EmailMessage()
to_address = email.headerregistry.Address("joe", "joe@joe.com")
but if I remove the import smtplib
it starts failing again, even though I haven't used anything in smtplib in those 4 lines.
I'm fairly certain I can just keep trying every combination I can think of and get it to work properly, but I'd prefer to understand the behavior. That way I would feel more confident running the code in a production environment.
Why can't I just call import email
and declare my objects with email.headderregistry.Address
, why do I have to explicitly import that specific function with from email.headerregistry import Address
? Why did it compile with import smtplib
but failed without it. Why does it work only after EmailMessage()
is called?
Normally I'm really good about finding answers, but I think in this situation I just don't know what to search for. There are a whole bunch of solutions to the "module object has no attribute", but most of them were duplicate named files, circular imports, calling functions that didn't exist or checking if an attribute exists. None of them seemed to address how import behavior worked. Am I structuring the code wrong or is the email module just acting up on me?
import email
will not automatically import all the modules inside the email
package. This means that, in order to use email.headerregistry
, you must import it, which can be as simple as:
import email.headerregistry
After that you'll be able to use email.headerregistry.Address
.
Your code also works after writing from email.headerregistry import Address
because that statement internally does the (equivalent of) import email.headerregistry
in order to load the module and get hold of Address
. Likewise, smtplib
imports email
-related modules, some of which likely imports email.headerregistry
.
To summarize: once any module performs the import of email.headerregistry
, that submodule becomes visible to all modules that imported email
, even if they never explicitly requested email.headerregistry
. The fact that importing a module can make submodules of an unrelated package available as a side effect can lead to nasty bugs where a module works only if imported after some other modules. Fortunately, modern tools like pylint and Eclipse's pydev are good at fixing this kind of pitfall.