运行导入的Flask应用时出现ModuleNotFoundError

时间:2018-08-23 14:53:59

标签: python-3.x flask module

我有一个具有以下布局的python模块:

 foo
  |  __init__.py
  |  __main__.py
  |  bar.py

__init__.py为空。

foo/bar.py的内容:

from flask import Flask
app = Flask(__name__)
def baz(): pass

运行python3 -m foo时,结果令人困惑。

foo/__main__.py

的内容
# Results in a ModuleNotFoundError: No module named 'foo'
from foo.bar import app
app.run()

# Raises no error and correctly prints the type
from foo.bar import app
print(type(app))

# Also runs without an error
from foo.bar import baz
baz()

为什么可以从此模块导入并执行功能,但是当尝试使用flask应用程序执行此操作时,结果为ModuleNotFoundError? 我只是看不到有任何意义。

编辑:

即使使用以下代码,该错误仍然存​​在:

from foo.bar import app

print(type(app))
app.run()

输出:

<class 'flask.app.Flask'>
 * Serving Flask app "foo.bar" (lazy loading)
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
Traceback (most recent call last):
  File "/home/user/projects/ftest/foo/__main__.py", line 1, in <module>
    from foo.bar import app
ModuleNotFoundError: No module named 'foo'

因此,显然可以导入模块 ,因为type(app)可以正常工作,并且烧瓶可以启动。好像flask会重新加载,并且以某种方式弄乱了导入。

编辑2:

我关闭了调试模式,并且工作正常。 仅当您设置export FLASK_DEBUG=True或通过app.config["DEBUG"] = True

显式启用调试时,才会发生此错误。

3 个答案:

答案 0 :(得分:1)

事实证明这是werkzeug中的错误。 如果werkzeug的重新加载器被禁用,代码将按预期工作。

如何重现行为

目录结构:

 foo
  |  __init__.py
  |  __main__.py

__init__.py的内容:

from flask import Flask
app = Flask(__name__)
app.config["DEBUG"] = True

__main__.py的内容:

from foo import app
app.run()

如果我们运行它:

$python3 -m foo
 * Serving Flask app "foo" (lazy loading)
 * Environment: development
 * Debug mode: on
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
Traceback (most recent call last):
  File "/home/user/projects/ftest/foo/__main__.py", line 1, in <module>
    from foo import app
ModuleNotFoundError: No module named 'foo'

如果我们更改__main__.py

from foo import app
app.run(use_reloader=False)

一切正常。

发生了什么事

问题出在werkzeug._reloader.ReloaderLoop.restart_with_reloader中。它使用werkzeug._reloader._get_args_for_reloading提供的参数调用子进程,但是通过-m开关执行程序包时,此函数的行为不符合预期。

def _get_args_for_reloading():
    """Returns the executable. This contains a workaround for windows
    if the executable is incorrectly reported to not have the .exe
    extension which can cause bugs on reloading.
    """
    rv = [sys.executable]
    py_script = sys.argv[0]
    if os.name == 'nt' and not os.path.exists(py_script) and \
       os.path.exists(py_script + '.exe'):
        py_script += '.exe'
    if os.path.splitext(rv[0])[1] == '.exe' and os.path.splitext(py_script)[1] == '.exe':
        rv.pop(0)
    rv.append(py_script)
    rv.extend(sys.argv[1:])
    return rv

在我们的例子中,它返回['/usr/local/bin/python3.7', '/home/user/projects/ftest/foo/__main__.py']。这是因为sys.argv[0]被设置为模块文件的完整路径,但是它应该返回['/usr/local/bin/python3.7','-m','foo']`(至少从我的理解应该如此,并且它会以这种方式起作用。)

我不知道如何解决此行为,或者是否需要解决此问题。对我来说,我是唯一遇到此问题的人,这对我来说似乎很奇怪,因为对我而言,这似乎并不过分。

答案 1 :(得分:0)

您是否尝试过在main.py文件中的foo导入应用程序中尝试使用

答案 2 :(得分:0)

在app.run()解决werkzeug重新加载器错误之前添加以下行:

os.environ['PYTHONPATH'] = os.getcwd()

感谢@bootc的提示! https://github.com/pallets/flask/issues/1246