Commit 84fc5560 authored by jpic ∞'s avatar jpic ∞ 💾
Browse files

Completed WebComponent transpile

parent 0f33c122
...@@ -28,36 +28,4 @@ also known as "Web Components". ...@@ -28,36 +28,4 @@ also known as "Web Components".
Web Components Web Components
============== ==============
There is also a feature to develop web components: WebComponent support is in development, (currently defunkt) demo in index.html
.. code-block:: python
from py2js import webcomponent
class YourTag:
def __init__(self):
super()
self.addEventListener('change', self.change.bind(self))
def change(ev):
console.log(ev)
js_code = webcomponent(YourTag)
Will generate such as script:
.. code-block:: js
class YourTag extends HTMLElement {
constructor() {
super()
this.addEventListener('change', this.change.bind(this))
}
change(ev) {
console.log(ev)
}
}
window.customElements.define('your-tag', YourTag);
In which case, an instance will be created and attached every time the browser
finds a ``<your-tag>`` HTML tag.
from .transpiler import transpile from .transpiler import transpile
class HTMLElement:
pass
from .transpiler import transpile
import inspect
class JS(object):
"""
Decorator that you can use to convert methods to JavaScript.
For example this code::
@JavaScript
class TestClass(object):
def __init__(self):
alert('TestClass created')
self.reset()
def reset(self):
self.value = 0
def inc(self):
alert(self.value)
self.value += 1
print str(TestClass)
prints::
function TestClass() {
return new _TestClass();
}
function _TestClass() {
this.__init__();
}
_TestClass.__name__ = 'TestClass'
_TestClass.prototype.__class__ = _TestClass
_TestClass.prototype.__init__ = function() {
alert("TestClass created");
this.reset();
}
_TestClass.prototype.reset = function() {
this.value = 0;
}
_TestClass.prototype.inc = function() {
alert(this.value);
this.value += 1;
}
Alternatively, an equivalent way is to use JavaScript() as a function:
class TestClass(object):
def __init__(self):
alert('TestClass created')
self.reset()
def reset(self):
self.value = 0
def inc(self):
alert(self.value)
self.value += 1
print str(JavaScript(TestClass))
If you want to call the original function/class as Python, use the
following syntax::
js = JavaScript(TestClass)
test_class = js() # Python instance of TestClass() will be created
js_class = str(js) # A string with the JS code
"""
def __init__(self, obj, context=None):
self._obj = obj
obj_source = inspect.getsource(obj)
self._js = convert_py2js(obj_source, context)
def __str__(self):
return self._js
def __call__(self, *args, **kwargs):
return str(self._obj(*args, **kwargs))
...@@ -1679,6 +1679,7 @@ var extend = function(cls, base_list) { ...@@ -1679,6 +1679,7 @@ var extend = function(cls, base_list) {
cls.prototype.__inherited__ = {}; cls.prototype.__inherited__ = {};
for (var i = 1; i < _mro.length; i++){ for (var i = 1; i < _mro.length; i++){
base = _mro[i]; base = _mro[i];
for(var property in base.prototype){ for(var property in base.prototype){
if(!(property in cls.prototype) && !(property in base.prototype.__inherited__)){ if(!(property in cls.prototype) && !(property in base.prototype.__inherited__)){
cls.prototype[property] = base.prototype[property]; cls.prototype[property] = base.prototype[property];
......
#! /usr/bin/env python
import ast import ast
import inspect import inspect
import textwrap import textwrap
...@@ -9,9 +7,11 @@ def scope(func): ...@@ -9,9 +7,11 @@ def scope(func):
func.scope = True func.scope = True
return func return func
class JSError(Exception): class JSError(Exception):
pass pass
class Transpiler: class Transpiler:
name_map = { name_map = {
...@@ -669,19 +669,23 @@ class Transpiler: ...@@ -669,19 +669,23 @@ class Transpiler:
def visit_Index(self, node): def visit_Index(self, node):
return self.visit(node.value) return self.visit(node.value)
def transpile(source, context=None): def convert_py2js(s, context=None):
""" """
Convert a Python object, or a string of Python code, into JS. Takes Python code as a string 's' and converts this to JavaScript.
Example: Example:
>>> transpile("x[3:]") >>> convert_py2js("x[3:]")
'x.__getitem__(slice(3, null));'
>>> transpile(lambda x: x + 3)
'x.__getitem__(slice(3, null));' 'x.__getitem__(slice(3, null));'
""" """
t = ast.parse(textwrap.dedent(s))
v = JS(context)
v.visit(t)
return v.read()
def transpile(source, context=None):
if not isinstance(source, str): if not isinstance(source, str):
source = inspect.getsource(source) source = inspect.getsource(source)
tree = ast.parse(textwrap.dedent(source)) tree = ast.parse(textwrap.dedent(source))
......
function YourTag() { class YourTag extends HTMLElement {
if( this === _global_this){ constructor() {
t = new YourTag(); super()
t.__init__.apply(t,arguments); this.addEventListener(str('click'),this.click.bind(this));
return t;
} }
} click(event) {
YourTag.__name__ = 'YourTag'; console.log(event);
YourTag.__setattr__ = object.prototype.__setattr__; }
YourTag.__getattr__ = object.prototype.__setattr__; }
YourTag.__call__ = object.prototype.__call__; \ No newline at end of file
YourTag.prototype.__class__ = YourTag;
YourTag.prototype.toString = _iter.prototype.toString;
YourTag.prototype.__init__ = function() {
_super();
this.addEventListener(str('change'),this.change.bind(this));
}
YourTag.__init__ = function() {
YourTag.prototype.__init__.apply(arguments[0],Array.slice(arguments,1));
}
YourTag.prototype.change = function(ev) {
console.log(ev);
}
YourTag.change = function() {
YourTag.prototype.change.apply(arguments[0],Array.slice(arguments,1));
}
extend(YourTag, [HTMLElement]);
...@@ -2,7 +2,7 @@ import difflib ...@@ -2,7 +2,7 @@ import difflib
import os import os
import pytest import pytest
from py2js import transpile, HTMLElement from py2js import transpile
def assert_equals_fixture(name, result): def assert_equals_fixture(name, result):
...@@ -21,7 +21,7 @@ def assert_equals_fixture(name, result): ...@@ -21,7 +21,7 @@ def assert_equals_fixture(name, result):
expected = f.read() expected = f.read()
diff = [*difflib.unified_diff(expected, result)] diff = [*difflib.unified_diff(expected, result)]
assert not diff, diff assert not diff, '\n'.join(diff)
def test_basic_class(): def test_basic_class():
...@@ -34,13 +34,43 @@ def test_basic_class(): ...@@ -34,13 +34,43 @@ def test_basic_class():
assert_equals_fixture('test_basic_class', transpile(TestClass)) assert_equals_fixture('test_basic_class', transpile(TestClass))
def test_web_component(): def test_transpile_text():
class YourTag(HTMLElement): source = '''
def __init__(self): class TestClass:
super() def __init__(self, x):
self.addEventListener('click', self.click.bind(self)) self.x = x
def plus(self, y):
self.x += y
'''
assert_equals_fixture('test_basic_class', transpile(source))
class HTMLElement:
def render_js(self):
js = ['class YourTag extends HTMLElement {']
for method in ('constructor', 'click'):
source = transpile(getattr(self, method))
source = source[4:-3] # remove var, and });
source = source.replace(' = $def({}, function', '')
source = source.split('\n')
if method == 'constructor':
source.insert(1, ' super()')
source = '\n'.join([
' ' + line
for line in source
]) # indentation
js.append(source)
js.append('}')
return '\n'.join(js)
def click(self, ev):
console.log(ev)
assert_equals_fixture('test_web_component', transpile(YourTag))
class YourTag(HTMLElement):
def constructor():
this.addEventListener('click', this.click.bind(this))
def click(event):
console.log(event)
def test_web_component():
assert_equals_fixture('test_web_component', YourTag().render_js())
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment