diff --git a/transcrypt/development/automated_tests/transcrypt/dictionaries/__init__.py b/transcrypt/development/automated_tests/transcrypt/dictionaries/__init__.py index 0bca348e..4fdeb0ff 100644 --- a/transcrypt/development/automated_tests/transcrypt/dictionaries/__init__.py +++ b/transcrypt/development/automated_tests/transcrypt/dictionaries/__init__.py @@ -122,6 +122,17 @@ def run (autoTester): d5 = d4.copy() autoTester.check(d5) + autoTester.check(dict.fromkeys(['b', 'c', 'a'])) + autoTester.check(dict.fromkeys(['b', 'c', 'a'], '42')) + autoTester.check(dict.fromkeys('bca')) + autoTester.check(d1.fromkeys('bca')) + autoTester.check(d1.fromkeys(['b', 'c', 'a'])) + autoTester.check(d1.fromkeys(['b', 'c', 'a'], '42')) + autoTester.check({}.fromkeys(['b', 'c', 'a'])) + + autoTester.check ("not iterable", autoTester.expectException ( lambda: dict.fromkeys(42) )) + autoTester.check ("missing argument", autoTester.expectException ( lambda: dict.fromkeys() )) + # Check pop of None value (issue 827) a = {'hello': None} value = a.pop('hello', '') diff --git a/transcrypt/development/automated_tests/transcrypt/module_builtin/__init__.py b/transcrypt/development/automated_tests/transcrypt/module_builtin/__init__.py index 51712ef5..79ef6c91 100644 --- a/transcrypt/development/automated_tests/transcrypt/module_builtin/__init__.py +++ b/transcrypt/development/automated_tests/transcrypt/module_builtin/__init__.py @@ -35,11 +35,11 @@ def run (autoTester): max([[5,6,7,8,9],[1,2,3,4]],default=[1,1,1],key=len), max ([], default='zzz'), ) - # autoTester.check ('max', autoTester.expectException(lambda: max () )) - # autoTester.check ('max', autoTester.expectException(lambda: max (1,2,3,4, default=5) )) - # autoTester.check ('max', autoTester.expectException(lambda: max (default=5))) - # autoTester.check ('max', autoTester.expectException(lambda: max ([]))) - # autoTester.check ('max', autoTester.expectException(lambda: max([5,6,7,8,9],[1,2,3,4],default=[1,1,1],key=len) )) + autoTester.check ('max', autoTester.expectException(lambda: max () )) + autoTester.check ('max', autoTester.expectException(lambda: max (1,2,3,4, default=5) )) + autoTester.check ('max', autoTester.expectException(lambda: max (default=5))) + autoTester.check ('max', autoTester.expectException(lambda: max ([]))) + autoTester.check ('max', autoTester.expectException(lambda: max([5,6,7,8,9],[1,2,3,4],default=[1,1,1],key=len) )) # autoTester.check ('max', autoTester.expectException(lambda: max ([4, 5, 'xyz', 'XYZ']) )) # exception not currently implemented autoTester.check ('abs', abs (-1), abs (1), abs (0), abs (-0.1), abs (0.1)) diff --git a/transcrypt/modules/org/transcrypt/__builtin__.js b/transcrypt/modules/org/transcrypt/__builtin__.js index 3c12be31..6dcc652a 100644 --- a/transcrypt/modules/org/transcrypt/__builtin__.js +++ b/transcrypt/modules/org/transcrypt/__builtin__.js @@ -1924,12 +1924,28 @@ function __update__ (aDict) { function __copy__ () { let dNew = {}; - for (var attrib in this) { + for (let attrib in this) { dNew[attrib] = this[attrib]; } return dict(dNew); } +function __fromkeys__ (iterable, defVal) { + if(iterable === undefined){ + throw TypeError("fromkeys expected at least 1 argument, got 0") + } + if ( !(['[object Array]', '[object String]'].includes(Object.prototype.toString.call(iterable))) ) { + throw TypeError("object is not iterable", new Error()); + } + + if(defVal === undefined) defVal = null; + let dNew = {}; + for (let idx= 0; idx < iterable.length; idx++) { + dNew[iterable[idx]] = defVal; + } + return dict(dNew); +} + function __values__ () { var values = []; for (var attrib in this) { @@ -1950,19 +1966,19 @@ function __dsetitem__ (aKey, aValue) { } export function dict (objectOrPairs) { - var instance = {}; + let instance = {}; if (!objectOrPairs || objectOrPairs instanceof Array) { // It's undefined or an array of pairs if (objectOrPairs) { - for (var index = 0; index < objectOrPairs.length; index++) { - var pair = objectOrPairs [index]; - if ( !(pair instanceof Array) || pair.length != 2) { + for (let index = 0; index < objectOrPairs.length; index++) { + const pair = objectOrPairs [index]; + if ( !(pair instanceof Array) || pair.length !== 2) { throw ValueError( "dict update sequence element #" + index + " has length " + pair.length + "; 2 is required", new Error()); } - var key = pair [0]; - var val = pair [1]; + const key = pair [0]; + let val = pair [1]; if (!(objectOrPairs instanceof Array) && objectOrPairs instanceof Object) { // User can potentially pass in an object // that has a hierarchy of objects. This @@ -1980,14 +1996,14 @@ export function dict (objectOrPairs) { } else { if (isinstance (objectOrPairs, dict)) { - // Passed object is a dict already so we need to be a little careful + // Passed object is a dict already, so we need to be a little careful // N.B. - this is a shallow copy per python std - so // it is assumed that children have already become // python objects at some point. - var aKeys = objectOrPairs.py_keys (); - for (var index = 0; index < aKeys.length; index++ ) { - var key = aKeys [index]; + const aKeys = objectOrPairs.py_keys (); + for (let index = 0; index < aKeys.length; index++ ) { + const key = aKeys [index]; instance [key] = objectOrPairs [key]; } } else if (objectOrPairs instanceof Object) { @@ -2002,7 +2018,7 @@ export function dict (objectOrPairs) { } } - // Trancrypt interprets e.g. {aKey: 'aValue'} as a Python dict literal rather than a JavaScript object literal + // Transcrypt interprets e.g. {aKey: 'aValue'} as a Python dict literal rather than a JavaScript object literal // So dict literals rather than bare Object literals will be passed to JavaScript libraries // Some JavaScript libraries call all enumerable callable properties of an object that's passed to them // So the properties of a dict should be non-enumerable @@ -2021,6 +2037,7 @@ export function dict (objectOrPairs) { __setproperty__ (instance, 'py_update', {value: __update__, enumerable: false}); __setproperty__ (instance, 'py_copy', {value: __copy__, enumerable: false}); __setproperty__ (instance, 'py_values', {value: __values__, enumerable: false}); + __setproperty__ (instance, 'py_fromkeys', {value: __fromkeys__, enumerable: false}); __setproperty__ (instance, '__getitem__', {value: __dgetitem__, enumerable: false}); // Needed since compound keys necessarily __setproperty__ (instance, '__setitem__', {value: __dsetitem__, enumerable: false}); // trigger overloading to deal with slices return instance; @@ -2028,6 +2045,7 @@ export function dict (objectOrPairs) { dict.__name__ = 'dict'; dict.__bases__ = [object]; +dict.py_fromkeys = __fromkeys__ // Docstring setter diff --git a/transcrypt/modules/org/transcrypt/autotester/__init__.py b/transcrypt/modules/org/transcrypt/autotester/__init__.py index ad12af1e..0905236d 100644 --- a/transcrypt/modules/org/transcrypt/autotester/__init__.py +++ b/transcrypt/modules/org/transcrypt/autotester/__init__.py @@ -174,7 +174,9 @@ def expectException(self, func): try: func() return("no exception") - except Exception as exc: + except Exception: + return("exception") + except object: # required to catch some JS exceptions in the browser return("exception") def throwToError(self, func): diff --git a/transcrypt/modules/org/transcrypt/compiler.py b/transcrypt/modules/org/transcrypt/compiler.py index 6acb4b3a..6ad08723 100644 --- a/transcrypt/modules/org/transcrypt/compiler.py +++ b/transcrypt/modules/org/transcrypt/compiler.py @@ -577,9 +577,10 @@ def __init__ (self, module): ('type', 'py_metatype'), ('js_type', 'type'), # Only for the type metaclass, the type operator is dealt with separately in visit_Call ('TypeError', 'py_TypeError'), ('js_TypeError', 'TypeError'), ('update', 'py_update'), ('js_update', 'update'), - ('copy', 'py_copy'), - ('deepcopy', 'py_deepcopy'), + ('copy', 'py_copy'), ('js_copy', 'copy'), + ('deepcopy', 'py_deepcopy'), ('js_deepcopy', 'deepcopy'), ('values', 'py_values'), ('js_values', 'values'), + ('fromkeys', 'py_fromkeys'), ('reversed', 'py_reversed'), ('js_reversed', 'reversed'), ('setdefault', 'py_setdefault'), ('js_setdefault', 'setdefault'), ('js_super', 'super'),