diff --git a/.github/workflows/linting.yaml b/.github/workflows/linting.yaml index 34bcb9d1..82a75b1d 100644 --- a/.github/workflows/linting.yaml +++ b/.github/workflows/linting.yaml @@ -218,3 +218,28 @@ jobs: run: poetry install --extras cli --no-interaction - name: ๐Ÿš€ Run zizmor run: poetry run zizmor .github/workflows/ + + vulture: + name: vulture + runs-on: ubuntu-latest + steps: + - name: โคต๏ธ Check out code from GitHub + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: ๐Ÿ— Set up Poetry + run: pipx install poetry + - name: ๐Ÿ— Set up Python ${{ env.DEFAULT_PYTHON }} + id: python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: ${{ env.DEFAULT_PYTHON }} + cache: "poetry" + - name: ๐Ÿ— Install workflow dependencies + run: | + poetry config virtualenvs.create true + poetry config virtualenvs.in-project true + - name: ๐Ÿ— Install Python dependencies + run: poetry install --extras cli --no-interaction + - name: ๐Ÿš€ Run vulture + run: poetry run vulture diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index da3df9f2..73b696df 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -127,6 +127,13 @@ repos: exclude: ^tests/(?:.*/)?__snapshots__/ entry: poetry run trailing-whitespace-fixer stages: [commit, push, manual] + - id: vulture + name: ๐Ÿฆ… Hunt for dead code with vulture + language: system + types: [python] + entry: poetry run vulture + pass_filenames: false + always_run: true - id: yamllint name: ๐ŸŽ— Check YAML files with yamllint language: system diff --git a/poetry.lock b/poetry.lock index 1549ae5e..b2b9b95b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.4.1 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -1999,8 +1999,8 @@ files = [ astroid = ">=4.0.2,<=4.1.dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = [ - {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, {version = ">=0.3.6", markers = "python_version == \"3.11\""}, + {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, ] isort = ">=5,<5.13 || >5.13,<9" mccabe = ">=0.6,<0.8" @@ -2604,6 +2604,18 @@ h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] +[[package]] +name = "vulture" +version = "2.16" +description = "Find dead code" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "vulture-2.16-py3-none-any.whl", hash = "sha256:6e0f1c312cef1c87856957e5c2ca9608834a7c794c2180477f30bf0e4cc58eee"}, + {file = "vulture-2.16.tar.gz", hash = "sha256:f8d9f6e2af03011664a3c6c240c9765b3f392917d3135fddca6d6a68d359f717"}, +] + [[package]] name = "yamllint" version = "1.38.0" @@ -2879,4 +2891,4 @@ cli = ["typer", "zeroconf"] [metadata] lock-version = "2.1" python-versions = ">=3.11,<4.0" -content-hash = "a003ede1342488494962be3fc597b0a74d6585c63130a9c3598d7f5aa007c7b4" +content-hash = "10719d1806b28b26f54480225f6d6b843e44d1c9b4ea9432e261619172d6e8b9" diff --git a/pyproject.toml b/pyproject.toml index 7aa26e83..d60b0222 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,6 +70,7 @@ safety = "3.7.0" syrupy = "5.1.0" ty = "0.0.31" typer = "0.24.1" +vulture = "2.16" yamllint = "1.38.0" zizmor = "1.24.1" @@ -145,6 +146,10 @@ max-complexity = 25 [tool.codespell] ignore-words-list = "abl" +[tool.vulture] +paths = ["src/wled", "tests"] +min_confidence = 80 + [build-system] requires = ["poetry-core>=2.0"] build-backend = "poetry.core.masonry.api" diff --git a/tests/conftest.py b/tests/conftest.py index d62f32d8..18da13f2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -101,12 +101,12 @@ class CustomDataclassPlugin(AmberDataSerializerPlugin): """Syrupy plugin that serializes dataclass instances recursively in Amber format.""" @classmethod - def is_data_serializable(cls, data: "SerializableData") -> bool: + def is_data_serializable(cls, data: SerializableData) -> bool: """Return True for dataclass instances (excludes dataclass types themselves).""" return dataclasses.is_dataclass(data) and not isinstance(data, type) @classmethod - def serialize(cls, data: "SerializableData", **kwargs: Any) -> str: + def serialize(cls, data: SerializableData, **kwargs: Any) -> str: """Serialize a dataclass instance into Amber format.""" keys = sorted(f.name for f in dataclasses.fields(data)) return CustomDataclassSerializer.serialize_custom_iterable(