Thoughts on using Flask (a Python web framework) as a server for a Yeoman powered application. I previously write an article on using Yeoman with a custom develoment server, but I didn't share my Python code.
Flask
While developing, Flask must serve both the app directory and the .tmp directory (used for js templates, et compass). If the file is not present in app, it must check in the .tmp directory as fallback.
In production, the server should serve the dist directory only.
Here is a basic Flask server that do the job, it rely on the FLASK_YEOMAN_DEBUG
environment variable to switch in dev/production mode.
from flask import Flask, current_app, send_file, render_template_string
from werkzeug.exceptions import NotFound
import os
app = Flask(__name__)
@app.route('/', defaults={'path': 'index.html'})
@app.route('/<path:path>')
def serve_index(path):
flask_yeoman_debug = int(os.environ.get('FLASK_YEOMAN_DEBUG', False))
fpath = 'dist'
# While developing, we serve the app directory
if flask_yeoman_debug:
fpath = 'app'
root_path = current_app.root_path
default_path = os.path.join(root_path, fpath)
default_path_abs = os.path.join(default_path, path)
if os.path.isfile(default_path_abs):
if path == 'index.html':
# If index.html is requested, we inject the Flask current_app config
return render_template_string(open(default_path_abs).read().decode('utf-8'),
config=current_app.config)
return send_file(default_path_abs)
# While development, we must check the .tmp dir as fallback
if flask_yeoman_debug:
# The .tmp dir is used by compass and for the template file
alt_path = os.path.join(root_path, '.tmp')
alt_path_abs = os.path.join(alt_path, path)
if os.path.isfile(alt_path_abs):
return send_file(alt_path_abs)
raise NotFound()
Grunt watch
Yeoman use Grunt as a task runner to automate tasks.
In the Gruntfile.js, let's create a new flask task to spawn our flask server, and add it to the server task.
The child_process node module.
First we create a new task:
// New task for flask server
grunt.registerTask('flask', 'Run flask server.', function() {
var spawn = require('child_process').spawn;
grunt.log.writeln('Starting Flak development server.');
// stdio: 'inherit' let us see flask output in grunt
var PIPE = {stdio: 'inherit'};
spawn('python', ['server.py'], PIPE);
});
Then we can replace the connect:livereload
by flask
:
grunt.registerTask('server', function (target) {
if (target === 'dist') {
return grunt.task.run(['build', 'open', 'connect:dist:keepalive']);
}
grunt.task.run([
'clean:server',
'coffee:dist',
'createDefaultTemplate',
'jst',
'compass:server',
'flask',
'open',
'watch',
]);
});
Live reloading support
To keep the live reloading working even with a custom webserver, you need to add this snippet manually in your index.html.
<!-- livereload script -->
<script>document.write('<script src="http://'
+ (location.host || 'localhost').split(':')[0]
+ ':35729/livereload.js?snipver=1" type="text/javascript"><\/script>')
</script>
Share Flask config with your Javascript app
In your index.html (the one in your app directory), you have access to Flask config directly, so you can share configuration between Flask and you javascript application and do something like this:
<script>
window.myapp.config = {var1: {{ config.VAR1|tojson }},
var2: {{ config.VAR2|tojson }}};
</script>
{% if config.FLASK_YEOMAN_DEBUG %}
<!-- livereload script -->
<script>document.write('<script src="http://'
+ (location.host || 'localhost').split(':')[0]
+ ':35729/livereload.js?snipver=1" type="text/javascript"><\/script>')
</script>
{% endif %}
Introducing Flask-Yeoman
I created a Flask Blueprint: Flask-Yeoman that allows you to quickly get up and running:
from flask import Flask, jsonify
from flask_yeoman import flask_yeoman
app = Flask(__name__)
app.register_blueprint(flask_yeoman)
if __name__ == "__main__":
app.run(host='0.0.0.0', port=5000)
Check out the README on GitHub.
Feedback
Please don't hesitate if you have any feedback/question/suggestion !
Tip with Bitcoin
Tip me with Bitcoin and vote for this post!
Leave a comment