| 655 | |
| 656 | |
| 657 | def partial_with_offset(offset=1): |
| 658 | def bound_partial_with_offset(func, *partial_args, **partial_kwargs): |
| 659 | kwargs_partial = partial(func, **partial_kwargs) |
| 660 | |
| 661 | @wraps(func) |
| 662 | def full_partial(*args, **kwargs): |
| 663 | full_args = args[:offset] + partial_args + args[offset:] |
| 664 | return kwargs_partial(*full_args, **kwargs) |
| 665 | |
| 666 | # set correct signature of returned partial |
| 667 | # todo: pass arguments as keywords to partial, instead of this decorator? |
| 668 | # (where args are always the same) |
| 669 | func_sig = signature(full_partial) |
| 670 | parameter_names = tuple(func_sig.parameters) |
| 671 | |
| 672 | partialed_positional_indices = [] |
| 673 | for kwarg in partial_kwargs: |
| 674 | param = func_sig.parameters[kwarg] |
| 675 | if param.default is param.empty: |
| 676 | partialed_positional_indices.append(parameter_names.index(kwarg)) |
| 677 | |
| 678 | partial_params = list(func_sig.parameters.values()) |
| 679 | for index in sorted(partialed_positional_indices, reverse=True): |
| 680 | # appending isn't needed for functionality, but more similar to partial |
| 681 | # and it shows that these arguments can still be updated as kwargs |
| 682 | partial_params.append( |
| 683 | partial_params[index].replace( |
| 684 | kind=Parameter.KEYWORD_ONLY, |
| 685 | default=partial_kwargs[partial_params[index].name], |
| 686 | ) |
| 687 | ) |
| 688 | del partial_params[index] |
| 689 | del partial_params[offset : offset + len(partial_args)] |
| 690 | |
| 691 | full_partial.__signature__ = func_sig.replace(parameters=partial_params) |
| 692 | |
| 693 | return full_partial |
| 694 | |
| 695 | return bound_partial_with_offset |
| 696 | |
| 697 | |
| 698 | state_partial = partial_with_offset() |