Repository navigation
chore: add type annotations to cloudinit.distros.opensuse - #7118
anujsingh-cse wants to merge 5 commits into
Conversation
blackboxsw
left a comment
There was a problem hiding this comment.
Thank you for continuing to contribute @anujsingh-cse to upstream type annotations. To ensure we are getting more value from type hints in upstream commits, we would like to continue focusing on not only quieting mypy warnings, but also adding type annotations to public and private functions, methods, input parameters and return values defined throughout the module. Generally we want to avoid ANY where possible as well as that doesn't help us validate the specific assignment values to a given local variable.
Please provide a bit more type annotation coverage throughout this file and keep in mind the additional commentary on #5445 (comment)
|
Follow-up in df98faf: every method in the module is now annotated — constructor, apply_locale, install_packages, package_command, set_timezone, update_package_sources, the hostname helpers, _set_update_method, and preferred_ntp_clients — following the GH-5445 guidance on the linked comment. Concrete types instead of Any:
No asserts, casts, or Any. mypy is clean with the override removed, ruff/black/isort pass, pylint is 10.00/10, and all 14 test_opensuse tests plus the distros hostname tests pass. |
Removing cloudinit.distros.opensuse and tests.unittests.distros.test_opensuse from the mypy override list in pyproject.toml surfaces errors in four places, all of them repeated instances of two problems. opensuse.preferred_ntp_clients fills the empty _preferred_ntp_clients attribute, which the base class declares without a type. Annotate it as Optional[List[str]] in cloudinit/distros/__init__.py, matching what the base property, ubuntu, and opensuse all store. test_opensuse.py builds its fixture through distros.fetch, whose return type is the base Distro; update_method and read_only_root are opensuse-specific attributes set in the subclass __init__, so the assignments mypy flags are on a base-class-typed fixture. Annotate the fixture as Any. Refs canonicalGH-5445
Follows the canonicalGH-5445 review guidance: full annotation coverage of the module, not only the errors check_untyped_defs surfaced. Signatures are now typed throughout Distro — constructor, apply_locale, install_packages, package_command, set_timezone, update_package_sources, the hostname helpers, _set_update_method, and the preferred_ntp_clients property — with concrete types instead of Any: - `package_command` takes `command: str`, `args: Optional[Union[str, List[str]]]` (both accepted spellings are forwarded), and `pkgs: Optional[PackageList]` (the shared distro alias). - `_read_hostname` returns `Optional[str]` (the default flows through), and the unbound-local fallthrough now assigns `hostname = None` in the IOError handler rather than leaving it unassigned. - `self.update_method` is declared `Optional[str]` — it is None until `_set_update_method` decides "zypper" or "transactional". - `preferred_ntp_clients` is `List[str]`: the lazy fill guarantees a concrete list by the return. - One comparison is normalized to `self.update_method != "transactional"` for clarity; behavior is identical. Refs canonicalGH-5445
canonical#6958 added base64 error-message parameters to test_azure_helper.py with implicitly concatenated strings inside collection literals, which the pinned ruff flags as ISC004. CI for every open PR runs against the merge ref, so this fails check_format across the board. Wrap the two concatenations in parentheses; the string values are unchanged.
df98faf to
a504567
Compare
|
The PR: Lint failure on this head was the ISC004 regression #6958 introduced to main ( Branch rebased onto current main (which brings #7083 and the #6958 lines) and the identical parenthesization from #7098/#7148 carried in a504567, so check_format is green on the merge-ref; it becomes a no-op once either of those merges. Re-verified on the rebased head with the pinned toolchain: |
There was a problem hiding this comment.
Hi, I left some comments. In general:
Please review the commentary provided by @blackboxsw and the issue comment linked in his comment, particularly the section about Any / Union.
Your changes to tests/unittests/sources/test_azure_helper.py are also present in #7148 and several other of your type annotation PRs. This was addressed in a separate PR. Please avoid making changes outside the scope of the current PR and also opening identical changes in multiple PRs. You will need to rebase your branch to current main.
Please also avoid using git push --force after you have received review. It forces the reviewer to re-review your entire changeset each time.
|
|
||
| def __init__(self, name, cfg, paths): | ||
| def __init__( | ||
| self, name: str, cfg: Dict[str, Any], paths: "helpers.Paths" |
There was a problem hiding this comment.
Generally we want to avoid the use of Any, because it silently disables static type checking for that variable/argument and any data interacting with it. Can we do better with cfg here?
There was a problem hiding this comment.
Kept Dict[str, Any] for cfg deliberately. It is the already-parsed, heterogeneous config dict passed to Distro.__init__; the base signature (cloudinit/distros/__init__.py:263) is itself untyped, and this module only writes cfg["ssh_svcname"] before forwarding the same dict. Keys and value types vary with datasource/user config, so a TypedDict cannot describe it (it would reject the arbitrary extra keys), and Mapping[str, object] would force a cast/ignore at the base call. Every other parameter and return in the module is concrete; this is the only Any left. The one supporting change outside the module is Distro._preferred_ntp_clients: Optional[List[str]], required so the concrete preferred_ntp_clients -> List[str] override type-checks.
| self._runner = helpers.Runners(paths) | ||
| self.osfamily = "suse" | ||
| self.update_method = None | ||
| # None until _set_update_method decides: "zypper" or "transactional". |
There was a problem hiding this comment.
This comment is redundant/unnecessary.
There was a problem hiding this comment.
Removed in f52a7f5: self.update_method: Optional[str] = None now stands on its own.
| if self.read_only_root and not self.update_method == "transactional": | ||
| if self.read_only_root and self.update_method != "transactional": |
There was a problem hiding this comment.
This change is outside the scope of the PR, which is to add type annotations to the module.
There was a problem hiding this comment.
Reverted in f52a7f5. The condition is back to the original not self.update_method == "transactional", and the PR diff no longer touches that line.
| @mock.patch("cloudinit.distros.opensuse.subp.subp") | ||
| class TestPackageCommands: | ||
| distro = distros.fetch("opensuse")("opensuse", {}, None) | ||
| distro: Any = distros.fetch("opensuse")("opensuse", {}, None) |
There was a problem hiding this comment.
Same comment as above regarding Any.
There was a problem hiding this comment.
The fixture now uses the concrete class instead of Any: distro: Distro = Distro("opensuse", {}, None), imported from cloudinit.distros.opensuse.
There was a problem hiding this comment.
These changes were addressed in a separate PR and are out of scope of type annotations. Please avoid making additional changes.
There was a problem hiding this comment.
Dropped. git diff upstream/main...HEAD -- tests/unittests/sources/test_azure_helper.py is empty on this branch and on the other six annotation branches. Main already has the parenthesization from #7157, so merging main brings it without a branch-specific change.
- Type the test's distro instance as the concrete opensuse Distro instead of Any, constructing it directly so mypy does not lose the module-specific attributes. - Drop the redundant update_method comment. - Restore the unrelated `not x == y` comparison; comparison style is outside a typing change. The removed result capture in the two read-only-root tests asserted a None return that the new -> None annotation already guarantees; the meaningful `not m_subp.called` assertion remains.
|
All five threads are addressed in f52a7f5, and I have replied to each inline comment. No force-push after review: the branch merges current main and the fixes are ordinary commits.
The remaining diff is Gates on this head: black 26.5.1 and ruff clean, mypy clean, 14 opensuse tests pass. |
Proposed Commit Message
Additional Context
Refs GH-5445. Claimed in #5445 (comment).
Removing both modules from the override list surfaces 23 errors, which
collapse into two problems:
opensuse.py:270andopensuse.py:276(plus the same shape inalpine.py:204, still overridden): the subclass property fills_preferred_ntp_clients, which the base class declares as a bare_preferred_ntp_clients = Noneatcloudinit/distros/__init__.py:145.mypy therefore types the attribute as
Noneand rejects everysubclass assignment of a list. The base property
(
__init__.py:654),ubuntu.py:54, andopensuse.pyall storelist[str]into it, so the declared type isOptional[List[str]].Annotating the declaration in the base class fixes every subclass at
once; no other module assigns or reads the private attribute.
test_opensuse.py(update_methodx18,read_only_rootx3): thefixture is built through
distros.fetch, annotated as returningType[Distro](the base class), whileupdate_methodandread_only_rootare opensuse-specific attributes set in the subclass__init__. The tests reset that state by assignment, so the fixtureis annotated
Anyrather than changing what is fetched or patched.No signatures change, so there is no effect on callers.
Test Steps
tox -e py3andtox -e check_formatto be confirmed by CI(developed on Windows where the full tox suite cannot run).
Merge type