0

There is a blank NatMailer/__init__.py.

Here's: NatMailer/NatMailer.py

# python -m smtpd -n -c DebuggingServer localhost:1025
class NatMailer:
    def __init__(self, smtp_server="localhost", port=1025, sender_email="[email protected]", debug=0):
        import logging

        logging.basicConfig(filename='example.log', level=logging.DEBUG)
        logging.info("Initiating NatMailer")

        import smtplib, ssl
        import json
        import csv
        import sqlite3

        sql = sqlite3.connect('example.db')
        self.debug = debug
        if (debug):
            self.smtp_server = "localhost"
            self.port = 1025
            self.sender_email = "[email protected]"
        else:
            self.smtp_server = smtp_server
            self.port = port
            self.sender_email = sender_email
    def send_email(self, receiver_email, message_contents):
        # Create a secure SSL context
        context = ssl.create_default_context()
        logging.info("Sending new email")

        # Try to log in to server and send email
        try:
            server = smtplib.SMTP(self.smtp_server,self.port)
            server.ehlo() # Can be omitted
            if (not self.debug):
                logging.info("Logging into " + self.sender_email)
                server.starttls(context=context) # Secure the connection
                server.ehlo() # Can be omitted
                server.login(self.sender_email, self.password)
            logging.info("Sending email to " + receiver_email)
            server.sendmail(self.sender_email, receiver_email, message_contents)
        except Exception as e:
            # Print any error messages to stdout
            logging.debug(e)
        finally:
            server.quit()

Then there is a debug_driver.py outside of NatMailer/.

import NatMailer
debug = 1
nm = NatMailer.NatMailer(debug=debug)
message = """\
            Subject: Hi there

            This message is sent from Python."""
nm.send_email('[email protected]', message)

I get this error:

Traceback (most recent call last):
  File "C:/Users/pat/PycharmProjects/NatMailer/debug_driver.py", line 3, in <module>
    nm = NatMailer.NatMailer(debug=debug)
AttributeError: module 'NatMailer' has no attribute 'NatMailer'

Process finished with exit code 1

What am I doing wrong? I want to be able to import a custom class into my debug_driver.py script.

1
  • 1
    To help you keep track of which thing is which, follow the Python naming guidelines and use lowercase names for modules. Then, at least, you would have a module natmailer.natmailer that contains a class NatMailer. Also, consider whether you really need a module with the same name as its containing package; you can put the class NatMailer directly in natmailer/__init__.py to do away with the intermediate module. Commented May 1, 2019 at 15:34

1 Answer 1

2

There are three levels involved here: directory (package), filename (module), and class. NatMailer refers to the package, NatMailer.NatMailer refers to the module, and NatMailer.NatMailer.NatMailer refers to the class.

So you would need something like

# import module from package
import NatMailer.NatMailer  

debug = 1
nm = NatMailer.NatMailer.NatMailer(debug=debug)

Brief explanation of the error message:

AttributeError: module 'NatMailer' has no attribute 'NatMailer'

You import just the package (or module, as stated here):

import NatMailer

That basically only loads the __init__.py file, which is empty. Hence, when you try to access anything of that module, Python will complain, because there is nothing there:

NatMailer.NatMailer

The attribute doesn't exist: it's not the (sub)module, because that isn't imported in __init__.py, nor is it the class, since that wasn't imported in __init__.py either. It's basically a near-empty import, and you'd have to explicitly import NatMailer.NatMailer. But see above and below.


Alternatives:

1/

# import module from package
from NatMailer import NatMailer  

debug = 1
nm = NatMailer.NatMailer(debug=debug)

2/

# import class from the module directly
from NatMailer.NatMailer import NatMailer  

debug = 1
nm = NatMailer(debug=debug)

3/ Slight more involved perhaps, but quite often used:

Put this in your package __init__.py:

from .NatMailer import NatMailer

and then use

# import the class from package
# note: now you can't distinguish the class from the module.
# see the remark at the bottom about naming conventions
from NatMailer import NatMailer  

debug = 1
nm = NatMailer(debug=debug)

Since the NatMailer class can now be found at package level, not just at module level.


Note: packages and modules are usually not CamelCased. That would make things slightly more insightful: natmailer.natmailer.NatMailer would be your class.

Sign up to request clarification or add additional context in comments.

2 Comments

Alternatively, in the __init__.py file do from NatMailer import NataMailer, then the original import would work.
ah ha, I'll accept this answer soon. Very insightful. I'll fix the CamelCase as well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.