refactor(snipeit): typisiere response payload als TypedDict statt dict[str, Any] (#67)#122
Conversation
…t[str, Any] (#67) Audit #67 Finding: das alte `dict[str, Any]` hat das Hub-mypy-strict-Verbot von `Any` in Plugin-Responses verletzt. Refactor zu `_SnipeITAssetResponse` TypedDict mit `total=False`: class _SnipeITAssetResponse(TypedDict, total=False): id: int asset_tag: str | None name: str | None serial: str | None Beschränkt auf die Felder die `_payload_to_label` tatsächlich konsumiert. Verhalten 1:1 (TypedDict wird zur Laufzeit als dict behandelt, nur mypy sieht die Struktur). Die Verwendung von `.get(...)` bleibt unverändert. Tests: - tests/unit/integrations/test_snipeit_plugin.py — 10 passed - mypy ✓ ruff ✓ Refs #67 Audit-Punkt: "PR #51 — snipeit/plugin.py:67 — Response payload dict[str, Any]" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! Dieser Pull Request verbessert die Typsicherheit innerhalb des Snipe-IT-Plugins, indem das generische 'dict[str, Any]' durch ein explizites 'TypedDict' ersetzt wird. Dies stellt sicher, dass die konsumierten Felder ('id', 'asset_tag', 'name', 'serial') korrekt typisiert sind, während die Laufzeitlogik unverändert bleibt. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request replaces the use of dict[str, Any] with a strictly-typed _SnipeITAssetResponse TypedDict in the Snipe-IT integration plugin to comply with strict type-checking rules. The review feedback suggests improving the type definition by removing total=False and explicitly marking optional fields with NotRequired so that the required id field remains mandatory in the type system.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| from __future__ import annotations | ||
|
|
||
| from typing import Any | ||
| from typing import TypedDict |
| class _SnipeITAssetResponse(TypedDict, total=False): | ||
| """Subset of Snipe-IT `/api/v1/hardware/bytag/{tag}` response we actually | ||
| read. Strikt-typisiert statt `dict[str, Any]` — beschränkt sich auf die | ||
| Felder die `_payload_to_label` konsumiert. `total=False` weil alle Felder | ||
| außer `id` optional sind (User-Mapping kann Snipe-IT-fields ausblenden). | ||
|
|
||
| Audit #67 / PR #51 Finding: das alte `dict[str, Any]` hat das Hub-mypy- | ||
| strict-Verbot von `Any` in Plugin-Responses verletzt. | ||
| """ | ||
|
|
||
| id: int | ||
| asset_tag: str | None | ||
| name: str | None | ||
| serial: str | None |
There was a problem hiding this comment.
Mit total=False werden alle Felder der TypedDict als optional markiert, einschließlich id. Dies widerspricht dem Docstring, der besagt, dass alle Felder außer id optional sind.
Da das Projekt Python 3.12+ verwendet, ist es sauberer, total=False zu entfernen und stattdessen die optionalen Felder explizit mit NotRequired zu typisieren. Dadurch bleibt id als einziges erforderliches Feld im Typ-System bestehen.
| class _SnipeITAssetResponse(TypedDict, total=False): | |
| """Subset of Snipe-IT `/api/v1/hardware/bytag/{tag}` response we actually | |
| read. Strikt-typisiert statt `dict[str, Any]` — beschränkt sich auf die | |
| Felder die `_payload_to_label` konsumiert. `total=False` weil alle Felder | |
| außer `id` optional sind (User-Mapping kann Snipe-IT-fields ausblenden). | |
| Audit #67 / PR #51 Finding: das alte `dict[str, Any]` hat das Hub-mypy- | |
| strict-Verbot von `Any` in Plugin-Responses verletzt. | |
| """ | |
| id: int | |
| asset_tag: str | None | |
| name: str | None | |
| serial: str | None | |
| class _SnipeITAssetResponse(TypedDict): | |
| """Subset of Snipe-IT /api/v1/hardware/bytag/{tag} response we actually | |
| read. Strikt-typisiert statt dict[str, Any] — beschränkt sich auf die | |
| Felder die _payload_to_label konsumiert. | |
| Audit #67 / PR #51 Finding: das alte dict[str, Any] hat das Hub-mypy- | |
| strict-Verbot von Any in Plugin-Responses verletzt. | |
| """ | |
| id: int | |
| asset_tag: NotRequired[str | None] | |
| name: NotRequired[str | None] | |
| serial: NotRequired[str | None] |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #122 +/- ##
==========================================
- Coverage 89.28% 89.27% -0.02%
==========================================
Files 91 91
Lines 4257 4262 +5
Branches 368 368
==========================================
+ Hits 3801 3805 +4
- Misses 358 359 +1
Partials 98 98
... and 1 file with indirect coverage changes
Flags with carried forward coverage won't be shown. Click here to find out more. Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
## <small>0.10.2 (2026-06-15)</small> * refactor(main): _resolve_model_id_from_config nimmt PrinterYAMLConfig statt Any (#67) (#123) ([2ff51d2](2ff51d2)), closes [#67](#67) [#123](#123) [#67](#67) [#59](#59) [#67](#67) [#59](#59) * refactor(snipeit): typisiere response payload als TypedDict statt dict[str, Any] (#67) (#122) ([e88601b](e88601b)), closes [#67](#67) [#122](#122) [#67](#67) [#67](#67) [#51](#51) * perf(lifespan): plugin-discovery in Thread auslagern (Audit #67) (#121) ([48215b4](48215b4)), closes [#67](#67) [#121](#121) [#67](#67) [#59](#59) * fix(config): stricter validation for log_level + webhook_api_key (#46) (#116) ([6b8ac30](6b8ac30)), closes [#46](#46) [#116](#116) [#46](#46) [#46](#46) [#46](#46) [#46](#46) [#116](#116) * fix(print): copies-replication erzeugt UNIQUE job_ids (Round 2 #hangar-109) (#120) ([bd781ae](bd781ae)), closes [#hangar-109](https://github.com/strausmann/label-printer-hub/issues/hangar-109) [#120](#120) [#115](#115) * fix(print): PrintOptions.copies replicates images for hardware print (#hangar-109) (#115) ([d27bbcc](d27bbcc)), closes [#hangar-109](https://github.com/strausmann/label-printer-hub/issues/hangar-109) [#115](#115) [strausmann/hangar#109](https://github.com/strausmann/hangar/issues/109) * docs(metrics): präzisiere sse_events_published_total Beschreibung (Audit #67) (#119) ([ab8c4c4](ab8c4c4)), closes [#67](#67) [#119](#119) [#67](#67) [#67](#67) [#65](#65) * docs(ptouch): korrigiere Tape-Klassen-Namen in ptouch-integration.md (Audit #67) (#118) ([0faab99](0faab99)), closes [#67](#67) [#118](#118) [#67](#67) [#59](#59) * chore(readme): codecov badge ohne embedded Token (Audit #67) (#117) ([4bb5423](4bb5423)), closes [#67](#67) [#117](#117) [#67](#67) [#59](#59) [skip ci]
Summary
dict[str, Any]→_SnipeITAssetResponseTypedDict mittotal=Falsefür die 4 Felder die der Plugin tatsächlich konsumiert (id,asset_tag,name,serial).Verhalten 1:1 (TypedDict ist zur Laufzeit ein dict, nur statische Analyse sieht die Struktur). Der
Any-Import ist entfernt.Test plan
tests/unit/integrations/test_snipeit_plugin.py— 10 passedRefs #67 Audit-Punkt:
PR #51 — snipeit/plugin.py:67 — Response payload dict[str, Any]