Transcrypt: technologie umožňující použití Pythonu v prohlížeči

24. 10. 2023
Doba čtení: 35 minut

Sdílet

Autor: Depositphotos
V dnešním článku se seznámíme s nástrojem Transcrypt, což je další překladač (přesněji řečeno transpřekladač – transpiler) zdrojových kódů z Pythonu do JavaScriptu. Nabízí i celou řadu funkcí ze standardní knihovny Pythonu.

Obsah

1. Transcrypt: další technologie umožňující použití Pythonu na front endu

2. Programovací jazyk Python a front end webových aplikací

3. Programovací jazyk Python a front end webových aplikací

4. Instalace Transcryptu nástrojem pip

5. Instalace v případě použití PDM

6. Google Closure Compiler

7. Instalace JDK pro Google Closure Compiler

8. Transpřeklad Pythonu do JavaScriptu

9. Překlad kódu, který manipuluje se seznamy, do JavaScriptu

10. Překlad kódu, který manipuluje se slovníky, do JavaScriptu

11. Transformace jednoduchých funkcí do JavaScriptu

12. Překlad rekurzivní funkce a taktéž generátoru range

13. Překlad uzávěru z Pythonu do JavaScriptu

14. Funkce s pojmenovanými parametry

15. Funkce vyhazující výjimku

16. Vykreslování na canvas (kreslicí plátno) na HTML stránce

17. Text na kreslicím plátnu

18. Cesta (path) na kreslicím plátnu

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. Transcrypt: další technologie umožňující použití Pythonu na front endu

V dnešním článku se seznámíme s nástrojem Transcrypt. Jedná se o překladač (přesněji řečeno transpřekladač – transpiler) zdrojových kódů z Pythonu do JavaScriptu. Navíc tento nástroj nabízí i celou řadu funkcí ze standardní knihovny Pythonu; tyto funkce je možné v transpřeloženém programu přímo použít. Vzhledem k tomu, že se Transcrypt bude primárně používat pro front end (webové aplikace), nabízí i podporu pro přístup k DOM (Document Object Model) HTML stránky, takže je možné nejenom reagovat na vzniklé události (kliknutí tlačítkem myši na ovládací prvek atd.), ale například i provádět vykreslování na kreslicí plátno (canvas) atd.

Poznámka: samostatnou kapitolou je pak Numscrypt, což je – stručně řečeno – realizace části knihovny NumPy nad otypovanými poli JavaScriptu, což by mělo zajistit dostatečnou rychlost výpočtů (v rámci možností nabízených enginem JavaScriptu).
Poznámka: zatímco klasický Python pracuje jako překladač zdrojového kódu do bajtkódu virtuálního stroje Pythonu, Transcrypt překládá do JavaScriptu, který tak můžeme s trochou nadsázky považovat za „bajtkód pro web“ (což není přesné, protože tuto funkci by měla plnit technologie WASM, ovšem JavaScript bývá skutečně označován jako „assembler pro web“; to je ovšem již velká nadsázka).

2. Programovací jazyk Python a front end webových aplikací

Programovací jazyk Python se v současnosti používá v mnoha aplikačních oblastech – od jednoduchých skriptů umožňujících a zjednodušujících administraci systému přes složitější utility, desktopové aplikace (PyQt, PySide, wxPython, Tkinter, PyGObject či Wax) a webové služby až po zpracování dat, strojové učení (ML – machine learning) a umělou inteligenci (AI – artificial intelligence).

V dnešním článku nás ovšem bude zajímat především použití Pythonu při tvorbě webových služeb a webových aplikací. V této oblasti se Python používá především na back endu, tj. pro tu část webové služby/aplikace, která je provozována na serveru a nějakým způsobem komunikuje s ostatními komponentami vytvářeného systému: front endem (typicky webový prohlížeč s interpretrem JavaScriptu), databází a dalšími (mikro)službami, například s využitím message brokerů, systémů pro monitoring a správu událostí atd. Tato oblast samozřejmě není pokryta pouze Pythonem, ale najdeme zde i další programovací jazyky a technologie, zejména Javu (a celý její middleware), JavaScript či TypeScript (node.js) a dnes taktéž programovací jazyk Go (i když není problém využít například i Rust či další jazyky).

Zatímco pozice Pythonu v oblasti back endu je poměrně zřejmá, je situace na front endu (tedy v současnosti na straně webového prohlížeče) mnohem složitější. Důvod je jednoduchý – v této oblasti z historických důvodů kraluje JavaScript, přičemž veškeré snahy a náhradu tohoto jazyka byly prozatím neúspěšné (pokusů bylo hned několik, připomeňme například browsery s nativní podporou programovacích jazyků TCL, VBScript či projekt Dart). Ovšem stále se můžeme setkat se snahami JavaScript nepoužívat přímo, popř. ho obejít jinými nástroji. Příkladem mohou být technologie umožňující tvorbu webových aplikací podobným způsobem, jakoby se jednalo o aplikace desktopové. Příklady tohoto přístupu mohou být projekty Pyjamas (pro Python), Google Web Toolkit (GWT), Apache Cordova atd. (což jsou v současnosti většinou již nepodporované projekty).

3. Programovací jazyk Python a front end webových aplikací

Je tedy vůbec možné a praktické použít Python přímo na webovém front endu a pokud to je možné, jakou za to zaplatíme cenu? Ve skutečnosti existuje hned několik projektů, které se více či méně úspěšným způsobem snaží o to, aby byl Python na front endu skutečně použitelný, a to i pro reálně provozované aplikace. Mezi tyto projekty patří například:

  1. Brython, což je transpřekladač z Pythonu do JavaScriptu, přičemž samotný překlad (resp. přesněji řečeno transpřeklad) probíhá na pozadí, což znamená, že přímo v kódu webové stránky je umístěn (či načítán) kód v Pythonu. Podobné řešení jsme již viděli, například u projektu Wisp (jazyk podobný Clojure) či lua.js (transpřeklad z jazyka Lua). Výhodou Brythonu je, že se jakékoli úpravy ihned projeví v aplikaci po znovunačtení stránky (F5), nevýhodou celková pomalost inicializace webové aplikace.
  2. Transcrypt je taktéž transpřekladačem Pythonu (konkrétně Pythonu 3) do JavaScriptu. Samotný transpřeklad je napsaný velmi dobře – zhruba platí, že velikost vygenerovaného kódu v JavaScriptu odpovídá velikosti původního kódu napsaného v Pythonu (případné zvětšení je o jednotky procent). Musíme však počítat s tím, že je nutné načíst i jádro Transcriptu, jehož velikost je přibližně 20 kB (tedy čím větší je kód samotné aplikace, tím (poměrově) menší budou režijní náklady. Zajímavá a užitečná je i podpora type hintů (informací o datovém typu parametrů, proměnných atd.). Tímto projektem se budeme zabývat v navazujících kapitolách.
  3. Podobným způsobem pracuje i projekt nazvaný pyjaco, který naleznete na adrese https://github.com/chrivers/pyjaco.
  4. Skulpt je naproti tomu v mnoha ohledech podobný projektu Brython, protože taktéž umožňuje provádět transpřeklad na pozadí (programátor tedy pracuje pouze se zdrojovým kódem vytvořeným v Pythonu).
  5. PyScript je oproti předchozím nástrojům realizován odlišně, protože je postaven na projektu nazvaném Pyodide. Jedná se v prvé řadě o upravený překlad celého standardního Pythonu (konkrétně Pythonu, resp. přesněji řečeno CPythonu 3.9), ovšem nikoli do nativního kódu spustitelného přímo z příkazové řádky, ale do bajtkódu WebAssembly. To mj. znamená, že interpret Pythonu, resp. přesněji řečeno programů napsaných pro Python, lze spustit přímo z webového prohlížeče, a to dokonce bez nutnosti mít Python lokálně nainstalovaný – musíme mít pouze k dispozici vhodný webový server (pro jednoduché projekty dokonce ani to ne – stačí lokálně uložené soubory), stránky na github.io atd. PyScript do tohoto ekosystému přidává podporu pro nové HTML značky, zejména <py-script> a <py-env>, jenž umožňují snadný zápis skriptu a navíc i specifikaci cest, modulů a knihoven, které budou na stránce použity.
  6. Velmi zajímavým nástrojem, který názorně ukazuje, jak vlastně transpřeklad funguje (a že jeho výsledek může být čitelný), je nástroj s poněkud zvláštním jménem fiddlesalad, který naleznete na adrese http://fiddlesalad.com/python/. Tento nástroj umožňuje zápis zdrojového kódu v Pythonu, který je ihned (již v době zápisu) transpřekládán do JavaScriptu a současně i spouštěn.
Poznámka: povšimněte si, že většina projektů zmíněných v předchozím textu (kromě PyScriptu) jsou ve skutečnosti transpřekladače neboli transpilery. Jedná se o technologii zajišťující, aby se i ve webovém prohlížeči mohlo (nepřímo) pracovat s dalšími programovacími jazyky, nejenom se všudypřítomným a nativně podporovaným JavaScriptem. Další variantou je interpretace s využitím interpretru psaného v JavaScriptu, popř. použití WebAssembly.

4. Instalace Transcryptu nástrojem pip

K dispozici je hned několik možností instalace Transcryptu. Vzhledem k tomu, že je tento nástroj dostupný přes PyPi, můžeme pro jeho instalaci použít standardní mechanismus dostupný přes pip, resp. pip3. Instalace pouze pro přihlášeného uživatele (nebo do virtuálního prostředí) je snadná:

$ pip3 install --user transcrypt
 
Collecting transcrypt
  Downloading Transcrypt-3.9.1-py2.py3-none-any.whl (19.0 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 19.0/19.0 MB 5.1 MB/s eta 0:00:00
Collecting mypy
  Downloading mypy-1.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (12.1 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.1/12.1 MB 5.9 MB/s eta 0:00:00
Collecting typing-extensions>=4.1.0
  Downloading typing_extensions-4.8.0-py3-none-any.whl (31 kB)
Collecting mypy-extensions>=1.0.0
  Downloading mypy_extensions-1.0.0-py3-none-any.whl (4.7 kB)
Installing collected packages: typing-extensions, mypy-extensions, mypy, transcrypt
Successfully installed mypy-1.6.1 mypy-extensions-1.0.0 transcrypt-3.9.1 typing-extensions-4.8.0
Poznámka: povšimněte si, že Transcrypt závisí na nástroji Mypy, kterému jsme se na stránkách Roota již věnovali [1] [2] [3].

Po instalaci si ověříme, zda je dostupný příkaz transcrypt:

$ transcrypt
 
Transcrypt (TM) Python to JavaScript Small Sane Subset Transpiler Version 3.9.0
Copyright (C) Geatec Engineering. License: Apache 2.0
 
 
Usage: transcrypt [-h] [-a] [-am] [-b] [-c] [-d] [-da] [-dc] [-de] [-dl] [-dm]
                  [-dn] [-ds] [-dt] [-e [esv]] [-ec] [-f] [-g] [-i] [-jc]
                  [-jk] [-jm] [-k] [-kc] [-l] [-m] [-n] [-o] [-od outdir]
                  [-p [parent]] [-r] [-s [symbols]] [-sf] [-t] [-u [unit]]
                  [-v] [-x x] [-xr] [-xg] [-xp [xpath]] [-xt] [-*]
                  [source]
Poznámka: jak uvidíme dále, není ve skutečnosti ještě instalace u konce, protože Transcrypt závisí i na JDK.

5. Instalace v případě použití PDM

Transcrypt lze nastavit i jako závislý balíček při použití nástroje PDM, který byl na Rootu popsán minulý týden. Ukažme si pro úplnost, jak by mohl vypadat projekt, který je na Transcryptu postaven.

Nejprve si necháme vytvořit kostru nového projektu:

$ pdm init

Poctivě odpovíme na položené otázky (většinou jen potvrzením klávesou Enter):

Creating a pyproject.toml for PDM...
Please enter the Python interpreter to use
0. /usr/bin/python (3.11)
1. /usr/bin/python3.11 (3.11)
2. /usr/bin/python3 (3.11)
Please select (0): 0
Would you like to create a virtualenv with /usr/bin/python? [y/n] (y): Enter
Virtualenv is created successfully at /home/ptisnovs/test1/.venv
Is the project a library that is installable?
If yes, we will need to ask a few more questions to include the project name and
build backend [y/n] (n): Enter
License(SPDX name) (MIT): Enter
Author name ():  Enter
Author email ():  Enter
Python requires('*' to allow any) (>=3.11): Enter
Project is initialized successfully

Do projektu přidáme závislost na Transcriptu. Výsledný projekt není běžným Pythonovským projektem, kde by výsledkem měla být aplikace nebo knihovna, takže je vlastně jedno, zda použijeme přímou závislost či závislost vývojářskou:

$ pdm add transcrypt

V tomto kroku by se měly nainstalovat stejné balíčky, jako při instalaci přes pip:

Adding packages to default dependencies: transcrypt
? Lock successful
Changes are written to pyproject.toml.
Synchronizing working set with resolved packages: 4 to add, 0 to update, 0 to
remove
 
  ✔ Install mypy-extensions 1.0.0 successful
  ✔ Install typing-extensions 4.8.0 successful
  ✔ Install transcrypt 3.9.1 successful
  ✔ Install mypy 1.6.1 successful
 
? All complete!

Výsledný projektový soubor bude vypadat takto:

[project]
name = ""
version = ""
description = ""
authors = [
    {name = "", email = ""},
]
dependencies = [
    "transcrypt>=3.9.1",
]
requires-python = ">=3.11"
readme = "README.md"
license = {text = "MIT"}

V případě, že zvolíme závislost pro vývojáře, bude soubor nepatrně odlišný:

[project]
name = ""
version = ""
description = ""
authors = [
    {name = "", email = ""},
]
[tool.pdm.dev-dependencies]
dev = [
    "transcrypt>=3.9.1",
]
requires-python = ">=3.11"
readme = "README.md"
license = {text = "MIT"}

Pro naprostou úplnost – takto bude vypadat lock file:

# This file is @generated by PDM.
# It is not intended for manual editing.
 
[metadata]
groups = ["default"]
cross_platform = true
static_urls = false
lock_version = "4.3"
content_hash = "sha256:80f8c67207f7ff039345d61bf38429c99a43845a0e7f3c5a710ac5fb657c23dd"
 
[[package]]
name = "asyncio"
version = "3.4.3"
summary = "reference implementation of PEP 3156"
files = [
    {file = "asyncio-3.4.3-py3-none-any.whl", hash = "sha256:c4d18b22701821de07bd6aea8b53d21449ec0ec5680645e5317062ea21817d2d"},
    {file = "asyncio-3.4.3.tar.gz", hash = "sha256:83360ff8bc97980e4ff25c964c7bd3923d333d177aa4f7fb736b019f26c7cb41"},
]
 
[[package]]
name = "mypy"
version = "1.6.1"
requires_python = ">=3.8"
summary = "Optional static typing for Python"
dependencies = [
    "mypy-extensions>=1.0.0",
    "typing-extensions>=4.1.0",
]
files = [
    {file = "mypy-1.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:81af8adaa5e3099469e7623436881eff6b3b06db5ef75e6f5b6d4871263547e5"},
    {file = "mypy-1.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8c223fa57cb154c7eab5156856c231c3f5eace1e0bed9b32a24696b7ba3c3245"},
    {file = "mypy-1.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8032e00ce71c3ceb93eeba63963b864bf635a18f6c0c12da6c13c450eedb183"},
    {file = "mypy-1.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4c46b51de523817a0045b150ed11b56f9fff55f12b9edd0f3ed35b15a2809de0"},
    {file = "mypy-1.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:19f905bcfd9e167159b3d63ecd8cb5e696151c3e59a1742e79bc3bcb540c42c7"},
    {file = "mypy-1.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:82e469518d3e9a321912955cc702d418773a2fd1e91c651280a1bda10622f02f"},
    {file = "mypy-1.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d4473c22cc296425bbbce7e9429588e76e05bc7342da359d6520b6427bf76660"},
    {file = "mypy-1.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59a0d7d24dfb26729e0a068639a6ce3500e31d6655df8557156c51c1cb874ce7"},
    {file = "mypy-1.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cfd13d47b29ed3bbaafaff7d8b21e90d827631afda134836962011acb5904b71"},
    {file = "mypy-1.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:eb4f18589d196a4cbe5290b435d135dee96567e07c2b2d43b5c4621b6501531a"},
    {file = "mypy-1.6.1-py3-none-any.whl", hash = "sha256:4cbe68ef919c28ea561165206a2dcb68591c50f3bcf777932323bc208d949cf1"},
    {file = "mypy-1.6.1.tar.gz", hash = "sha256:4d01c00d09a0be62a4ca3f933e315455bde83f37f892ba4b08ce92f3cf44bcc1"},
]
 
[[package]]
name = "mypy-extensions"
version = "1.0.0"
requires_python = ">=3.5"
summary = "Type system extensions for programs checked with the mypy type checker."
files = [
    {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
    {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
]
 
[[package]]
name = "transcrypt"
version = "3.9.1"
summary = "Python to JavaScript transpiler, supporting multiple inheritance and generating lean, highly readable code"
dependencies = [
    "mypy",
]
files = [
    {file = "Transcrypt-3.9.1-py2.py3-none-any.whl", hash = "sha256:73f5b3587f561aff66fa82f5fb3407f8b5950e991ad335f5f6dd889990ab0e10"},
]
 
[[package]]
name = "typing-extensions"
version = "4.8.0"
requires_python = ">=3.8"
summary = "Backported and Experimental Type Hints for Python 3.8+"
files = [
    {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"},
    {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"},
]

6. Google Closure Compiler

Nástroj Transcrypt ve skutečnosti při transpřekladu provádí několik operací:

  1. Převod zdrojového kódu Pythonu do AST (Abstract Syntax Tree)
  2. Transformace na úrovni AST
  3. Vygenerování JavaScriptového kódu z AST
  4. Optimalizace a minifikace výsledného JavaScriptového kódu

Pro poslední krok, tedy pro optimalizace a minifikace, se interně volá Google Closure Compiler. Tento krok je relativně pomalý (větší projekty budou optimalizovány řádově v sekundách), ovšem zásadnější je, že Google Closure Compiler vyžaduje JDK. To je buď nutné nainstalovat (což je téma navazující kapitoly) nebo lze alternativně optimalizaci a minifikaci vypnout zápisem přepínače -n. Výsledkem pak budou sice neoptimalizované, ale zato čitelné kódy v JavaScriptu, které budeme moci snadno porovnat s jejich Pythonovskými protějšky.

7. Instalace JDK pro Google Closure Compiler

V případě, že se nástrojem Transcrypt pokusíme přeložit (resp. přesněji řečeno transpřeložit) nějaký zdrojový soubor obsahující kód v Pythonu do JavaScriptu, bude se provádět následná optimalizace a minifikace kódu právě s využitím Google Closure Compileru. Ten je sice nainstalován současně s Transcryptem, ovšem samotné JDK již nikoli. To, zda je JDK dostupné či nikoli, lze snadno ověřit:

$ transcrypt test.py

nebo příkazem:

$ pdm run transcrypt test.py

Pokud se vypíše následující chyba (viz zvýrazněný řádek), je nutné doinstalovat celé JDK:

Saving target code in: /home/ptisnovs/test1/__target__/org.transcrypt.__runtime__.js
Saving minified target code in: /home/ptisnovs/test1/__target__/org.transcrypt.__runtime__.js
 
Error while compiling (offending file last):
        File 'org.transcrypt.__runtime__', line 0, namely:
 
        [Errno 2] No such file or directory: 'java'
 
Aborted
Poznámka: pokud použijeme přepínač -n, nebude výsledná optimalizace prováděna, což využijeme v dalších kapitolách.

8. Transpřeklad Pythonu do JavaScriptu

Spuštění transpřekladu je ve skutečnosti snadné. Vše si ukážeme na kódu hello.py, který je přímo součástí instalace Transcryptu (viz devatenáctou kapitolu). V případě, že máme nainstalován nástroj Transcrypt, můžeme transpřeklad spustit takto:

$ transcrypt hello.py

Popř. v případě, že používáme PDM, lze tranpřeklad spustit nepatrně odlišným příkazem:

$ pdm run transcrypt hello.py

Průběh překladu (transformace) je vypisován na terminál:

Transcrypt (TM) Python to JavaScript Small Sane Subset Transpiler Version 3.9.0
Copyright (C) Geatec Engineering. License: Apache 2.0
 
 
Saving target code in: /home/ptisnovs/test1/__target__/org.transcrypt.__runtime__.js
Saving minified target code in: /home/ptisnovs/test1/__target__/org.transcrypt.__runtime__.js
Saving target code in: /home/ptisnovs/test1/__target__/itertools.js
Saving minified target code in: /home/ptisnovs/test1/__target__/itertools.js
Saving target code in: /home/ptisnovs/test1/__target__/hello.js
Saving minified target code in: /home/ptisnovs/test1/__target__/hello.js
 
Ready

Původní zdrojový soubor měl velikost 1181 bajtů:

-rw-r--r--. 1 ptisnovs ptisnovs 1181 Oct 19 16:11 hello.py

Výsledkem je po optimalizaci a minifikaci soubor hello.js s délkou 2481 bajtů, přičemž cca kilobajt má řádek s importem:

-rw-r--r--. 1 ptisnovs ptisnovs  2481 Oct 19 16:14 hello.js
-rw-r--r--. 1 ptisnovs ptisnovs  1167 Oct 19 16:14 hello.project
-rw-r--r--. 1 ptisnovs ptisnovs  4367 Oct 19 16:14 itertools.js
-rw-r--r--. 1 ptisnovs ptisnovs 43192 Oct 19 16:14 org.transcrypt.__runtime__.js

Pokud je optimalizace zakázána přepínačem -n, bude výsledný soubor nepatrně delší:

-rw-r--r--. 1 ptisnovs ptisnovs  2834 Oct 19 16:33 hello.js
-rw-r--r--. 1 ptisnovs ptisnovs  1166 Oct 19 16:33 hello.project
-rw-r--r--. 1 ptisnovs ptisnovs  7737 Oct 19 16:33 itertools.js
-rw-r--r--. 1 ptisnovs ptisnovs 62007 Oct 19 16:33 org.transcrypt.__runtime__.js
Poznámka: zhruba je možné počítat s tím, že výsledný soubor v JavaScriptu bude o přibližně 20% větší, než původní soubor psaný v Pythonu. To je mimochodem velmi dobrý poměr (většinou bývá výsledek transpilace mnohem větší, než originální zdrojový kód).

Můžeme si spustit HTML server a zobrazit si stránku s příkladem (samotná HTML stránka načítá jak skript, tak i podpůrné runtime knihovny):

$ python3 -m http.server

V případě, že při transpilaci použijeme přepínač -m, vygeneruje se i soubor obsahující vazbu mezi řádky napsanými v JavaScriptu a řádky v původním zdrojovém kódu Pythonu (takzvaná source mapa). V praxi nám to umožní ladění, protože nás většinou příliš nezajímá, ve kterém místě vygenerovaného JavaScriptového kódu vznikla výjimka atd., ale o jaký se jedná řádek v původním kódu.

V našem konkrétním případě bude source mapa vypadat následovně:

{
        "version": 3,
        "file": "hello.js",
        "sources": [
                "hello.py"
        ],
        "mappings": "AAAA;AAAA;AAAA;AAAA;AAEA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAWA;AAMA;AACA;AAAA;AAEA;AACA;AACA;AACA;AAAA;AAEA;AAEA;AAEA;AAAA;AAAA;AAEA;AAlCA"
}
Poznámka: formát tohoto souboru, zejména atributu mappings, je popsán v tomto dokumentu.

9. Překlad kódu, který manipuluje se seznamy, do JavaScriptu

Podívejme se nyní na způsob překladu některých základních programových konstrukcí z Pythonu do JavaScriptu. Oba zmíněné programovací jazyky mají mnoho společných rysů, což by mělo znamenat, že se i jejich konstrukce budou podobat. Začneme jednoduchým skriptem, který manipuluje se seznamy – vytváří seznam, přidává do něj další prvek a následně celý výsledný seznam vypíše:

x = [1, 2, 3, 4, 5]
 
x.append(99)
 
print(x)
 
for item in x:
    print(item)

Po (trans)překladu do JavaScriptu získáme praktický stejný zdrojový kód, který bude pouze na začátku obsahovat import symbolů, které odpovídají standardní knihovně jazyka Python (důležitější je však ta část kódu, která následuje za importem):

// Transcrypt'ed from Python, 2023-10-19 16:36:48
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__, __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__, __mergefields__, __mergekwargtrans__, __mod__, __mul__, __ne__, __neg__, __nest__, __or__, __pow__, __pragma__, __pyUseJsNext__, __rshift__, __setitem__, __setproperty__, __setslice__, __sort__, __specialattrib__, __sub__, __super__, __t__, __terminal__, __truediv__, __withblock__, __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, pow, 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 x = [1, 2, 3, 4, 5];
x.append (99);
print (x);
for (var item of x) {
        print (item);
}
 
//# sourceMappingURL=lists.map

10. Překlad kódu, který manipuluje se slovníky, do JavaScriptu

Podobně se můžeme přesvědčit o tom, jak se do JavaScriptu přeloží kód, který manipuluje se slovníky:

x = {"foo": 1, "bar": 2, "baz": None}
 
print(x)
 
for key, value in enumerate(x):
    print(key, value)

Výsledný kód v JavaScriptu:

// Transcrypt'ed from Python, 2023-10-19 16:36:27
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__, __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__, __mergefields__, __mergekwargtrans__, __mod__, __mul__, __ne__, __neg__, __nest__, __or__, __pow__, __pragma__, __pyUseJsNext__, __rshift__, __setitem__, __setproperty__, __setslice__, __sort__, __specialattrib__, __sub__, __super__, __t__, __terminal__, __truediv__, __withblock__, __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, pow, 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 x = dict ({'foo': 1, 'bar': 2, 'baz': null});
print (x);
for (var [key, value] of enumerate (x)) {
        print (key, value);
}
 
//# sourceMappingURL=maps.map
Poznámka: v tomto případě je enumerate importovaným symbolem a nikoli součástí standardní knihovny jazyka JavaScript.

11. Transformace jednoduchých funkcí do JavaScriptu

Programovací jazyky Python a JavaScript mají velmi podobnou sémantiku funkcí (funkce jsou plnohodnotnými typy, lze pracovat s uzávěry atd.), takže by nemělo být větším překvapením, jak se transformují jednoduché funkce z Pythonu do JavaScriptu. Začneme opravdu triviálním příkladem (jehož chování však ve skutečnosti již tak triviální není – záleží na datových typech):

def add(a, b):
    return a+b

Překlad (transpřeklad) do JavaScriptu je v tomto případě poměrně přímočarý:

// Transcrypt'ed from Python, 2023-10-19 16:39:41
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__, __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__, __mergefields__, __mergekwargtrans__, __mod__, __mul__, __ne__, __neg__, __nest__, __or__, __pow__, __pragma__, __pyUseJsNext__, __rshift__, __setitem__, __setproperty__, __setslice__, __sort__, __specialattrib__, __sub__, __super__, __t__, __terminal__, __truediv__, __withblock__, __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, pow, 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 add = function (a, b) {
        return a + b;
};
 
//# sourceMappingURL=adder1.map

12. Překlad rekurzivní funkce a taktéž generátoru range

Nyní se podívejme na způsob překladu funkce pro výpočet faktoriálu (která vrací hodnotu None pro neplatný vstup). Při testu této funkce se používá generátor range, který nemá v JavaScriptu přímou obdobu. Bude tedy zajímavé se podívat na výsledky transpřekladu tohoto příkladu:

def factorial(n):
    """Rekurzivní výpočet faktoriálu."""
    assert isinstance(n, int), "Integer expected"
 
    if n < 0:
        return None
    if n == 0:
        return 1
    result = n * factorial(n - 1)
 
    assert isinstance(result, int), "Internal error in factorial computation"
    return result
 
 
def main():
    for n in range(0, 11):
        print(n, factorial(n))
 
 
if __name__ == "__main__":
    main()

Samotná funkce pro výpočet faktoriálu se přeloží takto:

export var factorial = function (n) {
        if (n < 0) {
                return null;
        }
        if (n == 0) {
                return 1;
        }
        var result = n * factorial (n - 1);
        return result;
};
Poznámka: blok s assert magicky zmizel :-)

A programová smyčka s range byla přeložena do explicitně zapsané počítané smyčky typu for:

for (var n = 0; n < 11; n++) {
        print (n, factorial (n));
}

Celý program po překladu do JavaScriptu vypadá takto:

// Transcrypt'ed from Python, 2023-10-19 16:44:39
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__, __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__, __mergefields__, __mergekwargtrans__, __mod__, __mul__, __ne__, __neg__, __nest__, __or__, __pow__, __pragma__, __pyUseJsNext__, __rshift__, __setitem__, __setproperty__, __setslice__, __sort__, __specialattrib__, __sub__, __super__, __t__, __terminal__, __truediv__, __withblock__, __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, pow, 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 factorial = function (n) {
        if (n < 0) {
                return null;
        }
        if (n == 0) {
                return 1;
        }
        var result = n * factorial (n - 1);
        return result;
};
 
export var main = function () {
        for (var n = 0; n < 11; n++) {
                print (n, factorial (n));
        }
};
if (__name__ == '__main__') {
        main ();
}
 
//# sourceMappingURL=factorial.map

13. Překlad uzávěru z Pythonu do JavaScriptu

Již v předchozích kapitolách jsme si řekli, že jak jazyk Python, tak i JavaScript mají podobný přístup k sémantice funkcí. Týká se to i podpory uzávěrů, kdy má funkce (resp. přesněji řečeno kód uvnitř nějaké funkce) přístup k nelokálním symbolům. Tuto problematiku si můžeme ukázat na realizaci jednoduchého čítače s konstruktorem createCounter, jehož funkce (interní funkce next) přistupuje a dokonce i mění nelokální symbol counter:

def createCounter():
    counter = 0
    def next():
        nonlocal counter
        counter += 1
        return counter
    return next
 
 
 
#
# Spusteni testu.
#
def main():
    counter1 = createCounter()
    counter2 = createCounter()
    for i in range(1,11):
        result1 = counter1()
        result2 = counter2()
        print("Iteration #%d" % i)
        print("    Counter1: %d" % result1)
        print("    Counter2: %d" % result2)
 
 
main()

Překlad tohoto demonstračního příkladu do JavaScriptu je v tomto případě idiomatický, protože JavaScript (na rozdíl od Pythonu) nemá důvod pro použití speciálního sémantického a současně i syntaktického pravidla pro přístup k nelokálnímu symbolu:

// Transcrypt'ed from Python, 2023-10-19 16:42:46
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__, __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__, __mergefields__, __mergekwargtrans__, __mod__, __mul__, __ne__, __neg__, __nest__, __or__, __pow__, __pragma__, __pyUseJsNext__, __rshift__, __setitem__, __setproperty__, __setslice__, __sort__, __specialattrib__, __sub__, __super__, __t__, __terminal__, __truediv__, __withblock__, __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, pow, 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 createCounter = function () {
        var counter = 0;
        var py_next = function () {
                counter++;
                return counter;
        };
        return py_next;
};
 
export var main = function () {
        var counter1 = createCounter ();
        var counter2 = createCounter ();
        for (var i = 1; i < 11; i++) {
                var result1 = counter1 ();
                var result2 = counter2 ();
                print (__mod__ ('Iteration #%d', i));
                print (__mod__ ('    Counter1: %d', result1));
                print (__mod__ ('    Counter2: %d', result2));
        }
};
main ();
 
//# sourceMappingURL=counter_closure.map

14. Funkce s pojmenovanými parametry

V Pythonu se navíc běžně používají funkce s pojmenovanými a nepovinnými parametry. Příkladem může být nepatrně upravený program s funkcí pro součet dvou hodnot, které nemusí být zadány:

def add(a=1, b=2):
    return a+b
 
 
print(add())
print(add(10))
print(add(10, 20))

Tento koncept není JavaScriptem přímo podporován a proto si musel transpiler vypomoci:

// Transcrypt'ed from Python, 2023-10-23 14:42:17
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__, __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__, __mergefields__, __mergekwargtrans__, __mod__, __mul__, __ne__, __neg__, __nest__, __or__, __pow__, __pragma__, __pyUseJsNext__, __rshift__, __setitem__, __setproperty__, __setslice__, __sort__, __specialattrib__, __sub__, __super__, __t__, __terminal__, __truediv__, __withblock__, __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, pow, 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 add = function (a, b) {
        if (typeof a == 'undefined' || (a != null && a.hasOwnProperty ("__kwargtrans__"))) {;
                var a = 1;
        };
        if (typeof b == 'undefined' || (b != null && b.hasOwnProperty ("__kwargtrans__"))) {;
                var b = 2;
        };
        return a + b;
};
 
print (add ());
print (add (10));
print (add (10, 20));
 
//# sourceMappingURL=adder2.map
Poznámka: výsledek není příliš pěkný, ovšem díky source mapám se na něj nemusíme dívat :-)

15. Funkce vyhazující výjimku

Pro zajímavost si ještě vyzkoušejme nepatrnou úpravu výpočtu faktoriálu. Pro záporné vstupy budeme vyhazovat výjimku typu ValueError:

def factorial(n):
    """Rekurzivní výpočet faktoriálu."""
    assert isinstance(n, int), "Integer expected"
 
    if n < 0:
        raise ValueError(n)
    if n == 0:
        return 1
    result = n * factorial(n - 1)
 
    assert isinstance(result, int), "Internal error in factorial computation"
    return result
 
 
def main():
    for n in range(0, 11):
        print(n, factorial(n))
    print(factorial(-1))
 
 
if __name__ == "__main__":
    main()

Výsledek transpilace je v tomto případě stále poměrně dobře čitelný, o čemž se můžeme velmi snadno přesvědčit:

// Transcrypt'ed from Python, 2023-10-23 14:44:23
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__, __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__, __mergefields__, __mergekwargtrans__, __mod__, __mul__, __ne__, __neg__, __nest__, __or__, __pow__, __pragma__, __pyUseJsNext__, __rshift__, __setitem__, __setproperty__, __setslice__, __sort__, __specialattrib__, __sub__, __super__, __t__, __terminal__, __truediv__, __withblock__, __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, pow, 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 factorial = function (n) {
        if (n < 0) {
                var __except0__ = ValueError (n);
                __except0__.__cause__ = null;
                throw __except0__;
        }
        if (n == 0) {
                return 1;
        }
        var result = n * factorial (n - 1);
        return result;
};
 
export var main = function () {
        for (var n = 0; n < 11; n++) {
                print (n, factorial (n));
        }
        print (factorial (-(1)));
};
if (__name__ == '__main__') {
        main ();
}
 
//# sourceMappingURL=factorial2.map

16. Vykreslování na canvas (kreslicí plátno) na HTML stránce

Poměrně elegantním způsobem je vyřešeno vykreslování na canvas neboli kreslicí plochu, kterou lze v rámci HTML stránky vytvořit. V transpilovaném zdrojovém kódu je vykreslování řešeno přes další JavaScriptovou knihovnu, což nás však – programátory v Pythonu – ve skutečnosti nemusí příliš zajímat.

17. Text na kreslicím plátnu

Podívejme se nejdříve na to, jak lze zajistit vytvoření kreslicí plochy, načtení skriptu a jeho inicializaci se spuštěním:

<html>
    <head>
        <title>Canvas</title>
        <style>
            body {
              background: #dddddd;
            }
 
            #canvas {
              margin: 10px;
              padding: 10px;
              background: #ffffff;
              border: thin inset #aaaaaa;
            }
        </style>
    </head>
    <body>
        <canvas id='canvas' width='800' height='600'>Canvas not supported</canvas>
        <script type="module">import * as canvas from "./__target__/canvas1.js";
        </script>
    </body>
</html>
Poznámka: povšimněte si, že nestačí pouze značka <script src=„canvas.js“>!

Vlastní skript psaný v Pythonu, který provádí vykreslování do kreslicí plochy, může vypadat následovně. Používáme zde dva základní grafické objekty – obrys textu a výplň textu:

canvas = document.getElementById('canvas')
context = canvas.getContext('2d')
 
context.font = '60pt Arial'
context.fillStyle = 'darkblue'
context.strokeStyle = 'navyblue'
 
context.fillText('Hello Canvas', canvas.width / 2 - 210, canvas.height / 2 + 15)
context.strokeText('Hello Canvas', canvas.width / 2 - 210, canvas.height / 2 + 15)

A takto vypadá výsledek transpilace výše uvedeného programu do JavaScriptu:

// Transcrypt'ed from Python, 2023-10-20 14:52:48
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__, __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__, __mergefields__, __mergekwargtrans__, __mod__, __mul__, __ne__, __neg__, __nest__, __or__, __pow__, __pragma__, __pyUseJsNext__, __rshift__, __setitem__, __setproperty__, __setslice__, __sort__, __specialattrib__, __sub__, __super__, __t__, __terminal__, __truediv__, __withblock__, __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, pow, 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 canvas = document.getElementById ('canvas');
export var context = canvas.getContext ('2d');
context.font = '60pt Arial';
context.fillStyle = 'darkblue';
context.strokeStyle = 'navyblue';
context.fillText ('Hello Canvas', canvas.width / 2 - 210, canvas.height / 2 + 15);
context.strokeText ('Hello Canvas', canvas.width / 2 - 210, canvas.height / 2 + 15);
 
//# sourceMappingURL=canvas1.map
Poznámka: opět si spusťte lokální HTTP server v adresáři s HTML stránkou. Je to většinou nutné, protože pouhé otevření lokálního souboru, který načítá JavaScriptové skripty, nemusí být v prohlížeči (dosti paradoxně!) podporováno (viz například Firefox):
$ python3 -m http.server

18. Cesta (path) na kreslicím plátnu

Podobně můžeme zkonstruovat a následně si nechat vykreslit takzvanou cestu (path). Ta může být složena z lineárních (úsečkových) segmentů, Bézierových kvadratických a kubických křivek a taktéž kruhových oblouků. Následující příklad je inspirován dokumentací Mozilly („obláček“):

canvas = document.getElementById('canvas')
context = canvas.getContext('2d')
 
context.fillStyle = 'darkblue'
context.beginPath();
context.moveTo(75, 50);
context.lineTo(100, 75);
context.lineTo(100, 25);
context.fill();
 
context.fillStyle = 'darkred'
context.beginPath();
context.moveTo(225, 50);
context.lineTo(200, 75);
context.lineTo(200, 25);
context.fill();
 
context.strokeStyle = 'darkgreen'
context.lineWidth = 10;
context.beginPath();
context.moveTo(75, 125);
context.quadraticCurveTo(25, 125, 25, 162.5);
context.quadraticCurveTo(25, 200, 50, 200);
context.quadraticCurveTo(50, 220, 30, 225);
context.quadraticCurveTo(60, 220, 65, 200);
context.quadraticCurveTo(125, 200, 125, 162.5);
context.quadraticCurveTo(125, 125, 75, 125);
context.stroke();

Opět si, dnes již naposledy, ukažme, jak byl předchozí příklad transpilován do JavaScriptu:

// Transcrypt'ed from Python, 2023-10-20 14:52:22
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__, __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__, __mergefields__, __mergekwargtrans__, __mod__, __mul__, __ne__, __neg__, __nest__, __or__, __pow__, __pragma__, __pyUseJsNext__, __rshift__, __setitem__, __setproperty__, __setslice__, __sort__, __specialattrib__, __sub__, __super__, __t__, __terminal__, __truediv__, __withblock__, __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, pow, 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 canvas = document.getElementById ('canvas');
export var context = canvas.getContext ('2d');
 
context.fillStyle = 'darkblue';
context.beginPath ();
context.moveTo (75, 50);
context.lineTo (100, 75);
context.lineTo (100, 25);
context.fill ();
context.fillStyle = 'darkred';
context.beginPath ();
context.moveTo (225, 50);
context.lineTo (200, 75);
context.lineTo (200, 25);
context.fill ();
context.strokeStyle = 'darkgreen';
context.lineWidth = 10;
context.beginPath ();
context.moveTo (75, 125);
context.quadraticCurveTo (25, 125, 25, 162.5);
context.quadraticCurveTo (25, 200, 50, 200);
context.quadraticCurveTo (50, 220, 30, 225);
context.quadraticCurveTo (60, 220, 65, 200);
context.quadraticCurveTo (125, 200, 125, 162.5);
context.quadraticCurveTo (125, 125, 75, 125);
context.stroke ();
 
//# sourceMappingURL=canvas2.map

19. Repositář s demonstračními příklady

Všechny Pythonovské skripty určené pro transpilaci do JavaScriptu, které jsme si v dnešním článku ukázali, naleznete společně s transpilovanými výsledky na adrese https://github.com/tisnik/most-popular-python-libs. Následují odkazy na jednotlivé příklady (pro jejich transpřeklad je nutné mít nainstalovánu některou z podporovaných verzí Pythonu 3, a pro dnešní příklady i výše popsaný nástroj transcrypt):

# Příklad Stručný popis Adresa
1 factorial.py výpočet faktoriálu realizovaný v Pythonu https://github.com/tisnik/most-popular-python-libs/blob/master/transcryp­t/factorial.py
2 factorial.js výsledek transpřekladu příkladu factorial.py do JavaScriptu https://github.com/tisnik/most-popular-python-libs/blob/master/transcryp­t/factorial.js
3 lists.py základní práce se seznamy v Pythonu https://github.com/tisnik/most-popular-python-libs/blob/master/transcrypt/lists.py
4 lists.js výsledek transpřekladu příkladu lists.py do JavaScriptu https://github.com/tisnik/most-popular-python-libs/blob/master/transcrypt/lists.js
5 maps.py základní práce s mapami (slovníky) v Pythonu https://github.com/tisnik/most-popular-python-libs/blob/master/transcrypt/maps.py
6 maps.js výsledek transpřekladu příkladu maps.py do JavaScriptu https://github.com/tisnik/most-popular-python-libs/blob/master/transcrypt/maps.js
7 adder.py jednoduchá funkce pro součet dvou hodnot https://github.com/tisnik/most-popular-python-libs/blob/master/transcrypt/adder.py
8 adder.js výsledek transpřekladu příkladu adder.py do JavaScriptu https://github.com/tisnik/most-popular-python-libs/blob/master/transcrypt/adder.js
9 counter_closure.py čítač realizovaný s využitím uzávěru v Pythonu https://github.com/tisnik/most-popular-python-libs/blob/master/transcryp­t/counter_closure.py
10 counter_closure.js výsledek transpřekladu příkladu counter_closure.py do JavaScriptu https://github.com/tisnik/most-popular-python-libs/blob/master/transcryp­t/counter_closure.js
11 hello.py demonstrační příklad dodávaný přímo s Transcryptem https://github.com/tisnik/most-popular-python-libs/blob/master/transcrypt/hello.py
12 hello.js výsledek transpřekladu příkladu hello.py do JavaScriptu https://github.com/tisnik/most-popular-python-libs/blob/master/transcrypt/hello.js
       
13 canvas1.html HTML stránka s kreslicí plochou a načtením skriptu https://github.com/tisnik/most-popular-python-libs/blob/master/transcryp­t/canvas1.html
14 canvas1.py vykreslení textu na kreslicí plochu naprogramované v Pythonu https://github.com/tisnik/most-popular-python-libs/blob/master/transcrypt/canvas1.py
15 canvas1.js výsledek transpřekladu příkladu canvas1.py do JavaScriptu https://github.com/tisnik/most-popular-python-libs/blob/master/transcrypt/canvas1.js
       
16 canvas2.html HTML stránka s kreslicí plochou a načtením skriptu https://github.com/tisnik/most-popular-python-libs/blob/master/transcryp­t/canvas2.html
17 canvas2.py vykreslení cesty složené ze křivek na kreslicí plochu naprogramované v Pythonu https://github.com/tisnik/most-popular-python-libs/blob/master/transcrypt/canvas2.py
18 canvas2.js výsledek transpřekladu příkladu canvas2.py do JavaScriptu https://github.com/tisnik/most-popular-python-libs/blob/master/transcrypt/canvas2.js

20. Odkazy na Internetu

  1. Stránky projektu Transcrypt
    https://www.transcrypt.org/
  2. Balíček Transcrypt na PyPi
    https://pypi.org/project/Transcrypt/
  3. Transcrypt na GitHubu
    https://github.com/Transcryp­tOrg/Transcrypt
  4. Transcrypt: getting started
    https://www.transcrypt.or­g/docs/html/installation_u­se.html
  5. Numscrypt: A tiny bit of NumPy for Transcrypt, using JavaScript typed arrays
    https://www.transcrypt.or­g/numscrypt/numscrypt.html
  6. Google Closure Compiler
    https://github.com/google/closure-compiler
  7. Statické typové kontroly zdrojových kódů Pythonu prováděné nástrojem Mypy
    https://www.root.cz/clanky/staticke-typove-kontroly-zdrojovych-kodu-pythonu-provadene-nastrojem-mypy/
  8. Statické typové kontroly zdrojových kódů Pythonu prováděné nástrojem Mypy (2.část)
    https://www.root.cz/clanky/staticke-typove-kontroly-zdrojovych-kodu-pythonu-provadene-nastrojem-mypy-2-cast/
  9. Statické typové kontroly zdrojových kódů Pythonu prováděné nástrojem Mypy (3)
    https://www.root.cz/clanky/staticke-typove-kontroly-zdrojovych-kodu-pythonu-provadene-nastrojem-mypy-3/
  10. PyScript
    https://pyscript.net/
  11. PyScript na GitHubu
    https://github.com/pyscript/pyscript
  12. Getting started with PyScript
    https://github.com/pyscrip­t/pyscript/blob/main/docs/tu­torials/getting-started.md
  13. PyScript examples
    https://github.com/pyscrip­t/pyscript/tree/main/exam­ples
  14. What is PyScript
    https://docs.pyscript.net/la­test/concepts/what-is-pyscript.html
  15. Pyodide
    https://pyodide.org/en/stable/
  16. PyScript: JavaScript and Python Interoperability
    https://www.jhanley.com/blog/pyscript-javascript-and-python-interoperability/
  17. Pyscript: JavaScript Event Callbacks
    https://www.jhanley.com/blog/pyscript-javascript-callbacks/
  18. Compiling to WebAssembly: It’s Happening!
    https://hacks.mozilla.org/2015/12/com­piling-to-webassembly-its-happening/
  19. WebAssembly
    https://webassembly.org/
  20. Blogy o WASM a Emscripten
    https://www.jamesfmackenzi­e.com/sitemap/#emscripten
  21. wat2wasm demo
    https://webassembly.github­.io/wabt/demo/wat2wasm/
  22. WABT: The WebAssembly Binary Toolkit
    https://github.com/WebAssembly/wabt
  23. Programming using Web Assembly
    https://medium.com/@alexc73/pro­gramming-using-web-assembly-c4c73a4e09a9
  24. Experiments with image manipulation in WASM using Go
    https://agniva.me/wasm/2018/06/18/shim­mer-wasm.html
  25. Fable
    https://fable.io/
  26. Využití WebAssembly z programovacího jazyka Go
    https://www.root.cz/clanky/vyuziti-webassembly-z-programovaciho-jazyka-go/
  27. WebAssembly prošlo standardizací ve W3C, byla vydána verze 1.0
    https://www.root.cz/zpravic­ky/webassembly-proslo-standardizaci-ve-w3c-byla-vydana-verze-1–0/
  28. WebAssembly na Wiki Golangu
    https://github.com/golang/go/wi­ki/WebAssembly
  29. The future of WebAssembly – A look at upcoming features and proposals
    https://blog.scottlogic.com/2018/07/20/wasm-future.html
  30. Writing WebAssembly By Hand
    https://blog.scottlogic.com/2018/04/26/we­bassembly-by-hand.html
  31. WebAssembly Specification
    https://webassembly.github­.io/spec/core/index.html
  32. Index of Instructions
    https://webassembly.github­.io/spec/core/appendix/in­dex-instructions.html
  33. The WebAssembly Binary Toolkit
    https://github.com/WebAssembly/wabt
  34. Will WebAssembly replace JavaScript? Or Will WASM Make JavaScript More Valuable in Future?
    https://dev.to/vaibhavshah/will-webassembly-replace-javascript-or-will-wasm-make-javascript-more-valuable-in-future-5c6e
  35. Roadmap (pro WebAssemly)
    https://webassembly.org/roadmap/
  36. S-expression
    https://en.wikipedia.org/wiki/S-expression
  37. Understanding WebAssembly text format
    https://developer.mozilla.org/en-US/docs/WebAssembly/Under­standing_the_text_format
  38. Learning Golang through WebAssembly – Part 1, Introduction and setup
    https://www.aaron-powell.com/posts/2019–02–04-golang-wasm-1-introduction/
  39. Learning Golang through WebAssembly – Part 2, Writing your first piece of Go
    https://www.aaron-powell.com/posts/2019–02–05-golang-wasm-2-writing-go/
  40. Learning Golang through WebAssembly – Part 3, Interacting with JavaScript from Go
    https://www.aaron-powell.com/posts/2019–02–06-golang-wasm-3-interacting-with-js-from-go/
  41. Golang webassembly (wasm) testing with examples
    https://jelinden.fi/blog/golang-webassembly-wasm-testing-with-examples/qB7Tb2KmR
  42. Use Cases (of WebAssembly)
    https://webassembly.org/docs/use-cases/
  43. JupyterLite na PyPi
    https://pypi.org/project/jupyterlite/
  44. JupyterLite na GitHubu
    https://github.com/jupyter­lite/jupyterlite
  45. Dokumentace k projektu JupyterLite
    https://github.com/jupyter­lite/jupyterlite
  46. A quick guide about Python implementations
    https://blog.rmotr.com/a-quick-guide-about-python-implementations-aa224109f321
  47. How Brython works
    https://github.com/brython-dev/brython/wiki/How%20Brython%20works
  48. Brython – A Python 3 implementation for client-side web programming
    http://www.brython.info/
  49. Brython videos and talks
    https://github.com/brython-dev/brython/wiki/Brython-videos-and-talks
  50. What is Brython?
    https://medium.com/frontendweb/what-is-brython-6edb424b07f6
  51. Python in browser (tabulka s porovnáními)
    http://stromberg.dnsalias­.org/~strombrg/pybrowser/pyt­hon-browser.html
  52. JavaScript is Assembly Language for the Web: Sematic Markup is Dead! Clean vs. Machine-coded HTML
    http://www.hanselman.com/blog/Ja­vaScriptIsAssemblyLanguage­ForTheWebSematicMarkupIsDe­adCleanVsMachinecodedHTML­.aspx
  53. pyscript VS brython
    https://www.libhunt.com/compare-pyscript-vs-brython
  54. PyScript – Run Python in the Browser! THE END of JavaScript???
    https://www.youtube.com/wat­ch?v=du8vQC44PC4
  55. PyScript is Python in Your Browser
    https://www.youtube.com/wat­ch?v=MJvCeKwr4z4
  56. JupyterLite na PyPi
    https://pypi.org/project/jupyterlite/
  57. JupyterLite na GitHubu
    https://github.com/jupyter­lite/jupyterlite
  58. Dokumentace k projektu JupyterLite
    https://github.com/jupyter­lite/jupyterlite
  59. Matplotlib Home Page
    http://matplotlib.org/
  60. Matplotlib (Wikipedia)
    https://en.wikipedia.org/wi­ki/Matplotlib
  61. Popis barvových map modulu matplotlib.cm
    https://gist.github.com/en­dolith/2719900#id7
  62. Ukázky (palety) barvových map modulu matplotlib.cm
    http://matplotlib.org/exam­ples/color/colormaps_refe­rence.html
  63. Galerie grafů vytvořených v Matplotlibu
    https://matplotlib.org/3.2.1/gallery/
  64. Replacing Javascript with Python
    https://stackoverflow.com/qu­estions/69510962/replacing-javascript-with-python
  65. Can Python Replace Javascript in the Future?
    https://dev.to/katholder/can-python-replace-javascript-in-the-future-4bbn
  66. asm.js
    http://asmjs.org/
  67. asm.js: Working Draft
    http://asmjs.org/spec/latest/
  68. Manual asm.js Demonstration
    https://www.youtube.com/wat­ch?v=qkiqMuf5M84
  69. asm.js – frequently asked questions
    http://asmjs.org/faq.html
  70. When asm.js is faster than normal JS code, why should I write new code in JS?
    https://stackoverflow.com/qu­estions/16527195/when-asm-js-is-faster-than-normal-js-code-why-should-i-write-new-code-in-js
  71. Faster Canvas Pixel Manipulation with Typed Arrays
    https://hacks.mozilla.org/2011/12/fas­ter-canvas-pixel-manipulation-with-typed-arrays/
  72. Anatomy of source maps
    https://www.bugsnag.com/blog/source-maps/
  73. Source Map Revision 3 Proposal
    https://sourcemaps.info/spec.html

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.