1

UPDATE:
The error (in this particular case) was not caused by circular import, but by flaw in virtualenv configuration. See my answer below, for elaboration.


I am using:

I am building a web app using Flask, among other things I need an ability to send mails to users. I have built a separate Python module, which will be responsible for mail handling. Though I've encountered a strange (as it seems to me, at least) import issue, after I added the email-processing module to my app.


Here is the (insulated) import issue I've encountered:

app.py

from flask import Flask
from test_mail import EmailTool

app = Flask(__name__)

@app.route('/')
def index():
    return 'Testing!'

test_mail.py

from email.message import EmailMessage

class EmailTool(object):
    pass

After launching my app and going to index (i.e. /) I am receiving:

Traceback (most recent call last):
  File "/app.py", line 2, in <module>
    from tmp_test_mail import EmailTool
  File "/test_mail.py", line 1, in <module>
    from email.message import EmailMessage
ImportError: cannot import name EmailMessage

I've changed code for test_mail.py, in order to make sure email module is accessible:

import email

class EmailTool(object):
    pass

This way I do not get an error.

Searching for possible causes&solutions led me to believe (1, 2, 3, 4, 5), it most likely has something to do with circular reference. Though even after reading through all the mentioned materials and insulating the problem cause, I still can not see how is it a circular reference. So I am concluding that either it is not circular and the cause lies in something else, or it is circular and I am missing something obvious here.


I am asking for help in understanding the following:

  1. Is the case presented above considered to be a circular reference? (If yes, in what way it is actually circular)?
  2. Why am I getting an error when I do from email.message import EmailMessage but don't get an error if I do import email instead?
  • 1
    Are you sure it is python 3.6? – hjpotter92 Jun 26 '18 at 09:58
  • 1
    no, the code above is not a circular reference – Ahmouse Jun 26 '18 at 10:24
  • unless, of course, the `email.message` module imports from `app`, then it would be circular reference. If you could provide code from the `email.message` module that would help. – Ahmouse Jun 26 '18 at 10:26
  • @Skilledfire, it's a part of standard library. Though, I have found the cause of the issue — see my answer below (which, by the way, contains links to the `email.message` source code =) –  Jun 27 '18 at 10:49
  • @hjpotter92, indeed it was the cause. Turned out my app was ran by Python 2.7.10 (see my answer below for details). Thanks for your suggestion! –  Jun 27 '18 at 12:06

2 Answers2

1

To answer your both of your questions:

First

No, the code provided above shouldn't be circular, unless email.message contains references to the app module, assuming app is a valid module.

Second

Importing from email instead of email.message doesn't cause any errors because you are not importing the (seemingly) problematic EmailMessage class since it resides in email.message, not email. My theory is that it's caused by some import looping back to the app module located in the email.message module.

Note:

At the time of writing I was not aware that email or email.message were part of the standard (3.6.x) library, as I have never used anything related to those modules (and I don't use python 3.x< very often) and thus I assumed this was caused by circular reference as the author suggested. As it turns out (and pointed out in the original post) this was caused by something else entirely.

Community
  • 1
  • 1
Ahmouse
  • 174
  • 1
  • 9
1

TL;DR: the actual issue had nothing to do with circular dependency — it turned out to be misconfiguration of my virtual environment (Python version was actually 2.7.10, as was suggested in the comment by hjpotter92).


How I found out it was the cause (posting as a collection of recipes for debugging your Python venv, which might later be handy for myself, and hopefully someone else):

  1. Right after reading the comments to my quertion (hjpotter92's comment, in particular) I've rushed to check the version of Python inside the virtualenv I run my app from (even though I've checked it right before posting the question — you can't be too careful in this kind of things =).

    Running (inside the virtual environment):

    python --version
    

    gave me (just as I have expected):

    Python 3.6.1
    
  2. I wasn't convinced, though =). As was suggested by the this answers: 1 and 2, I have added the following to code to both modules: app.py and test_mail.py (in order to check which version of Python they are actually ran by):

    import sys
    ...
    print(sys.version)
    

    it was outputting (surprisingly for me):

    2.7.10
    
  3. Ok, clearly something is wrong here. I have decided to refresh my basic knowledge of virtualenv setup. The first article I stumbled upon, suggested pip --version as a second step of the setup process (after python --version). Having nothing to loose I ran it (inside the virtual environment, of course), and (to my amazement) it gave me:

    pip 9.0.1 from /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages (python 2.7)
    

    So, somehow pip used inside the virtual environment was a system level one...

  4. At this point I was in doubt that my app was running from virtualenv at all. Following the recipe from this answer (and comment to it), I have composed and added to app.py and test_mail.py, the following snippet:

    import sys
    
    ...
    
    if hasattr(sys, 'real_prefix'):
        print('Python 2 venv')
    elif (hasattr(sys, 'base_prefix') and sys.prefix != sys.base_prefix):
        print('Python 3 venv')
    else:
        print('Not venv!')
    

    not much a surprise (at this point) it was printing Not venv!.

  5. To debug what exactly is wrong with current virtual environment seemed to time-costly. So, what I ended up doing:
    • removing Flask and related packages (itsdangerous, Jinja2, MarkupSafe, Werkzeug) from system level (which were installed there, for some reason) via pip uninstall <package_name>.
    • recreating virtual environment with python3 -m venv <env_name>

Now, to answer my own questions:

  1. Code in my question does not contain a circular reference.
  2. The error after trying to do from email.message import EmailMessage, occurred because Python 2.7.10 was actually used, and if we'll look into source code of message.py for this version of Python, we will see that it simply does not contain a class named EmailMessage. While the source code of message.py for Python 3.6.* library, does contain class named EmailMessage.
  • 1
    `email.message.EmailMessage` is new to python 3.6+, as can be seen from docs: https://docs.python.org/3/library/email.message.html which mentions: "_New in version 3.6_". – hjpotter92 Jun 27 '18 at 15:22
  • sorry for my answer, I never even new `email.message` was a built-in module. – Ahmouse Jun 27 '18 at 21:29
  • @Skilledfire, nothing to be sorry about! We all came here to learn ;-) –  Jun 30 '18 at 08:42