)]}'
{"/PATCHSET_LEVEL":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"54e259288a3bc48e8e339427220d8bb6aa2badc7","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":1,"id":"3f07daa3_e457bc0a","updated":"2023-08-16 12:21:57.000000000","message":"Makes sense","commit_id":"7ee91ba1bff33d89dab4b87e907d0fd30ce50bfe"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"8891ea78eb094f3648a3dbd51c695d63aba4f2e3","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":1,"id":"00c61d07_e23d2830","updated":"2023-08-16 12:23:09.000000000","message":"might be worth citing https://review.opendev.org/c/openstack/swift/+/193303 as a related change","commit_id":"7ee91ba1bff33d89dab4b87e907d0fd30ce50bfe"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"39c02895811b1a43ace030154f500ab90fc5854a","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":1,"id":"54f935c8_bac8f603","updated":"2023-08-16 23:54:58.000000000","message":"thanks for the feedback!  Good call on the RelatedChange, although reading it highlights a couple of things:\n\n1) everywhere else we just *close*, drain means reading (a poissbly large?) body - using drain_and_close in swob effects every HEAD response we currently return in prod - that probably *should* give us some pause.\n2) the original didn\u0027t close the HEAD app_iter even tho it was *right* there; I think it was probably justified as \"unnessesary\" rather than wrong.  I think the reasoning why you should *close* app_iters is quite obvious, not controversal to me at all - even if unnessaryily defensive for body-less HEADs in reality.","commit_id":"7ee91ba1bff33d89dab4b87e907d0fd30ce50bfe"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"189b1c0462f7771bedfd95a8c0f559c736feae51","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":1,"id":"ecb55795_ec5d2ab4","in_reply_to":"54f935c8_bac8f603","updated":"2023-08-17 04:39:47.000000000","message":"FWIW, the draining came much later, in https://review.opendev.org/c/openstack/swift/+/700959","commit_id":"7ee91ba1bff33d89dab4b87e907d0fd30ce50bfe"}],"swift/common/swob.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"54e259288a3bc48e8e339427220d8bb6aa2badc7","unresolved":true,"context_lines":[{"line_number":1400,"context_line":""},{"line_number":1401,"context_line":"        if self.request and self.request.method \u003d\u003d \u0027HEAD\u0027:"},{"line_number":1402,"context_line":"            # We explicitly do NOT want to set self.content_length to 0 here"},{"line_number":1403,"context_line":"            drain_and_close(app_iter)  # be friendly to our app_iter"},{"line_number":1404,"context_line":"            return [b\u0027\u0027]"},{"line_number":1405,"context_line":""},{"line_number":1406,"context_line":"        if self.conditional_response and self.request and \\"}],"source_content_type":"text/x-python","patch_set":1,"id":"84ef34bf_0f32d2ea","line":1403,"range":{"start_line":1403,"start_character":44,"end_line":1403,"end_character":52},"updated":"2023-08-16 12:21:57.000000000","message":"actually, perhaps more like do what WSGI requires\n\nsee Sam\u0027s commit message here https://review.opendev.org/c/openstack/swift/+/193303\n\nmight be worth citing that as a \n`RelatedChange: I168e147aae7c1728e7e3fdabb7fba6f2d747d937`\n\nbut curious that none of the other cases bother to drain the app_iter - and ironically in this case there *should* be nothing to drain.","commit_id":"7ee91ba1bff33d89dab4b87e907d0fd30ce50bfe"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"189b1c0462f7771bedfd95a8c0f559c736feae51","unresolved":false,"context_lines":[{"line_number":1400,"context_line":""},{"line_number":1401,"context_line":"        if self.request and self.request.method \u003d\u003d \u0027HEAD\u0027:"},{"line_number":1402,"context_line":"            # We explicitly do NOT want to set self.content_length to 0 here"},{"line_number":1403,"context_line":"            drain_and_close(app_iter)  # be friendly to our app_iter"},{"line_number":1404,"context_line":"            return [b\u0027\u0027]"},{"line_number":1405,"context_line":""},{"line_number":1406,"context_line":"        if self.conditional_response and self.request and \\"}],"source_content_type":"text/x-python","patch_set":1,"id":"c2af398b_3e3db4d2","line":1403,"range":{"start_line":1403,"start_character":44,"end_line":1403,"end_character":52},"in_reply_to":"51d0e5a9_05c52cd7","updated":"2023-08-17 04:39:47.000000000","message":"I was expecting a body, too -- especially since we\u0027ll send one when we\u0027re mis-using 412:\n```\n$ curl http://saio/asdf -v\n*   Trying 127.0.2.1:80...\n* Connected to saio (127.0.2.1) port 80 (#0)\n\u003e GET /asdf HTTP/1.1\n\u003e Host: saio\n\u003e User-Agent: curl/7.81.0\n\u003e Accept: */*\n\u003e \n* Mark bundle as not supporting multiuse\n\u003c HTTP/1.1 412 Precondition Failed\n\u003c Content-Type: text/html; charset\u003dUTF-8\n\u003c Content-Length: 7\n\u003c X-Trans-Id: txc2a4e0df638c4db097627-0064dda3ef\n\u003c X-Openstack-Request-Id: txc2a4e0df638c4db097627-0064dda3ef\n\u003c Date: Thu, 17 Aug 2023 04:37:03 GMT\n\u003c \n* Connection #0 to host saio left intact\nBad URL\n```\nMore I dig into it, `swob.Response`s are kinda weird...\n```\n\u003e\u003e\u003e from swift.common.swob import *\n\u003e\u003e\u003e HTTPPreconditionFailed().body\nb\u0027\u0027\n\u003e\u003e\u003e HTTPPreconditionFailed()(Request.blank(\u0027/\u0027).environ, lambda *a: None)\n[b\u0027\u003chtml\u003e\u003ch1\u003ePrecondition Failed\u003c/h1\u003e\u003cp\u003eA precondition for this request was not met.\u003c/p\u003e\u003c/html\u003e\u0027]\n```\nI was really expecting `_resp_body_property` to call `self._response_iter` at some point...","commit_id":"7ee91ba1bff33d89dab4b87e907d0fd30ce50bfe"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"39c02895811b1a43ace030154f500ab90fc5854a","unresolved":false,"context_lines":[{"line_number":1400,"context_line":""},{"line_number":1401,"context_line":"        if self.request and self.request.method \u003d\u003d \u0027HEAD\u0027:"},{"line_number":1402,"context_line":"            # We explicitly do NOT want to set self.content_length to 0 here"},{"line_number":1403,"context_line":"            drain_and_close(app_iter)  # be friendly to our app_iter"},{"line_number":1404,"context_line":"            return [b\u0027\u0027]"},{"line_number":1405,"context_line":""},{"line_number":1406,"context_line":"        if self.conditional_response and self.request and \\"}],"source_content_type":"text/x-python","patch_set":1,"id":"51d0e5a9_05c52cd7","line":1403,"range":{"start_line":1403,"start_character":44,"end_line":1403,"end_character":52},"in_reply_to":"84ef34bf_0f32d2ea","updated":"2023-08-16 23:54:58.000000000","message":"friendly is perhaps too flippant; but it IS a HEAD.  I think I\u0027d kind of learned over time to absorb the idea that \"you have to close iters if you don\u0027t return them\" so it didn\u0027t seem that serious or strange - the more curious bit to me was mostly \"why hasn\u0027t this ever been a problem for real!?\" so I assumed the answer was something like \"because it\u0027s a HEAD and it doesn\u0027t matter\" and this diff was ultimately mostly just to make tests happy.\n\nI like the idea of adding the RelatedChange!  I hadn\u0027t actually refreshed my mind on that diff when i was debugging/drafting this fix.  Useful context!\n\n\u003e curious that none of the other cases bother to drain the app_iter\n\ncurious indeed!  I\u0027m not actually sure I understand that `if self.conditional_response` branch.  I would have expect a 412 to have a body like a 404?\n\n\tvagrant@saio:~$ curl http://saio:8090/v1/AUTH_test/test/test -H \u0027if-match: nope\u0027 -v\n\t*   Trying 127.0.2.1:8090...\n\t* Connected to saio (127.0.2.1) port 8090 (#0)\n\t\u003e GET /v1/AUTH_test/test/test HTTP/1.1\n\t\u003e Host: saio:8090\n\t\u003e User-Agent: curl/7.81.0\n\t\u003e Accept: */*\n\t\u003e if-match: nope\n\t\u003e \n\t* Mark bundle as not supporting multiuse\n\t\u003c HTTP/1.1 412 Precondition Failed\n\t\u003c Content-Type: text/html; charset\u003dUTF-8\n\t\u003c Content-Length: 0\n\t\u003c X-Object-Meta-Mtime: 1690239416.175565\n\t\u003c Etag: 70c1db56f301c9e337b0099bd4174b28\n\t\u003c Last-Modified: Wed, 16 Aug 2023 23:32:21 GMT\n\t\u003c X-Timestamp: 1692228740.44523\n\t\u003c X-Trans-Id: tx7862190974a84def81160-0064dd5ca9\n\t\u003c X-Openstack-Request-Id: tx7862190974a84def81160-0064dd5ca9\n\t\u003c Date: Wed, 16 Aug 2023 23:32:57 GMT\n\t\u003c \n\t* Connection #0 to host saio left intact\n\tvagrant@saio:~$ \n\nI wonder if I continue to work on https://review.opendev.org/c/openstack/swift/+/891507/3 if I might find another existing test that gets a 412 and fails in tearDown with an unread LeakTrackingIter.","commit_id":"7ee91ba1bff33d89dab4b87e907d0fd30ce50bfe"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"ae557fe9a268f902623c0d1f6bdcdcff6d69175b","unresolved":false,"context_lines":[{"line_number":1400,"context_line":""},{"line_number":1401,"context_line":"        if self.request and self.request.method \u003d\u003d \u0027HEAD\u0027:"},{"line_number":1402,"context_line":"            # We explicitly do NOT want to set self.content_length to 0 here"},{"line_number":1403,"context_line":"            drain_and_close(app_iter)  # be friendly to our app_iter"},{"line_number":1404,"context_line":"            return [b\u0027\u0027]"},{"line_number":1405,"context_line":""},{"line_number":1406,"context_line":"        if self.conditional_response and self.request and \\"}],"source_content_type":"text/x-python","patch_set":1,"id":"93857a20_f8a74f7c","line":1403,"range":{"start_line":1403,"start_character":44,"end_line":1403,"end_character":52},"in_reply_to":"c2af398b_3e3db4d2","updated":"2023-08-17 12:08:36.000000000","message":"ok, this is off topic, but TIL that the swob.Response.conditional_response boolean is a means to delegate the checking of If-* conditions to the Response object https://docs.pylonsproject.org/projects/webob/en/stable/reference.html#conditional-wsgi-application . It\u0027s not an indication that a response is a 412 or w/e:\n\n```\n\u003e\u003e\u003e swob.HTTPPreconditionFailed().conditional_response\nFalse\n```\n\nSo I understand the _response_iter conditional_response handling not as \"412\u0027s shouldn\u0027t have bodies in general\" but \"swob.Response generated 412\u0027s don\u0027t have bodies\".","commit_id":"7ee91ba1bff33d89dab4b87e907d0fd30ce50bfe"}],"test/unit/common/middleware/test_object_versioning.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"54e259288a3bc48e8e339427220d8bb6aa2badc7","unresolved":true,"context_lines":[{"line_number":553,"context_line":"            (\u0027X-Symlink-Target\u0027, \u0027c/o?version-id\u003d0000001234.00000\u0027),"},{"line_number":554,"context_line":"            headers)"},{"line_number":555,"context_line":"        self.assertEqual(body, b\u0027\u0027)"},{"line_number":556,"context_line":"        # N.B. HEAD req already works with existing registered GET response"},{"line_number":557,"context_line":"        req \u003d Request.blank("},{"line_number":558,"context_line":"            \u0027/v1/a/c/o?symlink\u003dget\u0027, method\u003d\u0027HEAD\u0027,"},{"line_number":559,"context_line":"            environ\u003d{\u0027swift.cache\u0027: self.cache_version_on})"}],"source_content_type":"text/x-python","patch_set":1,"id":"166f4773_dceb4922","line":556,"updated":"2023-08-16 12:21:57.000000000","message":"ok, but why not break this out as test_head_symlink - it seems to also work with a registered HEAD?\n\n```\ndiff --git a/test/unit/common/middleware/test_object_versioning.py b/test/unit/common/middleware/test_object_versioning.py\nindex 1e5496e5f..8c4770ff9 100644\n--- a/test/unit/common/middleware/test_object_versioning.py\n+++ b/test/unit/common/middleware/test_object_versioning.py\n@@ -553,6 +553,17 @@ class ObjectVersioningTestCase(ObjectVersioningBaseTestCase):\n             (\u0027X-Symlink-Target\u0027, \u0027c/o?version-id\u003d0000001234.00000\u0027),\n             headers)\n         self.assertEqual(body, b\u0027\u0027)\n+\n+    def test_head_symlink(self):\n+        self.app.register(\n+            \u0027HEAD\u0027, \u0027/v1/a/c/o?symlink\u003dget\u0027, swob.HTTPOk, {\n+                \u0027X-Symlink-Target\u0027: \u0027%s/%s\u0027 % (\n+                    self.build_container_name(\u0027c\u0027),\n+                    self.build_object_name(\u0027o\u0027, \u00279999998765.99999\u0027),\n+                ),\n+                \u0027X-Symlink-Target-Etag\u0027: \u0027versioned-obj-etag\u0027,\n+            }, \u0027\u0027)\n+\n         # N.B. HEAD req already works with existing registered GET response\n         req \u003d Request.blank(\n             \u0027/v1/a/c/o?symlink\u003dget\u0027, method\u003d\u0027HEAD\u0027,\n```","commit_id":"7ee91ba1bff33d89dab4b87e907d0fd30ce50bfe"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"ae557fe9a268f902623c0d1f6bdcdcff6d69175b","unresolved":false,"context_lines":[{"line_number":553,"context_line":"            (\u0027X-Symlink-Target\u0027, \u0027c/o?version-id\u003d0000001234.00000\u0027),"},{"line_number":554,"context_line":"            headers)"},{"line_number":555,"context_line":"        self.assertEqual(body, b\u0027\u0027)"},{"line_number":556,"context_line":"        # N.B. HEAD req already works with existing registered GET response"},{"line_number":557,"context_line":"        req \u003d Request.blank("},{"line_number":558,"context_line":"            \u0027/v1/a/c/o?symlink\u003dget\u0027, method\u003d\u0027HEAD\u0027,"},{"line_number":559,"context_line":"            environ\u003d{\u0027swift.cache\u0027: self.cache_version_on})"}],"source_content_type":"text/x-python","patch_set":1,"id":"d6ae6351_aadb5887","line":556,"in_reply_to":"0d055497_d2c73884","updated":"2023-08-17 12:08:36.000000000","message":"Yes, once I widened my perspective to the broader context of FakeSwift\u0027s handling of HEAD+params, I could see exactly why this was written as it is.\n\nIt\u0027s fine 😊","commit_id":"7ee91ba1bff33d89dab4b87e907d0fd30ce50bfe"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"39c02895811b1a43ace030154f500ab90fc5854a","unresolved":true,"context_lines":[{"line_number":553,"context_line":"            (\u0027X-Symlink-Target\u0027, \u0027c/o?version-id\u003d0000001234.00000\u0027),"},{"line_number":554,"context_line":"            headers)"},{"line_number":555,"context_line":"        self.assertEqual(body, b\u0027\u0027)"},{"line_number":556,"context_line":"        # N.B. HEAD req already works with existing registered GET response"},{"line_number":557,"context_line":"        req \u003d Request.blank("},{"line_number":558,"context_line":"            \u0027/v1/a/c/o?symlink\u003dget\u0027, method\u003d\u0027HEAD\u0027,"},{"line_number":559,"context_line":"            environ\u003d{\u0027swift.cache\u0027: self.cache_version_on})"}],"source_content_type":"text/x-python","patch_set":1,"id":"0d055497_d2c73884","line":556,"in_reply_to":"166f4773_dceb4922","updated":"2023-08-16 23:54:58.000000000","message":"the comment definately came from the context in which this test was developed - the HEAD params FakeSwift change https://review.opendev.org/c/openstack/swift/+/889785/21 that discovered trying to add exemplar tests in versioned writes hit strange leak tracking errors.\n\nas far as \"why *not* break this out...\" I guess I\u0027d answer because this works and it\u0027s what I had when i was scrambling to get this diff together in a way that\u0027s at least somewhat scruitable?  It *could* be *re*written as a separate test, or with a registered HEAD, or a half-dozen other ways - but this is what I was thinking about when I was writing it.\n\nIf you are in ANY way trying to *imply* there\u0027s some defiencey with the way I wrote it - just say what\u0027s wrong!  Maybe I\u0027ll agree or push back.  Or were you just genuinely unaware of the context in which this test was developed?\n\nMy assumption is honestly this test will get read - in the context of this diff, this change, this patch series, a few dozen times by me and reviewers - and then execute successfully thousands of times without it causing any bother to ever.  We\u0027ll probably never look at it again (unless we need to go find a test to copy that does a HEAD with symlink\u003dget - which didn\u0027t previously exist AFAIK so it\u0027s not zero value add!)\n\nDoes it make sense why it\u0027s like this given that context?","commit_id":"7ee91ba1bff33d89dab4b87e907d0fd30ce50bfe"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"54e259288a3bc48e8e339427220d8bb6aa2badc7","unresolved":true,"context_lines":[{"line_number":565,"context_line":"        self.assertIn("},{"line_number":566,"context_line":"            (\u0027X-Symlink-Target\u0027, \u0027c/o?version-id\u003d0000001234.00000\u0027),"},{"line_number":567,"context_line":"            headers)"},{"line_number":568,"context_line":"        self.assertEqual(body, b\u0027\u0027)"},{"line_number":569,"context_line":""},{"line_number":570,"context_line":"    def test_put_object_no_versioning(self):"},{"line_number":571,"context_line":"        self.app.register("}],"source_content_type":"text/x-python","patch_set":1,"id":"6393ac26_25e07a80","line":568,"updated":"2023-08-16 12:21:57.000000000","message":"+1 this fails without the change in swob.py\n\n```\ntest/unit/common/middleware/test_object_versioning.py::ObjectVersioningTestCase::test_get_symlink FAILED                                 [100%]\n\n\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d FAILURES \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n__________________________________________________ ObjectVersioningTestCase.test_get_symlink ___________________________________________________\n\nself \u003d \u003ctest.unit.common.middleware.test_object_versioning.ObjectVersioningTestCase testMethod\u003dtest_get_symlink\u003e\n\n    def tearDown(self):\n\u003e       self.assertEqual(self.app.unclosed_requests, {})\nE       AssertionError: {(\u0027HEAD\u0027, \u0027/v1/a/c/o?symlink\u003dget\u0027): 1} !\u003d {}\nE       - {(\u0027HEAD\u0027, \u0027/v1/a/c/o?symlink\u003dget\u0027): 1}\nE       + {}\n\ntest/unit/common/middleware/test_object_versioning.py:112: AssertionError\n\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d short test summary info \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\nFAILED test/unit/common/middleware/test_object_versioning.py::ObjectVersioningTestCase::test_get_symlink - AssertionError: {(\u0027HEAD\u0027, \u0027/v1/a/c/o?symlink\u003dget\u0027): 1} !\u003d {}\n\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d 1 failed in 0.43s \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n```","commit_id":"7ee91ba1bff33d89dab4b87e907d0fd30ce50bfe"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"39c02895811b1a43ace030154f500ab90fc5854a","unresolved":false,"context_lines":[{"line_number":565,"context_line":"        self.assertIn("},{"line_number":566,"context_line":"            (\u0027X-Symlink-Target\u0027, \u0027c/o?version-id\u003d0000001234.00000\u0027),"},{"line_number":567,"context_line":"            headers)"},{"line_number":568,"context_line":"        self.assertEqual(body, b\u0027\u0027)"},{"line_number":569,"context_line":""},{"line_number":570,"context_line":"    def test_put_object_no_versioning(self):"},{"line_number":571,"context_line":"        self.app.register("}],"source_content_type":"text/x-python","patch_set":1,"id":"53239173_8ea9beb2","line":568,"in_reply_to":"6393ac26_25e07a80","updated":"2023-08-16 23:54:58.000000000","message":"Done","commit_id":"7ee91ba1bff33d89dab4b87e907d0fd30ce50bfe"}],"test/unit/common/test_swob.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"54e259288a3bc48e8e339427220d8bb6aa2badc7","unresolved":true,"context_lines":[{"line_number":1237,"context_line":"        tracking \u003d {"},{"line_number":1238,"context_line":"            \u0027closed\u0027: 0,"},{"line_number":1239,"context_line":"            \u0027read\u0027: 0,"},{"line_number":1240,"context_line":"        }"},{"line_number":1241,"context_line":""},{"line_number":1242,"context_line":"        def mark_closed(*args):"},{"line_number":1243,"context_line":"            tracking[\u0027closed\u0027] +\u003d 1"}],"source_content_type":"text/x-python","patch_set":1,"id":"6f8cd957_ebfe3187","line":1240,"updated":"2023-08-16 12:21:57.000000000","message":"nit: could use collections.Counter","commit_id":"7ee91ba1bff33d89dab4b87e907d0fd30ce50bfe"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"39c02895811b1a43ace030154f500ab90fc5854a","unresolved":false,"context_lines":[{"line_number":1237,"context_line":"        tracking \u003d {"},{"line_number":1238,"context_line":"            \u0027closed\u0027: 0,"},{"line_number":1239,"context_line":"            \u0027read\u0027: 0,"},{"line_number":1240,"context_line":"        }"},{"line_number":1241,"context_line":""},{"line_number":1242,"context_line":"        def mark_closed(*args):"},{"line_number":1243,"context_line":"            tracking[\u0027closed\u0027] +\u003d 1"}],"source_content_type":"text/x-python","patch_set":1,"id":"e9083214_70d96bc6","line":1240,"in_reply_to":"6f8cd957_ebfe3187","updated":"2023-08-16 23:54:58.000000000","message":"meh, one more thing for newbs to have to grok - maybe if it was already in this module, otherwise overkill - KISS","commit_id":"7ee91ba1bff33d89dab4b87e907d0fd30ce50bfe"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"54e259288a3bc48e8e339427220d8bb6aa2badc7","unresolved":true,"context_lines":[{"line_number":1256,"context_line":"                             app_iter\u003dapp_iter)"},{"line_number":1257,"context_line":"        output_iter \u003d resp(req.environ, lambda *_: None)"},{"line_number":1258,"context_line":"        with utils.closing_if_possible(output_iter):"},{"line_number":1259,"context_line":"            body \u003d b\u0027\u0027.join(output_iter)"},{"line_number":1260,"context_line":"        self.assertEqual(body, b\u0027\u0027)"},{"line_number":1261,"context_line":"        self.assertEqual(tracking, {"},{"line_number":1262,"context_line":"            \u0027closed\u0027: 1,"}],"source_content_type":"text/x-python","patch_set":1,"id":"33038dca_ddae3e76","line":1259,"updated":"2023-08-16 12:21:57.000000000","message":"it\u0027s not actually necessary to read the output_iter - the LeakTrackingIterator is closed as a side effect of swob.response.__call_","commit_id":"7ee91ba1bff33d89dab4b87e907d0fd30ce50bfe"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"39c02895811b1a43ace030154f500ab90fc5854a","unresolved":false,"context_lines":[{"line_number":1256,"context_line":"                             app_iter\u003dapp_iter)"},{"line_number":1257,"context_line":"        output_iter \u003d resp(req.environ, lambda *_: None)"},{"line_number":1258,"context_line":"        with utils.closing_if_possible(output_iter):"},{"line_number":1259,"context_line":"            body \u003d b\u0027\u0027.join(output_iter)"},{"line_number":1260,"context_line":"        self.assertEqual(body, b\u0027\u0027)"},{"line_number":1261,"context_line":"        self.assertEqual(tracking, {"},{"line_number":1262,"context_line":"            \u0027closed\u0027: 1,"}],"source_content_type":"text/x-python","patch_set":1,"id":"2c879bf5_06f2f7c5","line":1259,"in_reply_to":"33038dca_ddae3e76","updated":"2023-08-16 23:54:58.000000000","message":"100%, i wrote it this way so that you can revert the swob change and be 100% sure the \"problem\" isn\u0027t that: well sure it fails, but that\u0027s just cause the new test is \"doing it wrong\"\n\ndo the right thing, even when no one is looking","commit_id":"7ee91ba1bff33d89dab4b87e907d0fd30ce50bfe"}]}
