Make library py.typed#258
Conversation
There was a problem hiding this comment.
Pull request overview
This PR aims to make fritzconnection a typed package (PEP 561 via py.typed) and to tighten type-checking by running mypy in --strict mode, with broad annotation updates across core, library, and CLI modules.
Changes:
- Add
fritzconnection/py.typedmarker for PEP 561 typed-package support. - Update the nox mypy session to run mypy with
--strict. - Add/adjust typing annotations and small refactors across core/lib/cli modules to satisfy strict mypy.
Reviewed changes
Copilot reviewed 27 out of 28 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| noxfile.py | Runs mypy with --strict for selected modules/dirs. |
| fritzconnection/py.typed | Adds PEP 561 marker file for typed-package consumers. |
| fritzconnection/lib/fritzwlan.py | Adds typing and refactors optional segno import usage. |
| fritzconnection/lib/fritztopology.py | Adds typing/casts and hardens topology loading against unexpected JSON types. |
| fritzconnection/lib/fritztools.py | Adds typing to formatting utilities and ArgumentNamespace. |
| fritzconnection/lib/fritzstatus.py | Adds typing/casts and small refactors for mypy strictness. |
| fritzconnection/lib/fritzphonebook.py | Adds typing/casts and some defensive checks around phonebook loading. |
| fritzconnection/lib/fritzhosts.py | Adds typing/casts and annotates get_mesh_topology JSON parsing. |
| fritzconnection/lib/fritzhomeauto.py | Adds typing/casts and refactors XML parsing; introduces a new __getattr__. |
| fritzconnection/lib/fritzcall.py | Adds typing/casts and a guard for self.calls being None. |
| fritzconnection/lib/fritzbase.py | Adds typing for base class init and modelname. |
| fritzconnection/core/utils.py | Adds typing and makes localname() more robust to non-matching tags. |
| fritzconnection/core/soaper.py | Adds typing, improves some error handling, and renames some locals for clarity. |
| fritzconnection/core/processor.py | Adds typing across processors/serializers and simplifies some processing calls. |
| fritzconnection/core/logger.py | Adds typing to logger helper functions. |
| fritzconnection/core/fritzmonitor.py | Adds typing for queues/sockets and tightens some thread-state checks. |
| fritzconnection/core/fritzhttp.py | Adds typing and makes login challenge/SID parsing more defensive. |
| fritzconnection/core/fritzconnection.py | Adds typing/casts, adds assertions, and adjusts call_http header handling. |
| fritzconnection/core/devices.py | Adds typing for device manager APIs. |
| fritzconnection/cli/utils.py | Adds typing for CLI utility functions. |
| fritzconnection/cli/fritzwlan.py | Adds typing annotations for CLI entrypoints/handlers. |
| fritzconnection/cli/fritzstatus.py | Adds typing annotations for CLI entrypoints/handlers. |
| fritzconnection/cli/fritzphonebook.py | Adds typing annotations for CLI entrypoints/handlers. |
| fritzconnection/cli/fritzmonitor.py | Adds typing annotations for CLI entrypoints/handlers. |
| fritzconnection/cli/fritzinspection.py | Adds typing annotations and hardens related-state-variable lookup. |
| fritzconnection/cli/fritzhosts.py | Adds typing annotations for CLI entrypoints/handlers. |
| fritzconnection/cli/fritzhomeauto.py | Adds typing annotations for CLI entrypoints/handlers. |
| fritzconnection/cli/fritzcall.py | Adds typing annotations for CLI entrypoints/handlers. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if header is None: | ||
| header = "" | ||
| content_type, charset = [item.strip() for item in header.split(";")] | ||
| # extract the encoding from the charset-information | ||
| encoding = charset.split("=")[-1].strip() |
There was a problem hiding this comment.
call_http() sets header to an empty string when the response is missing a Content-Type, but then unconditionally unpacks header.split(";") into two values. This will raise ValueError for missing/invalid headers and break call_http().
Handle absent or unexpected Content-Type formats defensively (e.g., default to text/plain + utf-8, or parse only if a charset is present) before unpacking.
| if header is None: | |
| header = "" | |
| content_type, charset = [item.strip() for item in header.split(";")] | |
| # extract the encoding from the charset-information | |
| encoding = charset.split("=")[-1].strip() | |
| content_type = "text/plain" | |
| encoding = "utf-8" | |
| if header: | |
| parts = [item.strip() for item in header.split(";") if item.strip()] | |
| if parts: | |
| content_type = parts[0] | |
| # extract the encoding from charset-information if available | |
| for part in parts[1:]: | |
| key, sep, value = part.partition("=") | |
| if sep and key.strip().lower() == "charset" and value.strip(): | |
| encoding = value.strip() | |
| break |
|
|
||
| def __repr__(self): | ||
| def __getattr__(self, name: str) -> Any: | ||
| return self.__dict__[name] |
There was a problem hiding this comment.
HomeAutomationDevice.__getattr__ currently returns self.__dict__[name]. When the attribute is truly missing, this raises KeyError instead of AttributeError, which can break normal Python attribute semantics (e.g., hasattr(), getattr(..., default), introspection, and exception handling).
If you keep __getattr__, it should raise AttributeError(name) when the key isn’t present (or remove __getattr__ entirely if it’s not needed).
| return self.__dict__[name] | |
| try: | |
| return self.__dict__[name] | |
| except KeyError as exc: | |
| raise AttributeError(name) from exc |
Played 10 minutes with CoPilot and moved the library to py.typed :-)
I also updated noxfile.py to force mypy to run in "
--strict" mode.