Using lesscss with Pyramid and Fanstatic

We all know twitter bootstrap. It became really popular in the past few months. We saw it all over the internet with same header and same colors.

In this post I'll show you how to customize twitter bootstrap with pyramid and Fanstatic. In other words, how to use lesscss (the language used by bootstrap) with pyramid.

lesscss allow to write dynamic css stylesheets with a specific language. You can find a lot of examples and documentation on their website.

In this example we will use:

Ok. Let's go!

Create a virtualenv and install a bunch of packages:

$ virtualenv myenv
$ cd myenv
$ source bin/activate
$ pip install pyramid_fanstatic js.lescss

Now that we have a virtualenv with all dependencies we need to create a pyramid application:

$ pcreate -s starter -s pyramid_fanstatic lesscss_example
$ cd lesscss_example

A README_FANSTATIC.txt file will show you how to finalize your installation. We need that because the pyramid_fanstatic scaffold does not redifine files like setup.py and __init__.py so you can use it with all scaffolds included in pyramid and other projects. Here is the file content:

To finalize your installation you'll need to follow those steps.

Add those line the app:main section of your development.ini:

[app:main]

fanstatic.bottom = true
fanstatic.debug = true

Add some requirements to your setup.py:

requires = ['pyramid', 'pyramid_debugtoolbar',
            'pyramid_fanstatic',
            # if you want to use lesscss
            #'js.lesscss'
            ]

Also add those entry points to the same file:

# Fanstatic resource library
[fanstatic.libraries]
lesscss_example = lesscss_example.resources:library

# A console script to serve the application and monitor static resources
[console_scripts]
pserve-fanstatic = lesscss_example.resources:pserve

You also need to add pyramid_fanstatic tween to your applition. Add the following to your __init__.py file:

config.include('pyramid_fanstatic')

Run python setup.py develop to get the pserve-fanstatic script available.

That's it. Once all those steps are done we can have a look at the resources.py created by pyramid_fanstatic scaffold. Here is a modified version to activate the main.less resource:

from fanstatic import Library
from js.lesscss import LessResource

library = Library('lesscss_example', 'resources')

less_resource = LessResource(library, 'main.less')


def pserve():
    """A script aware of static resource"""
    import pyramid.scripts.pserve
    import pyramid_fanstatic
    import os

    dirname = os.path.dirname(__file__)
    dirname = os.path.join(dirname, 'resources')
    pyramid.scripts.pserve.add_file_callback(
                pyramid_fanstatic.file_callback(dirname))
    pyramid.scripts.pserve.main()

The trick is to use a LessResource instead of the standard Fanstatic Resource. This class compile the .less file at init time. The pserve() function is the target of our pserve-fanstatic entry point. It run pyramid's pserve script after adding the Fanstatic resources to the list of files watched by the monitor used to reload the application when a file changed. Don't forget the --reload option when running this script else the monitor is not initialized.

Modify the main.less file in the resources directory:

$ cat lesscss_example/resources/main.less                                                                    ✹
// main lesscss style sheet for lesscss_example

@color: #ccc;
@border: thin solid black;

blockquote {background-color: @color; border: @border;}

Ok, we are ready to build our first view. Just add this to your __init__.py:

from lesscss_example.resources import less_resource


def home(request):
    """a simple view to test our resource"""
    less_resource.need()
    return {}

Fanstatic use a thread local registry to store the resource used by a request so the only thing needed is to use the resource method .need() and this resource will be injected in your html.

Let's register the view and it's associated route:

config.add_route('home', '/')
config.add_view(home, route_name='home', renderer='templates/mytemplate.pt')

mytemplate.pt is a very basic template with an empty <head />. Fanstatic will inject resources tags for you. You remember that ?

Easy, right ? Check the result with pserve development.ini. At this time nothing should work. Why ? Because we want to use lessc and we dont have it.

I'll not cover node.js / less installation here. You can find a lot of tutorial on other places. You can also find a buildout.cfg to get lessc in your buildout at the bottom of this post. Notice than lesscss only work with node 0.4.12 for me at the moment.

pserve-Fanstatic will try to find lessc binary in the following directories:

Once you got a lessc binary, run pserve-fanstatic:

$ pserve-fanstatic --reload development.ini

This will run the app in "compile" mode. This mean that the .less is compiled when you are starting the server but the browser will use the .less file. When you are ok with the result, restart the server with the standard pserve script. Your browser will use a compiled version of the .less file (.less.min.css).

You can have a look at the minified resource:

$ cat lesscss_example/resources/main.less.min.css
blockquote{background-color:#cccccc;border:thin solid #000000;}

That's nice. How about twitter bootstrap ? Let's try it. Checkout bootstrap in your resources/ directory:

$ git clone https://github.com/twitter/bootstrap.git lesscss_example/resources/bootstrap

And register bootstrap.less in the Fanstatic registry. Add this to your resources.py file:

bootstrap = LessResource(library, 'bootstrap/lib/bootstrap.less')

Register a view to test the new resource:

from lesscss_example.resources import bootstrap


def home2(request):
    """a simple view who use bootstrap"""
    bootstrap.need()
    return {}


config.add_route('bootstrap', '/bootstrap')
config.add_view(home2, route_name='bootstrap',
                      renderer='templates/mytemplate.pt')

And thats it. Run pserve-fanstatic and go to /bootstrap. Play with the variables.less and have fun. You'll get a bootstrap.less.min.css usable in production.

As a bonus I'll show how to use buildout to make all that stuff easier. This will install node.js and lessc so it can be used by pserve-fanstatic.

[buildout]
# run eggs, pserve and node parts
parts = eggs node
# set our project as a develop egg
develop = .
# dont check for new releases
newest = false
# prefer stable release (only buildout can do that!)
prefer-final = true

[eggs]
# the eggs part will install all dependencies and setup our application in
# development mode
recipe = z3c.recipe.scripts
eggs =
    pyramid
    lesscss_example

[node]
# install node.js + lessc
recipe = gp.recipe.node
# you can use an existing node binary
#binary = /usr/local/bin/lessc
npms = less
scripts = lessc

That's all... All this code is available as a github repository.