Scope transpiled class attributes in a function instead of an object
Change Summary
Currently, the transpiled attributes of a class are scoped in an object as key/value pairs. This is problematic when trying to handle class scoped operation, for example:
class A:
print("hello from A")
A = 1
B = A + 1
The main objective of this PR is to facilitate the implementation of class scoped operations.
Secondary changes:
- Add support for class variable operations.
- Add support for class scoped expressions.
- Handle 'assignationless' annotated class variable.
Related issue number
Related to #630 and #663.
PR Checklist
- [ ] Adapted tests
- [ ] Passes tests
- [ ] Documented changes
- [x] Compatibility with
__iter__ - [x] Compatibility with
__next__ - [x] Compatibility with dataclass class decorator
- [x] Compatibility with user and built-in decorators
- [x] Compatibility with nested classes
Sample before/after
Python source:
class A:
print("hello from A")
z: str
a: str = ""
b = 1
c = b + 1
def __iter__(self):
return iter([1])
def __next__(self):
return next(iter([1]))
def func(self, arg):
return arg
@decor
def decorated_func(self, arg):
return arg
@staticmethod
def static_func(arg):
return arg
@classmethod
def classmethod_func(cls, arg):
return arg
@property
def property_func(self):
return 1
Before:
export var A = __class__ ('A', [object], {
__module__: __name__,
// no print()
// z: str causes an error at transpile time
a: '',
b: 1,
c: b + 1, // causes an error at JS runtime
get __iter__ () {return __get__ (this, function (self) {
return py_iter ([1]);
});},
[Symbol.iterator] () {return this.__iter__ ()},
get __next__ () {return __get__ (this, function (self) {
return py_next (py_iter ([1]));
});},
next: __jsUsePyNext__,
get func () {return __get__ (this, function (self, arg) {
return arg;
});},
get decorated_func () {return __get__ (this, decor (function (self, arg) {
return arg;
}));},
get static_func () {return function (arg) {
return arg;
};},
get classmethod_func () {return __getcm__ (this, function (cls, arg) {
return arg;
});},
get _get_property_func () {return __get__ (this, function (self) {
return 1;
});}
});
Object.defineProperty (A, 'property_func', property.call (A, A._get_property_func));;
After:
export var A = __class__ ('A', [object], (() => {
let cls = {};
cls.__module__ = __name__;
print ("hello from A");
var z = cls.z;
var a = cls.a = '';
var b = cls.b = 1;
var c = cls.c = b + 1;
__def__(cls, function __iter__() { return __get__ (this, function (self) {
return py_iter ([1]);
});});
cls[Symbol.iterator] = () => cls.__iter__();
__def__(cls, function __next__() { return __get__ (this, function (self) {
return py_next (py_iter ([1]));
});});
cls.next = __jsUsePyNext__;
__def__(cls, function func() { return __get__ (this, function (self, arg) {
return arg;
});});
__def__(cls, function decorated_func() { return __get__ (this, decor (function (self, arg) {
return arg;
}));});
__def__(cls, function static_func() { return function (arg) {
return arg;
};});
__def__(cls, function classmethod_func() { return __getcm__ (this, function (cls, arg) {
return arg;
});});
__def__(cls, function _get_property_func() { return __get__ (this, function (self) {
return 1;
});});
return cls;
})());
Object.defineProperty (A, 'property_func', property.call (A, A._get_property_func));;
It creates major problems like creating unnecessary scope when class attributes will overshadow global variables with the same name. If you have global name a and want to use it inside method, you will access var a in this class function instead.
I haven't been able to work on this, and probably won't unless I take a deeper dive into compiler.py.
Given:
class A:
z: str
a: str = ""
b = 1
c = b + 1
need a way to get
export var A = __class__ ('A', [object], (() => {
let cls = {};
cls.__module__ = __name__;
cls.z = undefined;
cls.a = '';
cls.b = 1;
cls.c = cls.b + 1;
};
instead of my current
export var A = __class__ ('A', [object], (() => {
let cls = {};
cls.__module__ = __name__;
var z = cls.z;
var a = cls.a = '';
var b = cls.b = 1;
var c = cls.c = b + 1;
};
the difficult part being to replace cls.c = b + 1 by cls.c = cls.b + 1.