def test_calling_method_from_init(self): # Note that all class names inside a module need to be unique # for js() to find the correct source. class MyClass11: def __init__(self): self._res = self.m1() + self.m2() + self.m3() def m1(self): return 100 def m2(self): return 10 class MyClass12(MyClass11): def m2(self): return 20 def m3(self): return 2 code = py2js(MyClass11) + py2js(MyClass12) assert evaljs(code + 'var m = new MyClass12(); m._res') == '122' assert evaljs(code + 'var m = new MyClass12(); m.m1()') == '100'
def test_overload_truthy(self): def foo(): PSCRIPT_OVERLOAD = False for v in [true, 0, "a", "", [], {}]: if v: print('1') else: print('0') return None or False js = py2js(foo) assert "PSCRIPT_OVERLOAD" not in js assert "pyfunc" not in js ans = '1', '0', '1', '0', '1', '1', 'false' assert evaljs(js + '\nfoo();') == '\n'.join(ans) def bar(): PSCRIPT_OVERLOAD = False for v in [true, 0, "a", "", [], {}]: if v: print('1' + bool(v)) else: print('0' + bool(v)) return None or False js = py2js(bar) assert "PSCRIPT_OVERLOAD" not in js # assert "pyfunc" not in js # bool() does pyfunc_truthy() ans = '1true', '0false', '1true', '0false', '1false', '1false', 'false' assert evaljs(js + '\nbar();') == '\n'.join(ans)
def test_comparisons(self): assert py2js('4 > 3') == '4 > 3;' assert py2js('4 is 3') == '4 === 3;' assert evalpy('4 > 4') == 'false' assert evalpy('4 >= 4') == 'true' assert evalpy('4 < 3') == 'false' assert evalpy('4 <= 4') == 'true' assert evalpy('4 == 3') == 'false' assert evalpy('4 != 3') == 'true' assert evalpy('4 == "4"') == 'true' # yuck! assert evalpy('4 is "4"') == 'false' assert evalpy('4 is not "4"') == 'true' assert evalpy('"c" in "abcd"') == 'true' assert evalpy('"x" in "abcd"') == 'false' assert evalpy('"x" not in "abcd"') == 'true' assert evalpy('3 in [1,2,3,4]') == 'true' assert evalpy('9 in [1,2,3,4]') == 'false' assert evalpy('9 not in [1,2,3,4]') == 'true' assert evalpy('"bar" in {"foo": 3}') == 'false' assert evalpy('"foo" in {"foo": 3}') == 'true' # was a bug assert evalpy('not (1 is null and 1 is null)') == 'true'
def test_overload_scope(self): # Can turn on and off def foo(): print({} or 'x') PSCRIPT_OVERLOAD = False print({} or 'x') PSCRIPT_OVERLOAD = True print({} or 'x') # js = py2js(foo) assert evaljs(js + '\nfoo();').replace('\n', ' ') == 'x {} x null' # Scoped per function def foo(): def x1(): print({} or 'x') def x2(): PSCRIPT_OVERLOAD = False print({} or 'x') def x3(): print({} or 'x') print({} or 'x') x1() x2() x3() print({} or 'x') # js = py2js(foo) assert evaljs(js + '\nfoo();').replace('\n', ' ') == 'x x {} x x null' # Scope is maintained def foo(): PSCRIPT_OVERLOAD = False def x1(): print({} or 'x') def x2(): PSCRIPT_OVERLOAD = False print({} or 'x') def x3(): print({} or 'x') print({} or 'x') x1() x2() x3() print({} or 'x') # js = py2js(foo) assert evaljs(js + '\nfoo();').replace('\n', ' ') == '{} x {} x {} null'
def test_bound_methods(self): class MyClass14: def __init__(self): self.a = 1 def add2(self): self.a += 2 class MyClass15(MyClass14): def add3(self): self.a += 3 code = py2js(MyClass14) + py2js(MyClass15) assert evaljs( code + 'var m = new MyClass14(); m.add2(); m.add2(); m.a') == '5' assert evaljs( code + 'var m = new MyClass14(); var f = m.add2; f(); f(); m.a') == '5' assert evaljs( code + 'var m = new MyClass15(); var f = m.add3; f(); f(); m.a') == '7' assert evaljs( code + 'var m = new MyClass15(); var f2 = m.add2, f3 = m.add3; f2(); f3(); m.a' ) == '6'
def test_py2s_rename_function(): code = py2js(foo1, 'bar') assert 'foo' not in code.lower() assert evaljs(code + 'bar()') == '42' code = py2js(foo1, 'xx.bar') assert 'foo' not in code.lower() assert evaljs('var xx={};\n' + code + 'xx.bar();') == '42'
def test_import(self): with raises(JSError): py2js('import time') # But we do support special time funcs import time assert abs(float(evalpy('time()')) - time.time()) < 0.5 evalpy('t0=perf_counter(); t1=perf_counter(); (t1-t0)').startswith('0.0')
def test_raw_js_overloading(self): # more RawJS tests in test_parser3.py s1 = 'a=3; b=4; c=1; a + b - c' s2 = 'a=3; b=4; c=1; RawJS("a + b") - c' assert evalpy(s1) == '6' assert evalpy(s2) == '6' assert 'pyfunc' in py2js(s1) assert 'pyfunc' not in py2js(s2)
def test_inheritance_and_super(self): class MyClass1: def __init__(self): self.bar = 7 def add(self, x=1): self.bar += x def addTwo(self): self.bar += 2 class MyClass2(MyClass1): def addTwo(self): super().addTwo() self.bar += 1 # haha, we add three! class MyClass3(MyClass2): def addTwo(self): super().addTwo() self.bar += 1 # haha, we add four! def addFour(self): super(MyClass3, self).add(4) # Use legacy Python syntax code = py2js(MyClass1) + py2js(MyClass2) + py2js(MyClass3) code += 'var m1=new MyClass1(), m2=new MyClass2(), m3=new MyClass3();' # m1 assert evaljs(code + 'm1.bar;') == '7' assert evaljs(code + 'm1.add();m1.bar;') == '8' assert evaljs(code + 'm1.addTwo();m1.bar;') == '9' # m2 assert evaljs(code + 'm2.bar;') == '7' assert evaljs(code + 'm2.add();m2.bar;') == '8' assert evaljs(code + 'm2.addTwo();m2.bar;') == '10' # m3 assert evaljs(code + 'm3.bar;') == '7' assert evaljs(code + 'm3.add();m3.bar;') == '8' assert evaljs(code + 'm3.addTwo();m3.bar;') == '11' assert evaljs(code + 'm3.addFour();m3.bar;') == '11' # super with args # Inhertance m1 assert evaljs(code + 'm1 instanceof MyClass3;') == 'false' assert evaljs(code + 'm1 instanceof MyClass2;') == 'false' assert evaljs(code + 'm1 instanceof MyClass1;') == 'true' assert evaljs(code + 'm1 instanceof Object;') == 'true' # Inhertance m2 assert evaljs(code + 'm2 instanceof MyClass3;') == 'false' assert evaljs(code + 'm2 instanceof MyClass2;') == 'true' assert evaljs(code + 'm2 instanceof MyClass1;') == 'true' assert evaljs(code + 'm2 instanceof Object;') == 'true' # Inhertance m3 assert evaljs(code + 'm3 instanceof MyClass3;') == 'true' assert evaljs(code + 'm3 instanceof MyClass2;') == 'true' assert evaljs(code + 'm3 instanceof MyClass1;') == 'true' assert evaljs(code + 'm3 instanceof Object;') == 'true'
def test_py2js_rename_class(): code = py2js(Foo2, 'Bar') assert 'foo' not in code.lower() assert evaljs(code + 'var m=new Bar(); [m.res1(), m.res2()];') == '[ 42, 7 ]' code = py2js(Foo2, 'xx.Bar') assert 'foo' not in code.lower() assert evaljs('var xx={};\n' + code + 'var m=new xx.Bar(); [m.res1(), m.res2()];') == '[ 42, 7 ]'
def test_assert(self): assert 'throw' in py2js('assert True') evalpy('assert true; 7') == '7' evalpy('assert true, "msg"; 7') == '7' catcher = 'try { %s } catch(err) { console.log(err); }' assert evaljs(catcher % py2js('assert false')).count('AssertionError') assert evaljs(catcher % py2js('assert false, "foo"')).count('foo')
def test_global_vs_nonlocal(self): js1 = py2js('global foo;foo = 3') js2 = py2js('nonlocal foo;foo = 3') assert js1.meta['vars_unknown'] == set() assert js2.meta['vars_unknown'] == set() assert js1.meta['vars_global'] == set(['foo']) assert js2.meta['vars_global'] == set() code = """if True:
def test_isinstance(self): # The resulting code is not particularly pretty, so we just # test outcome assert evalpy('isinstance(3.0, list) == True') == 'false' assert evalpy('isinstance(3.0, float) == True') == 'true' assert evalpy('x={}; isinstance(x.foo, "undefined")') == 'true' assert evalpy('isinstance(None, "null")') == 'true' assert evalpy('isinstance(undefined, "undefined")') == 'true' # assert evalpy('isinstance(None, "undefined")') == 'false' assert evalpy('isinstance(undefined, "null")') == 'false' assert evalpy('isinstance(3, float)') == 'true' assert evalpy('isinstance(3, (int, float))') == 'true' assert evalpy('isinstance(3, "number")') == 'true' # #assert evalpy('isinstance(3, int)') == 'false' # int is not defined assert evalpy('isinstance("", str)') == 'true' assert evalpy('isinstance("", "string")') == 'true' # assert evalpy('isinstance("", list)') == 'false' assert evalpy('isinstance(True, bool)') == 'true' assert evalpy('isinstance(True, "boolean")') == 'true' # assert evalpy('isinstance(True, float)') == 'false' assert evalpy('isinstance([], list)') == 'true' assert evalpy('isinstance([], "array")') == 'true' # assert evalpy('isinstance([], "object")') == 'false' assert evalpy('isinstance([], "Object")') == 'false' assert evalpy('isinstance([], dict)') == 'false' assert evalpy('isinstance({}, dict)') == 'true' assert evalpy('isinstance({}, "object")') == 'true' # assert evalpy('isinstance({}, list)') == 'false' assert evalpy('isinstance({}, "array")') == 'false' assert evalpy('isinstance(eval, types.FunctionType)') == 'true' assert evalpy('isinstance(eval, FunctionType)') == 'true' assert evalpy('isinstance(3, types.FunctionType)') == 'false' # own class code = 'function MyClass () {return this;}\nvar x = new MyClass();\n' assert evaljs(code + py2js('isinstance(x, "object")')) == 'true' assert evaljs(code + py2js('isinstance(x, "Object")')) == 'true' assert evaljs(code + py2js('isinstance(x, "MyClass")')) == 'true' assert evaljs(code + py2js('isinstance(x, MyClass)')) == 'true'
def test_nonlocal(self): assert py2js('nonlocal foo;foo = 3').strip() == 'foo = 3;' func3_code = """def func3(): def inner(): nonlocal x x = 3 x = 2 inner() return x """ assert evaljs(py2js(func3_code)+'func3()') == '3'
def test_py2js_on_function(): def foo(): pass # normal jscode = py2js(foo) assert jscode.startswith('var foo') assert jscode.meta['pycode'].startswith('def foo') # renamed jscode = py2js(foo, 'bar') assert jscode.meta['pycode'].startswith('def foo') assert 'foo' not in jscode assert jscode.startswith('var bar') assert 'bar = function ' in jscode # renamed 2 jscode = py2js(foo, 'bar.bla') assert jscode.meta['pycode'].startswith('def foo') assert 'foo' not in jscode assert not 'var bar.bla' in jscode assert 'bar.bla = function ' in jscode # Skip decorators stub1 = lambda x: x stub2 = lambda x=None: stub1 @stub1 @stub1 def foo1(): pass @stub2( ) def foo2(): pass @py2js def foo3(): pass @py2js(indent=1) def foo4(): pass assert callable(foo1) assert callable(foo2) assert py2js(foo1).meta['pycode'].startswith('def foo') assert py2js(foo2).meta['pycode'].startswith('def foo') assert foo3.startswith('var foo3') assert foo4.startswith(' var foo4')
def test_async_and_await(self): if sys.version_info < (3, 6): return foo = py2js('async def foo(): return 42\n\n') spam = py2js('async def spam(): print(await foo())\n\n') eggs = py2js('async def eggs(): return await foo()\n\n') js = foo + spam + eggs assert 'Promise' in evaljs(js + 'foo()') assert 'Promise' in evaljs(js + 'spam()') assert 'Promise' in evaljs(js + 'eggs()') assert '42' in evaljs(js + 'spam()') assert '42' not in evaljs(js + 'eggs()')
def test_stdlib_full_and_partial(): code = stdlib.get_full_std_lib() assert isinstance(code, str) assert 'var %shasattr =' % stdlib.FUNCTION_PREFIX in code assert 'var %slist =' % stdlib.FUNCTION_PREFIX in code assert code.count('var') > 10 code = stdlib.get_partial_std_lib(['hasattr'], [], []) assert isinstance(code, str) assert 'var %shasattr =' % stdlib.FUNCTION_PREFIX in code assert 'var %slist =' % stdlib.FUNCTION_PREFIX not in code assert code.count('var') == 1 assert '_hasattr = function' in py2js('hasattr(x, "foo")') assert '_hasattr = function' not in py2js('hasattr(x, "foo")', inline_stdlib=False)
def test_while(self): # Test code output line = nowhitespace(py2js('while(True): pass')) assert line == 'while(true){}' line = nowhitespace(py2js('while(not ok): pass')) assert 'while' in line # Test break and continue for9 = 'i=-1\nwhile(i<8):\n i+=1\n ' assert evalpy(for9 + 'if i==4:break\n print(i)\n0') == '0\n1\n2\n3\n0' assert evalpy(for9 + 'if i<6:continue\n print(i)\n0') == '6\n7\n8\n0' # Test else assert evalpy(for9 + 'if i==3:break\nelse: print(99)\n0') == '0' assert evalpy(for9 + 'if i==30:break\nelse: print(99)\n0') == '99\n0'
def test_overload_usage(self): # Can only use in a function with raises(JSError) as err_info: py2js("PSCRIPT_OVERLOAD=False\n3+4") assert 'PSCRIPT_OVERLOAD inside a function' in str(err_info.value) # Can only use with a bool def foo(): PSCRIPT_OVERLOAD = 0 return a + b with raises(JSError) as err_info: py2js(foo) assert 'PSCRIPT_OVERLOAD with a bool' in str(err_info.value)
def test_var_args2(self): def func(self, foo, *args): return args code1 = py2js(func) #lines = [line for line in code1.split('\n') if line] code2 = py2js('func(0, 2, 3)') assert evaljs(code1 + code2, False) == '[2,3]' code2 = py2js('func(0)') assert evaljs(code1 + code2, False) == '[]' code2 = py2js('a=[0,2,3]\nfunc(*a)') assert evaljs(code1 + code2, False) == '[2,3]' code2 = py2js('a=[2,3]\nfunc(0,1,2,*a)') assert evaljs(code1 + code2, False) == '[1,2,2,3]'
def test_scope(self): def func(self): def foo(z): y = 2 stub = False # noqa only_here = 1 # noqa return x + y + z x = 1 y = 0 y = 1 # noqa z = 1 # noqa res = foo(3) stub = True # noqa return res + y # should return 1+2+3+1 == 7 # Find function start code = py2js(func) i = code.splitlines().index('var func;') assert i >= 0 # Find first lines of functions, where the vars are defined vars1 = code.splitlines()[i+2] vars2 = code.splitlines()[i+4] assert vars1.strip().startswith('var ') assert vars2.strip().startswith('var ') assert 'y' in vars1 and 'y' in vars2 assert 'stub' in vars1 and 'stub' in vars2 assert 'only_here' in vars2 and 'only_here' not in vars1 assert evaljs(code + 'func()') == '7'
def test_time2str(): t1 = to_time_int("2018-04-24 13:18:00") t2 = to_time_int("2018-04-24 13:18:00Z") t3 = to_time_int("2018-04-24 13:18:00+0200") for t in (t1, t2, t2): assert isinstance(t, int) # Get outputs assert time2str(t1) == time2str(t1, None) s1 = time2str(t1, None) s2 = time2str(t2, 0) s3 = time2str(t3, 2) # Verify output in Python assert s1.startswith("2018-04-24T13:18:00") # exact output depends on timezone assert s2 == "2018-04-24T13:18:00Z" assert s3 == "2018-04-24T13:18:00+0200" if not HAS_NODE: print("skipping tests that use node") return # Verify that JS and Python produce the same results js = py2js(open(dt.__file__, "rb").read().decode(), docstrings=False) js1 = evaljs(js + f"time2str({t1})") js2 = evaljs(js + f"time2str({t2}, 0)") js3 = evaljs(js + f"time2str({t3}, 2)") assert js1 == s1.rstrip("0") assert js2 == s2.rstrip("0") assert js3 == s3.rstrip("0")
def test_bound_funcs_in_methods(self): class MyClass16: def foo1(self): self.a = 3 f = lambda i: self.a return f() def foo2(self): self.a = 3 def bar(): return self.a return bar() def foo3(self): self.a = 3 def bar(self): return self.a return bar() code = py2js(MyClass16) assert evaljs(code + 'var m = new MyClass16(); m.foo1()') == '3' assert evaljs(code + 'var m = new MyClass16(); m.foo2()') == '3' assert evaljs( code + 'var m = new MyClass16(); try {m.foo3();} catch (err) {"ok"}' ) == 'ok'
def test_self_becomes_this(self): def func(self): return self.foo code = py2js(func) lines = [line.strip() for line in code.split('\n') if line] assert 'return this.foo;' in lines
def py2js_tickformatter(formatter, msg=''): """ Uses py2js to compile a python tick formatter to JS code """ try: from pscript import py2js except ImportError: param.main.param.warning( msg+'Ensure pscript is installed ("conda install pscript" ' 'or "pip install pscript")') return try: jscode = py2js(formatter, 'formatter') except Exception as e: error = 'Pyscript raised an error: {0}'.format(e) error = error.replace('%', '%%') param.main.param.warning(msg+error) return args = _getargspec(formatter).args arg_define = 'var %s = tick;' % args[0] if args else '' return_js = 'return formatter();\n' jsfunc = '\n'.join([arg_define, jscode, return_js]) match = re.search('(formatter \= function \(.*\))', jsfunc ) return jsfunc[:match.start()] + 'formatter = function ()' + jsfunc[match.end():]
def test_func1(self): code = py2js(func1) lines = [line for line in code.split('\n') if line] assert len(lines) == 4 # only three lines + definition assert lines[1] == 'func1 = function flx_func1 () {' # no args assert lines[2].startswith(' ') # indented assert lines[3] == '};' # dedented
def test_method1(self): code = py2js(self.method1) lines = [line for line in code.split('\n') if line] assert len(lines) == 4 # only three lines + definition assert lines[1] == 'method1 = function () {' # no args, no self/this assert lines[2].startswith(' ') # indented assert lines[3] == '};' # dedented
def test_basic_types(self): assert py2js('True') == 'true;' assert py2js('False') == 'false;' assert py2js('None') == 'null;' assert py2js('"bla\\"bla"') == '"bla\\"bla";' assert py2js('3') == '3;' assert py2js('3.1415') == '3.1415;' assert py2js('[1,2,3]') == '[1, 2, 3];' assert py2js('(1,2,3)') == '[1, 2, 3];' assert py2js('{"foo": 3, "bar": 4}') == '({foo: 3, bar: 4});' assert evalpy('a={"foo": 3, "bar": 4};a') == '{ foo: 3, bar: 4 }'
def test_dotted_unknowns(): def func1(): x = ui._layouts.SomeLayout() y = ui.SomeLayout.YYY z = ui.SomeOtherLayout js = py2js(func1) assert js.meta['vars_unknown'] == set(['ui._layouts.SomeLayout', 'ui.SomeLayout.YYY', 'ui.SomeOtherLayout'])
def test_rawJS(self): code = py2js(foo) assert 'pyfunc' not in code assert ' x =' in code assert ' for' in code assert ' y +=' in code assert ' while' in code assert ' y -=' in code
def __init__(self, name, store): if not isinstance(name, str): raise TypeError('JSModule needs a str name.') if not isinstance(store, dict): raise TypeError('JSModule needs a dict store.') # Resolve name of Python module py_name = name if name.endswith('.__init__'): py_name = name.rsplit('.', 1)[0] if py_name not in sys.modules: raise ValueError("Cannot find Python module coresponding to %s." % name) # Store module and name self._pymodule = sys.modules[py_name] self._name = get_mod_name(self._pymodule) # Check if name matches the kind of module is_package = module_is_package(self._pymodule) if is_package and not name.endswith('.__init__'): raise ValueError('Modules representing the __init__ of a package ' 'should end with ".__init__".') elif not is_package and name.endswith('.__init__'): raise ValueError('Plain modules should not end with ".__init__".') # Self-register self._store = store if self.name in self._store: raise RuntimeError('Module %s already exists!' % self.name) self._store[self.name] = self # Bookkeeping content of the module self._provided_names = set() self._imported_names = set() # Stuff defined in this module (in JS) # We use dicts so that we can "overwrite" them in interactive mode self._component_classes = {} self._pscript_code = {} self._js_values = {} # Dependencies self._deps = {} # mod_name -> [mod_as_name, *imports] # Caches self._js_cache = None self._css_cache = None if is_pscript_module(self._pymodule): # PScript module; transpile as a whole js = py2js(self._pymodule, inline_stdlib=False, docstrings=False) self._pscript_code['__all__'] = js self._provided_names.update([n for n in js.meta['vars_defined'] if not n.startswith('_')]) else: self._init_default_objects()
def py2js(self, *args, **kwargs): kwargs['inline_stdlib'] = False kwargs['docstrings'] = False code = py2js(*args, **kwargs) return self.update(code)
def py2js(ctx, code): """transpile given Python code to JavaScript """ from pscript import py2js print(py2js(code))
def add_variable(self, name, is_global=False, _dep_stack=None): """ Mark the variable with the given name as used by JavaScript. The corresponding object must be a module, Component, class or function, or a json serializable value. If the object is defined here (or a json value) it will add JS to this module. Otherwise this module will import the name from another module. If ``is_global``, the name is considered global; it may be declared in this module, but it may also be a JS global. So we try to resolve the name, but do not care if it fails. """ _dep_stack = _dep_stack or [] if name in self._imported_names: return elif name in _dep_stack: return # avoid dependency recursion elif name in ('Infinity', 'NaN'): return # stubs elif name in self._provided_names and self.name != '__main__': return # in __main__ we allow redefinitions if is_pscript_module(self._pymodule): return # everything is transpiled and exported already _dep_stack.append(name) # Try getting value. We warn if there is no variable to match, but # if we do find a value we're either including it or raising an error try: val = self._pymodule nameparts = name.split('.') for i in range(len(nameparts)): val = getattr(val, nameparts[i]) # Maybe we "know" (this kind of) value ... if isinstance(val, json_types): name = '.'.join(nameparts[:i+1]) break elif isinstance(val, type) and issubclass(val, JsComponent): name = '.'.join(nameparts[:i+1]) break elif val is loop and i == 0: return self._add_dep_from_event_module('loop', nameparts[0]) elif isinstance(val, (JSConstant, Asset)): return # stubs elif isinstance(val, logging.Logger) and i == 0: # todo: hehe, we can do more here (issue #179) return self._add_dep_from_event_module('logger', nameparts[0]) except AttributeError: msg = 'JS in "%s" uses undefined variable %r.' % (self.filename, name) if is_global: pass # it may be a JS-global elif val is self._pymodule: # Did not resolve first part of the name, so cannot be a JS global logger.warning(msg) else: raise RuntimeError(msg) # E.g. typo in ui.Buttom return # Mark dirty self._changed_time = time.time() self._js_cache = self._css_cache = None if isinstance(val, types.ModuleType): # Modules as a whole can be converted if its a PScript module if is_pscript_module(val): self._import(val.__name__, None, None) self._deps[val.__name__][0] = name # set/overwrite as-name else: t = 'JS in "%s" cannot use module %s directly unless it defines %s.' raise ValueError(t % (self.filename, val.__name__, '"__pscript__"')) elif isinstance(val, type) and issubclass(val, Component): if val is Component: return self._add_dep_from_event_module('Component') elif val is BaseAppComponent or val.mro()[1] is BaseAppComponent: # BaseAppComponent, PyComponent, JsComponent or StubComponent # are covered in _component2.py return elif issubclass(val, (PyComponent, JsComponent)): # App Component class; we know that we can get the JS for this if val.__jsmodule__ == self.name: # Define here self._provided_names.add(name) self._component_classes[name] = val # Recurse self._collect_dependencies_from_bases(val) self._collect_dependencies(val.JS.CODE, _dep_stack) else: # Import from another module self._import(val.__jsmodule__, val.__name__, name) else: # Regular Component, similar to other classes, # but using create_js_component_class() mod_name = get_mod_name(val) if mod_name == self.name: # Define here js = create_js_component_class(val, val.__name__) self._provided_names.add(name) self._pscript_code[name] = js # Recurse self._collect_dependencies_from_bases(val) self._collect_dependencies(js, _dep_stack) else: # Import from another module self._import(mod_name, val.__name__, name) elif isinstance(val, type) and issubclass(val, bsdf.Extension): # A bit hacky mechanism to define BSDF extensions that also work in JS. # todo: can we make this better? See also app/_component2.py (issue #429) js = 'var %s = {name: "%s"' % (name, val.name) for mname in ('match', 'encode', 'decode'): func = getattr(val, mname + '_js') funccode = py2js(func, indent=1, inline_stdlib=False, docstrings=False) js += ',\n ' + mname + ':' + funccode.split('=', 1)[1].rstrip(' \n;') self._collect_dependencies(funccode, _dep_stack) js += '};\n' js += 'serializer.add_extension(%s);\n' % name js = JSString(js) js.meta = funccode.meta self._pscript_code[name] = js self._deps.setdefault('flexx.app._clientcore', ['flexx.app._clientcore']).append('serializer') elif isinstance(val, pscript_types) and hasattr(val, '__module__'): # Looks like something we can convert using PScript mod_name = get_mod_name(val) if mod_name == self.name: # Define here try: js = py2js(val, inline_stdlib=False, docstrings=False) except Exception as err: t = 'JS in "%s" uses %r but cannot transpile it with PScript:\n%s' raise ValueError(t % (self.filename, name, str(err))) self._provided_names.add(name) self._pscript_code[name] = js # Recurse if isinstance(val, type): self._collect_dependencies_from_bases(val) self._collect_dependencies(js, _dep_stack) elif mod_name.endswith('.event._property'): return self._add_dep_from_event_module(name.split('.')[-1], name) else: # Import from another module self._import(mod_name, val.__name__, name) elif isinstance(val, RawJS): # Verbatim JS if val.__module__ == self.name: self._provided_names.add(name) self._js_values[name] = val.get_code() else: self._import(val.__module__, val.get_defined_name(name), name) elif isinstance(val, json_types): # Looks like something we can serialize # Unlike with RawJS, we have no way to determine where it is defined try: js = json.dumps(val) except Exception as err: t = 'JS in "%s" uses %r but cannot serialize that value:\n%s' raise ValueError(t % (self.filename, name, str(err))) self._provided_names.add(name) self._js_values[name] = js elif (getattr(val, '__module__', None) and is_pscript_module(sys.modules[val.__module__]) and val is getattr(sys.modules[val.__module__], name, 'unlikely-val')): # An instance from a pscript module! # We cannot know the "name" as its known in the module, but # we assume that its the same as as_name and test whether # it matches in the test above. self._import(val.__module__, name, name) else: # Cannot convert to JS t = 'JS in "%s" uses %r but cannot convert %s to JS.' raise ValueError(t % (self.filename, name, val.__class__))