Planet Plone

This is where developers and integrators write about Plone, and is your best source for news and developments from the community.

January 16, 2017

eGenix: Python Meeting Düsseldorf - 2017-01-18


The following text is in German, since we're announcing a regional user group meeting in Düsseldorf, Germany.


Das nächste Python Meeting Düsseldorf findet an folgendem Termin statt:

18.01.2017, 18:00 Uhr
Raum 1, 2.OG im Bürgerhaus Stadtteilzentrum Bilk
Düsseldorfer Arcaden, Bachstr. 145, 40217 Düsseldorf


Bereits angemeldete Vorträge

Charlie Clark
        "Kurze Einführung in openpyxl und Pandas"

Jochen Wersdörfer

Marc-Andre Lemburg
        "Optimierung in Python mit PuLP"

Weitere Vorträge können gerne noch angemeldet werden. Bei Interesse, bitte unter melden.

Startzeit und Ort

Wir treffen uns um 18:00 Uhr im Bürgerhaus in den Düsseldorfer Arcaden.

Das Bürgerhaus teilt sich den Eingang mit dem Schwimmbad und befindet sich an der Seite der Tiefgarageneinfahrt der Düsseldorfer Arcaden.

Über dem Eingang steht ein großes "Schwimm’ in Bilk" Logo. Hinter der Tür direkt links zu den zwei Aufzügen, dann in den 2. Stock hochfahren. Der Eingang zum Raum 1 liegt direkt links, wenn man aus dem Aufzug kommt.

>>> Eingang in Google Street View


Das Python Meeting Düsseldorf ist eine regelmäßige Veranstaltung in Düsseldorf, die sich an Python Begeisterte aus der Region wendet.

Einen guten Überblick über die Vorträge bietet unser PyDDF YouTube-Kanal, auf dem wir Videos der Vorträge nach den Meetings veröffentlichen.

Veranstaltet wird das Meeting von der GmbH, Langenfeld, in Zusammenarbeit mit Clark Consulting & Research, Düsseldorf:


Das Python Meeting Düsseldorf nutzt eine Mischung aus (Lightning) Talks und offener Diskussion.

Vorträge können vorher angemeldet werden, oder auch spontan während des Treffens eingebracht werden. Ein Beamer mit XGA Auflösung steht zur Verfügung.

(Lightning) Talk Anmeldung bitte formlos per EMail an


Das Python Meeting Düsseldorf wird von Python Nutzern für Python Nutzer veranstaltet.

Da Tagungsraum, Beamer, Internet und Getränke Kosten produzieren, bitten wir die Teilnehmer um einen Beitrag in Höhe von EUR 10,00 inkl. 19% Mwst. Schüler und Studenten zahlen EUR 5,00 inkl. 19% Mwst.

Wir möchten alle Teilnehmer bitten, den Betrag in bar mitzubringen.


Da wir nur für ca. 20 Personen Sitzplätze haben, möchten wir bitten, sich per EMail anzumelden. Damit wird keine Verpflichtung eingegangen. Es erleichtert uns allerdings die Planung.

Meeting Anmeldung bitte formlos per EMail an

Weitere Informationen

Weitere Informationen finden Sie auf der Webseite des Meetings:


Viel Spaß !

Marc-Andre Lemburg,

December 30, 2016

T. Kim Nguyen: How to monitor your Plone servers with Sentry


The real-time error tracking service gives you detailed insight into production errors, so that you can often fix them BEFORE your clients even notice a problem and BEFORE their users get upset.

What Sentry provides

Sentry provides a free plan that is more than enough to handle a large number of Plone sites (limited to 1 team member and 5,000 events per day).

Sentry notifies you of errors in a variety of ways (email, by default) and it provides you with a dashboard of all your reported errors, which you can mark as resolved, you can ignore them (for a set amount of time), you can link them to an issue tracker such as GitHub, you can share the detailed error message (scrubbed of private data).

Effects of Sentry

If you are like me, once you start using Sentry, you will:

  • be amazed at how many errors your Plone site was logging but didn't know about before (not necessarily caused by bugs, but by search engine crawlers looking for things that aren't on the site, bad links from other sites, insufficient privileges because items are still private)
  • realize that you *can* provide a 100% satisfactory product and service, now that you can see and can fix all the errors, whatever the cause, encountered by your clients and their users
  • find yourself tracking down and finding and fixing bugs, not only in your sites and server, but sometimes in other code you're using

Boost user and client satisfaction

Other things Sentry does: you can easily customize Plone's default error message page to pop up a nice looking dialog box in which a user can provide their name, email address, and description of what they were trying to do when they encountered an error. Not only is this good for YOU (because it helps you understand what led to the error), it is also FANTASTIC FOR YOUR CLIENT because their users now know that administrators have noticed their problem and are trying to fix it. This is a level of user satisfaction that goes above and beyond what most people have come to accept, which is to suffer in silence.

How to set up Sentry with Plone

How it works: you sign up with Sentry, you declare any number of "projects", each of which gets a unique ID that you use in your buildout.cfg configuration.

In buildout.cfg, you want to add custom error logging configuration so that when an error is logged, it not only gets logged normally to your event.log or instance.log but it also gets sent to Sentry with the unique ID you were assigned.

(There are even finer grained ways of tracking multiple "releases" of each project, but that probably is useful only if you're deploying versions of, say,, which, presumably YOU are not if you are reading my lowly blog!)

Two example buildout configurations

Whether you installed Plone with the Unified Installer or some other buildout-based method, you will have to modify both buildout.cfg and base.cfg to use Sentry.

Plone 4.3 ZEO

In a ZEO Plone 4.3 deployment, which I installed with the Unified Installer, buildout.cfg defines two ZEO clients, [client1] and [client2]. To do that cleanly, buildout.cfg extends base.cfg, which in turn contains a [client_base] section, which both [client1] and [client2] derive from.

We modify [client_base] by adding these lines:

event-log-custom =
%import raven.contrib.zope
path ${buildout:var-dir}/${:_buildout_section_name_}/event.log
level INFO
level ERROR

Your specific "dns" line containing YOURUNIQUEID and MOREUNIQUEID will have come from your Sentry project definition.

You want your "path" value to match that of the "event-log" value defined in [client_base]. I haven't tried this, but you may be able to use a line like path ${event-log} instead of path ${buildout:var-dir}/${:_buildout_section_name_}/event.log 

Plone 4.2 ZEO

In contrast, a Plone 4.2 ZEO deployment (also using the Unified Installer) is slightly different: both [client1] and [client2] are defined in base.cfg, so you have to append your custom configuration to both, and the value for "path" is different:

event-log-custom =
%import raven.contrib.zope
path ${buildout:directory}/var/client1/event.log
level INFO
level ERROR


event-log-custom =
%import raven.contrib.zope
path ${buildout:directory}/var/client2/event.log
level INFO
level ERROR

In both cases, the key point here is to ensure your Sentry "path" value matches what was defined for each client. As in the previous section, I haven't tried this, but you may be able to use a line like path ${event-log} instead of path ${buildout:directory}/var/client1/event.log and path ${buildout:directory}/var/client2/event.log 

Plone "instance" (non-ZEO) deployments

I haven't tested this myself but the idea is the same. You need to customize logging for the [instance], and that will depend on your buildout.cfg and base.cfg. 

Add Raven

You also have to add "raven" to your buildout.cfg eggs:

eggs =

Run buildout and restart clients

Now run buildout. Once you restart your ZEO clients, when they log an error they will continue appending to the log file defined in the "path" above but will also send the error to Sentry. Depending on how you set up Sentry, you will receive email notifications and you can view a dashboard of your errors.

In all likelihood, you will begin to be able to fix errors (software, user, or other) BEFORE anyone reports them to you, if they even bother to report the errors at all. 

Increased client and user satisfaction

Your clients and their users may not even realize how much better a service you are providing them, but you will know, and you will have greatly increased your ability to retain satisfied customers, and that is golden.


December 27, 2016

T. Kim Nguyen: Quills blogging add-on for Plone gets some attention


The venerable Products.Quills blogging add-on for Plone has been neglected for some time. I'd last used it on some 4.x sites but the eggs on the PyPi package Index were missing some files. 

Quills has been tested with Plone 4.3.11. It has not been tested with Plone 5.

Now Products.Quills 1.8.1 and the matching 1.8.1 have been released to pypi:

Both add-ons have been updated with current Plone version PyPi classifiers

How to Install Quills

To install them in your Plone site, add this to your buildout.cfg:

eggs =

then run bin/buildout

If you want to ensure you get the latest version of Products.Quills:

eggs =

I always pin egg versions, either there in the eggs lines or later in a [versions] section or in a separate versions.cfg file, e.g.


Release Management

I used the lovely zest.releaser tool to do that. Among other things, it tags the release in the GitHub repo, so that later on we can all find the exact state of the source code used to release that particular egg, e.g. 

Recently I ran into difficulties (still unresolved) with another project that did NOT tag its releases in GitHub, and I've been unable to branch off the correct past state of its code to try to fix some bugs. 

T. Kim Nguyen: How to enable online reading of Taylor & Francis journals from your Plone site


For Plone 4.3 I implemented an External Method that obtains a special one-time access URL from Taylor & Francis' web site.

You can see this in action at the International Medieval Sermon Studies web site!

The External Method is secured with the ZMI's Security tab but it is also behind a couple of Plone pages, one of which requires logging in to read. 

The Python script itself is:

from Products.CMFCore.utils import getToolByName

def mss_online(self):
# check if we are logged in
pm = getToolByName(self, 'portal_membership', None)
if not pm:
return "Unable to check if you are logged in. Please notify a site administrator."
user = pm.getAuthenticatedMember()
if str(user) == "Anonymous User":
return "You are not logged in."
BIG_URL = ""
import urllib2
url = urllib2.urlopen(BIG_URL).read()
return "<META HTTP-EQUIV=\"Refresh\" CONTENT=\"0; URL=%s\"><html><head></head><body>You are being redirected to <a href=\"$redirURL\"></a></body></html>" % url

I named the script and placed it in the Plone installation directory’s “Extensions” subdirectory (e.g. /opt/Plone/zeocluster/Extensions/

Then, using the Zope Management Interface, e.g., I added an External Method, and set:

  • Id: mss_online
  • Title: (does not matter)
  • Module Name: mss_online
  • Function Name: mss_online

To protect it from non-logged in access, I then used the Security tab to uncheck “Acquire permission settings” and check “Authenticated” for the View permission.

December 23, 2016

Hector Velarde: Plone performance: threads or instances?

by hvelarde at 2016-12-23T18:55:29Z

Recently we had a discussion on the Plone community forum on
Recently we had a discussion on the Plone community forum on how to increase performance of Plone-based sites.

I was arguing in favor of instances, because some time ago I read a blog post by davisagli taking about the impact of the Python GIL on performance of multicore servers. Others, like jensens and djay, were skeptical on this argument and told me not to overestimate that.

So, I decide to test this using the production servers of one of our customers.

The site is currently running on 2 different DigitalOcean servers with 4 processors and 8GB RAM; we are using Cloudflare in front of it, and round-robin, DNS-based load balancing.

Prior to my changes, both servers were running with the same configuration:
  • nginx, doing caching of static files and proxy rewrites
  • Varnish, doing caching and URL-based load balancing
  • 4 Plone instances running on ZEO client mode, with 1 thread and 100.000 objects in cache

Both servers where running also a ZEO server on ZRS configuration, one as a master and the other as a slave, doing blob storage replication.

First, here we have some information from this morning, before I made the changes. Here are some graphics I obtained using New Relic on the master:

Here is the same information from the slave:

As you can see, everything is running smoothly: CPU consumption is low and memory consumption is high and… yes, we have an issue with some PhamtomJS processes left behind.

This is what I did later:

  • on the master server, I restarted the 4 instances
  • on the slave server, I changed the configuration of instance1 to use 4 threads and restarted it; I stopped the other 3 instances
I also stopped the memmon Supervisor plugin (just because I had no idea on how much memory the slave server instance will be consuming after the changes), and killed all PhamtomJS processes.

The servers have been running for a couple of hours now and I can share the results. This is the master server:

And this is now the slave:

The only obvious change here is in memory consumption: wow! the sole instance on the slave server is consuming 1GB less than the 4 instances in the master server!

Let's do a little bit more research now. Here we have some information on database activity on the master server (just one instance for the sake of simplicity):

Now here is some similar information for the slave server:

I can say that I was expecting this: there's a lot more activity and the caching is not very well utilized on the slave server (see, smcmahon, that's the beauty of the URL-based load balancing on Varnish demonstrated).

Let's try a different look, now using the vmstat command:

Not many differences here: the CPU is idle most of the time and the interrupts and context switching are almost the same.

Now let's see how much our instances are being used, with the varnishstat command:

Here you can see why there's not too much difference: in fact Varnish is taking care of nearly 90% of the requests and we have only around 3 requests/second hitting the servers.

Let's make another test to see how quickly we are responding the requests using the varnishhist command:

Again, there's almost no difference here.

Conclusion: for our particular case, using threads seems not to affect too much the performance and has a positive impact on memory consumption.

What I'm going to do now is to change the configuration used in production to have 2 instances and 2 threads… why? because restarting a single instance on a server for maintenance purposes would let us without backends during the process if we were using only one instance.

Share and enjoy!

December 22, 2016

Plumi: Plumi now on Debian Jessie, Ubuntu 16.04 and Centos 7

by anna at 2016-12-22T06:41:52Z

We are very excited to announce that after much effort, Plumi is now available to install on Debian Jessie, Ubuntu 16.04 (latest stable) and Centos 7.

The latest code is available here on Github:

Documentation on how to install is available here:

Further documentation including an introduction, installation, theming and maintenance guide has been updated here:

This means our free open source video platform now works across these up-to-date and secure major Linux based operating systems. Free community media infrastructure is needed now, more than ever before, and we are very proud to offer this with Plumi.

We want to heartily thank Markos Gogoulos for all his hard work to get us here, and for supporting EngageMedia in this work.

Anna Helme

on behalf of EngageMedia

Plumi: Ten years of Plumi and looking ahead to 2017

by anna at 2016-12-22T06:37:40Z

It’s been ten years now since we released our first public version of Plumi with a vision to provide free democratic access to video distribution, and I’m very proud of EngageMedia’s work to sustain the project through many successes and challenges.

I’d like to thank the whole team at EngageMedia, and all our visionaries, programmers, designers, testers, documenters, supporters and organisers over the years including Andrew Lowenthal, Dave Fregon, Andy Nicholson, Lachlan Musicman, Dimitris Moraitis, Chris Psaltis, Mike Muzurakis, Yiannis Chatzikonstantinou, Sam Stainsby, Jean Jordaan, Nate Aune, Rok Garbas, Steve Anderson, Giannis Stergiou and more. See also:

Impact producers, video and technology activists, human rights defenders and social justice and environmental advocates across SE Asia and internationally continue to work with EngageMedia across a number of program areas, and contribute as always to the vision for and purpose of Plumi development. See more about EngageMedia’s partnerships and projects here: and learn about the Video for Change network here:

Looking ahead, 2017 will hopefully see Plumi users join forces to take Plumi forward, with a particular eye on getting our “externally-hosted videos” feature out there, which we have done a lot of work on but isn’t quite finished. We are also talking with leaders in the Plone community about ideas such as inviting students to work on particular code or documentation projects, and look towards merging some of Plumi’s base video engine with Plone core and other major video products for Plone, which would help sustain Plumi’s viability into the future.

At this point we’d like to put it out there to Plumi users that we are looking for contributions in order to help EngageMedia maintain the project on behalf of the Plumi community, which has depended on philanthropic funding and donations – never a steady source.

Maintaining the project includes the email lists, issues tracker, Plumi blog, coordination of development, development on core functionality such as recent operating system compatibility updates, updating components, UI bug fixes and improvements, attending Plone conferences and liaising with the Plone community to find and work towards fruitful partnerships.

As always we welcome Python developers to get involved, but we’d also love some financial contributions in 2017 to keep us moving steadily into the future together.

Get in touch on the lists or via the contact form if you think you can help, or want to get involved!

Happy holidays!

Anna Helme on behalf of EngageMedia

December 15, 2016

Alex Clark: A Shout Out to Shout IRC


I’m back on IRC for the foreseeable future, and loving it. Thank you Shout IRC.



A few years ago, I got old and gave up running command line IRC clients. I’ve run them all or at least a lot of them, including one whose name is almost certainly in the crosshairs of political correctness. Most recently I ran Weechat and irssi before that. For a while, I gave up IRC completely because I couldn’t be bothered. But I missed it, and nothing else seemed to suffice. I tried Slack and thought it was OK, but not IRC. I tried various web clients, but couldn’t find one I could stand to use long term. Then Shout IRC came along.

Stay online

I tried Shout for the first time over a year ago, but never bothered to create a Shout account on my server. This was a mistake, since user account creation enables one of Shout’s most powerful features: Stay online on IRC even when you log out.


I had gotten annoyed with having to login each time, so I stopped using Shout for a while. I heard good things about Kiwi, but was disappointed to see no npm release. This led me back to Shout, which does have an npm release. What follows are configuration details for, for posterity. (I added Let’s Encrypt at the last minute for good measure.)



  • EC2 t2.micro running Ubuntu 16.04.1 LTS


apt-get install aptitude
aptitude update; aptitude upgrade -y
aptitude install nginx nodejs-legacy npm python python-pip


sudo -H pip install dotfiles


sudo npm install -g shout

Certbot (Let’s Encrypt)

sudo certbot certonly --manual


server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 301 https://$host$request_uri;
server {
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;
    root /var/www/html;
    server_name _;
    location / {
        proxy_pass http://localhost:9000;
    location /.well-known/acme-challenge/AamTqX-Ic-YERnU0RWS2X_WpszSUsi2lIoXkMYOy_Fs {
        add_header Content-Type text/plain;
        return 200 "AamTqX-Ic-YERnU0RWS2X_WpszSUsi2lIoXkMYOy_Fs.gPCswvmAzfObWoqUg6d_…";
    ssl    on;
    ssl_certificate    /etc/ssl/fullchain.pem;
    ssl_certificate_key    /etc/ssl/privkey.pem;


(I store my .shout directory, which includes my Shout & Freenode credentials, in a private dotfiles repository.)

git clone Dotfiles
dotfiles -s


I’m currently running shout --private in screen, but may eventually add a systemd service for it.

December 07, 2016

Andreas Jung: Integration of Plone 5 with SMASHDOCS


SMASHDOCS is a web-based collaborative editor solution that we integrated with Plone 5...and the good thing: Smashdocs is available for free for NGOs.

November 30, 2016

David "Pigeonflight" Bain: Small Plone team ready for interesting problems

by David Bain at 2016-11-30T21:17:00Z

Starting December 1, 2016 the Alteroo team will be available for new Plone gigs.

Our team members are ready to dive into modern Plone development including Diazo and Mosaic on Plone 5. We also have experience with older versions of Plone.

If you're into checking boxes we also have experience with Javascript/ReactJS/Webpack/Babel, Pyramid/Kotti, Firebase, QA, Linux server administration and General Design skills.

Send an email to newgigs - [ at ]-

November 28, 2016

Gil Forcada: Faster tests and python 3

by gforcada at 2016-11-28T19:40:39Z

2x faster

Thanks to the Plone Foundation that is sponsoring a new dedicated server for our jenkins nodes (the machines that run our test suite on every change),
the Plone community is starting to enjoy faster builds (twice as fast!).

If your pull request or jenkins job runs on Node 5 or Node 6 you will notice it :-)

Please report any misbehavior on github project if you happen to notice something not working as expected.

Happy testing!

Python 3

As the Zope community is getting closer and closer to make a Zope release Python 3 compatible, us, the Plone community have to step up and do the same.
For that, we are working on, guess what, a new Jenkins job that will only run the test suite of all packages that are known to work on Python 3 already.

The initial list isn’t that big, roughly 10 packages so far, but as more and more Zope packages are updated, the more Plone packages that can be made compatible as well.

The upcoming Alpine Sprint will be dedicated towards this: getting a Plone version compatible with the current Zope versions, which will eventually lead to this Zope on python 3 target (aimed to be released by the end of next year).

Happy hacking!

November 25, 2016

Four Digits: Using SCSS mixins to render repetitive selectors

by Thijs Kramer at 2016-11-25T15:57:31Z

I had the following things to keep in mind:

  • the designers provided me font-sizes defined in px while I wanted to use rem.
  • There were multiple themes, so the font sizes and line heights differ per theme.

At first I had to write a mixin that returned font-size and line-height properties, and converted pixel values to rem values:

@mixin fontsize-lineheight($font-size, $line-height, $base: 16px) {
    $remsize: ($font-size / $base);
    font-size: #{$remsize}rem;
    line-height: $line-height / $font-size;

However I wanted line-height to be unit-less, so I had to convert the variables to unit-less values using the following @function:

@function strip-units($value) {
    @return $value / ($value * 0 + 1);

(source: this Stackoverflow answer)

So the final result of the mixin was:

@mixin fontsize-lineheight($font-size, $line-height, $base: 16px) {
    $remsize: (strip-units($font-size) / strip-units($base));
    font-size: #{$remsize}rem;
    line-height: strip-units($line-height / $font-size);

Now this mixin was ready to use for every heading:

h1, .h1 {
    @include fontsize-lineheight(30px, 40px);
    @include media-breakpoint-up(md) {
        @include fontsize-lineheight(40px, 50px);
    @include media-breakpoint-up(lg) {
        @include fontsize-lineheight(60px, 72px);
// ....and so on

This still looks quite repetitive, so I made a map containing lists for the font sizes and line heights, like this:

// font-sizes, from left to right:
//      lg   md   sm
$heading-sizes: (
    h1: 72px 48px 36px,
    h2: 48px 32px 30px,
$heading-lineheights: (
    h1: 72px 48px 36px,
    h2: 48px 36px 30px,

Every entry in the map can be accessed with map-get, for example:

$font-size-h1: map-get($heading-sizes, h1);

And every entry in a list can be accessed by its index (1-based) using nth, for example:

$font-sizes-h1: map-get($heading-sizes, h1);
$font-size-h1-md: nth($font-sizes-h1, 2);

So all of the above can be combined into the following mixin:

@mixin heading($heading) {
    $sizes: map-get($heading-sizes, $heading);
    $line-heights: map-get($heading-lineheights, $heading);
    @include fontsize-lineheight(nth($sizes, 3), nth($line-heights, 3));
    @include media-breakpoint-up(md) {
        @include fontsize-lineheight(nth($sizes, 2), nth($line-heights, 2));
    @include media-breakpoint-up(lg) {
        @include fontsize-lineheight(nth($sizes, 1), nth($line-heights, 1));

So putting everything together the final result is:

@function strip-units($value) {
    @return $value / ($value * 0 + 1);
@mixin fontsize-lineheight($font-size, $line-height, $base: 16px) {
    $remsize: (strip-units($font-size) / strip-units($base));
    font-size: #{$remsize}rem;
    line-height: strip-units($line-height / $font-size);
@mixin heading($heading) {
    $sizes: map-get($heading-sizes, $heading);
    $line-heights: map-get($heading-lineheights, $heading);
    @include fontsize-lineheight(nth($sizes, 3), nth($line-heights, 3));
    @include media-breakpoint-up(md) {
        @include fontsize-lineheight(nth($sizes, 2), nth($line-heights, 2));
    @include media-breakpoint-up(lg) {
        @include fontsize-lineheight(nth($sizes, 1), nth($line-heights, 1));
$heading-sizes: (
    h1: 72px 48px 36px,
    h2: 48px 32px 30px,
    h3: 36px 30px 24px,
    h4: 30px 24px 20px,
    h5: 24px 20px 18px,
    h6: 20px 18px 16px
$heading-lineheights: (
    h1: 72px 48px 36px,
    h2: 48px 36px 30px,
    h3: 42px 36px 30px,
    h4: 36px 30px 24px,
    h5: 30px 24px 21px,
    h6: 24px 21px 18px
$headings: h1 h2 h3 h4 h5 h6;
@each $heading in $headings {
    #{$heading}, .#{$heading} {
        @include heading($heading);

If you have any reactions please leave them in the comments section below!

November 23, 2016

Martijn Faassen: Looking for new challenges

by Martijn Faassen at 2016-11-23T12:14:22Z

Fair warning: In this blog post I aim to sell myself. I'm looking for an exciting and challenging new freelance engagement.

I'm a software developer and I have been one professionally for about 20 years now. I have deep experience, and I continue to learn and create. I know what real-world codebases look like, and I know software development is also about people. I think I can offer lot of value. I can develop software for you, and I can also help you improve the way you develop software.

I see myself as a creative developer -- I want to invent things to improve life for myself and others. Creativity is what attracts me to software development the most. Creativity is transformative. If you need a bit of transformation in your software, talk to me.

I'm a web application developer. I've focused on web development since the late 90s. In that period I've seen the web grow from static websites and with a few server-side Perl scripts thrown in to the dynamic application platform it is today. I started developing web applications with the server-side, the only game in town then, but over the last 10 years I've shifted more and more to the client-side and JavaScript, where much of the creativity is today. I am however still very much at home on the server as well. I've done React, I've done REST, I've done hypermedia APIs, I've dug into GraphQL. If you need a web developer with deep insight in the whole stack, look no further.

I've focused on Python for the better part of my software development career. I came to Python early; I have seen Python grow from a small language with no name recognition to the enormously popular language it is today. I greatly enjoy using it. I've also criticized it where I felt it was needed, painful as it was. If you need a very experienced Python developer, contact me.

But in the last decade I've started to use JavaScript more and more. Thanks to the React community I've learned a few new things about functional programming; it's exciting to see it become relevant to the web. I haven't stopped looking at interesting new languages. I'm a developer who can look beyond a single programming language.

I have repeatedly demonstrated I can take a large piece of software and transform it:

  • Back in the day, I rejuvenated the Zope web framework with Five, a technology that is still in use by the Plone CMS today.
  • I took libxml2, a huge C library that was difficult to use for Python developers, and created the most powerful XML library in the Python world: lxml. I used the predecessor to Cython to do this (in 2004!).
  • I created a simpler, better way to use Zope with Grok. I've helped add web capabilities to existing codebases with a deep history.

I can help you open up your codebase to new possibilities.

I can build on other people's foundations, but I can also build new foundations. In the last few years I've created Morepath, a web framework that compresses the power I expect into a few thousand lines of Python code.

I'm not afraid to say I've also created many a thing that went nowhere. In 2010 I created Obviel, a client-side web framework. I took concepts like model/view separation and templates and a form library and brought them to JavaScript and the client. I thought I was doing something new in 2010, something that people hadn't really thought of yet. It turns out everybody had the same idea about the same time. Backbone burst upon the scene and Obviel never got any traction. Now I'm a frontend web framework hipster; I was doing them before it was cool. It was worth it, because I gained deep insights in what makes a frontend framework tick.

These days I prefer to use React for front-end application development. React is awesome. With React Native a mere web developer such as myself can even build a real mobile app. Want a developer that loves React but is also tempered by experience? Look no further.

I'm looking for a new challenge. I want to help build, create and transform. I work from home (in the Netherlands) so I can enjoy my family and garden, but I can certainly come visit you on a regular basis. I write code, I write docs, I write tests, I give talks, I give training, and I review your code. I am open, constructively critical and honest. I contribute a bit of insight here and there. My services are not cheap, but they are worth it.

In summary, here I am: a very experienced, creative web developer who looks a little bit beyond what's in front of him.

Think you have an interesting challenge for me? Please drop me a mail at and let's talk.

Alex Clark: Project Makefile Open for Business


Makefile for Python Web Development & Related Projects

A while back I was asked to speak to the Configuration Management Working Group of DC:


From that moment on, it was on: an excuse to finish and talk about the Makefile I’d been dragging around formerly since January and informerly much longer.

Finishing the Makefile

I started writing slides on the impressive then I realized I had to finished the Makefile to finish the slides. This mostly involved deciding on target names and testing target execution.

Finishing the Slides

As I mentioned above, is very nice. I had hoped to be able to build the slides myself with reveal.js, but in lieu of JavaScript skills I settled on using the editor. Later I exported and converted them to PDF with pandoc, which was not as nice (through no fault of pandoc, I’m sure; I just wish I could get a better PDF copy from the HTML export.)

Closed for Business

For month after month as I continued to tweak, the project-makefile repository README contained the following:

At some point I started using a ``Makefile`` in my Python projects. This repository
contains that ``Makefile``.

Open for Business

Now it contains this:

    curl -O
    Usage: make [TARGET]
    Available targets:
        - ablog
        - ablog-build
        - ablog-clean
        - ablog-init
        - ablog-install
        - ablog-serve
        - co
        - commit
        - commit-auto
        - commit-edit
        - django
        - django-clean
        - django-clean-migrations
        - django-init
        - django-install
        - django-migrate
        - django-migrations
        - django-serve
        - django-shell
        - django-static
        - django-su
        - django-test
        - django-yapf
        - git-checkout-remotes
        - git-commit
        - git-commit-auto
        - git-commit-auto-push
        - git-commit-edit
        - git-push
        - grunt
        - grunt-file
        - grunt-init
        - grunt-install
        - grunt-serve
        - h
        - he
        - heroku
        - heroku-debug-off
        - heroku-debug-on
        - heroku-init
        - heroku-push
        - heroku-remote
        - heroku-shell
        - heroku-web-off
        - heroku-web-on
        - install
        - lint
        - npm
        - npm-init
        - npm-install
        - package
        - package-check-manifest
        - package-init
        - package-lint
        - package-pyroma
        - package-readme
        - package-release
        - package-release-test
        - package-test
        - pdf
        - plone
        - plone-heroku
        - plone-init
        - plone-install
        - plone-serve
        - push
        - python-clean
        - python-flake
        - python-freeze
        - python-install
        - python-lint
        - python-serve
        - python-virtualenv
        - python-wc
        - python-yapf
        - release
        - release-test
        - review
        - serve
        - sphinx
        - sphinx-build
        - sphinx-clean
        - sphinx-init
        - sphinx-install
        - sphinx-serve
        - test
        - vagrant
        - vagrant-clean
        - vagrant-down
        - vagrant-init
        - vagrant-up
        - vagrant-update
        - vm
- Check out the `slides <>`_ for more information.

Now I invite everyone to use and contribute!

November 21, 2016

eGenix: eGenix PyRun - One file Python Runtime 2.2.3 GA



eGenix PyRun is our open source, one file, no installation version of Python, making the distribution of a Python interpreter to run based scripts and applications to Unix based systems as simple as copying a single file.

eGenix PyRun's executable only needs 11MB for Python 2 and 13MB for Python 3, but still supports most Python application and scripts - and it can be compressed to just 3-4MB using upx, if needed.

Compared to a regular Python installation of typically 100MB on disk, eGenix PyRun is ideal for applications and scripts that need to be distributed to several target machines, client installations or customers.

It makes "installing" Python on a Unix based system as simple as copying a single file.

eGenix has been using eGenix PyRun internally in the mxODBC Connect Server product since 2008 with great success and decided to make it available as a stand-alone open-source product.

We provide both the source archive to build your own eGenix PyRun, as well as pre-compiled binaries for Linux, FreeBSD and Mac OS X, as 32- and 64-bit versions. The binaries can be downloaded manually, or you can let our automatic install script install-pyrun take care of the installation: ./install-pyrun dir and you're done.

Please see the product page for more details:

    >>> eGenix PyRun - One file Python Runtime


This minor level release of eGenix PyRun comes with the following enhancements:

Enhancements / Changes

  • Removed lzma module from PyRun for Python 3.x again, since this caused too many issues with incompatible/missing references. The module is still being built as optional add-on and can be used if the necessary libs are available, but it will no longer prevent PyRun from working altogether.

install-pyrun Enhancements

  • Updated install-pyrun to default to eGenix PyRun 2.2.3 and its feature set.
For a complete list of changes, please see the eGenix PyRun Changelog.


Please visit the eGenix PyRun product page for downloads, instructions on installation and documentation of the product.


Commercial support for this product is available directly from

Please see the support section of our website for details.

More Information

For more information on eGenix PyRun, licensing and download instructions, please write to

Enjoy !

Marc-Andre Lemburg,