pythonflaskimportflask-mail

Not able to import mail in python/flask (ImportError)


I'm trying for days to get an email sending part configured in my Flask app. However I'm stuck in the first (import) part already.

Here's a simplified representation of my project:

└── my-project
    │
    ├── __init__.py
    ├── app.py
    │
    ├── api
    │   ├── __init__.py
    │   ├── list.py
    │   └── routes.py
    │
    └── mail
        ├── __init__.py
        └── email.py
  

All init.py files are empty. My list.py file does a sendemail() call at a certain moment.


<other imports>

from mail.email import sendemail

class FeedApi(Resource):
    
    def get(self, start: int, limit: int) -> Response:
                output = Lists.objects().aggregate(

<... query stuff ...>

                lists = json.loads(json_util.dumps(output))

                sendemail()

                return lists

This one comes from email.py, here is how the file looks:

from flask_mail import Message

def sendemail():
    print('\n\n trying to import this function ')

So far all good, the print gets printed.

However when I change the code to:

from flask_mail import Message

def sendemail():
    from app import mail
    print('\n\n trying to import this function ')

It gives me an error: ImportError: cannot import name 'mail' from 'app'

Tried all different things. If I don't include the import in the function it will give me a circular import error. Looked at this one for instance, but no idea how I can fix the issue: "ImportError: cannot import name mail" in Flask

Last but not least, here is my app.py file:

# flask packages
from flask import Flask, current_app, request, app
from flask_cors import CORS
from flask_restful import Api
from flask_mongoengine import MongoEngine
from flask_jwt_extended import JWTManager
from flask_cors import CORS
from flask_mail import Mail


# local packages
from api.routes import create_routes

# external packages
import os

# default mongodb configuration
default_config = {'MONGODB_SETTINGS': {
                    'db': '.... ',
                    'host': '....',
                    'port': 27017,
                    'username': '...',
                    'password': '...',
                    'authentication_source': '...'},
                    'JWT_SECRET_KEY': '...'}


def get_flask_app(config: dict = None) -> app.Flask:
    # init flask
    Flask.logger_name = "listlogger"
    flask_app = Flask(__name__)
    CORS(flask_app)
    

    # configure app
    flask_app.config.from_pyfile('config.py')

    config = default_config if config is None else config
    flask_app.config.update(config)

    # load config variables
    if 'MONGODB_URI' in os.environ:
        flask_app.config['MONGODB_SETTINGS'] = {'host': os.environ['MONGODB_URI'],
                                                'retryWrites': False}
    if 'JWT_SECRET_KEY' in os.environ:
        flask_app.config['JWT_SECRET_KEY'] = os.environ['JWT_SECRET_KEY']
    mail = Mail(app=flask_app)

    # init api and routes
    api = Api(app=flask_app)
    create_routes(api=api)


    # init mongoengine
    db = MongoEngine(app=flask_app)

    # init jwt manager
    jwt = JWTManager(app=flask_app)

    return flask_app

if __name__ == '__main__':
    # Main entry point when run in stand-alone mode.
    app = get_flask_app()
    app.run(debug=True, host='0.0.0.0', port=5050)

The issue is probably here. I hope you can help me to clean it up and help me forward. If there is a particular course I need to execute. Or anything else that would help here. Please let me know. Really looking forward to a solution! Thanks!


Solution

  • You can import api in app.py because it is at the same level in the hierarchy. But when you go deeper, like email.py, then python cannot know where to find app.

    You should look into using relative imports like:

    # in app.py
    from .api.routes import create_routes
    
    # in email.py
    from ..app import mail
    

    Then, mail only exists within get_flask_app, so you won't be able to import it even with relative imports.

    Finally, needing to import app in email.py means you won't be able to import email in app.py as it would cause a cyclic import.

    I suspect this is why you import inside the function and not at the top of the file.

    You should probably refactor that code to avoid importing from the app, like for instance passing an argument to sendemail rather than having it import from the app.