When extending JS classes, the super class' constructor isn't called + an exception is raised when calling `super()` in the subclass
denis-migdal opened this issue · comments
It seems Brython doesn't call the constructor of the super class when inheriting a JS class.
The JS class:
function BLISS() {
return class {
constructor() {
console.log("called");
}
foo() {
console.log("foo");
}
faa = 43;
set fii(a: number) {
console.log("fii");
}
get fuu() {
console.log("fuu");
return 43;
}
}
}
The Brython class:
import BLISS
class X( BLISS() ):
def __init__(self):
print(self)
print("?", dir(self) )
print( self.foo() )
print( self.faa )
self.fii(43)
print( self.fuu )
It prints:
<Javascript object: [object Object]>
? []
foo
<Javascript undefined> # should be 43
Uncaught (in promise) TypeError: $B.frame_obj is null
set_exception_offsets http://127.0.0.1:5500/brython/www/src/brython.js:4075
call http://127.0.0.1:5500/brython/www/src/brython.js:1604
PyClass http://127.0.0.1:5500/index.js:36
init http://127.0.0.1:5500/LISS/index.js:243
connectedCallback http://127.0.0.1:5500/LISS/index.js:194
define http://127.0.0.1:5500/LISS/index.js:332
test http://127.0.0.1:5500/index.js:40
res http://127.0.0.1:5500/brython/www/src/brython.js:9555
call http://127.0.0.1:5500/brython/www/src/brython.js:1603
anonymous http://127.0.0.1:5500/brython/www/src/brython.js line 1117 > Function:89
loop http://127.0.0.1:5500/brython/www/src/brython.js:1117
run_script http://127.0.0.1:5500/brython/www/src/brython.js:963
loop http://127.0.0.1:5500/brython/www/src/brython.js:1133
onreadystatechange http://127.0.0.1:5500/brython/www/src/brython.js:1086
ajax_load_script http://127.0.0.1:5500/brython/www/src/brython.js:1082
loop http://127.0.0.1:5500/brython/www/src/brython.js:1133
run_scripts http://127.0.0.1:5500/brython/www/src/brython.js:945
brython http://127.0.0.1:5500/brython/www/src/brython.js:891
onload http://127.0.0.1:5500/brython/www/src/brython.js:842
EventHandlerNonNull* http://127.0.0.1:5500/brython/www/src/brython.js:842
EventListener.handleEvent* http://127.0.0.1:5500/brython/www/src/brython.js:840
<anonymous> http://127.0.0.1:5500/brython/www/src/brython.js:964
Hum, it seems calling super()
in the subclass raises an error.
I'll have to check more in depth the error as I currently have TypeError: $B.frame_obj is null
.
It seems when calling super()
, the error is here :
Uncaught (in promise) Error:
exc_class http://127.0.0.1:5500/brython/www/src/brython.js:4220
super http://127.0.0.1:5500/brython/www/src/brython.js:3448
call http://127.0.0.1:5500/brython/www/src/brython.js:1603
__init__4 http://127.0.0.1:5500/brython/www/src/brython.js line 1117 > Function:54
anonymous http://127.0.0.1:5500/brython/www/src/brython.js line 9866 > Function:11
instance_creator http://127.0.0.1:5500/brython/www/src/brython.js:2445
call http://127.0.0.1:5500/brython/www/src/brython.js:1603
PyClass http://127.0.0.1:5500/index.js:48
init http://127.0.0.1:5500/LISS/index.js:243
connectedCallback http://127.0.0.1:5500/LISS/index.js:194
define http://127.0.0.1:5500/LISS/index.js:332
test http://127.0.0.1:5500/index.js:52
res http://127.0.0.1:5500/brython/www/src/brython.js:9555
call http://127.0.0.1:5500/brython/www/src/brython.js:1603
anonymous http://127.0.0.1:5500/brython/www/src/brython.js line 1117 > Function:83
loop http://127.0.0.1:5500/brython/www/src/brython.js:1117
run_script http://127.0.0.1:5500/brython/www/src/brython.js:963
loop http://127.0.0.1:5500/brython/www/src/brython.js:1133
onreadystatechange http://127.0.0.1:5500/brython/www/src/brython.js:1086
ajax_load_script http://127.0.0.1:5500/brython/www/src/brython.js:1082
loop http://127.0.0.1:5500/brython/www/src/brython.js:1133
run_scripts http://127.0.0.1:5500/brython/www/src/brython.js:945
brython http://127.0.0.1:5500/brython/www/src/brython.js:891
onload http://127.0.0.1:5500/brython/www/src/brython.js:842
EventHandlerNonNull* http://127.0.0.1:5500/brython/www/src/brython.js:842
EventListener.handleEvent* http://127.0.0.1:5500/brython/www/src/brython.js:840
<anonymous> http://127.0.0.1:5500/brython/www/src/brython.js:964
brython.js:4220:57
init http://127.0.0.1:5500/LISS/index.js:253
connectedCallback http://127.0.0.1:5500/LISS/index.js:194
define http://127.0.0.1:5500/LISS/index.js:332
InterpretGeneratorResume self-hosted:1465
AsyncFunctionNext self-hosted:852
(Asynchrone : async)
test http://127.0.0.1:5500/index.js:52
res http://127.0.0.1:5500/brython/www/src/brython.js:9555
call http://127.0.0.1:5500/brython/www/src/brython.js:1603
anonymous http://127.0.0.1:5500/brython/www/src/brython.js line 1117 > Function:83
loop http://127.0.0.1:5500/brython/www/src/brython.js:1117
run_script http://127.0.0.1:5500/brython/www/src/brython.js:963
loop http://127.0.0.1:5500/brython/www/src/brython.js:1133
onreadystatechange http://127.0.0.1:5500/brython/www/src/brython.js:1086
(Asynchrone : EventHandlerNonNull)
ajax_load_script http://127.0.0.1:5500/brython/www/src/brython.js:1082
loop http://127.0.0.1:5500/brython/www/src/brython.js:1133
run_scripts http://127.0.0.1:5500/brython/www/src/brython.js:945
brython http://127.0.0.1:5500/brython/www/src/brython.js:891
onload http://127.0.0.1:5500/brython/www/src/brython.js:842
(Asynchrone : EventHandlerNonNull)
<anonyme> http://127.0.0.1:5500/brython/www/src/brython.js:842
(Asynchrone : EventListener.handleEvent)
<anonyme> http://127.0.0.1:5500/brython/www/src/brython.js:840
<anonyme> http://127.0.0.1:5500/brython/www/src/brython.js:964
Proposal:
When extending a JS class:
- Make
super()
returning{__init__: function(){ ... }}
. - The subclass must call
super().__init__(...)
. super().__init__(*args)
will construct a new instance of the inherited JS class :new SuperClass(...args)
.- Then, consecutive calls to
super()
will return the built instance. - When accessing
self.foo
, if not in the subclass, search it in the built instance.
Alternative Proposal:
When doing : class PyClass(JSClass)
, do not directly inherit JSClass
, but instead inherit an ad hoc python class wrapping the JS class :
class Wrapper:
def __init__(self, *args):
self.__js = JSClass.new(*args)
def foo(self):
self.__js.foo()
class PyClass(Wrapper):
pass
This could be implemented in two ways :
-
internally, i.e. when Brython detects we inherit a JS class, automatically build the wrapper (more secure).
-
explicitly with an helper :
PyClass(BuildWrapper(JSClass))
(can be implemented directly in Brython) + raise an exception when trying to directly inherit a JS class.