What changed in version 3.1

Improved support for layout

The main feature of this release is changes related to dealing more efficiently with layout of Widgets.

Using Pure instead of Yui

The Yui 2 CSS grids project has quite a while ago been succeeded by Yui3, and more recently Yui3 has been succeeded by the Pure CSS library. In this release of Reahl, we switched to using Pure CSS instead.

Backwards compatibility (for Widgets dependend on Yui2) is maintained, but not the default. Using Yui2 and thus Widgets depending on it, requires the following line of code to be added in your reahl.web.config.py file:


Yui2 and Pure cannot be used concurrently in the same project, and the default library used is Pure.

Several Widgets are deprecated for this reason because their functionality is now provided in a slightly different way (see the next section), via reahl.web.pure:

- :class:`reahl.web.ui.YuiDoc`
- :class:`reahl.web.ui.YuiGrid`
- :class:`reahl.web.ui.YuiBlock`
- :class:`reahl.web.ui.YuiUnit`
- :class:`reahl.web.ui.TwoColumnPage`:

Switching over to Pure also changed the look of a site slightly. This change has to do with Pure’s reliance on Modernize.js which only selectively clears styling of elements instead of Yui2’s approach of clearing all styling from elements.

A new Layout concept

A simple, yet powerful new concept was introduced to help deal with the issue of layout. This makes it possible for us to wrap the functionality of advanced layout engines in Python in future.

Any reahl.web.fw.Widget can now optionally use a reahl.web.fw.Layout.

The Layout is attached to its Widget immediately after the Widget is constructed, by means of the reahl.web.fw.Widget.use_layout() method.

For example:

menu = Menu.from_bookmarks(view, bookmarks).use_layout(HorizontalLayout())

The Layout is responsible for changing the Widget in subtle ways (such as adding CSS classes to it which would make it render in a particular way. The Layout can also add children Widgets to its Widget, either just after construction of the Widget, or by special methods a programmer can call on a Widget.

For example:

It is possible to build, say a StackedFormLayout which allows a programmer to add inputs that stack on top of one another inside any Form using this Layout. Here is how that would look:

form = Form(view).use_layout(StackedFormLayout())
form.layout.add_stacked(TextInput(view, address.fields.name))

The concept of a Layout has made the following classes unnecessary, and thus deprecated:


We now build and distribute packages using Python Wheels instead of a source distribution. While Reahl packages at present are all pure Python packages, many of the projects that Reahl depends on need compiling. Using wheels sets us on the path to easier installation of these packages once they provide wheels too.

Other changed dependencies

The reahl-tofu component used to be dependent on reahl-component on nose. These dependencies were unnecessary and forced someone who only wanted to install reahl-tofu to also install reahl-component and nose. These dependencies have been removed.

Development infrastructure

Several internal changes were made internally, and so some development infrastructure to be able to use tools such as devpi and tox, which were not available when we started out. These resulted in a number of small changes that are visible to users:

  • The reahl upload command has two new options: –ignore-release-checks and –ignore-upload-check. The –ignore-release-checks option allows one to upload a package even if some release check (such as it not being committed) fails. The –ignore-upload-check allows an upload even when the package has already been uploaded previously.

    These switches make it possible to upload packages repeatedly to a devpi staging server before an actual release.

  • The reahl upload command now also does a register before it uploads to a pypi-like repository, so that a separate register step is not necessary anymore.

  • The reahl shell command gained the –generate-setup-py option. The setup.py file of a Reahl project is generated from its .reahlproject file. Sometimes one needs to execute a command (tox is an example) which is dependent on the setup.py. This switch allows execution via reahl shell. For example, the following command generates an up-to-date setup.py, runs tox, and then removes the generated setup.py again:

    reahl shell --generate-setup-py -- tox
  • Two commands were added to the reahl script: devpitest and devpipush. These run devpi test and devpi push on the current project, respectively, but with suitable arguments for the exact version of the current project. The devpi test command, for example, needs to be passed an spec, such as: reahl-doc==3.1.0 to test the exact version that is under development. We need to be able to run such a command for all components making up Reahl, each with a different spec. This is now possible via, for example:

    reahl devpitest -sX


    reahl devpipush -sX -- pypi:pypi
  • In order to use nosetests in a more natural way (from a nose perspective), we have added two features to reahl.tofu.nosesupport:

    • We like to do tests slightly differently to how they are done generally: we put all tests in a single directory and we do not want to use naming conventions for test discovery. The reahl.tofu.nosesupport.MarkedTestsPlugin (–with-marked-tests) allows this. It changes nose test discovery to apply the naming conventions to files only, and inside those files it only sees something as a test if it has been marked with the @istest, or tofu’s @test() decorators.

      –with-marked-tests plays well with other nose plugins, and supercedes the older –with-test-directory which does not play well with other nose plugins.

    • Using a run fixture per project (via the –with-run-fixture plugin) is not very “nose”. The “nose” way of doing things is to have a setup and teardown methods in, say the __init__.py of the package where such batch setup should apply.

      The function reahl.tofu.nosesupport.set_run_fixture() was added to deal with this situation. It can be called in an __init__.py to add setup and teardown methods there as nose expects. The effect of this is to make the run fixture apply for the duration of tests in that package.