)]}'
{".zuul.yaml":[{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"319af0479df4a93de50d64221e5e0bcb9c236879","unresolved":false,"context_lines":[{"line_number":1,"context_line":"---"},{"line_number":2,"context_line":""},{"line_number":3,"context_line":"- project:"},{"line_number":4,"context_line":"    templates:"},{"line_number":5,"context_line":"      - check-requirements"},{"line_number":6,"context_line":"      - lib-forward-testing-python3"},{"line_number":7,"context_line":"      - openstack-cover-jobs"},{"line_number":8,"context_line":"      - openstack-python3-jobs"},{"line_number":9,"context_line":"      - openstack-python3-next-jobs"},{"line_number":10,"context_line":"      - periodic-stable-jobs"},{"line_number":11,"context_line":"      - publish-openstack-docs-pti"},{"line_number":12,"context_line":"      - release-notes-jobs-python3"},{"line_number":13,"context_line":"    check:"},{"line_number":14,"context_line":"      jobs:"},{"line_number":15,"context_line":"        - tooz-tox-examples"},{"line_number":16,"context_line":"        - tooz-tox-py311-etcd3gw"},{"line_number":17,"context_line":"        - tooz-tox-py314-etcd3gw"},{"line_number":18,"context_line":"        - tooz-tox-py311-zookeeper"},{"line_number":19,"context_line":"        - tooz-tox-py314-zookeeper"},{"line_number":20,"context_line":"        - tooz-tox-py311-redis"},{"line_number":21,"context_line":"        - tooz-tox-py314-redis"},{"line_number":22,"context_line":"        - tooz-tox-py311-sentinel"},{"line_number":23,"context_line":"        - tooz-tox-py314-sentinel"},{"line_number":24,"context_line":"        - tooz-tox-py311-memcached"},{"line_number":25,"context_line":"        - tooz-tox-py314-memcached"},{"line_number":26,"context_line":"        - tooz-tox-py311-postgresql"},{"line_number":27,"context_line":"        - tooz-tox-py314-postgresql"},{"line_number":28,"context_line":"        - tooz-tox-py311-mysql"},{"line_number":29,"context_line":"        - tooz-tox-py314-mysql"},{"line_number":30,"context_line":"        - tooz-tox-py311-consul"},{"line_number":31,"context_line":"        - tooz-tox-py314-consul"},{"line_number":32,"context_line":"        - tooz-tox-py311-k8s"},{"line_number":33,"context_line":"        - tooz-tox-py314-k8s"},{"line_number":34,"context_line":"    gate:"},{"line_number":35,"context_line":"      jobs:"},{"line_number":36,"context_line":"        - tooz-tox-examples"},{"line_number":37,"context_line":"        - tooz-tox-py311-etcd3gw"},{"line_number":38,"context_line":"        - tooz-tox-py314-etcd3gw"},{"line_number":39,"context_line":"        - tooz-tox-py311-zookeeper"},{"line_number":40,"context_line":"        - tooz-tox-py314-zookeeper"},{"line_number":41,"context_line":"        - tooz-tox-py311-redis"},{"line_number":42,"context_line":"        - tooz-tox-py314-redis"},{"line_number":43,"context_line":"        - tooz-tox-py311-sentinel"},{"line_number":44,"context_line":"        - tooz-tox-py314-sentinel"},{"line_number":45,"context_line":"        - tooz-tox-py311-memcached"},{"line_number":46,"context_line":"        - tooz-tox-py314-memcached"},{"line_number":47,"context_line":"        - tooz-tox-py311-postgresql"},{"line_number":48,"context_line":"        - tooz-tox-py314-postgresql"},{"line_number":49,"context_line":"        - tooz-tox-py311-mysql"},{"line_number":50,"context_line":"        - tooz-tox-py314-mysql"},{"line_number":51,"context_line":"        - tooz-tox-py311-consul"},{"line_number":52,"context_line":"        - tooz-tox-py314-consul"},{"line_number":53,"context_line":"        - tooz-tox-py311-k8s"},{"line_number":54,"context_line":"        - tooz-tox-py314-k8s"},{"line_number":55,"context_line":""},{"line_number":56,"context_line":"- job:"},{"line_number":57,"context_line":"    name: tooz-tox-examples"},{"line_number":58,"context_line":"    parent: openstack-tox-py312"}],"source_content_type":"text/x-yaml","patch_set":9,"id":"f0878934_853be184","line":55,"range":{"start_line":3,"start_character":2,"end_line":55,"end_character":0},"updated":"2026-07-14 23:21:24.000000000","message":"Job tooz-tox-py311-k8s not defined","commit_id":"a5ff74c0bd3ebde27b518b0e0a971ac31826163d"}],"/COMMIT_MSG":[{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"5de111a073c3ed50877457d56a697a984515327c","unresolved":true,"context_lines":[{"line_number":5,"context_line":"CommitDate: 2026-09-10 07:52:47 -0400"},{"line_number":6,"context_line":""},{"line_number":7,"context_line":"Add Consul coordination driver using native services"},{"line_number":8,"context_line":""},{"line_number":9,"context_line":"Reintroduce Consul coordination with the maintained py-consul client. Use"},{"line_number":10,"context_line":"namespaced native Consul services for group membership, KV entries for group"},{"line_number":11,"context_line":"markers, and sessions with KV acquire operations for distributed locks."}],"source_content_type":"text/x-gerrit-commit-message","patch_set":29,"id":"835ff1f9_baffb1d0","line":8,"updated":"2026-09-18 16:57:04.000000000","message":"nit: can you wrap this at \u003c\u003d 72 characters?","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"e911de24b78bc16d043673b84f373aad410489f2","unresolved":false,"context_lines":[{"line_number":5,"context_line":"CommitDate: 2026-09-10 07:52:47 -0400"},{"line_number":6,"context_line":""},{"line_number":7,"context_line":"Add Consul coordination driver using native services"},{"line_number":8,"context_line":""},{"line_number":9,"context_line":"Reintroduce Consul coordination with the maintained py-consul client. Use"},{"line_number":10,"context_line":"namespaced native Consul services for group membership, KV entries for group"},{"line_number":11,"context_line":"markers, and sessions with KV acquire operations for distributed locks."}],"source_content_type":"text/x-gerrit-commit-message","patch_set":29,"id":"19ae3e70_374028b8","line":8,"in_reply_to":"835ff1f9_baffb1d0","updated":"2026-09-22 12:57:25.000000000","message":"Done","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"1490fb46d986504554736e255ee39bd7db09979d","unresolved":false,"context_lines":[{"line_number":5,"context_line":"CommitDate: 2026-09-10 07:52:47 -0400"},{"line_number":6,"context_line":""},{"line_number":7,"context_line":"Add Consul coordination driver using native services"},{"line_number":8,"context_line":""},{"line_number":9,"context_line":"Reintroduce Consul coordination with the maintained py-consul client. Use"},{"line_number":10,"context_line":"namespaced native Consul services for group membership, KV entries for group"},{"line_number":11,"context_line":"markers, and sessions with KV acquire operations for distributed locks."}],"source_content_type":"text/x-gerrit-commit-message","patch_set":29,"id":"c2f2f403_edb3ba49","line":8,"in_reply_to":"835ff1f9_baffb1d0","updated":"2026-09-22 12:17:22.000000000","message":"Wrapped at 72 columns.","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"}],"/PATCHSET_LEVEL":[{"author":{"_account_id":9816,"name":"Takashi Kajinami","email":"kajinamit@oss.nttdata.com","username":"kajinamit"},"change_message_id":"cb12a97d521094123e1b865827eea5d5fcd820b5","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":16,"id":"de0dd198_4305cead","updated":"2026-08-10 16:39:46.000000000","message":"recheck","commit_id":"2cd46ee7090e33222e04bd8812e449ad340896cb"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"c5e6edeff3f04575cc015d1970a065a7a71ed9c3","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":19,"id":"bed63073_af599f47","updated":"2026-08-13 11:47:39.000000000","message":"I did the following:\n\n1. Reverted the consul removal patch (`git revert 21fdc625dc938495ba4cff0204e87ec6fff0920e`)\n2. Reverted the revert (`git revert HEAD`)\n3. Applied this patch (`git review -x 997145`)\n4. Squashed this patch into the revert-revert from step 2.\n\nThis might be worth doing, to help (a) spot additional things from the old driver that we want to keep and (b) keep the changes here smaller (it\u0027s larger than it needs to be currently).\n\nCouple of comments inline based on the above review.","commit_id":"67f62a415d8554234cadcd5b356cb135c1cdf4ad"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"7c559259a5e93a3ec7d97c133532919c1276072d","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":19,"id":"dab67b7c_79e93a86","in_reply_to":"5741c18c_72b60126","updated":"2026-08-13 21:28:19.000000000","message":"OK, I tried that exact sequence locally and confirmed that it produces the same final tree as the current patch.\nIt did not reduce the overall diff, though. The current change is 14 files with 1,694 additions and 2 deletions. With the pure revert stack, the second change is 1,537 additions and 460 deletions, and the complete stack has 2,624 changed lines.\nI also tried modernizing the restoration commit first—using py-consul, current CI/setup, and preserving the historical removal release note. That narrows the second change to five files, but it is still 1,485 additions and 416 deletions, with 2,526 changed lines across the stack.\nMost of the additional churn comes from comparing the old 522-line KV implementation directly with the new 844-line native-services implementation, while the 726-line test suite remains entirely new. The pure revert also restores the obsolete python-consul2 dependency and removes the historical removal note, so it cannot be used unchanged.\nSince the stacked version has the same final tree, more total churn, and requires a dependent change, I think keeping this as one change is more straightforward.\nIs that going to be a sticking point? I\u0027d rather do whatever I need to do to get this merged, but I don\u0027t think it really buys us anything unfortunately, though it was a good idea and I guess exposed some of those missing bits that I\u0027ve re-added.","commit_id":"67f62a415d8554234cadcd5b356cb135c1cdf4ad"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"d1f19083033c15d8d518b479a9947767e7361bf8","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":19,"id":"5741c18c_72b60126","in_reply_to":"bed63073_af599f47","updated":"2026-08-13 21:01:54.000000000","message":"I\u0027m not sure if this is worth doing, but I can try it and see how much it actually reduces the churn.","commit_id":"67f62a415d8554234cadcd5b356cb135c1cdf4ad"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"77cc09579dd4c30dcc76cc23b1e249955ac83baa","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":21,"id":"9da888ba_2ee26dbb","updated":"2026-08-13 23:51:20.000000000","message":"FYI we are no longer following this path internally so likely won’t need to maintain this kind of quirky driver, considering abandoning… we will use Redis or Consul internally likely.","commit_id":"82168ba85fe54a54ffaa4112c0f89940d5b37267"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"906d226d4d6f504ca32327964fa66c277d6f928a","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":21,"id":"6b33c559_92e5735d","in_reply_to":"9da888ba_2ee26dbb","updated":"2026-08-13 23:54:26.000000000","message":"Err this was supposed to be posted on the k8s driver CR lol, we are definitely needing this one","commit_id":"82168ba85fe54a54ffaa4112c0f89940d5b37267"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"85509edaca412cb3f71bbe770c781831c31ac15b","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":29,"id":"971b5c79_21d4d9ae","updated":"2026-09-18 16:56:26.000000000","message":"Partial review. Need to come back to this Monday","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"1490fb46d986504554736e255ee39bd7db09979d","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":32,"id":"6a99c4ab_2321bfa7","updated":"2026-09-22 12:17:22.000000000","message":"I think Astra and I handled all of these? Tried not to shift things too much while you’re still reviewing.","commit_id":"e2a1b6874ef7fc8e4d61b4707c56ac23548fe5e8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"e911de24b78bc16d043673b84f373aad410489f2","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":32,"id":"0b0b2549_ec8d8b25","updated":"2026-09-22 12:57:25.000000000","message":"I\u0027m having a very hard time reviewing this. I can\u0027t see anything wrong from skimming, but at ~2 kLoC for both the driver and test, I\u0027m not sure how I can review in depth.\n\nOut of curiosity, would it be feasible to split this up so that we add support for the different APIs incrementally? That is, maybe we add support for locking first, then the next operation etc.","commit_id":"e2a1b6874ef7fc8e4d61b4707c56ac23548fe5e8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"45951117c3e484f218ded4d394f216f017e85132","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":32,"id":"82bf68b7_0f5cdcad","in_reply_to":"0b0b2549_ec8d8b25","updated":"2026-09-22 12:58:03.000000000","message":"\u003e Out of curiosity, would it be feasible to split this up so that we add support for the different APIs incrementally? That is, maybe we add support for locking first, then the next operation etc.\n\nNote that I\u0027m aware you\u0027re already doing this to a degree. I\u0027m asking can we take it further?","commit_id":"e2a1b6874ef7fc8e4d61b4707c56ac23548fe5e8"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"677183f32e60545bb5868512211cf7ba0504d6b1","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":32,"id":"439be3da_0b2350d0","in_reply_to":"82bf68b7_0f5cdcad","updated":"2026-09-23 11:23:51.000000000","message":"Split into locking ([1006887](https://review.opendev.org/c/openstack/tooz/+/1006887)), membership ([1006888](https://review.opendev.org/c/openstack/tooz/+/1006888)), HTTP checks ([1006889](https://review.opendev.org/c/openstack/tooz/+/1006889)), then discovery here. Kept this Change-Id at the end so existing dependencies still get the complete driver. Leader election remains 1000914. Each commit passes pep8, py3 and real-Consul tests locally, though was seeing some mirror issues last night so still waiting on rechecks on some here","commit_id":"e2a1b6874ef7fc8e4d61b4707c56ac23548fe5e8"}],"CONTRIBUTING.rst":[{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"e911de24b78bc16d043673b84f373aad410489f2","unresolved":true,"context_lines":[{"line_number":16,"context_line":""},{"line_number":17,"context_line":"   https://bugs.launchpad.net/python-tooz/"},{"line_number":18,"context_line":""},{"line_number":19,"context_line":"Consul functional tests"},{"line_number":20,"context_line":"-----------------------"},{"line_number":21,"context_line":""},{"line_number":22,"context_line":"Download a `Consul binary \u003chttps://developer.hashicorp.com/consul/install\u003e`_"},{"line_number":23,"context_line":"and add its directory to ``PATH`` before running ``tox -e py311-consul``."},{"line_number":24,"context_line":"Use the tox environment that matches your Python version. Pifpaf starts"},{"line_number":25,"context_line":"and stops a disposable Consul agent; a system service is not required."},{"line_number":26,"context_line":"The CI pre-run playbook provisions a job-local Consul binary automatically."}],"source_content_type":"text/x-rst","patch_set":32,"id":"c6437f21_c22a05d5","line":26,"range":{"start_line":19,"start_character":0,"end_line":26,"end_character":75},"updated":"2026-09-22 12:57:25.000000000","message":"This doesn\u0027t belong here. If you think we need this, can you put it into `doc/source/contributor` (which currently doesn\u0027t exist here). However, we should really be handling this with pifpaf. See Takashi-san\u0027s patch [here](https://review.opendev.org/c/openstack/tooz/+/997919/). There we migrated from using a `setup-env*` script to using bindep. You probably want to use the `setup-env*` approach yourself assuming consul is not packaged by distros.","commit_id":"e2a1b6874ef7fc8e4d61b4707c56ac23548fe5e8"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"677183f32e60545bb5868512211cf7ba0504d6b1","unresolved":true,"context_lines":[{"line_number":16,"context_line":""},{"line_number":17,"context_line":"   https://bugs.launchpad.net/python-tooz/"},{"line_number":18,"context_line":""},{"line_number":19,"context_line":"Consul functional tests"},{"line_number":20,"context_line":"-----------------------"},{"line_number":21,"context_line":""},{"line_number":22,"context_line":"Download a `Consul binary \u003chttps://developer.hashicorp.com/consul/install\u003e`_"},{"line_number":23,"context_line":"and add its directory to ``PATH`` before running ``tox -e py311-consul``."},{"line_number":24,"context_line":"Use the tox environment that matches your Python version. Pifpaf starts"},{"line_number":25,"context_line":"and stops a disposable Consul agent; a system service is not required."},{"line_number":26,"context_line":"The CI pre-run playbook provisions a job-local Consul binary automatically."}],"source_content_type":"text/x-rst","patch_set":32,"id":"d9c65adc_7c31d1c5","line":26,"range":{"start_line":19,"start_character":0,"end_line":26,"end_character":75},"in_reply_to":"c6437f21_c22a05d5","updated":"2026-09-23 11:23:51.000000000","message":"Yeah this shouldn’t have been added, removed. Pifpaf already starts/stops Consul; CI provisions the binary in 1006887, and local tests use PATH. This follows 997919\u0027s move of backend preparation into CI. I think that’s fine? I thought the hook was cute but this would have been the only user now so I switched to the other method.","commit_id":"e2a1b6874ef7fc8e4d61b4707c56ac23548fe5e8"}],"doc/source/user/drivers.rst":[{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"d35b4184e647eab6e7b5b89c00b4c3c720e878b2","unresolved":true,"context_lines":[{"line_number":190,"context_line":"The etcd3gw driver is a driver providing distributed locking and grouping APIs"},{"line_number":191,"context_line":"and is based on the `etcd server`_ supported key/value storage and associated"},{"line_number":192,"context_line":"primitives. It relies on the `GRPC Gateway`_ to provide HTTP access to etcd3."},{"line_number":193,"context_line":""},{"line_number":194,"context_line":"Kubernetes"},{"line_number":195,"context_line":"----------"},{"line_number":196,"context_line":""}],"source_content_type":"text/x-rst","patch_set":19,"id":"06c8263b_68dfaa82","line":193,"updated":"2026-08-13 11:51:30.000000000","message":"Can you re-add docs for the consul driver here?","commit_id":"67f62a415d8554234cadcd5b356cb135c1cdf4ad"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"a1af0b01a5f359df8aaa500840ec0b8d7199bfa4","unresolved":true,"context_lines":[{"line_number":190,"context_line":"The etcd3gw driver is a driver providing distributed locking and grouping APIs"},{"line_number":191,"context_line":"and is based on the `etcd server`_ supported key/value storage and associated"},{"line_number":192,"context_line":"primitives. It relies on the `GRPC Gateway`_ to provide HTTP access to etcd3."},{"line_number":193,"context_line":""},{"line_number":194,"context_line":"Kubernetes"},{"line_number":195,"context_line":"----------"},{"line_number":196,"context_line":""}],"source_content_type":"text/x-rst","patch_set":19,"id":"d31d7cab_95df6cf5","line":193,"in_reply_to":"06c8263b_68dfaa82","updated":"2026-08-13 20:58:34.000000000","message":"Done.","commit_id":"67f62a415d8554234cadcd5b356cb135c1cdf4ad"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"e911de24b78bc16d043673b84f373aad410489f2","unresolved":false,"context_lines":[{"line_number":190,"context_line":"The etcd3gw driver is a driver providing distributed locking and grouping APIs"},{"line_number":191,"context_line":"and is based on the `etcd server`_ supported key/value storage and associated"},{"line_number":192,"context_line":"primitives. It relies on the `GRPC Gateway`_ to provide HTTP access to etcd3."},{"line_number":193,"context_line":""},{"line_number":194,"context_line":"Kubernetes"},{"line_number":195,"context_line":"----------"},{"line_number":196,"context_line":""}],"source_content_type":"text/x-rst","patch_set":19,"id":"3c232ec5_df4ae7f3","line":193,"in_reply_to":"d31d7cab_95df6cf5","updated":"2026-09-22 12:57:25.000000000","message":"Done","commit_id":"67f62a415d8554234cadcd5b356cb135c1cdf4ad"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"e911de24b78bc16d043673b84f373aad410489f2","unresolved":true,"context_lines":[{"line_number":292,"context_line":"identity, so a stale instance cannot renew or modify its replacement."},{"line_number":293,"context_line":"Without a stable service ID, the registration ID is derived from the tooz"},{"line_number":294,"context_line":"member ID."},{"line_number":295,"context_line":""},{"line_number":296,"context_line":"Applications can export a ``service_metadata`` capability containing public"},{"line_number":297,"context_line":"string-valued Consul metadata, in addition to tooz\u0027s serialized capabilities."},{"line_number":298,"context_line":"Keys ``member_id``, ``owner`` and ``capabilities_*`` are reserved. Public"},{"line_number":299,"context_line":"entries and serialized capabilities share Consul\u0027s 64-pair metadata budget;"},{"line_number":300,"context_line":"values"},{"line_number":301,"context_line":"must fit its 512-byte limit. Invalid metadata is rejected before registration."},{"line_number":302,"context_line":""},{"line_number":303,"context_line":"The driver also provides an application-specific cross-datacenter discovery"},{"line_number":304,"context_line":"method. Normal queries prefer the local datacenter and try remote datacenters"}],"source_content_type":"text/x-rst","patch_set":32,"id":"9737e8cf_c4f47510","line":301,"range":{"start_line":295,"start_character":1,"end_line":301,"end_character":78},"updated":"2026-09-22 12:57:25.000000000","message":"nit:\n\n```suggestion\n\nApplications can export a ``service_metadata`` capability containing public\nstring-valued Consul metadata, in addition to tooz\u0027s serialized capabilities.\nKeys ``member_id``, ``owner`` and ``capabilities_*`` are reserved. Public\nentries and serialized capabilities share Consul\u0027s 64-pair metadata budget;\nvalues must fit its 512-byte limit. Invalid metadata is rejected before\nregistration.\n```","commit_id":"e2a1b6874ef7fc8e4d61b4707c56ac23548fe5e8"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"677183f32e60545bb5868512211cf7ba0504d6b1","unresolved":true,"context_lines":[{"line_number":298,"context_line":"Keys ``member_id``, ``owner`` and ``capabilities_*`` are reserved. Public"},{"line_number":299,"context_line":"entries and serialized capabilities share Consul\u0027s 64-pair metadata budget;"},{"line_number":300,"context_line":"values"},{"line_number":301,"context_line":"must fit its 512-byte limit. Invalid metadata is rejected before registration."},{"line_number":302,"context_line":""},{"line_number":303,"context_line":"The driver also provides an application-specific cross-datacenter discovery"},{"line_number":304,"context_line":"method. Normal queries prefer the local datacenter and try remote datacenters"}],"source_content_type":"text/x-rst","patch_set":32,"id":"51fd9d6b_91c9dbd1","line":301,"in_reply_to":"9737e8cf_c4f47510","updated":"2026-09-23 11:23:51.000000000","message":"Applied your wording; this now lives in the membership change, 1006888.","commit_id":"e2a1b6874ef7fc8e4d61b4707c56ac23548fe5e8"}],"releasenotes/notes/add-consul-driver-d459a6922df96621.yaml":[{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"e911de24b78bc16d043673b84f373aad410489f2","unresolved":true,"context_lines":[{"line_number":14,"context_line":""},{"line_number":15,"context_line":"    Select it with a ``consul://host:port``, ``consul+http://host:port``, or"},{"line_number":16,"context_line":"    ``consul+https://host:port`` coordination URL. The established"},{"line_number":17,"context_line":"    ``acl_token``, ``ttl``, and ``namespace`` options are supported. The"},{"line_number":18,"context_line":"    ``ca_cert``, ``cert_cert``, ``cert_key``, and ``tls_skip_verify`` options"},{"line_number":19,"context_line":"    configure Consul API HTTPS. Each API"},{"line_number":20,"context_line":"    request has a bounded socket timeout, set by ``request_timeout``. By"},{"line_number":21,"context_line":"    default, it is the smaller of five seconds or one third of ``ttl``."},{"line_number":22,"context_line":""},{"line_number":23,"context_line":"    The"},{"line_number":24,"context_line":"    ``health_check_interval``, ``health_check_timeout``,"},{"line_number":25,"context_line":"    ``health_check_deregister_after``, and"},{"line_number":26,"context_line":"    ``health_check_tls_skip_verify`` tune the server-side HTTP check. The last"},{"line_number":27,"context_line":"    option must be enabled when member endpoints present a certificate signed"},{"line_number":28,"context_line":"    by a CA the Consul agent does not trust."},{"line_number":29,"context_line":""},{"line_number":30,"context_line":"    The heartbeat operation also reconciles registrations with the local"},{"line_number":31,"context_line":"    Consul agent. It recreates missing group markers, services, and health"}],"source_content_type":"text/x-yaml","patch_set":32,"id":"54de2d6c_02b9fb70","line":28,"range":{"start_line":17,"start_character":72,"end_line":28,"end_character":44},"updated":"2026-09-22 12:57:25.000000000","message":"nit: Wrapping is weird here. Can you rewrap this?","commit_id":"e2a1b6874ef7fc8e4d61b4707c56ac23548fe5e8"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"677183f32e60545bb5868512211cf7ba0504d6b1","unresolved":true,"context_lines":[{"line_number":25,"context_line":"    ``health_check_deregister_after``, and"},{"line_number":26,"context_line":"    ``health_check_tls_skip_verify`` tune the server-side HTTP check. The last"},{"line_number":27,"context_line":"    option must be enabled when member endpoints present a certificate signed"},{"line_number":28,"context_line":"    by a CA the Consul agent does not trust."},{"line_number":29,"context_line":""},{"line_number":30,"context_line":"    The heartbeat operation also reconciles registrations with the local"},{"line_number":31,"context_line":"    Consul agent. It recreates missing group markers, services, and health"}],"source_content_type":"text/x-yaml","patch_set":32,"id":"216449c9_79af23cb","line":28,"in_reply_to":"54de2d6c_02b9fb70","updated":"2026-09-23 11:23:51.000000000","message":"Rewrapped. The feature note now grows with each commit in the split.","commit_id":"e2a1b6874ef7fc8e4d61b4707c56ac23548fe5e8"}],"tools/compat-matrix.py":[{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"d35b4184e647eab6e7b5b89c00b4c3c720e878b2","unresolved":true,"context_lines":[{"line_number":30,"context_line":""},{"line_number":31,"context_line":""},{"line_number":32,"context_line":"driver_tpl \u003d \":py:class:`~tooz.drivers.%s`\""},{"line_number":33,"context_line":"driver_class_names \u003d ["},{"line_number":34,"context_line":"    \"file.FileDriver\","},{"line_number":35,"context_line":"    \"ipc.IPCDriver\","},{"line_number":36,"context_line":"    \"kubernetes.SherlockDriver\","}],"source_content_type":"text/x-python","patch_set":19,"id":"b74c4e78_3673e57c","line":33,"updated":"2026-08-13 11:51:30.000000000","message":"Can you re-add the consul driver here and below?","commit_id":"67f62a415d8554234cadcd5b356cb135c1cdf4ad"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"e911de24b78bc16d043673b84f373aad410489f2","unresolved":false,"context_lines":[{"line_number":30,"context_line":""},{"line_number":31,"context_line":""},{"line_number":32,"context_line":"driver_tpl \u003d \":py:class:`~tooz.drivers.%s`\""},{"line_number":33,"context_line":"driver_class_names \u003d ["},{"line_number":34,"context_line":"    \"file.FileDriver\","},{"line_number":35,"context_line":"    \"ipc.IPCDriver\","},{"line_number":36,"context_line":"    \"kubernetes.SherlockDriver\","}],"source_content_type":"text/x-python","patch_set":19,"id":"aafcb8be_e70c1fa2","line":33,"in_reply_to":"7bbd0130_7371cef1","updated":"2026-09-22 12:57:25.000000000","message":"Done","commit_id":"67f62a415d8554234cadcd5b356cb135c1cdf4ad"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"a1af0b01a5f359df8aaa500840ec0b8d7199bfa4","unresolved":true,"context_lines":[{"line_number":30,"context_line":""},{"line_number":31,"context_line":""},{"line_number":32,"context_line":"driver_tpl \u003d \":py:class:`~tooz.drivers.%s`\""},{"line_number":33,"context_line":"driver_class_names \u003d ["},{"line_number":34,"context_line":"    \"file.FileDriver\","},{"line_number":35,"context_line":"    \"ipc.IPCDriver\","},{"line_number":36,"context_line":"    \"kubernetes.SherlockDriver\","}],"source_content_type":"text/x-python","patch_set":19,"id":"7bbd0130_7371cef1","line":33,"in_reply_to":"b74c4e78_3673e57c","updated":"2026-08-13 20:58:34.000000000","message":"Done.","commit_id":"67f62a415d8554234cadcd5b356cb135c1cdf4ad"}],"tooz/drivers/consul.py":[{"author":{"_account_id":9816,"name":"Takashi Kajinami","email":"kajinamit@oss.nttdata.com","username":"kajinamit"},"change_message_id":"1f2a0ca98cdb083b5d38c63709d1eeb686024815","unresolved":true,"context_lines":[{"line_number":66,"context_line":"    \"\"\"Return a member\u0027s explicit endpoint health-check URL, if any."},{"line_number":67,"context_line":""},{"line_number":68,"context_line":"    A member opts in to Consul server-side health checking by including"},{"line_number":69,"context_line":"    a fully-formed ``health_url`` in its capabilities at join time."},{"line_number":70,"context_line":"    Only an explicit, non-empty string triggers the HTTP check; other"},{"line_number":71,"context_line":"    capability keys are never used to derive a probe URL."},{"line_number":72,"context_line":"    \"\"\""}],"source_content_type":"text/x-python","patch_set":15,"id":"70250124_9060bd37","line":69,"range":{"start_line":69,"start_character":21,"end_line":69,"end_character":31},"updated":"2026-07-23 11:27:29.000000000","message":"Can we rename it to healthcheck_url (or health_check_url ) to make it more explicit ?","commit_id":"e3661f3f6338547bd4d29f54e359638b1bb14542"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"ae5dc46918086f85cfec5c32fb7896844e1f2430","unresolved":true,"context_lines":[{"line_number":66,"context_line":"    \"\"\"Return a member\u0027s explicit endpoint health-check URL, if any."},{"line_number":67,"context_line":""},{"line_number":68,"context_line":"    A member opts in to Consul server-side health checking by including"},{"line_number":69,"context_line":"    a fully-formed ``health_url`` in its capabilities at join time."},{"line_number":70,"context_line":"    Only an explicit, non-empty string triggers the HTTP check; other"},{"line_number":71,"context_line":"    capability keys are never used to derive a probe URL."},{"line_number":72,"context_line":"    \"\"\""}],"source_content_type":"text/x-python","patch_set":15,"id":"c67b20c5_871e724b","line":69,"range":{"start_line":69,"start_character":21,"end_line":69,"end_character":31},"in_reply_to":"70250124_9060bd37","updated":"2026-08-07 02:12:02.000000000","message":"If you think that\u0027s better I don\u0027t care that much -- I think operators will set it once in config and never look at it again, so that\u0027s fine if it\u0027s clearer, happy to change it!","commit_id":"e3661f3f6338547bd4d29f54e359638b1bb14542"},{"author":{"_account_id":9816,"name":"Takashi Kajinami","email":"kajinamit@oss.nttdata.com","username":"kajinamit"},"change_message_id":"28681df910c51a62d98bd60d1fc8bc901f283e4f","unresolved":false,"context_lines":[{"line_number":66,"context_line":"    \"\"\"Return a member\u0027s explicit endpoint health-check URL, if any."},{"line_number":67,"context_line":""},{"line_number":68,"context_line":"    A member opts in to Consul server-side health checking by including"},{"line_number":69,"context_line":"    a fully-formed ``health_url`` in its capabilities at join time."},{"line_number":70,"context_line":"    Only an explicit, non-empty string triggers the HTTP check; other"},{"line_number":71,"context_line":"    capability keys are never used to derive a probe URL."},{"line_number":72,"context_line":"    \"\"\""}],"source_content_type":"text/x-python","patch_set":15,"id":"5c112ed9_4fb2a5de","line":69,"range":{"start_line":69,"start_character":21,"end_line":69,"end_character":31},"in_reply_to":"c67b20c5_871e724b","updated":"2026-08-10 16:39:37.000000000","message":"Done","commit_id":"e3661f3f6338547bd4d29f54e359638b1bb14542"},{"author":{"_account_id":9816,"name":"Takashi Kajinami","email":"kajinamit@oss.nttdata.com","username":"kajinamit"},"change_message_id":"1f2a0ca98cdb083b5d38c63709d1eeb686024815","unresolved":true,"context_lines":[{"line_number":75,"context_line":"    url \u003d capabilities.get(\u0027health_url\u0027)"},{"line_number":76,"context_line":"    if isinstance(url, str) and url:"},{"line_number":77,"context_line":"        return url"},{"line_number":78,"context_line":"    return None"},{"line_number":79,"context_line":""},{"line_number":80,"context_line":""},{"line_number":81,"context_line":"R \u003d TypeVar(\u0027R\u0027)"}],"source_content_type":"text/x-python","patch_set":15,"id":"63ce2800_4b81442c","line":78,"range":{"start_line":78,"start_character":4,"end_line":78,"end_character":10},"updated":"2026-07-23 11:27:29.000000000","message":"should we log a warning here because this indicates something wrong with client side ?","commit_id":"e3661f3f6338547bd4d29f54e359638b1bb14542"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"ae5dc46918086f85cfec5c32fb7896844e1f2430","unresolved":true,"context_lines":[{"line_number":75,"context_line":"    url \u003d capabilities.get(\u0027health_url\u0027)"},{"line_number":76,"context_line":"    if isinstance(url, str) and url:"},{"line_number":77,"context_line":"        return url"},{"line_number":78,"context_line":"    return None"},{"line_number":79,"context_line":""},{"line_number":80,"context_line":""},{"line_number":81,"context_line":"R \u003d TypeVar(\u0027R\u0027)"}],"source_content_type":"text/x-python","patch_set":15,"id":"d806a4ef_ae349beb","line":78,"range":{"start_line":78,"start_character":4,"end_line":78,"end_character":10},"in_reply_to":"63ce2800_4b81442c","updated":"2026-08-07 02:12:02.000000000","message":"As in, if it falls through to \"no healthcheck endpoint url\"? I don\u0027t actually operate consul myself, I\u0027m not sure if that\u0027s an issue or not? I think it\u0027s perfectly valid without it? I can do some research and will check with Furukawa-san to see if he knows.","commit_id":"e3661f3f6338547bd4d29f54e359638b1bb14542"},{"author":{"_account_id":9816,"name":"Takashi Kajinami","email":"kajinamit@oss.nttdata.com","username":"kajinamit"},"change_message_id":"28681df910c51a62d98bd60d1fc8bc901f283e4f","unresolved":false,"context_lines":[{"line_number":75,"context_line":"    url \u003d capabilities.get(\u0027health_url\u0027)"},{"line_number":76,"context_line":"    if isinstance(url, str) and url:"},{"line_number":77,"context_line":"        return url"},{"line_number":78,"context_line":"    return None"},{"line_number":79,"context_line":""},{"line_number":80,"context_line":""},{"line_number":81,"context_line":"R \u003d TypeVar(\u0027R\u0027)"}],"source_content_type":"text/x-python","patch_set":15,"id":"da1574a1_6b457e2f","line":78,"range":{"start_line":78,"start_character":4,"end_line":78,"end_character":10},"in_reply_to":"d806a4ef_ae349beb","updated":"2026-08-10 16:39:37.000000000","message":"It may be correct from logic perspective, but would be confusing for developers, and I think it\u0027s better for operators/developers to emit this warning so that they can notice something with their application is wrong.","commit_id":"e3661f3f6338547bd4d29f54e359638b1bb14542"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"79e8b705021d66ad952c56a6868f3420e0368cf9","unresolved":false,"context_lines":[{"line_number":75,"context_line":"    url \u003d capabilities.get(\u0027health_url\u0027)"},{"line_number":76,"context_line":"    if isinstance(url, str) and url:"},{"line_number":77,"context_line":"        return url"},{"line_number":78,"context_line":"    return None"},{"line_number":79,"context_line":""},{"line_number":80,"context_line":""},{"line_number":81,"context_line":"R \u003d TypeVar(\u0027R\u0027)"}],"source_content_type":"text/x-python","patch_set":15,"id":"92e85a36_f06350c4","line":78,"range":{"start_line":78,"start_character":4,"end_line":78,"end_character":10},"in_reply_to":"da1574a1_6b457e2f","updated":"2026-08-11 07:01:47.000000000","message":"Ok, I looked at this again and I guess it *shouldn\u0027t* be triggered very often, so it probably can\u0027t hurt. Went ahead and added a warning.","commit_id":"e3661f3f6338547bd4d29f54e359638b1bb14542"},{"author":{"_account_id":9816,"name":"Takashi Kajinami","email":"kajinamit@oss.nttdata.com","username":"kajinamit"},"change_message_id":"1f2a0ca98cdb083b5d38c63709d1eeb686024815","unresolved":true,"context_lines":[{"line_number":87,"context_line":""},{"line_number":88,"context_line":""},{"line_number":89,"context_line":"# KV prefix for group markers (existence tracking)."},{"line_number":90,"context_line":"_GROUP_PREFIX \u003d \"tooz/groups/\""},{"line_number":91,"context_line":""},{"line_number":92,"context_line":"# How often a blocking lock acquire re-polls Consul while waiting."},{"line_number":93,"context_line":"_LOCK_POLL_INTERVAL \u003d 0.5"}],"source_content_type":"text/x-python","patch_set":15,"id":"06117b9d_2d5f6d99","line":90,"range":{"start_line":90,"start_character":17,"end_line":90,"end_character":21},"updated":"2026-07-23 11:27:29.000000000","message":"I wonder if we can make this \"tooz\" prefix configurable by the namespace option ? This is what was implemented in earlier consul driver, and may be useful in case multiple services share the same consul cluster.","commit_id":"e3661f3f6338547bd4d29f54e359638b1bb14542"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"ae5dc46918086f85cfec5c32fb7896844e1f2430","unresolved":true,"context_lines":[{"line_number":87,"context_line":""},{"line_number":88,"context_line":""},{"line_number":89,"context_line":"# KV prefix for group markers (existence tracking)."},{"line_number":90,"context_line":"_GROUP_PREFIX \u003d \"tooz/groups/\""},{"line_number":91,"context_line":""},{"line_number":92,"context_line":"# How often a blocking lock acquire re-polls Consul while waiting."},{"line_number":93,"context_line":"_LOCK_POLL_INTERVAL \u003d 0.5"}],"source_content_type":"text/x-python","patch_set":15,"id":"53d7383d_66086526","line":90,"range":{"start_line":90,"start_character":17,"end_line":90,"end_character":21},"in_reply_to":"06117b9d_2d5f6d99","updated":"2026-08-07 02:12:02.000000000","message":"ah yeah, can prolly do that, I will look into it.","commit_id":"e3661f3f6338547bd4d29f54e359638b1bb14542"},{"author":{"_account_id":9816,"name":"Takashi Kajinami","email":"kajinamit@oss.nttdata.com","username":"kajinamit"},"change_message_id":"28681df910c51a62d98bd60d1fc8bc901f283e4f","unresolved":false,"context_lines":[{"line_number":87,"context_line":""},{"line_number":88,"context_line":""},{"line_number":89,"context_line":"# KV prefix for group markers (existence tracking)."},{"line_number":90,"context_line":"_GROUP_PREFIX \u003d \"tooz/groups/\""},{"line_number":91,"context_line":""},{"line_number":92,"context_line":"# How often a blocking lock acquire re-polls Consul while waiting."},{"line_number":93,"context_line":"_LOCK_POLL_INTERVAL \u003d 0.5"}],"source_content_type":"text/x-python","patch_set":15,"id":"bad11778_44745bd5","line":90,"range":{"start_line":90,"start_character":17,"end_line":90,"end_character":21},"in_reply_to":"53d7383d_66086526","updated":"2026-08-10 16:39:37.000000000","message":"Done","commit_id":"e3661f3f6338547bd4d29f54e359638b1bb14542"},{"author":{"_account_id":9816,"name":"Takashi Kajinami","email":"kajinamit@oss.nttdata.com","username":"kajinamit"},"change_message_id":"91052ea8dd064327085eb05e72f275d5e73d1fd8","unresolved":true,"context_lines":[{"line_number":75,"context_line":"    url \u003d capabilities.get(\u0027healthcheck_url\u0027)"},{"line_number":76,"context_line":"    if isinstance(url, str) and url:"},{"line_number":77,"context_line":"        return url"},{"line_number":78,"context_line":"    LOG.warning("},{"line_number":79,"context_line":"        \"No valid healthcheck_url capability was provided; falling back \""},{"line_number":80,"context_line":"        \"to TTL-based member liveness\""},{"line_number":81,"context_line":"    )"}],"source_content_type":"text/x-python","patch_set":17,"id":"f5f2b7ec_d64a8d04","line":78,"range":{"start_line":78,"start_character":8,"end_line":78,"end_character":15},"updated":"2026-08-11 11:48:08.000000000","message":"sorry I wasn\u0027t specific. Can we show this warning only when healthcheck_url is set but is not string. That\u0027s a wrong usage pattern I\u0027m concerned.","commit_id":"4bb35da7c3256a62036aa250fdbecca1d8676675"},{"author":{"_account_id":9816,"name":"Takashi Kajinami","email":"kajinamit@oss.nttdata.com","username":"kajinamit"},"change_message_id":"6c609512cb278aabe5ad1c8e68b9bd608340557a","unresolved":false,"context_lines":[{"line_number":75,"context_line":"    url \u003d capabilities.get(\u0027healthcheck_url\u0027)"},{"line_number":76,"context_line":"    if isinstance(url, str) and url:"},{"line_number":77,"context_line":"        return url"},{"line_number":78,"context_line":"    LOG.warning("},{"line_number":79,"context_line":"        \"No valid healthcheck_url capability was provided; falling back \""},{"line_number":80,"context_line":"        \"to TTL-based member liveness\""},{"line_number":81,"context_line":"    )"}],"source_content_type":"text/x-python","patch_set":17,"id":"b66bad05_29301ca9","line":78,"range":{"start_line":78,"start_character":8,"end_line":78,"end_character":15},"in_reply_to":"a7d0d1f7_d81a2804","updated":"2026-08-13 03:54:10.000000000","message":"Done","commit_id":"4bb35da7c3256a62036aa250fdbecca1d8676675"},{"author":{"_account_id":9816,"name":"Takashi Kajinami","email":"kajinamit@oss.nttdata.com","username":"kajinamit"},"change_message_id":"672bf167a5c956ea284eee19564343500438023e","unresolved":true,"context_lines":[{"line_number":75,"context_line":"    url \u003d capabilities.get(\u0027healthcheck_url\u0027)"},{"line_number":76,"context_line":"    if isinstance(url, str) and url:"},{"line_number":77,"context_line":"        return url"},{"line_number":78,"context_line":"    LOG.warning("},{"line_number":79,"context_line":"        \"No valid healthcheck_url capability was provided; falling back \""},{"line_number":80,"context_line":"        \"to TTL-based member liveness\""},{"line_number":81,"context_line":"    )"}],"source_content_type":"text/x-python","patch_set":17,"id":"a7d0d1f7_d81a2804","line":78,"range":{"start_line":78,"start_character":8,"end_line":78,"end_character":15},"in_reply_to":"d923d8a2_7a98fe31","updated":"2026-08-13 01:10:15.000000000","message":"My point is that we don\u0027t have to show that warning if healthcheck_url is not set. See my suggestion (in a different comment) below.","commit_id":"4bb35da7c3256a62036aa250fdbecca1d8676675"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"c74aa0201deb64c2f3579715f4d8b9cb9b78e490","unresolved":true,"context_lines":[{"line_number":75,"context_line":"    url \u003d capabilities.get(\u0027healthcheck_url\u0027)"},{"line_number":76,"context_line":"    if isinstance(url, str) and url:"},{"line_number":77,"context_line":"        return url"},{"line_number":78,"context_line":"    LOG.warning("},{"line_number":79,"context_line":"        \"No valid healthcheck_url capability was provided; falling back \""},{"line_number":80,"context_line":"        \"to TTL-based member liveness\""},{"line_number":81,"context_line":"    )"}],"source_content_type":"text/x-python","patch_set":17,"id":"d923d8a2_7a98fe31","line":78,"range":{"start_line":78,"start_character":8,"end_line":78,"end_character":15},"in_reply_to":"f5f2b7ec_d64a8d04","updated":"2026-08-11 21:33:07.000000000","message":"Ok I thought that was already what I had, so maybe I’m still misunderstanding. Was it correct last patchset?","commit_id":"4bb35da7c3256a62036aa250fdbecca1d8676675"},{"author":{"_account_id":9816,"name":"Takashi Kajinami","email":"kajinamit@oss.nttdata.com","username":"kajinamit"},"change_message_id":"672bf167a5c956ea284eee19564343500438023e","unresolved":true,"context_lines":[{"line_number":73,"context_line":"    if not capabilities:"},{"line_number":74,"context_line":"        return None"},{"line_number":75,"context_line":"    url \u003d capabilities.get(\u0027healthcheck_url\u0027)"},{"line_number":76,"context_line":"    if isinstance(url, str) and url:"},{"line_number":77,"context_line":"        return url"},{"line_number":78,"context_line":"    LOG.warning("},{"line_number":79,"context_line":"        \"No valid healthcheck_url capability was provided; falling back \""},{"line_number":80,"context_line":"        \"to TTL-based member liveness\""},{"line_number":81,"context_line":"    )"},{"line_number":82,"context_line":"    return None"},{"line_number":83,"context_line":""},{"line_number":84,"context_line":""},{"line_number":85,"context_line":"R \u003d TypeVar(\u0027R\u0027)"}],"source_content_type":"text/x-python","patch_set":17,"id":"c15c2279_02ed50e1","line":82,"range":{"start_line":76,"start_character":0,"end_line":82,"end_character":15},"updated":"2026-08-13 01:10:15.000000000","message":"```suggestion\n    if not isinstance(url, str):\n        LOG.warning(\n            \"No valid healthcheck_url capability was provided; \"\n            \"falling back to TTL-based member liveness\"\n        )\n        return None\n    return url\n```","commit_id":"4bb35da7c3256a62036aa250fdbecca1d8676675"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"0b4c9a0e3e940b696e2a4e2196d7d6cd00a04feb","unresolved":true,"context_lines":[{"line_number":73,"context_line":"    if not capabilities:"},{"line_number":74,"context_line":"        return None"},{"line_number":75,"context_line":"    url \u003d capabilities.get(\u0027healthcheck_url\u0027)"},{"line_number":76,"context_line":"    if isinstance(url, str) and url:"},{"line_number":77,"context_line":"        return url"},{"line_number":78,"context_line":"    LOG.warning("},{"line_number":79,"context_line":"        \"No valid healthcheck_url capability was provided; falling back \""},{"line_number":80,"context_line":"        \"to TTL-based member liveness\""},{"line_number":81,"context_line":"    )"},{"line_number":82,"context_line":"    return None"},{"line_number":83,"context_line":""},{"line_number":84,"context_line":""},{"line_number":85,"context_line":"R \u003d TypeVar(\u0027R\u0027)"}],"source_content_type":"text/x-python","patch_set":17,"id":"f8103833_77045db3","line":82,"range":{"start_line":76,"start_character":0,"end_line":82,"end_character":15},"in_reply_to":"c15c2279_02ed50e1","updated":"2026-08-13 01:31:47.000000000","message":"OK, I think this is now how you wanted it?","commit_id":"4bb35da7c3256a62036aa250fdbecca1d8676675"},{"author":{"_account_id":9816,"name":"Takashi Kajinami","email":"kajinamit@oss.nttdata.com","username":"kajinamit"},"change_message_id":"6c609512cb278aabe5ad1c8e68b9bd608340557a","unresolved":false,"context_lines":[{"line_number":73,"context_line":"    if not capabilities:"},{"line_number":74,"context_line":"        return None"},{"line_number":75,"context_line":"    url \u003d capabilities.get(\u0027healthcheck_url\u0027)"},{"line_number":76,"context_line":"    if isinstance(url, str) and url:"},{"line_number":77,"context_line":"        return url"},{"line_number":78,"context_line":"    LOG.warning("},{"line_number":79,"context_line":"        \"No valid healthcheck_url capability was provided; falling back \""},{"line_number":80,"context_line":"        \"to TTL-based member liveness\""},{"line_number":81,"context_line":"    )"},{"line_number":82,"context_line":"    return None"},{"line_number":83,"context_line":""},{"line_number":84,"context_line":""},{"line_number":85,"context_line":"R \u003d TypeVar(\u0027R\u0027)"}],"source_content_type":"text/x-python","patch_set":17,"id":"598e3714_090cab9d","line":82,"range":{"start_line":76,"start_character":0,"end_line":82,"end_character":15},"in_reply_to":"f8103833_77045db3","updated":"2026-08-13 03:54:10.000000000","message":"Yes. I also noticed that we need to adjust the message to published the edit from my end.","commit_id":"4bb35da7c3256a62036aa250fdbecca1d8676675"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"85509edaca412cb3f71bbe770c781831c31ac15b","unresolved":true,"context_lines":[{"line_number":85,"context_line":"R \u003d TypeVar(\u0027R\u0027)"},{"line_number":86,"context_line":""},{"line_number":87,"context_line":""},{"line_number":88,"context_line":"def _to_bytes(value: bytes | str) -\u003e bytes:"},{"line_number":89,"context_line":"    \"\"\"Normalize group/member IDs to bytes.\"\"\""},{"line_number":90,"context_line":"    return value.encode() if isinstance(value, str) else value"},{"line_number":91,"context_line":""}],"source_content_type":"text/x-python","patch_set":19,"id":"90f6c84b_485e292b","line":88,"updated":"2026-09-18 16:56:26.000000000","message":"Can we use `tooz.utils.to_binary` instead?","commit_id":"67f62a415d8554234cadcd5b356cb135c1cdf4ad"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"e911de24b78bc16d043673b84f373aad410489f2","unresolved":false,"context_lines":[{"line_number":85,"context_line":"R \u003d TypeVar(\u0027R\u0027)"},{"line_number":86,"context_line":""},{"line_number":87,"context_line":""},{"line_number":88,"context_line":"def _to_bytes(value: bytes | str) -\u003e bytes:"},{"line_number":89,"context_line":"    \"\"\"Normalize group/member IDs to bytes.\"\"\""},{"line_number":90,"context_line":"    return value.encode() if isinstance(value, str) else value"},{"line_number":91,"context_line":""}],"source_content_type":"text/x-python","patch_set":19,"id":"a4a7aff8_aa2ddb88","line":88,"in_reply_to":"90f6c84b_485e292b","updated":"2026-09-22 12:57:25.000000000","message":"Done","commit_id":"67f62a415d8554234cadcd5b356cb135c1cdf4ad"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"1490fb46d986504554736e255ee39bd7db09979d","unresolved":true,"context_lines":[{"line_number":85,"context_line":"R \u003d TypeVar(\u0027R\u0027)"},{"line_number":86,"context_line":""},{"line_number":87,"context_line":""},{"line_number":88,"context_line":"def _to_bytes(value: bytes | str) -\u003e bytes:"},{"line_number":89,"context_line":"    \"\"\"Normalize group/member IDs to bytes.\"\"\""},{"line_number":90,"context_line":"    return value.encode() if isinstance(value, str) else value"},{"line_number":91,"context_line":""}],"source_content_type":"text/x-python","patch_set":19,"id":"de807876_fab08bde","line":88,"in_reply_to":"90f6c84b_485e292b","updated":"2026-09-22 12:17:22.000000000","message":"Replaced with utils.to_binary(..., encoding\u003d\u0027utf-8\u0027) to preserve the existing encoding.","commit_id":"67f62a415d8554234cadcd5b356cb135c1cdf4ad"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"85509edaca412cb3f71bbe770c781831c31ac15b","unresolved":true,"context_lines":[{"line_number":101,"context_line":""},{"line_number":102,"context_line":"R \u003d TypeVar(\u0027R\u0027)"},{"line_number":103,"context_line":""},{"line_number":104,"context_line":""},{"line_number":105,"context_line":"def _to_bytes(value: bytes | str) -\u003e bytes:"},{"line_number":106,"context_line":"    \"\"\"Normalize group/member IDs to bytes.\"\"\""},{"line_number":107,"context_line":"    return value.encode() if isinstance(value, str) else value"},{"line_number":108,"context_line":""},{"line_number":109,"context_line":""},{"line_number":110,"context_line":"_IDENTIFIER_ESCAPE_PREFIX \u003d \"b64~\""},{"line_number":111,"context_line":""}],"source_content_type":"text/x-python","patch_set":29,"id":"4b4b828d_af8fc143","line":108,"range":{"start_line":104,"start_character":1,"end_line":108,"end_character":1},"updated":"2026-09-18 16:56:26.000000000","message":"Can we drop this in favour of `tooz.utils.to_binary`?","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"e911de24b78bc16d043673b84f373aad410489f2","unresolved":false,"context_lines":[{"line_number":101,"context_line":""},{"line_number":102,"context_line":"R \u003d TypeVar(\u0027R\u0027)"},{"line_number":103,"context_line":""},{"line_number":104,"context_line":""},{"line_number":105,"context_line":"def _to_bytes(value: bytes | str) -\u003e bytes:"},{"line_number":106,"context_line":"    \"\"\"Normalize group/member IDs to bytes.\"\"\""},{"line_number":107,"context_line":"    return value.encode() if isinstance(value, str) else value"},{"line_number":108,"context_line":""},{"line_number":109,"context_line":""},{"line_number":110,"context_line":"_IDENTIFIER_ESCAPE_PREFIX \u003d \"b64~\""},{"line_number":111,"context_line":""}],"source_content_type":"text/x-python","patch_set":29,"id":"5d3694f6_085d0385","line":108,"range":{"start_line":104,"start_character":1,"end_line":108,"end_character":1},"in_reply_to":"4b4b828d_af8fc143","updated":"2026-09-22 12:57:25.000000000","message":"Done","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"1490fb46d986504554736e255ee39bd7db09979d","unresolved":true,"context_lines":[{"line_number":101,"context_line":""},{"line_number":102,"context_line":"R \u003d TypeVar(\u0027R\u0027)"},{"line_number":103,"context_line":""},{"line_number":104,"context_line":""},{"line_number":105,"context_line":"def _to_bytes(value: bytes | str) -\u003e bytes:"},{"line_number":106,"context_line":"    \"\"\"Normalize group/member IDs to bytes.\"\"\""},{"line_number":107,"context_line":"    return value.encode() if isinstance(value, str) else value"},{"line_number":108,"context_line":""},{"line_number":109,"context_line":""},{"line_number":110,"context_line":"_IDENTIFIER_ESCAPE_PREFIX \u003d \"b64~\""},{"line_number":111,"context_line":""}],"source_content_type":"text/x-python","patch_set":29,"id":"7992c3b1_ed1c3c98","line":108,"range":{"start_line":104,"start_character":1,"end_line":108,"end_character":1},"in_reply_to":"4b4b828d_af8fc143","updated":"2026-09-22 12:17:22.000000000","message":"Done, with encoding\u003d\u0027utf-8\u0027 to preserve non-ASCII identifiers.","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"85509edaca412cb3f71bbe770c781831c31ac15b","unresolved":false,"context_lines":[{"line_number":250,"context_line":"            OSError,"},{"line_number":251,"context_line":"            requests.exceptions.RequestException,"},{"line_number":252,"context_line":"        ) as e:"},{"line_number":253,"context_line":"            utils.raise_with_cause("},{"line_number":254,"context_line":"                coordination.ToozConnectionError, str(e), cause\u003de"},{"line_number":255,"context_line":"            )"},{"line_number":256,"context_line":"        except consul.ConsulException as e:"}],"source_content_type":"text/x-python","patch_set":29,"id":"3164b81c_a993a4da","line":253,"updated":"2026-09-18 16:56:26.000000000","message":"As an aside, we probably want to get rid of this util method (which I\u0027m guessing was written when Python 2.7 was something we cared about) in favour of `raise from` syntax. This is consistent for now though.","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"1490fb46d986504554736e255ee39bd7db09979d","unresolved":false,"context_lines":[{"line_number":250,"context_line":"            OSError,"},{"line_number":251,"context_line":"            requests.exceptions.RequestException,"},{"line_number":252,"context_line":"        ) as e:"},{"line_number":253,"context_line":"            utils.raise_with_cause("},{"line_number":254,"context_line":"                coordination.ToozConnectionError, str(e), cause\u003de"},{"line_number":255,"context_line":"            )"},{"line_number":256,"context_line":"        except consul.ConsulException as e:"}],"source_content_type":"text/x-python","patch_set":29,"id":"8bf668d1_6ceb16ed","line":253,"in_reply_to":"3164b81c_a993a4da","updated":"2026-09-22 12:17:22.000000000","message":"Agreed; kept this consistent with the other drivers for now.","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"85509edaca412cb3f71bbe770c781831c31ac15b","unresolved":true,"context_lines":[{"line_number":275,"context_line":"        namespace: str,"},{"line_number":276,"context_line":"        coordinator: ConsulDriver | None \u003d None,"},{"line_number":277,"context_line":"        lock_prefix: str \u003d \"locks\","},{"line_number":278,"context_line":"    ):"},{"line_number":279,"context_line":"        super().__init__(name)"},{"line_number":280,"context_line":"        self._name \u003d name"},{"line_number":281,"context_line":"        self._node \u003d node"}],"source_content_type":"text/x-python","patch_set":29,"id":"617eb937_3d8bfffa","line":278,"updated":"2026-09-18 16:56:26.000000000","message":"```suggestion\n    ) -\u003e None:\n```","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"1490fb46d986504554736e255ee39bd7db09979d","unresolved":true,"context_lines":[{"line_number":275,"context_line":"        namespace: str,"},{"line_number":276,"context_line":"        coordinator: ConsulDriver | None \u003d None,"},{"line_number":277,"context_line":"        lock_prefix: str \u003d \"locks\","},{"line_number":278,"context_line":"    ):"},{"line_number":279,"context_line":"        super().__init__(name)"},{"line_number":280,"context_line":"        self._name \u003d name"},{"line_number":281,"context_line":"        self._node \u003d node"}],"source_content_type":"text/x-python","patch_set":29,"id":"a4665da3_5e5dff66","line":278,"in_reply_to":"617eb937_3d8bfffa","updated":"2026-09-22 12:17:22.000000000","message":"Added.","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"e911de24b78bc16d043673b84f373aad410489f2","unresolved":false,"context_lines":[{"line_number":275,"context_line":"        namespace: str,"},{"line_number":276,"context_line":"        coordinator: ConsulDriver | None \u003d None,"},{"line_number":277,"context_line":"        lock_prefix: str \u003d \"locks\","},{"line_number":278,"context_line":"    ):"},{"line_number":279,"context_line":"        super().__init__(name)"},{"line_number":280,"context_line":"        self._name \u003d name"},{"line_number":281,"context_line":"        self._node \u003d node"}],"source_content_type":"text/x-python","patch_set":29,"id":"c66aaade_3905e9b4","line":278,"in_reply_to":"617eb937_3d8bfffa","updated":"2026-09-22 12:57:25.000000000","message":"Done","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"85509edaca412cb3f71bbe770c781831c31ac15b","unresolved":true,"context_lines":[{"line_number":289,"context_line":""},{"line_number":290,"context_line":"    @property"},{"line_number":291,"context_line":"    def _client(self) -\u003e consul.Consul:"},{"line_number":292,"context_line":"        if callable(self._client_source):"},{"line_number":293,"context_line":"            return self._client_source()"},{"line_number":294,"context_line":"        return self._client_source"},{"line_number":295,"context_line":""},{"line_number":296,"context_line":"    @_translate_failures"},{"line_number":297,"context_line":"    def acquire("}],"source_content_type":"text/x-python","patch_set":29,"id":"69e84d38_18b6e322","line":294,"range":{"start_line":292,"start_character":0,"end_line":294,"end_character":34},"updated":"2026-09-18 16:56:26.000000000","message":"This is unusual enough to warrant a comment, IMO","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"1490fb46d986504554736e255ee39bd7db09979d","unresolved":true,"context_lines":[{"line_number":289,"context_line":""},{"line_number":290,"context_line":"    @property"},{"line_number":291,"context_line":"    def _client(self) -\u003e consul.Consul:"},{"line_number":292,"context_line":"        if callable(self._client_source):"},{"line_number":293,"context_line":"            return self._client_source()"},{"line_number":294,"context_line":"        return self._client_source"},{"line_number":295,"context_line":""},{"line_number":296,"context_line":"    @_translate_failures"},{"line_number":297,"context_line":"    def acquire("}],"source_content_type":"text/x-python","patch_set":29,"id":"33a3eded_d9baabea","line":294,"range":{"start_line":292,"start_character":0,"end_line":294,"end_character":34},"in_reply_to":"69e84d38_18b6e322","updated":"2026-09-22 12:17:22.000000000","message":"Added a comment: deferred lookup gives each thread its own client/session.","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"85509edaca412cb3f71bbe770c781831c31ac15b","unresolved":true,"context_lines":[{"line_number":485,"context_line":"    provides_endpoint_health \u003d True"},{"line_number":486,"context_line":""},{"line_number":487,"context_line":"    def __init__("},{"line_number":488,"context_line":"        self, member_id: bytes, parsed_url: Any, options: dict[str, Any]"},{"line_number":489,"context_line":"    ) -\u003e None:"},{"line_number":490,"context_line":"        super().__init__(member_id, parsed_url, options)"},{"line_number":491,"context_line":"        host \u003d parsed_url.hostname or \"localhost\""}],"source_content_type":"text/x-python","patch_set":29,"id":"6902de54_266fa3bd","line":488,"updated":"2026-09-18 16:56:26.000000000","message":"```suggestion\n        self,\n        member_id: bytes,\n        parsed_url: netutils.SplitResult,\n        options: dict[str, Any],\n```","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"e911de24b78bc16d043673b84f373aad410489f2","unresolved":false,"context_lines":[{"line_number":485,"context_line":"    provides_endpoint_health \u003d True"},{"line_number":486,"context_line":""},{"line_number":487,"context_line":"    def __init__("},{"line_number":488,"context_line":"        self, member_id: bytes, parsed_url: Any, options: dict[str, Any]"},{"line_number":489,"context_line":"    ) -\u003e None:"},{"line_number":490,"context_line":"        super().__init__(member_id, parsed_url, options)"},{"line_number":491,"context_line":"        host \u003d parsed_url.hostname or \"localhost\""}],"source_content_type":"text/x-python","patch_set":29,"id":"e377444d_a05b35a4","line":488,"in_reply_to":"6902de54_266fa3bd","updated":"2026-09-22 12:57:25.000000000","message":"Done","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"1490fb46d986504554736e255ee39bd7db09979d","unresolved":true,"context_lines":[{"line_number":485,"context_line":"    provides_endpoint_health \u003d True"},{"line_number":486,"context_line":""},{"line_number":487,"context_line":"    def __init__("},{"line_number":488,"context_line":"        self, member_id: bytes, parsed_url: Any, options: dict[str, Any]"},{"line_number":489,"context_line":"    ) -\u003e None:"},{"line_number":490,"context_line":"        super().__init__(member_id, parsed_url, options)"},{"line_number":491,"context_line":"        host \u003d parsed_url.hostname or \"localhost\""}],"source_content_type":"text/x-python","patch_set":29,"id":"bca073b9_157d8e63","line":488,"in_reply_to":"6902de54_266fa3bd","updated":"2026-09-22 12:17:22.000000000","message":"Updated to netutils.SplitResult.","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"85509edaca412cb3f71bbe770c781831c31ac15b","unresolved":true,"context_lines":[{"line_number":493,"context_line":"        self._host \u003d host"},{"line_number":494,"context_line":"        self._port \u003d port"},{"line_number":495,"context_line":"        self._scheme \u003d ("},{"line_number":496,"context_line":"            \"https\" if parsed_url.scheme \u003d\u003d \"consul+https\" else \"http\""},{"line_number":497,"context_line":"        )"},{"line_number":498,"context_line":"        self._acl_token \u003d options.get(\"acl_token\", [None])[0]"},{"line_number":499,"context_line":"        self._namespace \u003d options.get(\"namespace\", [\"tooz\"])[0]"},{"line_number":500,"context_line":"        self._service_name_template: str \u003d options.get("},{"line_number":501,"context_line":"            \"service_name_template\", [\"{namespace}-{group}\"]"},{"line_number":502,"context_line":"        )[0]"},{"line_number":503,"context_line":"        fields \u003d set()"},{"line_number":504,"context_line":"        for _, field, spec, conversion in string.Formatter().parse("},{"line_number":505,"context_line":"            self._service_name_template"},{"line_number":506,"context_line":"        ):"},{"line_number":507,"context_line":"            if field is not None:"},{"line_number":508,"context_line":"                if field not in {\"namespace\", \"group\"} or spec or conversion:"},{"line_number":509,"context_line":"                    raise ValueError(\"Invalid Consul service_name_template\")"},{"line_number":510,"context_line":"                fields.add(field)"},{"line_number":511,"context_line":"        if \"group\" not in fields:"},{"line_number":512,"context_line":"            raise ValueError(\"service_name_template must contain {group}\")"},{"line_number":513,"context_line":"        self._group_prefix \u003d f\"{self._namespace}/groups/\""},{"line_number":514,"context_line":"        ttl_option \u003d options.get(\"ttl\", options.get(\"timeout\", [_DEFAULT_TTL]))"},{"line_number":515,"context_line":"        self._ttl \u003d int(ttl_option[0])"},{"line_number":516,"context_line":"        if not 1 \u003c\u003d self._ttl \u003c\u003d _CONSUL_SESSION_TTL_MAX:"}],"source_content_type":"text/x-python","patch_set":29,"id":"f4a720f4_8e6a16a0","line":513,"range":{"start_line":496,"start_character":70,"end_line":513,"end_character":57},"updated":"2026-09-18 16:56:26.000000000","message":"style nit: could we group these somewhat to make it a little easier to grok what\u0027s going on?\n\n```suggestion\n            \"https\" if parsed_url.scheme \u003d\u003d \"consul+https\" else \"http\"\n        )\n\n        self._acl_token \u003d options.get(\"acl_token\", [None])[0]\n        self._namespace \u003d options.get(\"namespace\", [\"tooz\"])[0]\n        self._service_name_template: str \u003d options.get(\n            \"service_name_template\", [\"{namespace}-{group}\"]\n        )[0]\n        \n        fields \u003d set()\n        for _, field, spec, conversion in string.Formatter().parse(\n            self._service_name_template\n        ):\n            if field is not None:\n                if field not in {\"namespace\", \"group\"} or spec or conversion:\n                    raise ValueError(\"Invalid Consul service_name_template\")\n                fields.add(field)\n\n        if \"group\" not in fields:\n            raise ValueError(\"service_name_template must contain {group}\")\n\n        self._group_prefix \u003d f\"{self._namespace}/groups/\"\n```\n\nAlso, do we want to check that `options[\u0027service_name_template\u0027]` is a list before we attempt to index it? Indexing also works for strings after all. Ditto for other list opts below.","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"1490fb46d986504554736e255ee39bd7db09979d","unresolved":true,"context_lines":[{"line_number":493,"context_line":"        self._host \u003d host"},{"line_number":494,"context_line":"        self._port \u003d port"},{"line_number":495,"context_line":"        self._scheme \u003d ("},{"line_number":496,"context_line":"            \"https\" if parsed_url.scheme \u003d\u003d \"consul+https\" else \"http\""},{"line_number":497,"context_line":"        )"},{"line_number":498,"context_line":"        self._acl_token \u003d options.get(\"acl_token\", [None])[0]"},{"line_number":499,"context_line":"        self._namespace \u003d options.get(\"namespace\", [\"tooz\"])[0]"},{"line_number":500,"context_line":"        self._service_name_template: str \u003d options.get("},{"line_number":501,"context_line":"            \"service_name_template\", [\"{namespace}-{group}\"]"},{"line_number":502,"context_line":"        )[0]"},{"line_number":503,"context_line":"        fields \u003d set()"},{"line_number":504,"context_line":"        for _, field, spec, conversion in string.Formatter().parse("},{"line_number":505,"context_line":"            self._service_name_template"},{"line_number":506,"context_line":"        ):"},{"line_number":507,"context_line":"            if field is not None:"},{"line_number":508,"context_line":"                if field not in {\"namespace\", \"group\"} or spec or conversion:"},{"line_number":509,"context_line":"                    raise ValueError(\"Invalid Consul service_name_template\")"},{"line_number":510,"context_line":"                fields.add(field)"},{"line_number":511,"context_line":"        if \"group\" not in fields:"},{"line_number":512,"context_line":"            raise ValueError(\"service_name_template must contain {group}\")"},{"line_number":513,"context_line":"        self._group_prefix \u003d f\"{self._namespace}/groups/\""},{"line_number":514,"context_line":"        ttl_option \u003d options.get(\"ttl\", options.get(\"timeout\", [_DEFAULT_TTL]))"},{"line_number":515,"context_line":"        self._ttl \u003d int(ttl_option[0])"},{"line_number":516,"context_line":"        if not 1 \u003c\u003d self._ttl \u003c\u003d _CONSUL_SESSION_TTL_MAX:"}],"source_content_type":"text/x-python","patch_set":29,"id":"91887cb2_a227f14e","line":513,"range":{"start_line":496,"start_character":70,"end_line":513,"end_character":57},"in_reply_to":"f4a720f4_8e6a16a0","updated":"2026-09-22 12:17:22.000000000","message":"Added the spacing and switched to the base class\u0027s normalized self._options, removing the list indexing. Added coverage for repeated URL options and keyword precedence.","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"e911de24b78bc16d043673b84f373aad410489f2","unresolved":false,"context_lines":[{"line_number":493,"context_line":"        self._host \u003d host"},{"line_number":494,"context_line":"        self._port \u003d port"},{"line_number":495,"context_line":"        self._scheme \u003d ("},{"line_number":496,"context_line":"            \"https\" if parsed_url.scheme \u003d\u003d \"consul+https\" else \"http\""},{"line_number":497,"context_line":"        )"},{"line_number":498,"context_line":"        self._acl_token \u003d options.get(\"acl_token\", [None])[0]"},{"line_number":499,"context_line":"        self._namespace \u003d options.get(\"namespace\", [\"tooz\"])[0]"},{"line_number":500,"context_line":"        self._service_name_template: str \u003d options.get("},{"line_number":501,"context_line":"            \"service_name_template\", [\"{namespace}-{group}\"]"},{"line_number":502,"context_line":"        )[0]"},{"line_number":503,"context_line":"        fields \u003d set()"},{"line_number":504,"context_line":"        for _, field, spec, conversion in string.Formatter().parse("},{"line_number":505,"context_line":"            self._service_name_template"},{"line_number":506,"context_line":"        ):"},{"line_number":507,"context_line":"            if field is not None:"},{"line_number":508,"context_line":"                if field not in {\"namespace\", \"group\"} or spec or conversion:"},{"line_number":509,"context_line":"                    raise ValueError(\"Invalid Consul service_name_template\")"},{"line_number":510,"context_line":"                fields.add(field)"},{"line_number":511,"context_line":"        if \"group\" not in fields:"},{"line_number":512,"context_line":"            raise ValueError(\"service_name_template must contain {group}\")"},{"line_number":513,"context_line":"        self._group_prefix \u003d f\"{self._namespace}/groups/\""},{"line_number":514,"context_line":"        ttl_option \u003d options.get(\"ttl\", options.get(\"timeout\", [_DEFAULT_TTL]))"},{"line_number":515,"context_line":"        self._ttl \u003d int(ttl_option[0])"},{"line_number":516,"context_line":"        if not 1 \u003c\u003d self._ttl \u003c\u003d _CONSUL_SESSION_TTL_MAX:"}],"source_content_type":"text/x-python","patch_set":29,"id":"7c20f8cd_61330e13","line":513,"range":{"start_line":496,"start_character":70,"end_line":513,"end_character":57},"in_reply_to":"f4a720f4_8e6a16a0","updated":"2026-09-22 12:57:25.000000000","message":"Done","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"85509edaca412cb3f71bbe770c781831c31ac15b","unresolved":true,"context_lines":[{"line_number":589,"context_line":"        self._acquired_locks: set[ConsulLock] \u003d set()"},{"line_number":590,"context_line":""},{"line_number":591,"context_line":"    @property"},{"line_number":592,"context_line":"    def _client(self) -\u003e Any:"},{"line_number":593,"context_line":"        client \u003d getattr(self._client_local, \"client\", None)"},{"line_number":594,"context_line":"        if client is not None:"},{"line_number":595,"context_line":"            return client"}],"source_content_type":"text/x-python","patch_set":29,"id":"d4defa5b_464c3063","line":592,"updated":"2026-09-18 16:56:26.000000000","message":"Do we know the actual type of this? We can make liberal use of `type: ignore` in the tests if needed (for a fake client)","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"e911de24b78bc16d043673b84f373aad410489f2","unresolved":false,"context_lines":[{"line_number":589,"context_line":"        self._acquired_locks: set[ConsulLock] \u003d set()"},{"line_number":590,"context_line":""},{"line_number":591,"context_line":"    @property"},{"line_number":592,"context_line":"    def _client(self) -\u003e Any:"},{"line_number":593,"context_line":"        client \u003d getattr(self._client_local, \"client\", None)"},{"line_number":594,"context_line":"        if client is not None:"},{"line_number":595,"context_line":"            return client"}],"source_content_type":"text/x-python","patch_set":29,"id":"18a6ac60_f5ed993c","line":592,"in_reply_to":"d4defa5b_464c3063","updated":"2026-09-22 12:57:25.000000000","message":"Done","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"1490fb46d986504554736e255ee39bd7db09979d","unresolved":true,"context_lines":[{"line_number":589,"context_line":"        self._acquired_locks: set[ConsulLock] \u003d set()"},{"line_number":590,"context_line":""},{"line_number":591,"context_line":"    @property"},{"line_number":592,"context_line":"    def _client(self) -\u003e Any:"},{"line_number":593,"context_line":"        client \u003d getattr(self._client_local, \"client\", None)"},{"line_number":594,"context_line":"        if client is not None:"},{"line_number":595,"context_line":"            return client"}],"source_content_type":"text/x-python","patch_set":29,"id":"0a31c347_4abd21f0","line":592,"in_reply_to":"d4defa5b_464c3063","updated":"2026-09-22 12:17:22.000000000","message":"Typed as consul.Consul | None; None remains the unstarted/stopped state.","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"85509edaca412cb3f71bbe770c781831c31ac15b","unresolved":true,"context_lines":[{"line_number":620,"context_line":"        session.mount(\"http://\", http_adapter)"},{"line_number":621,"context_line":"        session.mount(\"https://\", https_adapter)"},{"line_number":622,"context_line":"        client.http.session \u003d session"},{"line_number":623,"context_line":"        return client"},{"line_number":624,"context_line":""},{"line_number":625,"context_line":"    def _get_client(self) -\u003e consul.Consul:"},{"line_number":626,"context_line":"        return cast(consul.Consul, self._client)"},{"line_number":627,"context_line":""},{"line_number":628,"context_line":"    def _close_clients(self) -\u003e None:"},{"line_number":629,"context_line":"        adapters \u003d self._client_adapters"}],"source_content_type":"text/x-python","patch_set":29,"id":"d4beb159_4bdcaaec","line":626,"range":{"start_line":623,"start_character":21,"end_line":626,"end_character":48},"updated":"2026-09-18 16:56:26.000000000","message":"Why do we need this rather than using the `_client` property?","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"e911de24b78bc16d043673b84f373aad410489f2","unresolved":false,"context_lines":[{"line_number":620,"context_line":"        session.mount(\"http://\", http_adapter)"},{"line_number":621,"context_line":"        session.mount(\"https://\", https_adapter)"},{"line_number":622,"context_line":"        client.http.session \u003d session"},{"line_number":623,"context_line":"        return client"},{"line_number":624,"context_line":""},{"line_number":625,"context_line":"    def _get_client(self) -\u003e consul.Consul:"},{"line_number":626,"context_line":"        return cast(consul.Consul, self._client)"},{"line_number":627,"context_line":""},{"line_number":628,"context_line":"    def _close_clients(self) -\u003e None:"},{"line_number":629,"context_line":"        adapters \u003d self._client_adapters"}],"source_content_type":"text/x-python","patch_set":29,"id":"39ffb6d9_9a7ec011","line":626,"range":{"start_line":623,"start_character":21,"end_line":626,"end_character":48},"in_reply_to":"d4beb159_4bdcaaec","updated":"2026-09-22 12:57:25.000000000","message":"Done","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"1490fb46d986504554736e255ee39bd7db09979d","unresolved":true,"context_lines":[{"line_number":620,"context_line":"        session.mount(\"http://\", http_adapter)"},{"line_number":621,"context_line":"        session.mount(\"https://\", https_adapter)"},{"line_number":622,"context_line":"        client.http.session \u003d session"},{"line_number":623,"context_line":"        return client"},{"line_number":624,"context_line":""},{"line_number":625,"context_line":"    def _get_client(self) -\u003e consul.Consul:"},{"line_number":626,"context_line":"        return cast(consul.Consul, self._client)"},{"line_number":627,"context_line":""},{"line_number":628,"context_line":"    def _close_clients(self) -\u003e None:"},{"line_number":629,"context_line":"        adapters \u003d self._client_adapters"}],"source_content_type":"text/x-python","patch_set":29,"id":"cb4c6ab4_86fd43f6","line":626,"range":{"start_line":623,"start_character":21,"end_line":626,"end_character":48},"in_reply_to":"d4beb159_4bdcaaec","updated":"2026-09-22 12:17:22.000000000","message":"The bound method defers lookup until the lock is used, selecting that thread\u0027s client. Passing self._client would capture the creator\u0027s session. Added a comment.","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"85509edaca412cb3f71bbe770c781831c31ac15b","unresolved":true,"context_lines":[{"line_number":922,"context_line":"        raise tooz.NotImplemented(\"leader election not supported\")"},{"line_number":923,"context_line":""},{"line_number":924,"context_line":"    def unwatch_elected_as_leader("},{"line_number":925,"context_line":"        self, group_id: bytes, callback: Any"},{"line_number":926,"context_line":"    ) -\u003e None:"},{"line_number":927,"context_line":"        raise tooz.NotImplemented(\"leader election not supported\")"},{"line_number":928,"context_line":""}],"source_content_type":"text/x-python","patch_set":29,"id":"6190260d_fc8094ab","line":925,"updated":"2026-09-18 16:56:26.000000000","message":"Can we specify the type of `callback` here, even if we\u0027re not using it (it\u0027s a copy-paste job). Ditto for above.","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"1490fb46d986504554736e255ee39bd7db09979d","unresolved":true,"context_lines":[{"line_number":922,"context_line":"        raise tooz.NotImplemented(\"leader election not supported\")"},{"line_number":923,"context_line":""},{"line_number":924,"context_line":"    def unwatch_elected_as_leader("},{"line_number":925,"context_line":"        self, group_id: bytes, callback: Any"},{"line_number":926,"context_line":"    ) -\u003e None:"},{"line_number":927,"context_line":"        raise tooz.NotImplemented(\"leader election not supported\")"},{"line_number":928,"context_line":""}],"source_content_type":"text/x-python","patch_set":29,"id":"5f9fc3dd_5fe3f54a","line":925,"in_reply_to":"6190260d_fc8094ab","updated":"2026-09-22 12:17:22.000000000","message":"Both now use EventCallback[LeaderElected], matching the base class.","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"e911de24b78bc16d043673b84f373aad410489f2","unresolved":false,"context_lines":[{"line_number":922,"context_line":"        raise tooz.NotImplemented(\"leader election not supported\")"},{"line_number":923,"context_line":""},{"line_number":924,"context_line":"    def unwatch_elected_as_leader("},{"line_number":925,"context_line":"        self, group_id: bytes, callback: Any"},{"line_number":926,"context_line":"    ) -\u003e None:"},{"line_number":927,"context_line":"        raise tooz.NotImplemented(\"leader election not supported\")"},{"line_number":928,"context_line":""}],"source_content_type":"text/x-python","patch_set":29,"id":"b0574880_996997b1","line":925,"in_reply_to":"6190260d_fc8094ab","updated":"2026-09-22 12:57:25.000000000","message":"Done","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"85509edaca412cb3f71bbe770c781831c31ac15b","unresolved":true,"context_lines":[{"line_number":962,"context_line":"        return f\"service:{service_id}:{self._owner_token}\""},{"line_number":963,"context_line":""},{"line_number":964,"context_line":"    @staticmethod"},{"line_number":965,"context_line":"    def _capabilities_to_dict(capabilities: Any) -\u003e dict[str, Any]:"},{"line_number":966,"context_line":"        \"\"\"Return *capabilities* as a dict, or ``{}`` if it isn\u0027t one.\"\"\""},{"line_number":967,"context_line":"        if isinstance(capabilities, dict):"},{"line_number":968,"context_line":"            return capabilities"}],"source_content_type":"text/x-python","patch_set":29,"id":"fcd0b0bc_5cf17776","line":965,"updated":"2026-09-18 16:56:26.000000000","message":"This method shouldn\u0027t be necessary. tooz itself will return a dict","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"e911de24b78bc16d043673b84f373aad410489f2","unresolved":false,"context_lines":[{"line_number":962,"context_line":"        return f\"service:{service_id}:{self._owner_token}\""},{"line_number":963,"context_line":""},{"line_number":964,"context_line":"    @staticmethod"},{"line_number":965,"context_line":"    def _capabilities_to_dict(capabilities: Any) -\u003e dict[str, Any]:"},{"line_number":966,"context_line":"        \"\"\"Return *capabilities* as a dict, or ``{}`` if it isn\u0027t one.\"\"\""},{"line_number":967,"context_line":"        if isinstance(capabilities, dict):"},{"line_number":968,"context_line":"            return capabilities"}],"source_content_type":"text/x-python","patch_set":29,"id":"f43eb699_51e7b4a5","line":965,"in_reply_to":"fcd0b0bc_5cf17776","updated":"2026-09-22 12:57:25.000000000","message":"Done","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"1490fb46d986504554736e255ee39bd7db09979d","unresolved":true,"context_lines":[{"line_number":962,"context_line":"        return f\"service:{service_id}:{self._owner_token}\""},{"line_number":963,"context_line":""},{"line_number":964,"context_line":"    @staticmethod"},{"line_number":965,"context_line":"    def _capabilities_to_dict(capabilities: Any) -\u003e dict[str, Any]:"},{"line_number":966,"context_line":"        \"\"\"Return *capabilities* as a dict, or ``{}`` if it isn\u0027t one.\"\"\""},{"line_number":967,"context_line":"        if isinstance(capabilities, dict):"},{"line_number":968,"context_line":"            return capabilities"}],"source_content_type":"text/x-python","patch_set":29,"id":"bd8a9a66_1262c451","line":965,"in_reply_to":"fcd0b0bc_5cf17776","updated":"2026-09-22 12:17:22.000000000","message":"Removed; helpers now accept Capabilities | None. Updated the byte-valued test fixtures to use dictionaries.","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"85509edaca412cb3f71bbe770c781831c31ac15b","unresolved":true,"context_lines":[{"line_number":969,"context_line":"        return {}"},{"line_number":970,"context_line":""},{"line_number":971,"context_line":"    @classmethod"},{"line_number":972,"context_line":"    def _stable_service_identity(cls, capabilities: Any) -\u003e bytes | None:"},{"line_number":973,"context_line":"        identity \u003d cls._capabilities_to_dict(capabilities).get(\"service_id\")"},{"line_number":974,"context_line":"        if isinstance(identity, str):"},{"line_number":975,"context_line":"            identity \u003d identity.encode()"}],"source_content_type":"text/x-python","patch_set":29,"id":"d6b4be6d_2ff05768","line":972,"updated":"2026-09-18 16:56:26.000000000","message":"```suggestion\n    def _stable_service_identity(\n        cls, capabilities: coordination.Capabilities | None\n    ) -\u003e bytes | None:\n```","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"e911de24b78bc16d043673b84f373aad410489f2","unresolved":false,"context_lines":[{"line_number":969,"context_line":"        return {}"},{"line_number":970,"context_line":""},{"line_number":971,"context_line":"    @classmethod"},{"line_number":972,"context_line":"    def _stable_service_identity(cls, capabilities: Any) -\u003e bytes | None:"},{"line_number":973,"context_line":"        identity \u003d cls._capabilities_to_dict(capabilities).get(\"service_id\")"},{"line_number":974,"context_line":"        if isinstance(identity, str):"},{"line_number":975,"context_line":"            identity \u003d identity.encode()"}],"source_content_type":"text/x-python","patch_set":29,"id":"bb3e9bc3_024e7a9e","line":972,"in_reply_to":"d6b4be6d_2ff05768","updated":"2026-09-22 12:57:25.000000000","message":"Done","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"1490fb46d986504554736e255ee39bd7db09979d","unresolved":true,"context_lines":[{"line_number":969,"context_line":"        return {}"},{"line_number":970,"context_line":""},{"line_number":971,"context_line":"    @classmethod"},{"line_number":972,"context_line":"    def _stable_service_identity(cls, capabilities: Any) -\u003e bytes | None:"},{"line_number":973,"context_line":"        identity \u003d cls._capabilities_to_dict(capabilities).get(\"service_id\")"},{"line_number":974,"context_line":"        if isinstance(identity, str):"},{"line_number":975,"context_line":"            identity \u003d identity.encode()"}],"source_content_type":"text/x-python","patch_set":29,"id":"68373bbd_02cdba2d","line":972,"in_reply_to":"d6b4be6d_2ff05768","updated":"2026-09-22 12:17:22.000000000","message":"Updated.","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"85509edaca412cb3f71bbe770c781831c31ac15b","unresolved":true,"context_lines":[{"line_number":970,"context_line":""},{"line_number":971,"context_line":"    @classmethod"},{"line_number":972,"context_line":"    def _stable_service_identity(cls, capabilities: Any) -\u003e bytes | None:"},{"line_number":973,"context_line":"        identity \u003d cls._capabilities_to_dict(capabilities).get(\"service_id\")"},{"line_number":974,"context_line":"        if isinstance(identity, str):"},{"line_number":975,"context_line":"            identity \u003d identity.encode()"},{"line_number":976,"context_line":"        if isinstance(identity, bytes) and identity:"}],"source_content_type":"text/x-python","patch_set":29,"id":"37f78511_cc4cc990","line":973,"updated":"2026-09-18 16:56:26.000000000","message":"```suggestion\n        identity \u003d (capabilities or {}).get(\"service_id\")\n```","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"e911de24b78bc16d043673b84f373aad410489f2","unresolved":false,"context_lines":[{"line_number":970,"context_line":""},{"line_number":971,"context_line":"    @classmethod"},{"line_number":972,"context_line":"    def _stable_service_identity(cls, capabilities: Any) -\u003e bytes | None:"},{"line_number":973,"context_line":"        identity \u003d cls._capabilities_to_dict(capabilities).get(\"service_id\")"},{"line_number":974,"context_line":"        if isinstance(identity, str):"},{"line_number":975,"context_line":"            identity \u003d identity.encode()"},{"line_number":976,"context_line":"        if isinstance(identity, bytes) and identity:"}],"source_content_type":"text/x-python","patch_set":29,"id":"23c099e1_d86e37d1","line":973,"in_reply_to":"37f78511_cc4cc990","updated":"2026-09-22 12:57:25.000000000","message":"Done","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"1490fb46d986504554736e255ee39bd7db09979d","unresolved":true,"context_lines":[{"line_number":970,"context_line":""},{"line_number":971,"context_line":"    @classmethod"},{"line_number":972,"context_line":"    def _stable_service_identity(cls, capabilities: Any) -\u003e bytes | None:"},{"line_number":973,"context_line":"        identity \u003d cls._capabilities_to_dict(capabilities).get(\"service_id\")"},{"line_number":974,"context_line":"        if isinstance(identity, str):"},{"line_number":975,"context_line":"            identity \u003d identity.encode()"},{"line_number":976,"context_line":"        if isinstance(identity, bytes) and identity:"}],"source_content_type":"text/x-python","patch_set":29,"id":"7042faec_2bdcf124","line":973,"in_reply_to":"37f78511_cc4cc990","updated":"2026-09-22 12:17:22.000000000","message":"Done.","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"85509edaca412cb3f71bbe770c781831c31ac15b","unresolved":true,"context_lines":[{"line_number":971,"context_line":"    @classmethod"},{"line_number":972,"context_line":"    def _stable_service_identity(cls, capabilities: Any) -\u003e bytes | None:"},{"line_number":973,"context_line":"        identity \u003d cls._capabilities_to_dict(capabilities).get(\"service_id\")"},{"line_number":974,"context_line":"        if isinstance(identity, str):"},{"line_number":975,"context_line":"            identity \u003d identity.encode()"},{"line_number":976,"context_line":"        if isinstance(identity, bytes) and identity:"},{"line_number":977,"context_line":"            return identity"},{"line_number":978,"context_line":"        return None"},{"line_number":979,"context_line":""},{"line_number":980,"context_line":"    def _build_service_meta(self, capabilities: Any) -\u003e dict[str, str]:"}],"source_content_type":"text/x-python","patch_set":29,"id":"3df5a022_e1a84743","line":977,"range":{"start_line":974,"start_character":0,"end_line":977,"end_character":27},"updated":"2026-09-18 16:56:26.000000000","message":"Is there a reason we can\u0027t be more sure about the types we are retrieving here? This is a new driver: we control what we\u0027re storing, no?","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"1490fb46d986504554736e255ee39bd7db09979d","unresolved":true,"context_lines":[{"line_number":971,"context_line":"    @classmethod"},{"line_number":972,"context_line":"    def _stable_service_identity(cls, capabilities: Any) -\u003e bytes | None:"},{"line_number":973,"context_line":"        identity \u003d cls._capabilities_to_dict(capabilities).get(\"service_id\")"},{"line_number":974,"context_line":"        if isinstance(identity, str):"},{"line_number":975,"context_line":"            identity \u003d identity.encode()"},{"line_number":976,"context_line":"        if isinstance(identity, bytes) and identity:"},{"line_number":977,"context_line":"            return identity"},{"line_number":978,"context_line":"        return None"},{"line_number":979,"context_line":""},{"line_number":980,"context_line":"    def _build_service_meta(self, capabilities: Any) -\u003e dict[str, str]:"}],"source_content_type":"text/x-python","patch_set":29,"id":"eec4592b_2e27cf50","line":977,"range":{"start_line":974,"start_character":0,"end_line":977,"end_character":27},"in_reply_to":"3df5a022_e1a84743","updated":"2026-09-22 12:17:22.000000000","message":"Capabilities is dict[str, Any], so values come from applications. Kept the nonempty str/bytes check and documented it.","commit_id":"6e9d3c8c9ea27d423762fb61f21ea6816321d4f8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"e911de24b78bc16d043673b84f373aad410489f2","unresolved":true,"context_lines":[{"line_number":74,"context_line":"from tooz import utils"},{"line_number":75,"context_line":""},{"line_number":76,"context_line":"LOG \u003d logging.getLogger(__name__)"},{"line_number":77,"context_line":""},{"line_number":78,"context_line":"P \u003d ParamSpec(\u0027P\u0027)"},{"line_number":79,"context_line":""},{"line_number":80,"context_line":""},{"line_number":81,"context_line":"def _get_healthcheck_url("},{"line_number":82,"context_line":"    capabilities: dict[str, Any] | None,"},{"line_number":83,"context_line":") -\u003e str | None:"},{"line_number":84,"context_line":"    \"\"\"Return a member\u0027s explicit endpoint health-check URL, if any."},{"line_number":85,"context_line":""},{"line_number":86,"context_line":"    A member opts in to Consul server-side health checking by including"},{"line_number":87,"context_line":"    a fully-formed ``healthcheck_url`` in its capabilities at join time."},{"line_number":88,"context_line":"    Only an explicit, non-empty string triggers the HTTP check; other"},{"line_number":89,"context_line":"    capability keys are never used to derive a probe URL."},{"line_number":90,"context_line":"    \"\"\""},{"line_number":91,"context_line":"    if not capabilities or \u0027healthcheck_url\u0027 not in capabilities:"},{"line_number":92,"context_line":"        return None"},{"line_number":93,"context_line":"    url \u003d capabilities[\u0027healthcheck_url\u0027]"},{"line_number":94,"context_line":"    if not isinstance(url, str):"},{"line_number":95,"context_line":"        LOG.warning("},{"line_number":96,"context_line":"            \"Invalid healthcheck_url capability was provided; falling back \""},{"line_number":97,"context_line":"            \"to TTL-based member liveness\""},{"line_number":98,"context_line":"        )"},{"line_number":99,"context_line":"        return None"},{"line_number":100,"context_line":"    return url"},{"line_number":101,"context_line":""},{"line_number":102,"context_line":""},{"line_number":103,"context_line":"R \u003d TypeVar(\u0027R\u0027)"},{"line_number":104,"context_line":""},{"line_number":105,"context_line":""},{"line_number":106,"context_line":"_IDENTIFIER_ESCAPE_PREFIX \u003d \"b64~\""},{"line_number":107,"context_line":""},{"line_number":108,"context_line":""},{"line_number":109,"context_line":"def _encode_identifier(value: bytes) -\u003e str:"},{"line_number":110,"context_line":"    \"\"\"Return a reversible Consul-safe text form for a tooz identifier.\"\"\""}],"source_content_type":"text/x-python","patch_set":32,"id":"c7c6722a_7bc95963","line":107,"range":{"start_line":77,"start_character":1,"end_line":107,"end_character":1},"updated":"2026-09-22 12:57:25.000000000","message":"nit: let\u0027s keep constants and global variables up top\n\n```suggestion\n_IDENTIFIER_ESCAPE_PREFIX \u003d \"b64~\"\n\nP \u003d ParamSpec(\u0027P\u0027)\nR \u003d TypeVar(\u0027R\u0027)\n\n\ndef _get_healthcheck_url(\n    capabilities: dict[str, Any] | None,\n) -\u003e str | None:\n    \"\"\"Return a member\u0027s explicit endpoint health-check URL, if any.\n\n    A member opts in to Consul server-side health checking by including\n    a fully-formed ``healthcheck_url`` in its capabilities at join time.\n    Only an explicit, non-empty string triggers the HTTP check; other\n    capability keys are never used to derive a probe URL.\n    \"\"\"\n    if not capabilities or \u0027healthcheck_url\u0027 not in capabilities:\n        return None\n    url \u003d capabilities[\u0027healthcheck_url\u0027]\n    if not isinstance(url, str):\n        LOG.warning(\n            \"Invalid healthcheck_url capability was provided; falling back \"\n            \"to TTL-based member liveness\"\n        )\n        return None\n    return url\n\n```","commit_id":"e2a1b6874ef7fc8e4d61b4707c56ac23548fe5e8"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"677183f32e60545bb5868512211cf7ba0504d6b1","unresolved":true,"context_lines":[{"line_number":74,"context_line":"from tooz import utils"},{"line_number":75,"context_line":""},{"line_number":76,"context_line":"LOG \u003d logging.getLogger(__name__)"},{"line_number":77,"context_line":""},{"line_number":78,"context_line":"P \u003d ParamSpec(\u0027P\u0027)"},{"line_number":79,"context_line":""},{"line_number":80,"context_line":""},{"line_number":81,"context_line":"def _get_healthcheck_url("},{"line_number":82,"context_line":"    capabilities: dict[str, Any] | None,"},{"line_number":83,"context_line":") -\u003e str | None:"},{"line_number":84,"context_line":"    \"\"\"Return a member\u0027s explicit endpoint health-check URL, if any."},{"line_number":85,"context_line":""},{"line_number":86,"context_line":"    A member opts in to Consul server-side health checking by including"},{"line_number":87,"context_line":"    a fully-formed ``healthcheck_url`` in its capabilities at join time."},{"line_number":88,"context_line":"    Only an explicit, non-empty string triggers the HTTP check; other"},{"line_number":89,"context_line":"    capability keys are never used to derive a probe URL."},{"line_number":90,"context_line":"    \"\"\""},{"line_number":91,"context_line":"    if not capabilities or \u0027healthcheck_url\u0027 not in capabilities:"},{"line_number":92,"context_line":"        return None"},{"line_number":93,"context_line":"    url \u003d capabilities[\u0027healthcheck_url\u0027]"},{"line_number":94,"context_line":"    if not isinstance(url, str):"},{"line_number":95,"context_line":"        LOG.warning("},{"line_number":96,"context_line":"            \"Invalid healthcheck_url capability was provided; falling back \""},{"line_number":97,"context_line":"            \"to TTL-based member liveness\""},{"line_number":98,"context_line":"        )"},{"line_number":99,"context_line":"        return None"},{"line_number":100,"context_line":"    return url"},{"line_number":101,"context_line":""},{"line_number":102,"context_line":""},{"line_number":103,"context_line":"R \u003d TypeVar(\u0027R\u0027)"},{"line_number":104,"context_line":""},{"line_number":105,"context_line":""},{"line_number":106,"context_line":"_IDENTIFIER_ESCAPE_PREFIX \u003d \"b64~\""},{"line_number":107,"context_line":""},{"line_number":108,"context_line":""},{"line_number":109,"context_line":"def _encode_identifier(value: bytes) -\u003e str:"},{"line_number":110,"context_line":"    \"\"\"Return a reversible Consul-safe text form for a tooz identifier.\"\"\""}],"source_content_type":"text/x-python","patch_set":32,"id":"d95bca29_0cd0e537","line":107,"range":{"start_line":77,"start_character":1,"end_line":107,"end_character":1},"in_reply_to":"c7c6722a_7bc95963","updated":"2026-09-23 11:23:51.000000000","message":"Done","commit_id":"e2a1b6874ef7fc8e4d61b4707c56ac23548fe5e8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"e911de24b78bc16d043673b84f373aad410489f2","unresolved":true,"context_lines":[{"line_number":136,"context_line":"        # prefix but is not a value encoded by this driver."},{"line_number":137,"context_line":"        return value.encode(\"utf-8\")"},{"line_number":138,"context_line":""},{"line_number":139,"context_line":""},{"line_number":140,"context_line":"# How often a blocking lock acquire re-polls Consul while waiting."},{"line_number":141,"context_line":"_LOCK_POLL_INTERVAL \u003d 0.5"},{"line_number":142,"context_line":"# This is part of the group-deletion barrier: every participant must use the"},{"line_number":143,"context_line":"# same fixed shard set."},{"line_number":144,"context_line":"_GROUP_OPERATION_SHARDS \u003d 64"},{"line_number":145,"context_line":""},{"line_number":146,"context_line":"_CONSUL_SESSION_TTL_MIN \u003d 10"},{"line_number":147,"context_line":"_CONSUL_SESSION_TTL_MAX \u003d 86400"},{"line_number":148,"context_line":"_DEFAULT_TTL \u003d 15"},{"line_number":149,"context_line":"_DEFAULT_REQUEST_TIMEOUT \u003d 5.0"},{"line_number":150,"context_line":"_DEFAULT_HEALTH_CHECK_INTERVAL \u003d 60"},{"line_number":151,"context_line":"_DEFAULT_HEALTH_CHECK_TIMEOUT \u003d 10"},{"line_number":152,"context_line":"_DEFAULT_HEALTH_CHECK_DEREGISTER_AFTER \u003d 600"},{"line_number":153,"context_line":"_DATACENTER_CACHE_TTL \u003d 60"},{"line_number":154,"context_line":"_MALFORMED_WARNING_CACHE_SIZE \u003d 1024"},{"line_number":155,"context_line":"_MISSING_NODE_REGISTRATION \u003d \"Missing node registration\""},{"line_number":156,"context_line":""},{"line_number":157,"context_line":"# Consul service metadata values are limited to 512 characters and a service"},{"line_number":158,"context_line":"# can have at most 64 metadata pairs. Reserve the member ID and owner token."},{"line_number":159,"context_line":"_META_VALUE_MAX_LENGTH \u003d 512"},{"line_number":160,"context_line":"_CAPABILITIES_META_MAX_CHUNKS \u003d 62"},{"line_number":161,"context_line":""},{"line_number":162,"context_line":""},{"line_number":163,"context_line":"@dataclasses.dataclass(frozen\u003dTrue)"},{"line_number":164,"context_line":"class _ServiceRegistration:"}],"source_content_type":"text/x-python","patch_set":32,"id":"733e941f_6e89bc0f","line":161,"range":{"start_line":139,"start_character":1,"end_line":161,"end_character":1},"updated":"2026-09-22 12:57:25.000000000","message":"nit: Can you move these up top also?","commit_id":"e2a1b6874ef7fc8e4d61b4707c56ac23548fe5e8"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"677183f32e60545bb5868512211cf7ba0504d6b1","unresolved":true,"context_lines":[{"line_number":136,"context_line":"        # prefix but is not a value encoded by this driver."},{"line_number":137,"context_line":"        return value.encode(\"utf-8\")"},{"line_number":138,"context_line":""},{"line_number":139,"context_line":""},{"line_number":140,"context_line":"# How often a blocking lock acquire re-polls Consul while waiting."},{"line_number":141,"context_line":"_LOCK_POLL_INTERVAL \u003d 0.5"},{"line_number":142,"context_line":"# This is part of the group-deletion barrier: every participant must use the"},{"line_number":143,"context_line":"# same fixed shard set."},{"line_number":144,"context_line":"_GROUP_OPERATION_SHARDS \u003d 64"},{"line_number":145,"context_line":""},{"line_number":146,"context_line":"_CONSUL_SESSION_TTL_MIN \u003d 10"},{"line_number":147,"context_line":"_CONSUL_SESSION_TTL_MAX \u003d 86400"},{"line_number":148,"context_line":"_DEFAULT_TTL \u003d 15"},{"line_number":149,"context_line":"_DEFAULT_REQUEST_TIMEOUT \u003d 5.0"},{"line_number":150,"context_line":"_DEFAULT_HEALTH_CHECK_INTERVAL \u003d 60"},{"line_number":151,"context_line":"_DEFAULT_HEALTH_CHECK_TIMEOUT \u003d 10"},{"line_number":152,"context_line":"_DEFAULT_HEALTH_CHECK_DEREGISTER_AFTER \u003d 600"},{"line_number":153,"context_line":"_DATACENTER_CACHE_TTL \u003d 60"},{"line_number":154,"context_line":"_MALFORMED_WARNING_CACHE_SIZE \u003d 1024"},{"line_number":155,"context_line":"_MISSING_NODE_REGISTRATION \u003d \"Missing node registration\""},{"line_number":156,"context_line":""},{"line_number":157,"context_line":"# Consul service metadata values are limited to 512 characters and a service"},{"line_number":158,"context_line":"# can have at most 64 metadata pairs. Reserve the member ID and owner token."},{"line_number":159,"context_line":"_META_VALUE_MAX_LENGTH \u003d 512"},{"line_number":160,"context_line":"_CAPABILITIES_META_MAX_CHUNKS \u003d 62"},{"line_number":161,"context_line":""},{"line_number":162,"context_line":""},{"line_number":163,"context_line":"@dataclasses.dataclass(frozen\u003dTrue)"},{"line_number":164,"context_line":"class _ServiceRegistration:"}],"source_content_type":"text/x-python","patch_set":32,"id":"6c57ef92_455780b3","line":161,"range":{"start_line":139,"start_character":1,"end_line":161,"end_character":1},"in_reply_to":"733e941f_6e89bc0f","updated":"2026-09-23 11:23:51.000000000","message":"Done","commit_id":"e2a1b6874ef7fc8e4d61b4707c56ac23548fe5e8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"e911de24b78bc16d043673b84f373aad410489f2","unresolved":true,"context_lines":[{"line_number":596,"context_line":"        self._locks_lock \u003d threading.Lock()"},{"line_number":597,"context_line":"        self._acquired_locks: set[ConsulLock] \u003d set()"},{"line_number":598,"context_line":""},{"line_number":599,"context_line":"    @property"},{"line_number":600,"context_line":"    def _client(self) -\u003e consul.Consul | None:"},{"line_number":601,"context_line":"        client: consul.Consul | None \u003d getattr("},{"line_number":602,"context_line":"            self._client_local, \"client\", None"},{"line_number":603,"context_line":"        )"}],"source_content_type":"text/x-python","patch_set":32,"id":"2405f630_493b1391","line":600,"range":{"start_line":599,"start_character":13,"end_line":600,"end_character":46},"updated":"2026-09-22 12:57:25.000000000","message":"This will never return `None`, fwict?\n\n```suggestion\n    @property\n    def _client(self) -\u003e consul.Consul:\n```\n\nThis lets you remove the `cast` in `_get_client` below then.","commit_id":"e2a1b6874ef7fc8e4d61b4707c56ac23548fe5e8"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"677183f32e60545bb5868512211cf7ba0504d6b1","unresolved":true,"context_lines":[{"line_number":597,"context_line":"        self._acquired_locks: set[ConsulLock] \u003d set()"},{"line_number":598,"context_line":""},{"line_number":599,"context_line":"    @property"},{"line_number":600,"context_line":"    def _client(self) -\u003e consul.Consul | None:"},{"line_number":601,"context_line":"        client: consul.Consul | None \u003d getattr("},{"line_number":602,"context_line":"            self._client_local, \"client\", None"},{"line_number":603,"context_line":"        )"}],"source_content_type":"text/x-python","patch_set":32,"id":"26623292_1705ad1c","line":600,"in_reply_to":"2405f630_493b1391","updated":"2026-09-23 11:23:51.000000000","message":"Now returns consul.Consul; unstarted/failed-start access raises ToozConnectionError. Removed the cast and redundant assertions, with lifecycle regression tests in 1006887.","commit_id":"e2a1b6874ef7fc8e4d61b4707c56ac23548fe5e8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"e911de24b78bc16d043673b84f373aad410489f2","unresolved":true,"context_lines":[{"line_number":1556,"context_line":"        fetch_all: bool,"},{"line_number":1557,"context_line":"        decode: Callable[[list[dict[str, Any]]], dict[Any, Any]],"},{"line_number":1558,"context_line":"    ) -\u003e dict[Any, Any]:"},{"line_number":1559,"context_line":"        group_id \u003d utils.to_binary(group_id, encoding\u003d\"utf-8\")"},{"line_number":1560,"context_line":"        local_dc \u003d self._datacenter"},{"line_number":1561,"context_line":""},{"line_number":1562,"context_line":"        if fetch_all:"},{"line_number":1563,"context_line":"            datacenters \u003d self._get_datacenters()"},{"line_number":1564,"context_line":"            if local_dc:"},{"line_number":1565,"context_line":"                datacenters \u003d [local_dc] + ["},{"line_number":1566,"context_line":"                    dc for dc in datacenters if dc !\u003d local_dc"},{"line_number":1567,"context_line":"                ]"},{"line_number":1568,"context_line":"            result: dict[Any, Any] \u003d {}"},{"line_number":1569,"context_line":"            last_error: Exception | None \u003d None"},{"line_number":1570,"context_line":"            for datacenter in datacenters:"},{"line_number":1571,"context_line":"                try:"},{"line_number":1572,"context_line":"                    services \u003d self._query_services("},{"line_number":1573,"context_line":"                        group_id,"},{"line_number":1574,"context_line":"                        datacenter\u003ddatacenter,"},{"line_number":1575,"context_line":"                        server\u003dserver,"},{"line_number":1576,"context_line":"                    )"},{"line_number":1577,"context_line":"                    self._merge_members("},{"line_number":1578,"context_line":"                        result,"},{"line_number":1579,"context_line":"                        decode(services),"},{"line_number":1580,"context_line":"                        datacenter,"},{"line_number":1581,"context_line":"                    )"},{"line_number":1582,"context_line":"                except Exception as exc:"},{"line_number":1583,"context_line":"                    last_error \u003d exc"},{"line_number":1584,"context_line":"                    LOG.warning("},{"line_number":1585,"context_line":"                        \"Skipping Consul datacenter %s: %s\","},{"line_number":1586,"context_line":"                        datacenter,"},{"line_number":1587,"context_line":"                        exc,"},{"line_number":1588,"context_line":"                    )"},{"line_number":1589,"context_line":"            if not result and last_error is not None:"},{"line_number":1590,"context_line":"                raise last_error"},{"line_number":1591,"context_line":"            return result"},{"line_number":1592,"context_line":""},{"line_number":1593,"context_line":"        last_error \u003d None"},{"line_number":1594,"context_line":"        if local_dc:"},{"line_number":1595,"context_line":"            try:"},{"line_number":1596,"context_line":"                services \u003d self._query_services("},{"line_number":1597,"context_line":"                    group_id, datacenter\u003dlocal_dc, server\u003dserver"},{"line_number":1598,"context_line":"                )"},{"line_number":1599,"context_line":"                if services:"},{"line_number":1600,"context_line":"                    return decode(services)"},{"line_number":1601,"context_line":"            except Exception as exc:"},{"line_number":1602,"context_line":"                last_error \u003d exc"},{"line_number":1603,"context_line":"                LOG.warning("},{"line_number":1604,"context_line":"                    \"Failed to query local Consul datacenter %s: %s\","},{"line_number":1605,"context_line":"                    local_dc,"},{"line_number":1606,"context_line":"                    exc,"},{"line_number":1607,"context_line":"                )"},{"line_number":1608,"context_line":""},{"line_number":1609,"context_line":"        datacenters \u003d self._get_datacenters()"},{"line_number":1610,"context_line":"        other_datacenters \u003d [dc for dc in datacenters if dc !\u003d local_dc]"},{"line_number":1611,"context_line":"        random.shuffle(other_datacenters)"},{"line_number":1612,"context_line":"        for datacenter in other_datacenters:"},{"line_number":1613,"context_line":"            try:"},{"line_number":1614,"context_line":"                services \u003d self._query_services("},{"line_number":1615,"context_line":"                    group_id, datacenter\u003ddatacenter, server\u003dserver"},{"line_number":1616,"context_line":"                )"},{"line_number":1617,"context_line":"                if services:"},{"line_number":1618,"context_line":"                    return decode(services)"},{"line_number":1619,"context_line":"            except Exception as exc:"},{"line_number":1620,"context_line":"                last_error \u003d exc"},{"line_number":1621,"context_line":"                LOG.warning("},{"line_number":1622,"context_line":"                    \"Skipping Consul datacenter %s: %s\","},{"line_number":1623,"context_line":"                    datacenter,"},{"line_number":1624,"context_line":"                    exc,"},{"line_number":1625,"context_line":"                )"},{"line_number":1626,"context_line":"        if last_error is not None:"},{"line_number":1627,"context_line":"            raise last_error"},{"line_number":1628,"context_line":"        return {}"},{"line_number":1629,"context_line":""},{"line_number":1630,"context_line":"    def get_members("},{"line_number":1631,"context_line":"        self,"}],"source_content_type":"text/x-python","patch_set":32,"id":"dcb21eb9_d4e78a82","line":1628,"range":{"start_line":1559,"start_character":0,"end_line":1628,"end_character":17},"updated":"2026-09-22 12:57:25.000000000","message":"There\u0027s an awful lot of overlap between these two code paths (and it\u0027s implemented as a flag function currently). Is it possible move the common code further up the function and only apply the `fetch_all` logic (i.e. do or don\u0027t do the merge) at the end?","commit_id":"e2a1b6874ef7fc8e4d61b4707c56ac23548fe5e8"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"677183f32e60545bb5868512211cf7ba0504d6b1","unresolved":true,"context_lines":[{"line_number":1625,"context_line":"                )"},{"line_number":1626,"context_line":"        if last_error is not None:"},{"line_number":1627,"context_line":"            raise last_error"},{"line_number":1628,"context_line":"        return {}"},{"line_number":1629,"context_line":""},{"line_number":1630,"context_line":"    def get_members("},{"line_number":1631,"context_line":"        self,"}],"source_content_type":"text/x-python","patch_set":32,"id":"aba9d38f_fb667b64","line":1628,"in_reply_to":"dcb21eb9_d4e78a82","updated":"2026-09-23 11:23:51.000000000","message":"Unified the query, decode and error handling. A small iterator preserves local-first lookup and randomized remote fallback; only the result handling branches on fetch_all. Added regression coverage for early return, partial failures and duplicate-member precedence.","commit_id":"e2a1b6874ef7fc8e4d61b4707c56ac23548fe5e8"}],"tooz/tests/drivers/test_consul_driver.py":[{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"5a162918d38301ac4f54df2ab828e24f997bdaf6","unresolved":false,"context_lines":[{"line_number":16,"context_line":"from unittest import mock"},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"try:"},{"line_number":19,"context_line":"    import consul  # noqa: F401"},{"line_number":20,"context_line":""},{"line_number":21,"context_line":"    HAS_CONSUL \u003d True"},{"line_number":22,"context_line":"except ImportError:"}],"source_content_type":"text/x-python","patch_set":3,"id":"0fa1e436_bc8c5ecf","line":19,"updated":"2026-07-14 07:41:28.000000000","message":"pep8: error: Cannot find implementation or library stub for module named \"consul\"  [import-not-found]","commit_id":"b13961a15c8f25dab47f45926016577fdd42017c"},{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"5a162918d38301ac4f54df2ab828e24f997bdaf6","unresolved":false,"context_lines":[{"line_number":16,"context_line":"from unittest import mock"},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"try:"},{"line_number":19,"context_line":"    import consul  # noqa: F401"},{"line_number":20,"context_line":""},{"line_number":21,"context_line":"    HAS_CONSUL \u003d True"},{"line_number":22,"context_line":"except ImportError:"}],"source_content_type":"text/x-python","patch_set":3,"id":"0ead22f4_8b7eda64","line":19,"updated":"2026-07-14 07:41:28.000000000","message":"pep8: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports","commit_id":"b13961a15c8f25dab47f45926016577fdd42017c"}],"tox.ini":[{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"c5e6edeff3f04575cc015d1970a065a7a71ed9c3","unresolved":true,"context_lines":[{"line_number":9,"context_line":"  -r{toxinidir}/requirements.txt"},{"line_number":10,"context_line":"  -r{toxinidir}/test-requirements.txt"},{"line_number":11,"context_line":"  # TODO(rm_you): Remove once py-consul lands in global-requirements (997160)"},{"line_number":12,"context_line":"  consul: py-consul\u003e\u003d1.7.0"},{"line_number":13,"context_line":"setenv \u003d"},{"line_number":14,"context_line":"  TOOZ_TEST_URLS \u003d file:///tmp ipc://"},{"line_number":15,"context_line":"  zookeeper: TOOZ_TEST_DRIVERS \u003d zookeeper"}],"source_content_type":"text/x-properties","patch_set":19,"id":"0e6e67d8_6c006416","line":12,"updated":"2026-08-13 11:47:39.000000000","message":"Let\u0027s hold off merging this until that change lands. Can you add a `Depends-on` in the commit message?","commit_id":"67f62a415d8554234cadcd5b356cb135c1cdf4ad"},{"author":{"_account_id":10273,"name":"Adam Harwell","email":"flux.adam@gmail.com","username":"rm_you"},"change_message_id":"a1af0b01a5f359df8aaa500840ec0b8d7199bfa4","unresolved":true,"context_lines":[{"line_number":9,"context_line":"  -r{toxinidir}/requirements.txt"},{"line_number":10,"context_line":"  -r{toxinidir}/test-requirements.txt"},{"line_number":11,"context_line":"  # TODO(rm_you): Remove once py-consul lands in global-requirements (997160)"},{"line_number":12,"context_line":"  consul: py-consul\u003e\u003d1.7.0"},{"line_number":13,"context_line":"setenv \u003d"},{"line_number":14,"context_line":"  TOOZ_TEST_URLS \u003d file:///tmp ipc://"},{"line_number":15,"context_line":"  zookeeper: TOOZ_TEST_DRIVERS \u003d zookeeper"}],"source_content_type":"text/x-properties","patch_set":19,"id":"b6cd68eb_c7a20ef8","line":12,"in_reply_to":"0e6e67d8_6c006416","updated":"2026-08-13 20:58:34.000000000","message":"Sorry I had a change pending already to remove this -- it has already merged. Done.","commit_id":"67f62a415d8554234cadcd5b356cb135c1cdf4ad"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"e911de24b78bc16d043673b84f373aad410489f2","unresolved":false,"context_lines":[{"line_number":9,"context_line":"  -r{toxinidir}/requirements.txt"},{"line_number":10,"context_line":"  -r{toxinidir}/test-requirements.txt"},{"line_number":11,"context_line":"  # TODO(rm_you): Remove once py-consul lands in global-requirements (997160)"},{"line_number":12,"context_line":"  consul: py-consul\u003e\u003d1.7.0"},{"line_number":13,"context_line":"setenv \u003d"},{"line_number":14,"context_line":"  TOOZ_TEST_URLS \u003d file:///tmp ipc://"},{"line_number":15,"context_line":"  zookeeper: TOOZ_TEST_DRIVERS \u003d zookeeper"}],"source_content_type":"text/x-properties","patch_set":19,"id":"e166c8cb_dd618d5d","line":12,"in_reply_to":"b6cd68eb_c7a20ef8","updated":"2026-09-22 12:57:25.000000000","message":"Done","commit_id":"67f62a415d8554234cadcd5b356cb135c1cdf4ad"}]}
