)]}'
{"/PATCHSET_LEVEL":[{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"d32eda0255e7378ab871bc35316f4e4f3d947d3e","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":4,"id":"a1ba22a7_5734010c","updated":"2024-04-26 16:01:53.000000000","message":"recheck unrelated failure due to eventlet issues","commit_id":"6afdd9bbb018b8042f35e1a104f6f3537adc307f"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"96c10b44d9dc070e57b2ae729be968aede0390aa","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":8,"id":"df2dba5e_b231f382","updated":"2025-04-10 16:43:40.000000000","message":"Leaving a couple of notes for reviewers.","commit_id":"5c4f29320389c00ba39dfc25067a4b56ec6eec68"},{"author":{"_account_id":5314,"name":"Brian Rosmaita","email":"rosmaita.fossdev@gmail.com","username":"brian-rosmaita"},"change_message_id":"8572a8c00ff8b17c0ce0bd9e5dc6b7bf3eafe141","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":10,"id":"77d23a8d_b704e071","updated":"2025-11-07 02:28:13.000000000","message":"Thanks for the helpful comments, it made it easier to verify the changes.  LGTM.","commit_id":"3d296ed3d6146cb88b59dacb54df46ac748d580b"},{"author":{"_account_id":9236,"name":"Jon Bernard","email":"jobernar@redhat.com","username":"jbernard"},"change_message_id":"b26e55ba8fbe95d47dfd90e1f45ebff7f823d0cd","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":10,"id":"eaf9afe9_0eac6929","updated":"2025-10-30 15:10:01.000000000","message":"recheck unrelated","commit_id":"3d296ed3d6146cb88b59dacb54df46ac748d580b"}],"cinder/api/openstack/__init__.py":[{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"96c10b44d9dc070e57b2ae729be968aede0390aa","unresolved":false,"context_lines":[{"line_number":40,"context_line":"LOG \u003d logging.getLogger(__name__)"},{"line_number":41,"context_line":""},{"line_number":42,"context_line":""},{"line_number":43,"context_line":"class APIMapper(routes.Mapper):"},{"line_number":44,"context_line":"    def routematch(self, url\u003dNone, environ\u003dNone):"},{"line_number":45,"context_line":"        if url \u003d\u003d \"\":"},{"line_number":46,"context_line":"            result \u003d self._match(\"\", environ)"},{"line_number":47,"context_line":"            return result[0], result[1]"},{"line_number":48,"context_line":"        return routes.Mapper.routematch(self, url, environ)"},{"line_number":49,"context_line":""},{"line_number":50,"context_line":"    def connect(self, *args, **kwargs):"},{"line_number":51,"context_line":"        # NOTE(inhye): Default the format part of a route to only accept json"},{"line_number":52,"context_line":"        #             so it doesn\u0027t eat all characters after a \u0027.\u0027"},{"line_number":53,"context_line":"        #             in the url."},{"line_number":54,"context_line":"        kwargs.setdefault(\u0027requirements\u0027, {})"},{"line_number":55,"context_line":"        if not kwargs[\u0027requirements\u0027].get(\u0027format\u0027):"},{"line_number":56,"context_line":"            kwargs[\u0027requirements\u0027][\u0027format\u0027] \u003d \u0027json\u0027"},{"line_number":57,"context_line":"        return routes.Mapper.connect(self, *args, **kwargs)"},{"line_number":58,"context_line":""},{"line_number":59,"context_line":""},{"line_number":60,"context_line":"class ProjectMapper(APIMapper):"}],"source_content_type":"text/x-python","patch_set":8,"id":"7f5270cb_da5a4f4c","side":"PARENT","line":57,"range":{"start_line":43,"start_character":0,"end_line":57,"end_character":59},"updated":"2025-04-10 16:43:40.000000000","message":"This code is getting moved verbatim to the next file\n\nhttps://review.opendev.org/c/openstack/cinder/+/916464/8/cinder/api/openstack/wsgi.py#1287","commit_id":"9c2904da74a10bf1f610719d09e10676a89970b8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"bc1bfd7ef09ee164a029b699834cbc24cba7391c","unresolved":true,"context_lines":[{"line_number":40,"context_line":"LOG \u003d logging.getLogger(__name__)"},{"line_number":41,"context_line":""},{"line_number":42,"context_line":""},{"line_number":43,"context_line":"class APIMapper(routes.Mapper):"},{"line_number":44,"context_line":"    def routematch(self, url\u003dNone, environ\u003dNone):"},{"line_number":45,"context_line":"        if url \u003d\u003d \"\":"},{"line_number":46,"context_line":"            result \u003d self._match(\"\", environ)"},{"line_number":47,"context_line":"            return result[0], result[1]"},{"line_number":48,"context_line":"        return routes.Mapper.routematch(self, url, environ)"},{"line_number":49,"context_line":""},{"line_number":50,"context_line":"    def connect(self, *args, **kwargs):"},{"line_number":51,"context_line":"        # NOTE(inhye): Default the format part of a route to only accept json"},{"line_number":52,"context_line":"        #             so it doesn\u0027t eat all characters after a \u0027.\u0027"},{"line_number":53,"context_line":"        #             in the url."},{"line_number":54,"context_line":"        kwargs.setdefault(\u0027requirements\u0027, {})"},{"line_number":55,"context_line":"        if not kwargs[\u0027requirements\u0027].get(\u0027format\u0027):"},{"line_number":56,"context_line":"            kwargs[\u0027requirements\u0027][\u0027format\u0027] \u003d \u0027json\u0027"},{"line_number":57,"context_line":"        return routes.Mapper.connect(self, *args, **kwargs)"},{"line_number":58,"context_line":""},{"line_number":59,"context_line":""},{"line_number":60,"context_line":"class ProjectMapper(APIMapper):"}],"source_content_type":"text/x-python","patch_set":8,"id":"ebe3bc4c_cd4ed164","side":"PARENT","line":57,"range":{"start_line":43,"start_character":0,"end_line":57,"end_character":59},"in_reply_to":"7f5270cb_da5a4f4c","updated":"2025-10-31 10:21:10.000000000","message":"Marking as unresolved so reviewers can see this (will resolve again on merge)","commit_id":"9c2904da74a10bf1f610719d09e10676a89970b8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"86f2e59d683383d100addf8f413b42e0ac93b8cb","unresolved":false,"context_lines":[{"line_number":40,"context_line":"LOG \u003d logging.getLogger(__name__)"},{"line_number":41,"context_line":""},{"line_number":42,"context_line":""},{"line_number":43,"context_line":"class APIMapper(routes.Mapper):"},{"line_number":44,"context_line":"    def routematch(self, url\u003dNone, environ\u003dNone):"},{"line_number":45,"context_line":"        if url \u003d\u003d \"\":"},{"line_number":46,"context_line":"            result \u003d self._match(\"\", environ)"},{"line_number":47,"context_line":"            return result[0], result[1]"},{"line_number":48,"context_line":"        return routes.Mapper.routematch(self, url, environ)"},{"line_number":49,"context_line":""},{"line_number":50,"context_line":"    def connect(self, *args, **kwargs):"},{"line_number":51,"context_line":"        # NOTE(inhye): Default the format part of a route to only accept json"},{"line_number":52,"context_line":"        #             so it doesn\u0027t eat all characters after a \u0027.\u0027"},{"line_number":53,"context_line":"        #             in the url."},{"line_number":54,"context_line":"        kwargs.setdefault(\u0027requirements\u0027, {})"},{"line_number":55,"context_line":"        if not kwargs[\u0027requirements\u0027].get(\u0027format\u0027):"},{"line_number":56,"context_line":"            kwargs[\u0027requirements\u0027][\u0027format\u0027] \u003d \u0027json\u0027"},{"line_number":57,"context_line":"        return routes.Mapper.connect(self, *args, **kwargs)"},{"line_number":58,"context_line":""},{"line_number":59,"context_line":""},{"line_number":60,"context_line":"class ProjectMapper(APIMapper):"}],"source_content_type":"text/x-python","patch_set":8,"id":"e1db5a75_e1f6ab0e","side":"PARENT","line":57,"range":{"start_line":43,"start_character":0,"end_line":57,"end_character":59},"in_reply_to":"ebe3bc4c_cd4ed164","updated":"2025-11-07 13:32:16.000000000","message":"Done","commit_id":"9c2904da74a10bf1f610719d09e10676a89970b8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"96c10b44d9dc070e57b2ae729be968aede0390aa","unresolved":false,"context_lines":[{"line_number":57,"context_line":"        return routes.Mapper.connect(self, *args, **kwargs)"},{"line_number":58,"context_line":""},{"line_number":59,"context_line":""},{"line_number":60,"context_line":"class ProjectMapper(APIMapper):"},{"line_number":61,"context_line":"    def resource(self, member_name, collection_name, **kwargs):"},{"line_number":62,"context_line":"        \"\"\"Base resource path handler"},{"line_number":63,"context_line":""},{"line_number":64,"context_line":"        This method is compatible with resource paths that include a"},{"line_number":65,"context_line":"        project_id and those that don\u0027t. Including project_id in the URLs"},{"line_number":66,"context_line":"        was a legacy API requirement; and making API requests against"},{"line_number":67,"context_line":"        such endpoints won\u0027t work for users that don\u0027t belong to a"},{"line_number":68,"context_line":"        particular project."},{"line_number":69,"context_line":"        \"\"\""},{"line_number":70,"context_line":"        # NOTE: project_id parameter is only valid if its hex or hex + dashes"},{"line_number":71,"context_line":"        # (note, integers are a subset of this). This is required to handle"},{"line_number":72,"context_line":"        # our overlapping routes issues."},{"line_number":73,"context_line":"        project_id_regex \u003d CONF.project_id_regex"},{"line_number":74,"context_line":"        project_id_token \u003d \u0027{project_id:%s}\u0027 % project_id_regex"},{"line_number":75,"context_line":"        if \u0027parent_resource\u0027 not in kwargs:"},{"line_number":76,"context_line":"            kwargs[\u0027path_prefix\u0027] \u003d \u0027%s/\u0027 % project_id_token"},{"line_number":77,"context_line":"        else:"},{"line_number":78,"context_line":"            parent_resource \u003d kwargs[\u0027parent_resource\u0027]"},{"line_number":79,"context_line":"            p_collection \u003d parent_resource[\u0027collection_name\u0027]"},{"line_number":80,"context_line":"            p_member \u003d parent_resource[\u0027member_name\u0027]"},{"line_number":81,"context_line":"            kwargs[\u0027path_prefix\u0027] \u003d \u0027%s/%s/:%s_id\u0027 % (project_id_token,"},{"line_number":82,"context_line":"                                                      p_collection,"},{"line_number":83,"context_line":"                                                      p_member)"},{"line_number":84,"context_line":"        routes.Mapper.resource(self,"},{"line_number":85,"context_line":"                               member_name,"},{"line_number":86,"context_line":"                               collection_name,"},{"line_number":87,"context_line":"                               **kwargs)"},{"line_number":88,"context_line":""},{"line_number":89,"context_line":"        # Add additional routes without project_id."},{"line_number":90,"context_line":"        if \u0027parent_resource\u0027 not in kwargs:"},{"line_number":91,"context_line":"            del kwargs[\u0027path_prefix\u0027]"},{"line_number":92,"context_line":"        else:"},{"line_number":93,"context_line":"            parent_resource \u003d kwargs[\u0027parent_resource\u0027]"},{"line_number":94,"context_line":"            p_collection \u003d parent_resource[\u0027collection_name\u0027]"},{"line_number":95,"context_line":"            p_member \u003d parent_resource[\u0027member_name\u0027]"},{"line_number":96,"context_line":"            kwargs[\u0027path_prefix\u0027] \u003d \u0027%s/:%s_id\u0027 % (p_collection,"},{"line_number":97,"context_line":"                                                   p_member)"},{"line_number":98,"context_line":"        routes.Mapper.resource(self,"},{"line_number":99,"context_line":"                               member_name,"},{"line_number":100,"context_line":"                               collection_name,"},{"line_number":101,"context_line":"                               **kwargs)"},{"line_number":102,"context_line":""},{"line_number":103,"context_line":""},{"line_number":104,"context_line":"class APIRouter(base_wsgi.Router):"},{"line_number":105,"context_line":"    \"\"\"Routes requests on the API to the appropriate controller and method.\"\"\""}],"source_content_type":"text/x-python","patch_set":8,"id":"c9490c99_fc58e756","side":"PARENT","line":102,"range":{"start_line":60,"start_character":0,"end_line":102,"end_character":1},"updated":"2025-04-10 16:43:40.000000000","message":"This code is moved verbatim to `cinder/api/v3/router.py`\n\nhttps://review.opendev.org/c/openstack/cinder/+/916464/8/cinder/api/v3/router.py#56","commit_id":"9c2904da74a10bf1f610719d09e10676a89970b8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"bc1bfd7ef09ee164a029b699834cbc24cba7391c","unresolved":true,"context_lines":[{"line_number":57,"context_line":"        return routes.Mapper.connect(self, *args, **kwargs)"},{"line_number":58,"context_line":""},{"line_number":59,"context_line":""},{"line_number":60,"context_line":"class ProjectMapper(APIMapper):"},{"line_number":61,"context_line":"    def resource(self, member_name, collection_name, **kwargs):"},{"line_number":62,"context_line":"        \"\"\"Base resource path handler"},{"line_number":63,"context_line":""},{"line_number":64,"context_line":"        This method is compatible with resource paths that include a"},{"line_number":65,"context_line":"        project_id and those that don\u0027t. Including project_id in the URLs"},{"line_number":66,"context_line":"        was a legacy API requirement; and making API requests against"},{"line_number":67,"context_line":"        such endpoints won\u0027t work for users that don\u0027t belong to a"},{"line_number":68,"context_line":"        particular project."},{"line_number":69,"context_line":"        \"\"\""},{"line_number":70,"context_line":"        # NOTE: project_id parameter is only valid if its hex or hex + dashes"},{"line_number":71,"context_line":"        # (note, integers are a subset of this). This is required to handle"},{"line_number":72,"context_line":"        # our overlapping routes issues."},{"line_number":73,"context_line":"        project_id_regex \u003d CONF.project_id_regex"},{"line_number":74,"context_line":"        project_id_token \u003d \u0027{project_id:%s}\u0027 % project_id_regex"},{"line_number":75,"context_line":"        if \u0027parent_resource\u0027 not in kwargs:"},{"line_number":76,"context_line":"            kwargs[\u0027path_prefix\u0027] \u003d \u0027%s/\u0027 % project_id_token"},{"line_number":77,"context_line":"        else:"},{"line_number":78,"context_line":"            parent_resource \u003d kwargs[\u0027parent_resource\u0027]"},{"line_number":79,"context_line":"            p_collection \u003d parent_resource[\u0027collection_name\u0027]"},{"line_number":80,"context_line":"            p_member \u003d parent_resource[\u0027member_name\u0027]"},{"line_number":81,"context_line":"            kwargs[\u0027path_prefix\u0027] \u003d \u0027%s/%s/:%s_id\u0027 % (project_id_token,"},{"line_number":82,"context_line":"                                                      p_collection,"},{"line_number":83,"context_line":"                                                      p_member)"},{"line_number":84,"context_line":"        routes.Mapper.resource(self,"},{"line_number":85,"context_line":"                               member_name,"},{"line_number":86,"context_line":"                               collection_name,"},{"line_number":87,"context_line":"                               **kwargs)"},{"line_number":88,"context_line":""},{"line_number":89,"context_line":"        # Add additional routes without project_id."},{"line_number":90,"context_line":"        if \u0027parent_resource\u0027 not in kwargs:"},{"line_number":91,"context_line":"            del kwargs[\u0027path_prefix\u0027]"},{"line_number":92,"context_line":"        else:"},{"line_number":93,"context_line":"            parent_resource \u003d kwargs[\u0027parent_resource\u0027]"},{"line_number":94,"context_line":"            p_collection \u003d parent_resource[\u0027collection_name\u0027]"},{"line_number":95,"context_line":"            p_member \u003d parent_resource[\u0027member_name\u0027]"},{"line_number":96,"context_line":"            kwargs[\u0027path_prefix\u0027] \u003d \u0027%s/:%s_id\u0027 % (p_collection,"},{"line_number":97,"context_line":"                                                   p_member)"},{"line_number":98,"context_line":"        routes.Mapper.resource(self,"},{"line_number":99,"context_line":"                               member_name,"},{"line_number":100,"context_line":"                               collection_name,"},{"line_number":101,"context_line":"                               **kwargs)"},{"line_number":102,"context_line":""},{"line_number":103,"context_line":""},{"line_number":104,"context_line":"class APIRouter(base_wsgi.Router):"},{"line_number":105,"context_line":"    \"\"\"Routes requests on the API to the appropriate controller and method.\"\"\""}],"source_content_type":"text/x-python","patch_set":8,"id":"d0c173cb_6f435f95","side":"PARENT","line":102,"range":{"start_line":60,"start_character":0,"end_line":102,"end_character":1},"in_reply_to":"c9490c99_fc58e756","updated":"2025-10-31 10:21:10.000000000","message":"Marking as unresolved so reviewers can see this (will resolve again on merge)","commit_id":"9c2904da74a10bf1f610719d09e10676a89970b8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"86f2e59d683383d100addf8f413b42e0ac93b8cb","unresolved":false,"context_lines":[{"line_number":57,"context_line":"        return routes.Mapper.connect(self, *args, **kwargs)"},{"line_number":58,"context_line":""},{"line_number":59,"context_line":""},{"line_number":60,"context_line":"class ProjectMapper(APIMapper):"},{"line_number":61,"context_line":"    def resource(self, member_name, collection_name, **kwargs):"},{"line_number":62,"context_line":"        \"\"\"Base resource path handler"},{"line_number":63,"context_line":""},{"line_number":64,"context_line":"        This method is compatible with resource paths that include a"},{"line_number":65,"context_line":"        project_id and those that don\u0027t. Including project_id in the URLs"},{"line_number":66,"context_line":"        was a legacy API requirement; and making API requests against"},{"line_number":67,"context_line":"        such endpoints won\u0027t work for users that don\u0027t belong to a"},{"line_number":68,"context_line":"        particular project."},{"line_number":69,"context_line":"        \"\"\""},{"line_number":70,"context_line":"        # NOTE: project_id parameter is only valid if its hex or hex + dashes"},{"line_number":71,"context_line":"        # (note, integers are a subset of this). This is required to handle"},{"line_number":72,"context_line":"        # our overlapping routes issues."},{"line_number":73,"context_line":"        project_id_regex \u003d CONF.project_id_regex"},{"line_number":74,"context_line":"        project_id_token \u003d \u0027{project_id:%s}\u0027 % project_id_regex"},{"line_number":75,"context_line":"        if \u0027parent_resource\u0027 not in kwargs:"},{"line_number":76,"context_line":"            kwargs[\u0027path_prefix\u0027] \u003d \u0027%s/\u0027 % project_id_token"},{"line_number":77,"context_line":"        else:"},{"line_number":78,"context_line":"            parent_resource \u003d kwargs[\u0027parent_resource\u0027]"},{"line_number":79,"context_line":"            p_collection \u003d parent_resource[\u0027collection_name\u0027]"},{"line_number":80,"context_line":"            p_member \u003d parent_resource[\u0027member_name\u0027]"},{"line_number":81,"context_line":"            kwargs[\u0027path_prefix\u0027] \u003d \u0027%s/%s/:%s_id\u0027 % (project_id_token,"},{"line_number":82,"context_line":"                                                      p_collection,"},{"line_number":83,"context_line":"                                                      p_member)"},{"line_number":84,"context_line":"        routes.Mapper.resource(self,"},{"line_number":85,"context_line":"                               member_name,"},{"line_number":86,"context_line":"                               collection_name,"},{"line_number":87,"context_line":"                               **kwargs)"},{"line_number":88,"context_line":""},{"line_number":89,"context_line":"        # Add additional routes without project_id."},{"line_number":90,"context_line":"        if \u0027parent_resource\u0027 not in kwargs:"},{"line_number":91,"context_line":"            del kwargs[\u0027path_prefix\u0027]"},{"line_number":92,"context_line":"        else:"},{"line_number":93,"context_line":"            parent_resource \u003d kwargs[\u0027parent_resource\u0027]"},{"line_number":94,"context_line":"            p_collection \u003d parent_resource[\u0027collection_name\u0027]"},{"line_number":95,"context_line":"            p_member \u003d parent_resource[\u0027member_name\u0027]"},{"line_number":96,"context_line":"            kwargs[\u0027path_prefix\u0027] \u003d \u0027%s/:%s_id\u0027 % (p_collection,"},{"line_number":97,"context_line":"                                                   p_member)"},{"line_number":98,"context_line":"        routes.Mapper.resource(self,"},{"line_number":99,"context_line":"                               member_name,"},{"line_number":100,"context_line":"                               collection_name,"},{"line_number":101,"context_line":"                               **kwargs)"},{"line_number":102,"context_line":""},{"line_number":103,"context_line":""},{"line_number":104,"context_line":"class APIRouter(base_wsgi.Router):"},{"line_number":105,"context_line":"    \"\"\"Routes requests on the API to the appropriate controller and method.\"\"\""}],"source_content_type":"text/x-python","patch_set":8,"id":"c747f0da_5bc002aa","side":"PARENT","line":102,"range":{"start_line":60,"start_character":0,"end_line":102,"end_character":1},"in_reply_to":"d0c173cb_6f435f95","updated":"2025-11-07 13:32:16.000000000","message":"Done","commit_id":"9c2904da74a10bf1f610719d09e10676a89970b8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"96c10b44d9dc070e57b2ae729be968aede0390aa","unresolved":false,"context_lines":[{"line_number":101,"context_line":"                               **kwargs)"},{"line_number":102,"context_line":""},{"line_number":103,"context_line":""},{"line_number":104,"context_line":"class APIRouter(base_wsgi.Router):"},{"line_number":105,"context_line":"    \"\"\"Routes requests on the API to the appropriate controller and method.\"\"\""},{"line_number":106,"context_line":"    ExtensionManager \u003d None  # override in subclasses"},{"line_number":107,"context_line":""},{"line_number":108,"context_line":"    @classmethod"},{"line_number":109,"context_line":"    def factory(cls, global_config, **local_config):"},{"line_number":110,"context_line":"        \"\"\"Simple paste factory, :class:`cinder.wsgi.Router` doesn\u0027t have.\"\"\""},{"line_number":111,"context_line":"        return cls()"},{"line_number":112,"context_line":""},{"line_number":113,"context_line":"    def __init__(self, ext_mgr\u003dNone):"},{"line_number":114,"context_line":"        if ext_mgr is None:"},{"line_number":115,"context_line":"            if self.ExtensionManager:"},{"line_number":116,"context_line":"                ext_mgr \u003d self.ExtensionManager()"},{"line_number":117,"context_line":"            else:"},{"line_number":118,"context_line":"                raise Exception(_(\"Must specify an ExtensionManager class\"))"},{"line_number":119,"context_line":""},{"line_number":120,"context_line":"        mapper \u003d ProjectMapper()"},{"line_number":121,"context_line":"        self.resources \u003d {}"},{"line_number":122,"context_line":"        self._setup_routes(mapper, ext_mgr)"},{"line_number":123,"context_line":"        self._setup_ext_routes(mapper, ext_mgr)"},{"line_number":124,"context_line":"        self._setup_extensions(ext_mgr)"},{"line_number":125,"context_line":"        super(APIRouter, self).__init__(mapper)"},{"line_number":126,"context_line":""},{"line_number":127,"context_line":"    def _setup_ext_routes(self, mapper, ext_mgr):"},{"line_number":128,"context_line":"        for resource in ext_mgr.get_resources():"},{"line_number":129,"context_line":"            LOG.debug(\u0027Extended resource: %s\u0027,"},{"line_number":130,"context_line":"                      resource.collection)"},{"line_number":131,"context_line":""},{"line_number":132,"context_line":"            wsgi_resource \u003d wsgi.Resource(resource.controller)"},{"line_number":133,"context_line":"            self.resources[resource.collection] \u003d wsgi_resource"},{"line_number":134,"context_line":"            kargs \u003d dict("},{"line_number":135,"context_line":"                controller\u003dwsgi_resource,"},{"line_number":136,"context_line":"                collection\u003dresource.collection_actions,"},{"line_number":137,"context_line":"                member\u003dresource.member_actions)"},{"line_number":138,"context_line":""},{"line_number":139,"context_line":"            if resource.parent:"},{"line_number":140,"context_line":"                kargs[\u0027parent_resource\u0027] \u003d resource.parent"},{"line_number":141,"context_line":""},{"line_number":142,"context_line":"            mapper.resource(resource.collection, resource.collection, **kargs)"},{"line_number":143,"context_line":""},{"line_number":144,"context_line":"            if resource.custom_routes_fn:"},{"line_number":145,"context_line":"                resource.custom_routes_fn(mapper, wsgi_resource)"},{"line_number":146,"context_line":""},{"line_number":147,"context_line":"    def _setup_extensions(self, ext_mgr):"},{"line_number":148,"context_line":"        for extension in ext_mgr.get_controller_extensions():"},{"line_number":149,"context_line":"            collection \u003d extension.collection"},{"line_number":150,"context_line":"            controller \u003d extension.controller"},{"line_number":151,"context_line":""},{"line_number":152,"context_line":"            if collection not in self.resources:"},{"line_number":153,"context_line":"                LOG.warning(\u0027Extension %(ext_name)s: Cannot extend \u0027"},{"line_number":154,"context_line":"                            \u0027resource %(collection)s: No such resource\u0027,"},{"line_number":155,"context_line":"                            {\u0027ext_name\u0027: extension.extension.name,"},{"line_number":156,"context_line":"                             \u0027collection\u0027: collection})"},{"line_number":157,"context_line":"                continue"},{"line_number":158,"context_line":""},{"line_number":159,"context_line":"            LOG.debug(\u0027Extension %(ext_name)s extending resource: \u0027"},{"line_number":160,"context_line":"                      \u0027%(collection)s\u0027,"},{"line_number":161,"context_line":"                      {\u0027ext_name\u0027: extension.extension.name,"},{"line_number":162,"context_line":"                       \u0027collection\u0027: collection})"},{"line_number":163,"context_line":""},{"line_number":164,"context_line":"            resource \u003d self.resources[collection]"},{"line_number":165,"context_line":"            resource.register_actions(controller)"},{"line_number":166,"context_line":"            resource.register_extensions(controller)"},{"line_number":167,"context_line":""},{"line_number":168,"context_line":"    def _setup_routes(self, mapper, ext_mgr):"},{"line_number":169,"context_line":"        raise NotImplementedError"}],"source_content_type":"text/x-python","patch_set":8,"id":"a43d486a_3f62f89c","side":"PARENT","line":169,"range":{"start_line":104,"start_character":0,"end_line":169,"end_character":33},"updated":"2025-04-10 16:43:40.000000000","message":"This is also moved _mostly_ verbatim to `cinder/api/v3/router.py`.\n\nhttps://review.opendev.org/c/openstack/cinder/+/916464/8/cinder/api/v3/router.py#100\n\nThe only difference if we have an implementation of `_setup_routes` there so we don\u0027t need out stub one from here *and* the `ExtensionManager` class is always set so we don\u0027t need the `if self.ExtensionManager:` logic from `__init__`.","commit_id":"9c2904da74a10bf1f610719d09e10676a89970b8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"86f2e59d683383d100addf8f413b42e0ac93b8cb","unresolved":false,"context_lines":[{"line_number":101,"context_line":"                               **kwargs)"},{"line_number":102,"context_line":""},{"line_number":103,"context_line":""},{"line_number":104,"context_line":"class APIRouter(base_wsgi.Router):"},{"line_number":105,"context_line":"    \"\"\"Routes requests on the API to the appropriate controller and method.\"\"\""},{"line_number":106,"context_line":"    ExtensionManager \u003d None  # override in subclasses"},{"line_number":107,"context_line":""},{"line_number":108,"context_line":"    @classmethod"},{"line_number":109,"context_line":"    def factory(cls, global_config, **local_config):"},{"line_number":110,"context_line":"        \"\"\"Simple paste factory, :class:`cinder.wsgi.Router` doesn\u0027t have.\"\"\""},{"line_number":111,"context_line":"        return cls()"},{"line_number":112,"context_line":""},{"line_number":113,"context_line":"    def __init__(self, ext_mgr\u003dNone):"},{"line_number":114,"context_line":"        if ext_mgr is None:"},{"line_number":115,"context_line":"            if self.ExtensionManager:"},{"line_number":116,"context_line":"                ext_mgr \u003d self.ExtensionManager()"},{"line_number":117,"context_line":"            else:"},{"line_number":118,"context_line":"                raise Exception(_(\"Must specify an ExtensionManager class\"))"},{"line_number":119,"context_line":""},{"line_number":120,"context_line":"        mapper \u003d ProjectMapper()"},{"line_number":121,"context_line":"        self.resources \u003d {}"},{"line_number":122,"context_line":"        self._setup_routes(mapper, ext_mgr)"},{"line_number":123,"context_line":"        self._setup_ext_routes(mapper, ext_mgr)"},{"line_number":124,"context_line":"        self._setup_extensions(ext_mgr)"},{"line_number":125,"context_line":"        super(APIRouter, self).__init__(mapper)"},{"line_number":126,"context_line":""},{"line_number":127,"context_line":"    def _setup_ext_routes(self, mapper, ext_mgr):"},{"line_number":128,"context_line":"        for resource in ext_mgr.get_resources():"},{"line_number":129,"context_line":"            LOG.debug(\u0027Extended resource: %s\u0027,"},{"line_number":130,"context_line":"                      resource.collection)"},{"line_number":131,"context_line":""},{"line_number":132,"context_line":"            wsgi_resource \u003d wsgi.Resource(resource.controller)"},{"line_number":133,"context_line":"            self.resources[resource.collection] \u003d wsgi_resource"},{"line_number":134,"context_line":"            kargs \u003d dict("},{"line_number":135,"context_line":"                controller\u003dwsgi_resource,"},{"line_number":136,"context_line":"                collection\u003dresource.collection_actions,"},{"line_number":137,"context_line":"                member\u003dresource.member_actions)"},{"line_number":138,"context_line":""},{"line_number":139,"context_line":"            if resource.parent:"},{"line_number":140,"context_line":"                kargs[\u0027parent_resource\u0027] \u003d resource.parent"},{"line_number":141,"context_line":""},{"line_number":142,"context_line":"            mapper.resource(resource.collection, resource.collection, **kargs)"},{"line_number":143,"context_line":""},{"line_number":144,"context_line":"            if resource.custom_routes_fn:"},{"line_number":145,"context_line":"                resource.custom_routes_fn(mapper, wsgi_resource)"},{"line_number":146,"context_line":""},{"line_number":147,"context_line":"    def _setup_extensions(self, ext_mgr):"},{"line_number":148,"context_line":"        for extension in ext_mgr.get_controller_extensions():"},{"line_number":149,"context_line":"            collection \u003d extension.collection"},{"line_number":150,"context_line":"            controller \u003d extension.controller"},{"line_number":151,"context_line":""},{"line_number":152,"context_line":"            if collection not in self.resources:"},{"line_number":153,"context_line":"                LOG.warning(\u0027Extension %(ext_name)s: Cannot extend \u0027"},{"line_number":154,"context_line":"                            \u0027resource %(collection)s: No such resource\u0027,"},{"line_number":155,"context_line":"                            {\u0027ext_name\u0027: extension.extension.name,"},{"line_number":156,"context_line":"                             \u0027collection\u0027: collection})"},{"line_number":157,"context_line":"                continue"},{"line_number":158,"context_line":""},{"line_number":159,"context_line":"            LOG.debug(\u0027Extension %(ext_name)s extending resource: \u0027"},{"line_number":160,"context_line":"                      \u0027%(collection)s\u0027,"},{"line_number":161,"context_line":"                      {\u0027ext_name\u0027: extension.extension.name,"},{"line_number":162,"context_line":"                       \u0027collection\u0027: collection})"},{"line_number":163,"context_line":""},{"line_number":164,"context_line":"            resource \u003d self.resources[collection]"},{"line_number":165,"context_line":"            resource.register_actions(controller)"},{"line_number":166,"context_line":"            resource.register_extensions(controller)"},{"line_number":167,"context_line":""},{"line_number":168,"context_line":"    def _setup_routes(self, mapper, ext_mgr):"},{"line_number":169,"context_line":"        raise NotImplementedError"}],"source_content_type":"text/x-python","patch_set":8,"id":"50081494_27405e42","side":"PARENT","line":169,"range":{"start_line":104,"start_character":0,"end_line":169,"end_character":33},"in_reply_to":"23756300_3a904c50","updated":"2025-11-07 13:32:16.000000000","message":"Done","commit_id":"9c2904da74a10bf1f610719d09e10676a89970b8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"bc1bfd7ef09ee164a029b699834cbc24cba7391c","unresolved":true,"context_lines":[{"line_number":101,"context_line":"                               **kwargs)"},{"line_number":102,"context_line":""},{"line_number":103,"context_line":""},{"line_number":104,"context_line":"class APIRouter(base_wsgi.Router):"},{"line_number":105,"context_line":"    \"\"\"Routes requests on the API to the appropriate controller and method.\"\"\""},{"line_number":106,"context_line":"    ExtensionManager \u003d None  # override in subclasses"},{"line_number":107,"context_line":""},{"line_number":108,"context_line":"    @classmethod"},{"line_number":109,"context_line":"    def factory(cls, global_config, **local_config):"},{"line_number":110,"context_line":"        \"\"\"Simple paste factory, :class:`cinder.wsgi.Router` doesn\u0027t have.\"\"\""},{"line_number":111,"context_line":"        return cls()"},{"line_number":112,"context_line":""},{"line_number":113,"context_line":"    def __init__(self, ext_mgr\u003dNone):"},{"line_number":114,"context_line":"        if ext_mgr is None:"},{"line_number":115,"context_line":"            if self.ExtensionManager:"},{"line_number":116,"context_line":"                ext_mgr \u003d self.ExtensionManager()"},{"line_number":117,"context_line":"            else:"},{"line_number":118,"context_line":"                raise Exception(_(\"Must specify an ExtensionManager class\"))"},{"line_number":119,"context_line":""},{"line_number":120,"context_line":"        mapper \u003d ProjectMapper()"},{"line_number":121,"context_line":"        self.resources \u003d {}"},{"line_number":122,"context_line":"        self._setup_routes(mapper, ext_mgr)"},{"line_number":123,"context_line":"        self._setup_ext_routes(mapper, ext_mgr)"},{"line_number":124,"context_line":"        self._setup_extensions(ext_mgr)"},{"line_number":125,"context_line":"        super(APIRouter, self).__init__(mapper)"},{"line_number":126,"context_line":""},{"line_number":127,"context_line":"    def _setup_ext_routes(self, mapper, ext_mgr):"},{"line_number":128,"context_line":"        for resource in ext_mgr.get_resources():"},{"line_number":129,"context_line":"            LOG.debug(\u0027Extended resource: %s\u0027,"},{"line_number":130,"context_line":"                      resource.collection)"},{"line_number":131,"context_line":""},{"line_number":132,"context_line":"            wsgi_resource \u003d wsgi.Resource(resource.controller)"},{"line_number":133,"context_line":"            self.resources[resource.collection] \u003d wsgi_resource"},{"line_number":134,"context_line":"            kargs \u003d dict("},{"line_number":135,"context_line":"                controller\u003dwsgi_resource,"},{"line_number":136,"context_line":"                collection\u003dresource.collection_actions,"},{"line_number":137,"context_line":"                member\u003dresource.member_actions)"},{"line_number":138,"context_line":""},{"line_number":139,"context_line":"            if resource.parent:"},{"line_number":140,"context_line":"                kargs[\u0027parent_resource\u0027] \u003d resource.parent"},{"line_number":141,"context_line":""},{"line_number":142,"context_line":"            mapper.resource(resource.collection, resource.collection, **kargs)"},{"line_number":143,"context_line":""},{"line_number":144,"context_line":"            if resource.custom_routes_fn:"},{"line_number":145,"context_line":"                resource.custom_routes_fn(mapper, wsgi_resource)"},{"line_number":146,"context_line":""},{"line_number":147,"context_line":"    def _setup_extensions(self, ext_mgr):"},{"line_number":148,"context_line":"        for extension in ext_mgr.get_controller_extensions():"},{"line_number":149,"context_line":"            collection \u003d extension.collection"},{"line_number":150,"context_line":"            controller \u003d extension.controller"},{"line_number":151,"context_line":""},{"line_number":152,"context_line":"            if collection not in self.resources:"},{"line_number":153,"context_line":"                LOG.warning(\u0027Extension %(ext_name)s: Cannot extend \u0027"},{"line_number":154,"context_line":"                            \u0027resource %(collection)s: No such resource\u0027,"},{"line_number":155,"context_line":"                            {\u0027ext_name\u0027: extension.extension.name,"},{"line_number":156,"context_line":"                             \u0027collection\u0027: collection})"},{"line_number":157,"context_line":"                continue"},{"line_number":158,"context_line":""},{"line_number":159,"context_line":"            LOG.debug(\u0027Extension %(ext_name)s extending resource: \u0027"},{"line_number":160,"context_line":"                      \u0027%(collection)s\u0027,"},{"line_number":161,"context_line":"                      {\u0027ext_name\u0027: extension.extension.name,"},{"line_number":162,"context_line":"                       \u0027collection\u0027: collection})"},{"line_number":163,"context_line":""},{"line_number":164,"context_line":"            resource \u003d self.resources[collection]"},{"line_number":165,"context_line":"            resource.register_actions(controller)"},{"line_number":166,"context_line":"            resource.register_extensions(controller)"},{"line_number":167,"context_line":""},{"line_number":168,"context_line":"    def _setup_routes(self, mapper, ext_mgr):"},{"line_number":169,"context_line":"        raise NotImplementedError"}],"source_content_type":"text/x-python","patch_set":8,"id":"23756300_3a904c50","side":"PARENT","line":169,"range":{"start_line":104,"start_character":0,"end_line":169,"end_character":33},"in_reply_to":"a43d486a_3f62f89c","updated":"2025-10-31 10:21:10.000000000","message":"Marking as unresolved so reviewers can see this (will resolve again on merge)","commit_id":"9c2904da74a10bf1f610719d09e10676a89970b8"}],"cinder/api/versions.py":[{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"96c10b44d9dc070e57b2ae729be968aede0390aa","unresolved":false,"context_lines":[{"line_number":58,"context_line":""},{"line_number":59,"context_line":"    ExtensionManager \u003d extensions.ExtensionManager"},{"line_number":60,"context_line":""},{"line_number":61,"context_line":"    def _setup_routes(self, mapper, ext_mgr):"},{"line_number":62,"context_line":"        self.resources[\u0027versions\u0027] \u003d create_resource()"},{"line_number":63,"context_line":"        mapper.connect(\u0027versions\u0027, \u0027/\u0027,"},{"line_number":64,"context_line":"                       controller\u003dself.resources[\u0027versions\u0027],"},{"line_number":65,"context_line":"                       action\u003d\u0027all\u0027)"},{"line_number":66,"context_line":"        mapper.redirect(\u0027\u0027, \u0027/\u0027)"},{"line_number":67,"context_line":""},{"line_number":68,"context_line":"    def _setup_ext_routes(self, mapper, ext_mgr):"},{"line_number":69,"context_line":"        # NOTE(mriedem): The version router doesn\u0027t care about extensions."},{"line_number":70,"context_line":"        pass"},{"line_number":71,"context_line":""},{"line_number":72,"context_line":"    # NOTE (jose-castro-leon): Avoid to register extensions"},{"line_number":73,"context_line":"    # on the versions router, the versions router does not offer"},{"line_number":74,"context_line":"    # resources to be extended."},{"line_number":75,"context_line":"    def _setup_extensions(self, ext_mgr):"},{"line_number":76,"context_line":"        pass"},{"line_number":77,"context_line":""},{"line_number":78,"context_line":""},{"line_number":79,"context_line":"class VersionsController(wsgi.Controller):"}],"source_content_type":"text/x-python","patch_set":8,"id":"a5b79890_b569f053","side":"PARENT","line":76,"range":{"start_line":61,"start_character":0,"end_line":76,"end_character":12},"updated":"2025-04-10 16:43:40.000000000","message":"We can remove all of these because the latter two were effectively no-ops, and we no longer call them because we\u0027re overriding `__init__` instead (which is where this was being called from, as seen here https://review.opendev.org/c/openstack/cinder/+/916464/8/cinder/api/openstack/__init__.py#b114) while the other one is now done in `__init__` directly.","commit_id":"9c2904da74a10bf1f610719d09e10676a89970b8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"bc1bfd7ef09ee164a029b699834cbc24cba7391c","unresolved":true,"context_lines":[{"line_number":58,"context_line":""},{"line_number":59,"context_line":"    ExtensionManager \u003d extensions.ExtensionManager"},{"line_number":60,"context_line":""},{"line_number":61,"context_line":"    def _setup_routes(self, mapper, ext_mgr):"},{"line_number":62,"context_line":"        self.resources[\u0027versions\u0027] \u003d create_resource()"},{"line_number":63,"context_line":"        mapper.connect(\u0027versions\u0027, \u0027/\u0027,"},{"line_number":64,"context_line":"                       controller\u003dself.resources[\u0027versions\u0027],"},{"line_number":65,"context_line":"                       action\u003d\u0027all\u0027)"},{"line_number":66,"context_line":"        mapper.redirect(\u0027\u0027, \u0027/\u0027)"},{"line_number":67,"context_line":""},{"line_number":68,"context_line":"    def _setup_ext_routes(self, mapper, ext_mgr):"},{"line_number":69,"context_line":"        # NOTE(mriedem): The version router doesn\u0027t care about extensions."},{"line_number":70,"context_line":"        pass"},{"line_number":71,"context_line":""},{"line_number":72,"context_line":"    # NOTE (jose-castro-leon): Avoid to register extensions"},{"line_number":73,"context_line":"    # on the versions router, the versions router does not offer"},{"line_number":74,"context_line":"    # resources to be extended."},{"line_number":75,"context_line":"    def _setup_extensions(self, ext_mgr):"},{"line_number":76,"context_line":"        pass"},{"line_number":77,"context_line":""},{"line_number":78,"context_line":""},{"line_number":79,"context_line":"class VersionsController(wsgi.Controller):"}],"source_content_type":"text/x-python","patch_set":8,"id":"e860ab85_7eb9388a","side":"PARENT","line":76,"range":{"start_line":61,"start_character":0,"end_line":76,"end_character":12},"in_reply_to":"a5b79890_b569f053","updated":"2025-10-31 10:21:10.000000000","message":"Marking as unresolved so reviewers can see this (will resolve again on merge)","commit_id":"9c2904da74a10bf1f610719d09e10676a89970b8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"86f2e59d683383d100addf8f413b42e0ac93b8cb","unresolved":false,"context_lines":[{"line_number":58,"context_line":""},{"line_number":59,"context_line":"    ExtensionManager \u003d extensions.ExtensionManager"},{"line_number":60,"context_line":""},{"line_number":61,"context_line":"    def _setup_routes(self, mapper, ext_mgr):"},{"line_number":62,"context_line":"        self.resources[\u0027versions\u0027] \u003d create_resource()"},{"line_number":63,"context_line":"        mapper.connect(\u0027versions\u0027, \u0027/\u0027,"},{"line_number":64,"context_line":"                       controller\u003dself.resources[\u0027versions\u0027],"},{"line_number":65,"context_line":"                       action\u003d\u0027all\u0027)"},{"line_number":66,"context_line":"        mapper.redirect(\u0027\u0027, \u0027/\u0027)"},{"line_number":67,"context_line":""},{"line_number":68,"context_line":"    def _setup_ext_routes(self, mapper, ext_mgr):"},{"line_number":69,"context_line":"        # NOTE(mriedem): The version router doesn\u0027t care about extensions."},{"line_number":70,"context_line":"        pass"},{"line_number":71,"context_line":""},{"line_number":72,"context_line":"    # NOTE (jose-castro-leon): Avoid to register extensions"},{"line_number":73,"context_line":"    # on the versions router, the versions router does not offer"},{"line_number":74,"context_line":"    # resources to be extended."},{"line_number":75,"context_line":"    def _setup_extensions(self, ext_mgr):"},{"line_number":76,"context_line":"        pass"},{"line_number":77,"context_line":""},{"line_number":78,"context_line":""},{"line_number":79,"context_line":"class VersionsController(wsgi.Controller):"}],"source_content_type":"text/x-python","patch_set":8,"id":"2a59748d_0706c209","side":"PARENT","line":76,"range":{"start_line":61,"start_character":0,"end_line":76,"end_character":12},"in_reply_to":"e860ab85_7eb9388a","updated":"2025-11-07 13:32:16.000000000","message":"Done","commit_id":"9c2904da74a10bf1f610719d09e10676a89970b8"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"96c10b44d9dc070e57b2ae729be968aede0390aa","unresolved":false,"context_lines":[{"line_number":58,"context_line":"        mapper.redirect(\u0027\u0027, \u0027/\u0027)"},{"line_number":59,"context_line":"        super().__init__(mapper)"},{"line_number":60,"context_line":""},{"line_number":61,"context_line":"    @classmethod"},{"line_number":62,"context_line":"    def factory(cls, global_config, **local_config):"},{"line_number":63,"context_line":"        \"\"\"Simple paste factory."},{"line_number":64,"context_line":""},{"line_number":65,"context_line":"        :class:`oslo_service.wsgi.Router` doesn\u0027t have this."},{"line_number":66,"context_line":"        \"\"\""},{"line_number":67,"context_line":"        return cls()"},{"line_number":68,"context_line":""},{"line_number":69,"context_line":""},{"line_number":70,"context_line":"class VersionsController(wsgi.Controller):"}],"source_content_type":"text/x-python","patch_set":8,"id":"3c35585c_d19daa32","line":67,"range":{"start_line":61,"start_character":0,"end_line":67,"end_character":20},"updated":"2025-04-10 16:43:40.000000000","message":"We\u0027re now deriving from the `oslo_service` router directly and this is the only thing that we need to duplicate from `cinder.api.v3.router.APIRouter`. Yes, we could keep a base class for that, but for the sake of 5 lines it\u0027s not worth it.","commit_id":"5c4f29320389c00ba39dfc25067a4b56ec6eec68"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"bc1bfd7ef09ee164a029b699834cbc24cba7391c","unresolved":true,"context_lines":[{"line_number":58,"context_line":"        mapper.redirect(\u0027\u0027, \u0027/\u0027)"},{"line_number":59,"context_line":"        super().__init__(mapper)"},{"line_number":60,"context_line":""},{"line_number":61,"context_line":"    @classmethod"},{"line_number":62,"context_line":"    def factory(cls, global_config, **local_config):"},{"line_number":63,"context_line":"        \"\"\"Simple paste factory."},{"line_number":64,"context_line":""},{"line_number":65,"context_line":"        :class:`oslo_service.wsgi.Router` doesn\u0027t have this."},{"line_number":66,"context_line":"        \"\"\""},{"line_number":67,"context_line":"        return cls()"},{"line_number":68,"context_line":""},{"line_number":69,"context_line":""},{"line_number":70,"context_line":"class VersionsController(wsgi.Controller):"}],"source_content_type":"text/x-python","patch_set":8,"id":"9aa9ecdc_a1d9c584","line":67,"range":{"start_line":61,"start_character":0,"end_line":67,"end_character":20},"in_reply_to":"3c35585c_d19daa32","updated":"2025-10-31 10:21:10.000000000","message":"Marking as unresolved so reviewers can see this (will resolve again on merge)","commit_id":"5c4f29320389c00ba39dfc25067a4b56ec6eec68"},{"author":{"_account_id":15334,"name":"Stephen Finucane","display_name":"stephenfin","email":"stephenfin@redhat.com","username":"sfinucan"},"change_message_id":"86f2e59d683383d100addf8f413b42e0ac93b8cb","unresolved":false,"context_lines":[{"line_number":58,"context_line":"        mapper.redirect(\u0027\u0027, \u0027/\u0027)"},{"line_number":59,"context_line":"        super().__init__(mapper)"},{"line_number":60,"context_line":""},{"line_number":61,"context_line":"    @classmethod"},{"line_number":62,"context_line":"    def factory(cls, global_config, **local_config):"},{"line_number":63,"context_line":"        \"\"\"Simple paste factory."},{"line_number":64,"context_line":""},{"line_number":65,"context_line":"        :class:`oslo_service.wsgi.Router` doesn\u0027t have this."},{"line_number":66,"context_line":"        \"\"\""},{"line_number":67,"context_line":"        return cls()"},{"line_number":68,"context_line":""},{"line_number":69,"context_line":""},{"line_number":70,"context_line":"class VersionsController(wsgi.Controller):"}],"source_content_type":"text/x-python","patch_set":8,"id":"8f2793df_16e467bb","line":67,"range":{"start_line":61,"start_character":0,"end_line":67,"end_character":20},"in_reply_to":"9aa9ecdc_a1d9c584","updated":"2025-11-07 13:32:16.000000000","message":"Done","commit_id":"5c4f29320389c00ba39dfc25067a4b56ec6eec68"}]}
