)]}'
{"/COMMIT_MSG":[{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"6830c7aec44237a11a591bba2e1ec2cb2b9b3b36","unresolved":true,"context_lines":[{"line_number":7,"context_line":"catch_errors: handle Timeouts while reading body"},{"line_number":8,"context_line":""},{"line_number":9,"context_line":"The eventlet wsgi implementation will catch an Exception, log it and"},{"line_number":10,"context_line":"if possible send the client a 500 status. But wsgi does not catch"},{"line_number":11,"context_line":"Timeouts which extend BaseException rather than Exception."},{"line_number":12,"context_line":""},{"line_number":13,"context_line":"The CatchErrorsMiddleware already catches Exception and Timeouts"},{"line_number":14,"context_line":"raised *before* its app returns a response iterator. This patch adds"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":2,"id":"a8f29e62_78a729e6","line":11,"range":{"start_line":10,"start_character":42,"end_line":11,"end_character":58},"updated":"2025-06-27 20:36:21.000000000","message":"Since [this changed upstream](https://github.com/eventlet/eventlet/commit/dfcc93953d761f7e13c04800d4d3b35257d4b840), do we still want to push on this, or should we just abandon?","commit_id":"1df49c672d7a08ec90077028169d5c1ccf244f9a"}],"/PATCHSET_LEVEL":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"5637508ba9d940d425edc4c45424ceb6a8df39df","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":2,"id":"2ffec23c_6e8b7191","updated":"2025-07-01 12:42:43.000000000","message":"Are we going to bump our minimum eventlet version requirement? \n\nThis could be abandoned entirely, or we might retain the test coverage as a defence against eventlet (or our overriding of protocol pieces) changing?\n\nI\u0027ll push up a test only patch in case we think it is useful","commit_id":"1df49c672d7a08ec90077028169d5c1ccf244f9a"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"4777022c86a91397c299af2de3edfd6fd2b5c6d5","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":2,"id":"6b0cf4a6_5ba821ac","updated":"2023-10-30 23:41:06.000000000","message":"functionally I think using the TimeoutSquashingIterable in catch errors on the way out to the http_protocol handler is proven to be quite necessary by the new test_http_protocol test cases.\n\nintellectually I think this raises as many questions as it answers.  WSGIContext should probably trigger an error resp if it\u0027s start_response still hasn\u0027t been called after reiterate.  TimeoutSupressingIterator might only make sense in the catch errors middleware.  And where our ChunkReadTimeouts have been going when you really hit one on a real eventlet.wsgi server response WITHOUT this fix is kind of scary.","commit_id":"1df49c672d7a08ec90077028169d5c1ccf244f9a"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"19057fe4e81316a528dddfd96fe85c7fd86b428a","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":2,"id":"6a64a191_df7bdd44","in_reply_to":"2ffec23c_6e8b7191","updated":"2025-07-02 05:39:11.000000000","message":"Good point about the minimum eventlet version -- I suppose I was thinking that it might fall into the class of bugs (do we have a bug for this?) where if an operator cares about it, we could direct them to upgrade eventlet, but if they don\u0027t, let them upgrade at their leisure.\n\nI\u0027m not convinced that we want to *force* an upgrade to 0.35.2; it\u0027s not even a year and a half old -- I was thinking that it was more similar to a case like [we\u0027d seen ages ago in swift3](https://opendev.org/x/swift3/commit/f3a933aad1f29a90bd2dbbabe24bf7d1b62ede9b) where yes, there\u0027s a bug, but if you want to fix it, it\u0027s not worth us bending over backwards; just upgrade eventlet and things will start working better.\n\nWhich is, admittedly, different from cases where I *did* feel the need to bend over backwards to get py3 working like py2 so we could get *that* transition done more quickly and without requiring a couple years\u0027 bake-in with new eventlet fixes :-/","commit_id":"1df49c672d7a08ec90077028169d5c1ccf244f9a"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"47159e183c1f36a66e918b3a4f110b6d3417c59f","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"b4af59e6_30b03b22","updated":"2025-07-01 14:58:54.000000000","message":"Tests will fail until eventlet is pinned to \u003e\u003d0.35.2","commit_id":"f1980416e627f6e80ba4f6bb9ea6978e66644caf"}],"swift/common/exceptions.py":[{"author":{"_account_id":34892,"name":"ASHWIN A NAIR","display_name":"indianwhocodes","email":"nairashwin952013@gmail.com","username":"indianwhocodes","status":"Nvidia"},"change_message_id":"790d9351062361c6d8f1996db12985f2b817ff6e","unresolved":false,"context_lines":[{"line_number":133,"context_line":"    pass"},{"line_number":134,"context_line":""},{"line_number":135,"context_line":""},{"line_number":136,"context_line":"class ResponseIterException(SwiftException):"},{"line_number":137,"context_line":"    pass"},{"line_number":138,"context_line":""},{"line_number":139,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"7b652e16_18065d99","line":136,"updated":"2023-10-24 20:49:43.000000000","message":"Suitable name for a custom exception to handle specific errors during response iteration.","commit_id":"811f3490aba779462628230cb63f92c98c208199"}],"swift/common/middleware/catch_errors.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"4777022c86a91397c299af2de3edfd6fd2b5c6d5","unresolved":true,"context_lines":[{"line_number":50,"context_line":""},{"line_number":51,"context_line":"        if bytes_left:"},{"line_number":52,"context_line":"            raise BadResponseLength(\u0027Expected another %d bytes\u0027 % ("},{"line_number":53,"context_line":"                bytes_left,))"},{"line_number":54,"context_line":"    finally:"},{"line_number":55,"context_line":"        close_if_possible(inner_iter)"},{"line_number":56,"context_line":""}],"source_content_type":"text/x-python","patch_set":2,"id":"6e1bc5cb_f7fcf832","line":53,"updated":"2023-10-30 23:41:06.000000000","message":"so this isn\u0027t \"catching\" errors - it\u0027s *raising* them 😄\n\nso it\u0027s totally reasonable that a resp_iter we return to eventlet.wsgi server can and will raise exceptions; we just want it to raise the *right* exceptions in all the cases we want... so it\u0027s not \"catch\" as in \"handle/suppress\" it\u0027s \"notice/translate\"\n\nwe have an existing case were we turn a short read into an exception\nnow we\u0027re adding a new case where we turn a timeout into a normal exception\n\nbut we still let any other run of the mill ValueError/Exception that some middleware might explode with get out to eventlet.wsgi","commit_id":"1df49c672d7a08ec90077028169d5c1ccf244f9a"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"4777022c86a91397c299af2de3edfd6fd2b5c6d5","unresolved":true,"context_lines":[{"line_number":75,"context_line":"            # catch any errors in the pipeline"},{"line_number":76,"context_line":"            resp \u003d self._app_call(env)"},{"line_number":77,"context_line":"        except:  # noqa"},{"line_number":78,"context_line":"            self.logger.exception(_(\u0027Error: An error occurred\u0027))"},{"line_number":79,"context_line":"            resp \u003d HTTPServerError(request\u003dRequest(env),"},{"line_number":80,"context_line":"                                   body\u003db\u0027An error occurred\u0027,"},{"line_number":81,"context_line":"                                   content_type\u003d\u0027text/plain\u0027)"}],"source_content_type":"text/x-python","patch_set":2,"id":"cfc86a48_8a7b07b9","line":78,"updated":"2023-10-30 23:41:06.000000000","message":"I honestly expected something like this to wrap the response iter\n\nexcept in that handler, the \"proper\" way to signal \"error\" to eventlet.wsgi isn\u0027t to return a 500 resp, it\u0027s to raise an exception (ANY exception?  not a timeout!) so eventlet.wsgi knows to shutdown the the connection.","commit_id":"1df49c672d7a08ec90077028169d5c1ccf244f9a"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"4777022c86a91397c299af2de3edfd6fd2b5c6d5","unresolved":false,"context_lines":[{"line_number":122,"context_line":"        self._response_headers.append((\u0027X-Openstack-Request-Id\u0027, trans_id))"},{"line_number":123,"context_line":"        start_response(self._response_status, self._response_headers,"},{"line_number":124,"context_line":"                       self._response_exc_info)"},{"line_number":125,"context_line":"        return TimeoutCatchingIterator(resp)"},{"line_number":126,"context_line":""},{"line_number":127,"context_line":""},{"line_number":128,"context_line":"class CatchErrorMiddleware(object):"}],"source_content_type":"text/x-python","patch_set":2,"id":"62a628c5_16bf1ac9","line":125,"updated":"2023-10-30 23:41:06.000000000","message":"since we know that the only thing left of catch_errors is wsgi the suppression of timeout exceptions seems reasonable; no code to the left of us would have been handling catching/handling timeouts.\n\nregardless getting the iterator to always close on timeout is only a win.","commit_id":"1df49c672d7a08ec90077028169d5c1ccf244f9a"}],"swift/common/utils/__init__.py":[{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"c92ba2f1e82649c952eab45bba00b835fe9cbcb7","unresolved":true,"context_lines":[{"line_number":3730,"context_line":"            return super(TimeoutCatchingIterator, self).__next__()"},{"line_number":3731,"context_line":"        except Timeout as err:"},{"line_number":3732,"context_line":"            self.close()"},{"line_number":3733,"context_line":"            raise swift.common.exceptions.ResponseIterException("},{"line_number":3734,"context_line":"                \u0027Caused by %s: %s\u0027 % (type(err).__name__, str(err)))"},{"line_number":3735,"context_line":""},{"line_number":3736,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"63538450_271b9380","line":3733,"range":{"start_line":3733,"start_character":18,"end_line":3733,"end_character":63},"updated":"2023-10-30 05:41:24.000000000","message":"heh, full namespace path like this smells like there might have been a circular dependency or something 😊","commit_id":"811f3490aba779462628230cb63f92c98c208199"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2462d39af934f1e34605a35404cbae888607dbc7","unresolved":false,"context_lines":[{"line_number":3730,"context_line":"            return super(TimeoutCatchingIterator, self).__next__()"},{"line_number":3731,"context_line":"        except Timeout as err:"},{"line_number":3732,"context_line":"            self.close()"},{"line_number":3733,"context_line":"            raise swift.common.exceptions.ResponseIterException("},{"line_number":3734,"context_line":"                \u0027Caused by %s: %s\u0027 % (type(err).__name__, str(err)))"},{"line_number":3735,"context_line":""},{"line_number":3736,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"67792d15_03789d75","line":3733,"range":{"start_line":3733,"start_character":18,"end_line":3733,"end_character":63},"in_reply_to":"63538450_271b9380","updated":"2023-10-30 11:21:16.000000000","message":"Timestamp is used in swift.common.exceptions\n\nI\u0027m not sure there needs to be a circular dependency since 879688: Pull timestamp-related functions out to a separate module | https://review.opendev.org/c/openstack/swift/+/879688\n\nbut here I\u0027m using the pattern used in rest of utils","commit_id":"811f3490aba779462628230cb63f92c98c208199"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"c92ba2f1e82649c952eab45bba00b835fe9cbcb7","unresolved":true,"context_lines":[{"line_number":6392,"context_line":"        self.watchdog.stop(self.key)"},{"line_number":6393,"context_line":""},{"line_number":6394,"context_line":""},{"line_number":6395,"context_line":"class CooperativeIterator(ClosingIterator):"},{"line_number":6396,"context_line":"    \"\"\""},{"line_number":6397,"context_line":"    Wrapper to make a deliberate periodic call to ``sleep()`` while iterating"},{"line_number":6398,"context_line":"    over wrapped iterator, providing an opportunity to switch greenthreads."}],"source_content_type":"text/x-python","patch_set":1,"id":"e473505f_68b50c4e","line":6395,"range":{"start_line":6395,"start_character":6,"end_line":6395,"end_character":43},"updated":"2023-10-30 05:41:24.000000000","message":"Love this refactor, all our custom iters seem to be getting better!","commit_id":"811f3490aba779462628230cb63f92c98c208199"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2462d39af934f1e34605a35404cbae888607dbc7","unresolved":false,"context_lines":[{"line_number":6392,"context_line":"        self.watchdog.stop(self.key)"},{"line_number":6393,"context_line":""},{"line_number":6394,"context_line":""},{"line_number":6395,"context_line":"class CooperativeIterator(ClosingIterator):"},{"line_number":6396,"context_line":"    \"\"\""},{"line_number":6397,"context_line":"    Wrapper to make a deliberate periodic call to ``sleep()`` while iterating"},{"line_number":6398,"context_line":"    over wrapped iterator, providing an opportunity to switch greenthreads."}],"source_content_type":"text/x-python","patch_set":1,"id":"28b720bc_0328ee9f","line":6395,"range":{"start_line":6395,"start_character":6,"end_line":6395,"end_character":43},"in_reply_to":"e473505f_68b50c4e","updated":"2023-10-30 11:21:16.000000000","message":"Ack","commit_id":"811f3490aba779462628230cb63f92c98c208199"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"4777022c86a91397c299af2de3edfd6fd2b5c6d5","unresolved":false,"context_lines":[{"line_number":3702,"context_line":"        return next(self.wrapped_iter)"},{"line_number":3703,"context_line":""},{"line_number":3704,"context_line":"    def next(self):"},{"line_number":3705,"context_line":"        return self.__next__()"},{"line_number":3706,"context_line":""},{"line_number":3707,"context_line":"    def close(self):"},{"line_number":3708,"context_line":"        close_if_possible(self.wrapped)"}],"source_content_type":"text/x-python","patch_set":2,"id":"7f278f5d_6d9d01ed","line":3705,"updated":"2023-10-30 23:41:06.000000000","message":"IIRC py3 only requires __next__ and there was some consideration given to the pattern of `next \u003d __next__  # py2` in being only one line to delete.  I think i actually prefer having the explicit `def`","commit_id":"1df49c672d7a08ec90077028169d5c1ccf244f9a"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"4777022c86a91397c299af2de3edfd6fd2b5c6d5","unresolved":true,"context_lines":[{"line_number":3720,"context_line":""},{"line_number":3721,"context_line":"    def close(self):"},{"line_number":3722,"context_line":"        for it in self.iterables:"},{"line_number":3723,"context_line":"            close_if_possible(it)"},{"line_number":3724,"context_line":""},{"line_number":3725,"context_line":""},{"line_number":3726,"context_line":"class TimeoutCatchingIterator(ClosingIterator):"}],"source_content_type":"text/x-python","patch_set":2,"id":"668393a1_612aae41","line":3723,"updated":"2023-10-30 23:41:06.000000000","message":"Perhaps it\u0027s justifiably reasonable to \"only\" close the wrapped iterables and not the itertools.chain iterable; but for cleanliness I think I\u0027d prefer a super call to close.","commit_id":"1df49c672d7a08ec90077028169d5c1ccf244f9a"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"4777022c86a91397c299af2de3edfd6fd2b5c6d5","unresolved":false,"context_lines":[{"line_number":3729,"context_line":"        try:"},{"line_number":3730,"context_line":"            return super(TimeoutCatchingIterator, self).__next__()"},{"line_number":3731,"context_line":"        except Timeout as err:"},{"line_number":3732,"context_line":"            self.close()"},{"line_number":3733,"context_line":"            raise swift.common.exceptions.ResponseIterException("},{"line_number":3734,"context_line":"                \u0027Caused by %s: %s\u0027 % (type(err).__name__, str(err)))"},{"line_number":3735,"context_line":""}],"source_content_type":"text/x-python","patch_set":2,"id":"70f7ad5b_ae668c7e","line":3732,"updated":"2023-10-30 23:41:06.000000000","message":"forcing an iterator to close on timeout seems reasonable","commit_id":"1df49c672d7a08ec90077028169d5c1ccf244f9a"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"4777022c86a91397c299af2de3edfd6fd2b5c6d5","unresolved":true,"context_lines":[{"line_number":3731,"context_line":"        except Timeout as err:"},{"line_number":3732,"context_line":"            self.close()"},{"line_number":3733,"context_line":"            raise swift.common.exceptions.ResponseIterException("},{"line_number":3734,"context_line":"                \u0027Caused by %s: %s\u0027 % (type(err).__name__, str(err)))"},{"line_number":3735,"context_line":""},{"line_number":3736,"context_line":""},{"line_number":3737,"context_line":"def reiterate(iterable):"}],"source_content_type":"text/x-python","patch_set":2,"id":"ce49366f_9d787668","line":3734,"updated":"2023-10-30 23:41:06.000000000","message":"converting the timeout exception to a different exception is only reasonable depending on the context;  maybe TimeoutSupressingIterator","commit_id":"1df49c672d7a08ec90077028169d5c1ccf244f9a"}],"test/unit/common/middleware/test_catch_errors.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"4777022c86a91397c299af2de3edfd6fd2b5c6d5","unresolved":false,"context_lines":[{"line_number":40,"context_line":"            raise Exception(\u0027An error occurred\u0027)"},{"line_number":41,"context_line":"        if self.status:"},{"line_number":42,"context_line":"            # this is well-behaved"},{"line_number":43,"context_line":"            start_response(self.status, {})"},{"line_number":44,"context_line":"        if self.body_iter is None:"},{"line_number":45,"context_line":"            return [b\"FAKE APP\"]"},{"line_number":46,"context_line":"        else:"}],"source_content_type":"text/x-python","patch_set":2,"id":"44365743_43a6da1e","line":43,"updated":"2023-10-30 23:41:06.000000000","message":"this was a strange little FakeApp","commit_id":"1df49c672d7a08ec90077028169d5c1ccf244f9a"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"4777022c86a91397c299af2de3edfd6fd2b5c6d5","unresolved":true,"context_lines":[{"line_number":124,"context_line":"            b\u0027\u0027.join(resp)"},{"line_number":125,"context_line":"        self.assertIn(\u0027resp iter went boom!\u0027, str(cm.exception))"},{"line_number":126,"context_line":"        # start_response was called..."},{"line_number":127,"context_line":"        self.assertEqual([None], self.statuses)"},{"line_number":128,"context_line":""},{"line_number":129,"context_line":"    def test_timeout_in_iterator_no_reiterate(self):"},{"line_number":130,"context_line":"        def body_iter():"}],"source_content_type":"text/x-python","patch_set":2,"id":"7c58bb2a_9b8343d2","line":127,"updated":"2023-10-30 23:41:06.000000000","message":"WHO called start-response with None!?\n\napparently catch-errors did, because WSGI context expects getting a chunk from the response iter *should* call start_response; but our strange little FakeApp never does:\n\n```\n    def _app_call(self, env):\n        \"\"\"\n        Ensures start_response has been called before returning.\n        \"\"\"\n        self._response_status \u003d None\n        self._response_headers \u003d None\n        self._response_exc_info \u003d None\n        resp \u003d self.app(env, self._start_response)\n        # if start_response has not been called, iterate until we\u0027ve got a\n        # non-empty chunk, by which time the app *should* have called it\n        if self._response_status is None:\n            resp \u003d reiterate(resp)\n        return resp\n\n```\n\nI\u0027m not sure it\u0027s only a good thing that the FakeApp behaves this way or that this test expects it...","commit_id":"1df49c672d7a08ec90077028169d5c1ccf244f9a"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"4777022c86a91397c299af2de3edfd6fd2b5c6d5","unresolved":true,"context_lines":[{"line_number":156,"context_line":"        def body_iter():"},{"line_number":157,"context_line":"            yield \u0027start\u0027"},{"line_number":158,"context_line":"            raise ChunkReadTimeout"},{"line_number":159,"context_line":"        # no FakeApp status -\u003e no call to start_response -\u003e reiterate"},{"line_number":160,"context_line":"        app \u003d catch_errors.CatchErrorMiddleware("},{"line_number":161,"context_line":"            FakeApp(body_iter\u003dbody_iter()), {})"},{"line_number":162,"context_line":"        req \u003d Request.blank(\u0027/\u0027, environ\u003d{\u0027REQUEST_METHOD\u0027: \u0027GET\u0027})"}],"source_content_type":"text/x-python","patch_set":2,"id":"d18d2497_2c38f89f","line":159,"updated":"2023-10-30 23:41:06.000000000","message":"ok, so passing a status ends up being a way to short circut WSGIContext into NOT calling reiterate; it passes the wrapped app a wrapped self._start_response that will capture the status; and *if* the status isn\u0027t set when that returns the resp_iter then it calls reiterate (which it expects will call start_response)","commit_id":"1df49c672d7a08ec90077028169d5c1ccf244f9a"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"4777022c86a91397c299af2de3edfd6fd2b5c6d5","unresolved":true,"context_lines":[{"line_number":165,"context_line":"            b\u0027\u0027.join(resp)"},{"line_number":166,"context_line":"        self.assertIn(\u0027ChunkReadTimeout\u0027, str(cm.exception))"},{"line_number":167,"context_line":"        # start_response was called..."},{"line_number":168,"context_line":"        self.assertEqual([None], self.statuses)"},{"line_number":169,"context_line":""},{"line_number":170,"context_line":"    def test_trans_id_header_suffix(self):"},{"line_number":171,"context_line":"        self.assertIsNone(self.logger.txn_id)"}],"source_content_type":"text/x-python","patch_set":2,"id":"aa02360a_2deb21c8","line":168,"updated":"2023-10-30 23:41:06.000000000","message":"only our strange little FakeApp just never calls start_response; so WSGIContext does it anyway - which is bound to be some kind of badness.","commit_id":"1df49c672d7a08ec90077028169d5c1ccf244f9a"}],"test/unit/common/test_http_protocol.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2462d39af934f1e34605a35404cbae888607dbc7","unresolved":false,"context_lines":[{"line_number":292,"context_line":"        # but we can at least assert that the logger txn_id was set"},{"line_number":293,"context_line":"        self.assertEqual(\u0027test-trans-id\u0027, app_logger.txn_id)"},{"line_number":294,"context_line":""},{"line_number":295,"context_line":"    def _do_test_body_iter_exception("},{"line_number":296,"context_line":"            self, error, minimum_write_chunk_size\u003dNone, wfile\u003dNone):"},{"line_number":297,"context_line":""},{"line_number":298,"context_line":"        def body_iter():"}],"source_content_type":"text/x-python","patch_set":1,"id":"dc32cba7_80b455a6","line":295,"updated":"2023-10-30 11:21:16.000000000","message":"I\u0027m going to rename this because it is used for exception and timeout cases","commit_id":"811f3490aba779462628230cb63f92c98c208199"},{"author":{"_account_id":34892,"name":"ASHWIN A NAIR","display_name":"indianwhocodes","email":"nairashwin952013@gmail.com","username":"indianwhocodes","status":"Nvidia"},"change_message_id":"790d9351062361c6d8f1996db12985f2b817ff6e","unresolved":false,"context_lines":[{"line_number":312,"context_line":"        ), app\u003dapp, wfile\u003dwfile)"},{"line_number":313,"context_line":"        return [line for line in bytes_out.split(b\"\\r\\n\") if line]"},{"line_number":314,"context_line":""},{"line_number":315,"context_line":"    def test_body_iter_exception_before_headers_sent(self):"},{"line_number":316,"context_line":"        # headers not sent - client gets a 500"},{"line_number":317,"context_line":"        bytes_out_lines \u003d self._do_test_body_iter_exception("},{"line_number":318,"context_line":"            ResponseIterException(\u0027No More Bytes\u0027))"}],"source_content_type":"text/x-python","patch_set":1,"id":"ff7b339e_fddb31fc","line":315,"updated":"2023-10-24 20:49:43.000000000","message":"This is a handy test so, it evaluates  the scenario where an exception is raised before the headers are sent to the client. In this case, it expects that the client receives a “500 Internal Server Error” response","commit_id":"811f3490aba779462628230cb63f92c98c208199"},{"author":{"_account_id":34892,"name":"ASHWIN A NAIR","display_name":"indianwhocodes","email":"nairashwin952013@gmail.com","username":"indianwhocodes","status":"Nvidia"},"change_message_id":"790d9351062361c6d8f1996db12985f2b817ff6e","unresolved":false,"context_lines":[{"line_number":329,"context_line":"        self.assertFalse(self.logger.get_lines_for_level(\u0027warning\u0027))"},{"line_number":330,"context_line":"        self.assertFalse(self.logger.get_lines_for_level(\u0027error\u0027))"},{"line_number":331,"context_line":""},{"line_number":332,"context_line":"    def test_body_iter_exception_after_headers_sent(self):"},{"line_number":333,"context_line":"        # too late for a 500 if headers have been sent..."},{"line_number":334,"context_line":"        bytes_out_lines \u003d self._do_test_body_iter_exception("},{"line_number":335,"context_line":"            ResponseIterException(\u0027No More Bytes\u0027),"}],"source_content_type":"text/x-python","patch_set":1,"id":"28cdf198_4519dc0c","line":332,"updated":"2023-10-24 20:49:43.000000000","message":"This tests the scenario where an exception is raised after the headers are already sent to the client. In this case, it’s too late to send a “500 Internal Server Error” response, so it expects that the client receives a “200 OK” response, and that ‘start of body’ is included in the response body","commit_id":"811f3490aba779462628230cb63f92c98c208199"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"c92ba2f1e82649c952eab45bba00b835fe9cbcb7","unresolved":true,"context_lines":[{"line_number":345,"context_line":"        self.assertFalse(self.logger.get_lines_for_level(\u0027warning\u0027))"},{"line_number":346,"context_line":"        self.assertFalse(self.logger.get_lines_for_level(\u0027error\u0027))"},{"line_number":347,"context_line":""},{"line_number":348,"context_line":"    def test_body_iter_timeout_before_headers_sent(self):"},{"line_number":349,"context_line":"        # timeouts don\u0027t get handled"},{"line_number":350,"context_line":"        wfile \u003d BytesIO()"},{"line_number":351,"context_line":"        with self.assertRaises(ChunkReadTimeout):"}],"source_content_type":"text/x-python","patch_set":1,"id":"2de96a93_0e63ad5f","line":348,"range":{"start_line":348,"start_character":31,"end_line":348,"end_character":50},"updated":"2023-10-30 05:41:24.000000000","message":"Ahh isn\u0027t this suppose to \"after\" headers are sent?","commit_id":"811f3490aba779462628230cb63f92c98c208199"},{"author":{"_account_id":34892,"name":"ASHWIN A NAIR","display_name":"indianwhocodes","email":"nairashwin952013@gmail.com","username":"indianwhocodes","status":"Nvidia"},"change_message_id":"790d9351062361c6d8f1996db12985f2b817ff6e","unresolved":false,"context_lines":[{"line_number":345,"context_line":"        self.assertFalse(self.logger.get_lines_for_level(\u0027warning\u0027))"},{"line_number":346,"context_line":"        self.assertFalse(self.logger.get_lines_for_level(\u0027error\u0027))"},{"line_number":347,"context_line":""},{"line_number":348,"context_line":"    def test_body_iter_timeout_before_headers_sent(self):"},{"line_number":349,"context_line":"        # timeouts don\u0027t get handled"},{"line_number":350,"context_line":"        wfile \u003d BytesIO()"},{"line_number":351,"context_line":"        with self.assertRaises(ChunkReadTimeout):"}],"source_content_type":"text/x-python","patch_set":1,"id":"99d37ed1_b36a8b77","line":348,"updated":"2023-10-24 20:49:43.000000000","message":"An ‘info’ level log message is generated with the traceback and error message, and that no ‘warning’ or ‘error’ level log messages are generated.","commit_id":"811f3490aba779462628230cb63f92c98c208199"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2462d39af934f1e34605a35404cbae888607dbc7","unresolved":false,"context_lines":[{"line_number":345,"context_line":"        self.assertFalse(self.logger.get_lines_for_level(\u0027warning\u0027))"},{"line_number":346,"context_line":"        self.assertFalse(self.logger.get_lines_for_level(\u0027error\u0027))"},{"line_number":347,"context_line":""},{"line_number":348,"context_line":"    def test_body_iter_timeout_before_headers_sent(self):"},{"line_number":349,"context_line":"        # timeouts don\u0027t get handled"},{"line_number":350,"context_line":"        wfile \u003d BytesIO()"},{"line_number":351,"context_line":"        with self.assertRaises(ChunkReadTimeout):"}],"source_content_type":"text/x-python","patch_set":1,"id":"38893049_a72f9f8a","line":348,"range":{"start_line":348,"start_character":31,"end_line":348,"end_character":50},"in_reply_to":"2de96a93_0e63ad5f","updated":"2023-10-30 11:21:16.000000000","message":"good catch! and I\u0027m missing a timeout_before_headers case - maybe I messed up a rebase?","commit_id":"811f3490aba779462628230cb63f92c98c208199"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"4777022c86a91397c299af2de3edfd6fd2b5c6d5","unresolved":true,"context_lines":[{"line_number":316,"context_line":"        # headers not sent - client gets a 500"},{"line_number":317,"context_line":"        bytes_out_lines \u003d self._do_test_body_iter_error("},{"line_number":318,"context_line":"            ResponseIterException(\u0027No More Bytes\u0027))"},{"line_number":319,"context_line":"        # 500 sent to client"},{"line_number":320,"context_line":"        self.assertGreater(len(bytes_out_lines), 1)"},{"line_number":321,"context_line":"        self.assertEqual(b\"HTTP/1.1 500 Internal Server Error\","},{"line_number":322,"context_line":"                         bytes_out_lines[0])"}],"source_content_type":"text/x-python","patch_set":2,"id":"dee56c34_256f3839","line":319,"updated":"2023-10-30 23:41:06.000000000","message":"looks like 500 w/ a traceback!?\n\n[b\u0027HTTP/1.1 500 Internal Server Error\u0027, b\u0027Content-Type: text/plain\u0027, b\u0027Content-Length: 327\u0027, b\u0027Date: Mon, 30 Oct 2023 23:25:01 GMT\u0027, b\u0027Connection: close\u0027, b\u0027Traceback (most recent call last):\\n  File \"/usr/local/lib/python3.8/dist-packages/eventlet/wsgi.py\", line 587, in handle_one_response\\n    for data in result:\\n  File \"/home/vagrant/swift/test/unit/common/test_http_protocol.py\", line 300, in body_iter\\n    raise error\\nswift.common.exceptions.ResponseIterException: No More Bytes\\n\u0027]\n\n\nI like that it\u0027s \"connection: close\"","commit_id":"1df49c672d7a08ec90077028169d5c1ccf244f9a"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"4777022c86a91397c299af2de3edfd6fd2b5c6d5","unresolved":true,"context_lines":[{"line_number":343,"context_line":"        info_lines \u003d self.logger.get_lines_for_level(\u0027info\u0027)"},{"line_number":344,"context_line":"        self.assertGreater(len(info_lines), 2, info_lines)"},{"line_number":345,"context_line":"        self.assertIn(\u0027Traceback\u0027, info_lines[1])"},{"line_number":346,"context_line":"        self.assertIn(\u0027ResponseIterException: No More Bytes\u0027, info_lines[1])"},{"line_number":347,"context_line":"        self.assertFalse(self.logger.get_lines_for_level(\u0027warning\u0027))"},{"line_number":348,"context_line":"        self.assertFalse(self.logger.get_lines_for_level(\u0027error\u0027))"},{"line_number":349,"context_line":""}],"source_content_type":"text/x-python","patch_set":2,"id":"b4e73641_1cbc86d7","line":346,"updated":"2023-10-30 23:41:06.000000000","message":"log level info!?","commit_id":"1df49c672d7a08ec90077028169d5c1ccf244f9a"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"4777022c86a91397c299af2de3edfd6fd2b5c6d5","unresolved":true,"context_lines":[{"line_number":352,"context_line":"        wfile \u003d BytesIO()"},{"line_number":353,"context_line":"        with self.assertRaises(ChunkReadTimeout):"},{"line_number":354,"context_line":"            self._do_test_body_iter_error("},{"line_number":355,"context_line":"                ChunkReadTimeout(), wfile\u003dwfile)"},{"line_number":356,"context_line":"        # nothing sent to client"},{"line_number":357,"context_line":"        self.assertFalse(wfile.getvalue())"},{"line_number":358,"context_line":"        info_lines \u003d self.logger.get_lines_for_level(\u0027info\u0027)"}],"source_content_type":"text/x-python","patch_set":2,"id":"99a7983d_d5f3e4de","line":355,"updated":"2023-10-30 23:41:06.000000000","message":"ROFL, so who catches it!?","commit_id":"1df49c672d7a08ec90077028169d5c1ccf244f9a"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"4777022c86a91397c299af2de3edfd6fd2b5c6d5","unresolved":true,"context_lines":[{"line_number":376,"context_line":"        self.assertEqual(b\"start of body\", bytes_out_lines[-1])"},{"line_number":377,"context_line":"        info_lines \u003d self.logger.get_lines_for_level(\u0027info\u0027)"},{"line_number":378,"context_line":"        for line in info_lines:"},{"line_number":379,"context_line":"            self.assertNotIn(\u0027Traceback\u0027, line)"},{"line_number":380,"context_line":"        self.assertFalse(self.logger.get_lines_for_level(\u0027warning\u0027))"},{"line_number":381,"context_line":"        self.assertFalse(self.logger.get_lines_for_level(\u0027error\u0027))"},{"line_number":382,"context_line":""}],"source_content_type":"text/x-python","patch_set":2,"id":"6941beae_2b8a0b63","line":379,"updated":"2023-10-30 23:41:06.000000000","message":"WTF and we loose the exception logging - because the except was never caught/handled .... ohhh kay...","commit_id":"1df49c672d7a08ec90077028169d5c1ccf244f9a"}],"test/unit/common/test_utils.py":[{"author":{"_account_id":34892,"name":"ASHWIN A NAIR","display_name":"indianwhocodes","email":"nairashwin952013@gmail.com","username":"indianwhocodes","status":"Nvidia"},"change_message_id":"790d9351062361c6d8f1996db12985f2b817ff6e","unresolved":true,"context_lines":[{"line_number":71,"context_line":""},{"line_number":72,"context_line":"from swift.common.exceptions import Timeout, MessageTimeout, \\"},{"line_number":73,"context_line":"    ConnectionTimeout, LockTimeout, ReplicationLockTimeout, \\"},{"line_number":74,"context_line":"    MimeInvalid, ChunkReadTimeout, ResponseIterException"},{"line_number":75,"context_line":"from swift.common import utils"},{"line_number":76,"context_line":"from swift.common.utils import set_swift_dir, md5, ShardRangeList, \\"},{"line_number":77,"context_line":"    SwiftLogFormatter"}],"source_content_type":"text/x-python","patch_set":1,"id":"922a54a1_783a6642","line":74,"updated":"2023-10-24 20:49:43.000000000","message":"Should we also test for `StopIteration` \u0026 `GeneratorExit` as we do in other test cases for e.g `GeneratorExit` in TestCloseableChain(unittest.TestCase), ref: https://github.com/openstack/swift/blob/master/test/unit/common/test_utils.py#L9323, it is typically handled by the WSGI server or middleware, but if it’s not caught, it could propagate up to the application. I do see us handling `StopIteration` in several places, but in the middlware we handle GeneratorExit only here, ref: https://github.com/openstack/swift/blob/master/swift/common/middleware/proxy_logging.py#L447","commit_id":"811f3490aba779462628230cb63f92c98c208199"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2462d39af934f1e34605a35404cbae888607dbc7","unresolved":true,"context_lines":[{"line_number":71,"context_line":""},{"line_number":72,"context_line":"from swift.common.exceptions import Timeout, MessageTimeout, \\"},{"line_number":73,"context_line":"    ConnectionTimeout, LockTimeout, ReplicationLockTimeout, \\"},{"line_number":74,"context_line":"    MimeInvalid, ChunkReadTimeout, ResponseIterException"},{"line_number":75,"context_line":"from swift.common import utils"},{"line_number":76,"context_line":"from swift.common.utils import set_swift_dir, md5, ShardRangeList, \\"},{"line_number":77,"context_line":"    SwiftLogFormatter"}],"source_content_type":"text/x-python","patch_set":1,"id":"20d74b6c_472aea4c","line":74,"in_reply_to":"922a54a1_783a6642","updated":"2023-10-30 11:21:16.000000000","message":"I\u0027m not quite sure what you\u0027re suggesting. I\u0027ve added a test where the ClosingIterator wraps a generator function and explicitly checks that the GeneratorExit is raised, similar to the test for CloseableChain.\n\nThe TimeoutCatchingIterator does not catch Exceptions (only Timeouts). A StopIteration is an Exception.\n\nA GeneratorExit isn\u0027t typically raised out of a generator - it is handled within the generator (as I understand it). I\u0027m not sure how calling next(self._wrapped_iter) would raise a GeneratorExit.","commit_id":"811f3490aba779462628230cb63f92c98c208199"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"41f59a1adfcdd1c4ff26522eed890525efb8d38a","unresolved":true,"context_lines":[{"line_number":9328,"context_line":"                return (x for x in self.values)"},{"line_number":9329,"context_line":""},{"line_number":9330,"context_line":"        wrapped \u003d AltFakeIterable([1, 2, 3])"},{"line_number":9331,"context_line":"        # note: iter(FakeIterable) is a generator, not the same object"},{"line_number":9332,"context_line":"        self.assertIsNot(wrapped, iter(wrapped))"},{"line_number":9333,"context_line":"        it \u003d utils.ClosingIterator(wrapped)"},{"line_number":9334,"context_line":"        actual \u003d [x for x in it]"}],"source_content_type":"text/x-python","patch_set":1,"id":"eea6fdc9_17f8613d","line":9331,"updated":"2023-10-24 17:43:07.000000000","message":"should be iter(AltFakeIterable) is a generator","commit_id":"811f3490aba779462628230cb63f92c98c208199"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2462d39af934f1e34605a35404cbae888607dbc7","unresolved":false,"context_lines":[{"line_number":9328,"context_line":"                return (x for x in self.values)"},{"line_number":9329,"context_line":""},{"line_number":9330,"context_line":"        wrapped \u003d AltFakeIterable([1, 2, 3])"},{"line_number":9331,"context_line":"        # note: iter(FakeIterable) is a generator, not the same object"},{"line_number":9332,"context_line":"        self.assertIsNot(wrapped, iter(wrapped))"},{"line_number":9333,"context_line":"        it \u003d utils.ClosingIterator(wrapped)"},{"line_number":9334,"context_line":"        actual \u003d [x for x in it]"}],"source_content_type":"text/x-python","patch_set":1,"id":"09358a8e_f351e1f7","line":9331,"in_reply_to":"eea6fdc9_17f8613d","updated":"2023-10-30 11:21:16.000000000","message":"Done","commit_id":"811f3490aba779462628230cb63f92c98c208199"}]}
