Yesterday, I've come up with the idea of using python context manager to ensure cleanup available here. This time I am using that context manager to make a decorator.The context manager# SafeGPIO.py# updated, warning silencedfrom RPi import GPIOfrom exceptions import RuntimeWarningimport warningsclass SafeGPIO(object):    def __enter__(self):        return GPIO    def __exit__(self, *args, **kwargs):        with warnings.catch_warnings():            warnings.simplefilter(error) #turn warning into exceptions            try:                GPIO.cleanup()            except RuntimeWarning:                pass # silence itThe decorator#decorators.pyfrom . import SafeGPIOfrom RPi import GPIOfrom functools import wrapsdef safe_gpio(func):        This decorator ensure GPIO.cleanup() is called when function call ends,     also it injects GPIO as first argument into your function        @wraps(func) # using wraps preservses doc string    def wrapper(*args, **kwargs):        with SafeGPIO() as GPIO:            return func(GPIO, *args, **kwargs)    return wrapperdef gpio(func):        This decorator injects GPIO as first argument into your function        @wraps(func)    def wrapper(*args, **kwargs):        return func(GPIO, *args, **kwargs)    return wrapperUse like this:# decorator_test.pyfrom SafeGPIO.decorators import safe_gpio, gpiofrom time import sleepfrom random import choice, randintGPIO_PINS = (3,5,7,8,10,11,12,13,15,16,18,19,21,22,23,24,26)VALUES = (True, False)@safe_gpiodef do_random_things_with_pins_for_ten_times(GPIO):    GPIO.setmode(GPIO.BOARD)    for pin in GPIO_PINS:        GPIO.setup(pin, GPIO.OUT)    for _ in xrange(10):        pin = choice(GPIO_PINS)      # choose one of the GPIO pin        value = choice(VALUES)       # output either true or false        sleep_seconds = randint(1,3) # sleep from 1 to 3 seconds        print slected pin %d, output %r, sleep for %d seconds %                (pin, value, sleep_seconds)        GPIO.output(pin, value)        sleep(sleep_seconds)@safe_gpiodef do_real_work(GPIO):    GPIO.setmode(GPIO.BOARD)    GPIO.setup(7, GPIO.OUT)    print doing real work for 5 seconds    GPIO.output(7, True)    sleep(5)@safe_gpio # guarantee to clean up on exitdef main(GPIO):    do_random_things_with_pins_for_ten_times()    do_real_work()if __name__ == '__main__':    main()
Raspberry Pi Safe Clean Up - the decorator way
python;python 2.7;raspberry pi
My other answer says most of the stuff related to your underlying question of how to handle cleanup. In this answer I would like to focus on reviewing your code.Join both versions in one file I think I would like to have both of them in the same file, i.e. SafeGPIO, as that would allow for a slightly simpler structure, and the possibility to add a good description at top of file to properly highlight the options you present on how to do cleanup related to the GPIO.Missing documentation on use cases within module file I would very much appreciate to have the documentation on how to use it within the module itself, possibly with some links to explain why this is a focus areaPossible issues related to type of context manager - Using context manager can introduce issues related to single use, reusable and/or reentrant context managers, which needs some thinking. In your case, you might end up calling the GPIO.cleanup() multiple times, but there shouldn't be any harm in that. In the Technical details of Context Managers, some links are provided related to such issues.Simplify the decorator The decorator version can be simplified somwehat, using something like the following untested code:def cleanup_GPIO_at_end(func):   Decorator function executing func() followed by GPIO.cleanup().   @wraps(func):   def inner_wrapper(*args, **kwargs):       return_value = func(*args, **kwargs)       GPIO.cleanup()       return return_value   return inner_wrapperWhy the injection of GPIO as first argument? I don't see the reasoning of that in your current code, so I'd leave it out, currently.Consider at_exit() approach in addition to other means I commented on this before, and also in my other answer.Rename SafeGPIO to GPIO_cleanup SafeGPIO doesn't convey what the class does, so I would rename it to GPIO_cleanup or CleanupGPIO to better indicate what happens in the background.I've not commented upon the usage tests as I consider those test cases to illustrate usage of the cleanup variants.  And they look nice enough, although I would probably use print as a function, and introduce a little vertical spacing (with comments above instead of after code).Refactored codeHere is my suggestion for SafeGPIO.py:Documentation on how to use this module with use cases...from RPi import GPIOfrom exceptions import RuntimeWarningimport warningsimport atexitclass GPIO_cleanup(object):    ## ... Copy code from SafeGPIO your post here ...def GPIO_cleanup_at_end(func):   Decorator function executing func() followed by GPIO.cleanup().   @wraps(func):   def inner_wrapper(*args, **kwargs):       return_value = func(*args, **kwargs)       GPIO.cleanup()       return return_value   return inner_wrapper# Flag to indicate whether to cleanup or not at module exitdisable_cleanup_at_exit = Falsedisable_cleanup_at_exit(disable = True):    Disable the default behaviour of cleaning up at exit of module.    disable_cleanup_at_exit = disable;@atexit.registerdef cleanup():    if not disable_cleanup_at_exit:        GPIO.cleanup()This would enforce a default cleanup at end of script, unless abnormally terminated, and still allows for the use of with GPIO_cleanup as GPIO: or the decorator @GPIO_cleanup_at_end. And final recommendation, is to use class encapsulation for a larger project to hide the actual pinout allowing for a better focus on the higher level functions of your script.