Skip to content
p2k edited this page Sep 13, 2010 · 4 revisions

The behavior of PyCow can be seen at best on some examples.

Note: PyCow currently removes any comments in the code.

Namespaces

PyCow can wrap your Python module in a namespace (commandline: pycow.py -n namespace inputfile.py).
If you do that, PyCow will look for a optional `all` variable in your module and make only the specified
classes/functions publicly accessible.

Python:

__all__ = ["Someclass", "a_function"]

@Class
class Someclass(object):
    def __init__(self, something):
        self.something = something

def a_function():
    print "hello"

def another_function():
    print "test"

Resulting JavaScript:

var namespace = (function () {
    var Someclass = new Class({
        initialize: function (something) {
            this.something = something;
        }
    });

    var a_function = function () {
        dbgprint("hello");
    };

    var another_function = function () {
        dbgprint("test");
    };

    return {
        Someclass: Someclass,
        a_function: a_function
    }
})();

Imports

PyCow does not handle imports for now (it will probably in the next release), so import statements are simply ignored.

Python:

from decorators import Implements, Class
from utils import Events, Options

Resulting JavaScript:

/* from decorators import Implements, Class */;
/* from utils import Events, Options */;

Classes, subclasses and functions

I have ported some of MooTools’ functionality to Python in order to keep semantics clean
between the two languages. This introduces the “Class” decorator, which fixes an issue
and adds the “implements” method to the class. Note that class decorators are supported
in Python 2.6 and later only, please upgrade your installation if neccessary.

Python:

@Class
class Someclass(object):
    """
    Docstring of class
    """
    def __init__(self, something): # PyCow removes 'self' from method declarations
        """
        Docstring of constructor/method
        """
        self.something = something + "string literal" # PyCow replaces 'self' with 'this'

    def a_method(self, otherthing):
        print self.something + otherthing # 'print' is translated to 'dbgprint'

    def another_method(self):
        obj = SomeExtension() # PyCow can infer types of callables (even declared later); here it will place 'new', because SomeExtension is a class
        self.member = "test"

@Class
class SomeExtension(Someclass):
    def __init__(self):
        super(SomeExtension, self).__init__("1234") # PyCow correctly treats the 'super' function of Python; here it's the call to the super constructor

    def a_method(self, otherthing):
        super(SomeExtension, self).a_method(otherthing) # Here it's a call to the super class' method
        print otherthing, self.something

def a_function():
    """
    Docstring of function

    Note that PyCow removes
            whitespaces.



    And normalizes newlines.
    """
    test = 2 # PyCow automatically declares local variables
    test = 4 # once
    print test+     2 # Because PyCow parses semantics only, it will ignore whitespaces (but avoid to do something like that anyways)

obj = Someclass("a lengthy ")

obj.a_method("test") # PyCow's type inference does not include types of variables (atm)

a_function() # PyCow does not put "new" here, because a_function is a simple function

Resulting JavaScript:

/**
 * Docstring of class
 */
var Someclass = new Class({
    /**
     * Docstring of constructor/method
     */
    initialize: function (something) {
        this.something = something + "string literal";
    },
    a_method: function (otherthing) {
        dbgprint(this.something + otherthing);
    },
    another_method: function () {
        var obj = new SomeExtension();
        this.member = "test";
    }
});

var SomeExtension = new Class({
    Extends: Someclass,
    initialize: function () {
        this.parent("1234");
    },
    a_method: function (otherthing) {
        this.parent(otherthing);
        dbgprint(otherthing, this.something);
    }
});

/**
 * Docstring of function
 *
 * Note that PyCow removes
 * whitespaces.
 *
 * And normalizes newlines.
 */
var a_function = function () {
    var test = 2;
    test = 4;
    dbgprint(test + 2);
};

var obj = new Someclass("a lengthy ");

/* Warning: Cannot infer type of -> */ obj.a_method("test");

a_function();

The “Implements” decorator

There is another feature in MooTools besides the Extends property of classes: The Implements property.

From the MooTools documentation:

Implements is similar to Extends, except that it overrides properties without inheritance. Useful when
implementing a default set of properties in multiple Classes.

This is often used in conjunction with the “Options” and “Events” utility classes of MooTools. I have
ported them to Python for your convenience. The following example also explains what is fixed by the
“Class” decorator.

Python:

@Implements(Options)
@Class
class ClassWithOptions(object):
    """
    A class with implements Options using the `Implements` decorator.
    This is MooTools functionality ported to Python.
    """

    # Note: In Python semantics, this declares a class-bound member, but MooTools
    # sees this as object-bound members. The Class decorator will convert all
    # class-bound members to object-bound members on instantiation.
    options = {
        "name": "value",
        "foo": "bar",
    }

    def __init__(self, options):
        self.setOptions(options)
        print self.options["foo"], self.options["name"]

    # Static methods supported
    @staticmethod
    def somestatic(input):
        print "Static " + input

Resulting JavaScript:

/**
 * A class with implements Options using the `Implements` decorator.
 * This is MooTools functionality ported to Python.
 */
var ClassWithOptions = new Class({
    Implements: Options,
    options: {
        name: "value",
        foo: "bar"
    },
    initialize: function (options) {
        /* Warning: Cannot infer type of -> */ this.setOptions(options);
        dbgprint(this.options.foo, this.options.name);
    }
});
ClassWithOptions.somestatic = function (input) {
    dbgprint("Static " + input);
};

Variable scope

Python:

global x # Because of the 'global' statement
x = "hello again" # PyCow does not declare x as local here

def another_function():
    global x
    x = "go ahead" # and here
    return x

Resulting JavaScript:

x = "hello again";

var another_function = function () {
    x = "go ahead";
    return x;
};

If/else statement

Python:

if True:
    print "Welcome"
    if False:
        pass
    else:
        print "Nested if"
else:
    print "You're not welcome..."

Resulting JavaScript:

if (true) {
    dbgprint("Welcome");
    if (false)
        /* pass */;
    else
        dbgprint("Nested if");
}
else
    dbgprint("You're not welcome...");

While statement

Python:

i = 0
while i < 3 and not False: # While statement
    print i
    i += 2 # Assignment operator
    i += 1 # special case

Resulting JavaScript:

var i = 0;
while (i < 3 && !false) {
    dbgprint(i);
    i += 2;
    i++;
}

For statement

The “for” statement is a special case in Python. There is always a list (or generator) which is
iterated; this behaviour is just like the “foreach” statement in other languages. Because of that,
and some other side effects, Python “for” statements cannot be directly converted to JavaScript
“for” statements. PyCow solves this by using Java-Style iterators which come with the PyCow
runtime script.

Python:

for j in xrange(3): # For statement (xrange)
    print j

for j in xrange(1,4): # For statement (xrange; with start)
    print j

for j in xrange(1,4,2): # For statement (xrange; with start and step)
    print j

for j in xrange(4,1,-1): # For statement (xrange; with start and step backwards)
    print j

i = [1,2,3]
for j in i: # For statement (simple variable)
    print j

for j in ["a","b","c"+"d"]: # For statement (arbitrary expression)
    print j

Resulting JavaScript:

for (var __iter0_ = new XRange(3); __iter0_.hasNext();) {
    var j = __iter0_.next();
    dbgprint(j);
}
delete __iter0_;

for (var __iter0_ = new XRange(1, 4); __iter0_.hasNext();) {
    j = __iter0_.next();
    dbgprint(j);
}
delete __iter0_;

for (var __iter0_ = new XRange(1, 4, 2); __iter0_.hasNext();) {
    j = __iter0_.next();
    dbgprint(j);
}
delete __iter0_;

for (var __iter0_ = new XRange(4, 1, -1); __iter0_.hasNext();) {
    j = __iter0_.next();
    dbgprint(j);
}
delete __iter0_;

var i = [1, 2, 3];

for (var __iter0_ = new _Iterator(i); __iter0_.hasNext();) {
    j = __iter0_.next();
    dbgprint(j);
}
delete __iter0_;

for (var __iter0_ = new _Iterator(["a", "b", "c" + "d"]); __iter0_.hasNext();) {
    j = __iter0_.next();
    dbgprint(j);
}
delete __iter0_;

Lists and dictionaries

Python:

f = lambda x: x*2 # Lambda functions

a = [1,2,3,f(2)] # List expression

print a[1:3] # Slicing

b = {} # Empty dictionary

# Dictionary with strings and numbers as indices
b = {"a": 1, "b": 2,
     1: "x", 2: "y",
     "-test-": 1+2, "0HAY0": "a"+"B"}

# Accessing subscript (simple string)
print b["a"]

# Accessing subscript (other string; assignment)
b["-test-"] = 3

# Accessing subscript (number)
print b[1]

# Deleting from map
del b["a"]

Resulting JavaScript:

var f = function (x) {return x * 2;};

var a = [1, 2, 3, /* Warning: Cannot infer type of -> */ f(2)];

dbgprint(a.slice(1, 3));

var b = {};

b = {
    a: 1,
    b: 2,
    1: "x",
    2: "y",
    "-test-": 1 + 2,
    "0HAY0": "a" + "B"
};

dbgprint(b.a);

b["-test-"] = 3;

alert(b[1]);

delete b.a;

Special Utility Classes

The classes “Hash” and “Array” have been back-ported to Python and
thus can be used in both languages.