如何将这种导入视为循环导入(或为什么收到“ ImportError:无法导入名称EmailMessage”错误)

时间:2018-06-26 09:44:51

标签: python python-import circular-dependency circular-reference

更新
该错误(在这种情况下)不是由循环导入引起的,而是由virtualenv配置中的缺陷引起的。有关详细说明,请参见下面的my answer


我正在使用:

我正在使用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

这样我就不会出错。

寻找可能的原因和解决方案使我相信(12345),这很可能与事情有关与循环参考。尽管即使通读了所有上述材料并找出了问题的根源,但我仍然看不到它如何成为圆形参考。因此,我得出结论,这要么不是循环的,而是其他原因造成的,要么是循环的,而我在这里遗漏了一些明显的东西。


我正在寻求帮助以了解以下内容:

  1. 上述案例是否被视为循环引用? (如果是,则实际上是圆形)?
  2. 为什么我做from email.message import EmailMessage时会出错,但是如果我做import email却不出错?

2 个答案:

答案 0 :(得分:1)

要回答两个问题:

第一

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

第二

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

注意:

在撰写本文时,我还不知道emailemail.message是标准(3.6.x)库的一部分,因为我从未使用过与这些模块相关的任何东西(而我没有我不常使用python 3.x <),因此我认为这是由作者建议的循环引用引起的。事实证明(并在original post中指出),这是由something else entirely引起的。

答案 1 :(得分:0)

TL; DR :实际的问题与循环依赖无关-事实证明是我的虚拟环境配置错误( Python版本实际上是2.7.10 ,如hjpotter92的评论所建议)。


我是如何找到原因的(作为调试Python venv的食谱集发布,以后可能对我自己以及希望其他人都方便):

  1. 在阅读了我的问题的注释(特别是hjpotter92的注释)之后,我急着检查了virtualenv中我从(即使我在发布问题之前已经检查过它-在这种事情上也不能太小心=)。

    (在虚拟环境中)运行:

    python --version
    

    给我(正如我所期望的那样):

    Python 3.6.1
    
  2. 虽然=),但我还是不服气。如以下答案所建议:12,我已经在两个模块中添加了以下代码: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. 在这一点上,我怀疑我的应用程序是完全从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. 调试当前虚拟环境到底存在什么问题似乎很耗时。因此,我最终要做的是:
    • 从系统级别删除Flask和相关软件包(itsdangerousJinja2MarkupSafeWerkzeug)(出于某种原因安装在此处)通过pip uninstall <package_name>
    • 使用python3 -m venv <env_name>重新创建虚拟环境

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

  1. 问题中的代码不包含循环引用。
  2. 尝试执行from email.message import EmailMessage后发生错误,是因为实际上使用了python 2.7.10,如果我们研究此版本的Python的source code of message.py,我们会发现它确实可以不包含名为EmailMessage的类。 source code of message.py for Python 3.6.* library确实包含名为EmailMessage的类。