From fcb4c8ce3d440a6d6303209bded0eda908517c28 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Sat, 19 Oct 2024 14:42:31 +1000 Subject: [PATCH 1/4] tools: provision: run without `simple_term_menu` Support running without `simple_term_menu`, which does not work on Windows. All options must be provided on the command line in that case. Signed-off-by: Jordan Yates --- src/infuse_iot/tools/provision.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/infuse_iot/tools/provision.py b/src/infuse_iot/tools/provision.py index 7b4a729..9b58af1 100644 --- a/src/infuse_iot/tools/provision.py +++ b/src/infuse_iot/tools/provision.py @@ -9,7 +9,10 @@ from http import HTTPStatus import sys -from simple_term_menu import TerminalMenu +try: + from simple_term_menu import TerminalMenu +except NotImplementedError: + TerminalMenu = None from pynrfjprog import LowLevel, Parameters @@ -91,6 +94,8 @@ def nrf_device_info(self, api: LowLevel.API) -> tuple[int, int]: sys.exit(f"Unknown device: {device_info[1]}") soc = socs[device_info[1]] + uicr_addr = None + dev_id = None for desc in api.read_memory_descriptors(): if desc.type == Parameters.MemoryType.UICR: uicr_addr = desc.start + customer_offsets[family] @@ -104,16 +109,24 @@ def nrf_device_info(self, api: LowLevel.API) -> tuple[int, int]: def create_device(self, client, soc, hardware_id_str): if self._org is None: orgs = get_all_organisations.sync(client=client) - options = [f"{o.name:20s} ({o.id})" for o in orgs] + + if TerminalMenu is None: + sys.exit( + "Specify organisation with --organisation:\n" + "\n".join(options) + ) + terminal_menu = TerminalMenu(options) idx = terminal_menu.show() self._org = orgs[idx].id if self._board is None: boards = get_boards.sync(client=client, organisation_id=self._org) - options = [f"{b.name:20s} ({b.id})" for b in boards] + + if TerminalMenu is None: + sys.exit("Specify board with --board:\n" + "\n".join(options)) + terminal_menu = TerminalMenu(options) idx = terminal_menu.show() self._board = boards[idx].id From 8ef90ceffc6016042919bf659cfd044b6e8189ca Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Wed, 16 Oct 2024 18:30:43 +1000 Subject: [PATCH 2/4] requirements: update to pass `tox` checks Add missing requirements. Signed-off-by: Jordan Yates --- requirements.txt | 4 ++++ setup.py | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/requirements.txt b/requirements.txt index a87cd8d..e9de4bb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,13 @@ argcomplete attrs +cryptography colorama httpx pylink-square pynrfjprog pyserial +python-dateutil +simple-term-menu tabulate +typing-extensions keyring diff --git a/setup.py b/setup.py index 54cab2b..91ccf15 100644 --- a/setup.py +++ b/setup.py @@ -22,12 +22,16 @@ install_requires=[ "argcomplete", "attrs", + "cryptography", "colorama", "httpx", "pylink-square", "pynrfjprog", "pyserial", + "python-dateutil", + "simple-term-menu", "tabulate", + "typing-extensions", "keyring", ], python_requires=">=3.10", From 85896969d48d1d79c4971655dc281a67bba3173b Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Wed, 16 Oct 2024 18:33:23 +1000 Subject: [PATCH 3/4] app: main: add `--version` command Add `--version` command and format files. Signed-off-by: Jordan Yates --- src/infuse_iot/app/main.py | 8 +++++++- src/infuse_iot/version.py | 10 ++++++++++ src/infuse_iot/zephyr/net_if.py | 3 +++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 src/infuse_iot/version.py diff --git a/src/infuse_iot/app/main.py b/src/infuse_iot/app/main.py index 8e64430..cf8890d 100644 --- a/src/infuse_iot/app/main.py +++ b/src/infuse_iot/app/main.py @@ -14,6 +14,7 @@ import infuse_iot.tools from infuse_iot.commands import InfuseCommand +from infuse_iot.version import __version__ class InfuseApp: @@ -22,6 +23,9 @@ class InfuseApp: def __init__(self): self.args = None self.parser = argparse.ArgumentParser("infuse") + self.parser.add_argument( + "--version", action="version", version=f"{__version__}" + ) self._tools = {} # Load tools self._load_tools(self.parser) @@ -36,7 +40,9 @@ def run(self, argv): tool.run() def _load_tools(self, parser: argparse.ArgumentParser): - tools_parser = parser.add_subparsers(title="commands", metavar="", required=True) + tools_parser = parser.add_subparsers( + title="commands", metavar="", required=True + ) # Iterate over tools for _, name, _ in pkgutil.walk_packages(infuse_iot.tools.__path__): diff --git a/src/infuse_iot/version.py b/src/infuse_iot/version.py new file mode 100644 index 0000000..9de6b67 --- /dev/null +++ b/src/infuse_iot/version.py @@ -0,0 +1,10 @@ +# Copyright (c) 2019, Nordic Semiconductor ASA +# +# Don't put anything else in here! +# +# This is the Python 3 version of option 3 in: +# https://packaging.python.org/guides/single-sourcing-package-version/#single-sourcing-the-version + +import importlib.metadata + +__version__ = importlib.metadata.version("infuse_iot") diff --git a/src/infuse_iot/zephyr/net_if.py b/src/infuse_iot/zephyr/net_if.py index e9ec7ed..1bb9612 100644 --- a/src/infuse_iot/zephyr/net_if.py +++ b/src/infuse_iot/zephyr/net_if.py @@ -2,6 +2,7 @@ import enum + class OperationalState(enum.IntEnum): UNKNOWN = 0 NOTPRESENT = 1 @@ -11,6 +12,7 @@ class OperationalState(enum.IntEnum): DORMANT = 5 UP = 6 + class InterfaceFlags(enum.Flag): UP = 0x0001 POINTOPOINT = 0x0002 @@ -27,6 +29,7 @@ class InterfaceFlags(enum.Flag): IPV6_NO_MLD = 0x1000 NO_TX_LOCK = 0x2000 + class L2Flags(enum.Flag): MULTICAST = 0x0001 MULTICAST_SKIP_SOLICIT = 0x0002 From 92847eee8ad74956e179f13bb2c1c842b7b9f928 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Wed, 16 Oct 2024 18:34:05 +1000 Subject: [PATCH 4/4] ci: workflows: test: added Add minimal tests for installing and running from the command line. Signed-off-by: Jordan Yates --- .github/workflows/test.yml | 30 ++++++++++++++++++++++++++++++ tests/test_help.py | 16 ++++++++++++++++ tests/test_main.py | 22 ++++++++++++++++++++++ tox.ini | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+) create mode 100644 .github/workflows/test.yml create mode 100644 tests/test_help.py create mode 100644 tests/test_main.py create mode 100644 tox.ini diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..3643c6d --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,30 @@ +--- +name: Python Test + +# yamllint disable-line rule:truthy +on: [push, pull_request] + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + python-version: ['3.10', '3.11', '3.12', '3.13'] + steps: + - uses: actions/checkout@v4 + # This is enough to find many quoting issues + with: + path: "./check out" + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Display Python version + run: python -c "import sys; print(sys.version); print(sys.platform)" + - name: install tox + run: pip3 install tox + - name: tox + run: tox -c 'check out' diff --git a/tests/test_help.py b/tests/test_help.py new file mode 100644 index 0000000..e01f378 --- /dev/null +++ b/tests/test_help.py @@ -0,0 +1,16 @@ +import os +import subprocess +import sys + +assert "TOXTEMPDIR" in os.environ, "you must run these tests using tox" + + +def test_help(): + # A quick check that the package can be executed as a module which + # takes arguments, using e.g. "python3 -m west --version" to + # produce the same results as "west --version", and that both are + # sane (i.e. the actual version number is printed instead of + # simply an error message to stderr). + + subprocess.check_output([sys.executable, "-m", "infuse_iot", "--help"]) + subprocess.check_output(["infuse", "--help"]) diff --git a/tests/test_main.py b/tests/test_main.py new file mode 100644 index 0000000..5e01fb6 --- /dev/null +++ b/tests/test_main.py @@ -0,0 +1,22 @@ +import os +import subprocess +import sys + +import infuse_iot.version + +assert "TOXTEMPDIR" in os.environ, "you must run these tests using tox" + + +def test_main(): + # A quick check that the package can be executed as a module which + # takes arguments, using e.g. "python3 -m west --version" to + # produce the same results as "west --version", and that both are + # sane (i.e. the actual version number is printed instead of + # simply an error message to stderr). + + output_as_module = subprocess.check_output( + [sys.executable, "-m", "infuse_iot", "--version"] + ).decode() + output_directly = subprocess.check_output(["infuse", "--version"]).decode() + assert infuse_iot.version.__version__ in output_as_module + assert output_as_module == output_directly diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..6b1f02e --- /dev/null +++ b/tox.ini @@ -0,0 +1,36 @@ +# Benefits of using tox: +# +# 1. makes sure we are testing west as installed by setup.py +# 2. avoid touching user global / system config files +# 3. avoid touching any git repositories outside of tmpdirs +# 4. ensure global / system config settings have no effect on the tests + +[tox] +envlist=py3 + +[pycodestyle] +# We explicitly disable the following errors: +# +# - E126: continuation line over-indented for hanging indent +# - E261: at least two spaces before inline comment +# - E302: expected 2 blank lines, found 1 +# - E305: expected 2 blank lines after class or function definition, found 1 +# - W504: line break after binary operator +ignore = E126,E261,E302,E305,W504 +# Don't lint setup.py, the .tox or python virtualenv directory, or the build directory +exclude = setup.py,.tox,.venv,build +max-line-length = 120 + +[testenv] +deps = + setuptools-scm + pytest + pytest-cov + types-PyYAML + black +setenv = + # For instance: ./.tox/py3/tmp/ + TOXTEMPDIR={envtmpdir} +commands = + python -m pytest --basetemp='{envtmpdir}/pytest with space/' + python -m black --check '{toxinidir}' --extend-exclude 'api_client|tdf_definitions.py'