@@ -179,7 +179,7 @@ def patch_properties(self, classname, i1, i2):
179
179
elif "@apidiff.hide" in pre_lines :
180
180
pass # continue as normal
181
181
old_line = self .lines [j1 ]
182
- new_line = f" def { propname } (self):"
182
+ new_line = self . get_property_def ( classname , propname )
183
183
if old_line != new_line :
184
184
fixme_line = " # FIXME: was " + old_line .split ("def " , 1 )[- 1 ]
185
185
lines = [fixme_line , new_line ]
@@ -241,7 +241,7 @@ def get_missing_properties(self, classname, seen_props):
241
241
if propname not in seen_props :
242
242
lines .append (" # FIXME: new prop to implement" )
243
243
lines .append (" @property" )
244
- lines .append (f" def { propname } (self):" )
244
+ lines .append (self . get_property_def ( classname , propname ) )
245
245
lines .append (" raise NotImplementedError()" )
246
246
lines .append ("" )
247
247
return lines
@@ -265,16 +265,105 @@ class IdlPatcherMixin:
265
265
def __init__ (self ):
266
266
super ().__init__ ()
267
267
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 )
268
315
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 )
273
318
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 ]
278
367
279
368
def class_is_known (self , classname ):
280
369
return classname in self .idl .classes
@@ -295,22 +384,28 @@ def get_class_def(self, classname):
295
384
bases = "" if not bases else f"({ ', ' .join (bases )} )"
296
385
return f"class { classname } { bases } :"
297
386
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
+
298
397
def get_method_def (self , classname , methodname ):
299
- # Get the corresponding IDL line
300
398
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
307
401
308
402
# Construct preamble
309
403
preamble = "def " + to_snake_case (methodname ) + "("
310
- if "async" in methodname :
404
+ if methodname . endswith ( "_async" ) :
311
405
preamble = "async " + preamble
312
406
313
407
# Get arg names and types
408
+ idl_line = functions [name_idl ]
314
409
args = idl_line .split ("(" , 1 )[1 ].split (")" , 1 )[0 ].split ("," )
315
410
args = [arg .strip () for arg in args if arg .strip ()]
316
411
raw_defaults = [arg .partition ("=" )[2 ].strip () for arg in args ]
@@ -361,28 +456,31 @@ def _arg_from_struct_field(self, field):
361
456
return result
362
457
363
458
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
366
462
367
463
def method_is_known (self , classname , methodname ):
368
464
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
375
467
376
468
def get_class_names (self ):
377
469
return list (self .idl .classes .keys ())
378
470
379
471
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
382
477
383
478
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
386
484
387
485
388
486
class BaseApiPatcher (IdlPatcherMixin , AbstractApiPatcher ):
@@ -398,14 +496,16 @@ def get_class_comment(self, classname):
398
496
return None
399
497
400
498
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 ]
404
503
405
504
def get_method_comment (self , classname , methodname ):
505
+ functions = self .idl .classes [classname ].functions
406
506
name_idl = self .method_is_known (classname , methodname )
407
507
if name_idl :
408
- return " # IDL: " + self . idl . classes [ classname ]. functions [name_idl ]
508
+ return " # IDL: " + functions [name_idl ]
409
509
410
510
411
511
class BackendApiPatcher (AbstractApiPatcher ):
0 commit comments