3. Special facilities ********************* 3.1. Transcrypt's module mechanism ================================== Transcrypt's module mechanism looks a lot like Python's but there are a few differences. Firstly, in Transcrypt it is good practice to use url-based unique module identifiers, e.g. * *com.github..* * *org.python.pypi.* * *com..* To achieve optimal CPython compatibility, an exception is made for modules that are part of the CPython distribution, e.g. *math*. Note that Transcrypt is meant be to used with JavaScript rather than Python libraries, since its focus isn't on the desktop but on the browser. Nevertheless a growing set of standard Python modules is part of the distribution, currently cmat, datetime, itertools, logging, math, random (partially), re (almost complete), time, turtle and warnings. Other modules are in the making. Finding the code for a module proceeds as follows: Suppose you import a module *all.the.kings.men*. Then the following paths will be searched respectively: * */all/the/kings/men.py* * */all/the/kings/men.js* * */all/the/kings/men/__init__.py* * */all/the/kings/men/__init__.js* * *transcrypt/Transcrypt/modules/all/the/kings/men.py* * *transcrypt/Transcrypt/modules/all/the/kings/men.js* * *transcrypt/Transcrypt/modules/all/the/kings/men/__init__py* * *transcrypt/Transcrypt/modules/all/the/kings/men/__init__js* * */all/the/kings/men.py* * */all/the/kings/men.js* * */all/the/kings/men/__init__.py* * */all/the/kings/men/__init__.js* * */all/the/kings/men.py* * */all/the/kings/men.js* * */all/the/kings/men/__init__.py* * */all/the/kings/men/__init__.js* * *More CPython packages directories* As can be seen from the above list, modules local to your project take precedence over modules available in Transcrypt, which again take precedence over modules available globally in CPython. Note that even if modules are made available globally in CPython, importing them in Transcrypt gives special possibilities and restrictions. They are allowed to be written in native JavaScript, in which case they reside in the __javascript__ subdirectory of the module. They should not depend on C, C++ or features that are outside Python subset supported by Transcrypt. Although under these guidelines it's quite possible to write modules that are importable both by CPython and Transcrypt, most Transcrypt modules will be come from the JavaScript, rather than from the Python ecosystem. If both a Python and a JavaScript incarnation of a module are present, the Python module is only recompiled if it's younger than the corresponding JavaScript module, unless the -b switch is used. Furthermore, note that the *__init__.py* or *__init__.js* file of a module is executed if and only if that module is imported, not if it's just somewhere in the hierarchy of directories containing that module. Furthermore the global code of a module is executed only once, no matter how often that module is imported, as is equally the case with CPython. Another deviation from CPython is that the inclusion of a module has to be decided upon compile time. This means that imports cannot be runtime conditional, so e.g. cannot be under an *if*. For compile time conditional imports you can use __pragma__ ('ifdef'). Also, since imports are resolved in one pass, cyclic imports are not supported. As a consequence of the above, modules may be distributed as Python *.py* files, but also as JavaScript-only *.js* files. The JavaScript files may be hand written or generated by any tool including Transcrypt. 3.2. Using browser stubs to test non-GUI code that uses console.log and window.alert ==================================================================================== To test the non-GUI part of your code in a desktop rather than a browser environment, use *from org.transcrypt.stubs.browser import **. This will allow you to call the *window.alert* and *console.log* functions in your code when you run it from the command prompt, using the -r command line switch: *transcrypt -r *. This will invoke CPython, searching the appropriate module paths as compilation would have done. 3.3. Creating JavaScript objects with __new__ () ================================================================== While creating objects in Transcrypt mostly happens in the plain Python way, e.g. *canvas = Canvas ()*, objects from 3rd party JavaScript libraries often have to be created using *new*. In Transcrypt such objects are created by calling the *__new__* function, e.g. *canvas = __new__ (Canvas ()*, as can be seen in the Pong example. This mechanism is simple, follows Python's syntax rules and doesn't require updating of an encapsulation layer if a later version of the underlying JavaScript library features additional constructor functions. Therefore in most cases it is the preferred way to create objects who's initialization requires calling 3rd party JavaScript constructor functions. As an alternative, instantiation and construction can be encapsulated in one function call. Since this in fact creates an alternative API facade for the 3rd party JavaScript library, such an encapsulation should be kept separate from the original library, e.g. by putting it in a separate importable module. The JavaScript code for this encapsulation would e.g. be *.Canvas = function () {return new Canvas ();};*. After importing the facade module, canvas creation is straightforward: *canvas = Canvas ()*. As a third alternative, encapsulation can be done in Python rather than JavaScript: *def Canvas (): return __new__ (<3rd party module name>.Canvas ()*. Also in this case the creation syntax is simple: *canvas = Canvas ()*. 3.4. The __pragma__ mechanism ============================= Pragma's are directives in the source code, that locally alter the behaviour of the compiler. Pragma's come in four varieties. 3.4.1. The function-like variety -------------------------------- *__pragma__ ()* is acceptable only to Transcrypt, CPython requires a stub with parameter **args*. Such a stub can either be defined locally or imported: *from org.transcrypt.stubs.browser import __pragma__* 3.4.2. The comment-like variety ------------------------------- # __pragma__ () is acceptable both to Transcrypt and CPython. In CPython it does nothing. N.B. Both varieties above have to be properly indented, matching indentation of their context. 3.4.3. The single-line activation variety ----------------------------------------- # __: It will switch a facility on or off just for the line of code it's part of. Single line pragma's can only be used for pragma's which have a single parameter, it's name. For example the following line: *vector2 = vector0 + vector1 # __:opov* will be compiled identically to: *__pragma__ ('opov'); vector2 = vector0 + vector1; __pragma__ ('noopov')* 3.4.4. The single-line deactivation variety ------------------------------------------- *scalar2 = scalar0 + scalar1 # __:noopov* will be compiled identically to: *__pragma__ ('noopov'); scalar2 = scalar0 + scalar1; __pragma__ ('opov')* 3.5. Identifier aliasing: __pragma__ ('alias', ...) and __pragma__ ('noalias', ...) =================================================================================== Calling *__pragma__ ('alias', , )* at the start of a module will replace identifiers or parts thereof like in the following examples: Example 1: +--------------------------------------------+-----------------------------------+ | Used at the start of the module: *__pragma__ ('alias', 'S', '$')* | +--------------------------------------------+-----------------------------------+ | Original in Python: | Alias in JavaScript: | +--------------------------------------------+-----------------------------------+ | *S ('body')* | *$ ('body')* | +--------------------------------------------+-----------------------------------+ | *S__body* | *$body* | +--------------------------------------------+-----------------------------------+ | *S__She__S__Sells__S__Sea__S__Shells__S* | *$She$Sells$Sea$Shells$* | +--------------------------------------------+-----------------------------------+ Using the above alias, a piece of jQuery code will look like this in Python: Use of __pragma__ ('alias', 'S', '$') in jquery_demo.py __pragma__ ('alias', 'S', '$') def start (): def changeColors (): for div in S__divs: S (div) .css ({ 'color': 'rgb({},{},{})'.format (* [int (256 * Math.random ()) for i in range (3)]), }) S__divs = S ('div') changeColors () window.setInterval (changeColors, 500) Example 2: +-------------------------------------------+-----------------------------------+ | Used at the start of the module *__pragma__ ('alias', 'jq', '$')* | +-------------------------------------------+-----------------------------------+ | *jq__body = jq ('body')* | *$body = $ ('body')* | +-------------------------------------------+-----------------------------------+ Note that in the generated JavaScript only the modified identifiers will be present, not the original ones. So the JavaScript identifiers are only aliases for the Python ones, not for any identifier in the JavaScript code itself. A number of aliases are predefined in the source code of Transcrypt as follows: Transcrypt's predefined aliases # Format: ('', '') ('js_and', 'and'), ('arguments', 'py_arguments'), ('js_arguments', 'arguments'), ('case', 'py_case'), ('clear', 'py_clear'), ('js_clear', 'clear'), ('js_conjugate', 'conjugate'), ('default', 'py_default'), ('del', 'py_del'), ('js_del', 'del'), ('false', 'py_false'), ('js_from', 'from'), ('get', 'py_get'), ('js_get', 'get'), ('js_global', 'global'), ('Infinity', 'py_Infinity'), ('js_Infinity', 'Infinity'), ('is', 'py_is'), ('js_is', 'is'), ('isNaN', 'py_isNaN'), ('js_isNaN', 'isNaN'), ('iter', 'py_iter'), ('js_iter', 'iter'), ('items', 'py_items'), ('js_items', 'items'), ('keys', 'py_keys'), ('js_keys', 'keys'), ('name', 'py_name'), ('js_name', 'name'), ('NaN', 'py_NaN'), ('js_NaN', 'NaN'), ('new', 'py_new'), ('next', 'py_next'), ('js_next', 'next'), ('js_not', 'not'), ('js_or', 'or'), ('pop', 'py_pop'), ('js_pop', 'pop'), ('popitem', 'py_popitem'), ('js_popitem', 'popitem'), ('replace', 'py_replace'), ('js_replace', 'replace'), ('selector', 'py_selector'), ('js_selector', 'selector'), ('sort', 'py_sort'), ('js_sort', 'sort'), ('split', 'py_split'), ('js_split', 'split'), ('switch', 'py_switch'), ('type', 'py_metatype'), ('js_type', 'type'), # Only for the type metaclass, the type operator is dealt with separately in visit_Call ('TypeError', 'py_TypeError'), ('js_TypeError', 'TypeError'), ('update', 'py_update'), ('js_update', 'update'), ('values', 'py_values'), ('js_values', 'values'), ('reversed', 'py_reversed'), ('js_reversed', 'reversed'), ('setdefault', 'py_setdefault'), ('js_setdefault', 'setdefault'), ('js_super', 'super'), ('true', 'py_true'), ('undefined', 'py_undefined'), ('js_undefined', 'undefined'), The predefined aliases are used to resolve name conflicts between Python and JavaScript. Calling e.g *.sort ()* from Python will invoke a *sort* method with Python semantics, which in the generated JavaScript is called *py_sort*. Calling *.js_sort * from Python will invoke a *sort* method with JavaScript semantics, which in the generated JavaScript is simply called *sort*, as 3rd party JavaScript libraries will expect. Calling *__pragma__ ('noalias', )* will remove the alias. Calling *__pragma__ ('noalias')* without second parameter will remove all aliases. WARNING: This will also remove the predefined aliases. The alias mechanism is vital to both the compactness and speed of code generated by Transcrypt and to its seamless integration with JavaScript. It allows Transcrypt to extend native JavaScript objects without causing name conflicts even when JavaScript gets expanded over the years, but also without any conversions or other overhead. That's why in Transcrypt this approach is favored over wrapping native objects. 3.6. Generating __doc__ attributes from docstrings: __pragma__ ('docat') and __pragma__ ('nodocat') =================================================================================================== Even though docstrings may be present in your code, by default *__doc__* attributes are not generated, to keep the generated JavaScript code lean. You can switch on and off generation of *__doc__* attributes for modules, classes, methods and functions with *__pragma__ ('docat')* and *__pragma__ ('nodocat')* respectively. There's also a *-d* / *--docat* command line switch, which can be overruled per module by *__pragma__ ('nodocat')*. Advice: Though these pragma's are flexible, its advisable to use them only at the spot indicated in the docstrings testlet, to retain CPython compatibility. Note that *__pragma__ ('docat')* follows rather than precedes the module docstring, since CPython dictates the module docstring to be the first statement in a module. 3.7. Skipping Transcrypt code fragments when running with CPython: __pragma__ ('ecom') and __pragma__ ('noecom') ================================================================================================================ Executable comments are specially formatted comments, that are turned into executable statements by Transcrypt but, by nature, skipped by CPython. There are two types of executable comments: single-line and multi-line. Single-line executable comments start with #? at the current indentation level. Multi-line executable comments start with *'''?* or *"""?* and end with *?'''* or *?"""*, again at the current indentation level. There's also a *-ecom* command line switch, that can be annihilated locally by *__pragma__ ('noecom')* or its single line equivalent. If you want the opposite, skipping code in Transcrypt but executing it with CPython, use the the __pragma__ ('skip') ... __pragma__ ('noskip') pair or its single-line variety. An example of skipping code on either platform is the testlet below: The use of executable comments and, for the opposite effect, __pragma__ ('skip') and __pragma__ ('noskip') from org.transcrypt.stubs.browser import __pragma__ def run (autoTester): # __pragma__ ('ecom') # =================================================================== # --- Executed only by Transcrypt --- '''? for i in range (10): autoTester.check (i) ?''' # --- Executed only by CPython --- # __pragma__ ('skip') for i in range (10): autoTester.check (i) # __pragma__ ('noskip') # --- Executed only by Transcrypt --- #?autoTester.check (100) # --- Executed only by CPython --- autoTester.check (100) #__: skip #__pragma__ ('noecom') # =================================================================== # --- Executed by none --- '''? for i in range (10, 20): autoTester.check (i) ?''' # --- Executed by none --- #?autoTester.check (200) __pragma__ ('ecom') # =================================================================== # --- Executed only by Transcrypt --- '''? for i in range (20, 30): autoTester.check (i) ?''' # --- Executed only by CPython --- # __pragma__ ('skip') for i in range (20, 30): autoTester.check (i) # __pragma__ ('noskip') # --- Executed only by Transcrypt --- #?autoTester.check (300) # --- Executed only by CPython --- autoTester.check (300) #__: skip __pragma__ ('noecom') # =================================================================== # --- Executed by none --- '''? for i in range (30, 40): autoTester.check (i) ?''' # --- Executed by none --- #?autoTester.check (400) 3.8. Surpassing the speed of native JavaScript: __pragma__ ('fcall') and __pragma ('nofcall') ============================================================================================= Fast calls or fcalls are method calls where the method isn't an attribute of an object's prototype, but of the object itself, even if this method is inherited over multiple levels. This means that only for the first call the prototype chain is searched and the method is bound to the object. All subsequent calls invoke the bound method directly on the object. You can use the -f command line switch to generate fcall's pervasively, making your objects slightly larger since they will contain references to their methods. If you don't want that, just place the definition of the method(s) or class(es) you wish to optimize between *__pragma__ ('fcall')* and *__pragma__ ('nofcall')*. You can also do the opposite, using the -f switch and exempting some method(s) or class(es) by embedding them between *__pragma__ ('nofcall')* and *__pragma__ ('fcall')*. Note that these pragmas have to be applied on the function definition location rather than the call location. Placing *__pragma__ ('fcall')* or *__pragma__ ('nofcall')* at the beginning of a module will influence all methods defined in that module. The fcall mechanism is a form of memoization and one example of a transpiler being able to generate optimized code that surpasses common hand coding practice. The fcall mechanism influences neither the pure Python syntax nor the semantics of your program. 3.9. Enabling Pythons *send* syntax: __pragma__ ('gsend') and __pragma ('nogsend') ================================================================================== These pragmas enable respectively disable use of Pythons *send* syntax for generators, which is disabled by default. An example of its use is in the following code: Use of __pragma__ ('gsend') to enable Pythons *send* syntax fo generators __pragma__ ('gsend') def test0 (): r = 0 while True: r = r + (yield r) gen0 = test0() next (gen0) autoTester.check (gen0.send (1)) autoTester.check (gen0.send (2)) def test1 (): r = 0 while True: r = (yield r) + r gen1 = test1() next (gen1) autoTester.check (gen1.send (3)) autoTester.check (gen1.send (4)) 3.10. Automatic conversion to iterable: __pragma__ ('iconv') and __pragma__ ('noiconv') ======================================================================================= In CPython sometimes automatic conversion from a non-iterable to an iterable type takes place. This comes at the expense of a runtime type check and is by default avoided in Transcrypt for that reason. Iteration through the keys of a *dict* without explicitly calling its *keys ()* member is a frequent use case of automatic conversion. To switch on automatic conversion for dicts locally, *__pragma__ ('iconv')* and *__pragma__ ('noiconv')* can be used. The alternative is to switch on automatic conversion globally using the -i command line switch. Use of this switch is disadvised, especially for numerical processing code containing nested loops, since it adds the mentioned type check to each *for .. in ..* loop. When designing numerical processing libraries, it's advisable to use *__pragma__ ('noiconv')* explicitly at the start of each performance-sensitive module. The result will be that even when an application developer chooses to use the -i switch, the performance of the computations won't suffer. 3.11. Conditional compilation: __pragma__ ('ifdef', ), __pragma__ ('ifndef', ), __pragma__ ('else') and __pragma__ ('endif') ============================================================================================================================================ A piece of code in between *__pragma__ ('ifdef', )* and *__pragma__ ('endif')* will only be compiled if occurs in the global list of defined symbols. This pragma works mainly in combination with the *-s* / *--symbols * command line option. On top of that, some command line options automatically add symbols, without using the *-s* switch. TODO: Add example Code after *__pragma__ ('ifndef', )* is compiled if is NOT defined. To choose between two alternative source code blocks, precede the second block with *__pragma__ ('else')* and terminate it with *__pragma__ ('endif')*. Important: The conditional compilation pragma's also work when placed at the start of a line in precompiled or hand-written JavaScript code. 3.12. Inserting literal JavaScript: __pragma__ ('js', ...) and __include__ (...) ================================================================================ During compilation the *__pragma__ ('js', code, )* is replaced by the JavaScript code given in the *code* parameter. This code is formatted using the Python *str.format* method, using **. An example of its use is to encapsulate a JavaScript library as a Python module, as is shown for the fabric.js library. In that case there's usually one format parameter, namely a call to *__include__ ()*. The module path is either relative to the directory holding the main module of your project, or to the root of the modules directory, and searched in that order. So modules local to your project prevail over generally available modules. Note that since {} is used as placeholder in Python formatting, any normal { and } in your JavaScript in the code parameter have to be doubled. If you just want to include a literal piece of JavaScript without any replacements, you can avoid this doubling by using __pragma__ ('js', '{}', ). 3.13. Create bare JavaScript objects and iterate over their attributes from Python: __pragma__ ('jsiter') and __pragma__ ('nojsiter') ===================================================================================================================================== Normally a Python *{...}* literal is compiled to *dict ({...})* to include the special attributes and methods of a Python dict, including e.g. an iterator. When *__pragma__ ('jsiter')* is active, a *Python {...}* literal is compiled to a bare *{...}*, without special attributes or methods. To still be able to iterate over the attributes of such a bare JavaScript object from Python, when *__pragma__ ('jsiter')* is active, a Python *for ... in ...* is literally translated to a JavaScript *for (var ... in ...)*. The main use case for this pragma is conveniently looping through class attributes in the *__new__* method of a metaclass. As a more flexible, but less convenient alternative, *__pragma__ ('js', '{}', '''...''')* can be used. An example of the use of this pragma is the following: Use of __pragma__ ('jsiter') and __pragma ('nojsiter') to manipulate class attributes in a metaclass class UppercaserMeta (type): def __new__ (meta, name, bases, attribs): __pragma__ ('jsiter') # Translate for ... in directly to JavaScript for ... in ... and translate {} to bare {} rather than to dict {} # Using bare {} as attribs parameter to __new__ avoids dict attributes masking regular attributes # For more flexibility use __pragma__ ('js', '{}', '''...''') upperAttribs = {} for attribKey in attribs: # Translates to 'for (var attribKey in attribs)' by virtue of __pragma__ ('jsiter'), to iterate over the attributes of a bare JavaScript {} upperAttribs [attribKey if attribKey.startswith ('__') else attribKey.upper ()] = attribs [attribKey] __pragma__ ('nojsiter') return type.__new__ (meta, name, bases, upperAttribs) 3.14. __pragma__ ('jskeys') and __pragma__ ('nojskeys') ======================================================= Normally in Python, dictionary keys without quotes are interpreted as identifiers and dictionary keys with quotes as string literals. This is more flexible than the JavaScript approach, where dictionary keys with or without quotes are always interpreted as string literals. However to better match the documentation and habits with some JavaScript libraries, such as plotly.js, dictionary keys without quotes can be optionally interpreted as string literals. While the -jk command line switch achieves this globally, the preferred way is to switch on and off this facility locally. 3.15. Keeping your code lean: __pragma__ ('jsmod') and __pragma__ ('nojsmod') ============================================================================= When *__pragma__ ('jsmod')* is active, *%* has JavaScript rather than Python semantics. While the -jm command line switch achieves this globally, the preferred way is to switch on and off this facility locally. 3.16. __pragma__ ('keycheck') and __pragma__ ('nokeycheck') =========================================================== When *__pragma__ ('keycheck')* is active, an attempt to retrieve an element from a dictionary via a non-existing key results in a KeyError being raised, unless it happens at the left hand side of an augmented assignment. The run time support for this is expensive, as it requires passing the result of every such retrieval to a function that checks the definedness of the result. While the -kc command line switch switches key checking on globally, the preferred way is to switch on and off this facility locally to prevent code bloat. 3.17. Keeping your code lean: __pragma__ ('kwargs') and __pragma__ ('nokwargs') =============================================================================== While it's possible to compile with the -k command line switch, allowing keyword arguments in all flavors supported by Python 3.5 in all places, this disadvised, as it leads to bloated code. It is better to use the 'kwargs' and 'nokwargs' pragmas, to enable this feature only at definition (as opposed to calling) of functions that require it. You'll find an example of how to use these pragma's in the arguments autotest. You can use them on whole modules or any part thereof. Note that at due to the dynamic nature of Python, use of keyword arguments at call time cannot be predicted at definition time. When running with CPython from the command prompt using the browser stubs, these pragma's are ignored. 3.18. Preventing target annotation: __pragma__ ('noanno') ========================================================= The -a command line switch will comment target code compiled from Python with source file names and source line numbers. Since this interferes with literal inclusion of JavaScript code with multi-line comments, it can be switched off by including *__pragma__ ('noanno')* at the beginning of a module. An example of this can be seen in the encapsulation code for the fabric.js library. 3.19. Operator overloading: __pragma__ ('opov') and __pragma__ ('noopov') ========================================================================= Transcrypt currently supports overloading of the following operators: *, /, +, -, @, [], (), ==, !=, <, <=, >, and >=. These operators have been chosen since they can enhance the readability of computations involving matrices and vectors, enable the use of callable objects (functors) or are used to compare tuples, lists and sets. Using the -o command line switch will activate operator overloading globally. *This is strongly disadvised*, since even 1 + 2 will result in two function calls in that case. It's better to use *__pragma__ ('opov')* to switch it on locally and *__pragma__ ('noopov')* to switch it off again, activating operator overloading only for lines of code involving extensive matrix / vector computations or functor calls (as opposed to definitions), in which case the overhead is negligible in most cases. Formula v4 = M * (v1 + v2) + v3 is probably preferred over v4 = add (mul (M, add (v1, v2), v3)), which by the way closely resembles the JavaScript that will be generated for the expression that uses overloaded + and * operators. In order to support operator overloading your matrix and vector classes have to support the appropriate selection of functions out of *__mul__*, *__rmul__*, *__div__*, *__rdiv__*, *__add__*, *__radd__*, *__sub__*, *__rsub__*, *__matmul__*, *__rmatmul__*, *__getitem__*, *__setitem__* and *__call__* as described in the CPython documentation. The comparison operators are supported by the functions *__eq__*, *__ne__*, *__lt__*, *__le__*, *__gt__* and *__ge__*. 3.20. Skipping fragments while generating code: __pragma__ ('skip') and __pragma__ ('noskip') ============================================================================================= On some occasions it's handy to be able to skip code generation for fragments of Python source. The most important use case is when you perform a static check using the -c or --check command line switch. If you use global JavaScript names without importing them from some encapsulation module, you'll have to tell the static checker that these names are not to be flagged as undefined. Insert a line containing dummy definitions of the unknown names for the static checker, surrounded by *__pragma__ ('skip') and __pragma__ ('noskip')*, to make code generator skip these definitions. The beginning of the source code of the Pong demo, shown below, demonstrates how this is done. Preventing complaints of the static checker about unknown global JavaScript names in pong.py __pragma__ ('skip') document = window = Math = Date = 0 # Prevent complaints by optional static checker __pragma__ ('noskip') __pragma__ ('noalias', 'clear') from com.fabricjs import fabric orthoWidth = 1000 orthoHeight = 750 fieldHeight = 650 enter, esc, space = 13, 27, 32 window.onkeydown = lambda event: event.keyCode != space # Prevent scrolldown on spacebar press 3.21. Automatic conversion to truth value: __pragma__ ('tconv') and __pragma__ ('notconv') ========================================================================================== When automatic conversion to truth value is activated, an empty array, dict or set has truth value False just as in CPython, rather than True, as would be the case natively in JavaScript. This comes at the expense of a runtime type check and is by default avoided in Transcrypt for that reason. To switch on automatic conversion to truth values locally, *__pragma__ ('tconv')* and *__pragma__ ('notconv')* can be used. The alternative is to switch on automatic conversion globally using the -t command line switch. Use of this switch is disadvised if a lot of boolean expressions are computed in inner loops, since it would result in many extra type checks. When designing performance sensitive libraries, it's advisable to use *__pragma__ ('notconv')* explicitly at the start of each performance-sensitive module. The result will be that even when an application developer chooses to use the -t switch, performance of won't suffer. 3.22. Adding directories to the module search path: __pragma__ ('xpath', ) ========================================================================================== In addition to the regular module mechanism you can use *# __pragma__ ('xpath', )* to add directories to the module search path. Note that resolving search paths happens compile time, so dynamic manipulation of the module search path doesn't apply. If you want to alter the search path in code that is used both by Transcrypt and CPython, you can additionally manipulate *sys.path* in a *try* clause or under an *if* with condition *__envir__.executor_name == __envir__.interpreter_name*. The *-xp / --xpath* command line switch has the same effect. 3.23. Using an external transpiler: __pragma__ ('xtrans', , ..., cwd = ) ====================================================================================================== This pragma works the same as *__pragma__ ('js', ...)*, only now an external translator application can be specified, e.g. to translate JSX code. If the *cwd* parameter is present, it the external translator will use it as working directory. The code offered to this pragma is routed through this external translator by means of pipes. This pragma can use *__include__ (...)* just like *__pragma__ ('js', ...)*. Example: Test program using __pragma__ ('xtrans', ...) a = 1 __pragma__ ('xtrans', 'change_case.exe', '{}', '''/*
{self.props.students.map((s) => ( ))}
*/\f''', cwd = 'workdir', ) __pragma__ ('xtrans', 'change_case.exe -l', '{}', '''/*
{self.props.students.map((s) => ( ))}
*/\f''', cwd = 'workdir', ) b = 2 Simple external translator change_case.cpp, changing the case to upper or lower, depending on -l switch #include #include #include #include using namespace std; int main (int argc, char *argv []) { string buffer; getline (cin, buffer, '\f'); if (argc > 1 && string (argv [1]) == "-l") { transform (buffer.begin(), buffer.end(), buffer.begin(), ::tolower); } else { transform (buffer.begin(), buffer.end(), buffer.begin(), ::toupper); } cout << buffer; // Check if cwd parameter works correctly ofstream outputFile ("output.txt"); outputFile << buffer; outputFile.close (); } The resulting translated file test.js 'use strict';import{AssertionError,AttributeError,BaseException,DeprecationWarning,Exception,IndexError,IterableError,KeyError,NotImplementedError,RuntimeWarning,StopIteration,UserWarning,ValueError,Warning,__JsIterator__,__PyIterator__,__Terminal__,__add__,__and__,__call__,__class__,__envir__,__eq__,__floordiv__,__ge__,__get__,__getcm__,__getitem__,__getslice__,__getsm__,__globals__,__gt__,__i__,__iadd__,__iand__,__idiv__,__ijsmod__,__ilshift__,__imatmul__,__imod__,__imul__,__in__,__init__,__ior__, __ipow__,__irshift__,__isub__,__ixor__,__jsUsePyNext__,__jsmod__,__k__,__kwargtrans__,__le__,__lshift__,__lt__,__matmul__,__merge__,__mod__,__mul__,__ne__,__neg__,__nest__,__or__,__pow__,__pragma__,__proxy__,__pyUseJsNext__,__rshift__,__setProperty__,__setitem__,__setslice__,__sort__,__specialattrib__,__sub__,__super__,__t__,__terminal__,__truediv__,__xor__,abs,all,any,assert,bool,bytearray,bytes,callable,chr,copy,deepcopy,delattr,dict,dir,divmod,enumerate,filter,float,getattr,hasattr,input,int,isinstance, issubclass,len,list,map,max,min,object,ord,print,property,py_TypeError,py_iter,py_metatype,py_next,py_reversed,py_typeof,range,repr,round,set,setattr,sorted,str,sum,tuple,zip}from"./org.transcrypt.__runtime__.js";var __name__="__main__";export var a=1;export var b=2; //# sourceMappingURL=test.map