我有一个具有以下布局的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
答案 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