@@ -179,7 +179,7 @@ def patch_properties(self, classname, i1, i2):
179179 elif "@apidiff.hide" in pre_lines :
180180 pass # continue as normal
181181 old_line = self .lines [j1 ]
182- new_line = f" def { propname } (self):"
182+ new_line = self . get_property_def ( classname , propname )
183183 if old_line != new_line :
184184 fixme_line = " # FIXME: was " + old_line .split ("def " , 1 )[- 1 ]
185185 lines = [fixme_line , new_line ]
@@ -241,7 +241,7 @@ def get_missing_properties(self, classname, seen_props):
241241 if propname not in seen_props :
242242 lines .append (" # FIXME: new prop to implement" )
243243 lines .append (" @property" )
244- lines .append (f" def { propname } (self):" )
244+ lines .append (self . get_property_def ( classname , propname ) )
245245 lines .append (" raise NotImplementedError()" )
246246 lines .append ("" )
247247 return lines
@@ -265,16 +265,105 @@ class IdlPatcherMixin:
265265 def __init__ (self ):
266266 super ().__init__ ()
267267 self .idl = get_idl_parser ()
268+ self .detect_async_props_and_methods ()
269+
270+ def detect_async_props_and_methods (self ):
271+
272+ self .async_idl_names = async_idl_names = {} # (sync-name, async-name)
273+
274+ for classname , interface in self .idl .classes .items ():
275+ for namedict in [interface .attributes , interface .functions ]:
276+ for name_idl , idl_line in namedict .items ():
277+ idl_result = idl_line .split (name_idl )[0 ]
278+ if "Promise" in idl_result :
279+ # We found an async property or method.
280+ name_idl_base = name_idl
281+ if name_idl .endswith ("Async" ):
282+ name_idl_base = name_idl [:- 5 ]
283+ key = classname , name_idl_base
284+ # Now we determine the kind
285+ if name_idl_base != name_idl and name_idl_base in namedict :
286+ # Has both
287+ async_idl_names [key ] = name_idl_base , name_idl
288+ else :
289+ # Only has async
290+ async_idl_names [key ] = None , name_idl
291+
292+ def get_idl_name_variants (self , classname , base_name ):
293+ """Returns the names of an idl prop/method for its sync and async variant.
294+ Either can be None.
295+ """
296+ # Must be a base name, without the suffix
297+ assert not base_name .lower ().endswith (("sync" , "async" ))
298+
299+ key = classname , base_name
300+ default = base_name , None
301+ return self .async_idl_names .get (key , default )
302+
303+ def name2idl (self , classname , name_py ):
304+ """Map a python propname/methodname to the idl variant.
305+ Take async into account.
306+ """
307+ if name_py == "__init__" :
308+ return "constructor"
309+
310+ # Get idl base name
311+ if name_py .endswith (("_sync" , "_async" )):
312+ name_idl_base = to_camel_case (name_py .rsplit ("_" , 1 )[0 ])
313+ else :
314+ name_idl_base = to_camel_case (name_py )
268315
269- def name2idl (self , name ):
270- m = {"__init__" : "constructor" }
271- name = m .get (name , name )
272- return to_camel_case (name )
316+ # Get idl variant names
317+ idl_sync , idl_async = self .get_idl_name_variants (classname , name_idl_base )
273318
274- def name2py (self , name ):
275- m = {"constructor" : "__init__" }
276- name = m .get (name , name )
277- return to_snake_case (name )
319+ # Triage
320+ if idl_sync and idl_async :
321+ if name_py .endswith ("_async" ):
322+ return idl_async
323+ elif name_py .endswith ("_sync" ):
324+ return name_idl_base + "InvalidVariant"
325+ else :
326+ return idl_sync
327+ elif idl_async :
328+ if name_py .endswith ("_async" ):
329+ return idl_async
330+ elif name_py .endswith ("_sync" ):
331+ return idl_async
332+ else :
333+ return name_idl_base + "InvalidVariant"
334+ else : # idl_sync only
335+ if name_py .endswith ("_async" ):
336+ return name_idl_base + "InvalidVariant"
337+ elif name_py .endswith ("_sync" ):
338+ return name_idl_base + "InvalidVariant"
339+ else :
340+ return idl_sync
341+
342+ def name2py_names (self , classname , name_idl ):
343+ """Map a idl propname/methodname to the python variants.
344+ Take async into account. Returns a list with one or two names;
345+ for async props/methods Python has the sync and the async variant.
346+ """
347+
348+ if name_idl == "constructor" :
349+ return ["__init__" ]
350+
351+ # Get idl base name
352+ name_idl_base = name_idl
353+ if name_idl .endswith ("Async" ):
354+ name_idl_base = name_idl [:- 5 ]
355+ name_py_base = to_snake_case (name_idl_base )
356+
357+ # Get idl variant names
358+ idl_sync , idl_async = self .get_idl_name_variants (classname , name_idl_base )
359+
360+ if idl_sync and idl_async :
361+ return [to_snake_case (idl_sync ), name_py_base + "_async" ]
362+ elif idl_async :
363+ return [name_py_base + "_sync" , name_py_base + "_async" ]
364+ else :
365+ assert idl_sync == name_idl_base
366+ return [name_py_base ]
278367
279368 def class_is_known (self , classname ):
280369 return classname in self .idl .classes
@@ -295,22 +384,28 @@ def get_class_def(self, classname):
295384 bases = "" if not bases else f"({ ', ' .join (bases )} )"
296385 return f"class { classname } { bases } :"
297386
387+ def get_property_def (self , classname , propname ):
388+ attributes = self .idl .classes [classname ].attributes
389+ name_idl = self .name2idl (classname , propname )
390+ assert name_idl in attributes
391+
392+ line = "def " + to_snake_case (propname ) + "(self):"
393+ if propname .endswith ("_async" ):
394+ line = "async " + line
395+ return " " + line
396+
298397 def get_method_def (self , classname , methodname ):
299- # Get the corresponding IDL line
300398 functions = self .idl .classes [classname ].functions
301- name_idl = self .name2idl (methodname )
302- if methodname .endswith ("_async" ) and name_idl not in functions :
303- name_idl = self .name2idl (methodname .replace ("_async" , "" ))
304- elif name_idl not in functions and name_idl + "Async" in functions :
305- name_idl += "Async"
306- idl_line = functions [name_idl ]
399+ name_idl = self .name2idl (classname , methodname )
400+ assert name_idl in functions
307401
308402 # Construct preamble
309403 preamble = "def " + to_snake_case (methodname ) + "("
310- if "async" in methodname :
404+ if methodname . endswith ( "_async" ) :
311405 preamble = "async " + preamble
312406
313407 # Get arg names and types
408+ idl_line = functions [name_idl ]
314409 args = idl_line .split ("(" , 1 )[1 ].split (")" , 1 )[0 ].split ("," )
315410 args = [arg .strip () for arg in args if arg .strip ()]
316411 raw_defaults = [arg .partition ("=" )[2 ].strip () for arg in args ]
@@ -361,28 +456,31 @@ def _arg_from_struct_field(self, field):
361456 return result
362457
363458 def prop_is_known (self , classname , propname ):
364- propname_idl = self .name2idl (propname )
365- return propname_idl in self .idl .classes [classname ].attributes
459+ attributes = self .idl .classes [classname ].attributes
460+ propname_idl = self .name2idl (classname , propname )
461+ return propname_idl if propname_idl in attributes else None
366462
367463 def method_is_known (self , classname , methodname ):
368464 functions = self .idl .classes [classname ].functions
369- name_idl = self .name2idl (methodname )
370- if "_async" in methodname and name_idl not in functions :
371- name_idl = self .name2idl (methodname .replace ("_async" , "" ))
372- elif name_idl not in functions and name_idl + "Async" in functions :
373- name_idl += "Async"
374- return name_idl if name_idl in functions else None
465+ methodname_idl = self .name2idl (classname , methodname )
466+ return methodname_idl if methodname_idl in functions else None
375467
376468 def get_class_names (self ):
377469 return list (self .idl .classes .keys ())
378470
379471 def get_required_prop_names (self , classname ):
380- propnames_idl = self .idl .classes [classname ].attributes .keys ()
381- return [self .name2py (x ) for x in propnames_idl ]
472+ attributes = self .idl .classes [classname ].attributes
473+ names = []
474+ for name_idl in attributes .keys ():
475+ names .extend (self .name2py_names (classname , name_idl ))
476+ return names
382477
383478 def get_required_method_names (self , classname ):
384- methodnames_idl = self .idl .classes [classname ].functions .keys ()
385- return [self .name2py (x ) for x in methodnames_idl ]
479+ functions = self .idl .classes [classname ].functions
480+ names = []
481+ for name_idl in functions .keys ():
482+ names .extend (self .name2py_names (classname , name_idl ))
483+ return names
386484
387485
388486class BaseApiPatcher (IdlPatcherMixin , AbstractApiPatcher ):
@@ -398,14 +496,16 @@ def get_class_comment(self, classname):
398496 return None
399497
400498 def get_prop_comment (self , classname , propname ):
401- if self .prop_is_known (classname , propname ):
402- propname_idl = self .name2idl (propname )
403- return " # IDL: " + self .idl .classes [classname ].attributes [propname_idl ]
499+ attributes = self .idl .classes [classname ].attributes
500+ name_idl = self .prop_is_known (classname , propname )
501+ if name_idl :
502+ return " # IDL: " + attributes [name_idl ]
404503
405504 def get_method_comment (self , classname , methodname ):
505+ functions = self .idl .classes [classname ].functions
406506 name_idl = self .method_is_known (classname , methodname )
407507 if name_idl :
408- return " # IDL: " + self . idl . classes [ classname ]. functions [name_idl ]
508+ return " # IDL: " + functions [name_idl ]
409509
410510
411511class BackendApiPatcher (AbstractApiPatcher ):
0 commit comments