From fa9452f54750b7c118b724c201394b4a0eed9db7 Mon Sep 17 00:00:00 2001 From: awildtechno Date: Tue, 11 Jun 2019 16:26:56 -0700 Subject: [PATCH 01/17] Updated the how-tos with a bunch of stuff. Split out tests to its own section. Added a tour of the code. Fixed a grammar error. --- docs/how-to/builtins.rst | 79 ++++++++++---------- docs/how-to/contribute-docs.rst | 2 +- docs/how-to/contribute-tests.rst | 118 ++++++++++++++++++++++++++++++ docs/how-to/index.rst | 2 + docs/how-to/tour.rst | 122 +++++++++++++++++++++++++++++++ 5 files changed, 284 insertions(+), 39 deletions(-) create mode 100644 docs/how-to/contribute-tests.rst create mode 100644 docs/how-to/tour.rst diff --git a/docs/how-to/builtins.rst b/docs/how-to/builtins.rst index 1403a4e35..50eafffe8 100644 --- a/docs/how-to/builtins.rst +++ b/docs/how-to/builtins.rst @@ -22,7 +22,7 @@ directory in the Batavia code. Each built-in is placed inside its own file. 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 + // 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) { @@ -46,59 +46,62 @@ directory in the Batavia code. Each built-in is placed inside its own file. Javascript.Function.Stuff(); } - .__doc__ = 'docstring from Python 3.4 goes here, for documentation' + .__doc__ = 'docstring from Python 3.x goes here, for documentation' modules.export = +Building Blocks of Batavia +-------------------------- -Adding Tests ------------- +This section is a quick reference for things you'll see often in the codebase. -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. +builtins.js +*********** -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. +This contains all of the native Python builtin functions, like ``str``, ``len``, and ``iter``. -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. +When dealing with Python types, many of the native JavaScript operations have been overriden to +try to use these first. For instance, .toString() will often just call the object's __str__. -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__``. +Note again that *args* and *kwargs* are required for most builtins, even if empty. -.. code-block:: Python +types.js +******** - def test_bool_like(self): - self.assertCodeExecution(""" - class BoolLike: - def __init__(self, val): - self.val = val +This contains all of the native Python types that have been implemented in Batavia. It also has some helper functions: - def __bool__(self): - return self.val == 1 - print(bool(BoolLike(0))) - print(bool(BoolLike(1))) - """) +* ``types.js2py`` Converts a native JavaScript type to a corresponding Batavia type. +* ``types.isinstance`` checks to see if the object is a Python instance of the corresponding type. +* ``types.isbataviainstance`` checks to see if the object is an instance of any Batavia type. +* ``types.type_name`` get the name of the type. -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. +Where possible, use Python types from types.js instead of native JavaScript objects and types. +This allows us to avoid things like comparing ``Object.prototype.constructor``. Instead, use these helpers! +core/callables.js +***************** -Process ----------- +These methods ensure that all Python code is executed using the proper __call__ procedure, which could occasionally +have something special built in or be overriden or decorated by the programmer. -For a given function, run `functionname.__doc__` in the Python 3.4 repl +* ``callables.call_function`` Invokes a function using its __call__ method if possible. If not, just call it normally. +* ``callables.call_method`` Calls a class method using the call_function specification above. +* ``callables.iter_for_each`` Exhausts an iterable using the call_function specification above. -Copy the docstring into the doc +As a general rule, use the builtin where possible. If no builtin is available, use the appropriate version +of ``call_function`` instead of calling Python functions and methods directly. An example: -Run the function in Python 3.4 +.. code-block:: javascript + + // Avoid this + my_thing.__repr__() + + // Better + const callables = require('./core/callables.js') + callables.call_method(my_thing, '__repr__', [], {}) -Take a guess at the implementation structure based on the other functions. + // Best + const repr = require('./builtins.js').repr + repr(my_thing, [], {}) -Copy the style of the other implemented functions +Note the use of the Batavia calling convention in the two cases above! \ No newline at end of file diff --git a/docs/how-to/contribute-docs.rst b/docs/how-to/contribute-docs.rst index 3ef7f5d32..ff8bc02d1 100644 --- a/docs/how-to/contribute-docs.rst +++ b/docs/how-to/contribute-docs.rst @@ -28,6 +28,6 @@ Create the static files: :: $ make html -Check for any errors and,if possible, fix them. +Check for any errors and, if possible, fix them. The output of the file should be in the ``_build/html`` folder. Open the file you changed in the browser. diff --git a/docs/how-to/contribute-tests.rst b/docs/how-to/contribute-tests.rst new file mode 100644 index 000000000..e12013ef5 --- /dev/null +++ b/docs/how-to/contribute-tests.rst @@ -0,0 +1,118 @@ +Implementing Tests in Batavia +============================= + +Basic Test Structure +-------------------- + +Batavia's job is to run a browser-compatible Python compiler, which takes valid Python as input and runs it. +Therefore, tests should test that the output of the Batavia compiler matches the output of CPython:: + + print('Hello') # Code to test + Hello # CPython output + Hello # Batavia output + # Outputs match. Test passes! + +This test structure is simple and effective. It's used in almost every test we've written. + +Adding Tests +------------ + +In many cases, existing tests will not cover everything. Feel free to add your own! + +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. + +These all tests derive from ``TranspileTestCase``, which handles running your code in both interpreters +and comparing outputs. 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``. + +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__``. + +.. code-block:: Python + + 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. **Code that is not being printed is not being tested.** + +Template +-------- + +.. code-block:: python + + def test__(self): + # Valid Python code to be tested. + code = """ + print('>>> print()') + print() + """ + self.assertCodeExecution(code) + +This code block provides a printout of the code being run as well as the output of the code, +which can be very useful for debugging in test cases where a decent amount of code is being run. + +Testing for Errors +------------------ + +Since we're compiling Python, we need to ensure that errors for all of the builtins are thrown correctly. +We also want to ensure that we're not accepting the wrong errors in our tests. Simply include a try/except +block in your test. + +.. code-block:: python + + def test_some_error(self): + code = """ + try: + code_that_raises_a_ValueError() + except ValueError as err: + print(err) + """ + self.assertCodeExecution(code) + +Remember to catch the specific error you want, and then print the error. **Code that is not being printed +is not being tested.** + +Output Cleaners +--------------- + +In some cases, the test output will vary. ``TranspileTestCase`` will automatically apply some common output +cleanup for you. Some cases will need more or less cleanup. If you run your Python code directly in the REPL, +and the output differs from the test case output, you may need to modify what cleanup steps are being run. + +As such, ``assertCodeExecution`` accepts optional ``js_cleaner`` and ``py_cleaner`` objects. These can be provided by +the ``@transform`` decorator, located in ``tests/utils/output_cleaners.py``. Here's an example: + +.. code-block:: python + + @transform(float_exp=False) + def test_some_floats(self, js_cleaner, py_cleaner): # + Cleaner objects as arguments + code = ... + self.assertCodeExecution(code, js_cleaner=js_cleaner, py_cleaner=py_cleaner) # + Cleaner objects again + +This code means that the output of floating-point numbers will not be normalized using a regex. Refer to other +test cases and the docstring for ``@transform`` for more examples. + +Node/Python Crashes +------------------- + +If the CPython or JavaScript code crashes outright, UnitTest struggles. For instance, +``confused END_FINALLY`` in the middle of your test output tends to mean that the JavaScript code threw an +uncaught exception, causing Node to stop. It's hard for UnitTest to pull the details out of this type of thing +since that error occurred in the virtual machine. + +These types of errors will often appear above the test case as a crash report instead of in the usual section for the +output of your test's print() statements. Look there for clues. \ No newline at end of file diff --git a/docs/how-to/index.rst b/docs/how-to/index.rst index 1453651ac..22bf5c270 100644 --- a/docs/how-to/index.rst +++ b/docs/how-to/index.rst @@ -10,7 +10,9 @@ How-to guides are recipes that take the user through steps in key subjects. They :maxdepth: 1 :glob: + Tour of the Code Contribute to Batavia's code Contribute to Batavia's documentation + Follow the Goat: Batavia's Test Suite Implementing Python Built-ins in JavaScript Adding a module and testing it diff --git a/docs/how-to/tour.rst b/docs/how-to/tour.rst new file mode 100644 index 000000000..1d14be4f0 --- /dev/null +++ b/docs/how-to/tour.rst @@ -0,0 +1,122 @@ +Tour of the Code +================ + +Before getting started, it's nice to know a bit about how the project is structured and where +to look for examples. This document aims to provide a brief tour of the +important features of the code. It's aimed at new contributors, but frequent flyers can +skip down to the bottom for a quick reference. + +General Structure +----------------- + +Core Code +********* + +Batavia implements the Python native types, builtins, and standard library partially in JavaScript. +This ensures quick execution and less compiling of the compiler. These are orgainzed into the +``builtins``, ``core``, ``modules``, ``stdlib``, and ``types`` files of the main ``/batavia`` directory, with +corresponding subdirectories. Alongside the virtual machine and test suite, this code makes +up the bulk of Batavia. New contributors should start with the ``types`` and ``builtins`` sections +for the best examples to review and copy from. These implementations are the foundation of Python as you know it and +should be immediately familiar to a Python developer. + +Support +******* +You'll also notice folders for tests, docs, and a few other bits and bobs, like ``testserver``, which is +a Django project that allows code execution. Contributions to the +tests and documentation are always welcome and are great ways to familiarize yourself with the +code and meet the other contributors. + +The Core Code +------------- + +A Word on Args & Kwargs +*********************** + +Batavia's implementations of various builtin functions +often **require** ``args`` and ``kwargs`` as input. Here's an example of calling +the repr of a ``my_thing`` object: ``builtins.repr(my_thing, [], {})`` + +The empty [] and {} arguments represent empty argument and keyword argument parameters. +This mimics how Python handles function arguments behind the scenes, and it's important! + +For instance, what happens when you pass a keyword argument into a list? You might say, +"list() doesn't take keyword arguments." In actuality, the list function does receive those +arguments, and the result is that it throws ``TypeError: '' is an invalid keyword +argument for this function`` + +Batavia needs those arguments explicitly specified in a standard format so that it can +check for that case and generate the correct error. The below code examples all use this calling +convention, and you'll be up to your knees in ``BataviaErrors`` if you're not aware of it. + +Building Blocks of Batavia +************************** + +This section is a quick reference for the most common code you'll see. + +builtins.js +^^^^^^^^^^^ + +.. code-block:: javascript + + var builtins = require('./builtins.js') + builtins.abs([-1], {}) // equivalent to abs(-1) + +This contains all of the native Python builtin functions, like ``str``, ``len``, and ``iter``. + +When dealing with Python types, many of the native JavaScript operations have been modified to +try to use builtins first. For instance, .toString() will often just call the object's __str__ if +possible. Still, the best practice is to use the builtins and types wherever possible. + +types.js +^^^^^^^^ + +.. code-block:: javascript + + var types = require('./types.js) + var my_dict = new types.dict([], {}) + +This contains all of the native Python types that have been implemented in Batavia. It also has some helper functions: + +* ``types.js2py`` Converts a native JavaScript type to a corresponding Batavia type. +* ``types.isinstance`` checks to see if the object is a Python instance of the corresponding type. +* ``types.isbataviainstance`` checks to see if the object is an instance of **any** Batavia type. +* ``types.Type.type_name`` get the name of the type. + +This allows us to avoid ugly things like comparing ``Object.prototype.constructor``. Instead, +use ``types.isinstance`` or ``types.isbataviainstance``. Secondly, it's important that the inputs to Python +types are Pythonized themselves where needed. You should not be making a list() of JavaScript arrays, for +instance. That doesn't make sense! (It may even pass some tests, which is dangerous.) + +core/callables.js +^^^^^^^^^^^^^^^^^ + +These methods ensure that all Python code is executed using the proper __call__ procedure, which could be +overriden or decorated by the programmer. + +* ``callables.call_function`` Invokes a function using its __call__ method if possible. If not, just call it normally. +* ``callables.call_method`` Calls a class method using the call_function specification above. +* ``callables.iter_for_each`` Exhausts an iterable using the call_function specification above. + +As a general rule, use the builtin where possible. If no builtin is available, use the appropriate version +of ``call_function`` instead of calling Python functions and methods directly. An example: + +.. code-block:: javascript + + // Avoid this + my_thing.__repr__() + + // Better + var callables = require('./core/callables.js') + callables.call_method(my_thing, '__repr__', [], {}) + + // Best + var repr = require('./builtins.js').repr + repr(my_thing, [], {}) + +Note the use of the Batavia calling convention in the two cases above! + +/core/version.js +^^^^^^^^^^^^^^^^ +Some helper functions for distinguishing the version of Python that's running. Outputs +vary from version to version, so it's nice to have this handy. \ No newline at end of file From 9705679951435ab15799081d1f652dfc26a7d597 Mon Sep 17 00:00:00 2001 From: awildtechno Date: Thu, 13 Jun 2019 10:43:09 -0700 Subject: [PATCH 02/17] Updated builtins.rst to include a better breakdown and more information. --- docs/how-to/builtins.rst | 154 ++++++++++++++++++++++----------------- 1 file changed, 86 insertions(+), 68 deletions(-) diff --git a/docs/how-to/builtins.rst b/docs/how-to/builtins.rst index 50eafffe8..9b2e569dc 100644 --- a/docs/how-to/builtins.rst +++ b/docs/how-to/builtins.rst @@ -1,107 +1,125 @@ Implementing Python Built-ins in JavaScript =========================================== +Process +------- + +The first thing to do when adding anything to Batavia is to play around a bit with it in the Python REPL:: + + >> list() + [] + >> [] + [] + >> list(4) + Traceback (most recent call last): + File "", line 1, in + TypeError: 'int' object is not iterable + +Your goal is to find out how the function responds to various inputs and outputs. You may also +want to consult the offical documentation. Once you're a little familiar, you can add your +implementation to Batavia. + 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. +directory in the Batavia code. Each built-in is placed inside its own file. These builtins are +designed to be used only inside Batavia, as such they need to ensure +they are being used in a compatible manner. + +Each builtin function will receive arguments and keyword arguments and needs to handle them, +even if the result is throwing an error. Args should be an array, and kwargs should be a +JavaScript object. The first thing to do is check that both were passed in. .. code-block:: javascript // Example: a function that accepts exactly one argument, and no keyword arguments - var = function(, ) { - // 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 + var = function(args, kwargs) { + // Always add this code. 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("() doesn't accept keyword arguments."); - } +Next, we need to validate the arguments are correct. We can use JavaScript's ``Object.keys()`` to +get the keys of an object. If the function pass in the , we will check the Python REPL to see +what kind of error and throw it. - // 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("() expected exactly 1 argument (" + args.length + " given)"); - } +.. tabs:: - // If the function only works with a specific object type, add a test - var obj = args[0]; - if (!types.isinstance(obj, types.)) { - throw new builtins.TypeError.$pyclass( - "() expects a (" + type_name(obj) + " given)"); - } + .. group-tab:: Python REPL - // actual code goes here - Javascript.Function.Stuff(); - } + .. code-block:: - .__doc__ = 'docstring from Python 3.x goes here, for documentation' - - modules.export = + >> list(a=1) + TypeError: list() doesn't accept keyword arguments. + >> list(1, 2, 3) + TypeError: list() expected exactly 1 argument (3 given) -Building Blocks of Batavia --------------------------- + .. group-tab:: Batavia Code -This section is a quick reference for things you'll see often in the codebase. + .. code-block:: javascript -builtins.js -*********** + if (kwargs && Object.keys(kwargs).length > 0) { + throw new exceptions.TypeError.$pyclass("() doesn't accept keyword arguments."); + } -This contains all of the native Python builtin functions, like ``str``, ``len``, and ``iter``. + if (!args || args.length !== 1) { + throw new exceptions.TypeError.$pyclass("() expected exactly 1 argument (" + args.length + " given)"); + } -When dealing with Python types, many of the native JavaScript operations have been overriden to -try to use these first. For instance, .toString() will often just call the object's __str__. + // If the function only works with a specific object type, add a test + var obj = args[0]; + if (!types.isinstance(obj, types.)) { + throw new exceptions.TypeError.$pyclass( + "() expects a (" + type_name(obj) + " given)"); + } -Note again that *args* and *kwargs* are required for most builtins, even if empty. + Useful functions are ``types.isinstance``, which checks for a match against a Batavia type or list, + of Batavia types, ``types.isbataviainstance``, which checks for a match against any Batavia instance, + ``Object.keys(kwargs)`` for dealing with kwargs, and JavaScript's ``for in``, ``for of``, and + ``Array.forEach`` loops for iterating over the JavaScript arrays and objects. -types.js -******** + Note also the format for errors: ``throw new exceptions..$pyclass``. -This contains all of the native Python types that have been implemented in Batavia. It also has some helper functions: +Returning a value +***************** -* ``types.js2py`` Converts a native JavaScript type to a corresponding Batavia type. -* ``types.isinstance`` checks to see if the object is a Python instance of the corresponding type. -* ``types.isbataviainstance`` checks to see if the object is an instance of any Batavia type. -* ``types.type_name`` get the name of the type. +Builtins implement Python functions and should return a Python object. +Batavia implementations of all Python types are located in ``/batavia/types.js``. +JavaScript imports use the ``require`` keyword and can be imported inline or at +the top of the file. Inline imports can be preferable in some cases. -Where possible, use Python types from types.js instead of native JavaScript objects and types. -This allows us to avoid things like comparing ``Object.prototype.constructor``. Instead, use these helpers! +.. code-block:: javascript -core/callables.js -***************** + ... -These methods ensure that all Python code is executed using the proper __call__ procedure, which could occasionally -have something special built in or be overriden or decorated by the programmer. + Tuple = require('../types.js').Tuple + return new Tuple(my, results, here) + } -* ``callables.call_function`` Invokes a function using its __call__ method if possible. If not, just call it normally. -* ``callables.call_method`` Calls a class method using the call_function specification above. -* ``callables.iter_for_each`` Exhausts an iterable using the call_function specification above. +Documentation +************* -As a general rule, use the builtin where possible. If no builtin is available, use the appropriate version -of ``call_function`` instead of calling Python functions and methods directly. An example: +Finally, add the docstring to the function object. In JavaScript, like in Python, functions +are first-class objects and can have additional properties. .. code-block:: javascript - // Avoid this - my_thing.__repr__() + .__doc__ = 'docstring from Python 3.x goes here, for documentation' + + modules.export = - // Better - const callables = require('./core/callables.js') - callables.call_method(my_thing, '__repr__', [], {}) +Tests +***** - // Best - const repr = require('./builtins.js').repr - repr(my_thing, [], {}) +No implemenation for a project like this is complete without tests. Check out the other sections for +more details on test structure. Tests are located in ``/tests`` in a similar folder structure to the +core code, and most test files have already been created. Some things that should almost always be +tested: -Note the use of the Batavia calling convention in the two cases above! \ No newline at end of file +* Write a test or three to ensure your function returns the correct output with some normal inputs. +* Think of a few weird inputs that could throw off your code (or future code). Test them. +* If you are throwing an error (excluding ``BataviaError``) anywhere, write a test for that. +* If you accounted for an edge case (look for an ``if`` statement), +* Check out the official documentation (https://docs.python.org/3/) for more edge cases \ No newline at end of file From 1af71c9ae2a11cd6554d4a919c408a9275a33bb1 Mon Sep 17 00:00:00 2001 From: awildtechno Date: Thu, 13 Jun 2019 10:55:56 -0700 Subject: [PATCH 03/17] Added introduction. Bit of cleanup as well. --- docs/how-to/builtins.rst | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/docs/how-to/builtins.rst b/docs/how-to/builtins.rst index 9b2e569dc..a92775651 100644 --- a/docs/how-to/builtins.rst +++ b/docs/how-to/builtins.rst @@ -1,22 +1,30 @@ Implementing Python Built-ins in JavaScript =========================================== +Pythons builtins are the absolute core of the language. If you're new to Python, read up on them here: https://docs.python.org/3/library/functions.html + +Most builtins have already been added to the project, but many do not follow the correct spec outlined below. +Some may not handle certain types of inputs correctly. In addition, new builtins may of course arrive with the +latest and greatest Python version. This guide should serve as your field manual for adding, updating, and +navigating our implementations. + Process ------- -The first thing to do when adding anything to Batavia is to play around a bit with it in the Python REPL:: +The first thing to do when adding anything to Batavia is to play around a bit with it in the Python REPL. +Here's an example using ``list()``:: >> list() [] - >> [] - [] + >> list((1, 2, 3, 4)) + [1, 2, 3, 4] >> list(4) Traceback (most recent call last): File "", line 1, in TypeError: 'int' object is not iterable Your goal is to find out how the function responds to various inputs and outputs. You may also -want to consult the offical documentation. Once you're a little familiar, you can add your +want to consult the offical documentation. Once you're a little familiar, you can start to add your implementation to Batavia. General Structure @@ -24,8 +32,8 @@ 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. These builtins are -designed to be used only inside Batavia, as such they need to ensure -they are being used in a compatible manner. +designed to be used only inside Batavia, as such they need to ensure they are being used in +a compatible manner. Each builtin function will receive arguments and keyword arguments and needs to handle them, even if the result is throwing an error. Args should be an array, and kwargs should be a @@ -33,7 +41,7 @@ JavaScript object. The first thing to do is check that both were passed in. .. code-block:: javascript - // Example: a function that accepts exactly one argument, and no keyword arguments + // Example: accepts exactly one argument, and no keyword arguments var = function(args, kwargs) { // Always add this code. @@ -120,6 +128,6 @@ tested: * Write a test or three to ensure your function returns the correct output with some normal inputs. * Think of a few weird inputs that could throw off your code (or future code). Test them. -* If you are throwing an error (excluding ``BataviaError``) anywhere, write a test for that. -* If you accounted for an edge case (look for an ``if`` statement), -* Check out the official documentation (https://docs.python.org/3/) for more edge cases \ No newline at end of file +* If you are throwing an error (excluding ``BataviaError``) anywhere, write a test that tries to throw it. +* If you accounted for an edge case (look for an ``if`` statement), test it. +* Check out the official documentation (https://docs.python.org/3/) for more edge cases. \ No newline at end of file From dfa868b8d867ab0467aee9f2d361c04fc1771f62 Mon Sep 17 00:00:00 2001 From: awildtechno Date: Thu, 13 Jun 2019 12:38:30 -0700 Subject: [PATCH 04/17] Added types.rst, a similar document to builtins.rst for working with Batavia types. --- docs/how-to/index.rst | 1 + docs/how-to/types.rst | 152 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 docs/how-to/types.rst diff --git a/docs/how-to/index.rst b/docs/how-to/index.rst index 22bf5c270..9ca93aa21 100644 --- a/docs/how-to/index.rst +++ b/docs/how-to/index.rst @@ -15,4 +15,5 @@ How-to guides are recipes that take the user through steps in key subjects. They Contribute to Batavia's documentation Follow the Goat: Batavia's Test Suite Implementing Python Built-ins in JavaScript + Implementing Python Types in JavaScript Adding a module and testing it diff --git a/docs/how-to/types.rst b/docs/how-to/types.rst new file mode 100644 index 000000000..5b1c82420 --- /dev/null +++ b/docs/how-to/types.rst @@ -0,0 +1,152 @@ +Implementing Python Types +=========================================== + +Python native types are the building blocks for all code. Python's popularity is, in large part, due to the +wonderful flexibility of its native types, like ``List`` and ``Dict``. This document will cover how to +update the existing Batavia implementations. + +Process +------- + +The first thing to do when adding anything to Batavia is to play around a bit with it in the Python REPL. +Here's an example using ``int``:: + + >>> int + + >>> int(1) + 1 + >>> dir(int) + ['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes'] + +Your goal is to find out how the type responds to various inputs and outputs. You may also +want to consult the offical documentation. Once you're a little familiar, you can start to add your +implementation to Batavia. + +Anatomy of a Type +***************** + +Each Python type should be implemented as a JavaScript class. JavaScript handles classes similarly to Python, +but the syntax is very different. Each JavaScript class creates a constructor (similar to Python's ``__init__``), +which is any function that includes the ``this`` keyword, and a prototype, stored in ``.prototype``. +The prototype implements all of the methods for the class, including the constructor, and that's where we'll implement +the type's magic methods and other options. Let's take a look at ``List``. + +.. code-block:: javascript + + // Declare the list class. + function List() { + var builtins = require('../builtins') + + if (arguments.length === 0) { + this.push.apply(this) + } else if (arguments.length === 1) { + // Fast-path for native Array objects. + if (Array.isArray(arguments[0])) { + this.push.apply(this, arguments[0]) + } else { + var iterobj = builtins.iter([arguments[0]], null) + var self = this + callables.iter_for_each(iterobj, function(val) { + self.push(val) + }) + } + } else { + throw new exceptions.TypeError.$pyclass('list() takes at most 1 argument (' + arguments.length + ' given)') + } + } + + function Array_() {} + + Array_.prototype = [] + + List.prototype = Object.create(Array_.prototype) // Duplicates the prototype to avoid damaging the original + List.prototype.length = 0 + create_pyclass(List, 'list', true) // Register the class with Batavia + List.prototype.constructor = List + +This is the constructor, which is called by Batavia when someone invokes ``list()`` or ``[]``. We includes some code to inherit +JavaScript's native array prototype, which has much of the same functionality as List and has lots of quick functions. +You can use JavaScript natives in your implementation; this is a significant speed boost. + +Below that, you'll find all of the member methods added to the prototype. Note that each of these +should return a Python type from ``types.js``. + +.. code-block:: javascript + + List.prototype.__iter__ = function() { + return new ListIterator(this) + } + + List.prototype.__len__ = function() { + var types = require('../types') + return new types.Int(this.length) + } + +List also implements ``.toString()``, a JavaScript function that is sometimes called automatically when a string +conversion is needed. + +.. code-block:: javascript + + List.prototype.toString = function() { + return this.__str__() + } + +Note also the format for errors: ``throw new exceptions..$pyclass``. + +Making Changes +************** + +Make a Test +^^^^^^^^^^^ + +There is much work to be done in the types folder. When making changes, your goal is to match the output +of CPython and the output of the same call made in Batavia. Since we know the desired input and output, +we can use a test and then just fiddle. + +Head over to ``/tests`` and find the ``test_`` file. Many types have a robust test suite, but +do not assume that it is complete. +Follow the format there to add a test for your issue or modify an existing test. +Run it using the following command to check for errors. + +.. code-block:: bash + $ python setup.py -s tests.path.to.your.test.TestClass.test_function + +Note: ``@expectedFailure`` indicates a test that's not passing yet. Your issue may be tested in one of those cases already. + +Pass the Test +^^^^^^^^^^^^^ + +If the test code runs and fails, you've identified the bug and should have some helpful output comparisons. Head over to +the type you want and start making edits, running your test until it passes. Occasionally, your bug will take you into +other Batavia types and builtins. That's fine too! There are a million places for small omissions all over the codebase. +Just keep in mind that the further you go down the rabbit hole, the more likely you are to have missed something simple. + +Once the test passes, run all tests for the class and see what else broke. (There's always something):: + + $ python setup.py -s tests.path.to.your.test + +After that, it's a good idea to pull the upstream master and check for merge conflicts.:: + + $ git add . + $ git commit -m "" + $ git fetch upstream + $ git rebase origin/master + +If you made major changes, then it may be a good idea to run the full test suite before submitting your pull request.:: + + $ python setup.py -s tests + +(Check out the sidebar for better/faster ways to run the full suite.) Finally, push your code to your fork and submit +your pull request on GitHub to run the CI. Fix any issues and push again until CI passes. The Batavia team will get back +to you with any additional notes and edits. + +Tips +^^^^ + +Your goal is to mimic the CPython implementation as much as possible. If you do so, you'll often fix multiple issues at once. Here's some tips: + +* The original implementation is documented in detail at https://docs.python.org/3/ -- reading up there will definitely improve your understanding. +* If you're inherting your class from JavaScript, which is very common, you get JavaScript's internal methods for free. Oftentimes, they can be left as is or lightly wrapped. +* Make sure your test properly covers the issue. For instance, if a member function accepts any iterable, make a generic iterable instead of using a list or tuple. +* Make sure method implementations accept args and kwargs, and throw appropriate errors if the input doesn't match. +* Keep your Python REPL open to the side and test your assumptions with lots of inputs and outputs. From 38a2dcd27e7fba3d6ebd7c2a1c47a3d1bf02653a Mon Sep 17 00:00:00 2001 From: awildtechno Date: Thu, 13 Jun 2019 12:42:20 -0700 Subject: [PATCH 05/17] Flavor edits. --- docs/how-to/builtins.rst | 4 ++-- docs/how-to/types.rst | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/how-to/builtins.rst b/docs/how-to/builtins.rst index a92775651..b259ec214 100644 --- a/docs/how-to/builtins.rst +++ b/docs/how-to/builtins.rst @@ -1,9 +1,9 @@ Implementing Python Built-ins in JavaScript =========================================== -Pythons builtins are the absolute core of the language. If you're new to Python, read up on them here: https://docs.python.org/3/library/functions.html +Pythons builtins give Python its trademark magical feel. If you're new to Python, read up on them here: https://docs.python.org/3/library/functions.html -Most builtins have already been added to the project, but many do not follow the correct spec outlined below. +Most builtins have already been added to the project, but many do not follow the spec outlined below. Some may not handle certain types of inputs correctly. In addition, new builtins may of course arrive with the latest and greatest Python version. This guide should serve as your field manual for adding, updating, and navigating our implementations. diff --git a/docs/how-to/types.rst b/docs/how-to/types.rst index 5b1c82420..b6252c154 100644 --- a/docs/how-to/types.rst +++ b/docs/how-to/types.rst @@ -1,9 +1,8 @@ Implementing Python Types =========================================== -Python native types are the building blocks for all code. Python's popularity is, in large part, due to the -wonderful flexibility of its native types, like ``List`` and ``Dict``. This document will cover how to -update the existing Batavia implementations. +Python's popularity is, in large part, due to the wonderful flexibility of its native types, like ``List`` and ``Dict``. In Batavia, Python native types are the building blocks for all of our other code. +This document will cover the structure of Batavia types and guide you on how to update the existing Batavia implementations. Process ------- From 2d11319128164accd0542b9a505c454f1f9a5429 Mon Sep 17 00:00:00 2001 From: awildtechno Date: Thu, 13 Jun 2019 12:42:55 -0700 Subject: [PATCH 06/17] Flavor edits v2. --- docs/how-to/builtins.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/how-to/builtins.rst b/docs/how-to/builtins.rst index b259ec214..7e6da950a 100644 --- a/docs/how-to/builtins.rst +++ b/docs/how-to/builtins.rst @@ -4,7 +4,7 @@ Implementing Python Built-ins in JavaScript Pythons builtins give Python its trademark magical feel. If you're new to Python, read up on them here: https://docs.python.org/3/library/functions.html Most builtins have already been added to the project, but many do not follow the spec outlined below. -Some may not handle certain types of inputs correctly. In addition, new builtins may of course arrive with the +Some may not handle certain types of inputs correctly. In addition, new builtins may arrive with the latest and greatest Python version. This guide should serve as your field manual for adding, updating, and navigating our implementations. From 253094f2adf4f54e350dd53aa0806321998f727f Mon Sep 17 00:00:00 2001 From: awildtechno Date: Thu, 13 Jun 2019 12:43:42 -0700 Subject: [PATCH 07/17] Grammar. --- docs/how-to/builtins.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/how-to/builtins.rst b/docs/how-to/builtins.rst index 7e6da950a..c039032ab 100644 --- a/docs/how-to/builtins.rst +++ b/docs/how-to/builtins.rst @@ -1,7 +1,7 @@ Implementing Python Built-ins in JavaScript =========================================== -Pythons builtins give Python its trademark magical feel. If you're new to Python, read up on them here: https://docs.python.org/3/library/functions.html +Python's builtins give Python its trademark magical feel. If you're new to Python, read up on them here: https://docs.python.org/3/library/functions.html Most builtins have already been added to the project, but many do not follow the spec outlined below. Some may not handle certain types of inputs correctly. In addition, new builtins may arrive with the From f0ec5803806aa17548d82842e4256b12b7c92394 Mon Sep 17 00:00:00 2001 From: awildtechno Date: Thu, 13 Jun 2019 12:46:10 -0700 Subject: [PATCH 08/17] Finish my sentence --- docs/how-to/builtins.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/how-to/builtins.rst b/docs/how-to/builtins.rst index c039032ab..3789e3dbe 100644 --- a/docs/how-to/builtins.rst +++ b/docs/how-to/builtins.rst @@ -50,7 +50,7 @@ JavaScript object. The first thing to do is check that both were passed in. } Next, we need to validate the arguments are correct. We can use JavaScript's ``Object.keys()`` to -get the keys of an object. If the function pass in the , we will check the Python REPL to see +get the keys of an object. If we can't accept certain args or kwargs, we will check the Python REPL to see what kind of error and throw it. .. tabs:: From 25ff0f2f14af55fa9e10b11360f66379e8fb91a4 Mon Sep 17 00:00:00 2001 From: awildtechno Date: Thu, 13 Jun 2019 12:49:25 -0700 Subject: [PATCH 09/17] Explain a bit more. --- docs/how-to/tour.rst | 2 +- docs/how-to/types.rst | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/how-to/tour.rst b/docs/how-to/tour.rst index 1d14be4f0..82421d65d 100644 --- a/docs/how-to/tour.rst +++ b/docs/how-to/tour.rst @@ -23,7 +23,7 @@ should be immediately familiar to a Python developer. Support ******* You'll also notice folders for tests, docs, and a few other bits and bobs, like ``testserver``, which is -a Django project that allows code execution. Contributions to the +a Django project that allows code execution from your browser. Contributions to the tests and documentation are always welcome and are great ways to familiarize yourself with the code and meet the other contributors. diff --git a/docs/how-to/types.rst b/docs/how-to/types.rst index b6252c154..689b79578 100644 --- a/docs/how-to/types.rst +++ b/docs/how-to/types.rst @@ -108,6 +108,7 @@ Follow the format there to add a test for your issue or modify an existing test. Run it using the following command to check for errors. .. code-block:: bash + $ python setup.py -s tests.path.to.your.test.TestClass.test_function Note: ``@expectedFailure`` indicates a test that's not passing yet. Your issue may be tested in one of those cases already. From f73628c8c6457988760c5b3a817dd78b37537477 Mon Sep 17 00:00:00 2001 From: awildtechno Date: Thu, 13 Jun 2019 12:55:27 -0700 Subject: [PATCH 10/17] Added credit section. --- docs/how-to/builtins.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/how-to/builtins.rst b/docs/how-to/builtins.rst index 3789e3dbe..35ed7ad0c 100644 --- a/docs/how-to/builtins.rst +++ b/docs/how-to/builtins.rst @@ -130,4 +130,9 @@ tested: * Think of a few weird inputs that could throw off your code (or future code). Test them. * If you are throwing an error (excluding ``BataviaError``) anywhere, write a test that tries to throw it. * If you accounted for an edge case (look for an ``if`` statement), test it. -* Check out the official documentation (https://docs.python.org/3/) for more edge cases. \ No newline at end of file +* Check out the official documentation (https://docs.python.org/3/) for more edge cases. + +Credit +^^^^^^ + +Thanks to Kshitij Sobti for the original document that this guide is based on. Most code examples are from his original. \ No newline at end of file From 708005eed9087217d241d14c0b8c74837262566e Mon Sep 17 00:00:00 2001 From: awildtechno Date: Thu, 13 Jun 2019 13:05:25 -0700 Subject: [PATCH 11/17] Cleanup for contribute-tests --- docs/how-to/contribute-tests.rst | 16 ++++++++-------- docs/how-to/index.rst | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/how-to/contribute-tests.rst b/docs/how-to/contribute-tests.rst index e12013ef5..4f9ea6094 100644 --- a/docs/how-to/contribute-tests.rst +++ b/docs/how-to/contribute-tests.rst @@ -23,8 +23,8 @@ The tests corresponding to Batavia implementations of built-ins are available in ``tests/builtins``. The Batavia test infrastructure includes a system to check the compatibility of JavaScript implementation of Python with the reference CPython implementation. -These all tests derive from ``TranspileTestCase``, which handles running your code in both interpreters -and comparing outputs. For an example look at the ``test_bool.py`` file in ``tests/builtins``. You +These tests all derive from ``TranspileTestCase``, which handles running your code in both interpreters +and comparing outputs. 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``. @@ -68,18 +68,18 @@ which can be very useful for debugging in test cases where a decent amount of co Testing for Errors ------------------ -Since we're compiling Python, we need to ensure that errors for all of the builtins are thrown correctly. -We also want to ensure that we're not accepting the wrong errors in our tests. Simply include a try/except +Since we're testing the compiler, we need to ensure that errors for all of the builtins are thrown correctly. +We also want to ensure that we're not getting the wrong errors in our tests. Simply include a try/except block in your test. .. code-block:: python def test_some_error(self): code = """ - try: - code_that_raises_a_ValueError() - except ValueError as err: - print(err) + try: + code_that_raises_a_ValueError() + except ValueError as err: + print(err) """ self.assertCodeExecution(code) diff --git a/docs/how-to/index.rst b/docs/how-to/index.rst index 9ca93aa21..c9f6cb2ca 100644 --- a/docs/how-to/index.rst +++ b/docs/how-to/index.rst @@ -10,9 +10,9 @@ How-to guides are recipes that take the user through steps in key subjects. They :maxdepth: 1 :glob: - Tour of the Code Contribute to Batavia's code Contribute to Batavia's documentation + Tour of the Code Follow the Goat: Batavia's Test Suite Implementing Python Built-ins in JavaScript Implementing Python Types in JavaScript From 9a076c2c24ea9b605f56bbad9ad42778ae32284b Mon Sep 17 00:00:00 2001 From: awildtechno Date: Sun, 16 Jun 2019 09:06:38 -0700 Subject: [PATCH 12/17] Cleanup/suggested changes in builtins.rst. --- docs/how-to/builtins.rst | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/docs/how-to/builtins.rst b/docs/how-to/builtins.rst index 35ed7ad0c..2fc14c8e1 100644 --- a/docs/how-to/builtins.rst +++ b/docs/how-to/builtins.rst @@ -1,12 +1,13 @@ Implementing Python Built-ins in JavaScript =========================================== -Python's builtins give Python its trademark magical feel. If you're new to Python, read up on them here: https://docs.python.org/3/library/functions.html +Python's builtins give Python its trademark magical feel. If you're new to Python, read up on ``_ -Most builtins have already been added to the project, but many do not follow the spec outlined below. -Some may not handle certain types of inputs correctly. In addition, new builtins may arrive with the -latest and greatest Python version. This guide should serve as your field manual for adding, updating, and -navigating our implementations. +Most builtins have already been added to the project, but many are do not quite match the original +implementation exactly. Some may not handle certain types of inputs correctly. In addition, new builtins +may arrive with the latest and greatest Python version. This guide should serve as your field manual for +adding, updating, and navigating our implementations. Process ------- @@ -39,19 +40,21 @@ Each builtin function will receive arguments and keyword arguments and needs to even if the result is throwing an error. Args should be an array, and kwargs should be a JavaScript object. The first thing to do is check that both were passed in. +Let's take a look at an example using the ``list()`` builtin + .. code-block:: javascript - // Example: accepts exactly one argument, and no keyword arguments + // List accepts exactly one argument and no keyword arguments - var = function(args, kwargs) { + var list = function(args, kwargs) { // Always add this code. if (arguments.length !== 2) { throw new builtins.BataviaError.$pyclass("Batavia calling convention not used."); } -Next, we need to validate the arguments are correct. We can use JavaScript's ``Object.keys()`` to -get the keys of an object. If we can't accept certain args or kwargs, we will check the Python REPL to see -what kind of error and throw it. +This code ensures that the function can handle keyword arguments. Next, we need to validate the arguments are +correct. We can use JavaScript's ``Object.keys()`` to get the keys of an object. If we can't accept certain +args or kwargs, we will check the Python REPL to see what kind of error should be thrown and throw it. .. tabs:: @@ -130,9 +133,4 @@ tested: * Think of a few weird inputs that could throw off your code (or future code). Test them. * If you are throwing an error (excluding ``BataviaError``) anywhere, write a test that tries to throw it. * If you accounted for an edge case (look for an ``if`` statement), test it. -* Check out the official documentation (https://docs.python.org/3/) for more edge cases. - -Credit -^^^^^^ - -Thanks to Kshitij Sobti for the original document that this guide is based on. Most code examples are from his original. \ No newline at end of file +* Check out the `official documentation `_ for more edge cases. \ No newline at end of file From 8eaa1e8e9d37c4437baa8b7ce227157c15995bbd Mon Sep 17 00:00:00 2001 From: awildtechno Date: Sun, 16 Jun 2019 14:47:20 -0700 Subject: [PATCH 13/17] A bit more cleanup. --- docs/how-to/builtins.rst | 4 ++-- docs/how-to/contribute-tests.rst | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/how-to/builtins.rst b/docs/how-to/builtins.rst index 2fc14c8e1..a517340c6 100644 --- a/docs/how-to/builtins.rst +++ b/docs/how-to/builtins.rst @@ -117,9 +117,9 @@ are first-class objects and can have additional properties. .. code-block:: javascript - .__doc__ = 'docstring from Python 3.x goes here, for documentation' + list.__doc__ = 'docstring from Python 3.x goes here, for documentation' - modules.export = + module.exports = list Tests ***** diff --git a/docs/how-to/contribute-tests.rst b/docs/how-to/contribute-tests.rst index 4f9ea6094..df0823bdb 100644 --- a/docs/how-to/contribute-tests.rst +++ b/docs/how-to/contribute-tests.rst @@ -80,11 +80,12 @@ block in your test. code_that_raises_a_ValueError() except ValueError as err: print(err) + print("Test complete!") """ self.assertCodeExecution(code) -Remember to catch the specific error you want, and then print the error. **Code that is not being printed -is not being tested.** +Remember to catch the specific error you want, and then print the error. Then, print a success message to +validate that your except block didn't crash as well. **Code that is not being printed is not being tested.** Output Cleaners --------------- From dc4d547a2416d4d064558a242a9adcbffe1aa9ce Mon Sep 17 00:00:00 2001 From: awildtechno Date: Sun, 16 Jun 2019 15:35:38 -0700 Subject: [PATCH 14/17] Implement suggested changes to tour of the code. --- docs/how-to/tour.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/how-to/tour.rst b/docs/how-to/tour.rst index 82421d65d..a1e455891 100644 --- a/docs/how-to/tour.rst +++ b/docs/how-to/tour.rst @@ -22,8 +22,8 @@ should be immediately familiar to a Python developer. Support ******* -You'll also notice folders for tests, docs, and a few other bits and bobs, like ``testserver``, which is -a Django project that allows code execution from your browser. Contributions to the +You'll also notice folders for tests, docs, and a few other sections, like ``testserver``, which is +a sample deployment via Django that allows code execution in your browser. Contributions to the tests and documentation are always welcome and are great ways to familiarize yourself with the code and meet the other contributors. @@ -65,7 +65,7 @@ builtins.js This contains all of the native Python builtin functions, like ``str``, ``len``, and ``iter``. When dealing with Python types, many of the native JavaScript operations have been modified to -try to use builtins first. For instance, .toString() will often just call the object's __str__ if +try to use builtins first. For instance, ``.toString()`` will often just call the object's ``__str__`` if possible. Still, the best practice is to use the builtins and types wherever possible. types.js @@ -91,10 +91,10 @@ instance. That doesn't make sense! (It may even pass some tests, which is danger core/callables.js ^^^^^^^^^^^^^^^^^ -These methods ensure that all Python code is executed using the proper __call__ procedure, which could be +These methods ensure that all Python code is executed using the proper ``__call__`` procedure, which could be overriden or decorated by the programmer. -* ``callables.call_function`` Invokes a function using its __call__ method if possible. If not, just call it normally. +* ``callables.call_function`` Invokes a function using its ``__call__`` method if possible. If not, just call it normally. * ``callables.call_method`` Calls a class method using the call_function specification above. * ``callables.iter_for_each`` Exhausts an iterable using the call_function specification above. @@ -104,15 +104,15 @@ of ``call_function`` instead of calling Python functions and methods directly. A .. code-block:: javascript // Avoid this - my_thing.__repr__() + my_thing.__len__() // Better var callables = require('./core/callables.js') - callables.call_method(my_thing, '__repr__', [], {}) + callables.call_method(my_thing, '__len__', [], {}) // Best - var repr = require('./builtins.js').repr - repr(my_thing, [], {}) + var len = require('./builtins.js').len + len(my_thing, [], {}) Note the use of the Batavia calling convention in the two cases above! From 1e8e57aae29ddde44ab7d4ee699249945e8f5faf Mon Sep 17 00:00:00 2001 From: awildtechno Date: Sun, 16 Jun 2019 15:45:50 -0700 Subject: [PATCH 15/17] Added a bit about edge cases. --- docs/how-to/contribute-tests.rst | 6 +++++- docs/how-to/index.rst | 2 +- docs/how-to/tour.rst | 5 +++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/how-to/contribute-tests.rst b/docs/how-to/contribute-tests.rst index df0823bdb..c41c69c42 100644 --- a/docs/how-to/contribute-tests.rst +++ b/docs/how-to/contribute-tests.rst @@ -49,6 +49,10 @@ The ``assertCodeExecution`` method will run the code provided to it in both impl code needs to generate some output so that the output can be compared, hence the need to print the values. **Code that is not being printed is not being tested.** +Finally, ``print()`` is an imperfect creature for tests. Some things in Python aren't guaranteed to +print out in the same order or way, like sets dictionaries. Tests should be structured to compensate, +for instance by converting to a sorted list. See also the output cleaners section below. + Template -------- @@ -113,7 +117,7 @@ Node/Python Crashes If the CPython or JavaScript code crashes outright, UnitTest struggles. For instance, ``confused END_FINALLY`` in the middle of your test output tends to mean that the JavaScript code threw an uncaught exception, causing Node to stop. It's hard for UnitTest to pull the details out of this type of thing -since that error occurred in the virtual machine. +since that error occurred in Node, not Python. These types of errors will often appear above the test case as a crash report instead of in the usual section for the output of your test's print() statements. Look there for clues. \ No newline at end of file diff --git a/docs/how-to/index.rst b/docs/how-to/index.rst index c9f6cb2ca..86f80bd5f 100644 --- a/docs/how-to/index.rst +++ b/docs/how-to/index.rst @@ -13,7 +13,7 @@ How-to guides are recipes that take the user through steps in key subjects. They Contribute to Batavia's code Contribute to Batavia's documentation Tour of the Code - Follow the Goat: Batavia's Test Suite + Batavia's Test Suite Implementing Python Built-ins in JavaScript Implementing Python Types in JavaScript Adding a module and testing it diff --git a/docs/how-to/tour.rst b/docs/how-to/tour.rst index a1e455891..c7cceb39c 100644 --- a/docs/how-to/tour.rst +++ b/docs/how-to/tour.rst @@ -12,7 +12,8 @@ General Structure Core Code ********* -Batavia implements the Python native types, builtins, and standard library partially in JavaScript. +Batavia implements the core language (types, builtins and the core interpreter) are all javascript, as well those +portions of the standard library that depend on system services. This ensures quick execution and less compiling of the compiler. These are orgainzed into the ``builtins``, ``core``, ``modules``, ``stdlib``, and ``types`` files of the main ``/batavia`` directory, with corresponding subdirectories. Alongside the virtual machine and test suite, this code makes @@ -23,7 +24,7 @@ should be immediately familiar to a Python developer. Support ******* You'll also notice folders for tests, docs, and a few other sections, like ``testserver``, which is -a sample deployment via Django that allows code execution in your browser. Contributions to the +a sample deployment with Django that allows you to test code in the browser. Contributions to the tests and documentation are always welcome and are great ways to familiarize yourself with the code and meet the other contributors. From 3a348e461523554c4be46db73b1b6c4ab3ee9d0a Mon Sep 17 00:00:00 2001 From: awildtechno Date: Sun, 16 Jun 2019 15:45:53 -0700 Subject: [PATCH 16/17] Added a bit about edge cases. --- docs/how-to/contribute-tests.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/how-to/contribute-tests.rst b/docs/how-to/contribute-tests.rst index c41c69c42..addb7c8ef 100644 --- a/docs/how-to/contribute-tests.rst +++ b/docs/how-to/contribute-tests.rst @@ -50,7 +50,7 @@ code needs to generate some output so that the output can be compared, hence the values. **Code that is not being printed is not being tested.** Finally, ``print()`` is an imperfect creature for tests. Some things in Python aren't guaranteed to -print out in the same order or way, like sets dictionaries. Tests should be structured to compensate, +print out in the same order every time, like sets dictionaries. Tests should be structured to compensate, for instance by converting to a sorted list. See also the output cleaners section below. Template From 53053fd8017afe5882748c39a9e94bfa40fce1e4 Mon Sep 17 00:00:00 2001 From: awildtechno Date: Sun, 16 Jun 2019 15:48:36 -0700 Subject: [PATCH 17/17] Indentation --- docs/how-to/contribute-tests.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/how-to/contribute-tests.rst b/docs/how-to/contribute-tests.rst index addb7c8ef..fc40ec302 100644 --- a/docs/how-to/contribute-tests.rst +++ b/docs/how-to/contribute-tests.rst @@ -67,7 +67,7 @@ Template self.assertCodeExecution(code) This code block provides a printout of the code being run as well as the output of the code, -which can be very useful for debugging in test cases where a decent amount of code is being run. +which can be very useful for debugging in test cases where more than a few lines of code are being run. Testing for Errors ------------------ @@ -84,7 +84,7 @@ block in your test. code_that_raises_a_ValueError() except ValueError as err: print(err) - print("Test complete!") + print("Test complete!") """ self.assertCodeExecution(code)