diff --git a/gpio/__init__.py b/gpio/__init__.py index c6f37f7..10cf9d3 100644 --- a/gpio/__init__.py +++ b/gpio/__init__.py @@ -2,6 +2,7 @@ __version__ = '1.0.0' from threading import Lock +from time import sleep, time try: from collections.abc import Iterable except ImportError: @@ -16,7 +17,9 @@ GPIO_ROOT = '/sys/class/gpio' GPIO_EXPORT = os.path.join(GPIO_ROOT, 'export') GPIO_UNEXPORT = os.path.join(GPIO_ROOT, 'unexport') -FMODE = 'w+' # w+ overwrites and truncates existing files +FMODE_STR_WO = 'w' # /sys/class/gpio/export is not readable, even by root +FMODE_STR_RW = 'w+' # w+ overwrites and truncates existing files +FMODE_BIN_RW = 'wb+' # Using unbuffered binary IO is ~ 3x faster than text IN, OUT = 'in', 'out' LOW, HIGH = 0, 1 @@ -46,12 +49,18 @@ def __init__(self, pin, direction=None, initial=LOW, active_low=None): if not os.path.exists(self.root): with _export_lock: - with open(GPIO_EXPORT, FMODE) as f: + with open(GPIO_EXPORT, FMODE_STR_WO) as f: f.write(str(self.pin)) f.flush() + gpio_gid = os.stat(GPIO_ROOT).st_gid + value_path = os.path.join(self.root, 'value') + start = time() + # give udev a moment to update the group of newly created files + while os.stat(value_path).st_gid != gpio_gid and time() - start < .5: + sleep(.01) # Using unbuffered binary IO is ~ 3x faster than text - self.value = open(os.path.join(self.root, 'value'), 'wb+', buffering=0) + self.value = open(os.path.join(self.root, 'value'), FMODE_BIN_RW, buffering=0) # I hate manually calling .setup()! self.setup(direction, initial, active_low) @@ -100,7 +109,7 @@ def get_direction(self): Returns: str: "in" or "out" ''' - with open(os.path.join(self.root, 'direction'), FMODE) as f: + with open(os.path.join(self.root, 'direction'), FMODE_STR_RW) as f: return f.read().strip() def set_direction(self, mode): @@ -112,7 +121,7 @@ def set_direction(self, mode): if mode not in (IN, OUT, LOW, HIGH): raise ValueError("Unsupported pin mode {}".format(mode)) - with open(os.path.join(self.root, 'direction'), FMODE) as f: + with open(os.path.join(self.root, 'direction'), FMODE_STR_RW) as f: f.write(str(mode)) f.flush() @@ -125,7 +134,7 @@ def set_active_low(self, active_low): if not isinstance(active_low, bool): raise ValueError("active_low must be True or False") - with open(os.path.join(self.root, 'active_low'), FMODE) as f: + with open(os.path.join(self.root, 'active_low'), FMODE_STR_RW) as f: f.write('1' if active_low else '0') f.flush() @@ -168,7 +177,7 @@ def cleanup(self): if os.path.exists(self.root): with _export_lock: - with open(GPIO_UNEXPORT, FMODE) as f: + with open(GPIO_UNEXPORT, FMODE_STR_WO) as f: f.write(str(self.pin)) f.flush()