Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scope transpiled class attributes in a function instead of an object #725

Open
wants to merge 2 commits into
base: dev_fall_2019
Choose a base branch
from

Conversation

AlexECX
Copy link

@AlexECX AlexECX commented May 14, 2020

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
  • Compatibility with __iter__
  • Compatibility with __next__
  • Compatibility with dataclass class decorator
  • Compatibility with user and built-in decorators
  • 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));;

@AlexECX AlexECX changed the base branch from master to dev_fall_2019 May 14, 2020 18:39
@faerot
Copy link

faerot commented Nov 15, 2022

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.

@AlexECX
Copy link
Author

AlexECX commented Mar 2, 2023

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants