Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reloader fails to reload after SyntaxError when the app is in an __init__.py file #2423

Closed
segevfiner opened this issue Jul 19, 2017 · 5 comments

Comments

@segevfiner
Copy link

Expected Behavior

The reloader should work correctly even for an app in a package's __init__.py file.

In hello/__init__.py:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'
set FLASK_APP=hello
set FLASK_DEBUG=1
flask run

Than edit __init__.py and introduce a syntax error.

Actual Behavior

I get the SyntaxError the first time I try to browse and afterwards get:

Traceback (most recent call last):
  File "flask\cli.py", line 48, in find_best_app
    'using a factory function.' % modules.__name__)
NoAppException: Failed to find application in module "hello". Are you sure it contains a Flask application? Maybe you wrapped it in WSGI middleware or you are using a factory function.

Cause

This is caused by weird CPython behavior which might be a bug:

>>> __import__('syntax_error')
...
SyntaxError: ...
>>> a = __import__('syntax_error')  # woot it works?!
>>> a.__file__
'<path to the directory containing __init__.py>'  # Instead of path to __init__.py

After the SyntaxError in the app, the Wekzeug reloader won't know about the __init__.py file any more since it iterates sys.modules and the entry there contains the path to the directory instead of the path to the __init__.py file, and Flask will call __import__ repeatedly but will get the weird empty module and will fail to find an app in it, leading to the described exception.

Environment

  • Python version: 2.7.13
  • Flask version: 0.12.2
  • Werkzeug version: 0.12.2

Not sure if this happens in Python 3.

@davidism
Copy link
Member

Does this happen with current master?

@segevfiner
Copy link
Author

segevfiner commented Jul 19, 2017

Does this happen with current master?

Yep.

@ghost
Copy link

ghost commented Jul 19, 2017

I've been able to verify this behaviour with current master and Pythons 2.7 and 3.6. On first request, flask correctly identifies and returns the SyntaxError; on subsequent reloads, a NoAppException is returned instead. Presumably, as OP states, this is due to import module caching.

On Python3.6 no SyntaxError is ever returned; flask immediately replies with a NoAppException, but the error message is different:

Traceback (most recent call last):
# ... File this, File that, etc ...
    'Could not import "{name}"."'.format(name=module_name)
flask.cli.NoAppException: Could not import "hello"."

I'm assuming the desired behaviour is for Flask to forward errors that arise during importation rather than fail with NoAppException?

@segevfiner
Copy link
Author

A fix was released for the reloader failing in Werkzeug. The NoAppException on subsequent requests remains though. But the reloader will continue reloading on changes to the init.py files.

segevfiner added a commit to segevfiner/flask that referenced this issue Jan 8, 2018
When a SyntaxError occurrs in a package's `__init__.py` file, Python
seems to leave an empty module in `sys.modules`. This will result in any
request after the first to give you a `NoAppException` instead of the
`SyntaxError` again.

Fixes pallets#2423
@davidism
Copy link
Member

davidism commented Jan 9, 2018

I can't reproduce this on Python 2.7 or 3.6, Flask 0.12 or master. I tried starting with valid syntax then editing in an error, and vice versa.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants