HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Raspberry Pi Safe Clean Up - the decorator way

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
thewayraspberrysafedecoratorclean

Problem

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 silenced

from RPi import GPIO
from exceptions import RuntimeWarning
import warnings

class 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 it


The decorator

#decorators.py

from . import SafeGPIO
from RPi import GPIO
from functools import wraps

def 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 wrapper

def gpio(func):
    """
    This decorator injects GPIO as first argument into your function
    """
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(GPIO, *args, **kwargs)
    return wrapper


Use like this:

```
# decorator_test.py

from SafeGPIO.decorators import safe_gpio, gpio
from time import sleep
from random import choice, randint

GPIO_PINS = (3,5,7,8,10,11,12,13,15,16,18,19,21,22,23,24,26)
VALUES = (True, False)

@safe_gpio
def 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,

Solution

If you want to do gpio in one line, you can use functools.partial and either functools.wraps or functools.update_wrapper.

As you are using wraps already, you may as well use partial too.

def gpio(func):
    return update_wrapper(partial(func, GPIO), func)


Now it's a simple one-liner.
If you don't need @wraps, this can be further simplified to:

def gpio(func):
    return partial(func, GPIO)


Ways I would change the program:

-
Adding an option to SafeGPIO to have the option to handle errors.

-
Merging gpio and safe_gpio into one function would improve my user experience.

Less functions to remember, whilst keeping the option for it to be safe.

-
You don't use @gpio, so would you ever use it?

If your response is no, you should consider removing it.
As you could just import GPIO.
And it seems like a lot of overhead for importing GPIO.

Code Snippets

def gpio(func):
    return update_wrapper(partial(func, GPIO), func)
def gpio(func):
    return partial(func, GPIO)

Context

StackExchange Code Review Q#113605, answer score: 3

Revisions (0)

No revisions yet.