Generates a wrapper for native __setitem__ method (also works for __delitem__). This is used with the mapping protocol slot. Arguments are taken as *PyObjects and we return a negative C int on error. Create a separate wrapper function for __delitem__ as needed and have the __setite
(cl: ClassIR, fn: FuncIR, emitter: Emitter)
| 628 | |
| 629 | |
| 630 | def generate_set_del_item_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: |
| 631 | """Generates a wrapper for native __setitem__ method (also works for __delitem__). |
| 632 | |
| 633 | This is used with the mapping protocol slot. Arguments are taken as *PyObjects and we |
| 634 | return a negative C int on error. |
| 635 | |
| 636 | Create a separate wrapper function for __delitem__ as needed and have the |
| 637 | __setitem__ wrapper call it if the value is NULL. Return the name |
| 638 | of the outer (__setitem__) wrapper. |
| 639 | """ |
| 640 | method_cls = cl.get_method_and_class("__delitem__") |
| 641 | del_name = None |
| 642 | if method_cls and method_cls[1] == cl: |
| 643 | # Generate a separate wrapper for __delitem__ |
| 644 | del_name = generate_del_item_wrapper(cl, method_cls[0], emitter) |
| 645 | |
| 646 | args = fn.args |
| 647 | if fn.name == "__delitem__": |
| 648 | # Add an extra argument for value that we expect to be NULL. |
| 649 | args = list(args) + [RuntimeArg("___value", object_rprimitive, ARG_POS)] |
| 650 | |
| 651 | name = "{}{}{}".format(DUNDER_PREFIX, "__setitem__", cl.name_prefix(emitter.names)) |
| 652 | input_args = ", ".join(f"PyObject *obj_{arg.name}" for arg in args) |
| 653 | emitter.emit_line(f"static int {name}({input_args}) {{") |
| 654 | |
| 655 | # First check if this is __delitem__ |
| 656 | emitter.emit_line(f"if (obj_{args[2].name} == NULL) {{") |
| 657 | if del_name is not None: |
| 658 | # We have a native implementation, so call it |
| 659 | emitter.emit_line(f"return {del_name}(obj_{args[0].name}, obj_{args[1].name});") |
| 660 | else: |
| 661 | # Try to call superclass method instead |
| 662 | emitter.emit_line(f"PyObject *super = CPy_Super(CPyModule_builtins, obj_{args[0].name});") |
| 663 | emitter.emit_line("if (super == NULL) return -1;") |
| 664 | emitter.emit_line( |
| 665 | 'PyObject *result = PyObject_CallMethod(super, "__delitem__", "O", obj_{});'.format( |
| 666 | args[1].name |
| 667 | ) |
| 668 | ) |
| 669 | emitter.emit_line("Py_DECREF(super);") |
| 670 | emitter.emit_line("Py_XDECREF(result);") |
| 671 | emitter.emit_line("return result == NULL ? -1 : 0;") |
| 672 | emitter.emit_line("}") |
| 673 | |
| 674 | method_cls = cl.get_method_and_class("__setitem__") |
| 675 | if method_cls and method_cls[1] == cl: |
| 676 | generate_set_del_item_wrapper_inner(fn, emitter, args) |
| 677 | else: |
| 678 | emitter.emit_line(f"PyObject *super = CPy_Super(CPyModule_builtins, obj_{args[0].name});") |
| 679 | emitter.emit_line("if (super == NULL) return -1;") |
| 680 | emitter.emit_line("PyObject *result;") |
| 681 | |
| 682 | if method_cls is None and cl.builtin_base is None: |
| 683 | msg = f"'{cl.name}' object does not support item assignment" |
| 684 | emitter.emit_line(f'PyErr_SetString(PyExc_TypeError, "{msg}");') |
| 685 | emitter.emit_line("result = NULL;") |
| 686 | else: |
| 687 | # A base class may have __setitem__ |
nothing calls this directly
no test coverage detected
searching dependent graphs…