From ef39fa96b89ac7f9723442230baf27e1a5b6140e Mon Sep 17 00:00:00 2001 From: Peter Corke Date: Sat, 3 Oct 2026 16:04:44 -0400 Subject: [PATCH 1/2] fix: remove labeltool console script that points at a module that was never committed (#117) pyproject.toml has declared labeltool = machinevisiontoolbox.bin.labeltool:main since 3735f968, but bin/labeltool.py was never committed, so the published 2.4.0 wheel installs a 'labeltool' command that fails with ModuleNotFoundError. Remove the entry point until the tool exists; the intended LabelMe region selector is tracked in #117. Co-Authored-By: Claude Sonnet 5.5 --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index fb7dea7a..6a63b3a8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -138,7 +138,6 @@ imtool = "machinevisiontoolbox.bin.imtool:main" tagtool = "machinevisiontoolbox.bin.tagtool:main" ocrtool = "machinevisiontoolbox.bin.ocrtool:main" bagtool = "machinevisiontoolbox.bin.bagtool:main" -labeltool = "machinevisiontoolbox.bin.labeltool:main" [build-system] requires = ["hatchling"] From 1dc92daaddcab8845eb0d12e4df69f4d94ea177a Mon Sep 17 00:00:00 2001 From: Peter Corke Date: Sat, 3 Oct 2026 16:05:40 -0400 Subject: [PATCH 2/2] test: check every console script in pyproject.toml has a module behind it (#117) Would have caught the missing labeltool module before 2.4.0 shipped. Note it only fails in a clean checkout (as in CI): on a dev machine an untracked labeltool.py satisfies find_spec, which is how the bug went unnoticed. Co-Authored-By: Claude Sonnet 5.5 --- tests/test_entry_points.py | 57 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 tests/test_entry_points.py diff --git a/tests/test_entry_points.py b/tests/test_entry_points.py new file mode 100644 index 00000000..5430dea2 --- /dev/null +++ b/tests/test_entry_points.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python + +import importlib.util +import re +import unittest +from pathlib import Path + +PYPROJECT = Path(__file__).resolve().parent.parent / "pyproject.toml" + + +def console_scripts(text: str) -> list[tuple[str, str, str]]: + """Parse ``[project.scripts]`` out of pyproject.toml text. + + :param text: contents of a pyproject.toml file + :return: list of (command name, module, function) tuples + + A regex is used rather than ``tomllib`` because the latter is only in the + standard library from Python 3.11 and this package supports 3.10. + """ + match = re.search(r"^\[project\.scripts\]\n(.*?)(?=^\[|\Z)", text, re.S | re.M) + if match is None: + return [] + return re.findall(r'^([\w-]+)\s*=\s*"([\w.]+):(\w+)"', match.group(1), re.M) + + +def missing_modules(text: str) -> list[str]: + """Return the commands in ``[project.scripts]`` whose module cannot be found.""" + return [ + name + for name, module, _ in console_scripts(text) + if importlib.util.find_spec(module) is None + ] + + +class TestEntryPoints(unittest.TestCase): + def test_console_scripts_exist(self): + """Every console script declared in pyproject.toml must have a module + behind it -- regression test for ``labeltool`` being declared (and so + installed by pip) in 2.4.0 while bin/labeltool.py was never committed, + which made the command fail with ModuleNotFoundError""" + scripts = console_scripts(PYPROJECT.read_text()) + self.assertGreater(len(scripts), 0, "no console scripts found; parser broken?") + self.assertEqual(missing_modules(PYPROJECT.read_text()), []) + + def test_detects_missing_module(self): + """The check itself must flag a script whose module does not exist""" + text = ( + "[project.scripts]\n" + 'good = "machinevisiontoolbox.bin.imtool:main"\n' + 'bad = "machinevisiontoolbox.bin.no_such_tool:main"\n' + "\n[build-system]\n" + ) + self.assertEqual(missing_modules(text), ["bad"]) + + +if __name__ == "__main__": + unittest.main()