0x1F602
0x1F602

Reputation: 865

Can't import class from custom python module

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.

Upvotes: 0

Views: 1490

Answers (1)

9769953
9769953

Reputation: 12261

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.

Upvotes: 2

Related Questions