)]}'
{"/COMMIT_MSG":[{"author":{"_account_id":14070,"name":"Eric Fried","email":"openstack@fried.cc","username":"efried"},"change_message_id":"cb24baa85908fa8d9bae9659dff828ed58bfddfe","unresolved":false,"context_lines":[{"line_number":8,"context_line":""},{"line_number":9,"context_line":"The get_version method accepts a dict or list of tuples that"},{"line_number":10,"context_line":"represent HTTP request headers that will be processed to find a"},{"line_number":11,"context_line":"microversion header. Sometimes, for example in some middlwares,"},{"line_number":12,"context_line":"direct access to a headers dict will not be available and only the"},{"line_number":13,"context_line":"WSGI environ will be present."},{"line_number":14,"context_line":""}],"source_content_type":"text/x-gerrit-commit-message","patch_set":3,"id":"3f4b6375_766f2a92","line":11,"range":{"start_line":11,"start_character":52,"end_line":11,"end_character":62},"updated":"2017-10-23 12:21:50.000000000","message":"middlewares","commit_id":"1e7074d62882f2309ca7f3bea45d201aad6d3485"}],"microversion_parse/__init__.py":[{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"9b2d3167d79ef32098f6b3a469fd58f3e78ad6bd","unresolved":false,"context_lines":[{"line_number":108,"context_line":""},{"line_number":109,"context_line":"    Note that this does not change the keys in any way."},{"line_number":110,"context_line":"    \"\"\""},{"line_number":111,"context_line":"    return {key: environ[key] for key in environ if key.startswith(\u0027HTTP_\u0027)}"},{"line_number":112,"context_line":""},{"line_number":113,"context_line":""},{"line_number":114,"context_line":"def _extract_header_value(headers, header_name):"}],"source_content_type":"text/x-python","patch_set":4,"id":"df7087c5_dc72217d","line":111,"updated":"2018-03-07 00:20:32.000000000","message":"Depending on scope, you may want to grab CONTENT_LENGTH and CONTENT_TYPE, too.","commit_id":"383daf79e652e796dfe73474e7e79502ecf86897"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"057c755068f162e8a607bd33da330d772fd6ee37","unresolved":false,"context_lines":[{"line_number":108,"context_line":""},{"line_number":109,"context_line":"    Note that this does not change the keys in any way."},{"line_number":110,"context_line":"    \"\"\""},{"line_number":111,"context_line":"    return {key: environ[key] for key in environ if key.startswith(\u0027HTTP_\u0027)}"},{"line_number":112,"context_line":""},{"line_number":113,"context_line":""},{"line_number":114,"context_line":"def _extract_header_value(headers, header_name):"}],"source_content_type":"text/x-python","patch_set":4,"id":"df7087c5_5cc61119","line":111,"in_reply_to":"df7087c5_dc72217d","updated":"2018-03-07 00:33:13.000000000","message":"For our purposes here (extracting headers that might contain microversion information) we don\u0027t need those, especially given that WSGI itself treats those two headers as \"special\".\n\nIf the method was general purpose, then yes, you\u0027re right, but I\u0027m not sure in this context if that\u0027s necessary. Do you think people will expect more in this particular context?","commit_id":"383daf79e652e796dfe73474e7e79502ecf86897"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"9b2d3167d79ef32098f6b3a469fd58f3e78ad6bd","unresolved":false,"context_lines":[{"line_number":124,"context_line":"    except KeyError:"},{"line_number":125,"context_line":"        wsgi_header_name \u003d ENVIRON_HTTP_HEADER_FMT.format("},{"line_number":126,"context_line":"            header_name.replace(\u0027-\u0027, \u0027_\u0027))"},{"line_number":127,"context_line":"        value \u003d headers[wsgi_header_name]"},{"line_number":128,"context_line":"    return value"}],"source_content_type":"text/x-python","patch_set":4,"id":"df7087c5_bc696d8c","line":127,"updated":"2018-03-07 00:20:32.000000000","message":"I worry a bit about losing the ability to distinguish between\n\n environ[\u0027HTTP_SOME_NON_STANDARD_HEADER\u0027] \u003d \u0027value\n\nand the actual header\n\n Http-Some-Non-Standard-Header: value\n\nMaybe we should strip the leading HTTP_ in headers_from_wsgi_environ?","commit_id":"383daf79e652e796dfe73474e7e79502ecf86897"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"4afd2e460424775cf58948eaa7f4e4b5ddf355da","unresolved":false,"context_lines":[{"line_number":124,"context_line":"    except KeyError:"},{"line_number":125,"context_line":"        wsgi_header_name \u003d ENVIRON_HTTP_HEADER_FMT.format("},{"line_number":126,"context_line":"            header_name.replace(\u0027-\u0027, \u0027_\u0027))"},{"line_number":127,"context_line":"        value \u003d headers[wsgi_header_name]"},{"line_number":128,"context_line":"    return value"}],"source_content_type":"text/x-python","patch_set":4,"id":"df7087c5_5bb60edc","line":127,"in_reply_to":"df7087c5_5ffe0306","updated":"2018-03-07 10:23:55.000000000","message":"Yeah, the intention in these methods (as opposed to the middleware later in this stack of changes) is to allow us to avoid using webob.\n\nThe interface on get_version is preserving an existing interface already used by placement (which could be changed, but I was trying to be good about generic backwards compatibility).\n\nAll of these little functions are to allow someone to compose a solution without the tools imposing one.\n\nheaders_from_wsgi_environ will return all the HTTP_ prefixed environ entries, thus all the headers except for CONTENT_TYPE and CONTENT_LENGTH. That\u0027s the standard way of \"finding\" WSGI headers. The method does what it says on the tin. Would a different name help? It\u0027s returning those entries in the environ that are indicated, by WSGI, to be headers.\n\nThen the issues with _extract_header_value are for naught as that is explicitly private and only for use by the \u0027check_*\u0027 methods, which are only concerned \n\nMy intent here is to manipulate what WSGI gives you as little as possible because WSGI is nice.","commit_id":"383daf79e652e796dfe73474e7e79502ecf86897"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"d2411f5339b2a5d388eae752faaed46122c862ff","unresolved":false,"context_lines":[{"line_number":124,"context_line":"    except KeyError:"},{"line_number":125,"context_line":"        wsgi_header_name \u003d ENVIRON_HTTP_HEADER_FMT.format("},{"line_number":126,"context_line":"            header_name.replace(\u0027-\u0027, \u0027_\u0027))"},{"line_number":127,"context_line":"        value \u003d headers[wsgi_header_name]"},{"line_number":128,"context_line":"    return value"}],"source_content_type":"text/x-python","patch_set":4,"id":"df7087c5_5ffe0306","line":127,"in_reply_to":"df7087c5_7cf4355c","updated":"2018-03-07 00:50:27.000000000","message":"Suppose I\u0027ve got a middleware that wants to use this. I drop in\n\n    headers \u003d microversion_parse.headers_from_wsgi_environ(environ)\n    version \u003d microversion_parse.get_version(\n        headers, service_type\u003d\u0027placement\u0027)\n\nfrom the README, do what I want with the version I get, and move on. Later, someone wants to extend the middleware to handle some arbitrary headers, and only has microversions on their mind insofar as to only expect these new headers with some minimum version. I\u0027d worry they\u0027d take the function name and variables at their words.\n\nArguably, the whole thing should have just used webob Requests, but we\u0027re trying to let you *not* do that, right? Maybe it doesn\u0027t really matter -- they\u0027d quickly enough realize that `headers` doesn\u0027t get them what they want.\n\nThinking about it from another perspective, why not let get_version accept `headers_or_environ` as the arg? Why lead users into creating poorly named rather-temporary vars?","commit_id":"383daf79e652e796dfe73474e7e79502ecf86897"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"057c755068f162e8a607bd33da330d772fd6ee37","unresolved":false,"context_lines":[{"line_number":124,"context_line":"    except KeyError:"},{"line_number":125,"context_line":"        wsgi_header_name \u003d ENVIRON_HTTP_HEADER_FMT.format("},{"line_number":126,"context_line":"            header_name.replace(\u0027-\u0027, \u0027_\u0027))"},{"line_number":127,"context_line":"        value \u003d headers[wsgi_header_name]"},{"line_number":128,"context_line":"    return value"}],"source_content_type":"text/x-python","patch_set":4,"id":"df7087c5_7cf4355c","line":127,"in_reply_to":"df7087c5_bc696d8c","updated":"2018-03-07 00:33:13.000000000","message":"Again, for the purposes of extracting microversions, that\u0027s not going to be an issue. But if people are trying to use this for other kinds of headers that would be a problem. Will they be?","commit_id":"383daf79e652e796dfe73474e7e79502ecf86897"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"30bfdfd4fcec9d2a2b1261f4f4e689d4ddbe92f2","unresolved":false,"context_lines":[{"line_number":90,"context_line":"    \"\"\"Turn a list of headers into a folded dict.\"\"\""},{"line_number":91,"context_line":"    # If it behaves like a dict, return it. Webob uses objects which"},{"line_number":92,"context_line":"    # are not dicts, but behave like them."},{"line_number":93,"context_line":"    if hasattr(headers, \u0027keys\u0027):"},{"line_number":94,"context_line":"        return dict((k.lower(), v) for k, v in headers.items())"},{"line_number":95,"context_line":"    header_dict \u003d collections.defaultdict(list)"},{"line_number":96,"context_line":"    for header, value in headers:"},{"line_number":97,"context_line":"        header_dict[header.lower()].append(value.strip())"}],"source_content_type":"text/x-python","patch_set":5,"id":"df7087c5_53270ecb","line":94,"range":{"start_line":93,"start_character":0,"end_line":94,"end_character":63},"updated":"2018-03-15 12:47:25.000000000","message":"I know this isn\u0027t a change in this patch, but it caught my eye. Testing for dict-ness by checking for a \u0027keys\u0027 attribute seems weak. Also, since the att that is actually used is \u0027items\u0027, it would make more sense to check for the presence of \u0027items\u0027 to test if a value is a dict.","commit_id":"c0df7fa0349a6e406a73cf400d770e0b94633a13"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"d1ae30c7f0da37ca497907175900e0789bf5a948","unresolved":false,"context_lines":[{"line_number":90,"context_line":"    \"\"\"Turn a list of headers into a folded dict.\"\"\""},{"line_number":91,"context_line":"    # If it behaves like a dict, return it. Webob uses objects which"},{"line_number":92,"context_line":"    # are not dicts, but behave like them."},{"line_number":93,"context_line":"    if hasattr(headers, \u0027keys\u0027):"},{"line_number":94,"context_line":"        return dict((k.lower(), v) for k, v in headers.items())"},{"line_number":95,"context_line":"    header_dict \u003d collections.defaultdict(list)"},{"line_number":96,"context_line":"    for header, value in headers:"},{"line_number":97,"context_line":"        header_dict[header.lower()].append(value.strip())"}],"source_content_type":"text/x-python","patch_set":5,"id":"df7087c5_ae5ddd79","line":94,"range":{"start_line":93,"start_character":0,"end_line":94,"end_character":63},"in_reply_to":"df7087c5_53270ecb","updated":"2018-03-15 12:56:42.000000000","message":"yeah, would make sense to check for items instead","commit_id":"c0df7fa0349a6e406a73cf400d770e0b94633a13"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"72bb96d9041ef776610920ead646660e04052141","unresolved":false,"context_lines":[{"line_number":90,"context_line":"    \"\"\"Turn a list of headers into a folded dict.\"\"\""},{"line_number":91,"context_line":"    # If it behaves like a dict, return it. Webob uses objects which"},{"line_number":92,"context_line":"    # are not dicts, but behave like them."},{"line_number":93,"context_line":"    if hasattr(headers, \u0027keys\u0027):"},{"line_number":94,"context_line":"        return dict((k.lower(), v) for k, v in headers.items())"},{"line_number":95,"context_line":"    header_dict \u003d collections.defaultdict(list)"},{"line_number":96,"context_line":"    for header, value in headers:"},{"line_number":97,"context_line":"        header_dict[header.lower()].append(value.strip())"}],"source_content_type":"text/x-python","patch_set":5,"id":"df7087c5_2dd9ca20","line":94,"range":{"start_line":93,"start_character":0,"end_line":94,"end_character":63},"in_reply_to":"df7087c5_ae5ddd79","updated":"2018-03-15 18:14:33.000000000","message":"I\u0027ve made a bug https://bugs.launchpad.net/microversion-parse/+bug/1756157","commit_id":"c0df7fa0349a6e406a73cf400d770e0b94633a13"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"30bfdfd4fcec9d2a2b1261f4f4e689d4ddbe92f2","unresolved":false,"context_lines":[{"line_number":103,"context_line":"    return folded_headers"},{"line_number":104,"context_line":""},{"line_number":105,"context_line":""},{"line_number":106,"context_line":"def headers_from_wsgi_environ(environ):"},{"line_number":107,"context_line":"    \"\"\"Extract all the HTTP_ keys and values from environ to a new dict."},{"line_number":108,"context_line":""},{"line_number":109,"context_line":"    Note that this does not change the keys in any way."}],"source_content_type":"text/x-python","patch_set":5,"id":"df7087c5_4ed2012a","line":106,"range":{"start_line":106,"start_character":17,"end_line":106,"end_character":21},"updated":"2018-03-15 12:47:25.000000000","message":"nit: is there anything here that restricts this to a WSGI environment? It seems to work for any dict.","commit_id":"c0df7fa0349a6e406a73cf400d770e0b94633a13"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"72bb96d9041ef776610920ead646660e04052141","unresolved":false,"context_lines":[{"line_number":103,"context_line":"    return folded_headers"},{"line_number":104,"context_line":""},{"line_number":105,"context_line":""},{"line_number":106,"context_line":"def headers_from_wsgi_environ(environ):"},{"line_number":107,"context_line":"    \"\"\"Extract all the HTTP_ keys and values from environ to a new dict."},{"line_number":108,"context_line":""},{"line_number":109,"context_line":"    Note that this does not change the keys in any way."}],"source_content_type":"text/x-python","patch_set":5,"id":"df7087c5_4dd43ee9","line":106,"range":{"start_line":106,"start_character":17,"end_line":106,"end_character":21},"in_reply_to":"df7087c5_34ea80d7","updated":"2018-03-15 18:14:33.000000000","message":"Done","commit_id":"c0df7fa0349a6e406a73cf400d770e0b94633a13"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"d1ae30c7f0da37ca497907175900e0789bf5a948","unresolved":false,"context_lines":[{"line_number":103,"context_line":"    return folded_headers"},{"line_number":104,"context_line":""},{"line_number":105,"context_line":""},{"line_number":106,"context_line":"def headers_from_wsgi_environ(environ):"},{"line_number":107,"context_line":"    \"\"\"Extract all the HTTP_ keys and values from environ to a new dict."},{"line_number":108,"context_line":""},{"line_number":109,"context_line":"    Note that this does not change the keys in any way."}],"source_content_type":"text/x-python","patch_set":5,"id":"df7087c5_ce97f1eb","line":106,"range":{"start_line":106,"start_character":17,"end_line":106,"end_character":21},"in_reply_to":"df7087c5_4ed2012a","updated":"2018-03-15 12:56:42.000000000","message":"The input is expected to be a PEP 3333 environ, which happens to include any headers (except content length and content type) as keys prefixed with HTTP_*\n\nSo, yes, it is a dict. But the shape of the dict desired is the one that a wsgi server provides, and its name is environ in all the literature.\n\nAm I making too many assumptions about what people already know about PEP 333 and 3333?","commit_id":"c0df7fa0349a6e406a73cf400d770e0b94633a13"},{"author":{"_account_id":10670,"name":"Michael McCune","email":"elmiko@redhat.com","username":"mimccune"},"change_message_id":"23fcbe98f21e1e559fb7729248271e1adfed01e7","unresolved":false,"context_lines":[{"line_number":103,"context_line":"    return folded_headers"},{"line_number":104,"context_line":""},{"line_number":105,"context_line":""},{"line_number":106,"context_line":"def headers_from_wsgi_environ(environ):"},{"line_number":107,"context_line":"    \"\"\"Extract all the HTTP_ keys and values from environ to a new dict."},{"line_number":108,"context_line":""},{"line_number":109,"context_line":"    Note that this does not change the keys in any way."}],"source_content_type":"text/x-python","patch_set":5,"id":"df7087c5_34ea80d7","line":106,"range":{"start_line":106,"start_character":17,"end_line":106,"end_character":21},"in_reply_to":"df7087c5_ce97f1eb","updated":"2018-03-15 13:45:36.000000000","message":"imo, it might be nice to just mention in the docstring that it expects a pep 3333 environ. just eliminate the confusion.","commit_id":"c0df7fa0349a6e406a73cf400d770e0b94633a13"},{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"4aff39335f56dc69a211e8a0594773157f543c9a","unresolved":false,"context_lines":[{"line_number":90,"context_line":"    \"\"\"Turn a list of headers into a folded dict.\"\"\""},{"line_number":91,"context_line":"    # If it behaves like a dict, return it. Webob uses objects which"},{"line_number":92,"context_line":"    # are not dicts, but behave like them."},{"line_number":93,"context_line":"    if hasattr(headers, \u0027keys\u0027):"},{"line_number":94,"context_line":"        return dict((k.lower(), v) for k, v in headers.items())"},{"line_number":95,"context_line":"    header_dict \u003d collections.defaultdict(list)"},{"line_number":96,"context_line":"    for header, value in headers:"}],"source_content_type":"text/x-python","patch_set":6,"id":"df7087c5_517f9daa","line":93,"range":{"start_line":93,"start_character":7,"end_line":93,"end_character":31},"updated":"2018-03-20 13:08:11.000000000","message":"It probably would have been easier to just make the change, but as long as there is a bug tracking it, ok.\n\nhttps://bugs.launchpad.net/microversion-parse/+bug/1756157","commit_id":"bd003b52a5076905ceadf514c4d360fee545bfc5"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"cc8aaa005356747441dd1091d1a9e236b6fe67cd","unresolved":false,"context_lines":[{"line_number":90,"context_line":"    \"\"\"Turn a list of headers into a folded dict.\"\"\""},{"line_number":91,"context_line":"    # If it behaves like a dict, return it. Webob uses objects which"},{"line_number":92,"context_line":"    # are not dicts, but behave like them."},{"line_number":93,"context_line":"    if hasattr(headers, \u0027keys\u0027):"},{"line_number":94,"context_line":"        return dict((k.lower(), v) for k, v in headers.items())"},{"line_number":95,"context_line":"    header_dict \u003d collections.defaultdict(list)"},{"line_number":96,"context_line":"    for header, value in headers:"}],"source_content_type":"text/x-python","patch_set":6,"id":"df7087c5_c18c3a37","line":93,"range":{"start_line":93,"start_character":7,"end_line":93,"end_character":31},"in_reply_to":"df7087c5_517f9daa","updated":"2018-03-20 13:30:05.000000000","message":"Didn\u0027t want to do it in this stack because I hadn\u0027t otherwise touched any of this code. Do intend to fix it though.","commit_id":"bd003b52a5076905ceadf514c4d360fee545bfc5"}],"microversion_parse/tests/test_headers_from_wsgi_environ.py":[{"author":{"_account_id":1063,"name":"Ed Leafe","email":"ed@leafe.com","username":"ed-leafe"},"change_message_id":"59bd420a0340f677b0a8b08e6fe1cfec5bb76405","unresolved":false,"context_lines":[{"line_number":40,"context_line":"                    \u0027HTTP_CONTENT_TYPE\u0027: \u0027application/json\u0027}"},{"line_number":41,"context_line":"        self.assertEqual("},{"line_number":42,"context_line":"            expected,"},{"line_number":43,"context_line":"            microversion_parse.headers_from_wsgi_environ(environ))"},{"line_number":44,"context_line":""},{"line_number":45,"context_line":"    def test_get_version_from_environ(self):"},{"line_number":46,"context_line":"        environ \u003d {\u0027PATH_INFO\u0027: \u0027/foo/bar\u0027,"}],"source_content_type":"text/x-python","patch_set":2,"id":"7f287b81_354a2f80","line":43,"updated":"2017-08-22 13:41:03.000000000","message":"Nit: IMO it is clearer if you follow the pattern in the next two tests of calling the function, and then comparing the expected to the result, as opposed to what you do here and the above methods, where the call in inline with the assert.","commit_id":"307205b8cc16999343ffe6c8185f0842c78956e4"},{"author":{"_account_id":11564,"name":"Chris Dent","email":"cdent@anticdent.org","username":"chdent"},"change_message_id":"4ff59c6d9bc91f2182a49ef07003cbd1344da4e0","unresolved":false,"context_lines":[{"line_number":40,"context_line":"                    \u0027HTTP_CONTENT_TYPE\u0027: \u0027application/json\u0027}"},{"line_number":41,"context_line":"        self.assertEqual("},{"line_number":42,"context_line":"            expected,"},{"line_number":43,"context_line":"            microversion_parse.headers_from_wsgi_environ(environ))"},{"line_number":44,"context_line":""},{"line_number":45,"context_line":"    def test_get_version_from_environ(self):"},{"line_number":46,"context_line":"        environ \u003d {\u0027PATH_INFO\u0027: \u0027/foo/bar\u0027,"}],"source_content_type":"text/x-python","patch_set":2,"id":"7f287b81_6bb56bee","line":43,"in_reply_to":"7f287b81_354a2f80","updated":"2017-08-22 20:42:18.000000000","message":"Done","commit_id":"307205b8cc16999343ffe6c8185f0842c78956e4"}]}
