@@ -52,14 +52,10 @@ declare function __pyproxyGen_Send(ptr: number, arg: any): IteratorResult<any>
5252// This also has the benefit that it makes intellisense happy.
5353declare var HAS_CONTAINS : number ;
5454declare var HAS_GET : number ;
55- declare var HAS_HAS : number ;
56- declare var HAS_INCLUDES : number ;
5755declare var HAS_LENGTH : number ;
5856declare var HAS_SET : number ;
59- declare var IS_ARRAY : number ;
6057declare var IS_CALLABLE : number ;
61- declare var IS_ERROR : number ;
62- declare var IS_GENERATOR : number ;
58+ declare var IS_DICT : number ;
6359declare var IS_ITERABLE : number ;
6460declare var IS_ITERATOR : number ;
6561
@@ -187,6 +183,7 @@ function pyproxy_new(
187183 if ( flags === - 1 ) {
188184 _pythonexc2js ( ) ;
189185 }
186+ const is_dict = flags & IS_DICT ;
190187 const cls = getPyProxyClass ( flags ) ;
191188 let target : any ;
192189 if ( flags & IS_CALLABLE ) {
@@ -234,7 +231,11 @@ function pyproxy_new(
234231 props ,
235232 ) ;
236233 let handlers ;
237- handlers = PyProxyHandlers ;
234+ if ( is_dict ) {
235+ handlers = PyProxyDictHandlers ;
236+ } else {
237+ handlers = PyProxyHandlers ;
238+ }
238239 let proxy = new Proxy ( target , handlers ) ;
239240 if ( ! isAlias && gcRegister ) {
240241 // we need to register only once for a set of aliases. we can't register the
@@ -583,6 +584,138 @@ const PyProxyHandlers = {
583584} ;
584585
585586
587+
588+ const PyProxyDictHandlersSet = new Set ( [
589+ "copy" ,
590+ "constructor" ,
591+ "$$flags" ,
592+ "toString" ,
593+ "destroy" ,
594+ ] ) ;
595+
596+ interface PythonError {
597+ type : string ;
598+ }
599+
600+ function isPythonError ( e : any ) : e is PythonError {
601+ return (
602+ e &&
603+ typeof e === "object" &&
604+ e . constructor &&
605+ e . constructor . name === "PythonError"
606+ ) ;
607+ }
608+
609+ const PyProxyDictHandlers = {
610+ isExtensible ( ) : boolean {
611+ return true ;
612+ } ,
613+ has ( jsobj : PyProxy , jskey : string | symbol ) : boolean {
614+ if ( PyContainsMethods . prototype . has . call ( jsobj , jskey ) ) {
615+ return true ;
616+ }
617+ if ( typeof jskey === "string" && / ^ [ 0 - 9 ] + $ / . test ( jskey ) ) {
618+ return PyContainsMethods . prototype . has . call ( jsobj , Number ( jskey ) ) ;
619+ }
620+ return false ;
621+ } ,
622+ get ( jsobj : PyProxy , jskey : string | symbol ) : any {
623+ if (
624+ typeof jskey === "symbol" ||
625+ PyProxyDictHandlersSet . has ( jskey )
626+ ) {
627+ // @ts -ignore
628+ return Reflect . get ( ...arguments ) ;
629+ }
630+ const result = PyGetItemMethods . prototype . get . call ( jsobj , jskey ) ;
631+ if (
632+ result !== undefined ||
633+ PyContainsMethods . prototype . has . call ( jsobj , jskey )
634+ ) {
635+ return result ;
636+ }
637+ if ( typeof jskey === "string" && / ^ [ 0 - 9 ] + $ / . test ( jskey ) ) {
638+ return PyGetItemMethods . prototype . get . call ( jsobj , Number ( jskey ) ) ;
639+ }
640+ // @ts -ignore
641+ return Reflect . get ( ...arguments ) ;
642+ } ,
643+ set ( jsobj : PyProxy , jskey : string | symbol | number , jsval : any ) : boolean {
644+ if ( typeof jskey === "symbol" ) {
645+ return false ;
646+ }
647+ if (
648+ ! PyContainsMethods . prototype . has . call ( jsobj , jskey ) &&
649+ typeof jskey === "string" &&
650+ / ^ [ 0 - 9 ] + $ / . test ( jskey )
651+ ) {
652+ jskey = Number ( jskey ) ;
653+ }
654+ try {
655+ PySetItemMethods . prototype . set . call ( jsobj , jskey , jsval ) ;
656+ return true ;
657+ } catch ( e ) {
658+ if ( isPythonError ( e ) && e . type === "KeyError" ) {
659+ return false ;
660+ }
661+ throw e ;
662+ }
663+ } ,
664+ deleteProperty ( jsobj : PyProxy , jskey : string | symbol | number ) : boolean {
665+ if ( typeof jskey === "symbol" ) {
666+ return false ;
667+ }
668+ if (
669+ ! PyContainsMethods . prototype . has . call ( jsobj , jskey ) &&
670+ typeof jskey === "string" &&
671+ / ^ [ 0 - 9 ] + $ / . test ( jskey )
672+ ) {
673+ jskey = Number ( jskey ) ;
674+ }
675+ try {
676+ PySetItemMethods . prototype . delete . call ( jsobj , jskey ) ;
677+ return true ;
678+ } catch ( e ) {
679+ if ( isPythonError ( e ) && e . type === "KeyError" ) {
680+ return false ;
681+ }
682+ throw e ;
683+ }
684+ } ,
685+ getOwnPropertyDescriptor ( jsobj : PyProxy , prop : any ) {
686+ if ( ! PyProxyDictHandlers . has ( jsobj , prop ) ) {
687+ return undefined ;
688+ }
689+ const value = PyProxyDictHandlers . get ( jsobj , prop ) ;
690+ return {
691+ configurable : true ,
692+ enumerable : true ,
693+ value,
694+ writable : true ,
695+ } ;
696+ } ,
697+ ownKeys ( jsobj : PyProxy ) : ( string | symbol ) [ ] {
698+ const result : Set < string | symbol > = new Set ( ) ;
699+ dictOwnKeysHelper ( jsobj , result ) ;
700+ return Array . from ( result ) ;
701+ } ,
702+ } ;
703+
704+ function dictOwnKeysHelper ( jsobj : PyProxy , result : Set < string | symbol > ) : void {
705+ const dictKeysView : Iterable < any > & PyProxy = PyProxyHandlers . get (
706+ jsobj ,
707+ "keys" ,
708+ ) ( ) ;
709+ for ( const key of dictKeysView ) {
710+ if ( typeof key === "string" ) {
711+ result . add ( key ) ;
712+ } else if ( typeof key === "number" ) {
713+ result . add ( key . toString ( ) ) ;
714+ }
715+ }
716+ dictKeysView . destroy ( ) ;
717+ }
718+
586719// Another layer of boilerplate. The PyProxyHandlers have some annoying logic to
587720// deal with straining out the spurious "Function" properties "prototype",
588721// "arguments", and "length", to deal with correctly satisfying the Proxy
0 commit comments