)]}'
{"/PATCHSET_LEVEL":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"886fefd75a2716a5ce49182a38b00cd5ede21cb3","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":2,"id":"5ea2c876_f6708eca","updated":"2026-04-21 21:30:34.000000000","message":"the plan it to move all of this to `prom_metrics` and squash it in with the child patch:\n\n971208: PrometheusClient | https://review.opendev.org/c/openstack/swift/+/971208","commit_id":"49fbad12bf16d5ee3f2c85cd03ad868e0ad49e29"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"ea190ab730d59f1ff3b34167adcdcd2adbb51f83","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":4,"id":"cbafadf8_adc18b5f","updated":"2026-05-06 21:25:22.000000000","message":"you got this!","commit_id":"ece8d4ba720af979913f05b9e33cf1ad24c5a1b1"}],"swift/common/prom_metrics.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"ea190ab730d59f1ff3b34167adcdcd2adbb51f83","unresolved":true,"context_lines":[{"line_number":18,"context_line":"import os"},{"line_number":19,"context_line":"import re"},{"line_number":20,"context_line":"import tempfile"},{"line_number":21,"context_line":"from test import metric_key"},{"line_number":22,"context_line":""},{"line_number":23,"context_line":""},{"line_number":24,"context_line":"PROM_CONF_USER_LABEL_PREFIX \u003d \u0027prom_user_label_\u0027"}],"source_content_type":"text/x-python","patch_set":3,"id":"166c1d5f_b6832c4a","line":21,"updated":"2026-05-06 21:25:22.000000000","message":"we can\u0027t have `swift.common` import from `test`\n\nthis method should just be defined here in this module.","commit_id":"2baec20222a67228e4b9d141fe6b2b69966d6e79"},{"author":{"_account_id":35790,"name":"Shreeya Deshpande","email":"shreeyad@nvidia.com","username":"shreeyad"},"change_message_id":"0341844d48698873f17f4d9abdac3e9fcd04585a","unresolved":false,"context_lines":[{"line_number":18,"context_line":"import os"},{"line_number":19,"context_line":"import re"},{"line_number":20,"context_line":"import tempfile"},{"line_number":21,"context_line":"from test import metric_key"},{"line_number":22,"context_line":""},{"line_number":23,"context_line":""},{"line_number":24,"context_line":"PROM_CONF_USER_LABEL_PREFIX \u003d \u0027prom_user_label_\u0027"}],"source_content_type":"text/x-python","patch_set":3,"id":"d6e01308_2a30d0e4","line":21,"in_reply_to":"166c1d5f_b6832c4a","updated":"2026-05-07 21:57:43.000000000","message":"Done","commit_id":"2baec20222a67228e4b9d141fe6b2b69966d6e79"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"ea190ab730d59f1ff3b34167adcdcd2adbb51f83","unresolved":true,"context_lines":[{"line_number":24,"context_line":"    return (name, frozenset((k, str(v)) for k, v in labels.items()))"},{"line_number":25,"context_line":""},{"line_number":26,"context_line":""},{"line_number":27,"context_line":"def read_metrics(filename):"},{"line_number":28,"context_line":"    metrics \u003d {}"},{"line_number":29,"context_line":"    with open(filename) as f:"},{"line_number":30,"context_line":"        for line in f:"}],"source_content_type":"text/x-python","patch_set":4,"id":"56ebaad1_2a31c2dd","line":27,"updated":"2026-05-06 21:25:22.000000000","message":"since this isn\u0027t used in this module; it might be reasonable to move it into test\n\nthen in test you can test `parse_metrics` either directly or indirectly through `read_metrics`\n\n... however there may someday be a use-case for a `PrometheusClient.load(filename)` ... so maybe something like this comes back into this module eventually?  I\u0027m not sure if that\u0027s a good reason to keep an un-used function in here for NOW","commit_id":"ece8d4ba720af979913f05b9e33cf1ad24c5a1b1"},{"author":{"_account_id":35790,"name":"Shreeya Deshpande","email":"shreeyad@nvidia.com","username":"shreeyad"},"change_message_id":"0341844d48698873f17f4d9abdac3e9fcd04585a","unresolved":false,"context_lines":[{"line_number":24,"context_line":"    return (name, frozenset((k, str(v)) for k, v in labels.items()))"},{"line_number":25,"context_line":""},{"line_number":26,"context_line":""},{"line_number":27,"context_line":"def read_metrics(filename):"},{"line_number":28,"context_line":"    metrics \u003d {}"},{"line_number":29,"context_line":"    with open(filename) as f:"},{"line_number":30,"context_line":"        for line in f:"}],"source_content_type":"text/x-python","patch_set":4,"id":"05be0be9_8d5f73b1","line":27,"in_reply_to":"56ebaad1_2a31c2dd","updated":"2026-05-07 21:57:43.000000000","message":"fair, we can move read_metrics to test for now!","commit_id":"ece8d4ba720af979913f05b9e33cf1ad24c5a1b1"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"ea190ab730d59f1ff3b34167adcdcd2adbb51f83","unresolved":true,"context_lines":[{"line_number":113,"context_line":"    return name, labels, value"},{"line_number":114,"context_line":""},{"line_number":115,"context_line":""},{"line_number":116,"context_line":"def _build_line_parts(name, value, labels\u003dNone):"},{"line_number":117,"context_line":"    \"\"\""},{"line_number":118,"context_line":"    Format a single metric into Prometheus text format string."},{"line_number":119,"context_line":""}],"source_content_type":"text/x-python","patch_set":4,"id":"168c43ed_a4ce9796","line":116,"updated":"2026-05-06 21:25:22.000000000","message":"I like this signature a lot: `(name, value, labels\u003dNone)`\n\n... I think any place we have a signature that takes `metrics` (which can be a str, 3-tuple, or dict) - we should *just* accept ONLY this signature: `(name, value, labels\u003dNone)`","commit_id":"ece8d4ba720af979913f05b9e33cf1ad24c5a1b1"},{"author":{"_account_id":35790,"name":"Shreeya Deshpande","email":"shreeyad@nvidia.com","username":"shreeyad"},"change_message_id":"0341844d48698873f17f4d9abdac3e9fcd04585a","unresolved":false,"context_lines":[{"line_number":113,"context_line":"    return name, labels, value"},{"line_number":114,"context_line":""},{"line_number":115,"context_line":""},{"line_number":116,"context_line":"def _build_line_parts(name, value, labels\u003dNone):"},{"line_number":117,"context_line":"    \"\"\""},{"line_number":118,"context_line":"    Format a single metric into Prometheus text format string."},{"line_number":119,"context_line":""}],"source_content_type":"text/x-python","patch_set":4,"id":"683ef3d4_053a6b37","line":116,"in_reply_to":"168c43ed_a4ce9796","updated":"2026-05-07 21:57:43.000000000","message":"Done","commit_id":"ece8d4ba720af979913f05b9e33cf1ad24c5a1b1"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"ea190ab730d59f1ff3b34167adcdcd2adbb51f83","unresolved":true,"context_lines":[{"line_number":259,"context_line":"            # metrics_key is (name, frozenset(labels))"},{"line_number":260,"context_line":"            name, labels_frozenset \u003d metrics_key"},{"line_number":261,"context_line":"            labels_dict \u003d dict(labels_frozenset) if labels_frozenset else {}"},{"line_number":262,"context_line":"            line \u003d self._gen_str_metrics((name, labels_dict, value))"},{"line_number":263,"context_line":"            lines.append(line)"},{"line_number":264,"context_line":"        return \u0027\u0027.join(lines)"},{"line_number":265,"context_line":""}],"source_content_type":"text/x-python","patch_set":4,"id":"c701bf0f_e2daa1d2","line":262,"updated":"2026-05-06 21:25:22.000000000","message":"right here we should just call `_build_line_parts` - and then we should delete `_gen_str_metrics`","commit_id":"ece8d4ba720af979913f05b9e33cf1ad24c5a1b1"},{"author":{"_account_id":35790,"name":"Shreeya Deshpande","email":"shreeyad@nvidia.com","username":"shreeyad"},"change_message_id":"0341844d48698873f17f4d9abdac3e9fcd04585a","unresolved":false,"context_lines":[{"line_number":259,"context_line":"            # metrics_key is (name, frozenset(labels))"},{"line_number":260,"context_line":"            name, labels_frozenset \u003d metrics_key"},{"line_number":261,"context_line":"            labels_dict \u003d dict(labels_frozenset) if labels_frozenset else {}"},{"line_number":262,"context_line":"            line \u003d self._gen_str_metrics((name, labels_dict, value))"},{"line_number":263,"context_line":"            lines.append(line)"},{"line_number":264,"context_line":"        return \u0027\u0027.join(lines)"},{"line_number":265,"context_line":""}],"source_content_type":"text/x-python","patch_set":4,"id":"f1542617_3637727e","line":262,"in_reply_to":"c701bf0f_e2daa1d2","updated":"2026-05-07 21:57:43.000000000","message":"Acknowledged","commit_id":"ece8d4ba720af979913f05b9e33cf1ad24c5a1b1"}],"test/__init__.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"886fefd75a2716a5ce49182a38b00cd5ede21cb3","unresolved":true,"context_lines":[{"line_number":81,"context_line":"    :param labels: keyword arguments for label key\u003dvalue pairs"},{"line_number":82,"context_line":"    :returns: a tuple of (name, frozenset(labels)) suitable as a dict key"},{"line_number":83,"context_line":"    \"\"\""},{"line_number":84,"context_line":"    return (name, frozenset(labels.items()))"},{"line_number":85,"context_line":""},{"line_number":86,"context_line":""},{"line_number":87,"context_line":"def read_metrics(filename):"}],"source_content_type":"text/x-python","patch_set":2,"id":"500a09c1_843b2258","line":84,"updated":"2026-04-21 21:30:34.000000000","message":"as cute as it is that this is terse we should add some normalization:\n\n```\nfrozenset((k, str(v)) for k, v in labels.items())\n```\n\notherwise you get some non-sense like:\n\n```\nE       - {(\u0027swift_manage_expirer_expirer_task_container_objects\u0027, frozenset({(\u0027days\u0027, 1), (\u0027host\u0027, \u0027saio\u0027)})): 10.0,\nE       ?                                                                       -------------\nE       \nE       + {(\u0027swift_manage_expirer_expirer_task_container_objects\u0027, frozenset({(\u0027host\u0027, \u0027saio\u0027), (\u0027days\u0027, \u00271\u0027)})): 10.0,\nE       ?                                                                                     +++++++++++++++\n```\n\nand as best I can tell:\n\n\u003e days\u003d\"1\" (correct)\n\u003e days\u003d1 (missing quotes: will cause a parse error)\n\u003e Label values must be quoted strings","commit_id":"49fbad12bf16d5ee3f2c85cd03ad868e0ad49e29"},{"author":{"_account_id":35790,"name":"Shreeya Deshpande","email":"shreeyad@nvidia.com","username":"shreeyad"},"change_message_id":"0618cf817a6dc2c5152719882151601f12a79d15","unresolved":false,"context_lines":[{"line_number":81,"context_line":"    :param labels: keyword arguments for label key\u003dvalue pairs"},{"line_number":82,"context_line":"    :returns: a tuple of (name, frozenset(labels)) suitable as a dict key"},{"line_number":83,"context_line":"    \"\"\""},{"line_number":84,"context_line":"    return (name, frozenset(labels.items()))"},{"line_number":85,"context_line":""},{"line_number":86,"context_line":""},{"line_number":87,"context_line":"def read_metrics(filename):"}],"source_content_type":"text/x-python","patch_set":2,"id":"38daabe7_9d2b1c7f","line":84,"in_reply_to":"500a09c1_843b2258","updated":"2026-05-01 13:54:42.000000000","message":"Done","commit_id":"49fbad12bf16d5ee3f2c85cd03ad868e0ad49e29"}],"test/unit/test_helpers.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"ea190ab730d59f1ff3b34167adcdcd2adbb51f83","unresolved":true,"context_lines":[{"line_number":38,"context_line":"            metric_key("},{"line_number":39,"context_line":"                \u0027http_requests_total\u0027,"},{"line_number":40,"context_line":"                method\u003d\u0027POST\u0027, status\u003d\u0027201\u0027): 56.0,"},{"line_number":41,"context_line":"        })"},{"line_number":42,"context_line":""},{"line_number":43,"context_line":"    @with_tempdir"},{"line_number":44,"context_line":"    def test_read_metrics_without_labels(self, tempdir):"}],"source_content_type":"text/x-python","patch_set":4,"id":"48c915eb_c466ce81","line":41,"updated":"2026-05-06 21:25:22.000000000","message":"if we move `read_metrics` into the test file it\u0027s maybe optional - but probably still good - to have these tests that verify sort of explicitly it\u0027s working the way want.\n\nActually the way it\u0027s been refactored to use `_parse_line` (or whatever it\u0027s called) means this is some great direct coverage of that helper function.","commit_id":"ece8d4ba720af979913f05b9e33cf1ad24c5a1b1"},{"author":{"_account_id":35790,"name":"Shreeya Deshpande","email":"shreeyad@nvidia.com","username":"shreeyad"},"change_message_id":"0341844d48698873f17f4d9abdac3e9fcd04585a","unresolved":false,"context_lines":[{"line_number":38,"context_line":"            metric_key("},{"line_number":39,"context_line":"                \u0027http_requests_total\u0027,"},{"line_number":40,"context_line":"                method\u003d\u0027POST\u0027, status\u003d\u0027201\u0027): 56.0,"},{"line_number":41,"context_line":"        })"},{"line_number":42,"context_line":""},{"line_number":43,"context_line":"    @with_tempdir"},{"line_number":44,"context_line":"    def test_read_metrics_without_labels(self, tempdir):"}],"source_content_type":"text/x-python","patch_set":4,"id":"00afd84b_21b25855","line":41,"in_reply_to":"48c915eb_c466ce81","updated":"2026-05-07 21:57:43.000000000","message":"Acknowledged","commit_id":"ece8d4ba720af979913f05b9e33cf1ad24c5a1b1"}]}
