Implementing Python Built-ins in JavaScript¶
General Structure¶
JavaScript versions of Python built-in functions can be found inside the batavia/builtins
directory in the Batavia code. Each built-in is placed inside its own file.
// Example: a function that accepts exactly one argument, and no keyword arguments
var <fn> = function(<args>, <kwargs>) {
// These builtins are designed to be used only inside Batavia, as such they need to ensure
// they are being used in a compatible manner.
// Batavia will only ever pass two arguments, args and kwargs. If more or fewer arguments
// are passed, then Batavia is being used in an incompatible way.
// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments
if (arguments.length !== 2) {
throw new builtins.BataviaError.$pyclass("Batavia calling convention not used.");
}
// We are now checking if a kwargs object is passed. If it isn't kwargs will be null. Like
// obj.keys() in Python we can use Object.keys(obj) to get the keys of an object. If the
// function doesn't need support any kwargs we throw an error.
if (kwargs && Object.keys(kwargs).length > 0) {
throw new builtins.TypeError.$pyclass("<fn>() doesn't accept keyword arguments.");
}
// Now we can check if the function has the supported number of arguments. In this case a
// single required argument.
if (!args || args.length !== 1) {
throw new builtins.TypeError.$pyclass("<fn>() expected exactly 1 argument (" + args.length + " given)");
}
// If the function only works with a specific object type, add a test
var obj = args[0];
if (!types.isinstance(obj, types.<type>)) {
throw new builtins.TypeError.$pyclass(
"<fn>() expects a <type> (" + type_name(obj) + " given)");
}
// actual code goes here
Javascript.Function.Stuff();
}
<fn>.__doc__ = 'docstring from Python 3.4 goes here, for documentation'
modules.export = <fn>
Adding Tests¶
The tests corresponding to Batavia implementations of built-ins are available inside
tests/builtins
. The Batavia test infrastructure includes a system to check the compatibility of
JavaScript implementation of Python with the reference CPython implementation.
It does this by running a test in the Python interpreter, and then running the same code using Batavia in the Node.js JavaScript interpreter. It will compare the output in both cases to see if they match. Furthermore the test suite will automatically test the builtin against values of all data types to check if it gets the same response across both implementations.
In many cases these tests will not cover everything, so you can add your own. For an example look at
the test_bool.py
file in tests/builtins
. You will see two classes with test cases,
BoolTests
and BuiltinBoolFunctionTests
. Both derive from TranspileTestCase
which
handles running your code in both interpreters and comparing outputs.
Let’s look at some test code that checks if a the Batavia implementation of bool
can handle a
bool-like class that implements __bool__
.
def test_bool_like(self):
self.assertCodeExecution("""
class BoolLike:
def __init__(self, val):
self.val = val
def __bool__(self):
return self.val == 1
print(bool(BoolLike(0)))
print(bool(BoolLike(1)))
""")
The assertCodeExecution
method will run the code provided to it in both implementations. This
code needs to generate some output so that the output can be compared, hence the need to print the
values.
Process¶
For a given function, run functionname.__doc__ in the Python 3.4 repl
Copy the docstring into the doc
Run the function in Python 3.4
Take a guess at the implementation structure based on the other functions.
Copy the style of the other implemented functions