patternpythonMinor
Raspberry Pi Safe Clean Up - the decorator way
Viewed 0 times
thewayraspberrysafedecoratorclean
Problem
Yesterday, I've come up with the idea of using python context manager to ensure
The context manager
The decorator
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,
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 itThe 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 wrapperUse 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
As you are using
Now it's a simple one-liner.
If you don't need
Ways I would change the program:
-
Adding an option to
-
Merging
Less functions to remember, whilst keeping the option for it to be safe.
-
You don't use
If your response is no, you should consider removing it.
As you could just
And it seems like a lot of overhead for importing GPIO.
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.