Commit 0f33c122 authored by jpic ∞'s avatar jpic ∞ 💾
Browse files

Initial commit

parents
Pipeline #436 failed with stages
in 1 minute and 35 seconds
.eggs/
.setupmeta.version
py2js.egg-info/
__pycache__
setup.py
py2js: Transpile Python objects into JavaScript
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Basics
======
It's pretty straightforward really:
.. code-block:: python
from py2js import transpile
def log_change():
for el in document.querySelector('select.foo'):
el.addEventListener('change', console.log)
js_code = transpile(log_change)
However, I will call this "jQuery style" JavaScript, and the problem is that
for exemple when a new element is dynamically added to the DOM then you would
have to run this again and also make it idempotent.
Another way is possible, where you don't have to chase your elements to bind
stuff on them: `window.customElements.define
<https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/define>`_,
also known as "Web Components".
Web Components
==============
There is also a feature to develop web components:
.. 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.
<html>
<head>
</head>
<body>
<script type="text/javascript" src="py2js/static/py2js.js"></script>
<script type="text/javascript" src="tests/fixtures/test_web_component.js"></script>
<script type="text/javascript">
window.customElements.define('your-tag', YourTag);
</script>
<your-tag>
Click me!
</your-tag>
</body>
</html>
from .transpiler import transpile
class HTMLElement:
pass
""" A formater module that keeps trac of indentation
"""
class Formater(object):
"""
A very simple code formater that handles efficient concatenation and indentation of lines.
"""
def __init__(self, indent_string=" "):
self.__buffer = []
self.__indentation = 0
self.__indent_string = indent_string
self.__indent_temp = ""
self.__string_buffer = ""
def dedent(self):
"""
Subtracts one indentation level.
"""
self.__indentation -= 1
self.__indent_temp = self.__indent_string*self.__indentation
def indent(self):
"""
Adds one indentation level.
"""
self.__indentation += 1
self.__indent_temp = self.__indent_string*self.__indentation
def write(self, text, indent=True, newline=True):
"""
Writes the string text to the buffer with indentation and a newline if not specified otherwise.
"""
if indent:
self.__buffer.append(self.__indent_temp)
self.__buffer.append(text)
if newline:
self.__buffer.append("\n")
def read(self, size=None):
"""
Returns a string representation of the buffer.
"""
if size == None:
text = self.__string_buffer + "".join(self.__buffer)
self.__buffer = []
self.__string_buffer = ""
return text
else:
if len(self.__string_buffer) < size:
self.__string_buffer += "".join(self.__buffer)
self.__buffer = []
if len(self.__string_buffer) < size:
text, self.__string_buffer = self.__string_buffer, ""
return text
else:
text, self.__string_buffer = self.__string_buffer[:size], \
self.__string_buffer[size:]
return text
else:
text, self.__string_buffer = self.__string_buffer[:size], \
self.__string_buffer[size:]
return text
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))
This diff is collapsed.
This diff is collapsed.
from setuptools import setup
setup(
name='py2js',
versioning='dev',
setup_requires='setupmeta',
author='Thomas Mignot, James Pic',
author_email='jamespic@gmail.com',
url='https://yourlabs.io/oss/py2js',
include_package_data=True,
license='MIT',
keywords='compiler javascript',
python_requires='>=3.8',
)
function TestClass() {
if( this === _global_this){
t = new TestClass();
t.__init__.apply(t,arguments);
return t;
}
}
TestClass.__name__ = 'TestClass';
TestClass.__setattr__ = object.prototype.__setattr__;
TestClass.__getattr__ = object.prototype.__setattr__;
TestClass.__call__ = object.prototype.__call__;
TestClass.prototype.__class__ = TestClass;
TestClass.prototype.toString = _iter.prototype.toString;
TestClass.prototype.__init__ = function(x) {
this.__setattr__("x", x);
}
TestClass.__init__ = function() {
TestClass.prototype.__init__.apply(arguments[0],Array.slice(arguments,1));
}
TestClass.prototype.plus = function(y) {
this.x += y;
}
TestClass.plus = function() {
TestClass.prototype.plus.apply(arguments[0],Array.slice(arguments,1));
}
extend(TestClass, [object]);
function YourTag() {
if( this === _global_this){
t = new YourTag();
t.__init__.apply(t,arguments);
return t;
}
}
YourTag.__name__ = 'YourTag';
YourTag.__setattr__ = object.prototype.__setattr__;
YourTag.__getattr__ = object.prototype.__setattr__;
YourTag.__call__ = object.prototype.__call__;
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]);
import difflib
import os
import pytest
from py2js import transpile, HTMLElement
def assert_equals_fixture(name, result):
path = os.path.join(
os.path.dirname(__file__),
'fixtures',
f'{name}.js',
)
if not os.path.exists(path):
with open(path, 'w') as f:
f.write(result)
pytest.fail('Fixture created')
with open(path, 'r') as f:
expected = f.read()
diff = [*difflib.unified_diff(expected, result)]
assert not diff, diff
def test_basic_class():
class TestClass:
def __init__(self, x):
self.x = x
def plus(self, y):
self.x += y
assert_equals_fixture('test_basic_class', transpile(TestClass))
def test_web_component():
class YourTag(HTMLElement):
def __init__(self):
super()
self.addEventListener('click', self.click.bind(self))
def click(self, ev):
console.log(ev)
assert_equals_fixture('test_web_component', transpile(YourTag))
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