)]}'
{"/PATCHSET_LEVEL":[{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"94a4264c38e287587879b77320dc4acaf7fa97e7","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"1012bf98_f5a37203","updated":"2026-07-08 17:22:10.000000000","message":"lgtm, thanks for adding this!","commit_id":"05e028088484901cc8d55b52ad495d3298018883"}],"cyborg/hacking/__init__.py":[{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"31d2542521d3575870f3809d19a302079a9da57d","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":1,"id":"b10a83af_dd346fff","line":1,"updated":"2026-06-30 18:30:21.000000000","message":"The empty namespace marker cyborg/hacking/__init__.py (0 bytes) is added without an Apache 2.0 license header, unlike the other two new files which carry the full header.\n\n**Severity**: WARNING | **Confidence**: 0.7\n\n**Impact**: OpenStack licensing policy expects source files to carry the Apache header. While empty __init__.py markers are sometimes tolerated (and pbr/setuptools packaging is unaffected), a bare empty file is inconsistent with the sibling files in the same commit and some automated license scanners flag it.\n\n**Suggestion**:\nEither add the standard Apache 2.0 license header to __init__.py for consistency with checks.py and the test file, or confirm the project accepts bare empty package markers. Adding the header is low-cost and removes ambiguity.","commit_id":"85306aa866ebabca1f7294e67ab54b52fb37cb5f"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"e497ebf6907aff4f8acb1d161f2a3c9473caa151","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":1,"id":"06ce005f_26da0343","line":1,"in_reply_to":"b10a83af_dd346fff","updated":"2026-06-30 20:55:26.000000000","message":"oh i see ya this is incorrect\n\nill see if i can fix that","commit_id":"85306aa866ebabca1f7294e67ab54b52fb37cb5f"},{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"02464ff03c0534967e92e213840178ff3964d49b","unresolved":false,"context_lines":[{"line_number":1,"context_line":"# Licensed under the Apache License, Version 2.0 (the \"License\"); you may"},{"line_number":2,"context_line":"# not use this file except in compliance with the License. You may obtain"},{"line_number":3,"context_line":"# a copy of the License at"},{"line_number":4,"context_line":"#"}],"source_content_type":"text/x-python","patch_set":2,"id":"aad655a0_ed83add1","line":1,"updated":"2026-06-30 19:39:19.000000000","message":"pep8: H104: File contains nothing but comments","commit_id":"0428dcee53fcbe9af095d7c9fec2fc1a4f5eff38"},{"author":{"_account_id":4690,"name":"melanie witt","display_name":"melwitt","email":"melwittt@gmail.com","username":"melwitt"},"change_message_id":"1388a9c8bd97163840ff153b7973794772166dec","unresolved":false,"context_lines":[{"line_number":1,"context_line":"# Licensed under the Apache License, Version 2.0 (the \"License\"); you may"},{"line_number":2,"context_line":"# not use this file except in compliance with the License. You may obtain"},{"line_number":3,"context_line":"# a copy of the License at"},{"line_number":4,"context_line":"#"}],"source_content_type":"text/x-python","patch_set":2,"id":"98f510f8_fcead908","line":1,"in_reply_to":"38fb8536_a0cf327f","updated":"2026-06-30 20:17:00.000000000","message":"Bah, teim-ci led us astray on this one.","commit_id":"0428dcee53fcbe9af095d7c9fec2fc1a4f5eff38"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"33bdf25d11e76be9a56b3aeb78045f2c6424b6fd","unresolved":true,"context_lines":[{"line_number":1,"context_line":"# Licensed under the Apache License, Version 2.0 (the \"License\"); you may"},{"line_number":2,"context_line":"# not use this file except in compliance with the License. You may obtain"},{"line_number":3,"context_line":"# a copy of the License at"},{"line_number":4,"context_line":"#"}],"source_content_type":"text/x-python","patch_set":2,"id":"70390772_d917e5b9","line":1,"in_reply_to":"38fb8536_a0cf327f","updated":"2026-06-30 20:50:48.000000000","message":"Marked as unresolved.","commit_id":"0428dcee53fcbe9af095d7c9fec2fc1a4f5eff38"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"edbe35003ae3f9bd13e4918fbf601fe9d3f7ebe8","unresolved":false,"context_lines":[{"line_number":1,"context_line":"# Licensed under the Apache License, Version 2.0 (the \"License\"); you may"},{"line_number":2,"context_line":"# not use this file except in compliance with the License. You may obtain"},{"line_number":3,"context_line":"# a copy of the License at"},{"line_number":4,"context_line":"#"}],"source_content_type":"text/x-python","patch_set":2,"id":"72f01fde_edd37dc3","line":1,"in_reply_to":"70390772_d917e5b9","updated":"2026-07-02 16:05:16.000000000","message":"Done","commit_id":"0428dcee53fcbe9af095d7c9fec2fc1a4f5eff38"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"34d21ab6f8c57b6c3c6e0167f95562e91d33b2ba","unresolved":false,"context_lines":[{"line_number":1,"context_line":"# Licensed under the Apache License, Version 2.0 (the \"License\"); you may"},{"line_number":2,"context_line":"# not use this file except in compliance with the License. You may obtain"},{"line_number":3,"context_line":"# a copy of the License at"},{"line_number":4,"context_line":"#"}],"source_content_type":"text/x-python","patch_set":2,"id":"38fb8536_a0cf327f","line":1,"in_reply_to":"aad655a0_ed83add1","updated":"2026-06-30 20:04:12.000000000","message":"oh right just leave this blank and it will be happy","commit_id":"0428dcee53fcbe9af095d7c9fec2fc1a4f5eff38"}],"cyborg/hacking/checks.py":[{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"31d2542521d3575870f3809d19a302079a9da57d","unresolved":false,"context_lines":[{"line_number":14,"context_line":"import re"},{"line_number":15,"context_line":""},{"line_number":16,"context_line":""},{"line_number":17,"context_line":"NAMED_PLACEHOLDER_RE \u003d re.compile(r\"%\\([^)]+\\)\")"},{"line_number":18,"context_line":""},{"line_number":19,"context_line":"LOG_METHODS \u003d frozenset("},{"line_number":20,"context_line":"    ("}],"source_content_type":"text/x-python","patch_set":1,"id":"b4f4f0a0_68d1f20f","line":17,"updated":"2026-06-30 18:30:21.000000000","message":"The NAMED_PLACEHOLDER_RE \u003d re.compile(r\"%\\([^)]+\\)\") pattern matches %(name) but does not require a trailing conversion/type character (e.g. the \u0027s\u0027 in %(key)s).\n\n**Severity**: SUGGESTION | **Confidence**: 0.7\n\n**Benefit**: Tightening the regex to require a type suffix would make the heuristic slightly more precise, matching only real named format specs and reducing the already-low chance of a false positive on strings that contain %(foo) without a conversion.\n\n**Recommendation**:\nConsider anchoring the placeholder regex to the full %-mapping spec (e.g. r\"%\\([^)]+\\)[a-zA-Z%]\"). This is a minor refinement; the current regex is conservative (only flags when a Set literal is present, a strong signal) so the practical false-positive risk is negligible. Treat as optional polish.","commit_id":"85306aa866ebabca1f7294e67ab54b52fb37cb5f"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"31d2542521d3575870f3809d19a302079a9da57d","unresolved":false,"context_lines":[{"line_number":85,"context_line":"            isinstance(node.func, ast.Attribute)"},{"line_number":86,"context_line":"            and node.func.attr in LOG_METHODS"},{"line_number":87,"context_line":"        ):"},{"line_number":88,"context_line":"            if node.func.attr \u003d\u003d \"log\":"},{"line_number":89,"context_line":"                fmt_idx, dict_idx \u003d 1, 2"},{"line_number":90,"context_line":"            else:"},{"line_number":91,"context_line":"                fmt_idx, dict_idx \u003d 0, 1"}],"source_content_type":"text/x-python","patch_set":1,"id":"376757d6_ac2018ca","line":88,"updated":"2026-06-30 18:30:21.000000000","message":"The check key constants fmt_idx/dict_idx (0/1 for most methods, 1/2 for .log()) encode an implicit assumption about the logging call signature but the rationale is only visible from the .log() branch comment.\n\n**Severity**: SUGGESTION | **Confidence**: 0.8\n\n**Benefit**: Future maintainers immediately understand why index offsets differ for log() versus info()/error(), reducing the risk of an off-by-one regression if the offsets are ever adjusted.\n\n**Recommendation**:\nAdd a brief inline comment near the fmt_idx/dict_idx assignment noting that logging.Logger.log takes (level, msg, *args) so the format string sits at index 1 and the mapping at index 2, while the other methods take (msg, *args) with the mapping at index 1.","commit_id":"85306aa866ebabca1f7294e67ab54b52fb37cb5f"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"33bdf25d11e76be9a56b3aeb78045f2c6424b6fd","unresolved":true,"context_lines":[{"line_number":66,"context_line":"        \"C300 set literal passed where dict expected for %-format logging\""},{"line_number":67,"context_line":"    )"},{"line_number":68,"context_line":""},{"line_number":69,"context_line":"    def __init__(self, tree, filename):"},{"line_number":70,"context_line":"        self._tree \u003d tree"},{"line_number":71,"context_line":"        self._errors \u003d []"},{"line_number":72,"context_line":""},{"line_number":73,"context_line":"    def run(self):"},{"line_number":74,"context_line":"        self.visit(self._tree)"},{"line_number":75,"context_line":"        return self._errors"},{"line_number":76,"context_line":""},{"line_number":77,"context_line":"    def add_error(self, node, message\u003dNone):"},{"line_number":78,"context_line":"        message \u003d message or self.CHECK_DESC"},{"line_number":79,"context_line":"        self._errors.append("},{"line_number":80,"context_line":"            (node.lineno, node.col_offset, message, type(self))"},{"line_number":81,"context_line":"        )"},{"line_number":82,"context_line":""},{"line_number":83,"context_line":"    def visit_Call(self, node):"},{"line_number":84,"context_line":"        if ("}],"source_content_type":"text/x-python","patch_set":2,"id":"61985a58_955b2779","line":81,"range":{"start_line":69,"start_character":0,"end_line":81,"end_character":9},"updated":"2026-06-30 20:50:48.000000000","message":"ok os this alight with \n\nhttps://github.com/openstack/nova/blob/master/nova/hacking/checks.py#L160-L193","commit_id":"0428dcee53fcbe9af095d7c9fec2fc1a4f5eff38"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"edbe35003ae3f9bd13e4918fbf601fe9d3f7ebe8","unresolved":false,"context_lines":[{"line_number":66,"context_line":"        \"C300 set literal passed where dict expected for %-format logging\""},{"line_number":67,"context_line":"    )"},{"line_number":68,"context_line":""},{"line_number":69,"context_line":"    def __init__(self, tree, filename):"},{"line_number":70,"context_line":"        self._tree \u003d tree"},{"line_number":71,"context_line":"        self._errors \u003d []"},{"line_number":72,"context_line":""},{"line_number":73,"context_line":"    def run(self):"},{"line_number":74,"context_line":"        self.visit(self._tree)"},{"line_number":75,"context_line":"        return self._errors"},{"line_number":76,"context_line":""},{"line_number":77,"context_line":"    def add_error(self, node, message\u003dNone):"},{"line_number":78,"context_line":"        message \u003d message or self.CHECK_DESC"},{"line_number":79,"context_line":"        self._errors.append("},{"line_number":80,"context_line":"            (node.lineno, node.col_offset, message, type(self))"},{"line_number":81,"context_line":"        )"},{"line_number":82,"context_line":""},{"line_number":83,"context_line":"    def visit_Call(self, node):"},{"line_number":84,"context_line":"        if ("}],"source_content_type":"text/x-python","patch_set":2,"id":"6106ddd7_9e13e91e","line":81,"range":{"start_line":69,"start_character":0,"end_line":81,"end_character":9},"in_reply_to":"61985a58_955b2779","updated":"2026-07-02 16:05:16.000000000","message":"Acknowledged","commit_id":"0428dcee53fcbe9af095d7c9fec2fc1a4f5eff38"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"33bdf25d11e76be9a56b3aeb78045f2c6424b6fd","unresolved":true,"context_lines":[{"line_number":80,"context_line":"            (node.lineno, node.col_offset, message, type(self))"},{"line_number":81,"context_line":"        )"},{"line_number":82,"context_line":""},{"line_number":83,"context_line":"    def visit_Call(self, node):"},{"line_number":84,"context_line":"        if ("},{"line_number":85,"context_line":"            isinstance(node.func, ast.Attribute)"},{"line_number":86,"context_line":"            and node.func.attr in LOG_METHODS"}],"source_content_type":"text/x-python","patch_set":2,"id":"d859d142_e8976605","line":83,"range":{"start_line":83,"start_character":8,"end_line":83,"end_character":18},"updated":"2026-06-30 20:50:48.000000000","message":"and thne you implemint a python Call node visitor.\n\nok i was tryign to decied if you needed to use \n\n@core.flake8ext\n\nhttps://github.com/openstack/hacking/blob/master/hacking/core.py#L35-L41\n\nhackign itself does thing slightly difently \n\neither writign  the check as a single fucniton\n```\n@core.flake8ext\ndef hacking_assert_true_instance(logical_line):\n    \"\"\"Check for assertTrue(isinstance(a, b)) sentences\n\n    H211\n    \"\"\"\n    if RE_ASSERT_TRUE_INST.match(logical_line):\n        yield (\n            0,\n            \"H211: Use assert{Is,IsNot}instance\")\n\n```\not it tesd to skip defiening run and add error\n\nhttps://github.com/openstack/hacking/blob/master/hacking/checks/except_checks.py#L96-L164\n```\ndef _get_local_func_name(node):\n    if isinstance(node.func, ast.Attribute):\n        return node.func.attr\n    elif isinstance(node.func, ast.Name):\n        return node.func.id\n    else:\n        return None\n\n\nclass NoneArgChecker(ast.NodeVisitor):\n    \u0027\u0027\u0027NodeVisitor to check function calls for None arguments.\n\n    :param func_name: only check calls to functions with this name\n    :param num_args:  number of arguments to check for None\n\n    self.none_found will be True if any None arguments were found.\n    \u0027\u0027\u0027\n\n    def __init__(self, func_name, num_args\u003d2):\n        self.func_name \u003d func_name\n        self.num_args \u003d num_args\n        self.none_found \u003d False\n\n    def visit_Call(self, node):\n        local_func_name \u003d _get_local_func_name(node)\n\n        if local_func_name \u003d\u003d self.func_name:\n            args_to_check \u003d node.args[:self.num_args]\n            self.none_found |\u003d any(is_none(x) for x in args_to_check)\n        self.generic_visit(node)\n@core.flake8ext\n@core.off_by_default\ndef hacking_assert_is_none(logical_line, noqa):\n    \"\"\"Use assertIs(Not)None to check for None in assertions.\n\n    Okay: self.assertEqual(\u0027foo\u0027, \u0027bar\u0027)\n    Okay: self.assertNotEqual(\u0027foo\u0027, {}.get(\u0027bar\u0027, None))\n    Okay: self.assertIs(\u0027foo\u0027, \u0027bar\u0027)\n    Okay: self.assertIsNot(\u0027foo\u0027, \u0027bar\u0027, None)\n    Okay: foo(self.assertIsNot(\u0027foo\u0027, \u0027bar\u0027))\n    H203: self.assertEqual(None, \u0027foo\u0027)\n    H203: self.assertNotEqual(\u0027foo\u0027, None)\n    H203: self.assertIs(None, \u0027foo\u0027, \u0027bar\u0027)\n    H203: self.assertIsNot(\u0027foo\u0027, None, \u0027bar\u0027)\n    H203: foo(self.assertIsNot(\u0027foo\u0027, None, \u0027bar\u0027))\n    Okay: self.assertEqual(None, \u0027foo\u0027)  # noqa\n    Okay: self.assertIs(None, \u0027foo\u0027)  # noqa\n    Okay: self.assertIsNone(\u0027foo\u0027)\n    \"\"\"\n    if noqa:\n        return\n    for func_name in (\u0027assertEqual\u0027, \u0027assertIs\u0027, \u0027assertNotEqual\u0027,\n                      \u0027assertIsNot\u0027):\n        try:\n            start \u003d logical_line.index(\u0027.%s(\u0027 % func_name) + 1\n        except ValueError:\n            continue\n        checker \u003d NoneArgChecker(func_name)\n        try:\n            parsed_logical_line \u003d ast.parse(logical_line)\n        except SyntaxError:\n            # let flake8 catch this itself\n            # https://github.com/PyCQA/flake8/issues/1948\n            continue\n        checker.visit(parsed_logical_line)\n        if checker.none_found:\n            yield start, \"H203: Use assertIs(Not)None to check for None\"\n```\n\ni asked gemni why it hackign too the other aprpoch adn its apprenly for testablity but your tests seam to be pretty readable to me.\n\nhttps://github.com/openstack/hacking/blob/master/hacking/tests/checks/test_except_checks.py si shorter but i kind of prefer the way you wrote it.","commit_id":"0428dcee53fcbe9af095d7c9fec2fc1a4f5eff38"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"edbe35003ae3f9bd13e4918fbf601fe9d3f7ebe8","unresolved":false,"context_lines":[{"line_number":80,"context_line":"            (node.lineno, node.col_offset, message, type(self))"},{"line_number":81,"context_line":"        )"},{"line_number":82,"context_line":""},{"line_number":83,"context_line":"    def visit_Call(self, node):"},{"line_number":84,"context_line":"        if ("},{"line_number":85,"context_line":"            isinstance(node.func, ast.Attribute)"},{"line_number":86,"context_line":"            and node.func.attr in LOG_METHODS"}],"source_content_type":"text/x-python","patch_set":2,"id":"a327695d_5777a6ce","line":83,"range":{"start_line":83,"start_character":8,"end_line":83,"end_character":18},"in_reply_to":"d859d142_e8976605","updated":"2026-07-02 16:05:16.000000000","message":"Acknowledged","commit_id":"0428dcee53fcbe9af095d7c9fec2fc1a4f5eff38"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"1f9dfe7b4818fec59862dc94d93f33934a6c031f","unresolved":false,"context_lines":[{"line_number":20,"context_line":"    ("},{"line_number":21,"context_line":"        \"debug\","},{"line_number":22,"context_line":"        \"info\","},{"line_number":23,"context_line":"        \"warning\","},{"line_number":24,"context_line":"        \"error\","},{"line_number":25,"context_line":"        \"critical\","},{"line_number":26,"context_line":"        \"exception\","}],"source_content_type":"text/x-python","patch_set":3,"id":"6fc66f8c_8bcc997e","line":23,"updated":"2026-06-30 21:56:55.000000000","message":"LOG_METHODS includes \u0027exception\u0027, but logging.Logger.exception() is conventionally called as LOG.exception(msg) without a mapping argument (it captures exc_info). Including it is harmless, yet it widens the matched surface needlessly.\n\n**Severity**: SUGGESTION | **Confidence**: 0.7\n\n**Benefit**: Tighter scope reduces the chance of an unexpected future false positive and communicates intent: the check targets methods that commonly accept a %-mapping. Functionally the current set is correct and safe.\n\n**Recommendation**:\nConsider keeping \u0027exception\u0027 for symmetry (it does accept args) or add a one-line comment noting it is included for completeness. No code change required; this is a readability nit.","commit_id":"05e028088484901cc8d55b52ad495d3298018883"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"1f9dfe7b4818fec59862dc94d93f33934a6c031f","unresolved":false,"context_lines":[{"line_number":22,"context_line":"        \"info\","},{"line_number":23,"context_line":"        \"warning\","},{"line_number":24,"context_line":"        \"error\","},{"line_number":25,"context_line":"        \"critical\","},{"line_number":26,"context_line":"        \"exception\","},{"line_number":27,"context_line":"        \"log\","},{"line_number":28,"context_line":"    )"}],"source_content_type":"text/x-python","patch_set":3,"id":"a40fc807_e0e846d1","line":25,"updated":"2026-06-30 21:56:55.000000000","message":"_flatten_str_chain recurses into BinOp(Add) subtrees with no depth guard. A pathological format string built from a very long chain of \u0027+\u0027 concatenations could raise RecursionError during the lint pass.\n\n**Severity**: WARNING | **Confidence**: 0.6\n\n**Impact**: A flake8 crash on an unusually long concatenation chain would surface as a noisy tooling error rather than a graceful skip. Real logging format strings are never deeply nested, so this is theoretical, but a linter crashing on valid (if ugly) source is poor UX.\n\n**Suggestion**:\nWrap the recursion in a depth limit (e.g. return False after ~50 levels) or iterate left-spines iteratively. Given the realistic input, this is acceptable to defer; flagging only so the maintainer is aware of the unbounded recursion.","commit_id":"05e028088484901cc8d55b52ad495d3298018883"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"edbe35003ae3f9bd13e4918fbf601fe9d3f7ebe8","unresolved":false,"context_lines":[{"line_number":22,"context_line":"        \"info\","},{"line_number":23,"context_line":"        \"warning\","},{"line_number":24,"context_line":"        \"error\","},{"line_number":25,"context_line":"        \"critical\","},{"line_number":26,"context_line":"        \"exception\","},{"line_number":27,"context_line":"        \"log\","},{"line_number":28,"context_line":"    )"}],"source_content_type":"text/x-python","patch_set":3,"id":"7f059086_42145ea4","line":25,"in_reply_to":"a40fc807_e0e846d1","updated":"2026-07-02 16:05:16.000000000","message":"honestly that a feature not a bug\nif we have pathalogiclly long concationation chains instead of using a multi line string i want this to explode :)","commit_id":"05e028088484901cc8d55b52ad495d3298018883"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"1f9dfe7b4818fec59862dc94d93f33934a6c031f","unresolved":false,"context_lines":[{"line_number":90,"context_line":"                fmt_idx, dict_idx \u003d 1, 2"},{"line_number":91,"context_line":"            else:"},{"line_number":92,"context_line":"                fmt_idx, dict_idx \u003d 0, 1"},{"line_number":93,"context_line":""},{"line_number":94,"context_line":"            if len(node.args) \u003e\u003d dict_idx + 1 and isinstance("},{"line_number":95,"context_line":"                node.args[dict_idx], ast.Set"},{"line_number":96,"context_line":"            ):"}],"source_content_type":"text/x-python","patch_set":3,"id":"bb183f08_c227bd29","line":93,"updated":"2026-06-30 21:56:55.000000000","message":"The check does not flag a set literal passed as a keyword argument (e.g. LOG.info(\"%(a)s\", extra\u003d{\"a\", a}) or **kwargs), only positional args. The dict is assumed at a fixed positional index (1 for normal methods, 2 for log()).\n\n**Severity**: WARNING | **Confidence**: 0.8\n\n**Impact**: A set-as-mapping bug hidden behind a keyword argument or *args unpacking would slip past the check. In practice OpenStack logging almost always passes the mapping positionally, so the false-negative rate is low, but the gap is undocumented.\n\n**Suggestion**:\nEither document the positional-only scope in the CheckSetLiteralInLogging docstring (so future maintainers know kwargs are intentionally out of scope), or extend visit_Call to also inspect keyword arguments named for the context mapping. A docstring note is the lower-risk option for this patch.","commit_id":"05e028088484901cc8d55b52ad495d3298018883"}],"cyborg/tests/unit/test_hacking.py":[{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"31d2542521d3575870f3809d19a302079a9da57d","unresolved":false,"context_lines":[{"line_number":45,"context_line":"    def _assert_has_no_errors(self, code, checker, filename\u003dNone):"},{"line_number":46,"context_line":"        self._assert_has_errors(code, checker, filename\u003dfilename)"},{"line_number":47,"context_line":""},{"line_number":48,"context_line":"    def test_set_literal_attr_call(self):"},{"line_number":49,"context_line":"        checker \u003d checks.CheckSetLiteralInLogging"},{"line_number":50,"context_line":"        code \u003d \"\"\""},{"line_number":51,"context_line":"            import logging"}],"source_content_type":"text/x-python","patch_set":1,"id":"a44e9e10_03b53707","line":48,"updated":"2026-06-30 18:30:21.000000000","message":"The test suite lacks a case for the .warning(), .critical(), and .exception() log methods, exercising only info and log.\n\n**Severity**: SUGGESTION | **Confidence**: 0.7\n\n**Benefit**: Each entry in LOG_METHODS (debug, info, warning, error, critical, exception, log) is exercised, guarding against a future change that accidentally narrows LOG_METHODS or mis-tunes the index logic for one method.\n\n**Recommendation**:\nAdd at least one parametrized or representative positive/negative case for the remaining LOG_METHODS entries (warning, error, critical, exception, debug). Current coverage of info and log validates both the standard and the special-cased offset branches, so this is a robustness improvement rather than a correctness gap.","commit_id":"85306aa866ebabca1f7294e67ab54b52fb37cb5f"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"33bdf25d11e76be9a56b3aeb78045f2c6424b6fd","unresolved":true,"context_lines":[{"line_number":17,"context_line":"import pycodestyle"},{"line_number":18,"context_line":""},{"line_number":19,"context_line":"from cyborg.hacking import checks"},{"line_number":20,"context_line":"from cyborg.tests import base"},{"line_number":21,"context_line":""},{"line_number":22,"context_line":""},{"line_number":23,"context_line":"class HackingTestCase(base.TestCase):"}],"source_content_type":"text/x-python","patch_set":2,"id":"65d2f7a2_9de06957","line":20,"updated":"2026-06-30 20:50:48.000000000","message":"oh i guess this is why you added pycodestyle\n\nhttps://github.com/openstack/nova/blob/master/nova/tests/unit/test_hacking.py#L15-L21\n\nbecause the unit tests i.e. tox -e py3 need it not the pep8 target\n\nsince we do not list hackign that makes senes","commit_id":"0428dcee53fcbe9af095d7c9fec2fc1a4f5eff38"}],"test-requirements.txt":[{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"34d21ab6f8c57b6c3c6e0167f95562e91d33b2ba","unresolved":true,"context_lines":[{"line_number":6,"context_line":"doc8\u003e\u003d0.6.0 # Apache-2.0"},{"line_number":7,"context_line":"Pygments\u003e\u003d2.2.0 # BSD license"},{"line_number":8,"context_line":"WebTest\u003e\u003d2.0.27 # MIT"},{"line_number":9,"context_line":"pycodestyle\u003e\u003d2.11.0 # MIT"}],"source_content_type":"text/plain","patch_set":2,"id":"96aa8edd_20bb66a9","line":9,"range":{"start_line":9,"start_character":0,"end_line":9,"end_character":25},"updated":"2026-06-30 20:04:12.000000000","message":"so we run hacking via pre-commit so it not actully using these dependcies\nhackign itself uses flake8\n\nhttps://github.com/openstack/hacking/blob/master/requirements.txt#L1\nwhich depedn on pycodestyle\n\nhttps://github.com/PyCQA/flake8/blob/main/setup.cfg#L31\nso you shodul be abel to jsut drop this\n\nbut the correct way to add it to precommit woudl be to adde it here\nhttps://github.com/openstack/cyborg/blob/master/.pre-commit-config.yaml#L43\n\nif we dont want to just relay on hacking and flake8 providign the one they are compoatbl with","commit_id":"0428dcee53fcbe9af095d7c9fec2fc1a4f5eff38"},{"author":{"_account_id":4690,"name":"melanie witt","display_name":"melwitt","email":"melwittt@gmail.com","username":"melwitt"},"change_message_id":"1388a9c8bd97163840ff153b7973794772166dec","unresolved":true,"context_lines":[{"line_number":6,"context_line":"doc8\u003e\u003d0.6.0 # Apache-2.0"},{"line_number":7,"context_line":"Pygments\u003e\u003d2.2.0 # BSD license"},{"line_number":8,"context_line":"WebTest\u003e\u003d2.0.27 # MIT"},{"line_number":9,"context_line":"pycodestyle\u003e\u003d2.11.0 # MIT"}],"source_content_type":"text/plain","patch_set":2,"id":"65c212f7_8a8b3560","line":9,"range":{"start_line":9,"start_character":0,"end_line":9,"end_character":25},"in_reply_to":"96aa8edd_20bb66a9","updated":"2026-06-30 20:17:00.000000000","message":"Ah, thanks. Will fix.","commit_id":"0428dcee53fcbe9af095d7c9fec2fc1a4f5eff38"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"33bdf25d11e76be9a56b3aeb78045f2c6424b6fd","unresolved":false,"context_lines":[{"line_number":6,"context_line":"doc8\u003e\u003d0.6.0 # Apache-2.0"},{"line_number":7,"context_line":"Pygments\u003e\u003d2.2.0 # BSD license"},{"line_number":8,"context_line":"WebTest\u003e\u003d2.0.27 # MIT"},{"line_number":9,"context_line":"pycodestyle\u003e\u003d2.11.0 # MIT"}],"source_content_type":"text/plain","patch_set":2,"id":"a27cc9d6_6816b5e2","line":9,"range":{"start_line":9,"start_character":0,"end_line":9,"end_character":25},"in_reply_to":"96aa8edd_20bb66a9","updated":"2026-06-30 20:50:48.000000000","message":"never mind this is correct","commit_id":"0428dcee53fcbe9af095d7c9fec2fc1a4f5eff38"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"1f9dfe7b4818fec59862dc94d93f33934a6c031f","unresolved":false,"context_lines":[{"line_number":6,"context_line":"doc8\u003e\u003d0.6.0 # Apache-2.0"},{"line_number":7,"context_line":"Pygments\u003e\u003d2.2.0 # BSD license"},{"line_number":8,"context_line":"WebTest\u003e\u003d2.0.27 # MIT"},{"line_number":9,"context_line":"pycodestyle\u003e\u003d2.11.0 # MIT"}],"source_content_type":"text/plain","patch_set":3,"id":"b21c880a_7ece59f3","line":9,"updated":"2026-06-30 21:56:55.000000000","message":"The new pycodestyle dependency is added to test-requirements.txt with a floor of \u003e\u003d2.11.0, but the test harness uses register_check, Checker(lines\u003d...), and _deferred_print internals that are stable across a wide range of versions.\n\n**Severity**: SUGGESTION | **Confidence**: 0.7\n\n**Benefit**: A short comment explaining why pycodestyle is now a direct test dependency (the test_hacking.py harness imports it directly to run tree checks) helps reviewers and future dependency-bump authors understand the constraint.\n\n**Recommendation**:\nAdd an inline note such as \u0027# needed by cyborg/tests/unit/test_hacking.py to drive tree-based flake8 checks\u0027 so the rationale is co-located with the pin. The 2.11.0 floor itself is fine.","commit_id":"05e028088484901cc8d55b52ad495d3298018883"}],"tox.ini":[{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"34d21ab6f8c57b6c3c6e0167f95562e91d33b2ba","unresolved":true,"context_lines":[{"line_number":38,"context_line":"commands \u003d bindep test"},{"line_number":39,"context_line":"usedevelop \u003d False"},{"line_number":40,"context_line":""},{"line_number":41,"context_line":"[testenv:pep8]"},{"line_number":42,"context_line":"description \u003d"},{"line_number":43,"context_line":"  Run style checks."},{"line_number":44,"context_line":"deps \u003d pre-commit"},{"line_number":45,"context_line":"skip_install \u003d true"},{"line_number":46,"context_line":"commands \u003d pre-commit run -a --show-diff-on-failure"},{"line_number":47,"context_line":""},{"line_number":48,"context_line":"[testenv:venv]"},{"line_number":49,"context_line":"commands \u003d {posargs}"},{"line_number":50,"context_line":""}],"source_content_type":"text/x-properties","patch_set":2,"id":"dc3d30c3_3de3ede7","line":47,"range":{"start_line":41,"start_character":1,"end_line":47,"end_character":1},"updated":"2026-06-30 20:04:12.000000000","message":"note that here we are just ruing via pre-commit","commit_id":"0428dcee53fcbe9af095d7c9fec2fc1a4f5eff38"},{"author":{"_account_id":4690,"name":"melanie witt","display_name":"melwitt","email":"melwittt@gmail.com","username":"melwitt"},"change_message_id":"1388a9c8bd97163840ff153b7973794772166dec","unresolved":false,"context_lines":[{"line_number":38,"context_line":"commands \u003d bindep test"},{"line_number":39,"context_line":"usedevelop \u003d False"},{"line_number":40,"context_line":""},{"line_number":41,"context_line":"[testenv:pep8]"},{"line_number":42,"context_line":"description \u003d"},{"line_number":43,"context_line":"  Run style checks."},{"line_number":44,"context_line":"deps \u003d pre-commit"},{"line_number":45,"context_line":"skip_install \u003d true"},{"line_number":46,"context_line":"commands \u003d pre-commit run -a --show-diff-on-failure"},{"line_number":47,"context_line":""},{"line_number":48,"context_line":"[testenv:venv]"},{"line_number":49,"context_line":"commands \u003d {posargs}"},{"line_number":50,"context_line":""}],"source_content_type":"text/x-properties","patch_set":2,"id":"25ad959a_09f0a332","line":47,"range":{"start_line":41,"start_character":1,"end_line":47,"end_character":1},"in_reply_to":"dc3d30c3_3de3ede7","updated":"2026-06-30 20:17:00.000000000","message":"Acknowledged","commit_id":"0428dcee53fcbe9af095d7c9fec2fc1a4f5eff38"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"34d21ab6f8c57b6c3c6e0167f95562e91d33b2ba","unresolved":true,"context_lines":[{"line_number":122,"context_line":""},{"line_number":123,"context_line":"[flake8:local-plugins]"},{"line_number":124,"context_line":"extension \u003d"},{"line_number":125,"context_line":"  C300 \u003d checks:CheckSetLiteralInLogging"},{"line_number":126,"context_line":"paths \u003d"},{"line_number":127,"context_line":"  ./cyborg/hacking"}],"source_content_type":"text/x-properties","patch_set":2,"id":"3a63db4f_c175681b","line":125,"range":{"start_line":125,"start_character":2,"end_line":125,"end_character":40},"updated":"2026-06-30 20:04:12.000000000","message":"so this deos not currently collide with ruff\nbut we might want to use CBG300 or some other prefix then C as there si c90,C4\nCB or CBG is unlinkely to collide or at least less likely","commit_id":"0428dcee53fcbe9af095d7c9fec2fc1a4f5eff38"},{"author":{"_account_id":4690,"name":"melanie witt","display_name":"melwitt","email":"melwittt@gmail.com","username":"melwitt"},"change_message_id":"3c0ef2e7e0fd5d35cfdaaebddfd7c4a2dcb19ea7","unresolved":true,"context_lines":[{"line_number":122,"context_line":""},{"line_number":123,"context_line":"[flake8:local-plugins]"},{"line_number":124,"context_line":"extension \u003d"},{"line_number":125,"context_line":"  C300 \u003d checks:CheckSetLiteralInLogging"},{"line_number":126,"context_line":"paths \u003d"},{"line_number":127,"context_line":"  ./cyborg/hacking"}],"source_content_type":"text/x-properties","patch_set":2,"id":"ad9d8cd7_f4d86298","line":125,"range":{"start_line":125,"start_character":2,"end_line":125,"end_character":40},"in_reply_to":"35aa87aa_4b1c6ef3","updated":"2026-06-30 21:39:10.000000000","message":"OK, turns out I can\u0027t use any prefix longer than one char due to this [1]:\n\n```\nERRORCODE_REGEX \u003d re.compile(r\u0027\\b[A-Z]\\d{3}\\b\u0027)\n```\n\nI\u0027m going to leave it as C for now and let me know if you think a different letter or number would be better.\n\n[1] https://github.com/PyCQA/pycodestyle/blob/8b530596edc932c5652da3d8771b42f0bb506dce/pycodestyle.py#L114","commit_id":"0428dcee53fcbe9af095d7c9fec2fc1a4f5eff38"},{"author":{"_account_id":4690,"name":"melanie witt","display_name":"melwitt","email":"melwittt@gmail.com","username":"melwitt"},"change_message_id":"1388a9c8bd97163840ff153b7973794772166dec","unresolved":true,"context_lines":[{"line_number":122,"context_line":""},{"line_number":123,"context_line":"[flake8:local-plugins]"},{"line_number":124,"context_line":"extension \u003d"},{"line_number":125,"context_line":"  C300 \u003d checks:CheckSetLiteralInLogging"},{"line_number":126,"context_line":"paths \u003d"},{"line_number":127,"context_line":"  ./cyborg/hacking"}],"source_content_type":"text/x-properties","patch_set":2,"id":"35aa87aa_4b1c6ef3","line":125,"range":{"start_line":125,"start_character":2,"end_line":125,"end_character":40},"in_reply_to":"3a63db4f_c175681b","updated":"2026-06-30 20:17:00.000000000","message":"Fair point, will use CBG.","commit_id":"0428dcee53fcbe9af095d7c9fec2fc1a4f5eff38"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"edbe35003ae3f9bd13e4918fbf601fe9d3f7ebe8","unresolved":false,"context_lines":[{"line_number":122,"context_line":""},{"line_number":123,"context_line":"[flake8:local-plugins]"},{"line_number":124,"context_line":"extension \u003d"},{"line_number":125,"context_line":"  C300 \u003d checks:CheckSetLiteralInLogging"},{"line_number":126,"context_line":"paths \u003d"},{"line_number":127,"context_line":"  ./cyborg/hacking"}],"source_content_type":"text/x-properties","patch_set":2,"id":"d6d8b1a8_14b6778c","line":125,"range":{"start_line":125,"start_character":2,"end_line":125,"end_character":40},"in_reply_to":"ad9d8cd7_f4d86298","updated":"2026-07-02 16:05:16.000000000","message":"oh that unfortunate but thats valid","commit_id":"0428dcee53fcbe9af095d7c9fec2fc1a4f5eff38"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"1f9dfe7b4818fec59862dc94d93f33934a6c031f","unresolved":false,"context_lines":[{"line_number":121,"context_line":"exclude\u003d.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,releasenotes,*sqlalchemy/alembic/versions/*"},{"line_number":122,"context_line":""},{"line_number":123,"context_line":"[flake8:local-plugins]"},{"line_number":124,"context_line":"extension \u003d"},{"line_number":125,"context_line":"  C300 \u003d checks:CheckSetLiteralInLogging"},{"line_number":126,"context_line":"paths \u003d"},{"line_number":127,"context_line":"  ./cyborg/hacking"}],"source_content_type":"text/x-properties","patch_set":3,"id":"5a71dd43_8a3abad8","line":124,"updated":"2026-06-30 21:56:55.000000000","message":"The flake8 local-plugin registration maps C300 \u003d checks:CheckSetLiteralInLogging, which relies on the extension-prefix convention. There is no test asserting the plugin is discoverable by flake8 itself; tests invoke the checker class directly.\n\n**Severity**: SUGGESTION | **Confidence**: 0.7\n\n**Benefit**: A smoke assertion that \u0027flake8 .\u0027 actually loads the local plugin (e.g. via a subprocess or pep8 testenv) would guard against silent registration regressions such as a wrong paths entry or a missing __init__.py.\n\n**Recommendation**:\nThe direct-class tests are a good start; optionally add one integration-style assertion that runs flake8 over a small fixture and observes the C300 code emitted. Not blocking for this patch.","commit_id":"05e028088484901cc8d55b52ad495d3298018883"}]}
