Skip to content

chore: add type annotation to cloudinit.sources.helpers.azure - #7121

Open
anujsingh-cse wants to merge 4 commits into
canonical:mainfrom
anujsingh-cse:fix/untyped-defs-helpers-azure-5445
Open

anujsingh-cse wants to merge 4 commits into
canonical:mainfrom
anujsingh-cse:fix/untyped-defs-helpers-azure-5445

Conversation

@anujsingh-cse

Copy link
Copy Markdown
Contributor

Proposed Commit Message

chore: add type annotation to cloudinit.sources.helpers.azure

Removing cloudinit.sources.helpers.azure from the mypy override list
in pyproject.toml surfaces two errors in _decrypt_certs_from_xml.

The Data element lookup feeds certificates_content, which is then
encoded for the openssl call, and mypy sees both the Element and the
text as Optional. Raise InvalidGoalStateXMLException when the Data
element or its content is missing, matching the guard the caller
already applies to an empty certificates XML, instead of crashing
with an AttributeError deeper in the stack.

Refs GH-5445

Additional Context

Refs GH-5445. Claimed in #5445 (comment).

Removing the module from the override list surfaces two errors:

azure.py:512: error: Item "None" of "Element[str] | None"
           has no attribute "text"  [union-attr]
azure.py:519: error: Item "None" of "str | Any | None"
           has no attribute "encode"  [union-attr]

ET.fromstring(certificates_xml).find(".//Data") returns an
Optional[Element], and Element.text is itself Optional[str],
feeding certificates_content.encode(...) on the openssl call. A
single guard now raises the module's own
InvalidGoalStateXMLException when the Data element or content is
missing — the same exception the caller already raises for an empty
certificates XML (azure.py:413) — instead of an uncaught
AttributeError deeper in the stack. certificates_content is used
only once, so no other call sites are affected.

Test Steps

$ python -m mypy cloudinit/sources/helpers/azure.py
(0 errors with the override removed)

$ pytest tests/unittests/sources/test_azure_helper.py -q
104 passed, 2 skipped (the 2 Windows-only failures also fail on
untouched main; both involve os.fork/tmpdir paths that do not exist
on Windows)

$ ruff check / black --check / isort --check-only / pylint (target file)
All green

tox -e py3 and tox -e check_format to be confirmed by CI
(developed on Windows where the full tox suite cannot run).

Merge type

  • Squash merge using "Proposed Commit Message"
  • Rebase and merge unique commits. Requires commit messages per-commit each referencing the pull request number (#<PR_NUM>)

Removing cloudinit.sources.helpers.azure from the mypy override list
in pyproject.toml surfaces two errors in _decrypt_certs_from_xml.

The Data element lookup feeds certificates_content, which is then
encoded for the openssl call, and mypy sees both the Element and the
text as Optional. Raise InvalidGoalStateXMLException when the Data
element or its content is missing, matching the guard the caller
already applies to an empty certificates XML, instead of crashing
with an AttributeError deeper in the stack.

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.
@anujsingh-cse
anujsingh-cse force-pushed the fix/untyped-defs-helpers-azure-5445 branch from 7aee9f0 to cd2a5c0 Compare October 14, 2026 06:49

@nryanl nryanl left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nryanl nryanl added the incomplete Action required by submitter label Oct 19, 2026
Annotate every function in cloudinit.sources.helpers.azure so the module
no longer needs the check_untyped_defs exemption.

GoalState now resolves required xpath values through a helper that
returns str, preserving the existing InvalidGoalStateXMLException
diagnostics without a cast or an assert.

The property decode helper keeps an Any return because the value type
varies per call site (str, bytes or bool); default stays Any for the same
reason.
@anujsingh-cse

Copy link
Copy Markdown
Contributor Author

Rebased onto current main by merge — head bb5e480, no force-push after review. The duplicate tests/unittests/sources/test_azure_helper.py change is gone (empty diff against main).

All 33 functions in helpers/azure.py are annotated and the module is off the check_untyped_defs exemption list. Any survives in exactly two places, both genuinely heterogeneous:

  • the telemetry decorator's pass-through impl(*args: Any, **kwargs: Any);
  • _parse_property's value/default, whose type differs per call site (str, bytes or bool).

The GoalState attributes are str without a cast or assert: required values come from a new _required_text_from_xpath helper that raises the same InvalidGoalStateXMLException diagnostic when the xpath is missing. The secure-request header dict uses certificate or "" so its str values never see Optional[str].

Gates on this head: black 26.5.1, ruff and mypy clean. test_azure_helper.py + tests/unittests/sources/azure: 346 passed, 7 skipped; the 2 failures and 4 errors are the known Windows-only ones (os.getuid in temp_utils, KVP setup) — I re-ran the same suites on unmodified main and the failing/erroring test set is identical. The fresh CI runs will need approval to start.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

incomplete Action required by submitter

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants