How does python import order affect names? - python

I'm doing a flask tutorial (http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world) and I came upon a behaviour that I couldn't explain.
The main directory structure of the tutorial is :
microblog
|
|---- app
| |---- __init__.py
| |---- views.py
|
|---- flask
|---- run.py
and the contents of the files are :
microblog/run.py
#!flask/bin/python
from app import app
app.run(debug=True)
microblog/app/init.py
from flask import Flask
app = Flask(__name__)
from app import views
microblog/app/views.py
from app import app
#app.route("/")
#app.route("/index")
def index():
return "Hello World!"
everything works but if I transpose these two lines:
app = Flask(__name__)
from app import views
in views.py and then I execute run.py I get:
ImportError: cannot import name app
Why does that happen?

Because you're trying to import from the newly created variable app. If you want to import variable modules, then use importlib package:
my_module = importlib.import_module(app, 'view')

Contrary to what the other answer says, this is a circular import problem. app.__init__ tries to import app.views, which tries to import the app.app Flask created in app.__init__. If the Flask is created before app.__init__ imports app.views, app.views finds app.app. If the Flask is created after the import, it isn't there yet when app.views tries to find it.
Circular imports cause all sorts of horrible problems. It might be difficult, but the best way to handle them is generally to reorganize your code so there aren't circular imports.

Related

Getting 404 not found on Flask app deployed on PythonAnywhere

I've rummaged through maybe 50 different answers to this and still I haven't manage to fix it... I'm pretty new to flask and python.
I have an app which was running great locally but I've struggled with deploying on python anywhere. Initially I had a few import modules issues, now it runs but doesn't return any html template, despite not seeing any other issue. The main issue I had was that it couldn't find the "routes" app from wsgi, and I sort of fixed adding the app = Flask(name) line on routes.py (the short blueprint object is not callable).
routes.py:
from flask import Blueprint, render_template, request, redirect, send_file
import pyqrcode
from pyqrcode import QRCode
import subprocess
from extensions import db
from models import Link
app = Flask(__name__)
short = Blueprint('short', __name__, url_prefix='/')
#short.route('/index')
def index():
return render_template('index.html')
init.py
from flask import Flask
from extensions import db
from routes import short
def create_app(config_file='settings.py'):
app = Flask(__name__)
app.config.from_pyfile(config_file)
db.init_app(app)
app.register_blueprint(short)
return app
wsgi.py
import sys
# add your project directory to the sys.path
project_home = u'/home/b297py/mysite'
if project_home not in sys.path:
sys.path = [project_home] + sys.path
# import flask app but need to call it "application" for WSGI to work
from routes import app as application
For the purpose of testing, I've placed all the html templates both in the root directory and in the specific /template directory but it just didn't fix the issue.
In wsgi.py you are not calling create_app so you are never registering the blueprint. You should replace :
from routes import app as application
by something like :
from package_name import create_app
application = create_app()
(Example: https://www.pythonanywhere.com/forums/topic/12889/#id_post_50171)
Also as you mentioned, the fix adding app = Flask(__name__) to routes.pyallows you to bypass create_app (so you should remove it if you want to stick to the create_app approach).

Why `from . import views` can solve circle import in Flask? [duplicate]

This question already has answers here:
Why do circular imports seemingly work further up in the call stack but then raise an ImportError further down?
(9 answers)
Closed 6 years ago.
I learn from Larger Applications.In this document, It says: "all the view functions (the ones with a route() decorator on top) have to be imported in the init.py file. Not the object itself, but the module it is in."
I don't know why should when I do this: from . import views,It succeed.Though from views import * can also work well.
I organize these file like this:
myapplication/
runner.py
myflask/
__init__.py
views.py
templates/
static/
...
runner.py:
from testFlask import app
app.run()
myflask/__init__.py:
from flask import Flask
app = Flask(__name__)
from . import views # why this can work????
myflask/views.py:
from . import app
#app.route('/')
def index():
return 'Hello World!'
and I run it:
$ cd myapplication
$ python runner.py
It's OK to run this Flask app. However I want to know why from . import views can solve this circle import problem in flask? And why the doc says: Not the object itself, but the module it is in????
However,when I do like this:
#some_dir/
# application.py
# views.py
#application.py
from flask import Flask
app = Flask(__name__)
import views # it doesn't work
# from views import * # it works
app.run()
#views.py
from application import app
#app.route('/')
def index():
return 'Hello World!'
#run it
$ python application.py
It doesn't work.
It is a circular import. But in your case, the variable that might have been problematic (app) has already been defined in the imported script, so the import just causes the first "app" instance to be overwritten by the imported "app" instance. Which has no practical effect.
For details about this circular import situation, please read this post.
If you want to follow the pattern for a large flask application, you should look into blueprints and application factories.
It's not the specific command that solves the problem, but the order of the commands.
The trick is that you import the views after you created the app variable in your main script, so when the view script imports the main script the variable is already defined.
If you would try to import the views above declaring the app variable it would not work because it would cause an import loop and could not find the app variable.
Your project structure defines how the import would work best.
Here's an example of "A bit larger type of application" where you can use blueprints that defines a collection of views, templates, static files and other elements that can be applied to an application:
https://github.com/pallets/flask-website/tree/master/flask_website
Kind regards

Import statement flow

My app layout
my_app
__init__.py
my_app
__init__.py
startup
__init__.py
create_app.py
create_users.py
common_settings.py
core
__init__.py
models.py
views.py
errors
__init__.py
errors.py
Inner __init__.py
from flask import Flask
from flask_script import Manager
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__) # The WSGI compliant web application object
db = SQLAlchemy(app) # Setup Flask-SQLAlchemy
manager = Manager(app) # Setup Flask-Script
from my_app.startup.create_app import create_app
create_app()
create_app.py
def create_app(extra_config_settings={}):
# Load all blueprints with their manager commands, models and views
from my_app import core
return app
core/__init__.py
# from . import views
views.py
from my_app import app, db
from flask import Flask, request
#app.errorhandler(Error)
def handle_invalid_usage(error):
response = jsonify(data=error.to_dict())
response.status_code = error.status_code
return response
I based this code on a tutorial I found. Everything works fine as long as I leave the __init__.py in the core folder empty.
When I don't, I get a NameError: name Error is not defined in my views.py. Error comes from errors.py.
I have three questions:
1) Why does this happen only when I leave the import statement in core/__init__.py.
2)
create_app.py
app.config.from_envvar('ENV_SETTINGS_FILE')
# Other app.config commands here
from my_app import core
return app
What happens when from my_app import core runs?
3) Finally when I return app, is this to ensure that Inner __init__.py file contains the updated app object?
Any explanations would be greatly appreciated!
Trying to build and configure an app with dynamic imports is really bad news and confusing for the reasons you are discovering. A much better and understandable pattern would be a more typical factory:
def create_app():
app = Flask(__name__)
configure_app(app, config)
register_db(app)
add_views(app)
add_manager(app)
return app
if __name__ == '__main__':
app = create_app()
app.run()
But since you're asking, your problem is here:
from my_app import app, db
from flask import Flask, request
#app.errorhandler(Error) # Error is not imported
def handle_invalid_usage(error):
response = jsonify(data=error.to_dict())
response.status_code = error.status_code
return response
The error occurs because views.py is imported, the code compiler comes across Error and cannot find a reference to it.
For your second question: from my_app import core causes core.__init.__ to run, which (presumably) adds the views onto the app object.

Structuring Flask using Blueprints error

I'm attempting to break a small app into units and use the Blueprint pattern in Flask. However I seem to have trouble in getting the app running.
Here is my structure:
\myapp
login.py
\upload
__init__.py
views.py
Here is login.py:
import sys, os
from flask import Flask, Blueprint, request
from flask import render_template, session
from .upload import views
app = Flask(__name__)
app.register_blueprint(views)
#app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
print app.url_map
print app.blueprints
app.run(debug = True)
for __init__.py
from flask import Flask
app.register_blueprint('upload', __name__)
and in views.py
from flask import Flask, Blueprint, request, redirect, url_for, render_template
import views
upload = Blueprint('upload', __name__)
#upload.route('/uploaded', methods=['GET', 'POST'])
def upload_file():
...
Looking at the logs on heroku, here is the error:
from .upload import views
2015-09-05T10:59:00.506513+00:00 app[web.1]: ValueError: Attempted relative import in non-package
Have a structured my package and blueprint correctly? I used the documentation, but I think I'm missing something here.
There are three problems with your code:
You are using a relative import in login.py to include views, but given the folder structure and the fact that you use login.py as starting point, it cannot work here. Simply use from upload import views instead.
The __init__.py references an unknown variable, app. You actually don't need anything in this file, remove everything.
You try to register a module as a blueprint via app.register_blueprint(views) in login.py. This cannot work, a module is not a blueprint. Instead, import the upload variable defined in the views module, that is a blueprint: app.register_blueprint(views.upload).
Changing that should get you started. Two side notes:
You have an import in views.py that should probably not be there: import views. This should be harmless however.
I answered a question about Flask blueprints some days ago, and gave an example about how to use them. Maybe would you find the answer useful in your case as well (in particular the part where the blueprint definition is moved to the __init__.py file defining the blueprint).

Flask unable to find templates

My project structure is like below
run.py
lib/
mysite/
conf/
__init__.py (flask app)
settings.py
pages/
templates/
index.html
views.py
__init__.py
This is mysite.conf.__init__
from flask import Flask
app = Flask(__name__)
app.debug = True
My idea is to now import app to every other module to use it to create views. Here in this case there is a module pages.
In pages.views I have some code like
from flask import render_template
from mysite.conf import app
#app.route('/')
def index():
return render_template('index.html')
The index.html is placed in pages/templates
When I run this app from run.py which is like below
from mysite.conf import app
app.run()
I'm getting template not found error.
How to fix it? and why is this happening!
I'm basically a django guy, and facing a lot of inconvenience with importing the wsgi object every time to create a view in every module! It kinda feels crazy -- Which in a way encourages circular imports. Is there any way to avoid this?
Flask expects the templates directory to be in the same folder as the module in which it is created; it is looking for mysite/conf/templates, not mysite/pages/templates.
You'll need to tell Flask to look elsewhere instead:
app = Flask(__name__, template_folder='../pages/templates')
This works as the path is resolved relative to the current module path.
You cannot have per-module template directories, not without using blueprints. A common pattern is to use subdirectories of the templates folder instead to partition your templates. You'd use templates/pages/index.html, loaded with render_template('pages/index.html'), etc.
The alternative is to use a Blueprint instance per sub-module; you can give each blueprint a separate template folder, used for all views registered to that blueprint instance. Do note that all routes in a blueprint do have to start with a common prefix unique to that blueprint (which can be empty).
I ran in to this problem as well being brand new to Flask.
The solution for my situation was to create an __init__.py file in the main app directory so as to turn app/ in to a module since, as Martijn Pieters pointed out, flask expects templates to be in the main module directory by default. And to restate from his answer, you can reassign the default directory when instantiating the Flask class, e.g. Flask(__name__, template_folder=any/relative/path/from/app/dir/or/absolute/path/youd/like)
A simple directory structure should therefore look like this:
app/
run.py
templates/
sample_template.html
__init__.py # <---- This was the missing file in my case.
... where app.py might look like this:
from flask import Flask, render_template
app = Flask(__name__) # or e.g. Flask(__name__, template_folder='../otherdir')
#app.route("/")
def home():
return "Hello world"
#app.route("/hello/again")
def hello_again():
return render_template("hello_again.html")
if __name__ == "__main__":
app.run()
I'm pretty sure you made a mistaking spelling "template" on main folder. Just rename it
or:
do something like this (template_folder = 'the name of the folder your file belongs to')
from flask import Blueprint
from flask import render_template
views = Blueprint('views',__name__,template_folder='tamplate')
#views.route('/')
def home():
return render_template("home.html")

Categories

Resources