-
Notifications
You must be signed in to change notification settings - Fork 1.1k
fix(cmd): enable mypy strict checking for cloudinit.cmd #6861
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
9624efc
ac1ccdb
3c2dc87
db7b5a7
79370b4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,7 +19,7 @@ | |
| import traceback | ||
| import logging | ||
| import yaml | ||
| from typing import Optional, Tuple, Callable, Union | ||
| from typing import Any, Optional, Tuple, Callable, Union | ||
|
|
||
| from cloudinit import features, netinfo | ||
| from cloudinit import signal_handler | ||
|
|
@@ -83,6 +83,13 @@ class SubcommandAwareArgumentParser(argparse.ArgumentParser): | |
| def __init__(self, *args, **kwargs): | ||
| super().__init__(*args, **kwargs) | ||
| self._raw_args = None | ||
| self._subparsers_action = None | ||
|
|
||
| def add_subparsers(self, **kwargs): | ||
| """Override to capture the subparsers action for use in error().""" | ||
| action = super().add_subparsers(**kwargs) | ||
| self._subparsers_action = action | ||
| return action | ||
|
|
||
| def parse_args(self, args=None, namespace=None): | ||
| """Override parse_args to store raw arguments for error handling.""" | ||
|
|
@@ -98,18 +105,20 @@ def error(self, message): | |
| if not self._raw_args: | ||
| self._raw_args = sys.argv[1:] | ||
| subcommand = None | ||
| if self._raw_args: | ||
| for arg in self._raw_args: | ||
| if arg in self._subparsers._group_actions[0].choices: | ||
| subcommand = arg | ||
| break | ||
| # Check if the subcommand exists and show its help | ||
| if not self._subparsers_action: | ||
| self.print_help(file=sys.stderr) | ||
| sys.exit(2) | ||
|
|
||
| choices = self._subparsers_action.choices | ||
| for arg in self._raw_args: | ||
| if arg in choices: | ||
| subcommand = arg | ||
| break | ||
|
|
||
| # Check if the subcommand exists and show its help | ||
| if subcommand: | ||
| subparser = self._subparsers._group_actions[0].choices[subcommand] | ||
| subparser.print_help( | ||
| file=sys.stderr | ||
| ) # Print subcommand help to stderr | ||
| # Print subcommand help to stderr | ||
| choices[subcommand].print_help(file=sys.stderr) | ||
| else: | ||
| self.print_help(file=sys.stderr) | ||
| sys.exit(2) | ||
|
|
@@ -546,6 +555,13 @@ def main_init(name, args): | |
| bring_up_interfaces = _should_bring_up_interfaces(init, args) | ||
| try: | ||
| init.fetch(existing=existing) | ||
| if init.datasource is None: | ||
| LOG.debug( | ||
| "[%s] Exiting. datasource is None after fetch," | ||
| " cannot continue.", | ||
| mode, | ||
| ) | ||
| return (None, []) | ||
| # if in network mode, and the datasource is local | ||
| # then work was done at that stage. | ||
| if mode == sources.DSMODE_NETWORK and init.datasource.dsmode != mode: | ||
|
|
@@ -613,6 +629,13 @@ def main_init(name, args): | |
| ) | ||
| util.write_file(init.paths.get_runpath(".skip-network"), "") | ||
|
|
||
| if init.datasource is None: | ||
| LOG.debug( | ||
| "[%s] Exiting. datasource is None in local mode," | ||
| " cannot check dsmode.", | ||
| mode, | ||
| ) | ||
| return (None, []) | ||
| if init.datasource.dsmode != mode: | ||
| LOG.debug( | ||
| "[%s] Exiting. datasource %s not in local mode.", | ||
|
|
@@ -912,13 +935,13 @@ def status_wrapper(name, args): | |
| "Invalid cloud init mode specified '{0}'".format(mode) | ||
| ) | ||
|
|
||
| nullstatus = { | ||
| nullstatus: dict[str, list[Any] | dict[str, Any] | None] = { | ||
| "errors": [], | ||
| "recoverable_errors": {}, | ||
| "start": None, | ||
| "finished": None, | ||
| } | ||
| status = { | ||
| status: dict[str, Any] = { | ||
| "v1": { | ||
| "datasource": None, | ||
| "init": nullstatus.copy(), | ||
|
|
@@ -955,6 +978,8 @@ def status_wrapper(name, args): | |
| lambda h: isinstance(h, loggers.LogExporter), root_logger.handlers | ||
| ) | ||
| ) | ||
| if not isinstance(handler, loggers.LogExporter): | ||
| raise RuntimeError("LogExporter handler not found in root logger") | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be preferred to avoid changing control flow. Same comment elsewhere in this file.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assume the "elsewhere" applies to the changes in lines 557-633 and 631-637 (let me know if I am missing some other blocks of code). In this case, the three things that come to mind to solve this mypy error are using
If you have some other fix in mind I am skipping, please let me know! |
||
| preexisting_recoverable_errors = handler.export_logs() | ||
|
|
||
| # Write status.json prior to running init / module code | ||
|
|
@@ -1052,7 +1077,7 @@ def _maybe_set_hostname(init, stage, retry_stage): | |
| ) | ||
| if hostname: # meta-data or user-data hostname content | ||
| try: | ||
| cc_set_hostname.handle("set_hostname", init.cfg, cloud, None) | ||
| cc_set_hostname.handle("set_hostname", init.cfg, cloud, []) | ||
| except cc_set_hostname.SetHostnameError as e: | ||
| LOG.debug( | ||
| "Failed setting hostname in %s stage. Will" | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is odd that this type is not a string in the first place. Creating a tuple to append to a list of strings is unnecessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That said, this doesn't need to turn into refactoring. I'd just name the later re-assignment something else like "mime_msg".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the feedback!
Just to clarify my understanding:
(content_type, i + 1)does form a tuple as the right operand to%, which is standard for%-formatting with multiple arguments. However, the%operator itself returns astrdirectly, so what gets appended to errors is already a string, not a tuple. I believe the parentheses around the template string on the left were purely for line wrapping clarity. I have done a quick test to verify the typing and this assumption seems to be correct!That said, I'm happy to rename the variable from
err_msgtomime_msgor to split the formatting logic into several variables if it helps making the code clearer.What do you think is the best option?