Python duck typing (or automatic interfaces)

Working with python full-time now I, more often, have insights about its way of doing things (or Zen of python for some people). One thing that blows my mind every time I find out in my daily saga is the duck typing thing. Being honest, it is not a python exclusive feature, since almost every dynamic language presents that behavior. But be nice, I like Python.

If some reader doesn’t know what it is, duck typing is a feature of a type system where the semantics of a class is determined by his ability to respond to some message (method or property). The canonical example (and the reason behind the name) is the duck testIf it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.

class Duck:
   def quack():
      print('Quack!')
class Goose:
   def quack():
      print('Quack')
Goose().quack()
>> Quack!
Duck().quack()
>> Quack!

Ok, but what is the catch? There is nothing new here. Yes, nothing new. What I’m trying to show is the implications that this behavior has in the architecture of a system. And I’m advocating that this is a good implication. Let me explain.

Imagine a class named Car:

class Car:
   def __init__(self, engine):
      self.engine = engine
   def run():
       self.engine.turn_on()

This is a classical example of dependency injection. My class Car receives an instance of an engine and use it in the run method, where it calls the turn_onmethod. Note that my Car does not depends on any concrete implementation of engine. And I’m not importing any other type! Just using a dependency injected instance of something that responds to a turn_on message. I could say my class Car depends on an interface. But I did not have to declare it. It is an automatic interface!

In a language without duck typing I’ll probably have to declare an explicit interface named for example IEngine, have the implementation (for example EngineV1) and explicit define my Car parameter to be an implementation of IEngine.

interface IEngine {
    void turnOn();
}

public class EngineV1 implements IEngine {
    public void turnOn() {
        // do something here
    }
}

public class Car {
    public Car(IEngine engine) {
       this.engine = engine;
    }
    public void run() {
        this.engine.turnOn();
    }
}

Ugh! How much code.

So, as you can see the effect is the same. My class Car in both cases depends on an interface. But in the first case there are less code and I don’t need to explicit implement the interface. It’s already implemented if I define the method turn_on.

The weakness

I can see two problems here.

  1. Fat interface

The first is an incentive to interface bloat. As we don’t explicit define the API this can result in an interface with too much granular methods.

2. Unnamed dependencies

As we don’t know the name of the interfaces that a class depends we don’t have an automatic dependency tree. Because of this, all dependency injection frameworks have to workaround the dependency resolution in some way.

Injector lib, for example, was obligated to implement the concept of Keys to allow dependency tree to be resolved automatically. Keys, in injector terms, are nothing more than a way to name/identify an interface that a class implements.

Conclusion

I don’t think that duck typing was intentionally designed to have this behavior. I don’t even think that duck typing is intentionally designed in a language at all but instead it is a side-effect of the dynamic nature of those type systems. But it is interesting what it can brings in terms of conciseness and low-coupling to the system design and architecture.

Making a webpack python loader

For some time I was an observer of initiatives in using python in the browser. A long time ago I was a follower of Pyjamas/PyJS project, including watching in real time the leadership war between some of its contributors. Now we have a plethora of projects trying to make this a reality (1, 2, 3, 4, etc).

With the new wave of language transpilers for javascript that dream turned to be possible and almost inevitable. Without knowing the scope of each project I always had the impression that BabelJS would be the best place to make that process. But after investigating a little more and have used in some projects I realized that WebPack would be the right way to do that.

Last week I decided to experiment with this idea. I knew that PyJS (old Pyjamas) would not be the best option for the transpilation. After the leadership war the project almost died. Skulpt could be a strong choice if it wasn’t restricted to Python 2. The Transcrypt project seemed to be appropriated since supports Python 3 and the option for fast code generation (at least is what his slogan says) is a must, so I choose it.

(Edited: RapydScript and Brython could also be a good choice. I will try those in the future)

The next step would be implement the webpack loader. For that I have used the webpack documentation as reference (obvious) but also those loaders as example:

To implement a webpack loader we need to implement and export a javascript function that receives the original source code (python) and returns the javascript transpiled code as a string. The loader when installed in the webpack.config.js file will join the webpack transformation chain.

Async

Since we’ll use the node filesystem api to read and write the file we need to use the async webpack loaders api. For that we need just to call the this.async webpack method. This method returns a callback that we need to call when our processing was finished.

The canonical result is listed here (you can see the complete result in the repository)

const cmd = require('node-cmd')
const fs = require('fs');
const path = require('path');

module.exports = function (source) {

    var callback = this.async();

    var entry = this._module.resource;

    var name = path.basename(entry, ".py");

    cmd.get('transcrypt -b -n ' + entry, function(err, data, stderr) {
        if (!err) {
            js = fs.readFileSync("./__javascript__/" + name + ".js", "utf8")
            callback(null, js);
        }
        else {
            console.log("Some error occurred on Transcrypt compiler execution. Did you have installed Transcrypt? If no, please run `pip install transcrypt`");
            console.log("Error: " + err);
        }

    });
}

Let’s use it!

if someone have interest in helping I published the source code in github and uploaded to npm (py-loader). In the repository there are an example but if you want a quickstart just follow the steps below:

Install transcrypt (you will need pip)

pip install transcrypt

Install the package

npm install --save-dev py-loader

Add the loader inside your webpack.config.js

function cwd (path) {
  return require('path').resolve(__dirname, path)
}

module.exports = {
  entry: cwd('main.js'),

  output: {
    path: cwd('dist'),
    filename: 'app.js'
  },

  module: {
    rules: [
      {
        test: /\.py$/,
        loader: 'py-loader'
      }
    ]
  }
}

Create a python file named hello.py

def hello_world():
   print("Hello from python")
   print("document {}".format(document))

Create a javascript file (main.js) that imports the python file

import Hello from './hello.py'

Include the generated file in a html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body onload="hello.hello_world()">
  
https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js http://app.js </body> </html>

Execute the webpack build process

webpack --progress --profile --colors

Open the html in the browser. Voilá! You should see the “Hello from python” message in the browser console.

It also works with webpack-dev-server so you can have the same live-reload experience you have with javascript!

webpack-dev-server --progress --colors --port 8080 --content-base .

The future

There are still many things to do for making our dream to program Python in the browser (seamless) come true. Below I list some:

  1. Verify the possibility in Transcrypt to use an in-memory transpilation instead of saving to a temporary file. This could improve the speed of the process.
  2. Verify the viability in using another javascript libraries (like axios) inside python code.
  3. Experiment with other python/javascript transpilers (RapydScript and brython are in the queue).