10
10
or methods in a single compilation unit.
11
11
"""
12
12
13
- from typing import List , Optional
13
+ from typing import List , Optional , Sequence
14
14
15
15
from mypy .nodes import ARG_POS , ARG_OPT , ARG_NAMED_OPT , ARG_NAMED , ARG_STAR , ARG_STAR2
16
16
@@ -350,6 +350,29 @@ def generate_hash_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str:
350
350
return name
351
351
352
352
353
+ def generate_len_wrapper (cl : ClassIR , fn : FuncIR , emitter : Emitter ) -> str :
354
+ """Generates a wrapper for native __len__ methods."""
355
+ name = '{}{}{}' .format (DUNDER_PREFIX , fn .name , cl .name_prefix (emitter .names ))
356
+ emitter .emit_line ('static Py_ssize_t {name}(PyObject *self) {{' .format (
357
+ name = name
358
+ ))
359
+ emitter .emit_line ('{}retval = {}{}{}(self);' .format (emitter .ctype_spaced (fn .ret_type ),
360
+ emitter .get_group_prefix (fn .decl ),
361
+ NATIVE_PREFIX ,
362
+ fn .cname (emitter .names )))
363
+ emitter .emit_error_check ('retval' , fn .ret_type , 'return -1;' )
364
+ if is_int_rprimitive (fn .ret_type ):
365
+ emitter .emit_line ('Py_ssize_t val = CPyTagged_AsSsize_t(retval);' )
366
+ else :
367
+ emitter .emit_line ('Py_ssize_t val = PyLong_AsSsize_t(retval);' )
368
+ emitter .emit_dec_ref ('retval' , fn .ret_type )
369
+ emitter .emit_line ('if (PyErr_Occurred()) return -1;' )
370
+ emitter .emit_line ('return val;' )
371
+ emitter .emit_line ('}' )
372
+
373
+ return name
374
+
375
+
353
376
def generate_bool_wrapper (cl : ClassIR , fn : FuncIR , emitter : Emitter ) -> str :
354
377
"""Generates a wrapper for native __bool__ methods."""
355
378
name = '{}{}{}' .format (DUNDER_PREFIX , fn .name , cl .name_prefix (emitter .names ))
@@ -370,6 +393,134 @@ def generate_bool_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str:
370
393
return name
371
394
372
395
396
+ def generate_del_item_wrapper (cl : ClassIR , fn : FuncIR , emitter : Emitter ) -> str :
397
+ """Generates a wrapper for native __delitem__.
398
+
399
+ This is only called from a combined __delitem__/__setitem__ wrapper.
400
+ """
401
+ name = '{}{}{}' .format (DUNDER_PREFIX , '__delitem__' , cl .name_prefix (emitter .names ))
402
+ input_args = ', ' .join ('PyObject *obj_{}' .format (arg .name ) for arg in fn .args )
403
+ emitter .emit_line ('static int {name}({input_args}) {{' .format (
404
+ name = name ,
405
+ input_args = input_args ,
406
+ ))
407
+ generate_set_del_item_wrapper_inner (fn , emitter , fn .args )
408
+ return name
409
+
410
+
411
+ def generate_set_del_item_wrapper (cl : ClassIR , fn : FuncIR , emitter : Emitter ) -> str :
412
+ """Generates a wrapper for native __setitem__ method (also works for __delitem__).
413
+
414
+ This is used with the mapping protocol slot. Arguments are taken as *PyObjects and we
415
+ return a negative C int on error.
416
+
417
+ Create a separate wrapper function for __delitem__ as needed and have the
418
+ __setitem__ wrapper call it if the value is NULL. Return the name
419
+ of the outer (__setitem__) wrapper.
420
+ """
421
+ method_cls = cl .get_method_and_class ('__delitem__' )
422
+ del_name = None
423
+ if method_cls and method_cls [1 ] == cl :
424
+ # Generate a separate wrapper for __delitem__
425
+ del_name = generate_del_item_wrapper (cl , method_cls [0 ], emitter )
426
+
427
+ args = fn .args
428
+ if fn .name == '__delitem__' :
429
+ # Add an extra argument for value that we expect to be NULL.
430
+ args = list (args ) + [RuntimeArg ('___value' , object_rprimitive , ARG_POS )]
431
+
432
+ name = '{}{}{}' .format (DUNDER_PREFIX , '__setitem__' , cl .name_prefix (emitter .names ))
433
+ input_args = ', ' .join ('PyObject *obj_{}' .format (arg .name ) for arg in args )
434
+ emitter .emit_line ('static int {name}({input_args}) {{' .format (
435
+ name = name ,
436
+ input_args = input_args ,
437
+ ))
438
+
439
+ # First check if this is __delitem__
440
+ emitter .emit_line ('if (obj_{} == NULL) {{' .format (args [2 ].name ))
441
+ if del_name is not None :
442
+ # We have a native implementation, so call it
443
+ emitter .emit_line ('return {}(obj_{}, obj_{});' .format (del_name ,
444
+ args [0 ].name ,
445
+ args [1 ].name ))
446
+ else :
447
+ # Try to call superclass method instead
448
+ emitter .emit_line (
449
+ 'PyObject *super = CPy_Super(CPyModule_builtins, obj_{});' .format (args [0 ].name ))
450
+ emitter .emit_line ('if (super == NULL) return -1;' )
451
+ emitter .emit_line (
452
+ 'PyObject *result = PyObject_CallMethod(super, "__delitem__", "O", obj_{});' .format (
453
+ args [1 ].name ))
454
+ emitter .emit_line ('Py_DECREF(super);' )
455
+ emitter .emit_line ('Py_XDECREF(result);' )
456
+ emitter .emit_line ('return result == NULL ? -1 : 0;' )
457
+ emitter .emit_line ('}' )
458
+
459
+ method_cls = cl .get_method_and_class ('__setitem__' )
460
+ if method_cls and method_cls [1 ] == cl :
461
+ generate_set_del_item_wrapper_inner (fn , emitter , args )
462
+ else :
463
+ emitter .emit_line (
464
+ 'PyObject *super = CPy_Super(CPyModule_builtins, obj_{});' .format (args [0 ].name ))
465
+ emitter .emit_line ('if (super == NULL) return -1;' )
466
+ emitter .emit_line ('PyObject *result;' )
467
+
468
+ if method_cls is None and cl .builtin_base is None :
469
+ msg = "'{}' object does not support item assignment" .format (cl .name )
470
+ emitter .emit_line (
471
+ 'PyErr_SetString(PyExc_TypeError, "{}");' .format (msg ))
472
+ emitter .emit_line ('result = NULL;' )
473
+ else :
474
+ # A base class may have __setitem__
475
+ emitter .emit_line (
476
+ 'result = PyObject_CallMethod(super, "__setitem__", "OO", obj_{}, obj_{});' .format (
477
+ args [1 ].name , args [2 ].name ))
478
+ emitter .emit_line ('Py_DECREF(super);' )
479
+ emitter .emit_line ('Py_XDECREF(result);' )
480
+ emitter .emit_line ('return result == NULL ? -1 : 0;' )
481
+ emitter .emit_line ('}' )
482
+ return name
483
+
484
+
485
+ def generate_set_del_item_wrapper_inner (fn : FuncIR , emitter : Emitter ,
486
+ args : Sequence [RuntimeArg ]) -> None :
487
+ for arg in args :
488
+ generate_arg_check (arg .name , arg .type , emitter , 'goto fail;' , False )
489
+ native_args = ', ' .join ('arg_{}' .format (arg .name ) for arg in args )
490
+ emitter .emit_line ('{}val = {}{}({});' .format (emitter .ctype_spaced (fn .ret_type ),
491
+ NATIVE_PREFIX ,
492
+ fn .cname (emitter .names ),
493
+ native_args ))
494
+ emitter .emit_error_check ('val' , fn .ret_type , 'goto fail;' )
495
+ emitter .emit_dec_ref ('val' , fn .ret_type )
496
+ emitter .emit_line ('return 0;' )
497
+ emitter .emit_label ('fail' )
498
+ emitter .emit_line ('return -1;' )
499
+ emitter .emit_line ('}' )
500
+
501
+
502
+ def generate_contains_wrapper (cl : ClassIR , fn : FuncIR , emitter : Emitter ) -> str :
503
+ """Generates a wrapper for a native __contains__ method."""
504
+ name = '{}{}{}' .format (DUNDER_PREFIX , fn .name , cl .name_prefix (emitter .names ))
505
+ emitter .emit_line (
506
+ 'static int {name}(PyObject *self, PyObject *obj_item) {{' .
507
+ format (name = name ))
508
+ generate_arg_check ('item' , fn .args [1 ].type , emitter , 'return -1;' , False )
509
+ emitter .emit_line ('{}val = {}{}(self, arg_item);' .format (emitter .ctype_spaced (fn .ret_type ),
510
+ NATIVE_PREFIX ,
511
+ fn .cname (emitter .names )))
512
+ emitter .emit_error_check ('val' , fn .ret_type , 'return -1;' )
513
+ if is_bool_rprimitive (fn .ret_type ):
514
+ emitter .emit_line ('return val;' )
515
+ else :
516
+ emitter .emit_line ('int boolval = PyObject_IsTrue(val);' )
517
+ emitter .emit_dec_ref ('val' , fn .ret_type )
518
+ emitter .emit_line ('return boolval;' )
519
+ emitter .emit_line ('}' )
520
+
521
+ return name
522
+
523
+
373
524
# Helpers
374
525
375
526
0 commit comments