Responsibility in Software

Pepper & carrot creator David Revoy has created a good blog post that goes into the problem that he’s personally had with the new release of Inkscape 0.92.

The issue with text and svg is actually kind of complex. It’s at the junction of specification, feature management and dealing with old formats. But it’s also a lot about how Free Software projects deal with users to a degree too.

This is because Inkscape is entirely volunteer driven, which means when Inkscape fails for us developers, only our pride is hurt. But actually out there in the big world there are real people who will be materially hurt by a bad inkscape release.

And my frustration is that there’s no serious Free Software way to connect developers to users in that essentially material way that binds them strongly. I’ve been banging the Money and Economics drum for A VERY LONG TIME, but fellow developers are just not interested in the idea that either Free Software could be a job of service instead of indulgence and that there really is a responsibility that we quite often neglect when we don’t have the right resources to deal with them properly.

This isn’t the case for all projects. Quite a few projects have key developers that manage to turn their pet project into a real full time job. OK so they’ll sometimes get some bias from their employer and the project can turn corporate, but that’s the trade off.

This is where the Inkscape projects really hits the wall. It’s a very big and useful project, that has an incredibly poor user to developer material binding. We need about 50 cents from every inkscape user to hire ten to twenty full time developers, managers and ancillary support. Of course the money would likely be bunched up into a few hands, but the project yearns to be in the greatest number of hands and not a few big players.

And maybe that’s the big barrier, a cultural one. Inkscape is built on the idea that all developers are equal and the project can be driven forwards in many directions by lots of developers at once.

I really wish I had some solutions. But given Inkscaoe’s current issues, I’m going to focus on actually fixing the issues we have and I’ll have to come back to how we solve the resources problem more fundamentally.

Responsibility & Cost

These are some notes about a open source presentation I may make at some point in the future. It’s target is programmers and technical staff and project managers who want to understand Free and Open Source better.

Back in the old days of programming, everything you made was your responsibility. Computers were simple, programs were simple and programmers were self reliant. It was a golden age of programming innovation where programmers could explore their entire field and stake claims in the uncharted areas of the software hinterland and most of the time were able to earn decent money while doing so.

But progress meant complexity. Complexity brought with it a phase change from programs developed by single individuals to programs developed by teams, or entire large corporations. No matter how large you made the company or how much money you could push into a project, software projects just seemingly grew to fit their environment, always growing more in complexity than any company could reasonably keep up with.

And so there developed a solution to the complexity. We pay someone else to create a generic system that allows our team to create more complex software at a higher level. These generic systems always had limitations, but with competition, systems like operating systems, databases, web-servers all came into existence to solve to major problem in software: Just how much complexity can a programmer possibly write before they can’t remember it all or how complex a piece of software gets before a team can no longer coordinate effectively around it.

So we have this desire for every greater complexity, but also a growing need to reduce which bits of the complexity that we are personally responsible for.

It’s not difficult to see how paying a company for a piece of software that solves the problem is a fine way to reduce the personal responsibility. You get a piece of software that does almost everything you need and your work then only consists of learning how to use the software and how to customise it for your specific business.

The way the software was sold was very much like selling bananas off a shelf. You’d get the rights to run a single copy that you paid for and that would be that. Any copying would be as if you stole a bunch of bananas from the grocers. This thinking is required if you want to sell software like this, but it isn’t the only way software can be sold.

This is where Free Software comes in. It’s a way of licensing software that works with the realities of software instead of against them. It doesn’t pretend that code is a banana, it treats the code as if it WILL be copied. And that’s ok. What’s specifically important about Free Software is the freedom that it demands users have. No matter how much money you pay for a piece of software, the user must ALWAYS have control over their own software and must ALWAYS be free.

More to follow…

Software Freedom Society

I’ve been mulling over a new idea and I’ve come to my blog to draft my thoughts on it. I’m probably wrong, but I really want you to comment your thoughts below.

We have a Free Software world which is dominated by schools of thought, each focusing on a particular piece of the Free Software problem. Some of them know that they have a limited scope such as the Freedom Software Law Center and Software Freedom Conservancy (legal and sub-incorporation respectively) and then there are groups which try to be all things to all people like the grand daddy of them all, the Free Software Foundation.

I think the FSF itself wants to have a very limited scope, but it just can’t shake the fact that it’s political protests have implications outside of what it wants to do. Things like economics, interpersonal relationships, community culture and creative rights and remuneration to name a short few examples. I think perhaps that people who believe in the FSF’s political campaign, want it to be more. I know I’m frustrated with it’s lack of “do anything productive at all”, that seems to defined the last nine years. Many people who I know here in Boston voice similar low level grumblings think it’s lost focus a great deal because of what users want it to be.

Then there’s another organisation, the Canonical Ltd hype machine that is the Ubuntu community. It’s a pretty good force in the Free Software world (some people will disagree) but again, it’s members want it to be more than a corporate cheer-leading squad. They desire an authority that can standardise, set example, lead. But let’s be honest, Canonical can’t possibly do anything right in a lot of eyes outside of the Ubuntu community itself (even if it empirically did so). So the route to using the boundless capacity of enthusiasm and good ideas about community management and treating non-coders with respect has been watered down or rejected, ever tied to a company that is desperate to make money and over eager for your attention about it’s latest announcement that it drowns it’s own community out.

Now imagine a community that’s dedicated to Software Freedom like the FSF, as respectful and energetic as the Ubuntu Community, as transparent as Debian and as well defined as the Law Center. It’s goals would be to host an open membership, be an arena for debate about community structure, a place to document and explore social standards in both projects and the user communities surrounding them and to invite all to participate in projects as non-coding members.

We could take charge of the areas that the FSF struggles to host, help generalise existing community wisdom that might be tainted by the Canonical brand and provide valuable guides and education on the best ways to run projects and all the pieces needed to make them work.

We could do this by beating a drum and collecting together everyone until we get a critical mass that the social project works. But there is another more useful way too. Many coding projects suffer from a lack of infrastructure for their community operations, not just a lack of know-how. So we’d also start providing the-best-we-know-how pieces of community infrastructure such as mailing lists, forums, chat servers and social media mechanisms. The idea would be to share a lot of the technical burdens and let smaller projects have all the things they need to run a fun, inclusive and accessible community.

This project would of course ask other organisations to dismantle some of their existing structure. We’d have to gain trust and try and close down duplication as much as creating new spaces. This is after all about standardisation of social and community tools and practices and like the xkcd comic states, making new generic standards often leads to more standards. So making our infrastructure fast, pretty and reliable would also be important goals.

So, given that I’ve just rambled on about a passionate but off the top of my head idea (thanks for reading it!), what do you think? Please do comment below, comment in the social media link you might have used or you can email me at doctormo áŧ gmail.com with your thoughts. I really do want to hear from people as this idea could be important to the whole Free and Open Source Software universe.

Science bootstrap glyph icons

I’m starting a new repository on GitHub which will be an svg based set of scientific icons.

This is just the start of making some free to use (CC-BY-SA 4.0), science based icons that will be compiled into a web font (ttf, oeff, svg etc) and provide a css file to easy drop in placement into many websites in the science fields.

A lot of the icons are meant as inaccurate depictions. The goal is to convey the general idea behind the button or status without having to have every proton in the right place.

If you’d like to help, there are instructions on the github page for contributions.

GPG with Confirmation

I got tired of Evolution email client giving me those horrid error messages when ever I try to email someone who’s key isn’t in my current list of keys.

The design of this is appallingly bad. It discourages the use of GPG rather than encouraging the importing of keys and it makes no mention of helping you acquire keys if possible. It also allows for no additional or optional footer to explain to the recipient that their message couldn’t be encrypted because they don’t use GPG.

While I couldn’t do much about the later, without hacking on the evolution codebase directly. I did do a bit of hacking on the former with a gpg middlware. Yes, when I say hack, I mean HACK. A dangerous and potentially devastating way of wrapping the gpg binary with my own python script that could intercept the evolution call and do work to search, display and add keys to encourage the use of encryption overall.

The design was simple. When we are asked to encrypt for a person who we don’t have the keys for, we do a search. The results are shown in a GUI to the user and they can select a key to use. This then is added to the key ring and used to encrypt the email.

This setup allows for experimentation with user prompting and workflow. It’s not something I would recommend be installed on user’s computers. But for designers and developers, this sort of match-stick making is a valuable platform to build, try, test and rebuild quickly.

I use zenity for the user interface. This is a Gtk command line tool that lets you launch a window from the command line and the interface is good enough to support photos in lists and returning which item was selected. Very cool.

Bellow you will find the script I created for this hack, this is saved to /usr/bin/gpg and gpg is moved to gpg.orig:

#!/usr/bin/python
#
# Wrap the gpg command to provide evolution with a bit of extra functionality
# This is certainly a hack and you should feel very bad about using it.
#
# Public Domain, Authored by Martin Owens  2016
#
import os
import sys
import atexit

from collections import defaultdict
from subprocess import Popen, PIPE, call
from tempfile import mkdtemp, mktemp
from datetime import date
from shutil import rmtree

to_date = lambda d: date(*[int(p) for p in d.split('-')])


class GPG(object):
    keyserver = 'hkp://pgp.mit.edu'
    remote_commands = ['--search-keys', '--recv-keys']

    def __init__(self, cmd='/usr/bin/gpg', local=False):
        self.command = cmd
        self.photos = []
        self.local = local
        self.homedir = mkdtemp() if local else None
        atexit.register(self.at_exit)

    def at_exit(self):
        """Remove any temporary files and cleanup"""
        # Clean up any used local home directory (only if it's local)
        if self.local and self.homedir and os.path.isdir(self.homedir):
            rmtree(self.homedir)

        # Clean up any downloaded photo-ids
        for photo in self.photos:
            if os.path.isfile(photo):
                os.unlink(photo)
            try:
                os.rmdir(os.path.dirname(photo))
            except OSError:
                pass

    def __call__(self, *args):
        """Call gpg command for result"""
        # Add key server if required
        if any([cmd in args for cmd in self.remote_commands]):
            args = ('--keyserver', self.keyserver) + args
        if self.homedir:
            args = ('--homedir', self.homedir) + args

        command = Popen([self.command, '--batch'] + list(args), stdout=PIPE)
        (out, err) = command.communicate()
        self.status = command.returncode
        return out

    def list_keys(self, *keys, **options):
        """Returns a list of keys (with photos if needed)"""
        with_photos = options.get('photos', False)
        args = ()
        if with_photos:
            args += ('--list-options', 'show-photos',
                     '--photo-viewer', 'echo PHOTO:%I')
        out = self(*(args + ('--list-keys',) + keys))

        # Processing the output with this parser
        units = []
        current = defaultdict(list)
        for line in out.split('\n'):
            if not line.strip():
                # We should always output entries if they have a uid and key
                if current and 'uid' in current and 'key' in current:
                    # But ignore revoked keys if revoked option is True
                    if not (current.get('revoked', False) and options.get('revoked', False)):
                        units.append(dict(current))

                current = defaultdict(list)

            elif line.startswith('PHOTO:'):
                current['photo'] = line.split(':', 1)[-1]
                self.photos.append(current['photo'])
            elif ' of size ' in line:
                continue
            elif '   ' in line:
                (kind, line) = line.split('   ', 1)
                if kind == 'pub':
                    current['expires'] = False
                    current['revoked'] = False

                    if '[' in line:
                        (line, mod) = line.strip().split('[', 1)
                        (mod, _) = mod.split(']', 1)
                        if ': ' in mod:
                            (mod, edited) = mod.split(': ', 1)
                            current[mod] = to_date(edited)

                    (key, created) = line.split(' ', 1)
                    current['created'] = to_date(created)
                    (current['bits'], current['key']) = key.split('/', 1)
                elif kind in ('uid', 'sub'):
                    current[kind].append(line.strip())
                else:
                    current[kind] = line.strip()

        return units

    @property
    def default_photo(self):
        if not hasattr(self, '_photo'):
            self._photo = mktemp('.svg')
            with open(self._photo, 'w') as fhl:
                fhl.write("""
  
""")
            self.photos.append(self._photo)
        return self._photo

    def recieve_keys(self, *keys, **options):
        """Present the opotunity to add the key to the user:
         
        Returns
          - True if the key was already or is now imported.
          - False if keys were available but the user canceled.
          - None if no keys were found within the search.

        """
        keys = self.search_keys(*keys)
        if not keys:
            return None # User doesn't have GPG

        # Always use a temporary gpg home to review keys
        gpg = GPG(cmd=self.command, local=True) if not self.local else self

        # B. Import each of the keys
        gpg('--recv-keys', *zip(*keys)[0])

        # C. List keys (with photo options)
        choices = []
        for key in gpg.list_keys(photos=True):
            choices.append(key.get('photo', self.default_photo))
            choices.append('\n'.join(key['uid']))
            choices.append(key['key'])
            choices.append(str(key['expires']))

        if len(choices) / 4 == 1:
            title = "Can I use this GPG key to encrypt for this user?"
        else:
            title = "Please select the GPG key to use for encryption"

        # Show using gtk zenity (easier than gtk3 directly)
        p = Popen(['zenity',
            '--width', '900', '--height', '700', '--title', title,
            '--list', '--imagelist', '--print-column', '3',
              '--column', 'Photo ID',
              '--column', 'ID',
              '--column', 'Key',
              '--column', 'Expires',
            ] + choices, stdout=PIPE, stderr=PIPE)

        # Returncode is generated after communicate!
        key = p.communicate()[0].strip()

        # Select the default first key if one choice.
        # (person pressed ok without looking)
        if not key and len(choices) == 4:
            key = choices[2]

        if p.returncode != 0:
            # Cancel was pressed
            return False

        # E. Import the selected key
        self('--recv-keys', key)
        return self.status == 0

    def is_key_available(self, search):
        """Return False if the email is not found in the local key list"""
        self('--list-keys', search)
        if self.status == 2: # Keys not found
            return False
        # We return true, even if gpg returned some other kind of error
        # Because this prevents us running more commands to a broken gpg
        return True

    def search_keys(self, *keys):
        """Returns a list of (key_id, info) tuples from a search"""
        out = self('--search-keys', *keys)
        found = []
        prev = []
        for line in out.split("\n"):
            if line.startswith('gpg:'):
                continue
            if 'created:' in line:
                key_id = line.split('key ')[-1].split(',')[0]
                if '(revoked)' not in line:
                    found.append((key_id, prev))
                prev = []
            else:
                prev.append(line)
        return found


if __name__ == '__main__':
    cmd = sys.argv[0] + '.orig'
    if not os.path.isfile(cmd):
        sys.stderr.write("Can't find pass-through command '%s'\n" % args[0])
        sys.exit(-13)

    args = [cmd] + sys.argv[1:]
    # Check to see if call is from an application
    if 'GIO_LAUNCHED_DESKTOP_FILE' in os.environ:
        # We use our moved gpg command file
        gpg = GPG(cmd=cmd)
        # Check if we've got a missing key during an encryption, we get the
        # very next argument after a -r or -R argument (which should be
        # the email address)
        for recipient in [args[i+1] for (i, v) in enumerate(args) if v in ('-r', '-R')]:
            # Only check email addresses
            if '@' in recipient:
                if not gpg.is_key_available(recipient):
                    if gpg.recieve_keys(recipient) is None:
                        pass
                        # We can add a footer to the message here explaining GPG
                        # We can't do this, evolution will wrap it all up in a
                        # message structure.
                        #msg = sys.stdin.read()
                        #if msg:
                        #    msg += GPG_TRIED_FOOTER
                        #sys.stdout.write(msg)
                        #sys.exit(0)

    # We call and do not PIPE anything (pass-through)
    try:
        sys.exit(call(args))
    except KeyboardInterrupt:
        sys.exit(-14)

Free Software Faith for the Long Term

I’ve been an advocate for Free and Open Source Software for a long, long time. When I first got into it, it felt right, just, progressive. I struggle with how to communicate that feeling of freedom to others, to make them understand how important Free Software is.

When we talk of Free Software dialectic conflicts, there are two big fronts; the first is the idea that Proprietary software is a /better/ way to make software. Developers get paid, investors make money, huge profits can be poured into research and development. This is your Microsofts and Apples. Let’s ignore that idea today.

The second battle is practicalism. This idea says that it doesn’t matter how the software was made, just that it needs to work. Let’s explore why this idea is an important conflict. Practical solutions often favour the short term and the local. That is, they are solutions which usually a single person will make a decision about what software to use and the criteria most important is the cost (money, time, effort) to get it working in the here and now.

The conflict comes about because often Free Software is a more expensive proposition in the here and now. It’s more expensive because it takes more time to set up, or it’s tools are not tested as much, or not designed as well, or the more insidious reason: the wider world does not support Free Software causing the Free Software solution to be on the loosing end of a powerful network effect.

But proprietary software often has hidden costs. After the initial purchase costs, these are often either societal or long term costs. Societal and long term are the direct opposite of a practical decision. Thus they are not considered, or not valued highly when making the decision.

There is one parallel which I hate to make. Religion. Here is another societal and long term cultural device. Most religions ask participants to give up the bad behaviours in the here and now and be a little more patient for the better life or better after life. I’m horribly simplifying here so please forgive me. But religions mostly work on faith and their evangelicalism pressures people to consider the societal and long term. This is why I think Free Software advocates are so often compared to religious fundermentalists. It’s a cheap shot; it does not follow that faith in Free Software is faith against evidence. That’s just a bad argument.

But it’s worth considering that Free Software is a hard sell precisely because it’s a societal good that requires powerful network effects on it’s side in order to be fully effective. Having a self-sacrificing religion of one is foolish, but a society of good intentions can be a powerful force. We in the Free Software world often have to invest more, pay more and spend more time to make the Free Software world we want to see, and to see it happen for ourselves and our friends and families. But this will only be the case so long as the network effects are against us and I don’t believe they always will be.

Now consider Ubuntu. Here’s platform that tried to move some of the power away from practicamism by making Ubuntu easy to install, easy to use, a joy to behold. Things that are genuinely empowering to Free Software. As it built itself up, the negative network effects started to weaken and Ubuntu users enjoyed for a time, a level of support from the wider world that had not been experienced before.

But that naturally led to the in-fighting. It’s typical for the front runner to be targeted by all the also-ran distributions. The FSF targeted Ubuntu’s practicalist concessions (even though they were fairly minimal), Other distributions ripped Ubuntu and their community apart, trying to block Ubuntu’s success. I’m not saying they meant to do it, or that it was a conspiracy. But that these other communities did not see Ubuntu’s success as their own success and naturally tried to undermine it as humans are likely to do.

So for very human reasons, we’re here with no real champion for Free Software in the practical arena. Ubuntu has fallen for its own hype and is not able to being the Free Software faith with it, even if it was successful. The societal and long term benefits of Free software remain largely unknown to the majority of the world and we wait patiently for a successor that can try again to change the world.

What do you think? Comment below.

Short Text Alert Notification (STAN)

I recently got a new phone, and a new place to keep it. My previous phone was damaged when I sat on it and realized that keeping it in a back pocket was a bad idea. I’m keeping my new phone in a belt pouch with a Velcro seal and it takes me slightly longer to get at the phone than it would if it was in my pocket.

And so I was, a few days ago, messaging people using SMS and then putting my phone away. Several times my interlocutors would send me short affirmative messages which I would have to read by pulling out my phone from it’s pouch, turn it on with it’s (not always first try) security and then read a message that says “OK” or “Yes”.

So I thought; why not have the phone change it’s notification sound for simple messages? A table of possible short messages which would then translate into a different notification sound and allow the user to understand a simple message without having to pick up their phone, unlock it and read the message.

This would certainly make sending short messages slightly more polite as it can seem aggravating sometimes to have to read short messages. Even though it’s not the fault of the sender really.

  1. The idea would require a customized default messaging app for android, or possibly system configuration, I’m unsure how notification sounds are stored.
  2. A research stage where short messages on multiple phones was collected into a corpus of English messages. Each classified into what it probably means.
  3. An additional corpus of emoji messages that can be easily converted into sound. Example a kissing emoji into a kissing sound.
  4. A restriction on the time between the previous message to/from the sender and the notification. Say half an hour. This is to prevent an affirmative messages which arrive a week after the last correspondence from confusing the recipient.
  5. A secondary research phase where a group of people’s would have the app installed (replacing their messaging app) and would self report if the new notification sounds made any difference over a couple of months of use.

This is a sort of imaginary plan, but I could see this being useful. But the only way it would be come mainstream is if it was adopted by Google directly into it’s own messaging app. A stepping stone towards that would be adoption of the modified message app into after market android such as the Cyanogenmod project. The stepping stone would certainly allow more data to be collected about it’s functional use out int he wild.

Another alternative stepping stone would be to approach a manufacturer or telco network. But quite often apps these layers install are seen as bloat-ware and it might be better to avoid that route if possible.

What do you think? Good idea? bad idea?

What is Art? is code Art?

The musings of today’s Thought for Today on BBc Radio Four are often interesting perspectives that drive at something both personal and social. Today’s subject was the concept of modern art, it’s valuation and the way in which artists invest in the art while knowing little about it.

This got me thinking about code. You see code is something that requires an imense amount of creative thinking. Not just problem solving and puzzle mastery; but down right honest to god design and humble craftsmanship to boot. A piece of code must be more than just functional for the user, it must be maintainable in an ever changing world.

This requires that the code be readable and possibly even attractive to potential maintainers as a learning exercise. The best code is obvious where is can be and smartly presented where it needs to be clever. It must deliver it’s cleverness carefully and in reasonable chunks, much like a classical lesson in latin or a course of antibiotics. The code needs to cozy up to the reader and be as familiar with it’s patterns, syntax choices and variable naming conventions as a well worn pair of slippers.

Start using single letter names, odd abbreviations or inventing undocumented artifice and you’ll lose the audience. You’ll alienate the future from your comfortable seat in the past with a smug sense of converse hindsight. The arrogant developer assumes all things are known in the future and all maintainers are themselves or someone very much like themselves. And the trouble with people is, no matter how many you know, there’s always one strange outliers you’ve never met and one day they’ll be looking at your code thinking to themselves that you must have been enjoying your legal high quite a lot on the day you wrote /this/.

So what is art and how does it fit into this whole “understandable code” thought?

I’m not going to pretend there’s not seven billion ways to define art. But I believe art to be “the intentional communication through emotional language”. This means I consider stand up comics to be artists, I consider Fox news to be an arts show and music like rap to be one of the most powerful forms of art around today. But art can be bad like Fox, art can be good like Banksy and that doesn’t detract from it’s medium.

Art can be a failure when it fails to deliver the intended emotions like most modern visual art (to the general population anyway). We can feel disappointed in politicians for failing to be concise and factual, while at the same time marveling at their artistry for using their home spun bull shit to evoke the emotions they want in their audience. It’s wonderfully successful art, and a terrible education for the public. Not that art needs to be true, or that it needs to not be true of course.

Code in this narrowed definition of art, can be art. Sure as above we really want code to be artful as in crafted well; but we also could have code that intends to and successfully delivers an emotion. It has two ways. The usual way is that the code runs a game or some other intended visual art say. It’s the mechanism by which art is delivered and the code in there is part of the whole art.

I remember the radio head “Big Ideas” video that uses a specrum and hard disk array. That delivers art through it’s code is some interesting ways.

But I think most interesting to developers is how their emotions can be engaged by just reading code and repositories. I think source code poetry is a pretty well established way of making art out of code and I really enjoy reading some of it and running it. There are code flowers and other clever mechanisms that evoke wonder and joy as they are compiled and run.

But what of every day code. I think all our code evokes some emotion in those that have to read it and fix it. Mostly this is frustration and annoyance that you didn’t write it in a way more comforting to the reader. But there’s got to be scope here for making functional code that’s beautiful, interesting, passionate, lovely, hateful or just plain fun.

And not just for the user.

What do you think? Can your code be art?

Using PhantomJS with django for d3 testing

I’ve been working with phantomjs recently. It’s a javascript based system wrapper around a webkit backend. This allows one to make requests against a website and run the client side javascript, and make screenshots of the way the webpage would be generated including all css.

I’m using it to generate d3 graphs for testings whereby images of the webpage as saved and compared to expected images.

Of course I’m using python since my websites are all in python and usually django these days. So calling a javascript command and pointing it at a running webserver sounds like a really hard task. But actually django provides a live TestCase which runs a test web server which phantomjs can use as it’s source. It even includes fixtures and other test features which makes it fairly easy to put together the basics of what’s needed.

import logging
from subprocess import Popen, PIPE
from django.test import LiveServerTestCase

class GraphTestCase(LiveServerTestCase):
# Tests and fixtures go here as well as login via self.client (as usual)

def phantom(self, filename, **kwargs):
"""Run a phantomjs script and return True if it was successful"""
pre_args = []

# These cookies require the target js to support them.
cn = settings.SESSION_COOKIE_NAME
if cn in self.client.cookies:
kwargs['cookie-' + cn] = self.client.cookies[cn].value

args = ['--%s=%s' % item for item in kwargs.items()]
cmd = [self.script] + pre_args + [self.get_js_file(filename)] + args

process = Popen(cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True)
out, err = process.communicate()
if process.returncode != 0:
logging.info(out)
logging.error(err)
raise IOError("Process did not complete: %d" % process.returncode)
return out

The above code should be well formatted, but wordpress (or this version of it) doesn’t want to use the right tags for code. :-/