diff --git a/gpio/__init__.py b/gpio/__init__.py index 566d044..e127d2c 100644 --- a/gpio/__init__.py +++ b/gpio/__init__.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- -__version__ = '1.0.0' +__version__ = "1.0.0" from threading import Lock + try: from collections.abc import Iterable except ImportError: @@ -13,11 +14,11 @@ _open_pins = {} -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 -IN, OUT = 'in', 'out' +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 +IN, OUT = "in", "out" LOW, HIGH = 0, 1 @@ -35,6 +36,7 @@ class GPIOPin(object): Raises: RuntimeError: if pin is already configured """ + def __init__(self, pin, direction=None, initial=LOW, active_low=None): # .configured() will raise a TypeError if "pin" is not convertible to int if GPIOPin.configured(pin, False) is not None: @@ -42,7 +44,7 @@ def __init__(self, pin, direction=None, initial=LOW, active_low=None): self.value = None self.pin = int(pin) - self.root = os.path.join(GPIO_ROOT, 'gpio{0}'.format(self.pin)) + self.root = os.path.join(GPIO_ROOT, "gpio{0}".format(self.pin)) if not os.path.exists(self.root): with _export_lock: @@ -51,7 +53,7 @@ def __init__(self, pin, direction=None, initial=LOW, active_low=None): f.flush() # 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"), "wb+", buffering=0) # I hate manually calling .setup()! self.setup(direction, initial, active_low) @@ -95,46 +97,46 @@ def configured(pin, assert_configured=True): return _open_pins.get(pin) def get_direction(self): - '''Get the direction of pin + """Get the direction of pin 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) as f: return f.read().strip() def set_direction(self, mode): - '''Set the direction of pin + """Set the direction of pin Args: mode (str): use either gpio.OUT or gpio.IN - ''' + """ 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) as f: f.write(str(mode)) f.flush() def set_active_low(self, active_low): - '''Set the polarity of pin + """Set the polarity of pin Args: mode (bool): True = active low / False = active high - ''' + """ 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: - f.write('1' if active_low else '0') + with open(os.path.join(self.root, "active_low"), FMODE) as f: + f.write("1" if active_low else "0") f.flush() def read(self): - '''Read pin value + """Read pin value Returns: int: gpio.HIGH or gpio.LOW - ''' + """ self.value.seek(0) value = self.value.read() try: @@ -147,20 +149,20 @@ def read(self): return int(value) def write(self, value): - '''Write pin value + """Write pin value Args: value (bool): use either gpio.HIGH or gpio.LOW - ''' + """ # write as bytes, about 3x faster than string IO - self.value.write(b'1' if value else b'0') + self.value.write(b"1" if value else b"0") def cleanup(self): - '''Clean up pin + """Clean up pin Unexports the pin and deletes it from the open list. - ''' + """ # Note: I have not put "cleanup" into the __del__ method since it's not # always desirable to unexport pins at program exit. # Additionally "open" can be deleted *before* the GPIOPin instance. @@ -203,7 +205,7 @@ def cleanup(pin=None, assert_exists=False): # TODO RPi.GPIO uses "pull_up_down", does rpio differ? def setup(pins, mode, pullup=None, initial=LOW, active_low=None): - '''Setup pin with mode IN or OUT. + """Setup pin with mode IN or OUT. Args: pin (int): @@ -213,7 +215,7 @@ def setup(pins, mode, pullup=None, initial=LOW, active_low=None): initial (bool, optional): Initial pin value. Default is LOW active_low (bool, optional): Set the pin to active low. Default is None which leaves things as configured in sysfs - ''' + """ if not isinstance(pins, Iterable): pins = [pins] @@ -231,20 +233,20 @@ def setup(pins, mode, pullup=None, initial=LOW, active_low=None): def mode(pin): - '''get the pin mode + """get the pin mode Returns: str: "in" or "out" - ''' + """ return GPIOPin.configured(pin).get_direction() def read(pin): - '''read the pin value + """read the pin value Returns: bool: either gpio.LOW or gpio.HIGH - ''' + """ # These function calls lose us a little speed # but we're already > 2x faster so... # If you want things to be faster use a GPIOPin instance directly. @@ -252,12 +254,12 @@ def read(pin): def write(pin, value): - '''set the pin value to LOW or HIGH + """set the pin value to LOW or HIGH Args: pin (int): any configured pin value (bool): use gpio.LOW or gpio.HIGH - ''' + """ # These function calls lose us a little speed # but we're already > 2x faster so... # If you want things to be faster use a GPIOPin instance directly. diff --git a/tests/conftest.py b/tests/conftest.py index 6ceed2b..537ee22 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -15,5 +15,6 @@ def patch_open(): @pytest.fixture def gpio(): import gpio + yield gpio - del sys.modules['gpio'] \ No newline at end of file + del sys.modules["gpio"] diff --git a/tests/test_gpio.py b/tests/test_gpio.py index 861d0aa..1e107a5 100644 --- a/tests/test_gpio.py +++ b/tests/test_gpio.py @@ -5,22 +5,22 @@ def test_setup_rpio(gpio, patch_open): gpio.setup(10, gpio.OUT) - patch_open.assert_any_call('/sys/class/gpio/export', 'w+') - patch_open().__enter__().write.assert_any_call('10') + patch_open.assert_any_call("/sys/class/gpio/export", "w+") + patch_open().__enter__().write.assert_any_call("10") - patch_open.assert_any_call('/sys/class/gpio/gpio10/value', 'wb+', buffering=0) - patch_open.assert_any_call('/sys/class/gpio/gpio10/direction', 'w+') + patch_open.assert_any_call("/sys/class/gpio/gpio10/value", "wb+", buffering=0) + patch_open.assert_any_call("/sys/class/gpio/gpio10/direction", "w+") patch_open().__enter__().write.assert_any_call(str(gpio.OUT)) def test_setup_class(gpio, patch_open): _ = gpio.GPIOPin(10, gpio.OUT) - patch_open.assert_any_call('/sys/class/gpio/export', 'w+') - patch_open().__enter__().write.assert_any_call('10') + patch_open.assert_any_call("/sys/class/gpio/export", "w+") + patch_open().__enter__().write.assert_any_call("10") - patch_open.assert_any_call('/sys/class/gpio/gpio10/value', 'wb+', buffering=0) - patch_open.assert_any_call('/sys/class/gpio/gpio10/direction', 'w+') + patch_open.assert_any_call("/sys/class/gpio/gpio10/value", "wb+", buffering=0) + patch_open.assert_any_call("/sys/class/gpio/gpio10/direction", "w+") patch_open().__enter__().write.assert_any_call(str(gpio.OUT)) @@ -99,8 +99,8 @@ def test_cleanup_class_unexports_pin(gpio, patch_open): pin.root = "/dev/null" # Pass os.path.exists check pin.cleanup() - patch_open.assert_any_call('/sys/class/gpio/unexport', 'w+') - patch_open().__enter__().write.assert_any_call('10') + patch_open.assert_any_call("/sys/class/gpio/unexport", "w+") + patch_open().__enter__().write.assert_any_call("10") def test_setup_pin_is_not_int(gpio, patch_open): @@ -123,17 +123,21 @@ def test_set_active_low(gpio, patch_open): patch_open.reset_mock() pin.set_active_low(False) - patch_open.assert_has_calls(( - mock.call().__enter__().write('0'), - mock.call().__enter__().flush(), - )) + patch_open.assert_has_calls( + ( + mock.call().__enter__().write("0"), + mock.call().__enter__().flush(), + ) + ) patch_open.reset_mock() pin.set_active_low(True) - patch_open.assert_has_calls(( - mock.call().__enter__().write('1'), - mock.call().__enter__().flush(), - )) + patch_open.assert_has_calls( + ( + mock.call().__enter__().write("1"), + mock.call().__enter__().flush(), + ) + ) with pytest.raises(ValueError): pin.set_active_low(None) @@ -141,28 +145,32 @@ def test_set_active_low(gpio, patch_open): def test_setup_active_low(gpio, patch_open): pin = gpio.GPIOPin(10, gpio.OUT, active_low=False) - patch_open.assert_has_calls(( - mock.call().__enter__().write('0'), - mock.call().__enter__().flush(), - )) + patch_open.assert_has_calls( + ( + mock.call().__enter__().write("0"), + mock.call().__enter__().flush(), + ) + ) pin.cleanup() patch_open.reset_mock() pin = gpio.GPIOPin(10, gpio.OUT, active_low=True) - patch_open.assert_has_calls(( - mock.call().__enter__().write('1'), - mock.call().__enter__().flush(), - )) + patch_open.assert_has_calls( + ( + mock.call().__enter__().write("1"), + mock.call().__enter__().flush(), + ) + ) def test_get_direction(gpio, patch_open): pin = gpio.GPIOPin(10, gpio.IN) - patch_open().__enter__().read.return_value = 'in\n' + patch_open().__enter__().read.return_value = "in\n" assert pin.get_direction() == gpio.IN assert gpio.mode(10) == gpio.IN - patch_open().__enter__().read.return_value = 'out\n' + patch_open().__enter__().read.return_value = "out\n" assert pin.get_direction() == gpio.OUT assert gpio.mode(10) == gpio.OUT @@ -173,9 +181,7 @@ def test_set_direction(gpio, patch_open): for direction in (gpio.IN, gpio.OUT): patch_open.reset_mock() pin.set_direction(direction) - patch_open.assert_has_calls(( - mock.call().__enter__().write(direction), - )) + patch_open.assert_has_calls((mock.call().__enter__().write(direction),)) with pytest.raises(ValueError): pin.set_direction(None) @@ -191,36 +197,28 @@ def test_write(gpio, patch_open): patch_open.reset_mock() pin.write(False) - patch_open.assert_has_calls(( - mock.call().write(b'0'), - )) + patch_open.assert_has_calls((mock.call().write(b"0"),)) patch_open.reset_mock() gpio.write(10, False) - patch_open.assert_has_calls(( - mock.call().write(b'0'), - )) + patch_open.assert_has_calls((mock.call().write(b"0"),)) patch_open.reset_mock() pin.write(True) - patch_open.assert_has_calls(( - mock.call().write(b'1'), - )) + patch_open.assert_has_calls((mock.call().write(b"1"),)) patch_open.reset_mock() gpio.write(10, True) - patch_open.assert_has_calls(( - mock.call().write(b'1'), - )) + patch_open.assert_has_calls((mock.call().write(b"1"),)) def test_read(gpio, patch_open): pin = gpio.GPIOPin(10, gpio.IN) - patch_open().read.return_value = b'1\n' + patch_open().read.return_value = b"1\n" assert pin.read() == gpio.HIGH assert gpio.read(10) == gpio.HIGH - patch_open().read.return_value = b'0\n' + patch_open().read.return_value = b"0\n" assert pin.read() == gpio.LOW assert gpio.read(10) == gpio.LOW