Skip to content

Instantly share code, notes, and snippets.

@jviide
Last active October 19, 2019 13:17
Show Gist options
  • Save jviide/7f2746e02897505ad46b00e7c28af9e0 to your computer and use it in GitHub Desktop.
Save jviide/7f2746e02897505ad46b00e7c28af9e0 to your computer and use it in GitHub Desktop.
Proof-of-concept: Universal htm.py with the Python-to-JS compiler Transcrypt

Proof-of-concept: Universal htm.py with the Python-to-JS compiler Transcrypt

This is a Rube Goldberg machine that aims to demonstrate how htm.py could be used with the Python-to-JS compiler Transcrypt. This idea could, maybe, be combined with this gist.

It contains the following moving parts:

  • myhtm.py is a wrapper for htm.py that transparently switches to developit/htm when it's compiled with Transcrypt.

  • compile.py precompiles Python 3 code from STDIN to one that doesn't depend on tagged's stack inspection shenanigans (which htm.py depends on). This should make the code more compatible with Transcrypt.

  • main.py contains some example code that uses myhtm. It's our app's entrypoint.

  • npm run build runs compile.py against main.pyand then transpiles the result to JavaScript with Transcrypt.

  • npm start runs the transpiled code.

Requirements:

  • Python 3.6 or so, assumed to be executable with the command python3.

  • A recent version of Transcrypt

  • A recent version of Node.js

Start by installing the JavaScript dependencies:

$ npm install

Then transpile main.py to JavaScript:

$ npm run build

Then run the transpiled JavaScript program:

$ npm start
import ast
import astor
import pathlib
from tagged import split
class Rewrite(ast.NodeTransformer):
def visit_Call(self, node):
self.generic_visit(node)
if node.func.__class__.__name__ != "Name" or node.func.id != "html":
return node
if len(node.args) != 1 or node.args[0].__class__.__name__ != "Str":
return node
strings, exprs = split(node.args[0].s)
strings_node = ast.Tuple(elts=[ast.Str(s=s)
for s in strings], ctx=ast.Load())
exprs_nodes = [ast.parse(expr, mode="eval") for expr in exprs]
new_node = ast.Call(func=node.func, args=[
strings_node, *exprs_nodes], keywords=[])
return ast.copy_location(new_node, node)
def compile(source):
root = ast.parse(source)
root = Rewrite().visit(root)
return astor.to_source(root)
if __name__ == "__main__":
import sys
sys.stdout.write(compile(sys.stdin.read()))
from myhtm import htm
@htm
def html(tag, props, children):
return (tag, props, children)
name = "World"
print(html("""
<div class="main">
Hello, <b>{name}!</b>
</div>
"""))
# __pragma__ ('skip')
from htm import htm
# __pragma__ ('noskip')
# __pragma__ ('ecom')
'''?
__pragma__('js', 'var module = {{}}; {}; var htmjs = module.exports;', __include__("./node_modules/htm/dist/htm.js"))
def htm(h):
def wrapped_h(type, props, *children):
return h(type, props, children)
def call_htmjs(strings, *values):
return htmjs.call(wrapped_h, strings, *values)
return call_htmjs
?'''
# __pragma__ ('noecom')
{
"scripts": {
"build": "python3 compile.py < main.py > main_compiled.py && python3 -m transcrypt -b -p .none -n main_compiled.py",
"start": "node -r esm __target__/main_compiled.js"
},
"dependencies": {
"esm": "^3.2.25",
"htm": "^2.2.1"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment