@@ -104,6 +104,26 @@ def get_pythonjs_function_name(self, name):
104104 assert name in self .methods
105105 return '__%s_%s' % (self .name , name ) ## class name
106106
107+ def check_for_parent_with (self , method = None , property = None , operator = None , class_attribute = None ):
108+ for parent_name in self .parents :
109+ typedef = self .compiler .get_typedef ( class_name = parent_name )
110+ if method and method in typedef .methods :
111+ return typedef
112+ elif property and property in typedef .properties :
113+ return typedef
114+ elif operator and typedef .operators :
115+ return typedef
116+ elif class_attribute in typedef .class_attributes :
117+ return typedef
118+ elif typedef .parents :
119+ res = typedef .check_for_parent_with (
120+ method = method ,
121+ property = property ,
122+ operator = operator ,
123+ class_attribute = class_attribute
124+ )
125+ if res :
126+ return res
107127
108128class PythonToPythonJS (NodeVisitor ):
109129
@@ -112,6 +132,7 @@ class PythonToPythonJS(NodeVisitor):
112132 def __init__ (self , module = None , module_path = None ):
113133 super (PythonToPythonJS , self ).__init__ ()
114134 self ._classes = dict () ## class name : [method names]
135+ self ._class_parents = dict () ## class name : parents
115136 self ._instance_attributes = dict () ## class name : [attribute names]
116137 self ._class_attributes = dict ()
117138 self ._catch_attributes = None
@@ -124,21 +145,27 @@ def __init__(self, module=None, module_path=None):
124145 self ._module = module
125146 self ._module_path = module_path
126147
148+ self ._typedefs = dict () ## class name : typedef (not pickled)
149+
127150 def get_typedef (self , instance = None , class_name = None ):
128151 assert instance or class_name
129152 if isinstance (instance , Name ) and instance .id in self ._instances :
130153 class_name = self ._instances [ instance .id ]
131154
132155 if class_name :
133156 assert class_name in self ._classes
134- typedef = Typedef (
135- name = class_name ,
136- methods = self ._classes [ class_name ],
137- properties = self ._decorator_class_props [ class_name ],
138- attributes = self ._instance_attributes [ class_name ],
139- class_attributes = self ._class_attributes [ class_name ],
140- )
141- return typedef
157+
158+ if class_name not in self ._typedefs :
159+ self ._typedefs [ class_name ] = Typedef (
160+ name = class_name ,
161+ methods = self ._classes [ class_name ],
162+ properties = self ._decorator_class_props [ class_name ],
163+ attributes = self ._instance_attributes [ class_name ],
164+ class_attributes = self ._class_attributes [ class_name ],
165+ parents = self ._class_parents [ class_name ],
166+ compiler = self ,
167+ )
168+ return self ._typedefs [ class_name ]
142169
143170 def save_module (self ):
144171 if self ._module and self ._module_path :
@@ -148,6 +175,7 @@ def save_module(self):
148175 class_attributes = self ._class_attributes ,
149176 decorator_class_props = self ._decorator_class_props ,
150177 function_return_types = self ._function_return_types ,
178+ class_parents = self ._class_parents ,
151179 )
152180 pickle .dump ( a , open (os .path .join (self ._module_path , self ._module + '.module' ), 'wb' ) )
153181
@@ -165,6 +193,7 @@ def visit_ImportFrom(self, node):
165193 self ._instance_attributes .update ( a ['instance_attributes' ] )
166194 self ._decorator_class_props .update ( a ['decorator_class_props' ] )
167195 self ._function_return_types .update ( a ['function_return_types' ] )
196+ self ._class_parents .update ( a ['class_parents' ] )
168197
169198 def visit_Assert (self , node ):
170199 ## hijacking "assert isinstance(a,A)" as a type system ##
@@ -200,7 +229,7 @@ def visit_AugAssign(self, node):
200229 def visit_Yield (self , node ):
201230 return 'yield %s' % self .visit (node .value )
202231
203- def _gen_getattr_helper (self , class_name , func_name ):
232+ def _gen_getattr_helper (self , class_name , func_name ): ## DEPRECATED
204233 '''
205234 This helper is used to emulate how Python works, __getattr__ is only supposed
206235 to be called when the attribute is not found on the instance.
@@ -222,6 +251,7 @@ def _gen_getattr_helper(self, class_name, func_name):
222251 def visit_ClassDef (self , node ):
223252 name = node .name
224253 self ._classes [ name ] = list () ## method names
254+ self ._class_parents [ name ] = set ()
225255 self ._class_attributes [ name ] = set ()
226256 self ._catch_attributes = None
227257 self ._decorator_properties = dict () ## property names : {'get':func, 'set':func}
@@ -237,11 +267,16 @@ def visit_ClassDef(self, node):
237267
238268
239269 writer .write ('var(%s, __%s_attrs, __%s_parents)' % (name , name , name ))
240- writer .write ('__%s_attrs = JSObject()' % name )
241- writer .write ('__%s_parents = JSArray()' % name )
270+ writer .write ('window[" __%s_attrs"] = JSObject()' % name )
271+ writer .write ('window[" __%s_parents"] = JSArray()' % name )
242272 for base in node .bases :
243273 code = '__%s_parents.push(%s)' % (name , self .visit (base ))
244274 writer .write (code )
275+ if isinstance (base , Name ):
276+ self ._class_parents [ name ] = base .id
277+ else :
278+ raise NotImplementedError
279+
245280 for item in node .body :
246281 if isinstance (item , FunctionDef ):
247282 self ._classes [ name ].append ( item .name )
@@ -254,7 +289,7 @@ def visit_ClassDef(self, node):
254289 if item_name in self ._decorator_properties :
255290 pass
256291 else :
257- writer .write ('__%s_attrs["%s"] = %s' % (name , item_name , item .name ))
292+ writer .write ('window[" __%s_attrs"] ["%s"] = %s' % (name , item_name , item .name ))
258293
259294 #if item_name == '__getattr__':
260295 # writer.write( self._gen_getattr_helper(name, item.name) )
@@ -263,7 +298,7 @@ def visit_ClassDef(self, node):
263298 item_name = item .targets [0 ].id
264299 item .targets [0 ].id = '__%s_%s' % (name , item_name )
265300 self .visit (item ) # this will output the code for the assign
266- writer .write ('__%s_attrs["%s"] = %s' % (name , item_name , item .targets [0 ].id ))
301+ writer .write ('window[" __%s_attrs"] ["%s"] = %s' % (name , item_name , item .targets [0 ].id ))
267302 self ._class_attributes [ name ].add ( item_name ) ## should this come before self.visit(item) ??
268303 else :
269304 raise NotImplementedError
@@ -275,7 +310,7 @@ def visit_ClassDef(self, node):
275310 self ._decorator_properties = None
276311 self ._instances .pop ('self' )
277312
278- writer .write ('%s = create_class("%s", __%s_parents, __%s_attrs)' % (name , name , name , name ))
313+ writer .write ('%s = create_class("%s", window[" __%s_parents"], window[" __%s_attrs"] )' % (name , name , name , name ))
279314
280315 def visit_If (self , node ):
281316 writer .write ('if %s:' % self .visit (node .test ))
@@ -427,6 +462,22 @@ def visit_Attribute(self, node):
427462 elif '__getattr__' in typedef .methods :
428463 func = typedef .get_pythonjs_function_name ( '__getattr__' )
429464 return '%s([%s, "%s"])' % (func , node_value , node .attr )
465+
466+ elif typedef .check_for_parent_with ( property = node .attr ):
467+ parent = typedef .check_for_parent_with ( property = node .attr )
468+ getter = parent .properties [ node .attr ]['get' ]
469+ if getter in self ._function_return_types :
470+ node .returns_type = self ._function_return_types [getter ]
471+ return '%s( [%s] )' % (getter , node_value )
472+ elif typedef .check_for_parent_with ( class_attribute = node .attr ):
473+ #return 'get_attribute(%s, "%s")' % (node_value, node.attr) ## get_attribute is broken with grandparent class attributes
474+ parent = typedef .check_for_parent_with ( class_attribute = node .attr )
475+ return "window['__%s_attrs']['%s']" % (parent .name , node .attr )
476+ elif typedef .check_for_parent_with ( method = '__getattr__' ):
477+ parent = typedef .check_for_parent_with ( method = '__getattr__' )
478+ func = parent .get_pythonjs_function_name ( '__getattr__' )
479+ return '%s([%s, "%s"])' % (func , node_value , node .attr )
480+
430481 else :
431482 return 'get_attribute(%s, "%s")' % (node_value , node .attr )
432483 else :
0 commit comments