From e1f3783ed07682e62761550b892801b8d8bb216f Mon Sep 17 00:00:00 2001 From: Andrea Mistrali Date: Mon, 17 Jun 2024 12:06:04 +0200 Subject: [PATCH] Improvements No more docopt dependency, switched to argparse. Some extra cleanups --- poetry.lock | 19 +++++++---- pulses/cli.py | 41 ++++++++++++----------- pulses/cli_parser.py | 78 ++++++++++++++++++++++++++++++++++++++++++++ pulses/pulses.py | 28 ++++++++++------ pyproject.toml | 2 +- 5 files changed, 129 insertions(+), 39 deletions(-) create mode 100644 pulses/cli_parser.py diff --git a/poetry.lock b/poetry.lock index 2a27a40..434793f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,16 +1,21 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] -name = "docopt" -version = "0.6.2" -description = "Pythonic argument parser, that will make you smile" +name = "rpi-gpio" +version = "0.7.1" +description = "A module to control Raspberry Pi GPIO channels" optional = false python-versions = "*" files = [ - {file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"}, + {file = "RPi.GPIO-0.7.1-cp27-cp27mu-linux_armv6l.whl", hash = "sha256:b86b66dc02faa5461b443a1e1f0c1d209d64ab5229696f32fb3b0215e0600c8c"}, + {file = "RPi.GPIO-0.7.1-cp310-cp310-linux_armv6l.whl", hash = "sha256:57b6c044ef5375a78c8dda27cdfadf329e76aa6943cd6cffbbbd345a9adf9ca5"}, + {file = "RPi.GPIO-0.7.1-cp37-cp37m-linux_armv6l.whl", hash = "sha256:77afb817b81331ce3049a4b8f94a85e41b7c404d8e56b61ac0f1eb75c3120868"}, + {file = "RPi.GPIO-0.7.1-cp38-cp38-linux_armv6l.whl", hash = "sha256:29226823da8b5ccb9001d795a944f2e00924eeae583490f0bc7317581172c624"}, + {file = "RPi.GPIO-0.7.1-cp39-cp39-linux_armv6l.whl", hash = "sha256:15311d3b063b71dee738cd26570effc9985a952454d162937c34e08c0fc99902"}, + {file = "RPi.GPIO-0.7.1.tar.gz", hash = "sha256:cd61c4b03c37b62bba4a5acfea9862749c33c618e0295e7e90aa4713fb373b70"}, ] [metadata] lock-version = "2.0" -python-versions = "^3.11" -content-hash = "020d3e03335e70ca8b1cfd769838e50546a63b9ed4e4034ba584fd3a3ec497fd" +python-versions = ">=3.9,<4.0" +content-hash = "5b3b5679f5c6b9e375be39f892efbaabceb43d1d7a346c78386ad876cfa286be" diff --git a/pulses/cli.py b/pulses/cli.py index 07e3939..e3e5f27 100644 --- a/pulses/cli.py +++ b/pulses/cli.py @@ -16,62 +16,61 @@ Options: -M --max= Maximum value [default: 50] -D --delay-val= Base delay value [default: 0.01] - -V --verbose Verbose mode [default: True] + -V --verbose Verbose mode [default: False] -h --help Show help. -v --version Show version. -""" # NOQA +""" # NOQA import sys import time import logging import signal -from docopt import docopt +# from docopt import docopt +from pulses.cli_parser import parser from pulses import VERSION, ledPulse -logging.basicConfig(format="%(asctime)-15s %(levelname)5s %(name)s[%(process)d] %(threadName)s: %(message)s") - - -def signal_handler(sig, frame): - print('stopping pulse...', end="", flush=True) - led.stop() - led.join() - print('done.\nBailing out') - sys.exit(0) +logging.basicConfig( + format="%(asctime)-15s %(levelname)5s %(name)s[%(process)d] %(threadName)s: %(message)s") def main(): - global led + def signal_handler(sig: int, frame): + print('stopping pulse...', end="", flush=True) + led.stop() + led.join() + print('done.\nBailing out') + sys.exit(0) - args = docopt(__doc__, version=f'pulses {VERSION}') + args = parser.parse_args() signal.signal(signal.SIGINT, signal_handler) log = logging.getLogger() log.setLevel('WARNING') - if args.get('--verbose'): + if args.verbose: log.setLevel('DEBUG') log.info('setting verbose mode') - led = ledPulse(int(args.get('--gpio'))) + led = ledPulse(int(args.gpio)) params = {} for method in ['initial', 'loop', 'final']: - methodName = args.get(f"--{method}") + methodName = getattr(args, method) if methodName not in led.valueMethods: print(f"error: no value method '{methodName}' defined") sys.exit(1) params[f"{method}Method"] = methodName - methodName = args.get("--delay") + methodName = args.delay if methodName not in led.delayMethods: print(f"error: no delay method '{methodName}' defined") sys.exit(1) params['delayMethod'] = methodName - params['min'] = int(args.get('--min')) - params['max'] = int(args.get('--max')) - params['delay'] = float(args.get('--delay-val')) + params['min'] = args.min + params['max'] = args.max + params['delay'] = args.delay_val print("-" * 20) print("pulses CLI test tool") diff --git a/pulses/cli_parser.py b/pulses/cli_parser.py new file mode 100644 index 0000000..4a3e50c --- /dev/null +++ b/pulses/cli_parser.py @@ -0,0 +1,78 @@ +""" +Usage: + pulses [--gpio ] [--initial ] [--loop ] + [--final ] [--delay ] + [--min ] [--max ] [--delay-val ] + [--verbose] + +Options: + -g --gpio= GPIO to use [default: 12] + -i --initial= Initial method [default: linear] + -l --loop= Loop method [default: sin] + -f --final= Final method [default: linear] + -d --delay= Delay method [default: constant] + + -m --min= Minimum value [default: 2] + -M --max= Maximum value [default: 50] + -D --delay-val= Base delay value [default: 0.01] + + -V --verbose Verbose mode [default: True] + + -h --help Show help. + -v --version Show version. + +""" # NOQA + +import argparse + +from pulses import VERSION + +epilogue = """ +""" + + +class Formatter(argparse.RawDescriptionHelpFormatter, + argparse.ArgumentDefaultsHelpFormatter): + """ + We want to show the default values and show the description + and epilogue not reformatted, so we create our own formatter class + """ + pass + + +parser = argparse.ArgumentParser(prog="pulses", + description="Pulse your LED(s)", + epilog=epilogue, + formatter_class=Formatter) + +parser.add_argument('-g', '--gpio', type=int, default=12, + metavar="", + help="GPIO to use") +parser.add_argument('-i', '--initial', type=str, default='linear', + metavar="", + help="Initial method") +parser.add_argument('-l', '--loop', type=str, default='sin', + metavar="", + help="Loop method") +parser.add_argument('-f', '--final', type=str, default='linear', + metavar="", + help="Final method") +parser.add_argument('-d', '--delay', type=str, default='constant', + metavar="", + help="Delay method") + +parser.add_argument('-m', '--min', type=int, default=2, + metavar="", + help="Minimum value") +parser.add_argument('-M', '--max', type=int, default=50, + metavar="", + help="Max value") +parser.add_argument('-D', '--delay-val', type=float, default=0.01, + metavar="", + help="Base delay value") + +parser.add_argument('-V', '--verbose', action='store_true', default=False, + help="Verbose mode") + +parser.add_argument('--version', action='version', + version=f'%(prog)s {VERSION}') diff --git a/pulses/pulses.py b/pulses/pulses.py index f056200..36fc338 100644 --- a/pulses/pulses.py +++ b/pulses/pulses.py @@ -4,16 +4,17 @@ import time import threading from queue import Queue +from typing import Callable from .methods import * # NOQA class ledPulse(threading.Thread): - delayMethods = ['constant', 'sin', 'cos'] - valueMethods = ['on', 'off', 'linear', 'vshape', 'sin', 'cos'] - methods = {'delay': {}, 'value': {}} + delayMethods: list = ['constant', 'sin', 'cos'] + valueMethods: list = ['on', 'off', 'linear', 'vshape', 'sin', 'cos'] + methods: dict = {'delay': {}, 'value': {}} - defaultSet = { + defaultSet: dict = { 'min': 2, 'max': 50, 'delay': 0.01, @@ -22,8 +23,15 @@ class ledPulse(threading.Thread): 'loopMethod': 'linear', 'finalMethod': None } + initialMethod: str + loopMethod: str + finalMethod: str + delayMethod: str + min: int + max: int + delay: float - def __init__(self, gpio, name="led0"): + def __init__(self, gpio: int, name: str = "led0") -> None: super().__init__(name=name, daemon=True) @@ -69,7 +77,7 @@ class ledPulse(threading.Thread): self.model = sys.platform try: with open('/sys/firmware/devicetree/base/model', 'r') as m: - model = m.read().strip() + model = m.read()[:-1] if model.lower().startswith('raspberry pi'): self.supported = True self.model = model @@ -97,7 +105,7 @@ class ledPulse(threading.Thread): return (self.supported, sys.platform) - def set(self, **kwargs): + def set(self, **kwargs: dict): for key, value in kwargs.items(): if key in self.defaultSet.keys(): @@ -198,13 +206,13 @@ class ledPulse(threading.Thread): # in the loop we are and is added as a method to self, so # it has access to all the properties of self: max, min, delay ### - def register_value_method(self, name, fn): + def register_value_method(self, name: str, fn: Callable): self.register_method('value', name, fn) - def register_delay_method(self, name, fn): + def register_delay_method(self, name: str, fn: Callable): self.register_method('delay', name, fn) - def register_method(self, kind, name, fn): + def register_method(self, kind: str, name: str, fn: Callable): if kind not in ['delay', 'value']: self.log.warning(f"unknown kind {kind}") return diff --git a/pyproject.toml b/pyproject.toml index 86b4f58..5514930 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ readme = "README.md" [tool.poetry.dependencies] python = ">=3.9,<4.0" -docopt = "^0.6.2" +rpi-gpio = {version = "^0.7.1", platform = "linux"} [tool.poetry.scripts] pulses = "pulses.cli:main" -- 2.40.1