Permalink
Feb 1, 2020
May 17, 2019
May 17, 2019
May 17, 2019
May 17, 2019
Feb 1, 2020
Jan 15, 2018
Apr 3, 2019
Apr 3, 2019
Feb 1, 2020
Apr 3, 2019
Apr 3, 2019
Apr 3, 2019
Dec 13, 2018
Nov 6, 2019
Dec 10, 2018
Dec 10, 2018
Mar 7, 2018
Feb 26, 2018
Feb 1, 2020
Dec 10, 2018
Dec 10, 2018
Dec 10, 2018
Jul 31, 2018
Jul 31, 2018
Dec 10, 2018
Nov 2, 2018
Jul 31, 2018
Dec 10, 2018
Dec 10, 2018
Nov 28, 2018
Apr 18, 2020
May 27, 2018
Apr 18, 2020
Feb 1, 2020
Oct 24, 2018
Feb 3, 2018
Feb 1, 2020
Mar 7, 2018
Mar 7, 2018
Nov 9, 2015
May 17, 2019
Feb 26, 2018
Apr 3, 2019
May 17, 2019
Feb 26, 2018
Apr 3, 2019
Dec 13, 2018
Mar 7, 2018
Mar 7, 2018
Feb 26, 2018
Apr 14, 2019
Dec 13, 2018
May 17, 2019
Feb 26, 2018
Apr 3, 2019
Dec 1, 2018
Jun 28, 2018
May 16, 2020
Feb 1, 2020
Mar 7, 2018
Aug 2, 2018
Apr 18, 2020
Feb 26, 2018
Newer
100644
1035 lines (923 sloc)
30.5 KB
3
/*
4
Implementation of Python dictionaries
5
6
We can't use Javascript's Map here, because the behaviour is not exactly the
7
same (eg with keys that are instances of classes with a __hash__ method...)
8
and because Map is much slower than regular Javascript objects.
9
10
A Python dictionary is implemented as a Javascript objects with these
11
attributes:
12
. $version: an integer with an initial value of 0, incremented at each
13
insertion
14
. $numeric_dict: for keys of type int
15
. $string_dict and $str_hash: for keys of type str
16
. $object_dict: for keys of other types
17
18
The value associated to a key in $numeric_dict and $string_dict is a pair
19
[value, rank] where "value" is the value associated with the key and "rank"
20
is the value of the dict attribute $version when the pair is inserted. This
21
is required to keep track of the insertion order, mandatory since Python 3.7.
22
23
For keys that are not str or int, their hash value is computed. Since several
34
var set_ops = ["eq", "add", "sub", "and", "or", "xor", "le", "lt", "ge", "gt"]
35
36
$B.make_view = function(name, set_like){
37
var klass = $B.make_class(name, function(items){
38
return {
39
__class__: klass,
40
__dict__: _b_.dict.$factory(),
41
counter: -1,
42
items: items,
43
len: items.length
44
}
45
})
46
47
if(set_like){
48
for(var i = 0, len = set_ops.length; i < len; i++){
49
var op = "__" + set_ops[i] + "__"
50
klass[op] = (function(op){
51
return function(self, other){
52
// compare set of items to other
53
return _b_.set[op](_b_.set.$factory(self),
54
_b_.set.$factory(other))
55
}
56
})(op)
57
}
58
}
59
klass.__iter__ = function(self){
60
var it = klass.$iterator.$factory(self.items)
61
it.len_func = self.len_func
62
return it
63
}
69
klass.__repr__ = function(self){
70
return klass.$infos.__name__ + '(' + _b_.repr(self.items) + ')'
71
}
72
74
return klass
75
}
76
77
// Special version of __next__ for iterators on dict keys / values / items.
78
// Checks that the dictionary size didn't change during iteration.
79
function dict_iterator_next(self){
80
if(self.len_func() != self.len){
82
}
83
self.counter++
84
if(self.counter < self.items.length){
85
return self.items[self.counter]
86
}
87
throw _b_.StopIteration.$factory("StopIteration")
88
}
89
101
dict.$to_obj = function(d){
102
// Function applied to dictionary that only have string keys,
103
// return a Javascript objects with the kays mapped to the value,
104
// excluding the insertion rank
105
var res = {}
106
for(var key in d.$string_dict){
107
res[key] = d.$string_dict[key][0]
108
}
109
return res
110
}
111
121
if(val === undefined){val = _b_.NotImplemented}
122
else if(val === null){val = $N}
126
}else{
127
for(var k in d.$numeric_dict){
128
items.push([parseFloat(k), d.$numeric_dict[k]])
129
}
133
for(var k in d.$object_dict){
134
d.$object_dict[k].forEach(function(item){
135
items.push(item)
136
})
137
}
138
// sort by insertion order
139
items.sort(function(a, b){
140
return a[1][1] - b[1][1]
141
})
142
items = items.map(function(item){return [item[0], item[1][0]]})
145
if(ix !== undefined){
146
return items.map(function(item){return item[ix]})
147
}else{
148
items.__class__ = _b_.tuple
149
return items.map(function(item){
150
item.__class__ = _b_.tuple; return item}
151
)
152
}
155
$B.dict_to_list = to_list // used in py_types.js
156
157
// Special version of __next__ for iterators on dict keys / values / items.
158
// Checks that the dictionary size didn't change during iteration.
159
function dict_iterator_next(self){
160
if(self.len_func() != self.len){
162
}
163
self.counter++
164
if(self.counter < self.items.length){
165
return self.items[self.counter]
177
si(left, _l[i][0], _l[i][1])
178
if(right.$version != right_version){
179
throw _b_.RuntimeError.$factory("dict mutated during update")
180
}
181
}
184
function rank(self, hash, key){
185
// Search if object key, with hash = hash(key), is in
186
// self.$object_dict
187
var pairs = self.$object_dict[hash]
188
if(pairs !== undefined){
189
for(var i = 0, len = pairs.length; i < len; i++){
190
if($B.rich_comp("__eq__", key, pairs[i][0])){
191
return i
192
}
193
}
194
}
195
return -1
196
}
197
206
var $ = $B.args("__contains__", 2, {self: null, key: null},
207
["self", "key"], arguments, {}, null, null),
210
if(self.$is_namespace){key = $B.to_alias(key)} // issue 1244
211
212
if(self.$jsobj){
213
return self.$jsobj[key] !== undefined
214
}
220
return self.$numeric_dict[key] !== undefined
221
}
222
223
var hash = _b_.hash(key)
224
if(self.$str_hash[hash] !== undefined &&
225
$B.rich_comp("__eq__", key, self.$str_hash[hash])){return true}
226
if(self.$numeric_dict[hash] !== undefined &&
227
$B.rich_comp("__eq__", key, hash)){return true}
228
return rank(self, hash, key) > -1
233
var $ = $B.args("__eq__", 2, {self: null, arg: null},
234
["self", "arg"], arguments, {}, null, null),
243
switch(typeof arg){
244
case "string":
245
if(self.$string_dict[arg] === undefined){
265
if((ix = rank(self, hash, arg)) > -1){
266
self.$object_dict[hash].splice(ix, 1)
267
}else{
276
var $ = $B.args("__eq__", 2, {self: null, other: null},
277
["self", "other"], arguments, {}, null, null),
283
if(self.$jsobj){self = jsobj2dict(self.$jsobj)}
284
if(other.$jsobj){other = jsobj2dict(other.$jsobj)}
295
if(!$B.rich_comp("__eq__", other.$numeric_dict[k][0],
296
self.$numeric_dict[k][0])){
300
var pairs = other.$object_dict[k],
301
flag = false
302
for(var i = 0, len = pairs.length; i < len; i++){
303
if($B.rich_comp("__eq__", k, pairs[i][0]) &&
304
$B.rich_comp("__eq__", self.$numeric_dict[k],
305
pairs[i][1])){
306
flag = true
307
break
308
}
323
var pairs = self.$object_dict[hash]
324
// Get all (key, value) pairs in other that have the same hash
325
var other_pairs = []
326
if(other.$numeric_dict[hash] !== undefined){
327
other_pairs.push([hash, other.$numeric_dict[hash]])
328
}
330
other_pairs = other_pairs.concat(other.$object_dict[hash])
331
}
332
if(other_pairs.length == 0){
333
return false
334
}
335
for(var i = 0, len_i = pairs.length; i < len_i; i++){
336
var flag = false
337
var key = pairs[i][0],
339
for(var j = 0, len_j = other_pairs.length; j < len_j; j++){
340
if($B.rich_comp("__eq__", key, other_pairs[j][0]) &&
355
var $ = $B.args("__getitem__", 2, {self: null, arg: null},
356
["self", "arg"], arguments, {}, null, null),
372
373
switch(typeof arg){
374
case "string":
375
if(self.$string_dict[arg] !== undefined){
383
break
384
}
385
386
// since the key is more complex use 'default' method of getting item
387
454
if(first === undefined){return $N}
455
if(second === undefined){
456
if(first.__class__ === $B.JSObject){
457
self.$jsobj = first.js
458
return $N
459
}else if(first.$jsobj){
460
self.$jsobj = {}
461
for(var attr in first.$jsobj){
462
self.$jsobj[attr] = first.$jsobj[attr]
472
arguments, {}, "first", "second")
473
var args = $.first
474
if(args.length > 1){
475
throw _b_.TypeError.$factory("dict expected at most 1 argument" +
476
", got 2")
477
}else if(args.length == 1){
478
args = args[0]
479
if(args.__class__ === dict){
480
['$string_dict', '$str_hash', '$numeric_dict', '$object_dict'].
481
forEach(function(d){
482
for(key in args[d]){self[d][key] = args[d][key]}
483
})
487
var keys = $B.$getattr(args, "keys", null)
488
if(keys !== null){
489
var gi = $B.$getattr(args, "__getitem__", null)
490
if(gi !== null){
491
// has keys and __getitem__ : it's a mapping, iterate on
492
// keys and values
493
gi = $B.$call(gi)
494
var kiter = _b_.iter($B.$call(keys)())
495
while(true){
496
try{
497
var key = _b_.next(kiter),
498
value = gi(key)
499
dict.__setitem__(self, key, value)
500
}catch(err){
501
if(err.__class__ === _b_.StopIteration){
502
break
503
}
504
throw err
505
}
506
}
507
return $N
508
}
509
}
510
if(! Array.isArray(args)){
511
args = _b_.list.$factory(args)
512
}
513
// Form "dict([[key1, value1], [key2,value2], ...])"
519
switch(typeof attr){
520
case "string":
521
self.$string_dict[attr] = kw[attr]
522
self.$str_hash[str_hash(attr)] = attr
523
break
524
case "number":
525
self.$numeric_dict[attr] = kw[attr]
526
break
527
default:
528
si(self, attr, kw[attr])
529
break
530
}
547
for(var k in self.$numeric_dict){_count++}
548
for(var k in self.$string_dict){_count++}
549
for(var hash in self.$object_dict){
550
_count += self.$object_dict[hash].length
551
}
570
if(cls !== dict){
571
instance.__dict__ = _b_.dict.$factory()
572
}
573
return instance
586
try{
587
res.push(repr(item[0]) + ": " + repr(item[1]))
588
}catch(err){
589
throw err
590
}
598
["self", "key", "value"], arguments, {}, null, null)
599
return dict.$setitem($.self, $.key, $.value)
600
}
605
// If key is a string, set:
606
// - $string_dict[key] = [value, order] where "order" is an auto-increment
607
// unique id to keep track of insertion order
608
// - $str_hash[hash(key)] to key
609
//
610
// If key is a number, set $numeric_dict[key] = value
611
//
612
// If key is another object, compute its hash value:
613
// - if the hash is a key of $str_hash, and key == $str_hash[hash],
614
// replace $string_dict[$str_hash[hash]] by value
615
// - if the hash is a key of $numeric_dict, and hash == key, replace
616
// $numeric_dict[hash] by value
617
// - if the hash is a key of $object_dict: $object_dict[hash] is a list
618
// of [k, v] pairs. If key is equal to one of the "k", replace the
619
// matching v by value. Otherwise, add [key, value] to the list
620
// - else set $object_dict[hash] = [[key, value]]
621
//
622
// In all cases, increment attribute $version, used to detect dictionary
625
// Parameter $hash is only set if this method is called by setdefault.
626
// In this case the hash of key has already been computed and we
627
// know that the key is not present in the dictionary, so it's no
628
// use computing hash(key) again, nor testing equality of keys
630
if(self.$from_js){
631
// dictionary created by method to_dict of JSObject instances
632
value = $B.pyobj2jsobj(value)
633
}
637
// If class attribute __init__ or __new__ are reset,
638
// the factory function has to change
639
self.$jsobj.$factory = $B.$instance_creator(self.$jsobj)
640
}
641
}else{
649
if(self.$string_dict === undefined){
650
console.log("pas de string dict", self, key, value)
651
}
652
self.$string_dict[key] = [value, self.$version]
679
// If $setitem is called from setdefault, don't test equality of key
680
// with any object
681
if($hash){
682
if(self.$object_dict[$hash] !== undefined){
686
}
687
self.$version++
688
return $N
689
}
690
var ix = rank(self, hash, key)
691
if(ix > -1){
692
// reset value
746
var $ = $B.args("fromkeys", 3, {cls: null, keys: null, value: null},
747
["cls", "keys", "value"], arguments, {value: _b_.None}, null, null),
759
if(klass === dict){dict.$setitem(res, key, value)}
760
else{$B.$getattr(res, "__setitem__")(key, value)}
771
var $ = $B.args("get", 3, {self: null, key: null, _default: null},
772
["self", "key", "_default"], arguments, {_default: $N}, null, null)
775
catch(err){
776
if(_b_.isinstance(err, _b_.KeyError)){return $._default}
777
else{throw err}
778
}
779
}
780
781
var dict_items = $B.make_view("dict_items", true)
782
dict_items.$iterator = $B.make_iterator_class("dict_itemiterator")
786
var _len = arguments.length - 1,
787
_msg = "items() takes no arguments (" + _len + " given)"
790
var it = dict_items.$factory(to_list(self))
791
it.len_func = function(){return dict.__len__(self)}
792
return it
795
var dict_keys = $B.make_view("dict_keys", true)
796
dict_keys.$iterator = $B.make_iterator_class("dict_keyiterator")
800
var _len = arguments.length - 1,
801
_msg = "keys() takes no arguments (" + _len + " given)"
804
var it = dict_keys.$factory(to_list(self, 0))
805
it.len_func = function(){return dict.__len__(self)}
806
return it
811
var missing = {},
812
$ = $B.args("pop", 3, {self: null, key: null, _default: null},
813
["self", "key", "_default"], arguments, {_default: missing}, null, null),
845
var $ = $B.args("setdefault", 3, {self: null, key: null, _default: null},
846
["self", "key", "_default"], arguments, {_default: $N}, null, null),
857
var hash = key.$hash
858
key.$hash = undefined
859
dict.$setitem(self, key, _default, hash)
866
var $ = $B.args("update", 1, {"self": null}, ["self"], arguments,
867
{}, "args", "kw"),
868
self = $.self,
869
args = $.args,
870
kw = $.kw
871
if(args.length > 0){
872
var o = args[0]
879
var _keys = _b_.list.$factory($B.$call($B.$getattr(o, "keys"))())
880
for(var i = 0, len = _keys.length; i < len; i++){
882
dict.$setitem(self, _keys[i], _value)
883
}
884
}else{
885
var it = _b_.iter(o),
886
i = 0
887
while(true){
888
try{
889
var item = _b_.next(it)
890
}catch(err){
891
if(err.__class__ === _b_.StopIteration){break}
892
throw err
893
}
894
try{
895
key_value = _b_.list.$factory(item)
896
}catch(err){
897
throw _b_.TypeError.$factory("cannot convert dictionary" +
898
" update sequence element #" + i + " to a sequence")
899
}
900
if(key_value.length !== 2){
901
throw _b_.ValueError.$factory("dictionary update " +
902
"sequence element #" + i + " has length " +
903
key_value.length + "; 2 is required")
904
}
905
dict.$setitem(self, key_value[0], key_value[1])
906
i++
915
var dict_values = $B.make_view("dict_values")
916
dict_values.$iterator = $B.make_iterator_class("dict_valueiterator")
920
var _len = arguments.length - 1,
921
_msg = "values() takes no arguments (" + _len + " given)"
924
var it = dict_values.$factory(to_list(self, 1))
925
it.len_func = function(){return dict.__len__(self)}
926
return it
931
var args = [res]
932
for(var i = 0, len = arguments.length; i < len ; i++){
933
args.push(arguments[i])
934
}
935
dict.__init__.apply(null, args)
943
// This must be done after set_func_names, otherwise dict.fromkeys doesn't
944
// have the attribute $infos
945
dict.fromkeys = _b_.classmethod.$factory(dict.fromkeys)
946
947
$B.getset_descriptor = $B.make_class("getset_descriptor",
948
function(klass, attr){
949
return {
950
__class__: $B.getset_descriptor,
951
cls: klass,
952
attr: attr
953
}
954
}
955
)
956
957
$B.getset_descriptor.__repr__ = $B.getset_descriptor.__str__ = function(self){
958
return `<attribute '${self.attr}' of '${self.cls.$infos.__name__}' objects>`
959
}
960
961
$B.set_func_names($B.getset_descriptor, "builtins")
962
967
// obj is a dictionary, with $string_dict table such that
968
// obj.$string_dict[key] = [value, rank]
969
// Transform it into an object with attribute $jsobj such that
970
// res.$jsobj[key] = value
971
var res = $B.obj_dict(dict.$to_obj(obj))
981
throw _b_.TypeError.$factory("'mappingproxy' object does not support " +
982
"item assignment")
985
for(var attr in dict){
986
if(mappingproxy[attr] !== undefined ||
987
["__class__", "__mro__", "__new__", "__init__", "__delitem__",
988
"clear", "fromkeys", "pop", "popitem", "setdefault",
989
"update"].indexOf(attr) > -1){
990
continue
991
}
992
if(typeof dict[attr] == "function"){
993
mappingproxy[attr] = (function(key){
994
return function(){
995
return dict[key].apply(null, arguments)
996
}
997
})(attr)
998
}else{
999
mappingproxy[attr] = dict[attr]
1000
}
1001
}
1002
1009
if(x[attr] === null){
1010
d.$string_dict[attr] = [_b_.None, d.$version]
1011
}else if(x[attr] === undefined){
1026
if(klass !== undefined && klass.$native){
1027
throw _b_.AttributeError.$factory(klass.__name__ +