problem with tuple+obj and list+obj expressions
There is an apparent problem with tuple + obj and list + obj expressions. Rather than call obj.__radd__, it seems that tuple.__add__/list.__add__ are called. Similar problem occurs with tuple * obj and list * obj. Problem appears to stem from the fact that tuple and list have __add__ and __mul__ methods that seem to take priority.
python code:
# __pragma__ ('opov')
class Obj:
def __init__(self, x, y):
self.x = x
self.y = y
self._message = None
def __str__(self):
return 'obj'
def __add__(self, other):
self._message = 'obj.__add__'
return (self.x + other[0], self.y + other[1])
def __sub__(self, other):
self._message = 'obj.__sub__'
return (self.x - other[0], self.y - other[1])
def __radd__(self, other):
self._message = 'obj.__radd__'
return (other[0] + self.x, other[1] + self.y)
def __rsub__(self, other):
self._message = 'obj.__rsub__'
return (other[0] - self.x, other[1] - self.y)
@property
def message(self):
message = self._message
self._message = None
return message
obj = Obj(4, 2)
_tuple = (4, 2)
_list = [4, 2]
print('obj + tuple:', obj + _tuple, '\t\tcalled:', obj.message)
print('obj - tuple:', obj - _tuple, '\t\tcalled:', obj.message)
print('tuple + obj:', _tuple + obj, '\tcalled:', obj.message)
print('tuple - obj:', _tuple - obj, '\t\tcalled:', obj.message)
print('obj + list:', obj + _list, '\t\tcalled:', obj.message)
print('obj - list:', obj - _list, '\t\tcalled:', obj.message)
print('list + obj:', _list + obj, '\tcalled:', obj.message)
print('list - obj:', _list - obj, '\t\tcalled:', obj.message)
console output:
obj + tuple: (8, 4) called: obj.__add__
obj - tuple: (0, 0) called: obj.__sub__
tuple + obj: [4, 2, obj] called: None
tuple - obj: (0, 0) called: obj.__rsub__
obj + list: (8, 4) called: obj.__add__
obj - list: (0, 0) called: obj.__sub__
list + obj: [4, 2, obj] called: None
list - obj: (0, 0) called: obj.__rsub__
env: Python3.9 venv with pip install Transcrypt testing: Firefox version 133 in Linux
This is a solution to deal with the issue with tuple + obj, tuple * obj, list + obj, and list * obj expressions. As tuple and list constructs are built on Javascript Array, perhaps this issue is due to maintaining the efficiency of tuple/list __add__ and __mul__ methods, therefore developers may prefer to maintain efficiency of these tuple/list functionality. The following changes to Transcrypt Array __add__ and __mul__ methods can be added to your personal code to fix the issue:
__pragma__ ('js', {},
"""
Array.prototype.__add__ = function (aList) {
if (typeof aList == 'object' && '__radd__' in aList) {
return aList.__radd__ (this);
}
else {
return list (this.concat (aList));
}
};
""")
__pragma__ ('js', {},
"""
Array.prototype.__mul__ = function (scalar) {
if (typeof scalar == 'object' && '__rmul__' in scalar) {
return scalar.__rmul__ (this);
}
else {
let result = this;
for (let i = 1; i < scalar; i++) {
result = result.concat (this);
}
return result;
}
};
Although the change in the previous comment solves the issue it may not be the best approach. The Python way would be to have type checking and return NotImplemented if incorrect type that would trigger __radd__ \ __rmul__. Transcrypt does not seem to have NotImplemented. With Transcrypt I think the following approach is better, and it solves the tuple + obj / list + obj / tuple * obj / list * obj issue while maintaining tuple + tuple / list + list / tuple * number / list * number functionality. The pragmas can be added to personal code to fix the issue.
__pragma__ ('js', {},
"""
Array.prototype.__add__ = function (other) {
if (other.__class__ == this.__class__) {
return list (this.concat (other));
}
else if (typeof other == 'object' && '__radd__' in other) {
return other.__radd__ (this);
}
else {
throw py_TypeError ('unsupported operand type');
}
};
""")
__pragma__ ('js', {},
"""
Array.prototype.__mul__ = function (other) {
if (typeof other == 'number') {
let result = this;
for (let i = 1; i < other; i++) {
result = result.concat (this);
}
return result;
}
else if (typeof other == 'object' && '__rmul__' in other) {
return other.__rmul__ (this);
}
else {
throw py_TypeError ('unsupported operand type');
}
};
""")
Tested Javascript changes on local Transcrypt cloned from current git repository. Tested with compiled Python code similar to first comment, verified that tuple and list functionality (+, +=, *, *=) was maintained, and that tuple + obj / list + obj called obj.__radd__ and tuple * obj / list * obj called obj.__rmul__. In addition, the automated_tests showed no errors. I can make a pull request of revised Javascript code if this change is acceptable.