# 2. Getting started

## 2.1. Installation

Transcrypt is currently tested under Windows, Linux and OSX, with Chrome, Internet Explorer and Firefox. To be able to use it, Python 3.5 or 3.6 has to be installed. After that, install virtualenv as explained in Jamie Matthews very clear and brief introduction. Be sure to install virtualenv for the right Python version, by using the right pip. For each Transcrypt project (or group of projects using the same Transcrypt version) create an environment as described in the referenced introduction. To install Trancrypt into that environment, activate the environment as also described there, and then type:

pip install transcrypt

from the command prompt. This is the recommended way to install Transcrypt. It is flexible and sets the proper access rights.

Alternatively, for manual installation under Windows or Linux, follow the steps below:

1. Download the Transcrypt zip and unpack it anywhere you like
2. Add ../Transcrypt-<version>/transcrypt to your system path

To enable minification, additionally the Java Runtime Environment 6 or later has to be installed.

N.B. If you install Transcrypt manually, Trancrypt is started by typing run_transcrypt rather than transcrypt. This allows a pip installed Transcrypt and a manually installed Transcrypt to by used side by side selectively.

N.B.2 If you also use Numscrypt under Linux or OSX, use the MiniConda installer rather than virtualenv, as described in the Numscrypt documentation, since it will allow you to obtain the right version of NumPy.

You can test your installation as follows (replacing transcrypt by run_transcrypt if you installed manually rather than with pip):

1. Go to directory ../Transcrypt-<version>/transcrypt/development/automated_tests/transcrypt
2. From the command prompt run transcrypt -b autotest.py. This will compile the autotests into file autotest.js and put it into the __javascript__ subdirectory. Do NOT go to that directory (there's no need, stay where you went at point 4)
3. From the command prompt run transcrypt -r autotest.py. This will run the autotests with CPython creating file autotest.html that refers to the generated autotest.js file
4. Load the autotest.html into your browser, e.g. by clicking on it (tests were done with Chrome). It will load autotest.js, run it and compare the output with what was generated by CPython. It should report no errors

To experiment with Transcrypt yourself:

1. Create a directory for your experiments
2. Make your own thing there, e.g. experiment1.py
3. Compile with transcrypt -b experiment1.py
4. Make an HTML page that will load your code in the browser. Use the HTML file generated by the autotest as an example of how to do that
5. Load and run the HTML + JS

You may also want to try the demo's.

### 2.1.1. Troubleshooting checklist

1. Transcrypt was installed using pip, but import transcrypt fails. Transcrypt isn't a library but a compiler. Install and run it as described in this chapter.
2. Transcrypt reports an error containing the word 'java'. Transcrypt produces both prettyfied and minified JavaScript output. For the minification it makes use of the Google Closure Compiler, which is included in the distribution and requires Java to run. You can check proper installation of Java by typing the word java on the command line. This should give you a list of options: Usage: java [-options] class []args...] and so on. If you can't or won't install Java, you can run Transcrypt without minification by using the -n command line switch.
3. The static checker doesn't find all errors it could. The static checks, performed by the PyFlakes package that's part of the distribution, are of a 'light' variety. Style checks and false positives are avoided. The accent is on finding undefined identifiers and unused variables.

## 2.2. Your first Transcrypt program

Open a command prompt in the demos/hello directory and type transcrypt hello.py. Then click on hello.html to load it into your browser.

After clicking on both buttons a few times, take a look at hello.html. As you can see, eventhandlers are connected to the buttons exactly as you would do with JavaScript.

Then look at hello.py. Note that JavaScript functions like document.getElementById can be called from Python exactly as you would call them from JavaScript, but with Python data.

The minified JavaScript file including the Transcrypt runtime is only 10kB. With non-trivial programs this overhead becomes negligible. Transcrypt applications in themselves don't need any external files. Of course you can use extensive JavaScript libraries served by content delivery networks as you normally would. But you can also make very compact stand alone applications. The most efficient thing to do is pack all functionality for a page in a single Transcrypt program. Note that the source can consist of many modules if needed and many callback entrypoints can be provided. But it is one stateful application, just like it would be on the desktop.

In hello.html, Python handlers are attached directly to the onclick events

    <script src="__javascript__/hello.js"></script>
    <h2>Hello demo</h2>

    <p>
    <div id = "greet">...</div>
    <button onclick="hello.solarSystem.greet ()">Click me repeatedly!</button>

    <p>
    <div id = "explain">...</div>
    <button onclick="hello.solarSystem.explain ()">And click me repeatedly too!</button>

In hello.py, JavaScript function document.getElementById is called directly from Python, using plain Python values

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    from itertools import chain

    class SolarSystem:
        planets = [list (chain (planet, (index + 1,))) for index, planet in enumerate ((
            ('Mercury', 'hot', 2240),
            ('Venus', 'sulphurous', 6052),
            ('Earth', 'fertile', 6378),
            ('Mars', 'reddish', 3397),
            ('Jupiter', 'stormy', 71492),
            ('Saturn', 'ringed', 60268),
            ('Uranus', 'cold', 25559),
            ('Neptune', 'very cold', 24766)
        ))]

        lines = (
            '{} is a {} planet',
            'The radius of {} is {} km',
            '{} is planet nr. {} counting from the sun'
        )

        def __init__ (self):
            self.lineIndex = 0

        def greet (self):
            self.planet = self.planets [int (Math.random () * len (self.planets))]
            document.getElementById ('greet') .innerHTML = 'Hello {}'.format (self.planet [0])
            self.explain ()

        def explain (self):
            document.getElementById ('explain').innerHTML = (
                self.lines [self.lineIndex] .format (self.planet [0], self.planet [self.lineIndex + 1])
            )
            self.lineIndex = (self.lineIndex + 1) % 3

    solarSystem = SolarSystem ()

## 2.3. Available command line switches

The available command line switches will be shown if you run transcript -h. They are specified in the source code of Transcrypt as follows:

Transcrypt command line switches as specified in module/orgs/transcrypt/utils.py

    class CommandArgs:
        def parse (self):
            self.argParser = ArgumentParser ()

            self.argParser.add_argument ('source', nargs='?', help = ".py file containing source code of main module")
            self.argParser.add_argument ('-a', '--anno', help = "annotate target files that were compiled from Python with source file names and source line numbers", action = 'store_true')
            self.argParser.add_argument ('-b', '--build', help = "rebuild all target files from scratch", action = 'store_true')
            self.argParser.add_argument ('-c', '--complex', help = "enable complex number support, locally requires operator overloading", action = 'store_true')
            self.argParser.add_argument ('-d', '--docat', help = "enable __doc__ attributes. Apply sparsely, since it will make docstrings part of the generated code", action = 'store_true')
            self.argParser.add_argument ('-dc', '--dcheck', help = "debug: perform lightweight consistency check", action = 'store_true')
            self.argParser.add_argument ('-da', '--dassert', help = "debug: activate assertions", action = 'store_true')
            self.argParser.add_argument ('-de', '--dextex', help = "debug: show extended exception reports", action = 'store_true')
            self.argParser.add_argument ('-dm', '--dmap', help = "debug: dump human readable source map", action = 'store_true')
            self.argParser.add_argument ('-dt', '--dtree', help = "debug: dump syntax tree", action = 'store_true')
            self.argParser.add_argument ('-ds', '--dstat', help = "debug: validate static typing using annotations", action = 'store_true')
            self.argParser.add_argument ('-e', '--esv', nargs='?', help = "ecma script version of generated code, default = 5. The symbol __esv<versionnr>__ is added to the global symbol list, e.g. __esv6__.")
            self.argParser.add_argument ('-f', '--fcall', help = "enable fastcall mechanism by default. You can also use __pragma__ ('fcal') and __pragma__ (\'nofcall\')", action = 'store_true')
            self.argParser.add_argument ('-g', '--gen', help = "enable generators and iterators. Disadvised, since it will result in a function call for each loop iteration. Preferably use __pragma__ ('gen') and __pragma__ ('nogen')", action = 'store_true')
            self.argParser.add_argument ('-i', '--iconv', help = "enable automatic conversion to iterable by default. Disadvised, since it will result in a type check for each for-loop. Preferably use __pragma__ ('iconv') and __pragma__ (\'noiconv\') to enable automatic conversion locally", action = 'store_true')
            self.argParser.add_argument ('-jk', '--jskeys', help = "interpret {key: 'value'} as {'key': 'value'} and forbid {key (): 'value'}, as JavaScript does. Disadvised, since it's less flexible than the Python interpretation. Either follow Python semantics by using {'key': 'value'} explicitly if you want literal keys or use __pragma__ ('jskeys') and __pragma__ ('nojskeys') locally instead to make clear local deviation from Python semantics", action = 'store_true')
            self.argParser.add_argument ('-jm', '--jsmod', help = "give %% and %%= JavaScript rather than Python behaviour. Disadvised, since it deviates from the mathematical 'modulo' operator. Either follow Python semantics or use __pragma__ ('jskeys') and __pragma__ ('nojskeys') locally instead to make clear local deviation.", action = 'store_true')
            self.argParser.add_argument ('-k', '--kwargs', help = "enable keyword arguments by default. In general this is disadvised, use __pragma__ ('kwargs') and __pragma__('nokwargs') locally instead to prevent bloated code", action = 'store_true')
            self.argParser.add_argument ('-l', '--license', help = "show license", action = 'store_true')
            self.argParser.add_argument ('-m', '--map', help = "generate source map", action = 'store_true')
            self.argParser.add_argument ('-n', '--nomin', help = "no minification", action = 'store_true')
            self.argParser.add_argument ('-o', '--opov', help = "enable operator overloading by default. In general this is disadvised, use __pragma__ ('opov') and __pragma__('noopov') locally instead to prevent slow code", action = 'store_true')
            self.argParser.add_argument ('-p', '--parent', nargs='?', help = "object that will hold module, default is window. Use -p .none to generate orphan module, e.g. for use in node.js. Use -p .user to generate module that has to be explicitly initialized by calling <modulename> (), e.g. after the full page has loaded")
            self.argParser.add_argument ('-r', '--run', help = "run source file rather than compiling it", action = 'store_true')
            self.argParser.add_argument ('-s', '--symbols', nargs='?', help = "names, joined by $, separately passed to main module in __symbols__ variable")
            self.argParser.add_argument ('-t', '--tconv', help = "enable automatic conversion to truth value by default. Disadvised, since it will result in a conversion for each boolean. Preferably use __pragma__ ('tconv') and __pragma__ (\'notconv\') to enable automatic conversion locally", action = 'store_true')
            self.argParser.add_argument ('-v', '--verbose', help = "show all messages", action = 'store_true')
            self.argParser.add_argument ('-x', '--ext', help = "reserved for extended options")
            self.argParser.add_argument ('-*', '--star', help = "Like it? Grow it! Go to GitHub and then click [* Star]", action = 'store_true')

            self.__dict__.update (self.argParser.parse_args () .__dict__)

            if not (self.license or self.star or self.source):
                log (True,  self.argParser.format_usage () .capitalize ())
                sys.exit (1)

            global extraLines
            extraLines = [
                # Make identifier __pragma__ known to static checker
                # It was only known in JavaScript from __core__.mod.js, which the checker doesn't see
                # __pragma__ ('<all>') in JavaScript requires it to remain a function, as it was in the core
                # It can't be skipped, since it has to precede __pragma__ ('skip'), to make the checker accept that
                'def __pragma__ (): pass',

                # Make __include__ known to the static checker
                '__pragma__ (\'skip\')',
                '__new__ = __include__ = 0',
                '__pragma__ (\'noskip\')',
                ''
            ] if commandArgs.dcheck else []
            global nrOfExtraLines
            nrOfExtraLines = max (len (extraLines) - 1, 0)  # Last line only serves to force linefeed
            extraLines = '\n'.join (extraLines)

If static checking is enabled, insert dummy definitions of global JavaScript variables between __pragma__ ('skip') and __pragma__ ('noskip') to prevent needless complaints of the checker. The static checks are geared towards avoiding false alarms, and mainly check undefined names and unused variables. Style checks are deliberately avoided.

## 2.4. Compiling to JavaScript 6

Transcrypt is gradually moving towards constructs that require JavaScript 6 as output language. Currently this is the case when using iterators and generators. Still the minified end result can be used in any JavaScript 5 compatible browser. This is because the Google Closure compiler, that does the minification, translates these constructs to minified JavaScript 5 code. Since multilevel sourcemaps are provided, the minified code can be easily debugged, because it directly links back to the original Python sourcecode.

If you want to include Python code that makes full use of generators and iterators, c.q. the yield statement, the following workflow is advised:

- Initially compile your code using the switches: -b -m -e 6 -n.
- Debug your non-minified code in a JavaScript 6 compatible browser like Google Chrome. Both .js and the .py files will be human readable. The sourcemap will refer from the non-minified JavaScript target code to the Python source code, allowing you to debug both in Python and in JavaScript.
- If it all works, compile your code using the switches -b -m and distribute the minified version. It will run in any JavaScript 5 compatible browser. Python source level debugging is still possible since the sourcemap will refer from the minified JavaScript target code to the Python source code.

## 2.5. Compiling for node.js

Transcrypt will allow you to target node.js while writing your server code in Python. This opens up the exploding world of node.js libraries from your favorite programming language. In the demo/nodejs_demo subdirectory of the installation, you'll find the following trivial example of a node.js server app:

Using node.js from Transcrypt is trivial, including the use of 'require' to tap into a vast set of libraries

    # Compile with p. command line switch (see docs).
    # The example will be served at URL: http://localhost:8080 in your browser

    http = require ('http')

    class Demo:
        texts = (
            'Welcome to the world of node.js',
            'You can have your cake and eat it',
            'Use node\'s ecosystem while programming in Python',
            'Using node.js from Transcrypt is easy',
            'Take a Python ride into the node.js world'
        )

        def __init__ (self, port):
            print ('Demo server started on port', port)
            self.server = http.createServer (self.serve)
            self.server.listen (port)
            self.oldIndex = 0
            self.newIndex = 0
            self.count = 0

        def serve (self, request, response):
            response.writeHead (200)

            print ('Serving page', self.count)
            self.count += 1

            while self.newIndex == self.oldIndex:
                self.newIndex = int (Math.random () * len (self.texts))
            self.oldIndex = self.newIndex

            response.end ('<h1>{}</h1>'.format (
                self.texts [self.newIndex]
            ))

    demo = Demo (8080)

Follow the steps below to run this demo:

- Install node.js from https://nodejs.org
- Open a node.js command prompt
- Go to the demo/nodejs_demo directory
- Compile the demo with transcrypt -b -p .none nodejs_demo.py, to generate an orphan module rather than a child of window
- Go to demo/nodejs_demo/__javascript__ directory
- Type node nodejs_demo.js (or node nodejs_demo.min.js if you want to run the minified version)
- In your browser, view the result at http://localhost:8080
- Repeatedly reload the page to see the text change (Google Chrome may actually reload twice)

## 2.6. Using sourcemaps and annotated target code

### 2.6.1. Sourcemaps

Sourcemaps enable debugging from the original Python source code rather then from the generated JavaScript. Transcrypt supports the use of single- and multi-level sourcemaps, using the -m switch. This means that you can source-level debug both non-minified and minified JavaScript target code. Sourcemaps are routinely tested for Google Chrome only, both under Windows and Linux, but they also have been observed to work for Firefox. Combined with the high readability of the JavaScript code generated by Transcrypt, this enables efficient debugging for real-world projects consisting of many modules.

### 2.6.2. Annotated target code

In addition to generating sourcemaps, you can use the -a switch to annotate non-minified JavaScript files with comments, referring to the original Python file names and line numbers. So even if your browser doesn't support sourcemaps, it's easy to find back the original Python source code location from any JavaScript statement.

Annotated target code for hello.py

    // ============ Source: D:/activ_tosh/geatec/transcrypt/transcrypt/demos/hello/hello.py ============

    /* 000001 */    (function () {
    /* 000001 */        var chain = __init__ (__world__.itertools).chain;
    /* 000003 */        var SolarSystem = __class__ ('SolarSystem', [object], {
    /* 000021 */            get __init__ () {return __get__ (this, function (self) {
    /* 000022 */                self.lineIndex = 0;
    /* 000022 */            });},
    /* 000024 */            get greet () {return __get__ (this, function (self) {
    /* 000025 */                self.planet = self.planets [int (Math.random () * len (self.planets))];
    /* 000026 */                document.getElementById ('greet').innerHTML = 'Hello {}'.format (self.planet [0]);
    /* 000027 */                self.explain ();
    /* 000027 */            });},
    /* 000029 */            get explain () {return __get__ (this, function (self) {
    /* 000031 */                document.getElementById ('explain').innerHTML = self.lines [self.lineIndex].format (self.planet [0], self.planet [self.lineIndex + 1]);
    /* 000033 */                self.lineIndex = (self.lineIndex + 1) % 3;
    /* 000033 */            });}
    /* 000033 */        });
    /* 000004 */        SolarSystem.planets = function () {
    /* 000004 */            var __accu0__ = [];
    /* 000004 */            var __iter0__ = enumerate (tuple ([tuple (['Mercury', 'hot', 2240]), tuple (['Venus', 'sulphurous', 6052]), tuple (['Earth', 'fertile', 6378]), tuple (['Mars', 'reddish', 3397]), tuple (['Jupiter', 'stormy', 71492]), tuple (['Saturn', 'ringed', 60268]), tuple (['Uranus', 'cold', 25559]), tuple (['Neptune', 'very cold', 24766])]));
    /* 000004 */            for (var __index0__ = 0; __index0__ < __iter0__.length; __index0__++) {
    /* 000012 */                var __left0__ = __iter0__ [__index0__];
    /* 000012 */                var index = __left0__ [0];
    /* 000012 */                var planet = __left0__ [1];
    /* 000004 */                __accu0__.append (chain (planet, tuple ([index + 1])));
    /* 000004 */            }
    /* 000004 */            return __accu0__;
    /* 000004 */        } ();
    /* 000015 */        SolarSystem.lines = tuple (['{} is a {} planet', 'The radius of {} is {} km', '{} is planet nr. {} counting from the sun']);
    /* 000035 */        var solarSystem = SolarSystem ();
    /* 000035 */        __pragma__ ('<use>' +
    /* 000035 */            'itertools' +
    /* 000035 */        '</use>')
    /* 000035 */        __pragma__ ('<all>')
    /* 000035 */            __all__.SolarSystem = SolarSystem;
    /* 000035 */            __all__.solarSystem = solarSystem;
    /* 000035 */        __pragma__ ('</all>')
    /* 000035 */    }) ();
    /* 000035 */    return __all__;

Source code annotation only happens for Python sources, not for JavaScript-only modules, that have a trivial correspondence between non-minified target code and source code.

## 2.7. Static type validation

Static type validation is both a powerful method to catch a variety of bugs and a way to add clear, automatically checked documentation to your source code. Transcrypt includes an experimental version of Jukka Lehtosalo's mypy static type validator This validator uses type hints to rigorously cross-check correct use of datatypes all through your application. To activate static type validation use the -ds switch. You can combine static type validation with lightweight consistency checking using both the -ds and -dc switches.

Below is an example of code with type hints and deliberate inconsistencies, and the output of both the static type validator and the lightweight consistency checker. As can be seen, many errors can be caught in this way.

manual_tests/static_types/static_types.py

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    # from org.transcrypt.stubs.browser import __pragma__

    from typing import Iterator #, List, Dict, ClassVar

    import mod1
    import mod2

    def fib (n: int) -> Iterator [int]:
        a, b = 0, 1
        while a < n:
            # yield a
            a, b = b, a + b

        return 3

    def add (a: int, b: int) -> None:
        return a + b

    class A:
        def __init__ (self) -> None:
            pass

        def test (self) -> None:
            return 'test'

    '''
    __pragma__ ('ifdef', '__undefined__')   # Needed because Transcrypt imports are compile time
    if '__undefined__' in __symbols__:      # Needed because CPython doesn't understand pragmas

        # Variable annotations

        aList: List [int] = []

        aString: str    # Note: no initial value!

        class aClass:
            aClassVar: ClassVar [Dict [str, int]] = {}

        aList = [1.1, 2.2]
        aString = 1000
        aClass.aClassVar = {'aString', 3.14}

    __pragma__ ('endif')
    '''

manual_tests/static_types/mod1.py

    1
    2
    3
    4
    5
    def test (i: int) -> int:
        a = 3
        a = 4.5
        return str (i)

manual_tests/static_types/mod2/__init__.py

    1
    2
    3
    def test (i: str) -> str:
        return 3

Results of the static type validation and the lightweight consistency check

    D:\activ_tosh\geatec\transcrypt\transcrypt\development\manual_tests\static_types>C:\Python35\python.exe D:\activ_tosh\geatec\transcrypt\transcrypt\__main__.py -b -m -dm -dt -da -e 6 -ds -dc -n static_types.py
    Transcrypt (TM) Python to JavaScript Small Sane Subset Transpiler Version 3.6.2
    Copyright (C) Geatec Engineering. License: Apache 2.0

    Performing static type validation on application: D:/activ_tosh/geatec/transcrypt/transcrypt/development/manual_tests/static_types/static_types.py
    Validating: D:/activ_tosh/geatec/transcrypt/transcrypt/development/manual_tests/static_types/static_types.py and dependencies
        Internal error in static typing validator
    Performing lightweight consistency check on module: D:/activ_tosh/geatec/transcrypt/transcrypt/development/manual_tests/static_types/static_types.py
        Line 5: 'mod1' imported but unused
        Line 6: 'mod2' imported but unused

    Performing lightweight consistency check on module: D:/activ_tosh/geatec/transcrypt/transcrypt/development/manual_tests/static_types/mod1.py
        Line 3: local variable 'a' is assigned to but never used

    Saving result in: D:/activ_tosh/geatec/transcrypt/transcrypt/development/manual_tests/static_types/__javascript__/static_types.js

    Ready