)]}'
{"/PATCHSET_LEVEL":[{"author":{"_account_id":38828,"name":"Karan Anand","display_name":"Karan","email":"anandkarancompsci@gmail.com","username":"anandkaranubc"},"change_message_id":"7eff069b4bd1829749abcb1e3e686fde333f1c20","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":1,"id":"02f69e05_6928e77c","updated":"2026-03-01 01:00:19.000000000","message":"I don\u0027t think the `POST_FAILURE` is related to my changes.","commit_id":"fd2001ea6bca3b0cdb5842ed3dd90da41b780f12"},{"author":{"_account_id":36770,"name":"cid","display_name":"cid","email":"cid@gr-oss.io","username":"cidelight","status":"@gr-oss upstream: Doing good IRONIC things..."},"change_message_id":"ec6d10dbaabfa4fb4752aa7bc3114f317311f651","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":1,"id":"1eae593f_7e5e9286","updated":"2026-03-03 13:10:20.000000000","message":"recheck - POST_FAILURE","commit_id":"fd2001ea6bca3b0cdb5842ed3dd90da41b780f12"},{"author":{"_account_id":36770,"name":"cid","display_name":"cid","email":"cid@gr-oss.io","username":"cidelight","status":"@gr-oss upstream: Doing good IRONIC things..."},"change_message_id":"e73ecd4824c107eaac507da32c66622d57304e7d","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"35714c74_bd23e2f1","updated":"2026-03-11 14:35:53.000000000","message":"I tried to narrow down a few Any types by printing at runtime.","commit_id":"3d502b0dce6ec7248692b253eb9e930b93bf235b"},{"author":{"_account_id":10342,"name":"Jay Faulkner","display_name":"JayF","email":"jay@jvf.cc","username":"JayF","status":"youtube.com/@oss-gr / podcast.gr-oss.io"},"change_message_id":"32340abd06e4c1d97bf54f73fd570c5d006dd308","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":3,"id":"9cdb3aa6_1b5d8e0c","updated":"2026-03-11 14:59:02.000000000","message":"Please fix issues indicated by cid.","commit_id":"3d502b0dce6ec7248692b253eb9e930b93bf235b"},{"author":{"_account_id":38828,"name":"Karan Anand","display_name":"Karan","email":"anandkarancompsci@gmail.com","username":"anandkaranubc"},"change_message_id":"f17e6386f497f7e802d9be8476d9d5c38d4e0666","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":4,"id":"a6803c2e_537e8afd","updated":"2026-03-11 15:16:58.000000000","message":"Thanks @cid@gr-oss.io for the review! The changes have been added.","commit_id":"49eac0dac4fb786a465d259e5707dd916af1e5e5"}],"ironicclient/common/utils.py":[{"author":{"_account_id":36770,"name":"cid","display_name":"cid","email":"cid@gr-oss.io","username":"cidelight","status":"@gr-oss upstream: Doing good IRONIC things..."},"change_message_id":"e73ecd4824c107eaac507da32c66622d57304e7d","unresolved":true,"context_lines":[{"line_number":49,"context_line":"    subparsers: Any,"},{"line_number":50,"context_line":"    command: str,"},{"line_number":51,"context_line":"    callback: Callable[..., Any],"},{"line_number":52,"context_line":"    cmd_mapper: dict[str, Any],"},{"line_number":53,"context_line":") -\u003e None:"},{"line_number":54,"context_line":"    \"\"\"Define a command in the subparsers collection."},{"line_number":55,"context_line":""}],"source_content_type":"text/x-python","patch_set":3,"id":"74567128_9a0e991c","line":52,"range":{"start_line":52,"start_character":0,"end_line":52,"end_character":31},"updated":"2026-03-11 14:35:53.000000000","message":"cmd_mapper: `dict[str, Any]` to `dict[str, argparse.ArgumentParser]`\n\nSo, printing the type in the cli: `python3 -c \"import argparse; a \u003d argparse.ArgumentParser(); sp \u003d a.add_subparsers(); print(type(sp.\nadd_parser(\u0027test\u0027)))\"`","commit_id":"3d502b0dce6ec7248692b253eb9e930b93bf235b"},{"author":{"_account_id":36770,"name":"cid","display_name":"cid","email":"cid@gr-oss.io","username":"cidelight","status":"@gr-oss upstream: Doing good IRONIC things..."},"change_message_id":"e73ecd4824c107eaac507da32c66622d57304e7d","unresolved":true,"context_lines":[{"line_number":80,"context_line":""},{"line_number":81,"context_line":"def define_commands_from_module("},{"line_number":82,"context_line":"    subparsers: Any,"},{"line_number":83,"context_line":"    command_module: Any,"},{"line_number":84,"context_line":"    cmd_mapper: dict[str, Any],"},{"line_number":85,"context_line":") -\u003e None:"},{"line_number":86,"context_line":"    \"\"\"Add *do_* methods in a module and add as commands into a subparsers.\"\"\""}],"source_content_type":"text/x-python","patch_set":3,"id":"94533ff5_b2e9eb6a","line":83,"range":{"start_line":83,"start_character":0,"end_line":83,"end_character":24},"updated":"2026-03-11 14:35:53.000000000","message":"From the import to the usage, I don\u0027t think this takes any other type than a `types.ModuleType`.","commit_id":"3d502b0dce6ec7248692b253eb9e930b93bf235b"},{"author":{"_account_id":10342,"name":"Jay Faulkner","display_name":"JayF","email":"jay@jvf.cc","username":"JayF","status":"youtube.com/@oss-gr / podcast.gr-oss.io"},"change_message_id":"bddc20fb77ef0b53603744f86dd72ff70414eb2c","unresolved":true,"context_lines":[{"line_number":79,"context_line":"    subparser.set_defaults(func\u003dcallback)"},{"line_number":80,"context_line":""},{"line_number":81,"context_line":""},{"line_number":82,"context_line":"def define_commands_from_module("},{"line_number":83,"context_line":"    subparsers: Any,"},{"line_number":84,"context_line":"    command_module: types.ModuleType,"},{"line_number":85,"context_line":"    cmd_mapper: dict[str, argparse.ArgumentParser],"}],"source_content_type":"text/x-python","patch_set":4,"id":"b16a5114_b876e75c","line":82,"updated":"2026-03-11 17:25:33.000000000","message":"https://github.com/openstack/python-ironicclient/commit/f62758cda5953ac22614a783df0165e42e76d60b\n\ndefine_command and define_commands_from_module are dead code and should\u0027be been removed with the above commit AFAICT. I\u0027ve audited openstackclient and osc-lib, there are no calls to it, and it only shows up in a couple client implementations using codesearch. let\u0027s remove it rather than typing it (and please put the removal in a separate change)","commit_id":"49eac0dac4fb786a465d259e5707dd916af1e5e5"},{"author":{"_account_id":10342,"name":"Jay Faulkner","display_name":"JayF","email":"jay@jvf.cc","username":"JayF","status":"youtube.com/@oss-gr / podcast.gr-oss.io"},"change_message_id":"ef99b0a2545ae5f53ad0d0aaeb0cb6071902f487","unresolved":true,"context_lines":[{"line_number":79,"context_line":"    subparser.set_defaults(func\u003dcallback)"},{"line_number":80,"context_line":""},{"line_number":81,"context_line":""},{"line_number":82,"context_line":"def define_commands_from_module("},{"line_number":83,"context_line":"    subparsers: Any,"},{"line_number":84,"context_line":"    command_module: types.ModuleType,"},{"line_number":85,"context_line":"    cmd_mapper: dict[str, argparse.ArgumentParser],"}],"source_content_type":"text/x-python","patch_set":4,"id":"476a63fa_869213e8","line":82,"in_reply_to":"b16a5114_b876e75c","updated":"2026-03-11 17:28:53.000000000","message":"claude says HelpFormatter can go too:\n\n● These functions are dead code. The only caller of define_command is define_commands_from_module, and nothing calls define_commands_from_module. The\n  old ironic CLI that used them was removed in commit f62758c (2019).                                                                                 \n                                                                                                                                                      \n  The reason subparsers is hard to type: argparse.ArgumentParser.add_subparsers() returns argparse._SubParsersAction[ArgumentParser], and             \n  _SubParsersAction is a private/underscore-prefixed class. The proper public type would be argparse._SubParsersAction[argparse.ArgumentParser], but  \n  since it\u0027s private API, Any is a reasonable escape hatch.                                                                                         \n\n  My recommendation: Since these functions have been dead code for ~7 years, the simplest fix is to just remove them entirely along with HelpFormatter\n   (which is only used by define_command). No need to struggle with typing code that has no callers.","commit_id":"49eac0dac4fb786a465d259e5707dd916af1e5e5"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"8d011ca913f927d7aadaa47d5f6c594fecbe906d","unresolved":true,"context_lines":[{"line_number":26,"context_line":"import tempfile"},{"line_number":27,"context_line":"import time"},{"line_number":28,"context_line":"from typing import Any"},{"line_number":29,"context_line":"from typing import Callable"},{"line_number":30,"context_line":"from typing import cast"},{"line_number":31,"context_line":"from typing import Generator"},{"line_number":32,"context_line":"from typing import Iterator"}],"source_content_type":"text/x-python","patch_set":6,"id":"ebe88843_0a4fee48","line":29,"updated":"2026-03-19 11:18:48.000000000","message":"This is a deprecated alias. You should import this from `collections.abc` instead.","commit_id":"4a3a1a682d68a817398e69d94588209cc3f04161"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"8d011ca913f927d7aadaa47d5f6c594fecbe906d","unresolved":true,"context_lines":[{"line_number":28,"context_line":"from typing import Any"},{"line_number":29,"context_line":"from typing import Callable"},{"line_number":30,"context_line":"from typing import cast"},{"line_number":31,"context_line":"from typing import Generator"},{"line_number":32,"context_line":"from typing import Iterator"},{"line_number":33,"context_line":"from typing import Protocol"},{"line_number":34,"context_line":""},{"line_number":35,"context_line":"from cliff import columns"}],"source_content_type":"text/x-python","patch_set":6,"id":"8215d9ed_f934a353","line":32,"range":{"start_line":31,"start_character":0,"end_line":32,"end_character":27},"updated":"2026-03-19 11:18:48.000000000","message":"As above","commit_id":"4a3a1a682d68a817398e69d94588209cc3f04161"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"8d011ca913f927d7aadaa47d5f6c594fecbe906d","unresolved":true,"context_lines":[{"line_number":34,"context_line":""},{"line_number":35,"context_line":"from cliff import columns"},{"line_number":36,"context_line":"from oslo_utils import strutils"},{"line_number":37,"context_line":"import yaml  # type: ignore[import-untyped]  # PyYAML has no type stubs"},{"line_number":38,"context_line":""},{"line_number":39,"context_line":"from ironicclient.common.i18n import _"},{"line_number":40,"context_line":"from ironicclient import exc"}],"source_content_type":"text/x-python","patch_set":6,"id":"d41af59b_157d120c","line":37,"range":{"start_line":37,"start_character":15,"end_line":37,"end_character":23},"updated":"2026-03-19 11:18:48.000000000","message":"You want `types-PyYAMl`. If you run mypy with the `--install-types` flag it will automatically suggest stub packages for libraries that don\u0027t have them included.","commit_id":"4a3a1a682d68a817398e69d94588209cc3f04161"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"8d011ca913f927d7aadaa47d5f6c594fecbe906d","unresolved":true,"context_lines":[{"line_number":156,"context_line":""},{"line_number":157,"context_line":""},{"line_number":158,"context_line":"def common_params_for_list("},{"line_number":159,"context_line":"    args: ListCommandArgs,"},{"line_number":160,"context_line":"    fields: list[str],"},{"line_number":161,"context_line":"    field_labels: list[str],"},{"line_number":162,"context_line":") -\u003e dict[str, Any]:"}],"source_content_type":"text/x-python","patch_set":6,"id":"5ee9328e_50a3e668","line":159,"updated":"2026-03-19 11:18:48.000000000","message":"I get what you\u0027re doing here, but this is an `argparse.Namespace` instance. I\u0027m surprised this isn\u0027t being flagged by mypy (I suspect it will be in strict mode) since the attibutes you\u0027ve given for this protocol do not exist on `Namespace`...","commit_id":"4a3a1a682d68a817398e69d94588209cc3f04161"},{"author":{"_account_id":10342,"name":"Jay Faulkner","display_name":"JayF","email":"jay@jvf.cc","username":"JayF","status":"youtube.com/@oss-gr / podcast.gr-oss.io"},"change_message_id":"d8d6c0450ff264f071f0aee441ee37a0cbf956a4","unresolved":true,"context_lines":[{"line_number":249,"context_line":"    return filters"},{"line_number":250,"context_line":""},{"line_number":251,"context_line":""},{"line_number":252,"context_line":"# NOTE(karan): *args/**kwargs can be narrowed to match tempfile.mkdtemp:"},{"line_number":253,"context_line":"# use keyword-only params (suffix: str | None, prefix: str | None,"},{"line_number":254,"context_line":"# dir: str | None) and call mkdtemp(suffix\u003d..., prefix\u003d..., dir\u003d...) for"},{"line_number":255,"context_line":"# stricter typing."}],"source_content_type":"text/x-python","patch_set":6,"id":"605fc339_7259c081","line":252,"updated":"2026-03-18 15:22:48.000000000","message":"nit: s/NOTE/TODO/","commit_id":"4a3a1a682d68a817398e69d94588209cc3f04161"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"8d011ca913f927d7aadaa47d5f6c594fecbe906d","unresolved":true,"context_lines":[{"line_number":357,"context_line":"    except ValueError as e:"},{"line_number":358,"context_line":"        raise exc.CommandError(_(\"argument %(arg)s: %(err)s.\")"},{"line_number":359,"context_line":"                               % {\u0027arg\u0027: arg_name, \u0027err\u0027: e})"},{"line_number":360,"context_line":"    return cast(bool, val)"},{"line_number":361,"context_line":""},{"line_number":362,"context_line":""},{"line_number":363,"context_line":"def check_for_invalid_fields("}],"source_content_type":"text/x-python","patch_set":6,"id":"d58f7316_af9002dd","line":360,"updated":"2026-03-19 11:18:48.000000000","message":"oslo.utils is typed so this shouldn\u0027t be necessary. It\u0027s happening because you\u0027re using pre-commit for running mypy and haven\u0027t included oslo.utils in `additional_dependencies`. This is the exact reason I\u0027ve stopped using pre-commit to run mypy and switched everything else to tox instead.","commit_id":"4a3a1a682d68a817398e69d94588209cc3f04161"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"8d011ca913f927d7aadaa47d5f6c594fecbe906d","unresolved":true,"context_lines":[{"line_number":360,"context_line":"    return cast(bool, val)"},{"line_number":361,"context_line":""},{"line_number":362,"context_line":""},{"line_number":363,"context_line":"def check_for_invalid_fields("},{"line_number":364,"context_line":"    fields: list[str] | None,"},{"line_number":365,"context_line":"    valid_fields: list[str],"},{"line_number":366,"context_line":") -\u003e None:"},{"line_number":367,"context_line":"    \"\"\"Check for invalid fields."},{"line_number":368,"context_line":""}],"source_content_type":"text/x-python","patch_set":6,"id":"aded2fb6_93e7146b","line":365,"range":{"start_line":363,"start_character":29,"end_line":365,"end_character":28},"updated":"2026-03-19 11:18:48.000000000","message":"You can be less specific here if you want. This just needs to be iterable fwict.\n\n\n```suggestion\ndef check_for_invalid_fields(\n    fields: Iterable[str] | None,\n    valid_fields: Iterable[str],\n```\n\n(`Iterable` comes from `collections.abc`)","commit_id":"4a3a1a682d68a817398e69d94588209cc3f04161"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"8d011ca913f927d7aadaa47d5f6c594fecbe906d","unresolved":true,"context_lines":[{"line_number":417,"context_line":"                    \"Error: %(err)s\") % {\u0027err\u0027: e, \u0027file\u0027: json_arg}"},{"line_number":418,"context_line":"            raise exc.InvalidAttribute(err)"},{"line_number":419,"context_line":"    try:"},{"line_number":420,"context_line":"        result: list[Any] | dict[str, Any] \u003d json.loads(json_arg)"},{"line_number":421,"context_line":"    except ValueError as e:"},{"line_number":422,"context_line":"        err \u003d (_(\"Value \u0027%(string)s\u0027 is not a file and cannot be parsed \""},{"line_number":423,"context_line":"                 \"as JSON: \u0027%(err)s\u0027\") % {\u0027err\u0027: e, \u0027string\u0027: json_arg})"}],"source_content_type":"text/x-python","patch_set":6,"id":"3c4af4d0_194af256","line":420,"updated":"2026-03-19 11:18:48.000000000","message":"Is this true? The below is a valid JSON file:\n\n```\nnull\n```\n\nAs is this:\n\n```\n1\n```\n\nI suspect the return type for this function is in fact `object`","commit_id":"4a3a1a682d68a817398e69d94588209cc3f04161"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"8d011ca913f927d7aadaa47d5f6c594fecbe906d","unresolved":true,"context_lines":[{"line_number":497,"context_line":"    except ValueError:"},{"line_number":498,"context_line":"        return None"},{"line_number":499,"context_line":""},{"line_number":500,"context_line":""},{"line_number":501,"context_line":"def format_hash("},{"line_number":502,"context_line":"    data: dict[str, Any] | None,"},{"line_number":503,"context_line":"    prefix: str \u003d \"\","},{"line_number":504,"context_line":") -\u003e str | None:"},{"line_number":505,"context_line":"    if data is None:"},{"line_number":506,"context_line":"        return None"},{"line_number":507,"context_line":""}],"source_content_type":"text/x-python","patch_set":6,"id":"39f767da_7680ac83","line":504,"range":{"start_line":500,"start_character":1,"end_line":504,"end_character":16},"updated":"2026-03-19 11:18:48.000000000","message":"We can be more specific here\n\n```suggestion\n\n@overload\ndef format_hash(data: dict[str, Any], prefix: str \u003d \"\") -\u003e str:\n    ...\n\n\n@overload\ndef format_hash(data: None, prefix: str \u003d \"\") -\u003e None:\n    ...\n\n\ndef format_hash(data: dict[str, Any] | None, prefix: str \u003d \"\") -\u003e str | None:\n```","commit_id":"4a3a1a682d68a817398e69d94588209cc3f04161"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"8d011ca913f927d7aadaa47d5f6c594fecbe906d","unresolved":true,"context_lines":[{"line_number":521,"context_line":""},{"line_number":522,"context_line":""},{"line_number":523,"context_line":"class HashColumn(columns.FormattableColumn):"},{"line_number":524,"context_line":"    # base returns Sequence; we return str | None"},{"line_number":525,"context_line":"    def human_readable(self) -\u003e str | None:  # type: ignore[override]"},{"line_number":526,"context_line":"        return format_hash(self._value)"}],"source_content_type":"text/x-python","patch_set":6,"id":"199d3e62_b8312625","line":524,"updated":"2026-03-19 11:18:48.000000000","message":"This looks like a valid bug: human readable fields should always be returned as strings. You probably want to add `or \u0027\u0027` to the return value below.","commit_id":"4a3a1a682d68a817398e69d94588209cc3f04161"}]}
