Good interface for reverting effect of function In my job, we often write functions that do some calculation and write the result to a database. Sometimes mistakes are made and wrong stuff ends up being saved, therefore we also want to supply an interface to clean up after the function. This is an attempt of solving that problem.
from functools import wraps

def revert(func):
    if not hasattr(func,'_revert'):
        raise NotImplementedError(func.__name__ + &quot; does not have a revert registered&quot;)

    @wraps(func._revert)
    def wrapper(*args,**kwargs):
        return func._revert(*args,**kwargs)
    return wrapper

def register_revert(revert):
    def revert_added(func):
        func._revert = revert

        @wraps(func)
        def wrapper(*args,**kwargs):
            return func(*args,**kwargs)
        return wrapper
    return revert_added

def _is_it_a_str(maybe_str):
    if not isinstance(maybe_str,str):
         raise ValueError(&quot;I need a string&quot;)

def _revert_stuff(a_str):
    _is_it_a_str(a_str)
    print('revert stuff with ' + a_str)

@register_revert(_revert_stuff)
def do_stuff(a_str):
    _is_it_a_str(a_str)
    print('doing stuff with ' + a_str)


do_stuff('something')
# Oops:
revert(do_stuff)('something')

The idea is to write your function doing your job - do_something in this case. And then when you are ready to take it to the next level, you write its revert, register it and you are done.
I like that

the solution does not require changing anything in the main functionality.
you can use the same identical interface for many functions
the syntax reads relatively intuitively

I think it is inconvenient that

I need to maintain two identical interfaces - the one on do_stuff and _revert_stuff needs to be identical (or at least _revert_stuff needs to work getting the one from do_stuff).
sometimes I want to type-check the arguments which I then also have to repeat (exemplified here with _is_it_a_str). I thought about moving that to another decorator, but I'm afraid it gets too confusing.

Edit: We use the newest stable version of Python
p.s. My first question tried my best. Critique welcome!
def revert(func):
    @wraps(func._revert)
    def wrapper(*args,**kwargs):
        return func._revert(*args,**kwargs)
    return wrapper

You are returning a wrapper, which takes arguments, calls a function, and returns results...
I'm pretty sure that is just a long way of writing:
def revert(func):
    return func._revert

This begs the question: why store the revert function as func._revert, when you could store it as func.revert?  Then instead of:
revert(do_stuff)('something')

you could write:
do_stuff.revert('something')

which is one character shorter.  Even better: autocomplete can tell you whether there is a .revert member for do_stuff, which is not possible using revert(...).