What changed, and why it matters
This commit adds a development tool called Vulture that finds unused ('dead') code and removes the dead code it detected. It is a routine code-cleanup change, not a security fix or vulnerability patch. There is no indication it introduces a security issue or addresses one.
No security action required. Treat as normal maintenance; verify that removed code was genuinely unused and that tests still pass.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit integrates the Vulture static-analysis tool into the project’s Poetry dev dependencies and pre-commit/pre-release tasks. It then removes unused variables, constants, methods, and the entire EncryptedQRCode class that Vulture flagged as dead code. Corresponding tests for the removed code are also deleted. No functional security behavior is changed; this is purely cleanup.
Changed components
pyproject.tomlpoetry.locksrc/krux/encryption.pysrc/krux/light.pysrc/krux/pages/device_tests.pysrc/krux/pages/encryption_ui.pysrc/krux/pages/keypads.pysrc/krux/pages/wallet_settings.pysrc/krux/printers/thermal.pysrc/krux/sd_card.pysrc/krux/settings.pysrc/krux/themes.pysrc/krux/touchscreens/ft6x36.pysrc/krux/touchscreens/gt911.pytests/test_encryption.pytests/test_light.pyvulture_whitelist.pyInspect captured patch +34 / −323
diff --git a/poetry.lock b/poetry.lock
index 347cab2..d9a269d 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1654,6 +1654,17 @@ develop = true
type = "directory"
url = "vendor/urtypes"
+[[package]]
+name = "vulture"
+version = "2.14"
+description = "Find dead code"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "vulture-2.14-py2.py3-none-any.whl", hash = "sha256:d9a90dba89607489548a49d557f8bac8112bd25d3cbc8aeef23e860811bd5ed9"},
+ {file = "vulture-2.14.tar.gz", hash = "sha256:cb8277902a1138deeab796ec5bef7076a6e0248ca3607a3f3dee0b6d9e9b8415"},
+]
+
[[package]]
name = "watchdog"
version = "6.0.0"
@@ -1705,4 +1716,4 @@ simulator = ["Pillow", "numpy", "opencv-python", "pygame", "pyzbar"]
[metadata]
lock-version = "2.0"
python-versions = "^3.12.3"
-content-hash = "16d6a945791d7bb5d4a31b1db12250f4ae00a46f8c2763be3275ce2de3771190"
+content-hash = "b7fd8425d885565e58dcc11beeae1dd3a685ed84479c66c32c9756ecb4674ddc"
diff --git a/pyproject.toml b/pyproject.toml
index 2f19c2e..186daaf 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -64,6 +64,7 @@ pytest-mock = "^3.15.0"
PyQRCode = "^1.2.1"
pycryptodome = "^3.23.0"
poethepoet = "^0.37.0"
+vulture = "^2.14"
[tool.poetry.extras]
docs = ["mkdocs", "mkdocs-material", "mkdocs-static-i18n", "pymdown-extensions", "mkdocs-macros-plugin"]
@@ -88,6 +89,12 @@ lint = ["lint-src", "lint-scripts"]
# aliases
pylint.ref = "lint"
+# vulture tasks
+vulture-src = "vulture src"
+vulture.ref = "vulture-src vulture_whitelist.py"
+vulture-make = { shell = "vulture src --make-whitelist > vulture_whitelist.py" }
+vulture-whitelist.ref = "vulture-make"
+
# test tasks
test-clean = """python -c 'import shutil, os; os.path.exists("htmlcov") and shutil.rmtree("htmlcov")'"""
test-cov = "pytest --cache-clear --cov src/krux --cov-report html ./tests --cov-context=test --cov-report term-missing"
@@ -99,8 +106,8 @@ test.ref = "tests"
pytest.ref = "tests"
# pre commit task (do formatting, linting and tests)
-pre-commit = ["format", "lint", "i18n validate", "test-simple"]
-pre-release = ["format", "lint", "i18n validate", "i18n-build", "update-glyphs", "test-simple"]
+pre-commit = ["format", "lint", "i18n validate", "vulture", "test-simple"]
+pre-release = ["format", "lint", "i18n validate", "i18n-build", "update-glyphs", "vulture", "test-simple"]
# run docs locally
docs = "poetry run mkdocs serve"
diff --git a/src/krux/display.py b/src/krux/display.py
index d6c65be..93e0c40 100644
--- a/src/krux/display.py
+++ b/src/krux/display.py
@@ -30,7 +30,7 @@ from .kboard import kboard
DEFAULT_PADDING = 10
MINIMAL_PADDING = 5
FONT_WIDTH, FONT_HEIGHT = board.config["krux"]["display"]["font"]
-FONT_WIDTH_WIDE, FONT_HEIGHT_WIDE = board.config["krux"]["display"]["font_wide"]
+FONT_WIDTH_WIDE, _ = board.config["krux"]["display"]["font_wide"]
PORTRAIT, LANDSCAPE = [2, 3] if kboard.is_cube else [1, 2]
QR_DARK_COLOR, QR_LIGHT_COLOR = [16904, 61307] if kboard.is_m5stickv else [0, 6342]
TOTAL_LINES = board.config["lcd"]["width"] // FONT_HEIGHT
diff --git a/src/krux/encryption.py b/src/krux/encryption.py
index 431d030..be3f5f9 100644
--- a/src/krux/encryption.py
+++ b/src/krux/encryption.py
@@ -25,7 +25,6 @@ import hashlib
from krux import kef
from .baseconv import base_encode, base_decode
from .sd_card import SDHandler
-from .krux_settings import Settings
from embit import bip39
from .settings import FLASH_PATH, MNEMONICS_FILE
@@ -165,51 +164,3 @@ class MnemonicStorage:
self.stored.pop(mnemonic_id)
with open(FLASH_PATH_STR % MNEMONICS_FILE, "w") as f:
f.write(json.dumps(self.stored))
-
-
-class EncryptedQRCode:
- """Creates and decrypts encrypted mnemonic QR codes"""
-
- def __init__(self) -> None:
- self.mnemonic_id = None
- self.version = None
- self.iterations = Settings().encryption.pbkdf2_iterations
- self.encrypted_data = None
-
- def create(self, key, mnemonic_id, mnemonic, i_vector=None):
- """encrypted mnemonic QR codes"""
- mode_name = Settings().encryption.version
- encryptor = kef.Cipher(key, mnemonic_id, self.iterations)
- bytes_to_encrypt = bip39.mnemonic_to_bytes(mnemonic)
- self.version = kef.suggest_versions(bytes_to_encrypt, mode_name)[0]
- bytes_encrypted = encryptor.encrypt(bytes_to_encrypt, self.version, i_vector)
- return kef.wrap(mnemonic_id, self.version, self.iterations, bytes_encrypted)
-
- def public_data(self, data):
- """Parse and returns encrypted mnemonic QR codes public data"""
- try:
- (self.mnemonic_id, self.version, self.iterations, self.encrypted_data) = (
- kef.unwrap(data)
- )
- version_name = kef.VERSIONS[self.version]["name"]
- except:
- return None
-
- try:
- displayable_id = self.mnemonic_id.decode()
- except UnicodeDecodeError:
- displayable_id = repr(self.mnemonic_id) # id_ could be any bytes
- return "\n".join(
- [
- "Encrypted QR Code:",
- "ID: " + displayable_id,
- "Version: " + version_name,
- "PBKDF2 iter.: " + str(self.iterations),
- ]
- )
-
- def decrypt(self, key):
- """Decrypts encrypted mnemonic QR codes"""
- decryptor = kef.Cipher(key, self.mnemonic_id, self.iterations)
- decrypted_data = decryptor.decrypt(self.encrypted_data, self.version)
- return decrypted_data
diff --git a/src/krux/light.py b/src/krux/light.py
index 070f9c1..461bb18 100644
--- a/src/krux/light.py
+++ b/src/krux/light.py
@@ -33,12 +33,6 @@ class IOCircuit:
self.circuit = GPIO(maixpy_gpio, GPIO.OUT)
self.turn_off()
- def is_on(self):
- """Returns a boolean indicating if the circuit is currently on"""
- if kboard.is_wonder_mv:
- return self.circuit.value() == 1
- return self.circuit.value() == 0
-
def turn_on(self):
"""Turns on the circuit"""
if kboard.is_wonder_mv:
@@ -53,13 +47,6 @@ class IOCircuit:
else:
self.circuit.value(1)
- def toggle(self):
- """Toggles the circuit on or off"""
- if self.is_on():
- self.turn_off()
- else:
- self.turn_on()
-
class Light(IOCircuit):
"""Light is a singleton interface for interacting with the device's LED light"""
diff --git a/src/krux/pages/device_tests.py b/src/krux/pages/device_tests.py
index 72b036b..88d2c2a 100644
--- a/src/krux/pages/device_tests.py
+++ b/src/krux/pages/device_tests.py
@@ -194,7 +194,7 @@ class DeviceTests(Page):
success = bool(result)
except Exception as err:
result = err
- idx = [i for i, (t, r) in enumerate(self.results) if t == test][0]
+ idx = [i for i, (t, _) in enumerate(self.results) if t == test][0]
self.results[idx] = (test, success)
self.ctx.display.clear()
diff --git a/src/krux/pages/encryption_ui.py b/src/krux/pages/encryption_ui.py
index a377584..e0bd4c1 100644
--- a/src/krux/pages/encryption_ui.py
+++ b/src/krux/pages/encryption_ui.py
@@ -223,7 +223,7 @@ class KEFEnvelope(Page):
idx, _ = Menu(
self.ctx, [(x[0], lambda: None) for x in menu_items], back_label=None
).run_loop()
- self.version = [v for i, (name, v) in enumerate(menu_items) if i == idx][0]
+ self.version = [v for i, (_, v) in enumerate(menu_items) if i == idx][0]
self.version_name = kef.VERSIONS[self.version]["name"]
self.mode = kef.VERSIONS[self.version]["mode"]
self.mode_name = [k for k, v in kef.MODE_NUMBERS.items() if v == self.mode][0]
diff --git a/src/krux/pages/keypads.py b/src/krux/pages/keypads.py
index 8f50f2e..f465258 100644
--- a/src/krux/pages/keypads.py
+++ b/src/krux/pages/keypads.py
@@ -40,8 +40,6 @@ from ..kboard import kboard
FIXED_KEYS = 3 # 'More' key only appears when there are multiple keysets.
-KEYPAD_OFFSET = DEFAULT_PADDING + FONT_HEIGHT * 3
-
class KeypadLayout:
"""Groups layout-related attributes for Keypad."""
@@ -279,15 +277,6 @@ class Keypad:
elif btn in (SWIPE_DOWN, SWIPE_RIGHT):
self.previous_keyset()
- def _clean_keypad_area(self):
- self.ctx.display.fill_rectangle(
- 0,
- KEYPAD_OFFSET,
- self.ctx.display.width(),
- self.ctx.display.height() - KEYPAD_OFFSET,
- theme.bg_color,
- )
-
def _next_key(self):
"""Increments cursor when page button is pressed"""
self.moving_forward = True
diff --git a/src/krux/pages/wallet_settings.py b/src/krux/pages/wallet_settings.py
index 3909a47..6ac172e 100644
--- a/src/krux/pages/wallet_settings.py
+++ b/src/krux/pages/wallet_settings.py
@@ -58,7 +58,6 @@ PASSPHRASE_MAX_LEN = 200
DERIVATION_KEYPAD = "1234567890/h"
P2SH_DEFAULT_DERIVATION = "m/45h"
-MINISCRIPT_DEFAULT_DERIVATION = "m/48h/0h/0h/2h"
class PassphraseEditor(Page):
diff --git a/src/krux/printers/thermal.py b/src/krux/printers/thermal.py
index 977cbe5..a3c56fd 100644
--- a/src/krux/printers/thermal.py
+++ b/src/krux/printers/thermal.py
@@ -46,8 +46,6 @@ from ..krux_settings import Settings
from ..wdt import wdt
from . import Printer
-INITIALIZE_WAIT_TIME = 500
-
class AdafruitPrinter(Printer):
"""AdafruitPrinter is a minimal wrapper around a serial connection to
diff --git a/src/krux/sd_card.py b/src/krux/sd_card.py
index d693619..e288964 100644
--- a/src/krux/sd_card.py
+++ b/src/krux/sd_card.py
@@ -54,7 +54,7 @@ class SDHandler:
# if the code reaches here, no exception was raised
return self
- def __exit__(self, exc_type, exc_val, exc_tb):
+ def __exit__(self, _exc_type, _exc_val, _exc_tb):
pass
def write_binary(self, filename, data):
diff --git a/src/krux/settings.py b/src/krux/settings.py
index 745b444..f19eb60 100644
--- a/src/krux/settings.py
+++ b/src/krux/settings.py
@@ -76,7 +76,7 @@ class Setting:
self.attr = attr
self.default_value = default_value
- def __get__(self, obj, objtype=None):
+ def __get__(self, obj, _objtype=None):
if obj is None:
return self
return store.get(obj.namespace, self.attr, self.default_value)
@@ -97,7 +97,7 @@ class CategorySetting(Setting):
super().__init__(attr, default_value)
self.categories = categories
- def __get__(self, obj, objtype=None):
+ def __get__(self, obj, _objtype=None):
if obj is None:
return self
@@ -115,7 +115,7 @@ class LinkedCategorySetting(CategorySetting):
self.linked_setting = linked_setting
self.conditions = [fn for fn in args if callable(fn)]
- def __get__(self, obj, objtype=None):
+ def __get__(self, obj, _objtype=None):
if obj is None:
return self
@@ -139,7 +139,7 @@ class NumberSetting(Setting):
self.numtype = numtype
self.value_range = value_range
- def __get__(self, obj, objtype=None):
+ def __get__(self, obj, _objtype=None):
if obj is None:
return self
diff --git a/src/krux/themes.py b/src/krux/themes.py
index 1d6db7e..cc10867 100644
--- a/src/krux/themes.py
+++ b/src/krux/themes.py
@@ -23,8 +23,6 @@
from .krux_settings import Settings, ThemeSettings
from .kboard import kboard
-DEFAULT_THEME = ThemeSettings.DARK_THEME_NAME
-
# To create new colors from RGB values use firmware/scripts/rgbconv.py script
BLACK = 0x0000
@@ -34,7 +32,6 @@ GREY = 0x14A5
LIGHTGREY = 0x38C6
DARKWHITE = 0x1CE7
WHITE = 0xFFFF
-LIGHT_GREEN = 0xEC9F
GREEN = 0xE007
DARKGREEN = 0x8005
RED = 0x00F8
diff --git a/src/krux/touchscreens/ft6x36.py b/src/krux/touchscreens/ft6x36.py
index 1263176..ec09321 100644
--- a/src/krux/touchscreens/ft6x36.py
+++ b/src/krux/touchscreens/ft6x36.py
@@ -30,12 +30,12 @@ from . import Touchscreen
from ..i2c import i2c_bus
FT_DEVICE_MODE = 0x00
-GEST_ID = 0x01
+# GEST_ID = 0x01
TD_STATUS = 0x02
PN_XH = 0x03
FT_ID_G_MODE = 0xA4
FT_ID_G_THGROUP = 0x80
-FT_ID_SR_PERIODACTIVE = 0x88
+# FT_ID_SR_PERIODACTIVE = 0x88
FT6X36_ADDR = 0x38
TOUCH_THRESHOLD = 22 # Default 22
diff --git a/src/krux/touchscreens/gt911.py b/src/krux/touchscreens/gt911.py
index 0b99934..5c67c85 100644
--- a/src/krux/touchscreens/gt911.py
+++ b/src/krux/touchscreens/gt911.py
@@ -33,7 +33,7 @@ from ..i2c import i2c_bus
GT911_PRODUCT_ID = 0x8140
GT911_CONFIG_DATA = 0x8047
GT911_COORD_ADDR = 0x814E
-GT911_CHECK_SUM = 0x80FF
+# GT911_CHECK_SUM = 0x80FF
GT911_COMMAND = 0x8040
GT911_CONFIG_TRIGGER = 0x8057
diff --git a/tests/test_encryption.py b/tests/test_encryption.py
index d8a888c..bc36d73 100644
--- a/tests/test_encryption.py
+++ b/tests/test_encryption.py
@@ -390,175 +390,3 @@ def test_delete_from_sd(m5stickv, mocker, mock_file_operations):
padding_size = len(KEF_GCMENTROPY_ONLY_JSON)
expected = SEEDS_JSON.replace(KEF_GCMENTROPY_ONLY_JSON[1:-1] + ", ", "")
m().write.assert_called_once_with(expected + " " * padding_size)
-
-
-def test_create_ecb_encrypted_qr_code(m5stickv):
- from krux.encryption import EncryptedQRCode
- from krux.krux_settings import Settings
-
- Settings().encryption.version = "AES-ECB"
- encrypted_qr = EncryptedQRCode()
- qr_data = encrypted_qr.create(TEST_KEY, TEST_MNEMONIC_ID, TEST_WORDS)
- print("qr_data: {}".format(qr_data))
- assert qr_data == ECB_ENCRYPTED_QR
-
-
-def test_create_cbc_encrypted_qr_code(m5stickv):
- from krux.encryption import EncryptedQRCode
- from krux.krux_settings import Settings
-
- Settings().encryption.version = "AES-CBC"
- encrypted_qr = EncryptedQRCode()
- qr_data = encrypted_qr.create(TEST_KEY, TEST_MNEMONIC_ID, TEST_WORDS, I_VECTOR)
- print("qr_data: {}".format(qr_data))
- assert qr_data == CBC_ENCRYPTED_QR
-
-
-def test_create_gcm_encrypted_qr_code(m5stickv):
- from krux.encryption import EncryptedQRCode
- from krux.krux_settings import Settings
-
- Settings().encryption.version = "AES-GCM"
- encrypted_qr = EncryptedQRCode()
- qr_data = encrypted_qr.create(TEST_KEY, TEST_MNEMONIC_ID, TEST_WORDS, I_VECTOR[:12])
- print("qr_data: {}".format(qr_data))
- assert qr_data == GCM_ENCRYPTED_QR
-
-
-def test_decode_ecb_encrypted_qr_code(m5stickv):
- from krux.encryption import EncryptedQRCode
- from embit import bip39
-
- encrypted_qr = EncryptedQRCode()
- public_data = encrypted_qr.public_data(ECB_ENCRYPTED_QR)
- print("public_data: {}".format(public_data))
- assert public_data == ECB_QR_PUBLIC_DATA
- word_bytes = encrypted_qr.decrypt(TEST_KEY)
- words = bip39.mnemonic_from_bytes(word_bytes)
- assert words == TEST_WORDS
-
- # legacy still decrypts
- public_data = encrypted_qr.public_data(OLDECB_ENCRYPTED_QR)
- assert public_data == OLDECB_QR_PUBLIC_DATA
- word_bytes = encrypted_qr.decrypt(TEST_KEY)
- words = bip39.mnemonic_from_bytes(word_bytes)
- assert words == TEST_WORDS
-
-
-def test_decode_cbc_encrypted_qr_code(m5stickv):
- from krux.encryption import EncryptedQRCode
- from embit import bip39
-
- encrypted_qr = EncryptedQRCode()
- public_data = encrypted_qr.public_data(CBC_ENCRYPTED_QR)
- print("public_data: {}".format(public_data))
- assert public_data == CBC_QR_PUBLIC_DATA
- word_bytes = encrypted_qr.decrypt(TEST_KEY)
- words = bip39.mnemonic_from_bytes(word_bytes)
- assert words == TEST_WORDS
-
- # legacy still decrypts
- public_data = encrypted_qr.public_data(OLDCBC_ENCRYPTED_QR)
- assert public_data == OLDCBC_QR_PUBLIC_DATA
- word_bytes = encrypted_qr.decrypt(TEST_KEY)
- words = bip39.mnemonic_from_bytes(word_bytes)
- assert words == TEST_WORDS
-
-
-def test_decode_gcm_encrypted_qr_code(m5stickv):
- from krux.encryption import EncryptedQRCode
- from embit import bip39
-
- encrypted_qr = EncryptedQRCode()
- public_data = encrypted_qr.public_data(GCM_ENCRYPTED_QR)
- print("public_data: {}".format(public_data))
- assert public_data == GCM_QR_PUBLIC_DATA
- word_bytes = encrypted_qr.decrypt(TEST_KEY)
- words = bip39.mnemonic_from_bytes(word_bytes)
- assert words == TEST_WORDS
-
-
-def test_check_encrypted_qr_code_lengths(m5stickv):
- from krux.encryption import EncryptedQRCode, kef
- from krux.krux_settings import Settings, EncryptionSettings
- from krux.baseconv import base_encode
-
- # make encrypted_mnemonics using encryption settings mode preference
- qr_code_datum = {}
- for mode, mode_name in EncryptionSettings.MODE_NAMES.items():
- if (
- mode is None
- or len(
- [
- x
- for x in kef.VERSIONS.values()
- if isinstance(x, dict) and x["mode"] == mode
- ]
- )
- == 0
- ):
- continue
-
- Settings().encryption.version = mode_name
- iv = I_VECTOR[: kef.MODE_IVS.get(kef.MODE_NUMBERS[mode_name], 0)]
- encrypted_qr = EncryptedQRCode()
- qr_data = encrypted_qr.create(TEST_KEY, TEST_MNEMONIC_ID, TEST_WORDS, iv)
- if mode_name == "AES-ECB":
- assert len(qr_data) == 31
- assert len(base_encode(qr_data, 43)) == 45
- elif mode_name == "AES-CBC":
- assert len(qr_data) == 48
- assert len(base_encode(qr_data, 43)) == 70
- elif mode_name in ("AES-CTR", "AES-GCM"):
- assert len(qr_data) == 44
- assert len(base_encode(qr_data, 43)) == 64
- else:
- print(f"Unknown mode: {mode_name}")
- assert 0
- qr_code_datum[mode_name] = qr_data
-
- # re-create similar cipher-payloads for all versions, assert their kef-encoding is not any smaller
- from hashlib import sha256
- from embit.bip39 import mnemonic_to_bytes
-
- TEST_ENTROPY = mnemonic_to_bytes(TEST_WORDS)
- ITERATIONS = Settings().encryption.pbkdf2_iterations
- encryptor = kef.Cipher(TEST_KEY, TEST_MNEMONIC_ID, ITERATIONS)
- for version, value in kef.VERSIONS.items():
- if value is None or value["mode"] is None:
- continue
-
- implied_mode = value["name"][:7]
- iv = I_VECTOR[: kef.MODE_IVS.get(value["mode"], 0)]
- payload = encryptor.encrypt(TEST_ENTROPY, version, iv)
- envelope = kef.wrap(TEST_MNEMONIC_ID, version, ITERATIONS, payload)
- qr_data = qr_code_datum[implied_mode]
-
- # looping through all versions, we didn't do any better than suggested version above
- assert len(qr_data) <= len(envelope)
-
- # where it's the same size, it's the same data
- if len(qr_data) == len(envelope):
- assert qr_data == envelope
-
-
-def test_customize_pbkdf2_iterations_create_and_decode(m5stickv):
- from krux.encryption import EncryptedQRCode
- from krux.krux_settings import Settings
- from embit import bip39
-
- print("case Encode: customize_pbkdf2_iterations")
- Settings().encryption.version = "AES-ECB"
- Settings().encryption.pbkdf2_iterations = 99999
- encrypted_qr = EncryptedQRCode()
- qr_data = encrypted_qr.create(TEST_KEY, TEST_MNEMONIC_ID, TEST_WORDS)
- print("qr_data: {}".format(qr_data))
-
- print("case Decode: customize_pbkdf2_iterations")
- public_data = encrypted_qr.public_data(qr_data)
- assert public_data == (
- "Encrypted QR Code:\nID: test ID\nVersion: AES-ECB\nPBKDF2 iter.: 99999"
- )
- word_bytes = encrypted_qr.decrypt(TEST_KEY)
- words = bip39.mnemonic_from_bytes(word_bytes)
- assert words == TEST_WORDS
diff --git a/tests/test_light.py b/tests/test_light.py
index e7c9a92..1df0130 100644
--- a/tests/test_light.py
+++ b/tests/test_light.py
@@ -27,19 +27,6 @@ def test_init(mocker, m5stickv):
light.turn_off.assert_called()
-def test_is_on(mocker, m5stickv):
- mock_modules(mocker)
- from krux.light import Light
-
- light = Light()
- mocker.spy(light.circuit, "value")
-
- on = light.is_on()
-
- assert isinstance(on, bool)
- light.circuit.value.assert_called_with()
-
-
def test_turn_on(mocker, m5stickv):
mock_modules(mocker)
from krux.light import Light
@@ -62,48 +49,3 @@ def test_turn_off(mocker, m5stickv):
light.turn_off()
light.circuit.value.assert_called_with(1)
-
-
-def test_toggle_from_off(mocker, m5stickv):
- mock_modules(mocker)
- from krux.light import Light
-
- light = Light()
- mocker.patch.object(light, "is_on", new=lambda: False)
- mocker.spy(light, "turn_on")
-
- light.toggle()
-
- light.turn_on.assert_called()
-
-
-def test_toggle_from_on(mocker, m5stickv):
- mock_modules(mocker)
- from krux.light import Light
-
- light = Light()
- mocker.patch.object(light, "is_on", new=lambda: True)
- mocker.spy(light, "turn_off")
-
- light.toggle()
-
- light.turn_off.assert_called()
-
-
-def test_toggle_on_wonder_mv(mocker, wonder_mv):
- mock_modules(mocker)
- from krux.light import Light
-
- light = Light()
- mocker.spy(light, "turn_off")
- mocker.spy(light, "turn_on")
-
- # Toggle from on
- mocker.patch.object(light.circuit, "value", return_value=1)
- light.toggle()
- light.turn_off.assert_called()
-
- # Toggle again, now from off
- mocker.patch.object(light.circuit, "value", return_value=0)
- light.toggle()
- light.turn_on.assert_called()
diff --git a/vulture_whitelist.py b/vulture_whitelist.py
new file mode 100644
index 0000000..388f58f
--- /dev/null
+++ b/vulture_whitelist.py
@@ -0,0 +1,2 @@
+GRBLPrinter # unused class (src/krux/printers/cnc.py:361)
+AdafruitPrinter # unused class (src/krux/printers/thermal.py:50)
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.