TranscryptOrg / Transcrypt

Python 3.9 to JavaScript compiler - Lean, fast, open!
https://www.transcrypt.org
Apache License 2.0
2.85k stars 214 forks source link

Support for dict unpacking (object spread equivalent) #820

Open lrowe opened 2 years ago

lrowe commented 2 years ago

It would be nice to support dict unpacking {**a, "b": 2}, the Python equivalent to JavaScript object spread { ...a, b: 2}.

# testme.py
a = {"a": 1}
b = {**a, "b": 2}
print(b)
% python testme.py 
{'a': 1, 'b': 2}
% transcrypt testme.py        

Transcrypt (TM) Python to JavaScript Small Sane Subset Transpiler Version 3.9.0
Copyright (C) Geatec Engineering. License: Apache 2.0

Saving target code in: ./__target__/org.transcrypt.__runtime__.js
Saving minified target code in: ./__target__/org.transcrypt.__runtime__.js

Error while compiling (offending file last):
        File 'testme', line 3, namely:

        Error while compiling (offending file last):
        File 'testme', line 3, namely:

Aborted

Using dict with kw args compiles but doesn't work:

# testme.py
a = {"a": 1}
b = dict(**a, **{"b": 2})
print(b)
% python testme.py 
{'a': 1, 'b': 2}
% transcrypt testme.py 

Transcrypt (TM) Python to JavaScript Small Sane Subset Transpiler Version 3.9.0
Copyright (C) Geatec Engineering. License: Apache 2.0

Ready

% deno run __target__/testme.js
error: Uncaught ReferenceError: adict is not defined
    at file://./__target__/testme.js:3:316
lrowe commented 2 years ago

For the dict(**a, **{"b": 2}) case this seems to be a limitation of the kwargtrans handling:

# testme.py
a = {"a": 1}
b = {"b": 2}
c = dict(**a, **b)
print(c)

Which transcrypt -n compiles to:

// Transcrypt'ed from Python, 2022-03-24 17:45:17
import {AssertionError, AttributeError, BaseException, DeprecationWarning, Exception, IndexError, IterableError, KeyError, NotImplementedError, RuntimeWarning, StopIteration, UserWarning, ValueError, Warning, __JsIterator__, __PyIterator__, __Terminal__, __add__, __and__, __call__, __class__, __envir__, __eq__, __floordiv__, __ge__, __get__, __getcm__, __getitem__, __getslice__, __getsm__, __gt__, __i__, __iadd__, __iand__, __idiv__, __ijsmod__, __ilshift__, __imatmul__, __imod__, __imul__, __in__, __init__, __ior__, __ipow__, __irshift__, __isub__, __ixor__, __jsUsePyNext__, __jsmod__, __k__, __kwargtrans__, __le__, __lshift__, __lt__, __matmul__, __mergefields__, __mergekwargtrans__, __mod__, __mul__, __ne__, __neg__, __nest__, __or__, __pow__, __pragma__, __pyUseJsNext__, __rshift__, __setitem__, __setproperty__, __setslice__, __sort__, __specialattrib__, __sub__, __super__, __t__, __terminal__, __truediv__, __withblock__, __xor__, abs, all, any, assert, bool, bytearray, bytes, callable, chr, copy, deepcopy, delattr, dict, dir, divmod, enumerate, filter, float, getattr, hasattr, input, int, isinstance, issubclass, len, list, map, max, min, object, ord, pow, print, property, py_TypeError, py_iter, py_metatype, py_next, py_reversed, py_typeof, range, repr, round, set, setattr, sorted, str, sum, tuple, zip} from './org.transcrypt.__runtime__.js';
var __name__ = '__main__';
export var a = dict ({'a': 1});
export var b = dict ({'b': 2});
export var c = dict (__kwargtrans__ (ab));
print (c);

//# sourceMappingURL=testme.map

Note how there is no comma between the two. kwargtrans does not support multiple arguments, but perhaps it could be replaced with a JS object spread since replacing that line with the following works:

export var c = dict ({ ...a, ...b });