Feeds

Asko Soukka: Cross-browser test your Plone add-on with Robot Framework, Travis-CI and Sauce Labs

By noreply@blogger.com (Asko Soukka) from Planet Plone. Published on May 19, 2013.

Thanks to Rok Garbas, I became aware of Sauce Labsduring the Plone testing sprint.

Finally, I had some time to try it myself, and I managed to make it work pretty well with Robot Framework and Travis-CI: travis saucelabs

I try to start from the very beginning, but if you already have Robot Framework tests, or even Travis-CI-integration, you could just skip these initial steps.

Bootstrap Templer

Create buildout directory for Templer installation:


$ mkdir templer-buildout
$ cd templer-buildout/

Get bootstrap.py:


$ curl -o http://downloads.buildout.org/2/bootstrap.py

Create buildout.cfg:


[buildout]
parts = templer

[templer]
recipe = zc.recipe.egg
eggs =
templer.core
templer.plone

Run bootstrap and buildout to install Templer:


$ python bootstrap.py
$ bin/buildout

Create a new product with Templer

Call the buildout-installed to create a new product with Robot Framework test example:


$ templer-buildout/bin/templer plone_basic example.product

Be careful to answer true for the following question about including Robot test templates:


robot tests (should the default robot test be included) [false]: true

Run buildout:


$ cd example.product
$ python bootstrap.py --distribute
$ bin/buildout

Update Robot Framework tests to be Selenium grid ready

Using Sauce Labs with Robot Framework (Selenium library) is similar to using robot with your own selenium grid. It mainly requires making the browser opening keyword configurable with a few selected variables.

Update src/example/product/tests/robot_test.txt with:


*** Settings ***

Library Selenium2Library timeout=10 implicit_wait=0.5

Suite Setup Start browser
Suite Teardown Close All Browsers

*** Variables ***

${
ZOPE_HOST} localhost
${
ZOPE_PORT} 55001
${
ZOPE_URL} http://${ZOPE_HOST}:${ZOPE_PORT}

${
PLONE_SITE_ID} plone
${
PLONE_URL} ${ZOPE_URL}/${PLONE_SITE_ID}

${
BROWSER} Firefox
${
REMOTE_URL}
${
DESIRED_CAPABILITIES} platform:Linux
${
BUILD_NUMBER} manual

*** Test Cases ***

Plone site
[
Tags] start
Go to ${PLONE_URL}
Page should contain Plone site

*** Keywords ***

Start browser
${
BUILD_INFO} = Set variable
...
build:${BUILD_NUMBER},name:${SUITE_NAME} | ${TEST_NAME}
Open browser ${PLONE_URL} ${BROWSER}
...
remote_url=${REMOTE_URL}
...
desired_capabilities=${DESIRED_CAPABILITIES},${BUILD_INFO}

Let me explain what all those variables are about:

  • ZOPE_HOST should match the host for which ZServer is started during the test setup (ZServer host is configured with ZSERVER_HOST-environment variable. It defaults to localhost.
  • ZOPE_PORT should match the port number which ZServer is started to listen during the test setup (ZServer pot is configured with ZSERVER_PORT-environment variable. It defaults to 55001, but we reconfigure it later by environment variables with one of the ports currently supported by Sauce Labs.
  • ZOPE_URL is a convenience variable for accessing Zope application root.
  • PLONE_SITE_ID is the Plone portal object id (and path name) for the test site. It default to plone, but it can be configured with PLONE_SITE_ID-environment variable. The default should be ok for most cases.
  • PLONE_URL is a convenience variable for accessing the Plone site front-page.
  • BROWSER selects the browser to run the tests with. The supported values depend on Selenium Python-package and can also be read from the documentation of Open browser-keyword in Selenium2Library keywordsdocumentation.
  • REMOTE_URL enables testing with Selenium grid by defining the url of the Selenium hub to use.
  • DESIRED_CAPABILITIES is used to pass various extra parameters for Selenium hub (e.g. the browser version to use or test metadata).
  • BUILD_NUMBER is used to identify the Travis-CI build on Sauce Labs.

When robot tests for Plone are run using bin/test, all the variables above can be overridden by defining corresponding ROBOT_-prefixed environment variable (e.g. ROBOT_REMOTE_URL).

Add Travis-CI configuration with Sauce Labs -support

There are a few steps in adding Travis-CI-support into your product.

At first, create travis.cfg to do the required magic for minimizing buildout-time and setting a few required environment variables. Thanks to the great community, we can just extend a public template:


[buildout]
extends =
https://raw.github.com/collective/buildout.plonetest/master/travis-4.x.cfg


package-name = example.product
package-extras = [test]

allow-hosts +=
code.google.com
robotframework.googlecode.com


[versions]
plone.app.testing = 4.2.2

[environment]
ZSERVER_PORT= 8080
ROBOT_ZOPE_PORT= 8080

[test]
environment = environment

Create .travis.yml for letting Travis-CI to know how the environment should be set up and the tests run:


---
language: python
python: '2.7'
install:
- mkdir -p buildout-cache/downloads
- python bootstrap.py -c travis.cfg
- bin/buildout -N -t 3 -c travis.cfg
- curl -O http://saucelabs.com/downloads/Sauce-Connect-latest.zip
- unzip Sauce-Connect-latest.zip
- java -jar Sauce-Connect.jar $SAUCE_USERNAME $SAUCE_ACCESS_KEY -i $TRAVIS_JOB_ID
-f CONNECTED &
- JAVA_PID=$!
before_script:
- bash -c "while [ ! -f CONNECTED ]; do sleep 2; done"
script: bin/test
after_script:
- kill $JAVA_PID
env:
global:
- ROBOT_BUILD_NUMBER=travis-$TRAVIS_BUILD_NUMBER
- ROBOT_REMOTE_URL=http://$SAUCE_USERNAME:$SAUCE_ACCESS_KEY@ondemand.saucelabs.com:80/wd/hub
matrix:
- ROBOT_BROWSER=firefox ROBOT_DESIRED_CAPABILITIES=tunnel-identifier:$TRAVIS_JOB_ID
- ROBOT_BROWSER=ie ROBOT_DESIRED_CAPABILITIES=tunnel-identifier:$TRAVIS_JOB_ID
- ROBOT_DESIRED_CAPABILITIES="platform:OS X 10.8,browserName:iPad,version:6,tunnel-identifier:$TRAVIS_JOB_ID"

Let me describe:

  1. Lines 4-7: Run buildout.
  2. Lines 8-14: Download and start Sauce Connect.
  3. Line 15: Run tests.
  4. Lines 16-17: Shutdown Sauce Connect.
  5. Lines 18-21: Define required environment variables for letting robot to use Sauce Labs.
  6. Lines 22-25: Define build matrix for running the tests with Sauce Labs' default Firefox, Internet Explorer and Mobile Safari. tunnel-identifier-stuff is required for Sauce Labs to allow more than one simultaneous tunnels for the same user account.

Next, define your Sauce Labs username and access key as secret, encrypted, environment variables SAUCE_USERNAME and SAUCE_ACCESS_KEY.

Currently, Sauce Labs offers unlimited free subscription with three simultaneous connections (e.g. running tests for three different browsers at the same time) for Open Source projects. Just make sure to register the account for your project, not yourself. Public repository url is required for the creating the account and it cannot be changed afterwards.

  1. Install Travis gem for Ruby (and install Ruby before that when required):


    $ gem install travis # or sudo gem ...
  2. use travis-command to insert encrypted environment variables into the product's .travis.yml:


    $ travis encrypt SAUCE_USERNAME=myusername -r mygithubname/example.product --add env.global
    $ travis encrypt SAUCE_ACCESS_KEY=myaccesskey -r mygithubname/example.product --add env.global

Make sure to use your own Sauce Labs username and access key, and your product's Github-repository path (with format username/repo).

Finally, enable Travis-CI-tests for you product either at Travis-CI.org or at GitHub.

Done. If I forgot something, I'll update this post.

Behind the basics: Test level status reporting for Sauce Labs

By default, Sauce Labs doesn't really know did the Selenium tests on it pass or fail. To pass that information from our test runner on Travis-CI to Sauce Labs, we need to add some extra code into our test setup.

At first, append the following into the end of src/example/product/testing.py:


import re
import os
import httplib
import base64
try:
import json
assert json # pyflakes
except ImportError:
import simplejson as json

from robot.libraries.BuiltIn import BuiltIn

USERNAME_ACCESS_KEY = re.compile('^(http|https):\/\/([^:]+):([^@]+)@')


class Keywords:
def report_sauce_status(self, status, tags=[], remote_url=''):
job_id = BuiltIn().get_library_instance(
'Selenium2Library')._current_browser().session_id

if USERNAME_ACCESS_KEY.match(remote_url):
username, access_key =\
USERNAME_ACCESS_KEY.findall(remote_url)[0][1:]
else:
username = os.environ.get('SAUCE_USERNAME')
access_key = os.environ.get('SAUCE_ACCESS_KEY')

if not job_id:
return u"No Sauce job id found. Skipping..."
elif not username or not access_key:
return u"No Sauce environment variables found. Skipping..."

token = base64.encodestring('%s:%s' % (username, access_key))[:-1]
body = json.dumps({'passed': status == 'PASS',
'tags': tags})

connection = httplib.HTTPConnection('saucelabs.com')
connection.request('PUT', '/rest/v1/%s/jobs/%s' % (
username, job_id), body,
headers={'Authorization': 'Basic %s' % token}
)
return connection.getresponse().status

This code defines a custom Robot Framework keyword library with a keyword for passing the test status (and other information) back to Sauce Labs.

Next, we update src/example/product/tests/robot_test.txt to store the session id during the setup of every test and send the test result back to Sauce Labs during the teardown of every test:


*** Settings ***

Library Selenium2Library timeout=10 implicit_wait=0.5
Library example.product.testing.Keywords

Test Setup Start browser
Test Teardown Run keywords Report test status Close All Browsers

*** Variables ***

${
ZOPE_HOST} = localhost
${
ZOPE_PORT} = 55001
${
ZOPE_URL} = http://${ZOPE_HOST}:${ZOPE_PORT}

${
PLONE_SITE_ID} = plone
${
PLONE_URL} = ${ZOPE_URL}/${PLONE_SITE_ID}

${
BROWSER} = Firefox
${
REMOTE_URL} =
${
DESIRED_CAPABILITIES} = platform:Linux
${
BUILD_NUMBER} = manual

*** Test Cases ***

Plone site
[
Tags] start
Go to ${PLONE_URL}
Page should contain Plone site

*** Keywords ***

Start browser
${
BUILD_INFO} = Set variable
...
build:${BUILD_NUMBER},name:${SUITE_NAME} | ${TEST_NAME}
Open browser ${PLONE_URL} ${BROWSER}
...
remote_url=${REMOTE_URL}
...
desired_capabilities=${DESIRED_CAPABILITIES},${BUILD_INFO}

Report test status
Report sauce status ${TEST_STATUS} ${TEST_TAGS} ${REMOTE_URL}

Please, note how we had to replace suite setup and teardown with test setup and teardown) to open a new Selenium session for every test.

This worked for me. I hope it works for you too.

example.product is available at: https://github.com/datakurre/example.product

Asko Soukka: Speed up your Plone add-on tests on Travis CI with the Unified Installer

By noreply@blogger.com (Asko Soukka) from Planet Plone. Published on May 19, 2013.

Many thanks for Héctor Verlarde for encouraging us to try out Travis CI for testing our own Plone add-ons. Also, thanks for Godefroid Chapelle for showing me, how to run Selenium tests on a headless server, e.g. on Travis CI.

As you may already know, the main issue in testing Plone add-ons on Travis CI is its strict 15 minute time limit on running your test suite. And as you may also know, 15 minutes is not much time for our dear buildout to gather all the required dependencies of Plone or plone.app.testing, and still run our test after the buildout.

As expected, neither did I get far without having issues with the time limit. And for some reason, I couldn't get the earlier solutions to work for me. Eventually, I found out a new solution, surprisingly, with the help of Plone Unified Installer.

Because Plone Unified Installer comes in a single downloadable package and includes a complete buildout-cache usable also in a test buildout, I realized, that it could speed up my test buildout a lot, and it did. Yet, with Plone 4.3 shipping with Dexterity, I would expect it to speed it up even more.

Update: The method described here is adopted as part of buildout.plonetest, which includes more generic configuration to work with all Plone-versions.

Enough talk. Here's my setup:

buildout.cfg


[buildout]
extends = http://dist.plone.org/release/4.2.1/versions.cfg
develop = .
parts = test
versions = versions

[test]
recipe = zc.recipe.testrunner
eggs = my_package[test]

Nothing special here. I expect setup.py of the tested package to include complete extras_require={'test': ... } with all the required dependencies for testing.

So, on a local machine, python bootstrap.py, bin/buildout and bin/test combo should run tests for a freshly cloned package repository just as expected.

travis.cfg


[buildout]
extends = buildout.cfg
parts =
download
install
test

eggs-directory = buildout-cache/eggs
download-cache = buildout-cache/downloads

[download]
recipe = hexagonit.recipe.download
url = https://launchpad.net/plone/4.2/4.2.1/+download/Plone-4.2.1-UnifiedInstaller.tgz

[install]
recipe = collective.recipe.cmd
on_install = true
cmds = tar jxvf ${download:location}/Plone-4.2.1-UnifiedInstaller/packages/buildout-cache.tar.bz2 1>/dev/null

Here's the magic for re-using Plone Unified Installer for your test buildout:

  1. At first, download and unpack the installer in [download] part
  2. then extract its buildout-cache in [install] part into the locations defined in [buildout] part.

As you might guessed, after this, buildout needs to download only the extra requirements of the tested package! Long live Plone Unified Installer!

.travis.yml


language: python
python: "2.7"
install:
- mkdir -p buildout-cache/downloads
- python bootstrap.py -c travis.cfg
- bin/buildout -N -t 3 -c travis.cfg
script: bin/test

Note, how we need to create a buildout-cache-directory for downloads as defined earlier in travis.cfg. The rest should be easy: we just do the bootstrap and run our tests with sane buildout-options, and... that's all.

.travis.yml for robotsuite

Oh, in the beginning, I mentiond about learning something important from Godefroid. Well, if you have followed me on creating zope.testrunner-compatible Robot Framework -tests with plone.app.testing, you only need to add a few extra lines to make your Robot Framework tests runnable on Travis CI:


language: python
python: "2.7"
install:
- mkdir -p buildout-cache/downloads
- python bootstrap.py -c travis.cfg
- bin/buildout -N -t 3 -c travis.cfg
before_script:
- "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"
script: bin/test

If you think, this is cool, please, give some love for the Travis CI team!

travis

Asko Soukka: Getting started with Robot Framework and plone.app.testing

By noreply@blogger.com (Asko Soukka) from Planet Plone. Published on May 19, 2013.

Selenium testing Plone doesn't need to be difficult. Actually, with the recent hard work done for robotframework-selenium2libraryit's the easiest way to test your add-ons! (Thanks a lot to these folks!)

I'll show you, how to create your first zope.testrunnercompatible Robot Framework tests for your custom Plone add-on. Also, everything you already know about plone.app.testing, zope.testrunner or Python unittest-library, should apply here.

Update: An up-to-date documentation for writing Robot Framework tests for Plone is available as part of Plone Developer Documentation.

Environment

Here's our dummy Plone add-on package with its testing buildout:


bootstrap.py
buildout.cfg
CHANGES.txt
README.txt
setup.py
src
src/my
src/my_package
src/my_package/__init__.py
src/my_package/tests
src/my_package/tests/__init__.py
src/my_package/tests/test_robot.py
src/my_package/tests/test_accessibility.robot

We've got bootstrap.py, empty text files for README and CHANGES, and the following setup.pyto define our (empty) add-on package:


from setuptools import setup, find_packages

version = "1.0.0"

setup(
name="my-package",
version=version,
description="An example Plone add-on",
long_description=(open("README.txt").read() + "\n" +
open("CHANGES.txt").read()),
# Get more strings from
# http://pypi.python.org/pypi?%3Aaction=list_classifiers
classifiers=[
"Programming Language :: Python",
],
keywords="",
author="",
author_email="",
url="",
license="GPL",
packages=find_packages("src", exclude=["ez_setup"]),
package_dir={"": "src"},
include_package_data=True,
zip_safe=False,
install_requires=[
"setuptools",
],
extras_require={"test": [
"plone.app.testing",
"rootsuite",
"robotframework-selenium2library",
]},
entry_points="""
# -*- Entry points: -*-
[z3c.autoinclude.plugin]
target = plone
"""

)

Note, how we've defined test-extras for our package to require robotsuite and robotframework-selenium2librarypackages in addition to the usual plone.app.testing.

And here's our buildout.cfg to set up the test runner:


[buildout]
extends = http://dist.plone.org/release/4.2-latest/versions.cfg
parts = test
develop = .

[test]
recipe = zc.recipe.testrunner
eggs = my-package [test]

Test suite

Let's write our first test suite in Robot Framework syntax into src/my_package/tests/test_accessibility.robot:


*** Settings ***

Library Selenium2Library timeout=10 implicit_wait=0.5

Suite Setup Start browser
Suite Teardown Close All Browsers

*** Test Cases ***

Plone Accessibility
Goto homepage
Click link Accessibility
Page should contain Accessibility Statement

*** Keywords ***

Start browser
Open browser http://localhost:55001/plone/

Goto homepage
Go to http://localhost:55001/plone/
Page should contain Plone site

Note, how we import and configure Selenium2Library, and how we expect Plone to be found at http://localhost:55001/plone/. That's how plone.app.testing serves it.

Robotsuite

The last step is to glue our Robot Framework test suite and plone.app.testing together. That's done with robotsuite-package by defining new a RobotTestSuite with the default PLONE_ZSERVER-layer from plone.app.testing in src/my_package/tests/test_robot.py:


import unittest

import robotsuite
from plone.app.testing import PLONE_ZSERVER
from plone.testing import layered


def test_suite():
suite = unittest.TestSuite()
suite.addTests([
layered(robotsuite.RobotTestSuite("test_accessibility.robot"),
layer=PLONE_ZSERVER),
])
return suite

If you have ever defined a Python doctest test suite to be used with plone.app.testing, the above should look very familiar.

Running

With everything above in place, just run:

  1. bootstrap (with a Plone-compatible Python or virtualenv)


    $ python bootstrap.py
  2. buildout


    $ bin/buildout
  3. and test


    $ bin/test

and you should see something like:


$ python bootstrap.py
Downloading http://pypi.python.org/packages/source/d/distribute/distribute-0.6.35.tar.gz
Extracting in /var/folders/b1/mld_r9wj1jbfwf2jcfl_d61sc6kdnb/T/tmp902seC
Now working in /var/folders/b1/mld_r9wj1jbfwf2jcfl_d61sc6kdnb/T/tmp902seC/distribute-0.6.35
Building a Distribute egg in /var/folders/b1/mld_r9wj1jbfwf2jcfl_d61sc6kdnb/T/tmp69NImk
/var/folders/b1/mld_r9wj1jbfwf2jcfl_d61sc6kdnb/T/tmp69NImk/distribute-0.6.35-py2.7.egg
Creating directory '/.../bin'.
Creating directory '/.../parts'.
Creating directory '/.../develop-eggs'.
Generated script '/.../bin/buildout'.

$ bin/buildout
Develop: '/.../.'
Installing test.
...
Generated script '/.../bin/test'.

$ bin/test
Running plone.app.testing.layers.Plone:ZServer tests:
Set up plone.testing.zca.LayerCleanup in 0.000 seconds.
Set up plone.testing.z2.Startup in 0.398 seconds.
Set up plone.app.testing.layers.PloneFixture in 9.921 seconds.
Set up plone.testing.z2.ZServer in 0.506 seconds.
Set up plone.app.testing.layers.Plone:ZServer in 0.000 seconds.
Ran 1 tests with 0 failures and 0 errors in 2.969 seconds.
Tearing down left over layers:
Tear down plone.app.testing.layers.Plone:ZServer in 0.000 seconds.
Tear down plone.app.testing.layers.PloneFixture in 0.088 seconds.
Tear down plone.testing.z2.ZServer in 5.151 seconds.
Tear down plone.testing.z2.Startup in 0.009 seconds.
Tear down plone.testing.zca.LayerCleanup in 0.005 seconds.

You should also find Robot Framework logs and reports being generated into your buildout directory under parts/test.

Custom layer

Obviously, we'd like to run our test against a Plone with our own add-on installed. That requires a custom test layer, as described at plone.app.testing.

Let's start by adding a few more files:


src/my_package/configure.zcml
src/my_package/hello_world.pt
src/my_package/testing.py
src/my_package/tests/test_hello_world.robot

At first we define our custom view to be tested in src/my_package/configure.zcml:


<configure xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser">

<browser:page
name="hello-world"
for="Products.CMFCore.interfaces.ISiteRoot"
template="hello_world.pt"
permission="zope2.View"
/>

</configure>

and in src/my_package/hello_world.pt:


<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal"
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
lang="en"
metal:use-macro="context/main_template/macros/master"
i18n:domain="plone">
<body>

<metal:content-core fill-slot="content-core">
<metal:content-core define-macro="content-core">
<p>Hello World!</p>
</metal:content-core>
</metal:content-core>

</body>
</html>

Then we define our custom test layer in src/my_package/testing.py:


from plone.app.testing import (
PloneSandboxLayer,
FunctionalTesting,
PLONE_FIXTURE,
)

from plone.testing.z2 import ZSERVER_FIXTURE


class MyPackageLayer(PloneSandboxLayer):
defaultBases = (PLONE_FIXTURE,)

def setUpZope(self, app, configurationContext):
import my_package
self.loadZCML(package=my_package)


MY_PACKAGE_FIXTURE = MyPackageLayer()

MY_PACKAGE_ROBOT_TESTING = FunctionalTesting(
bases=(MY_PACKAGE_FIXTURE, ZSERVER_FIXTURE),
name="MyPackage:Robot")

Note, how we build on top of PloneSandboxLayer and how we create our final acceptance test layer by combining our custom MY_PACKAGE_FIXTURE and ZSERVER_FIXTURE. The latter would make our Plone sandbox served at http://localhost:55001/. Finally, FunctionalTesting gives us a clean isolated Plone site to be played with for each test case.

Finally, we write a new Robot Framework test suite into src/my_package/tests/test_hello_world.robot:


*** Settings ***

Library Selenium2Library timeout=10 implicit_wait=0.5

Suite Setup Start browser
Suite Teardown Close All Browsers

*** Test Cases ***

Hello World
Go to http://localhost:55001/plone/hello-world
Page should contain Hello World!

*** Keywords ***

Start browser
Open browser http://localhost:55001/plone/

We can now include our new test suite in src/my_package/tests/test_robot.py:


import unittest

import robotsuite
from my_package.testing import MY_PACKAGE_ROBOT_TESTING
from plone.app.testing import PLONE_ZSERVER
from plone.testing import layered


def test_suite():
suite = unittest.TestSuite()
suite.addTests([
layered(robotsuite.RobotTestSuite("test_accessibility.robot"),
layer=PLONE_ZSERVER),
layered(robotsuite.RobotTestSuite("test_hello_world.robot"),
layer=MY_PACKAGE_ROBOT_TESTING),
])
return suite

and re-run our tests:


$ bin/test --list-tests
Listing my_package.testing.MyPackage:Robot tests:
Hello_World (test_hello_world.robot)
Listing plone.app.testing.layers.Plone:ZServer tests:
Plone_Accessibility (test_accessibility.robot)

$ bin/test
Running my_package.testing.MyPackage:Robot tests:
Set up plone.testing.zca.LayerCleanup in 0.000 seconds.
Set up plone.testing.z2.Startup in 0.219 seconds.
Set up plone.app.testing.layers.PloneFixture in 7.204 seconds.
Set up my_package.testing.MyPackageLayer in 0.028 seconds.
Set up plone.testing.z2.ZServer in 0.503 seconds.
Set up my_package.testing.MyPackage:Robot in 0.000 seconds.
Ran 1 tests with 0 failures and 0 errors in 2.493 seconds.
Running plone.app.testing.layers.Plone:ZServer tests:
Tear down my_package.testing.MyPackage:Robot in 0.000 seconds.
Tear down my_package.testing.MyPackageLayer in 0.002 seconds.
Set up plone.app.testing.layers.Plone:ZServer in 0.000 seconds.
Ran 1 tests with 0 failures and 0 errors in 2.213 seconds.
Tearing down left over layers:
Tear down plone.app.testing.layers.Plone:ZServer in 0.000 seconds.
Tear down plone.app.testing.layers.PloneFixture in 0.091 seconds.
Tear down plone.testing.z2.ZServer in 5.155 seconds.
Tear down plone.testing.z2.Startup in 0.009 seconds.
Tear down plone.testing.zca.LayerCleanup in 0.005 seconds.
Total: 2 tests, 0 failures, 0 errors in 18.305 seconds.

Logging in

plone.app.testing defines a test user for our test site, but how could our Robot Framework test know her login credentials? Well, we have to make our test to ask for the credentials by defining custom Robot Framework test keywords in Python.

Let's add a couple of more files, as in:


src/my_package/testing_keywords.py
src/my_package/tests/test_login.robot

At first, we type our custom Robot Framework keyword library with test keywords for retrieving the test users credentials into src/my_package/testing_keywords.py:


class Keywords(object):
"""Robot Framework keyword library
"""


def get_test_user_name(self):
import plone.app.testing
return plone.app.testing.interfaces.TEST_USER_NAME

def get_test_user_password(self):
import plone.app.testing
return plone.app.testing.interfaces.TEST_USER_PASSWORD

Then, we can write our new login test into src/my_package/tests/test_login.robot:


*** Settings ***

Library Selenium2Library timeout=10 implicit_wait=0.5
Library my_package.testing_keywords.Keywords

Suite Setup Start browser
Suite Teardown Close All Browsers

*** Test Cases ***

Log in
${
TEST_USER_NAME} = Get test user name
${
TEST_USER_PASSWORD} = Get test user password
Go to http://localhost:55001/plone/login_form
Page should contain element __ac_name
Input text __ac_name ${TEST_USER_NAME}
Input text __ac_password ${TEST_USER_PASSWORD}
Click Button Log in
Page should contain element css=#user-name

*** Keywords ***

Start browser
Open browser http://localhost:55001/plone/

Note, how we can import our custom keyword library right after Selenium2Libary. Also, see how we use our custom keywords to retrieve test user's login credentials into Robot Framework test variables and how we use them later in the test.

We can now include our new test suite in src/my_package/tests/test_robot.py:


import unittest

import robotsuite
from my_package.testing import MY_PACKAGE_ROBOT_TESTING
from plone.app.testing import PLONE_ZSERVER
from plone.testing import layered


def test_suite():
suite = unittest.TestSuite()
suite.addTests([
layered(robotsuite.RobotTestSuite("test_accessibility.robot"),
layer=PLONE_ZSERVER),
layered(robotsuite.RobotTestSuite("test_hello_world.robot"),
layer=MY_PACKAGE_ROBOT_TESTING),
layered(robotsuite.RobotTestSuite("test_login.robot"),
layer=PLONE_ZSERVER),
])
return suite

and re-run our tests:


$ bin/test --list-tests
Listing my_package.testing.MyPackage:Robot tests:
Hello_World (test_hello_world.robot)
Listing plone.app.testing.layers.Plone:ZServer tests:
Plone_Accessibility (test_accessibility.robot)
Log_in (test_login.robot)

$ bin/test
Running my_package.testing.MyPackage:Robot tests:
Set up plone.testing.zca.LayerCleanup in 0.000 seconds.
Set up plone.testing.z2.Startup in 0.217 seconds.
Set up plone.app.testing.layers.PloneFixture in 7.132 seconds.
Set up my_package.testing.MyPackageLayer in 0.026 seconds.
Set up plone.testing.z2.ZServer in 0.503 seconds.
Set up my_package.testing.MyPackage:Robot in 0.000 seconds.
Ran 1 tests with 0 failures and 0 errors in 2.473 seconds.
Running plone.app.testing.layers.Plone:ZServer tests:
Tear down my_package.testing.MyPackage:Robot in 0.000 seconds.
Tear down my_package.testing.MyPackageLayer in 0.002 seconds.
Set up plone.app.testing.layers.Plone:ZServer in 0.000 seconds.
Ran 2 tests with 0 failures and 0 errors in 7.766 seconds.
Tearing down left over layers:
Tear down plone.app.testing.layers.Plone:ZServer in 0.000 seconds.
Tear down plone.app.testing.layers.PloneFixture in 0.088 seconds.
Tear down plone.testing.z2.ZServer in 5.156 seconds.
Tear down plone.testing.z2.Startup in 0.009 seconds.
Tear down plone.testing.zca.LayerCleanup in 0.005 seconds.
Total: 3 tests, 0 failures, 0 errors in 23.765 seconds.

Debugging

There's one catch in debugging your code while running Robot Framework tests. It eats your standard input and output, which prevents you to just import pdb; pdb.set_trace(). Instead, you have to add a few more lines to reclaim your I/O at first, and only then let your debugger in:


import sys
for attr in ('stdin', 'stdout', 'stderr'):
setattr(sys, attr, getattr(sys, '__%s__' % attr))
import pdb; pdb.set_trace()

Resources

That's all about it to get started. Have fun!

Plumi: Welcome to Plumi 4.5

From Planet Plone. Published on May 18, 2013.

Plumi 4.5 was soft-launched at the beginning of the year. Now that it’s been running smoothly for a while, we’d love to introduce you to all the new features and improvements.

New User Interface

demo-screenshot-thumb

The first thing you’ll notice about Plumi 4.5 is the beautiful new skin. We’ve left the old layout inherited from older versions of Plone behind. Right out of the box you will be pleased to see a shiny new visual theme, with a grid-layout and contemporary styling, just right for a video sharing site.

On the front page of the new Plumi skin you can view all the latest videos that have been uploaded, plus feature a video in the slot on top, ready to play back using mediaelement.js player – an HTML5 player that will work in any modern browser. We’re re-worked templates throughout the site, making improvements to UI, with more planned for the future.

Diazo

You can also customise Plumi’s visual theme for your own needs, and in Plumi 4.5 it is easier using a new implementation of the Diazo theming engine and plone.app theming. Diazo allows you to apply a theme contained in a static HTML web page to a dynamic website created using any server-side technology. With Diazo, you can take an HTML wireframe created by a web designer and turn it into a theme for Plumi.

Mobile Friendly Adaptive Layout

The site is designed to adapt to different screen sizes, and videos will play back on both Android and iOS devices.

New Video Publishing Form

publishA new video publishing form makes it even easier for users to upload video to a Plumi site. Just drag’n'drop or click browse to select a video file, and watch it upload in the new progress indicator, while you add metadata to your video.

You can click over to another dynamically loaded page as you upload, where you can categorise the film and add a Creative Commons license.

Subtitling Using Amara

We have integrated Amara (formerly Universal Subtitles) which allows users of your Plumi site to easily add or view subtitles for each video, created or attached to the video using the Amara system. Watch the video above to learn more about how easy it is to use Amara, which is a powerful addition to Plumi in terms of accessibility, and use in multi-lingual websites.

Other Improvements

Other fixes and improvements since our last stable release (Plumi 4.4) include replacing gunicorn with uwsgi, making upload of large files more stable, removing views/downloads from the <iframe>, fixing fullscreen video playback, removal of the obsolete callouts content type, and updating mediaelement.js.

Plumi Roadmap

We are looking forward to a 4.5.1 release that may include some more work on templates, followed by 4.6 in which we plan to integrate videos that are hosted on other sites, and new features designed to enhance the ability to use Plumi for social change impact.

You can read the full list of Plumi features here.

 

4teamwork: TooManyRepositories - Manage your GitHub Subscriptions!

From Planet Plone. Published on May 17, 2013.

If you are a member of collective organisation on GitHub you know the pain: getting lots of GitHub emails for repositories you don’t care about.

The collective organisation on GitHub currently has 909 repositories, the Plone organisation has 228 – together, that’s over a thousand repositories. That’s a lot of emails!

GitHub changed the watching feature last year, which makes it a little bit better. But still, the problems when participating in hundreds of repositories are not solved for me.

There are various solutions for the problem, like using Gmail filters or clicking through the Watched Repositories view on GitHub – but who wants to unwatch a thousand repositories? Disabling automatically watch is also no option for me – I really want to know what’s going on in my employer’s organisation and the option cannot be configured per organisation.

That’s why I wrote github-watchlist, a small script for mass subscribing and unsubscribing repositories using regular expressions.

The Python script let’s you configure your subscriptions with a regular expression pipeline and automatically subscribes or unsubscribes the repositories for you.

My configuration looks like this (short version):

1
2
3
4
5
6
7
8
9
10
11
12
watchlist =
    watching:      4teamwork/.*
    watching:      jone/.*
    watching:      collective/collective.dexteritytextindexer
    watching:      collective/collective.elephantvocabulary
    watching:      collective/collective.i18nreport
    watching:      collective/collective.js.jqtooltip
    watching:      collective/collective.jsonmigrator
    watching:      collective/collective.mtrsetup
    watching:      collective/collective.z3cinspector
    watching:      collective/mrsd.el
    not-watching:  collective/.*

The list of expressions is applied top-down on most repositories I have access to and it does what you think it does :–)

After installing and configuring the github-watchlist you can simply run the update script.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ ./bin/update-watchlist
NO SUBSCRIPTION CHANGES:
 - keep not watching: collective/ArchGenXML
 - keep watching: jone/github-watchlist

SUBSCRIPTION CHANGES:
 - add subscription: 4teamwork/ftw.lawgiver
 - remove subscription: collective/collective.dancing

SUMMARY:
 - Keep watching: 1
 - Keep not watching: 1
 - Start watching: 1
 - Stop watching: 1

Continue updating subscriptions? [Yes/No]: yes
INFO create subscription: 4teamwork/ftw.lawgiver
INFO delete subscription: plone/collective.dancing

As the script is idempotent we can run it periodically with a cronjob:

1
0 0 * * *   /Users/jone/github-watchlist/bin/update-watchlist --confirmed

See the github-watchlist readme for more details and have fun with it. Give me some feedback if you use it!

Reinout van Rees: Principle philosophy - Swift

From Planet Plone. Published on May 17, 2013.

Principle philosophy: a way to discuss our rules and beliefs that govern our actions. He tells it from his personal experience.

His parents wanted to raise him as a good person. So they thought him good principles (like don't be a quitter, don't steal, etc). This is quite black/white though. We are all more gray/gray.

What about the question "how can I be a good programmer"? Programmers use logic, which sounds black/white again: write tests, don't repeat yourself. Sigh.

Talking about things like this is impossible without Immanuel Kant. He differentiates between reason and instinct. If "be happy" were our life goal, we'd just follow our instincts. So what is reason for, then, apart for doing good? Reason has to do with moral. There are three ways of looking at "doing good":

  • Duty. Good things can come from duty. Duty can also lead to non-good things, though. Hm, so this is not it.
  • Make a difference between the goal and the outcome. The outcome might be bad even though the goal could be worthy.
  • Universal lawfullness. Only do something if you know that everybody thinks it is a good idea.

Does this help with a question like "is testing good"?

Gandhi said that a man is the sum of his actions.

In a sense we are the sum of our experiences. So increase the amount of experience that you have. Either have the experiences yourself, or share them like on this conference. Everything looks different from the trenches: learn from eachother.

Some lessons he learned from a little baseball league experience (where he sucked) as a kid:

  • Swing for the fences. Aim for a home run. It allows you take great risks (because you have great goals). It motivates you.
  • Set reasonable goals, too. Incremental intermediate goals. Those intermediate goals help you progress.
  • You suck... and that's totally OK. You're not good at everything. It gives you a different perspective. And you can still give it your best. Also to that almost-unused old project that you get a bug report for.

Some take-aways:

  • Build a strong foundation of principles.
  • One size doesn't fit all
  • Learn from your experiences and share them.
  • Build a great network.
  • Ask all the right questions.

4teamwork: Our blog about Plone and Python

From Planet Plone. Published on May 16, 2013.

This blog is about Plone, Python and related topics. It reflects the personal views and opinions of its different authors of 4teamwork. If you have questions or corrections to our articles, you are invited to get in touch with us!

4teamwork is a Plone software company based in Bern, Switzerland, consisting of a young and motivated team of 15 skilled developers and consultants.

We have been around in the Plone community for quite some time: Starting at the very early stage of Plone in 2002 and 2003, we have been witnessing its overwhelming growth and maturation during the past decade. On a side note, we had the privilege of organizing the very first Plone sprint in winter 2003 in Bern — as it turns out, the original website of this historic sprint is still available here. Since then, we have been designing, developing, and maintaining sophisticated web solutions in Plone for many governmental and non profit organizations, like zg.ch (canton of Zug, Switzerland), bern.ch (capital of Switzerland) or amnesty.ch (Amnesty International Switzerland). Furthermore, we help companies setting up their own intranet and/or extranet web service using Plone.

In the past few years, we have started to make our various developments publicly available. All these modules are actively maintained and continuously developed. Feel free to browse (and use) our numerous repositories on github.com/4teamwork. Any feedback (or pull requests ;) is welcome!

IMHO, Plone and Python are still going strong – let’s see what the future will hold!

Reinout van Rees: The web of stuff - Zack Voase

From Planet Plone. Published on May 16, 2013.

A plane flew over (noisily) at the start of his presentation. He put our work in perspective by saying that that was a 80 ton plane and that we're just building websites :-)

Possibilities

Computers used to take up whole rooms, now you have a smartphone. Big data is really big data now. Moore's lawworks both ways, though, so you have really small computers now. An arduino for instance.

He often makes comparison to the human body. All over our body, sensors give off signals that go into the central nervous system. The brain processes it and gives signals back to muscles if necessary. Sensing, feedback, understanding, reaction.

Stuff can talk to the cloud. Like a sensor in your body talks to your mind, stuff can treat the cloud as a brain. The cloud is what allows small tools to be smart.

Stuff does often need a human to interact with it. Like a smartphone. There's all sorts of people thinking about how to "liberate the computers from their human overlords". Why cannot computers sense and act on their own account?

So how do you bridge the gap betwen sensing and acting of stuff? How do you use Django for it? There's a lot available online about sensing and about acting, but not the communication in between.

The communication medium itself is a bit of a problem. You don't want to have a telephone data contract for every single small piece of stuff. A physical connection isn't always handy either.

His preferred communication medium is Twilio for sending SMSs. The stuff has low memory, so the message length limit is fine.

He showed a demo with a card reader that read his London transport card and sended an SMS to his Django site. The card reader was a combination between an arduino, a 'shield' sms sender and an RFID reader. The django app then submits it to foursquare. (The last part didn't work, probably due to a local foursquare problem, but the django app did have all the data he send from his card reader). Nice.

SUCCESS: after the lightning talks he did it again and now it worked!

Personal development

He had never done any hardware work until four months ago. No compiling for arduino. It sounded a bit scary to him.

It is normal, if you start as a beginner, you're slowly getting better if you keep at something. Then you automatically learn more and thus learn that there's a lot you don't know. That's the dip in the middle. Those are the people we need to keep on board so that they push through to the expert stage.

When you're in the middle, you know how bad you are (or how good you aren't yet). That's the risky phase were people quit.

Likewise documentation. Tutorials are useful for beginners. Reference material is useful for experts. There's not a lot in the middle and you're bound to be a bit frustrated in that stage.

So if you're going to start experimenting with electronics, you're bound to hit a wall, for instance when calculating complex electronic schemas. Push on anyway: the first time you make a phone call with your own device is totally worth it.

Two books he recommends to get you started:

  • Getting started with Arduino.
  • The art of electronics.

Martijn Faassen: JS Dependency Tools Redux

From Planet Plone. Published on May 16, 2013.

Introduction

Recently I looked into JavaScript dependency management and wrote a long post about it where I was overwhelmed and found some solutions. Since then I've learned a few new things and have thought a bit more, and I thought I'd share.

My goal is still to be able to develop Obviel with smaller modules than I do now. Obviel is a client-side web framework, but besides that you should not need to know anything about it in order to understand this post.

So, I'm looking for a JavaScript dependency framework for the client-side.

AMD versus CommonJS

Last time I mentioned Asynchronous Module Definition (AMD) and how it contrasts with the CommonJS module definition. AMD wraps everything in your module in a function:

define([jquery], function($) {
   var myModule = {
      foo: function() { ... };
   };
   return myModule;
});

Whereas CommonJS uses this pattern:

var $ = require('jquery');

module.exports = { foo: function() { ... }};

Though AMD has sugar to make it look much like CommonJS plus the define() function wrapper.

AMD was designed to work in web-browsers, client-side, whereas CommonJS modules are mostly used on the server, typically in a NodeJS environment. AMD modules can be directly loaded into the browser without build step, which is an important advantage during development. AMD modules like CommonJS modules can also refer to individual modules in other packages. You could install such an AMD-based package using Bower, like you'd install a CommonJS-based package using npm.

The most well known AMD implementation is called RequireJS. There is a small implementation of it called Almond that RequireJS can use in JavaScript modules that are packaged together for release.

Recently I also learned about RaptorJS, which is a larger framework that features an extended AMD implementation as well as a separate server-side module loader. It contains some interesting ideas such as "adaptive packaging" which helps adjust codebases to different target environments (client, servers, different browsers, etc).

CommonJS on the client

Of course people have worked on bringing CommonJS modules to the client too. And have they! I ran into these so far:

May a thousand flowers bloom! Overwhelmed, me?

I've done a cursory review of these, so I apologize if I get something wrong, but here goes:

  • browserify is a tool that can take a file with a CommonJS module (and its dependencies) and bundle them all up in a .js file you can use in the browser.

  • OneJS seems to do something very similar. The docs don't make it immediately obvious what its distinctive features are.

  • commonjs-everywhere does the same again (I think... the docs are a bit technical...), but has more features.

    One cool thing is source maps support. Source maps I just found out about: they are basically a way to add debugging symbols to a minified, bundled .js file so the debugger can find out about the original source. This is handy if you bundle plain JS, and also makes it possible to offer better debugger support for languages such as CoffeeScript which compile to JavaScript. Source maps are only supported in Chrome right now, with Firefox support coming up.

    [update: a commenter pointed out that browserify supports source maps too,

    pass a --debug flag during building to enable this]

  • browser-build is a very recent implementation that does the same as the others, but is focused on performance: producing a browser version of CommonJS modules of your project really fast, so you never have to wait for your tool during development. It has support for source maps.

    But it also does something more: if you write your code in plain JavaScript (as opposed to CoffeeScript, say), it makes your original modules available to the browser in only very slightly edited form (the line numbers are the same). This should help debugging a great deal in browsers that don't support source maps.

    But I'm unsure about the details, as browser-build needs to have CoffeeScript available to compile the sources and run it and I too lazy to try this right now.

  • component is also a package manager (I'll mention it again later in that context), but also contains a build system to generate a single .js file from CommonJS modules.

All of these approaches need a build step during development, which makes debugging harder, though source maps will help. browser-build minimizes the build step during development to the bare minimum, however, and this will help debugging.

Both? uRequire

Then there's uRequire.

uRequire allows you to use CommonJS or AMD or both and converts them to whatever is needed. It talks about a Universal Module Definition (UMD) I haven't studied yet, but apparently its not necessary to use its boilerplate when using uRequire. From what I understand in order to use code in the browser a build step is required.

JS frameworks with a module system

There are lots of JavaScript client-side frameworks that have grown their own module systems. I'll only talk about a few here that seem relevant.

  • Dojo had its own module system, but has started to use AMD in recent versions and has been pushing this forward. You can use Dojo modules directly in your own codebase - this kind of inter-package reuse is something I will go into more detail later.
  • Closure Tools by Google contains a lot of things, such as a powerful JavaScript compiler and various JavaScript libraries. It also features its own module system, which I'll also talk about more later.

JavaScript Packages and Modules Redux

In the previous blog entry I explored some concepts surrounding dependencies and modules. I've had some new insights on how these concepts apply to the JavaScript world. I'll review some of this again, this time with a focus on what these concepts look like in JavaScript.

  • a project is the codebase you're hacking on. In open-source JS land, typically it's something published on github. It's in hackable form, so contains a lot of separate .js files to separate concerns: modules.

  • a module is a JavaScript file that provides some functionality by exposing an API.

  • a module may depend on another module. A module dependency is expressed in the module itself, in source code. In JavaScript there are multiple systems for expressing modules and their dependencies, such as CommonJS, AMD and Google Closure.

  • a package is a collection of one or more modules that is published somewhere so others may use it (this may be published on the internet, or internal to a project). It has metadata that describes the package, its version number, who wrote it, and what other published packages it depends on.

    CommonJS packages on the server-side are distributed as essentially an archive of a CommonJS project: a lib directory full of modules, with package.json for metadata.

    Traditionally client-side JavaScript packages are just distributed as URLs pointing to a .js file that people can download. So, to get jQuery, you download a version at the jQuery website. This is a very large difference between browser and server.

    Bower packages are a formalization of this traditional pattern: there is a single .js file with bower.json metadata to describe it. Bower adds metadata (bower.json) and a package index to the original story.

    In fact the Bower story is more complicated: it does allow you to package up a directory of multiple modules too, which you could then use using, say, RequireJS. This is an entirely different way to use modules, but Bower is agnostic and just installs the stuff. Bower also supports listing more than one .js file in its bower.json configuration file; it's unclear to me what the semantics of this is exactly.

  • Package generation. This is something I skipped in the previous discussion of concepts, but is very important in the JavaScript world especially.

    CommonJS packages are just archived versions of a particular project layout: a directory with a package.json, with a lib subdirectory which contains the individual .js modules.

    Browser-targeted packages are most commonly shipped as a single .js file as mentioned before. In the most simple case you maintain this .js file by hand directly and give it to others to use.

    But you can also generate a single .js package file by compiling a bunch of .js module files together. This is what the CommonJS generators described above do, except for browser-build, which actually maintains a tree of unbundled .js modules.

    The realization I had, perhaps obvious, is that a client-side JavaScript package is often shipped as a single compiled .js file. It's like how a C linker works - it bundles individually units into a larger library file (.so, .dll).

  • A package manager is a tool that installs a package into your system so you can start using it. npm is popular for NodeJS, Bower is focused on client-side packages and tries to be very package format agnostic. component contains a package manager too, centered around CommonJS (and also the build tool I mentioned earlier).

  • A package registry is a system where packages can be registered so that others may find and download them. npm has an index, and so do Bower and component.

MantriJS

Another dependency system I ran into since my last post is MantriJS. MantriJS is built around the Google Closure Tools but hides them from the developer, except for the dependency system.

You define a module that depends on others like this in Mantri/Closure Tools:

goog.provide('obviel.template');
goog.require('obviel.util');

obviel.template.foo = function() { ... };

Here you say you are defining a module called obviel.template and that in this module obviel.util needs to be available. Once you require something you have that available to use in your module, so you can now do this:

obviel.util.blah();

Mantri has a build step for development, but only to build a deps.js file and only when you've changed a dependency. The modules themselves are directly exposed to the browser during development, meaning you can debug them. In this it looks quite similar to browser-build, though browser-build does touch the individual modules in a minor way, something MantriJS does not.

MantriJS does offer a build step to generate a single file .js package from your modules, using the Closure Tools.

I tried to see whether MantriJS was easy to integrate with the Buster.JS test runner; I had to wrestle quite a bit to get RequireJS running properly before. It turned out to be very easy (it Just Worked ™!). See the jspakmantri example project and compare it with the original RequireJS-based jspak project if you like.

Thinking about MantriJS I realized something: MantriJS actually allows you to have modules the way many existing client libraries do it: create a namespace object and fill it with properties you want to expose. This is important to me because that's how Obviel does it now, and I'd prefer not to break that client API.

Global Namespace Modules

So what is this client library module definition pattern MantriJS supports? Everybody is familiar with it. It's what jQuery does for instance: it fills the jQuery object ($) with the jQuery API and exposes this.

For example, to make a module, you create an empty object, perhaps listed in another object to namespace it, and make it globally available:

var obviel = {};
obviel.template = {};

You then fill it with the API you want somehow, for instance like this:

obviel.template.foo = function() { ... };

or like this:

(function(module) {
   module.foo = function() { ... };
}(obviel.template));

To use a module from another one, you simply refer to it:

obviel.template.foo();

That's all that's needed, but there are also frameworks that help you declare and use modules like this, such as MantriJS mentioned earlier; YUI has another one. The primary benefit these add is the ability to express module dependencies better, avoiding the need to mess around with <script> tags.

So this pattern is neither CommonJS or AMD. But it is very widely used on the client-side. Obviel uses it for instance, and Backbone too, and Ember, and Knockout, and Mustache, and YUI, and Google Closure Tools. To just list a few. Let's call it the Global Namespace Modules pattern (GNM).

GNM is not a module definition pattern like CommonJS or AMD. Instead it is defined by how modules are used: you refer to the API of a module using a global namespace that the module exposes (jQuery, obviel, Backbone, Mustache, etc).

GNM assumes that modules are loaded in a particular order, synchronously. You ensure this order by listing <script> tags in a particular order, or by using a smart module loader like MantriJS, or by bundling modules in order into a single .js package file.

Getting this more clear for myself is quite important to me. It had been bugging me for a while after reviewing RequireJS: if I start using it for Obviel, do I need to to break the Obviel API, which assumes GNM. Or do I tell all developers to start using AMD for their code that uses Obviel too?

[update: here is a post with more on this pattern; here's another]

Requirements

After thinking about all this, here are some varying requirements for a JavaScript module dependency system. Ideally Obviel can adopt one that has all of these properties, or as close as possible:

  • automated loader: no <script> tag management. (loader)

  • encourage fine-grained modules. (fine)

  • being able to use browser debuggers like Firebug or the Chrome Dev tools. (debug)

  • source maps not required: being able to use these debuggers without relying on new source map technology. (nosm)

  • no build step needed during development. (nobuild)

  • support for exposing modules using the GNM pattern. Is this really important? Yes, as it's a very popular pattern on the web. Dojo went the way of telling people to use AMD for their own code, and that does help with fine-grained reuse between packages... (gnm)

  • compilation tools: bundling, minification to deliver easy to use .js files. This way the browser can load a package efficiently and it becomes easy for people to start using the API the package exposes: just drop in a file. (comp)

  • inter-package reuse: being able to require just one module from another package without having to load all of them. (reuse)

    There is some tension here with the bundling into a single .js package approach - if there's a module in a package that I don't use, why does it still get shipped to a web browser? On the server installing a bit more JS code in a package is not a problem, but on browsers people tend to start counting bytes.

    This tension can be reduced in various ways: jQuery now offers various smaller builds with less code. Build tools can cleverly only include modules that are really required, though for inter-package reuse this can defeat the benefit of caching.

  • integration with BusterJS test runner. As this is the test runner I use for Obviel. Preferably with the least hassle. (bjs)

  • CommonJS everywhere: client definition of modules same as on server, so CommonJS packages can be used on the client too. There is after all potentially a lot of useful code available as a CommonJS package that can be used on the client too, and potentially some of my Obviel code can be run on the server too. (cjs)

Review

Let's review some of the systems mentioned in the light of these requirements. If I get it wrong, please let me know!

system loader fine debug nosm nobuild gnm comp reuse bjs cjs
manual N N Y Y Y Y Y Y Y N
RequireJS Y Y Y Y Y N Y Y Y N
browserify Y Y Y N N N Y Y? Y? Y
cjs-everywhere Y Y Y N N N Y Y? Y? Y
browser-build Y Y Y Y N N Y Y? Y? Y
uRequire Y Y ? ? N N Y Y? ? Y
MantriJS Y Y Y Y N Y Y N Y N

[update: source maps are also a browserify feature]

A few notes from the perspective of Obviel:

Nothing ticks all the boxes for Obviel from what I can see. RequireJS, MantriJS and browser-build come closest.

The manual system involves maintaining <script> tags yourself. That is what I'm doing with Obviel now. It involves no build step, so debugging is easy during development. It supports the popular global namespace modules pattern. If a framework exposes multiple modules that users are to include using <script> tags, like Obviel currently does, then inter-package reuse is possible. Compilation into a single .js file is not needed but there are tools that can do it for you. But it's not fine-grained at all, breaking a fundamental requirement for Obviel.

RequireJS is quite nice; script tag management goes away, no build step is needed but compilation to a .js file is still possible. It allows fine-grained reuse of modules in other RequireJS based packages, which is very nice. After some effort it integrates with BusterJS. But it doesn't offer Global Namespace Modules support out of the box. It shouldn't be too hard to make it do that, though, by simply exposing some modules myself, possibly during a build step.

The various CommonJS approaches are interesting. It is attractive is to be able to use same approach on the browser as on the server. But most tools require a bundling build step and I'd like to avoid having to rely on still uncommon source maps to do debugging. That's why browser-build is one of the more interesting ones, as it minimizes the build step required and makes debugging easier.

I still a bit unclear to me whether fine-grained module reuse of other npm-installed packages is possible - do these modules get exposed to the browser too (in a bundle or directly for browser-build?). From what I've read here and there I think so. I also haven't explored how easy it is to integrate these with client-side Buster (server-side Buster integration is supported by Buster), but I get the impression it's posible.

The CommonJS approaches don't offer Global Namespace Modules support so I'd have to hack that up as for RequireJS.

MantriJS was quite a revelation to me as it helped me come clarify my thinking about the Global Namespace Modules pattern. I've contacted the author and he's very responsive to my questions, also nice. It turned out to be dead-easy to integrate with Buster.JS. MantriJS assumes that external JS packages are bundled up in a single .js file for reuse however, so fine-grained module reuse of other packages is not possible.

Still overwhelmed

I'm still overwhelmed by the choices available as well as all the details. But I know a bit more about what's possible and what I want now. Are there any players in this field that I missed? Undoubtedly more will come out of the woodwork soon. What do you think about my requirements? Should I just give up on GNM, or forget about not having a build step during development? Am I missing an important requirement? Please let me know!

Reinout van Rees: Growing open source seeds - Kenneth Reitz

From Planet Plone. Published on May 16, 2013.

He shows us three kinds of (more or less) open source projects.

Type 1: public source

Once upon a time there was an "open source project" called the facebook SDK. Basically it just stopped working one day and nobody could help, despite offers for help on the issue tracker. Hacker news got wind of it and it was on the front page for a while. Facebook's reaction? Disabling the issue tracker... (Later on they fixed it).

That's not open source, that's public source. Often it is abandoned due to loack of interest, change of focus or so. The motivation for having it as open source simply is not clear.

Type 2: shared investment

A different example: gittip. They aim to be the world's first open company. There's a github issue for everything, even the company name. Major decisions are voted for on github. The code is open source, of course. All interviews with journalists are filmed and live-streamed. And all otherwise-often-backdoor-cooperation-agreements are fully open.

Projects like gittip are shared investment projects. Shared ownership, extreme transparency. There is very little questioning of motivations. The motivation is clear and public. There's a documented process for new contributers. The advantage? It is low risk. There's a high bus factor.

Type 3: dictatorship project

Kenneth is the author of requests. An open source project, very succesful. But all the decisions are made by Kenneth.

That's really more of an dictatorship project. A totalitarian BDFL that owns everything. The dictator is responsible for all decisions. Requests' values lie in its extreme opinions. If he'd involve more people, the value would be dilluted. There are drawbacks. A low bus factor. High risk of burnout: Kenneth is the single point of failure.

Lessons learned

  • Be cordial or be on your way. As a user, you need to keep all your interactions with the maintainer as respectful as possible. The maintainer put a lot of work in it and they don't owe you any of their time.

    As a maintainer, you also must be cordial. Be thankful to all contributions. Feedback is the liveblood of your project, even the negative. You'll need to ignore non-constructive comments. Be careful with the words you choose, sometimes contributors take what you say VERY personally. You might have to educate your users. And: a bit of kindness goes a long way.

  • Sustainability is almost the biggest challenge. Don't burn out. Try to get others to help.

    He quotes Wes Beary: "open source provides a unique opportunity for the trifecta of purpose, mastery and autonomy". Pay equal attention to all of these three. Learn to do less, focus more on your purpose, for instance.

  • Learn to say no. People ask for crazy features. Or they submit quite sane pull requests that, if you allow them all in, makes your project slow and unfocused. Kenneth wants as few lines of codes in his project. Negative diffs are the best diffs!

  • Open source makes the world a better place. Don't make it complicated!

Reinout van Rees: Advanced Python through Django: metaclasses - Peter Inglesby

From Planet Plone. Published on May 16, 2013.

Metaclasses are a handy feature of Python and Django makes good use of them.

When you create certain kinds of classes in Django, a metaclass will do something to the class before it is created. For forms, the various attributes of the class are converted into a base_fields dictionary on the class.

Similarly, a subclass of Model also fires up a metaclass that does some registering. A foreignkey to another model adds a relation back on that other model, for instance.

As a recap, a class is something that can be instantiated into an object. It can have an __init__() method that does something upon instantiation. type(your_instance) will return the class.

Did you know that you can create classes dynamically? See for yourself:

>>> name = 'ExampleClass'
>>> bases = (object,)
>>> attrs = {'__init__': lambda self: print('Hello from __init__')}
>>> ExampleClass = type(name, bases, attrs)
>>> example = ExampleClass()
Hello From __init__
>>> type(example)
<class '__console__.ExampleClass'>

So... we can actually control how classes are created! You could create a create_class() method that calls type but that modifies, for instance, the name. Or we could take all the attributes and add them to a base_fields dictionary on the instance. Hey, that's what we saw in the first Django form example!

Now, what is type exactly? It is a class that creates classes.

This also means we can subclass it! The most useful thing to override in our subclass is the __new__() method. The __init__() method creates instances from the class, the __new__() creates classes. So again we can modify the name and/or the attributes.

How do you use it in practice? Normally you'd set a __metaclass__ attribute on a class. This tells python to use that metaclass for creating the class. The same for subclasses. This is how our Django form classes use the metaclass specified in Django's base Form class.

Django uses metaclasses in five places: admin media, models, forms, formfields, form widgets. Grep for metaclass in your local django source code once to get a better feel for how Django uses it.

Note on python 3: it uses a slightly different syntax for specifying metaclasses. So Django 1.5 uses six to support both ways in a single codebase.

Warning: don't overuse metaclasses. They can make code difficult to debug and follow. Use Django as a good example of how to use metaclasses. Django saves you a lot of work by using metaclasses in a few locations.

See https://github.com/inglesp/Metaclasses

Nice way of giving a presentation, btw. Some sort of semi-interactive python prompt. The software is online at https://github.com/inglesp/prescons

Reinout van Rees: Thread profiling in Python - Amjith Ramanujam

From Planet Plone. Published on May 16, 2013.

Amjith Ramanujam recently wrote a thread profiler in Python and it was rediculously simple. He works for New Relic, which is all about performance and expecially performance measuring.

Python comes with batteries included, so it also includes a C profiler that works pretty well. But it doesn't work nice for django because the output is so huge. If you use the GUI RunSnakeRun, it is more managable that way.

Additional problem with cProfile: it has about 100% overhead, so you can't run it in production (which they need).

You can do more targeted profiling. For instance in Django. The way a web framework processes requests is normally always in the same way. You can use that during profiling.

There are two important stages: interrupt and inquire.

Interrupt
A statistical profiler looks how often a function is called and by who. For this it needs to interrupt the regular process. You could set an OS-level signal to call your profiler every x miliseconds so that it can do something. It only works in linux, btw. Another way is to create a python background thread that wakes up every x miliseconds. It is cross-platform and mod_wsgi compatible. It is less accurate for CPU tasks and it cannot interrupt C extensions.
Inquire
You can use sys._current_frames() to get yourself the frames ("stack trace") from the current thread. Here you can extract the filename, line number and so of the most interesting frames. He's building a "call tree" structure, mapping how often a function is called, preserving the parent/child relation between functions. The tricky part was visualizing it :-) In the end they did it with d3.js.

There's some 3% overhead in the thread approach, so that's real good. They can switch it on for a type of request and it'll profile the next 100 requests of that type.

Reinout van Rees: The imaginative programmer - Zed Shaw

From Planet Plone. Published on May 16, 2013.

His goal: teach programmers to be more creative.

He's got a love/hate relationship with creativity. The first part of his talk was impossible to summarize. You'll have to watch the video later on :-)

  • Artists tell him he's not artistic because he works on developing technical skills.
  • Guitarists tell him he's not a real guitarist as he doesn't play in a band. And 'cause he builds his own guitars he's a programmer, not a Real Guitarist.
  • Writers tell him he's not a writer because he writes technical books.
  • Programmers tell him he's not a programmer because he doesn't work on their project. And by the way, he's a (technical) writer now, so he's not a programmer.

He's not creative enough. Or so the others say. Or he's not acceptable. How to deal with creativity? In a way, you can re-phrase creativity. Programmers are always making something from nothing, right? Isn't that the pinnacle of creativity?

Here are four hypothetical persons:

  • Technique, no imagination: a stereotypical programmer.
  • Imagination, no technique: stereotypical biz dude.
  • No imagination, no technique. Probably doesn't exist.
  • Both imagination and technique. Zed's goal.

Zed's imaginative programmer process. Everyone has a process (if they're good), here's the one he proposes to help you be more creative:

  • You start with an idea.
  • You establish a concept that helps form the idea. It gives your idea clothes.
  • Research techniques or tools. Do some research or you'll pay for it later on.
  • Refine the concept through composition. So put a box around the vast world of available possibilities. Just mark which features are inside and outside the concept.
  • Explore through prototypes. Throw away code or use paper prototypes for instance. This saves you so much time later.
  • You make it real.

We are programmers, so we should iterate this process.

He tried this process with http://projectzorn.com/, going through all the stages. And he's probably going to work on it

Reinout van Rees: Copernicus, the great refactorer - Brandon Rhodes

From Planet Plone. Published on May 16, 2013.

Brandon Rhodes mentions Nicolai Kopernik, a famous Polish scientist. He "lifted Earth into Heaven" by his book where he put the sun in the center of our universe instead of Earth. We're no longer at the bottom.

The near-earth environment was pretty well mapped out. 300BC, the size of the spherical earth was already known. Around 100BC the distance to the moon was known.

But what about those planets? They were harder. Stars did move around the sky more or less linearly. But those planets. They seemed to move back and forth a bit. Did they need to have a different model? The sun-centric model was already known, but it didn't catch on. The reason? Medieval science was too emperic: the earth cannot be moving, it seems to stay in place. Throw a ball and it falls down again towards the earth, it doesn't career off into space. The church wasn't totally idiotic by later convicting Galileo: there was just no solid emperical evidence :-)

One of the pieces of evidence that was missing was that there was no observable stellar parralax; visible movement between stars because of earth movement. It was only in 1838 that the instruments were good enough to actually observe parralax. Only then did we figure out how far away the stars were. So it took 200-300 year to get real evidence.

So why did Kopernik trust his theory despite the lack of evidence? Because of beauty. Brandon showed Kopernik's theory as Python code; an algorithm for planet movements. And showed his theory as a code refactoring. The end result was quite simple and nice once you factored out Earth's motion. The code looks nicely ordered.

There's truth in beauty.

Brandon proposes a new term: Kopernican refactoring: making a system simpler by moving something new into the center.

It is so easy to behave according to a rule that's not even really there. You look at a problem in a certain way and you're stuck. Once you put something else in the center, you look at the problem in a different way.

For instance, you can use argparse to generate a help message when you pass --help. You can also do it the other way around: using docopt which generates a parser from your help message! You get a much nicer help message.

So if you're stuck, think of Copernicus and his refactoring.

Reinout van Rees: Bleed for speed - Rob Spectre

From Planet Plone. Published on May 16, 2013.

He started with a little history lesson. The sea battle of mobile bay. The admiral (Faragut) ordered the ships straight through the minefield (called "torpedoes" at the time). "Damn the torpedoes, full speed ahead". And it worked.

What does this to have to do with Django? Well, "damn the torpedoes, full speed ahead" feels a bit like how rapid prototyping feels afterwards. He's often involved with hackathons. Lot of quick coding in limited time with a lot of people. He learned a lot about his tools that way (and he often used Django).

There's a time to make a distinction between production and prototype. Sometimes it is better to just try something with a prototype. Throw-away code.

Aaargh! Throw-away code?!? We never throw code away. But it is something we must learn. It is good to let go once in a while. Let your code go. It isn't yourself, it is just some code.

The danger is that prototype code is put into action as production code. With some work, this danger can be prevented.

What about Django? Django is the best for prototyping. For rapid prototyping, Django is better than micro-frameworks like Flask that might seem better at first glance. Here are some reasons:

  • Django was build for rapid prototyping. It originated at a newspaper! 24 hours to build something.

  • It is flexible. It was build to bend. He can prepare something for the other people programmign with him and get them going and still keep the code in reasonable shape.

  • Us. The strength of Django is the community that supports it. Stack overflow questions and answers. The django websites. Books like two scoops of Django (see my review). That's not something you have with many other frameworks!

    Tip: read especially chapter 2 and 3 of the two scoops book.

    One thing he'd add to the book is stuff like fabfiles and makefiles. Handy for rapid prototyping.

Use stuff that's available. For instance Django's staticfiles app for grabbing together all the css/js/png. Whether it is in one directory or split out over multiple apps. It also helps with production.

Also look at brunch for setting up your javascript app's structure. It works well with Django.

Deployment: you need to show your prototype. Heroku is very quick for prototypes. (He mentioned that they have a data center in Europe now in case you need it).

Deployment: use chef. Lots of recipes. You could also use Salt if you're more into Python. Also lots of stuff available. Both take a while to learn, but it is a very good investment.

Configuration management is an extremely useful skill. Do it well.

Tastypie is the quickest way to get a REST api out of your Django. It is the best for rapid prototyping. Another good one is django-rest-framework. It will take a little bit longer to set up, but once done you're working with actual Django views. And django-rest-framework's browseable API is very helpful when you're working with a couple of others

Social auth connectors: everyone makes one and there are way too many half-working ones. He's got two that he can recommend. django-social-auth is very complete. The other is django-allauth for when speed is important for you.

If you don't want to play fair to others during a hackathon: use celery. It is very unfair to use celery, python and Django. The combination with Django is pretty OK to set up. You can do a lot what others cannot do easily. So use it for rapid prototyping. (Regarding setting it up: there are good chef recipes for it).

TEST. Yes, even during a hackathon. He doesn't advocate full test driven development. It is a balance. But the errors that kill you during a hackathon are the errors you make twice. So, for instance, test that all your views simply return a 200 Ok. This already helps prevent a lot of problems.

Look at AngularJS. Even if you don't use the framework itself. Why? It has a great javascript test runner. Good for testing while rapid prototyping.

Personal tools