SaltyCrane Blog — Notes on Python and web development on Ubuntu Linux

Customizing Bootstrap (Sass version) using Grunt and Bower

Update 2015-02-22: My co-workers informed me that Grunt is so last year and Gulp is the new hotness. You wish this post covered Gulp, but instead it covers Grunt.

I recently converted this blog to use Twitter's Bootstrap CSS framework to make it responsive. (In particular, I wanted to read it on my phone.) I had been using Blueprint CSS from 2008 so it was about time for an update. Unfortunately my blog design is also from 2008 but I'm not updating that. (Web design is hard.) Update: I ended up refreshing the design a little. (Web design is still hard.)

My problem was that 2014 Bootrap's 200px gutter width didn't match my 2008 design's 2px gutter. So I wanted to customize the gutter width.

There are a few ways to customize Bootstrap. One option is creating a custom version on Bootstrap's website and downloading it for use on your site. However this doesn't allow you to change things quickly as you develop. Another option is overriding Bootstrap's style in your site stylesheet. However, it's hacky to write compilcated selectors to override something in multiple places when it is set in a single variable in Bootstrap. For example, to change the gutter width, here is the single variable that needs to be changed.

Approach

Here is the approach I took to customize Bootstrap. I'm running on Ubuntu 14.10 Utopic Unicorn 64-bit.

  • Use the Sass version of Bootstrap. The standard Bootstrap project uses Less but my limited knowledge of frontend technology tells me Sass (specifically SCSS) is a better choice.
  • Install Bootstrap using Bower. Bower is a tool used to install frontend packages.
    "What's bower?" "A package manager, install it with npm." "What's npm?" "A package manager, you can install it with APT" "What's APT?" - adapted from Stefan Baumgartner's tweet
    An alternative is to just download Bootstrap and add it to your project. I chose Bower because I wanted to be like the cool kids from 2 years ago and it would be nice to upgrade my libraries with a simple command.

    Update 2015-03-22: Another alternative is to use npm to install pacakges. Bower is optimized for frontend components while npm historically is used for backend packages[1] but there is discussion about merging functionality[2].

  • Commit the bower_components directory to git. Alternatively you can leave bower_components out of git and use something like grunt-bower-task or grunt-bower-copy to copy stuff from bower_components into your project. I chose to commit bower_components to git so that I wouldn't need an extra grunt task. The downside is that I have big long ugly paths in my bootstrap.scss file.
  • Override Bootstrap's Sass variables, remove unneeded components, and combine Bootstrap with my site's stylesheet using Sass @imports.
  • Compile the Sass SCSS files to CSS using grunt-contrib-sass.
  • Commit the compiled Sass files to git. An alternative is to compile the files as part of the deploy process. At work, it is a pain keeping these files in git because there are many merge conflicts and differences in build tool versions and platforms between developers. For my blog, I am the only committer so I won't run into this.

Project directory structure

Here is what my project directory structure looks like:

my-project
├── Gruntfile.js
├── bower.json
├── bower_components
│   ├── bootstrap-sass-official
│   └── jquery
├── node_modules
│   ├── grunt
│   ├── grunt-contrib-sass
│   └── grunt-contrib-watch
├── package.json
├── sass
│   ├── _bootstrap-variables-override.scss
│   ├── _bootstrap.scss
│   └── mystyle.scss
└── static
    └── css

Install Sass

Sass is installed using RubyGems, the Ruby package manager.

$ sudo apt-get install ruby 
$ sudo gem install sass 
$ sass --version 
Sass 3.4.13 (Selective Steve)

Install grunt-cli, and bower

Both Grunt and Bower depend on npm, the Node.js package manager.

$ sudo add-apt-repository ppa:chris-lea/node.js 
$ sudo apt-get update 
$ sudo apt-get install nodejs 
$ npm --version
1.4.28
$ sudo npm install -g grunt-cli 
$ sudo npm install -g bower 
$ grunt --version 
grunt-cli v0.1.13
$ bower --version 
1.3.12

Install bootstrap-sass using Bower

bootstrap-sass is the official Sass port of Bootstrap. First create a bower.json file, /tmp/my-project/bower.json. For more information about bower.json files and installing packages, see the Bower documentation.

{
  "name": "my-project",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "bootstrap-sass-official": "3.3.1"
  }
}

Then I ran bower install to download the files. The packages are stored in the bower_components directory.

$ cd /tmp/my-project 
$ bower install 

Customize Boostrap

To customize Boostrap, I copied the bower_components/bootstrap-sass-official/assets/stylesheets/boostrap.scss file to my sass directory, changed the paths, deleted the components that I didn't need, and added a line for my custom variable overrides. If anyone knows how to put that long path into a single place, I'd appreciate the tip. Here is my final file, named /tmp/my-project/sass/_bootstrap.scss:

Update 2015-03-18: To answer my own question, I think setting the loadPath option with grunt-contrib-sass will solve the long path problem.

// Core variables and mixins
// variable overrides come first (without the !default)
// bootstrap default variables come second because they use sass !default (won't get set if set already')
// and some variables in here depend on our overrides.
@import "bootstrap-variables-override";
@import "../bower_components/bootstrap-sass-official/assets/stylesheets/bootstrap/variables";
@import "../bower_components/bootstrap-sass-official/assets/stylesheets/bootstrap/mixins";

// Reset and dependencies
@import "../bower_components/bootstrap-sass-official/assets/stylesheets/bootstrap/normalize";
@import "../bower_components/bootstrap-sass-official/assets/stylesheets/bootstrap/print";
@import "../bower_components/bootstrap-sass-official/assets/stylesheets/bootstrap/glyphicons";

// Core CSS
@import "../bower_components/bootstrap-sass-official/assets/stylesheets/bootstrap/scaffolding";
@import "../bower_components/bootstrap-sass-official/assets/stylesheets/bootstrap/type";
@import "../bower_components/bootstrap-sass-official/assets/stylesheets/bootstrap/code";
@import "../bower_components/bootstrap-sass-official/assets/stylesheets/bootstrap/grid";
@import "../bower_components/bootstrap-sass-official/assets/stylesheets/bootstrap/tables";
@import "../bower_components/bootstrap-sass-official/assets/stylesheets/bootstrap/forms";
@import "../bower_components/bootstrap-sass-official/assets/stylesheets/bootstrap/buttons";

// Utility classes
@import "../bower_components/bootstrap-sass-official/assets/stylesheets/bootstrap/utilities";
@import "../bower_components/bootstrap-sass-official/assets/stylesheets/bootstrap/responsive-utilities";

The Boostrap variables are located in bower_components/bootstrap-sass-official/assets/stylesheets/bootstrap/_variables.scss I copied the ones that I was interested in into a _bootstrap-variables-override.scss file. Be sure to remove the !default flag and import this file before the default bootstrap variables. For more information about !default, see the Sass documentation. Here is my /tmp/my-project/sass/_bootstrap-variables-override.scss file:

// overrides here (do not use !default here)
$grid-gutter-width: 10px;
$font-size-base: 16px;
$headings-font-weight: 800;

In my site stylesheet, I @import the _bootstrap.scss file. Here is my /tmp/my-project/sass/mystyle.scss file:

@import "bootstrap";
/* add site style here */

Install grunt plugins

To compile the Sass files to CSS, I am using grunt-contrib-sass. I created this package.json file, /tmp/my-project/package.json. For more information about the package.json file and installing plugins, see the Grunt documentation.

{
  "name": "my-project",
  "version": "0.1.0",
  "devDependencies": {
    "grunt": "0.4.5",
    "grunt-contrib-sass": "0.8.1",
    "grunt-contrib-watch": "0.6.1"
  }
}

Then I ran npm install to install grunt, grunt-contrib-sass, and grunt-contrib-watch.

$ cd /tmp/my-project 
$ npm install 

Create Gruntfile.js

My Gruntfile.js uses the grunt-contrib-sass plugin to compile the Sass to CSS. My input file is sass/mystyle.scss and my output file is static/css/mystyle.css. I am also using the grunt-contrib-watch plugin during development to automatically run the compilation whenever a SCSS file changes. For more information on configuring tasks, see the Grunt documentation. Here is my Gruntfile, /tmp/my-project/Gruntfile.js:

sassFiles = {
    'static/css/mystyle.css': 'sass/mystyle.scss'
}

module.exports = function(grunt) {
    grunt.initConfig({
        sass: {
            dev: {
                options: {
                    style: 'expanded'
                },
                files: sassFiles
            },
            dist: {
                options: {
                    style: 'compressed'
                },
                files: sassFiles
            }
        },
        watch: {
            sass: {
                files: 'sass/*.scss',
                tasks: ['sass:dev']
            }
        }
    });
    grunt.loadNpmTasks('grunt-contrib-sass');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.registerTask('buildcss', ['sass:dist']);
};

Run grunt

To compile the Sass to CSS for production, I run grunt buildcss:

$ cd /tmp/my-project 
$ grunt buildcss 

During development, I use grunt watch to build whenever a SCSS (Sass) file changes.

$ cd /tmp/my-project 
$ grunt watch 

See Also / References

Ordered a Lenovo X1 Carbon for 40% off

I ordered a ~$1200 2.8 lb. 14" Lenovo Thinkpad X1 Carbon (2014, 2nd generation, 20A7) today at 40% off for the Black Friday / Cyber Monday sale. I hope I like the keyboard. I splurged for an Intel Core i7-4600U, 8GB RAM, 256GB SSD, and the WQHD 2560x1440 display. I have enjoyed 7 months with my ~$200 2.8 lb. 11" Acer C720 Chromebook. It's a pleasant contrast to my work-issued 5.9 lb. 17" Lenovo Thinkpad W510. Crouton worked well for running Linux on the Chromebook but I wanted a dedicated Linux laptop and a better screen. I will bequeath the C720 to my wife since she currently uses my 7+ year old Dell Inspiron E1405. I considered the 3.5 lb. 14" Lenovo Thinkpad T440s due to better battery/keyboard/flexibility, but the X1 Carbon was ~$200 cheaper for a similarly spec'd model due to a bigger discount and I liked the thinner and lighter design and better display. Many reviews complained about the new keyboard layout and adaptive function row. I hope key remapping will reduce the pain enough. LWN.net editor, Jonathan Corbet mentioned he bought an X1 Carbon in High-DPI displays and Linux, so it can't be too bad, right?

Here is the full 2014 2nd Gen ThinkPad X1 Carbon (Type 20A7, 20A8) spec sheet.

Update 2015-03-14: Lenovo released the 3rd generation of the X1 Carbon which fixes the keyboard and improves battery life. Too bad I didn't wait a few months. Here is the new 2015 3rd Gen spec sheet.

How to install grunt on Ubuntu 14.04

Grunt is a Javascript task runner that can be used to compile Sass, run JSHint, or run many other plugins. It depends on the Node.js package manager, npm.

If you use the standard Ubuntu Trusty Tahr repository to install nodejs/npm, you will get this error when running grunt:

/usr/bin/env: node: No such file or directory
Instead, use the chris-lea nodejs repository.

Install nodejs, npm, and grunt-cli

$ sudo add-apt-repository ppa:chris-lea/node.js 
$ sudo apt-get update 
$ sudo apt-get install nodejs 
$ sudo npm install -g grunt-cli 

Install grunt in your project directory

$ cd ~/myproject 
$ echo "{}" > package.json 
$ npm install grunt --save-dev 

Verify grunt is installed

$ nodejs --version 
v0.10.33
$ npm --version 
1.4.28
$ grunt --version 
grunt-cli v0.1.13
grunt v0.4.5

Run a simple grunt task

  1. $ cd ~/myproject
    
  2. Create a package.json file:
    {
      "name": "my-project-name",
      "version": "0.1.0",
      "devDependencies": {
        "grunt": "~0.4.5",
        "grunt-contrib-uglify": "~0.5.0"
      }
    }
    
  3. Install grunt-contrib-uglify
    $ npm install 
    npm WARN package.json my-project-name@0.1.0 No description
    npm WARN package.json my-project-name@0.1.0 No repository field.
    npm WARN package.json my-project-name@0.1.0 No README data
    grunt-contrib-uglify@0.5.1 node_modules/grunt-contrib-uglify
    ├── chalk@0.5.1 (ansi-styles@1.1.0, escape-string-regexp@1.0.2, supports-color@0.2.0, strip-ansi@0.3.0, has-ansi@0.1.0)
    ├── lodash@2.4.1
    ├── maxmin@0.2.2 (figures@1.3.5, pretty-bytes@0.1.2, gzip-size@0.2.0)
    └── uglify-js@2.4.15 (uglify-to-browserify@1.0.2, async@0.2.10, optimist@0.3.7, source-map@0.1.34)
    
  4. Get an example unminified JS file:
    $ wget http://code.jquery.com/jquery-2.1.1.js 
    --2014-11-22 00:47:31--  http://code.jquery.com/jquery-2.1.1.js
    Resolving code.jquery.com (code.jquery.com)... 94.31.29.53, 94.31.29.230
    Connecting to code.jquery.com (code.jquery.com)|94.31.29.53|:80... connected.
    HTTP request sent, awaiting response... 200 OK
    Length: 247351 (242K) [application/x-javascript]
    Saving to: ‘jquery-2.1.1.js’
    
    100%[================================================================================================================>] 247,351     --.-K/s   in 0.1s    
    
    2014-11-22 00:47:31 (1.71 MB/s) - ‘jquery-2.1.1.js’ saved [247351/247351]
    
  5. Create a Gruntfile.js file:
    module.exports = function(grunt) {
      grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        uglify: {
          build: {
            src: 'jquery-2.1.1.js',
            dest: 'jquery-2.1.1.min.js'
          }
        }
      });
      grunt.loadNpmTasks('grunt-contrib-uglify');
      grunt.registerTask('default', ['uglify']);
    };
    
  6. Run the grunt task:
    $ grunt 
    Running "uglify:build" (uglify) task
    
    Done, without errors.
    
  7. You should now have a minified file, jquery-2.1.1.min.js
    $ ls -gG jquery* 
    -rw-rw-r-- 1 247351 2014 10/23 17:16 jquery-2.1.1.js
    -rw-rw-r-- 1  84113 2014 11/22 00:48 jquery-2.1.1.min.js
    

References

A bank style session timeout example using jQuery, Bootstrap, and Flask

This is an example that uses Javascript to display a session timeout warning modal 10 minutes before session expiration. It also resets the session expiration whenever the user clicks the mouse. It uses Javascript, jQuery, and Boostrap on the frontend and Python, Flask, Flask-Login, and WTForms on the backend.

  • Mouse clicks anywhere on the page ping the server at a maximum frequecy of once per minute and reset the session expiration.
  • 10 minutes before the session expiration, a warning modal is displayed with two buttons: "Log out" and "Stay Logged In".
  • If the user clicks "Stay Logged In" the session expiration is reset.
  • If the user clicks "Log out", the user is logged out.
  • If the user does nothing for 10 minutes, the user is logged out and displayed a message that the session timed out.

Here is the Javascript (session-monitor.js):

sessionMonitor = function(options) {
    "use strict";

    var defaults = {
            // Session lifetime (milliseconds)
            sessionLifetime: 60 * 60 * 1000,
            // Amount of time before session expiration when the warning is shown (milliseconds)
            timeBeforeWarning: 10 * 60 * 1000,
            // Minimum time between pings to the server (milliseconds)
            minPingInterval: 1 * 60 * 1000,
            // Space-separated list of events passed to $(document).on() that indicate a user is active
            activityEvents: 'mouseup',
            // URL to ping the server using HTTP POST to extend the session
            pingUrl: '/ping',
            // URL used to log out when the user clicks a "Log out" button
            logoutUrl: '/logout',
            // URL used to log out when the session times out
            timeoutUrl: '/logout?timeout=1',
            ping: function() {
                // Ping the server to extend the session expiration using a POST request.
                $.ajax({
                    type: 'POST',
                    url: self.pingUrl
                });
            },
            logout: function() {
                // Go to the logout page.
                window.location.href = self.logoutUrl;
            },
            onwarning: function() {
                // Below is example code to demonstrate basic functionality. Use this to warn
                // the user that the session will expire and allow the user to take action.
                // Override this method to customize the warning.
                var warningMinutes = Math.round(self.timeBeforeWarning / 60 / 1000),
                    $alert = $('<div id="jqsm-warning">Your session will expire in ' + warningMinutes + ' minutes. ' +
                               '<button id="jqsm-stay-logged-in">Stay Logged In</button>' +
                               '<button id="jqsm-log-out">Log Out</button>' +
                               '</div>');

                if (!$('body').children('div#jqsm-warning').length) {
                    $('body').prepend($alert);
                }
                $('div#jqsm-warning').show();
                $('button#jqsm-stay-logged-in').on('click', self.extendsess)
                    .on('click', function() { $alert.hide(); });
                $('button#jqsm-log-out').on('click', self.logout);
            },
            onbeforetimeout: function() {
                // By default this does nothing. Override this method to perform actions
                // (such as saving draft data) before the user is automatically logged out.
                // This may optionally return a jQuery Deferred object, in which case
                // ontimeout will be executed when the deferred is resolved or rejected.
            },
            ontimeout: function() {
                // Go to the timeout page.
                window.location.href = self.timeoutUrl;
            }
        },
        self = {},
        _warningTimeoutID,
        _expirationTimeoutID,
        // The time of the last ping to the server.
        _lastPingTime = 0;

    function extendsess() {
        // Extend the session expiration. Ping the server and reset the timers if
        // the minimum interval has passed since the last ping.
        var now = $.now(),
            timeSinceLastPing = now - _lastPingTime;

        if (timeSinceLastPing > self.minPingInterval) {
            _lastPingTime = now;
            _resetTimers();
            self.ping();
        }
    }

    function _resetTimers() {
        // Reset the session warning and session expiration timers.
        var warningTimeout = self.sessionLifetime - self.timeBeforeWarning;

        window.clearTimeout(_warningTimeoutID);
        window.clearTimeout(_expirationTimeoutID);
        _warningTimeoutID = window.setTimeout(self.onwarning, warningTimeout);
        _expirationTimeoutID = window.setTimeout(_onTimeout, self.sessionLifetime);
    }

    function _onTimeout() {
        // A wrapper that calls onbeforetimeout and ontimeout and supports asynchronous code.
        $.when(self.onbeforetimeout()).always(self.ontimeout);
    }

    // Add default variables and methods, user specified options, and non-overridable
    // public methods to the session monitor instance.
    $.extend(self, defaults, options, {
        extendsess: extendsess
    });
    // Set an event handler to extend the session upon user activity (e.g. mouseup).
    $(document).on(self.activityEvents, extendsess);
    // Start the timers and ping the server to ensure they are in sync with the backend session expiration.
    extendsess();

    return self;
};

Here is the important HTML:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>Login</title>
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" />
    </head>
    <body>
        <div id="session-warning-modal" class="modal">
            <div class="modal-dialog">
                <div class="modal-content">
                    <div class="modal-header">
                        <h4 class="modal-title" id="sessWarnLabel">Your session is about to expire<a class="sectionlink" title="Section permalink" href="#sessWarnLabel"></a></h4>
                    </div>
                    <div class="modal-body">
                        Your session will expire in <span id="remaining-time"></span> minutes due to inactivity.
                    </div>
                    <div class="modal-footer">
                        <button id="log-out" class="btn btn-default" type="button" data-dismiss="modal">Log Out</button>
                        <button id="stay-logged-in" class="btn btn-warning" type="button" data-dismiss="modal">Stay Logged In</button>
                    </div>
                </div>
            </div>
        </div>
        <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
        <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
        <script src="{{ url_for('static', filename='session-monitor.js')}}"></script>
        <script type="text/javascript">
            // Configure and start the session timeout monitor
            sessMon = sessionMonitor({
                // Subtract 1 minute to ensure the backend doesn't expire the session first
                sessionLifetime: {{ PERMANENT_SESSION_LIFETIME_MS }} - (1 * 60 * 1000),
                timeBeforeWarning: 10 * 60 * 1000,  // 10 minutes
                minPingInterval: 1 * 60 * 1000,  // 1 minute
                pingUrl: '/ping',
                logoutUrl: '/logout',
                timeoutUrl: '/logged-out?timeout=1&next=' + encodeURIComponent(
                    window.location.pathname + window.location.search + window.location.hash),
                // The "mouseup" event was used instead of "click" because some of the
                // inner elements on some pages have click event handlers that stop propagation.
                activityEvents: 'mouseup',
                onwarning: function() {
                    $("#session-warning-modal").modal("show");
                }
            });
            $(document).ready( function() {
                // Configure the session timeout warning modal
                $("#session-warning-modal")
                    .modal({
                        "backdrop": "static",
                        "keyboard": false,
                        "show": false
                    })
                    .on("click", "#stay-logged-in", sessMon.extendsess)
                    .on("click", "#log-out", sessMon.logout)
                    .find("#remaining-time").text(Math.round(sessMon.timeBeforeWarning / 60 / 1000));
            });
            window.sessMon = sessMon;
        </script>
    </body>
</html>

Here is the Python Flask app (myapp.py):

import collections
import datetime

from flask import Flask, request, render_template, redirect, url_for, session
from flask.ext.login import (
    LoginManager, login_user, logout_user,  UserMixin, login_required)
from wtforms.fields import PasswordField, StringField
from wtforms.form import Form


UserRow = collections.namedtuple('UserRow', ['id', 'password'])
TOY_USER_DATABASE = {
    'george': UserRow(id=1, password='george'),
}


# settings ###############################################################
# Set a secret key to sign the session (Flask config value)
SECRET_KEY = 'insert secret key here'

# The amount of time after which the user's session expires
# (this is a Flask setting and is also used by the Javascript)
PERMANENT_SESSION_LIFETIME = datetime.timedelta(minutes=60)


# init ###############################################################
app = Flask(__name__)
app.config.from_object(__name__)
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = '.login'


@login_manager.user_loader
def load_user(userid):
    return User(userid)


@app.context_processor
def add_session_config():
    """Add current_app.permanent_session_lifetime converted to milliseconds
    to context. The config variable PERMANENT_SESSION_LIFETIME is not
    used because it could be either a timedelta object or an integer
    representing seconds.
    """
    return {
        'PERMANENT_SESSION_LIFETIME_MS': (
            app.permanent_session_lifetime.seconds * 1000),
    }


# models ###############################################################
class User(UserMixin):
    def __init__(self, id):
        self.id = id


# forms ###############################################################
class LoginForm(Form):
    username = StringField()
    password = PasswordField()


# views ###############################################################
@app.route("/login", methods=['GET', 'POST'])
def login():
    form = LoginForm(request.form)
    message = ''

    if request.method == 'POST' and form.validate():
        db_user = TOY_USER_DATABASE.get(form.username.data)
        if form.password.data == db_user.password:
            user = User(db_user.id)
            login_user(user)
            return redirect(url_for('.home'))
        else:
            message = 'Login failed.'

    context = {
        'form': form,
        'message': message,
    }
    return render_template('login.html', **context)


@app.route("/")
@login_required
def home():
    return render_template('home.html')


@app.route("/another-page")
@login_required
def another_page():
    return render_template('another_page.html')


@app.route("/logout")
@login_required
def logout():
    logout_user()
    return redirect(url_for('.logged_out') + '?' + request.query_string)


@app.route("/logged-out")
def logged_out():
    timed_out = request.args.get('timeout')
    return render_template('logged_out.html', timed_out=timed_out)


@app.route("/ping", methods=['POST'])
def ping():
    session.modified = True
    return 'OK'


if __name__ == "__main__":
    app.run(debug=True)

The full example is also on github at: https://github.com/saltycrane/session-timeout-example.

Initial ideas were taken from http://www.itworld.com/development/335546/how-create-session-timeout-warning-your-web-application-using-jquery but, from what I could tell, it pinged the server whether the user was active or not.

The old "%" string formatting and the new string .format() method handle unicode differently

Today I learned that the old style "%" string formatting and the new string .format() method behave differently when interpolating unicode strings. I was suprised to find out that one of these lines raised an error while one did not:

'%s' % u'O\u2019Connor'
'{}'.format(u'O\u2019Connor')

The old style "%" formatting operation returns a unicode string if one of the values is a unicode string even when the format string is a non-unicode string:

Python 2.7.3 (default, Feb 27 2014, 19:58:35) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> '%s' % u'O\u2019Connor'
u'O\u2019Connor'

The new string .format() method called on a non-unicode string with a unicode string argument tries to encode the unicode string to a non-unicode string (bytestring) possibly raising a UnicodeEncodeError:

Python 2.7.3 (default, Feb 27 2014, 19:58:35) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> '{}'.format(u'O\u2019Connor')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\u2019' in position 1: ordinal not in range(128)</module></stdin>

I guess the correct thing to do is to start with a unicode format string:

Python 2.7.3 (default, Feb 27 2014, 19:58:35) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> u'{}'.format(u'O\u2019Connor')
u'O\u2019Connor'

See also

Python logging filters do not propagate like handlers and levels do

Loggers are organized in a hierarchical fashion. A logger named 'foo.bar' is a child of a logger named 'foo'.

getLogger() returns a reference to a logger instance with the specified name if it is provided, or root if not. The names are period-separated hierarchical structures. Multiple calls to getLogger() with the same name will return a reference to the same logger object. Loggers that are further down in the hierarchical list are children of loggers higher up in the list. For example, given a logger with a name of foo, loggers with names of foo.bar, foo.bar.baz, and foo.bam are all descendants of foo. - Loggers documentation

If the level is not set on a logger, the level of the parent is used.

Loggers have a concept of effective level. If a level is not explicitly set on a logger, the level of its parent is used instead as its effective level. If the parent has no explicit level set, its parent is examined, and so on - all ancestors are searched until an explicitly set level is found. The root logger always has an explicit level set (WARNING by default). When deciding whether to process an event, the effective level of the logger is used to determine whether the event is passed to the logger’s handlers. - Loggers documentation

import logging

foo_logger = logging.getLogger('foo')
foo_logger.setLevel(20)

foo_bar_logger = logging.getLogger('foo.bar')

print foo_logger.getEffectiveLevel()
print foo_bar_logger.getEffectiveLevel()
20
20

Similarly, if a handler is not defined for a logger, the handler of the parent is used.

Child loggers propagate messages up to the handlers associated with their ancestor loggers. Because of this, it is unnecessary to define and configure handlers for all the loggers an application uses. It is sufficient to configure handlers for a top-level logger and create child loggers as needed. (You can, however, turn off propagation by setting the propagate attribute of a logger to False.) - Loggers documentation

import logging

myformatter = logging.Formatter("MY HANDLER: %(name)s - %(message)s")

myhandler = logging.StreamHandler()
myhandler.setFormatter(myformatter)

foo_logger = logging.getLogger('foo')
foo_logger.addHandler(myhandler)

foo_bar_logger = logging.getLogger('foo.bar')

foo_logger.error('asdfasdf')
foo_bar_logger.error('zxcvzxcv')
MY HANDLER: 40 foo - asdfasdf
MY HANDLER: 40 foo.bar - zxcvzxcv

However, filters, unlike levels and handlers, do not propagate. If a filter is not defined for a logger, the filter of the parent is NOT used.

Note that filters attached to handlers are consulted before an event is emitted by the handler, whereas filters attached to loggers are consulted whenever an event is logged (using debug(), info(), etc.), before sending an event to handlers. This means that events which have been generated by descendant loggers will not be filtered by a logger’s filter setting, unless the filter has also been applied to those descendant loggers. - Filter Objects documentation
See also the logging flowchart.

import logging

class MyFilter(logging.Filter):
    def filter(self, record):
        record.msg = 'MY FILTER: ' + record.msg
        return 1

myfilter = MyFilter()

myformatter = logging.Formatter("MY HANDLER: %(name)s - %(message)s")

myhandler = logging.StreamHandler()
myhandler.setFormatter(myformatter)

foo_logger = logging.getLogger('foo')
foo_logger.addFilter(myfilter)
foo_logger.addHandler(myhandler)

foo_bar_logger = logging.getLogger('foo.bar')

foo_logger.error('asdfasdf')
foo_bar_logger.error('zxcvzxcv')
MY HANDLER: foo - MY FILTER: asdfasdf
MY HANDLER: foo.bar - zxcvzxcv

I guess I'll attach it to the handler instead...

import logging

class MyFilter(logging.Filter):
    def filter(self, record):
        record.msg = 'MY FILTER: ' + record.msg
        return 1

myfilter = MyFilter()

myformatter = logging.Formatter("MY HANDLER: %(name)s - %(message)s")

myhandler = logging.StreamHandler()
myhandler.setFormatter(myformatter)
myhandler.addFilter(myfilter)

foo_logger = logging.getLogger('foo')
foo_logger.addHandler(myhandler)

foo_bar_logger = logging.getLogger('foo.bar')

foo_logger.error('asdfasdf')
foo_bar_logger.error('zxcvzxcv')
MY HANDLER: foo - MY FILTER: asdfasdf
MY HANDLER: foo.bar - MY FILTER: zxcvzxcv

Subdomain-based configuration for a Flask local development server

This example shows how to set up a Flask local development server to use a different configuration based on the subdomain of the request. The project I work on has several environments (dev, qa, staging, etc). Each environment has different database and API hostnames. I use this to switch between database and API environments quickly while using my local development server.

This assumes a create_app function is used to create the Flask application instance as described in the Application Factories Flask documentation.

create_app

Modify the create_app function to take a configobj argument and use it to override the default configuration.

def create_app(configobj=None):
    app = Flask(__name__)

    # Default configuration
    app.config.from_object(__name__)

    # Override configuration using config passed into create_app
    if configobj:
        app.config.from_object(configobj)

    return app

SubdomainDispatcher

The SubdomainDispatcher is taken from the Application Dispatching Flask documentation. It is WSGI middleware that looks at the subdomain of the request and returns a different application instance for each subdomain. It calls the create_app function above and passes it the appropriate configuration object for the subdomain.

class SubdomainDispatcher(object):

    def __init__(self, create_app, domain=''):
        """
        :param create_app: a function that returns a `flask.Flask` instance
        :param domain: str - used to determine the subdomain
        """
        self.create_app = create_app
        self.domain = domain
        self.lock = Lock()
        self.instances = {}

    def __call__(self, environ, start_response):
        app = self._get_application(environ['HTTP_HOST'])
        return app(environ, start_response)

    def _get_application(self, host):
        host = host.split(':')[0]
        assert host.endswith(self.domain), 'Configuration error'
        subdomain = host[:-len(self.domain)].rstrip('.')
        with self.lock:
            app = self.instances.get(subdomain)
            if app is None:
                configobj = self._get_subdomain_based_config(subdomain)
                app = self.create_app(configobj=configobj)
                self.instances[subdomain] = app
            return app

    @staticmethod
    def _get_subdomain_based_config(subdomain):

        class Config(object):
            pass
        config = Config()

        if subdomain == 'dev':
            config.API_HOST = 'dev-host'
            config.DB_SERVER = 'dev-db-server'
        elif subdomain == 'qa':
            config.API_HOST = 'qa-host'
            config.DB_SERVER = 'qa-db-server'

        return config

rundevserver

rundevserver is similar to flask.Flask.run but uses the SubdomainDispatcher middleware before calling werkzeug.serving.run_simple.

def rundevserver(host=None, port=None, domain='', debug=True, **options):
    """
    Modified from `flask.Flask.run`

    Runs the application on a local development server.

    :param host: the hostname to listen on. Set this to ``'0.0.0.0'`` to
                 have the server available externally as well. Defaults to
                 ``'127.0.0.1'``.
    :param port: the port of the webserver. Defaults to ``5000``
    :param domain: used to determine the subdomain
    :param debug: if given, enable or disable debug mode.
                  See :attr:`debug`.
    :param options: the options to be forwarded to the underlying
                    Werkzeug server. See
                    :func:`werkzeug.serving.run_simple` for more
                    information.
    """
    from werkzeug.serving import run_simple

    if host is None:
        host = '127.0.0.1'
    if port is None:
        port = 5000
    options.setdefault('use_reloader', debug)
    options.setdefault('use_debugger', debug)

    app = SubdomainDispatcher(create_app, domain, debug=debug)

    run_simple(host, port, app, **options)

Usage

  1. Add the following to your hosts file (/etc/hosts on Ubuntu):
    0.0.0.0 dev.localhost
    0.0.0.0 qa.localhost
  2. Run the local dev server:
    if __name__ == '__main__':
        rundevserver(host='0.0.0.0', port=5000, domain='localhost')
    
  3. Use the following URLs to get different app configurations:

Github code

A full working example is located on github: flask-subdomain-dispatcher-example

How to add a margin around markers in the Google Static Maps API using Python

This example shows how to use Python to generate a Google Static Map URL for a map that contains markers within some dimensions which are smaller than the map image dimensions. This effectively allows for setting minimum X and Y margins around the markers in a map. This is useful for a "fluid" web design where a maximum map size is requested from Google and is then cut off at the edges for small browser windows.

The bulk of this solution is based on the Javascript code here: http://stackoverflow.com/questions/6048975/google-maps-v3-how-to-calculate-the-zoom-level-for-a-given-bounds

import math


def generate_map_url(
        min_map_width_px,
        max_map_width_px,
        min_map_height_px,
        max_map_height_px,
        marker_groups):
    """
    Return a Google Static Map URL for a map that contains markers within
    some dimensions which are smaller than the map image dimensions. This
    effectively allows for setting minimum X and Y margins around the markers
    in a map. This is useful for a "fluid" web design where a maximum map
    size is requested from Google and is then cut off at the edges for
    small browser windows.
    """
    # Determine the maximum zoom to contain markers at the minimum map size
    lat_list = [
        lat for markers in marker_groups for lat, lng in markers['lat_lng']]
    lng_list = [
        lng for markers in marker_groups for lat, lng in markers['lat_lng']]
    max_zoom = get_zoom_to_fit(
        min(lat_list), max(lat_list), min(lng_list), max(lng_list),
        min_map_width_px, min_map_height_px,
    )

    # Build the markers query string arguments
    markers_args = ''
    for markers in marker_groups:
        lat_lng = '|'.join([
            '{},{}'.format(lat, lng) for lat, lng in markers['lat_lng']])
        markers_args += '&markers;=color:{}|{}'.format(markers['color'], lat_lng)

    # Build and return the map URL
    return ''.join([
        'http://maps.googleapis.com/maps/api/staticmap',
        '?sensor=false&v;=3&visual;_refresh=true',
        '&size;={}x{}&zoom;={}'.format(
            max_map_width_px, max_map_height_px, max_zoom),
        markers_args,
    ])


def get_zoom_to_fit(min_lat, max_lat, min_lng, max_lng, width_px, height_px):
    """
    Return the maximum zoom that will fit the given min/max lat/lng
    coordinates in a map of the given dimensions. This is used to
    override the zoom set by Google's implicit positioning.

    Calculation translated from Javascript to Python from:
    http://stackoverflow.com/questions/6048975/google-maps-v3-how-to-calculate-the-zoom-level-for-a-given-bounds
    """
    GOOGLE_WORLD_WIDTH = 256
    GOOGLE_WORLD_HEIGHT = 256
    MAX_ZOOM = 17

    def lat2rad(lat):
        sinlat = math.sin(math.radians(lat))
        radx2 = math.log((1 + sinlat) / (1 - sinlat)) / 2.0
        return max(min(radx2, math.pi), -math.pi) / 2.0

    def zoom(map_px, world_px, fraction):
        # Use int() to round down to the nearest integer
        return int(
            math.log(float(map_px) / float(world_px) / fraction)
            / math.log(2.0)
        )

    # Determine the maximum zoom based on height and latitude
    if min_lat == max_lat:
        lat_zoom = MAX_ZOOM
    else:
        lat_fraction = (lat2rad(max_lat) - lat2rad(min_lat)) / math.pi
        lat_zoom = zoom(height_px, GOOGLE_WORLD_HEIGHT, lat_fraction)

    # Determine the maximum zoom based on width and longitude
    if min_lng == max_lng:
        lng_zoom = MAX_ZOOM
    else:
        lng_range = max_lng - min_lng
        if lng_range < 0:
            lng_range += 360.0
        lng_fraction = lng_range / 360.0
        lng_zoom = zoom(width_px, GOOGLE_WORLD_WIDTH, lng_fraction)

    return min(lat_zoom, lng_zoom, MAX_ZOOM)

Here is an example:

map_url = generate_map_url(
    min_map_width_px=240, max_map_width_px=380,
    min_map_height_px=285, max_map_height_px=325,
    marker_groups=[
        {'color': 'blue',
         'lat_lng': [(34.0993, -118.8394)]},
        {'color': 'orange',
         'lat_lng': [
             (34.3997, -119.2002),
             (34.5389, -118.4499),
             (34.0983, -118.1285),
             (33.5932, -117.9455),
             (33.8322, -117.3958),
         ]}
    ]
)
print map_url

Here is a map without the margin: http://maps.googleapis.com/maps/api/staticmap?sensor=false&v;=3&visual;_refresh=true&size;=380x325&markers;=color:blue|34.0993,-118.8394&markers;=color:orange|34.3997,-119.2002|34.5389,-118.4499|34.0983,-118.1285|33.5932,-117.9455|33.8322,-117.3958

Here is the result with the margin: http://maps.googleapis.com/maps/api/staticmap?sensor=false&v;=3&visual;_refresh=true&size;=380x325&zoom;=7&markers;=color:blue|34.0993,-118.8394&markers;=color:orange|34.3997,-119.2002|34.5389,-118.4499|34.0983,-118.1285|33.5932,-117.9455|33.8322,-117.3958

Do you have a lot of short, single-use, private functions in your Python code?

Do you have a lot of short, single-use, private functions in your Python code? For example, below is some stubbed out authentication code I've been working on. It checks if a user's password is correct and updates the hash algorithm to use bcrypt. The 4 private functions with the leading underscore are from 1 to 10 lines long and are only used by the check_password function. These functions are part of a larger module with about 20 functions. I don't like that these 4 functions add clutter to the module and are not grouped with the function that uses them, check_password.

def _get_password_hash_from_db(email_address):
    """Get the user's password hash from the database.
    """


def _determine_password_hash_algorithm(password_hash):
    """Determine the hash algorithm.
    """


def _hash_password_old(password):
    """This is the OLD password hash algorithm.
    """


def _hash_existing_password_bcrypt(password, db_password_hash):
    """This is the NEW algorithm used for hashing existing passwords.
    """


def check_password(email_address, password):
    """Check if a user's supplied password is correct.
    """
    db_password_hash = _get_password_hash_from_db(email_address)
    hash_alg = _determine_password_hash_algorithm(db_password_hash)
    if hash_alg == 'BCRYPT':
        input_password_hash = _hash_existing_password_bcrypt(password, db_password_hash)
    else:
        input_password_hash = _hash_password_old(password)
    password_correct = (input_password_hash == db_password_hash)
    if password_correct and hash_alg != 'BCRYPT':
        call_change_password(email_address, password)
    return password_correct


def call_change_password(email_address, new_password):
    """Change the user's password.
    """

Sometimes, in cases like this, I move the 4 private functions to be nested functions inside check_password. I like how the functions are grouped together and that the module is not littered with extraneous functions. However, the inner functions are not easily testable and I don't see many people doing this.

def check_password(email_address, password):
    """Check if a user's supplied password is correct.
    """

    def get_password_hash_from_db(email_address):
        """Get the user's password hash from the database.
        """

    def determine_password_hash_algorithm(password_hash):
        """Determine the hash algorithm.
        """

    def hash_password_old(password):
        """This is the OLD password hash algorithm.
        """

    def hash_existing_password_bcrypt(password, db_password_hash):
        """This is the NEW algorithm used for hashing existing passwords.
        """

    db_password_hash = get_password_hash_from_db(email_address)
    hash_alg = determine_password_hash_algorithm(db_password_hash)
    if hash_alg == 'BCRYPT':
        input_password_hash = hash_existing_password_bcrypt(password, db_password_hash)
    else:
        input_password_hash = hash_password_old(password)
    password_correct = (input_password_hash == db_password_hash)
    if password_correct and hash_alg != 'BCRYPT':
        call_change_password(email_address, password)
    return password_correct


def call_change_password(email_address, new_password):
    """Change the user's password.
    """

Another option is to create a PasswordChecker class instead. This seems the most powerful and now the private methods are testable. However, this adds more overhead and I hear Jack Diederich telling me to Stop Writing Classes!

class _PasswordChecker(object):
    """Check if a user's supplied password is correct.
    """

    @staticmethod
    def _get_password_hash_from_db(email_address):
        """Get the user's password hash from the database.
        """

    @staticmethod
    def _determine_password_hash_algorithm(password_hash):
        """Determine the hash algorithm.
        """

    @staticmethod
    def _hash_password_old(password):
        """This is the OLD password hash algorithm.
        """

    @staticmethod
    def _hash_existing_password_bcrypt(password, db_password_hash):
        """This is the NEW algorithm used for hashing existing passwords.
        """

    def __call__(self, email_address, password):
        db_password_hash = self._get_password_hash_from_db(email_address)
        hash_alg = self._determine_password_hash_algorithm(db_password_hash)
        if hash_alg == 'BCRYPT':
            input_password_hash = self._hash_existing_password_bcrypt(password, db_password_hash)
        else:
            input_password_hash = self._hash_password_old(password)
        password_correct = (input_password_hash == db_password_hash)
        if password_correct and hash_alg != 'BCRYPT':
            call_change_password(email_address, password)
        return password_correct


check_password = _PasswordChecker()


def call_change_password(email_address, new_password):
    """Change the user's password.
    """

Maybe the solution is to break up the module into smaller modules which act like the class above? However this might leave me with some unevenly sized modules. How do you handle this?

How to expose a Flask local development server to the public using SSH remote port forwarding

Here is how to run a Flask local development server on your local machine and expose it to the public via a remote server you have control over. This uses SSH remote port forwarding which is a converse of local port forwarding described here: How to run a Django local development server on a remote machine and access it in your browser on your local machine using SSH port forwarding

  1. On the remote host, edit the sshd_config file (mine was located at /etc/ssh/sshd_config) to allow remote hosts to connect to ports forwarded for the client:
    GatewayPorts yes
  2. On the remote host, restart the SSH server:
    $ sudo service sshd restart 
    
  3. On the local host, SSH to the remote host:
    $ ssh -v -R 50051:localhost:5000 eliot@my.remotehost.com 
    
  4. On the local host, run the Flask dev server:
    $ python runserver.py localhost 5000 
    
  5. Go to http://my.remotehost.com:50051 in the browser

Using RemoteForward in your ~/.ssh/config

You can also achieve the same results by using the RemoteForward in your ~/.ssh/config file:

Host myremote
  User eliot
  HostName my.remotehost.com
  RemoteForward 50051 localhost:5000

References

See also

localtunnel by Jeff Lindsay exposes your local development server without requiring a public remote server.