Python 这种导入如何被认为是循环的(或者为什么我会收到“ImportError:cannotimportname EmailMessage”错误)

Python 这种导入如何被认为是循环的(或者为什么我会收到“ImportError:cannotimportname EmailMessage”错误),python,python-import,circular-dependency,circular-reference,Python,Python Import,Circular Dependency,Circular Reference,更新: 错误(在这种特殊情况下)不是由循环导入引起的,而是由配置中的缺陷引起的。详见下文 我正在使用: 0.12.2 3.6.1 我正在使用Flask构建一个web应用程序,除此之外,我还需要能够向用户发送邮件。我已经构建了一个单独的Python模块,它将负责邮件处理。虽然在我将电子邮件处理模块添加到我的应用程序后,我遇到了一个奇怪的问题(至少在我看来是这样)import 以下是我遇到的(隔离)导入问题: app.py from flask import Flask from test_

更新
错误(在这种特殊情况下)不是由循环导入引起的,而是由配置中的缺陷引起的。详见下文


我正在使用:

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


以下是我遇到的(隔离)
导入问题:

app.py

from flask import Flask
from test_mail import EmailTool

app = Flask(__name__)

@app.route('/')
def index():
    return 'Testing!'
from email.message import EmailMessage

class EmailTool(object):
    pass
测试邮件.py

from flask import Flask
from test_mail import EmailTool

app = Flask(__name__)

@app.route('/')
def index():
    return 'Testing!'
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
这样我就不会出错

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


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

  • 上述案例是否被视为循环参考?(如果是,它实际上是以什么方式循环的
  • 为什么当我从email.message导入EmailMessage
  • 中执行
    操作时会出现错误,但如果我执行
    导入email
    操作,则不会出现错误
    要回答这两个问题,请执行以下操作:

    首先

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

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

    注:
    在撰写本文时,我并不知道
    email
    email.message
    是标准(3.6.x)库的一部分,因为我从未使用过与这些模块相关的任何东西(而且我也不经常使用python 3.x),因此我认为这是由作者建议的循环引用引起的。事实证明(并在报告中指出),这是由TL引起的;DR:实际问题与循环依赖性无关-结果是我的虚拟环境配置错误(Python版本实际上是2.7.10,正如作者在评论中所建议的)


    我是如何找到原因的(作为调试Python
    venv
    的方法集发布的,这可能对我自己和其他人都很方便):

  • 在阅读了对我的查询的评论(“特别是我的评论”)之后,我立即检查了我运行我的应用程序时使用的Python版本(尽管我在发布问题之前就检查了它-在这类事情上,你再小心也不为过=)。

    正在运行(在虚拟环境中):

    给了我(正如我所预料的):

  • 不过,我不相信。正如this answers所建议的那样:并且,我已经在这两个模块的代码中添加了以下内容:
    app.py
    test\u mail.py
    (为了检查它们实际运行的Python版本):

    它的输出(让我惊讶):

  • 好的,很明显这里出了点问题。我决定更新我的
    virtualenv
    设置的基本知识。我偶然发现的,建议将
    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)
    
    所以,在虚拟环境中使用的某种方式是系统级的

  • 在这一点上,我怀疑我的应用程序是从
    virtualenv
    运行的。按照(和注释)中的配方,我编写并添加了以下代码片段到
    app.py
    test\u 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!')
    
    (此时)它正在打印
    而不是venv,这并不奇怪

  • 调试当前虚拟环境到底出了什么问题似乎花费了大量的时间。所以,我最后做的是:
    • 通过
      pip卸载
      从系统级(出于某种原因安装在那里)移除
      Flask
      和相关软件包(
      itsDanger
      Jinja2
      MarkupSafe
      Werkzeug
    • 使用python3-m venv重新创建虚拟环境

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

  • 我问题中的代码不包含循环引用
  • 尝试从email.message导入EmailMessage
  • 执行
    后出现错误,因为实际使用的是Python 2.7.10,如果我们查看此版本的Python,就会发现它根本不包含名为
    EmailMessage
    的类。而,不包含名为
    EmailMessage
    的类
    您确定它是python 3.6吗?不,上面的代码不是循环引用,当然,除非
    email.message
    模块从
    app
    导入,否则它将是循环引用
    2.7.10
    
    pip 9.0.1 from /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages (python 2.7)
    
    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!')