Skip to content

chore: add type annotations to cloudinit.netinfo#6930

Open
ashmitjsg wants to merge 6 commits into
canonical:mainfrom
ashmitjsg:fix/typing-cloudinit-netinfo
Open

chore: add type annotations to cloudinit.netinfo#6930
ashmitjsg wants to merge 6 commits into
canonical:mainfrom
ashmitjsg:fix/typing-cloudinit-netinfo

Conversation

@ashmitjsg

Copy link
Copy Markdown
  • I have signed the CLA: https://ubuntu.com/legal/contributors
  • I have included a comprehensive commit message using the guide below
  • I have added unit tests to cover the new behavior under tests/unittests/
    • No new tests: this is a typing-only change with no runtime behavior change;
      the existing tests/unittests/test_netinfo.py suite passes unmodified.
  • I have kept the change small, avoiding unnecessary whitespace or non-functional changes.
  • I have added a reference to issues that this PR relates to in the PR message (Refs [enhancement]: fix typing of untyped-defs #5445)
  • I have updated the documentation with the changed behavior.
    • Skipped: no user-facing or interface change.

Proposed Commit Message

chore: add type annotations to cloudinit.netinfo

Enable mypy's check_untyped_defs for cloudinit.netinfo (removed from the override list in pyproject.toml) 
by threading the existing Interface TypedDict through the netdev parsers and fixing the netdev_info return type.

Refs GH-5445

Additional Context

Part of the GH-5445 effort to enable check_untyped_defs module by module, following the same approach as #6861 and #6916.

Almost all of the errors resolved by annotating the devs containers and DEFAULT_NETDEV_INFO with the existing Interface TypedDict. Enabling the check also surfaced two genuine issues, fixed here:

  • netdev_info was annotated Dict[str, Dict[str, Interface]] but actually returns Dict[str, Interface]; the annotation is corrected.
  • in _netdev_info_iproute an address line appearing before the first device header would index devs with an unset (None) device name; such lines are now skipped, avoiding a latent KeyError.

Test Steps

tox -e mypy    # passes with cloudinit.netinfo removed from the override list
tox -e py3 -- tests/unittests/test_netinfo.py

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>)

Enable mypy's check_untyped_defs for cloudinit.netinfo (removed from the
override list in pyproject.toml) by threading the existing Interface
TypedDict through the netdev parsers and fixing the netdev_info return type.
Comment thread cloudinit/netinfo.py

LOG = logging.getLogger(__name__)


Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please don't make unnecessary whitespace changes.

This comment applies other places too.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Removed the whitespace change; that blank line was done by black enforcing 2 lines before a top-level class, which showed up because I'd moved DEFAULT_NETDEV_INFO below Interface. I've kept it in its original position and used a forward-reference annotation ("Interface"), since the module-level annotation is evaluated at import while the class is defined just below.

Comment thread cloudinit/netinfo.py Outdated
Comment on lines +137 to +139
elif dev_name is None:
# Skip any address lines appearing before the first device header
continue

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This would have caused a KeyError exception for the following conditions before this change. Unless we have a good reason to change that behavior, lets please raise that exception here too.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done.

@ashmitjsg
ashmitjsg requested a review from holmanb July 16, 2026 02:00
Comment thread cloudinit/netinfo.py Outdated
)

ipv6_addrs = data.get("ipv6")
ipv6_addrs = data["ipv6"]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This will cause a KeyError when the key doesn't exist. Can we just use an empty iterable as the default when the key doesn't exist?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed it

@ashmitjsg
ashmitjsg requested a review from holmanb July 17, 2026 02:00
Comment thread cloudinit/netinfo.py Outdated
# 'ipv6': [{'ip': '::1/128', 'scope6': 'host'}],
# 'up': True}}
DEFAULT_NETDEV_INFO = {"ipv4": [], "ipv6": [], "hwaddr": "", "up": False}
DEFAULT_NETDEV_INFO: "Interface" = {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we move this definition down a few lines so we don't need the string type definition?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Hi @holmanb, I did it, but it comes back to the original situation as we were before reverting the extra line introduced by the black: With DEFAULT_NETDEV_INFO below the class, class Interface becomes the first top-level construct after LOG, and black enforces two blank lines before a top-level class.

@ashmitjsg
ashmitjsg requested a review from holmanb July 17, 2026 16:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants