How is this kind of import considered circular (or why am I getting "ImportError: cannot import name EmailMessage" error)

How is this kind of import considered circular (or why am I getting "ImportError: cannot import name EmailMessage" error)

更新
错误(在这种特殊情况下)不是由循环导入引起的,而是由下面 virtualenv configuration. See 中的缺陷引起的,以供详细说明。


我正在使用:

我正在使用 Flask 构建一个 Web 应用程序,除其他外,我需要能够向用户发送邮件。我已经建立了一个单独的 Python 模块,它将负责邮件处理。虽然我遇到了一个奇怪的(至少在我看来)import 问题,但在我将电子邮件处理模块添加到我的应用程序之后。


这是我遇到的(绝缘)import问题:

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

启动我的应用程序并进行索引(即 /)后,我收到:

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

我已经更改了 test_mail.py 的代码,以确保可以访问 email 模块:

import email

class EmailTool(object):
    pass

这样我就不会报错了。

寻找可能的原因和解决方案使我相信 (1, 2, 3, 4, 5),它很可能与循环引用有关。尽管阅读了所有提到的材料并隔离了问题原因,但我仍然看不出它是如何成为 circular 参考的。所以我得出结论,要么它不是循环的,原因在于其他东西,要么它是循环的,我在这里遗漏了一些明显的东西。


我请求帮助以理解以下内容:

  1. 以上案例是否属于循环引用? (如果是,它实际上是循环)是什么?
  2. 为什么我在执行 from email.message import EmailMessage 时会收到错误消息,但如果我执行 import email 却不会收到错误消息?

回答你的两个问题:

第一

不,上面提供的代码不应该是循环的,除非email.message包含对app模块的引用,假设app是一个有效的模块。

第二

email 而不是 email.message 导入不会导致任何错误,因为您没有导入(看似)有问题的 EmailMessage class 因为它位于 email.message,而不是 email。我的理论是,它是由一些导入循环回到 email.message 模块中的 app 模块引起的。

注:

在撰写本文时,我并不知道 emailemail.message 是标准 (3.6.x) 库的一部分,因为我从未使用过任何与这些模块(而且我不经常使用 python 3.x<),因此我认为这是由作者建议的 循环引用 引起的。事实证明(并在 ) this was caused by .

中指出

TL;DR: 实际问题与循环依赖无关——结果是我的虚拟环境配置错误 (Python 版本实际上是 2.7.10,正如 hjpotter92) 在评论中所建议的那样。


我是如何发现它的原因的(作为调试您的 Python venv 的食谱集发布,以后可能对我自己以及希望对其他人有用):

  1. 在阅读了对我的问题的评论(特别是hjpotter92的评论)后,我赶紧检查了 [=77] 中 Python 的版本=]virtualenv 我 运行 我的应用程序来自(即使我在发布问题之前已经检查过它 - 你在这种事情上不能太小心 =)。

    运行(在虚拟环境中):

    python --version
    

    给了我(正如我所料):

    Python 3.6.1
    
  2. 虽然我不相信 =)。正如这个答案所建议的:1 and 2,我在两个模块的代码中添加了以下内容:app.pytest_mail.py(为了检查 Python 的哪个版本实际上是运行:

    import sys
    ...
    print(sys.version)
    

    它正在输出(令我感到惊讶):

    2.7.10
    
  3. 好吧,显然这里有问题。我决定复习一下 virtualenv 设置的基础知识。我偶然发现的 first article 建议将 pip --version 作为设置过程的第二步(在 python --version 之后)。没有什么可以松动的,我 运行 它(当然是在虚拟环境中),并且(令我惊讶的是)它给了我:

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

    所以,不知何故 pip 在虚拟环境中使用的是系统级别的...

  4. 此时我怀疑我的应用程序是否 运行ning 来自 virtualenv。根据 this answer 的食谱( 和评论 ),我编写并添加到 app.pytest_mail.py,以下片段:

    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 venv!

  5. 不足为奇(此时)
  6. 调试当前虚拟环境到底出了什么问题似乎很费时间。所以,我最终做了什么:
    • 从系统级别(安装在那里,对于一些原因)通过 pip uninstall <package_name>.
    • 正在使用 python3 -m venv <env_name>
    • 重新创建虚拟环境

现在,回答我自己的问题:

  1. 我问题中的代码不包含循环引用。
  2. 尝试执行 from email.message import EmailMessage 后出现错误,因为实际使用了 Python 2.7.10,如果我们查看 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,确实包含 class 命名为 EmailMessage.