)]}'
{"rally/plugins/openstack/services/identity.py":[{"author":{"_account_id":10475,"name":"Alexander Maretskiy","email":"amaretskiy@mirantis.com","username":"maretskiy"},"change_message_id":"c55bcb8aacc99661508c8049ea219c7deed577ff","unresolved":false,"context_lines":[{"line_number":67,"context_line":"                            implementations that don\u0027t support domains this"},{"line_number":68,"context_line":"                            argument must be None or \u0027Default\u0027."},{"line_number":69,"context_line":"        \"\"\""},{"line_number":70,"context_line":"        return self._impl.create_user(username\u003dusername,"},{"line_number":71,"context_line":"                                      password\u003dpassword,"},{"line_number":72,"context_line":"                                      email\u003demail,"},{"line_number":73,"context_line":"                                      project_id\u003dproject_id,"}],"source_content_type":"text/x-python","patch_set":14,"id":"dab17558_b1dee50e","line":70,"updated":"2016-05-16 12:49:34.000000000","message":"Let we have:\n\n svc \u003d Identity(clients) # v2\n for i in range(5):\n     svc.create_user(\u003csome args\u003e)\n svc.list_users()\n\nAs result, we would have these atomic actions:\n\n (keystone_v2.create_user, ...), # L136\n (keystone_v2.create_user, ...),\n (keystone_v2.create_user, ...),\n (keystone_v2.create_user, ...),\n (keystone_v2.create_user. ...),\n (keystone_v2.list_users. ...)\n\nThe question is how to mute \"create_user\" and get this instead:\n\n (keystone_v2.create_5_users. ...),\n (keystone_v2.list_users. ...)","commit_id":"489e549fa09fd0c972b18fd70c522903cf04b4e7"},{"author":{"_account_id":10475,"name":"Alexander Maretskiy","email":"amaretskiy@mirantis.com","username":"maretskiy"},"change_message_id":"290b2fdacdca63e9eefe3134b882647a82c27af9","unresolved":false,"context_lines":[{"line_number":67,"context_line":"                            implementations that don\u0027t support domains this"},{"line_number":68,"context_line":"                            argument must be None or \u0027Default\u0027."},{"line_number":69,"context_line":"        \"\"\""},{"line_number":70,"context_line":"        return self._impl.create_user(username\u003dusername,"},{"line_number":71,"context_line":"                                      password\u003dpassword,"},{"line_number":72,"context_line":"                                      email\u003demail,"},{"line_number":73,"context_line":"                                      project_id\u003dproject_id,"}],"source_content_type":"text/x-python","patch_set":14,"id":"dab17558_cd43d340","line":70,"in_reply_to":"dab17558_b1553c1f","updated":"2016-05-17 10:11:42.000000000","message":"We need some solution for that, like using argument which disables actions timer","commit_id":"489e549fa09fd0c972b18fd70c522903cf04b4e7"},{"author":{"_account_id":9545,"name":"Andriy Kurilin","email":"andr.kurilin@gmail.com","username":"akurilin"},"change_message_id":"810dbe4dec25a9175fbad6d6bd2ae4397a82e09f","unresolved":false,"context_lines":[{"line_number":67,"context_line":"                            implementations that don\u0027t support domains this"},{"line_number":68,"context_line":"                            argument must be None or \u0027Default\u0027."},{"line_number":69,"context_line":"        \"\"\""},{"line_number":70,"context_line":"        return self._impl.create_user(username\u003dusername,"},{"line_number":71,"context_line":"                                      password\u003dpassword,"},{"line_number":72,"context_line":"                                      email\u003demail,"},{"line_number":73,"context_line":"                                      project_id\u003dproject_id,"}],"source_content_type":"text/x-python","patch_set":14,"id":"dab17558_b1553c1f","line":70,"in_reply_to":"dab17558_b1dee50e","updated":"2016-05-17 09:45:53.000000000","message":"_atomic_actions is an instance of OrderedDict, so as a result of your code, we will have only one atomic action. \nI do not know how we can make create_N_users in a good way without argument \"no_atomics\"","commit_id":"489e549fa09fd0c972b18fd70c522903cf04b4e7"},{"author":{"_account_id":9545,"name":"Andriy Kurilin","email":"andr.kurilin@gmail.com","username":"akurilin"},"change_message_id":"4b50bf610419bb140536f8c5fc81d2761822bce1","unresolved":false,"context_lines":[{"line_number":67,"context_line":"                            implementations that don\u0027t support domains this"},{"line_number":68,"context_line":"                            argument must be None or \u0027Default\u0027."},{"line_number":69,"context_line":"        \"\"\""},{"line_number":70,"context_line":"        return self._impl.create_user(username\u003dusername,"},{"line_number":71,"context_line":"                                      password\u003dpassword,"},{"line_number":72,"context_line":"                                      email\u003demail,"},{"line_number":73,"context_line":"                                      project_id\u003dproject_id,"}],"source_content_type":"text/x-python","patch_set":14,"id":"3aaa91ec_4784d409","line":70,"in_reply_to":"dab17558_cd43d340","updated":"2016-06-26 12:53:26.000000000","message":"done, in base service class.","commit_id":"489e549fa09fd0c972b18fd70c522903cf04b4e7"},{"author":{"_account_id":10475,"name":"Alexander Maretskiy","email":"amaretskiy@mirantis.com","username":"maretskiy"},"change_message_id":"c55bcb8aacc99661508c8049ea219c7deed577ff","unresolved":false,"context_lines":[{"line_number":117,"context_line":""},{"line_number":118,"context_line":""},{"line_number":119,"context_line":"@base.service(\"keystone\", service_type\u003d\"identity\", version\u003d\"2\")"},{"line_number":120,"context_line":"class KeystoneServiceV2(base.Service):"},{"line_number":121,"context_line":""},{"line_number":122,"context_line":"    is_applicable \u003d _is_applicable"},{"line_number":123,"context_line":""}],"source_content_type":"text/x-python","patch_set":14,"id":"dab17558_1ee2632f","line":120,"updated":"2016-05-16 12:49:34.000000000","message":"KeystoneV2Service","commit_id":"489e549fa09fd0c972b18fd70c522903cf04b4e7"},{"author":{"_account_id":9545,"name":"Andriy Kurilin","email":"andr.kurilin@gmail.com","username":"akurilin"},"change_message_id":"4b50bf610419bb140536f8c5fc81d2761822bce1","unresolved":false,"context_lines":[{"line_number":117,"context_line":""},{"line_number":118,"context_line":""},{"line_number":119,"context_line":"@base.service(\"keystone\", service_type\u003d\"identity\", version\u003d\"2\")"},{"line_number":120,"context_line":"class KeystoneServiceV2(base.Service):"},{"line_number":121,"context_line":""},{"line_number":122,"context_line":"    is_applicable \u003d _is_applicable"},{"line_number":123,"context_line":""}],"source_content_type":"text/x-python","patch_set":14,"id":"3aaa91ec_d2524034","line":120,"in_reply_to":"dab17558_1ee2632f","updated":"2016-06-26 12:53:26.000000000","message":"Done","commit_id":"489e549fa09fd0c972b18fd70c522903cf04b4e7"},{"author":{"_account_id":10475,"name":"Alexander Maretskiy","email":"amaretskiy@mirantis.com","username":"maretskiy"},"change_message_id":"c55bcb8aacc99661508c8049ea219c7deed577ff","unresolved":false,"context_lines":[{"line_number":192,"context_line":"                                      \"in Keystone v2\")"},{"line_number":193,"context_line":""},{"line_number":194,"context_line":"    @staticmethod"},{"line_number":195,"context_line":"    def _wrap_tenant(tenant):"},{"line_number":196,"context_line":"        return Project(id\u003dtenant.id, name\u003dtenant.name, domain_id\u003d\"default\")"},{"line_number":197,"context_line":""},{"line_number":198,"context_line":"    @staticmethod"}],"source_content_type":"text/x-python","patch_set":14,"id":"dab17558_9e465379","line":195,"updated":"2016-05-16 12:49:34.000000000","message":"Suggestion: in this kind of classes (Unified\u003cSmth\u003e) name \"unify\" sounds better than \"wrap\", for example: \"def _unify_tenant(tenant):\"","commit_id":"489e549fa09fd0c972b18fd70c522903cf04b4e7"},{"author":{"_account_id":9545,"name":"Andriy Kurilin","email":"andr.kurilin@gmail.com","username":"akurilin"},"change_message_id":"4b50bf610419bb140536f8c5fc81d2761822bce1","unresolved":false,"context_lines":[{"line_number":192,"context_line":"                                      \"in Keystone v2\")"},{"line_number":193,"context_line":""},{"line_number":194,"context_line":"    @staticmethod"},{"line_number":195,"context_line":"    def _wrap_tenant(tenant):"},{"line_number":196,"context_line":"        return Project(id\u003dtenant.id, name\u003dtenant.name, domain_id\u003d\"default\")"},{"line_number":197,"context_line":""},{"line_number":198,"context_line":"    @staticmethod"}],"source_content_type":"text/x-python","patch_set":14,"id":"3aaa91ec_523db0db","line":195,"in_reply_to":"dab17558_9e465379","updated":"2016-06-26 12:53:26.000000000","message":"Done","commit_id":"489e549fa09fd0c972b18fd70c522903cf04b4e7"},{"author":{"_account_id":10475,"name":"Alexander Maretskiy","email":"amaretskiy@mirantis.com","username":"maretskiy"},"change_message_id":"c55bcb8aacc99661508c8049ea219c7deed577ff","unresolved":false,"context_lines":[{"line_number":218,"context_line":""},{"line_number":219,"context_line":"    def list_projects(self):"},{"line_number":220,"context_line":"        \"\"\"List all projects.\"\"\""},{"line_number":221,"context_line":"        return self._impl.list_tenants()"},{"line_number":222,"context_line":""},{"line_number":223,"context_line":"    def create_user(self, username, password, email\u003dNone, project_id\u003dNone,"},{"line_number":224,"context_line":"                    domain_name\u003d\"Default\"):"}],"source_content_type":"text/x-python","patch_set":14,"id":"dab17558_de529b66","line":221,"updated":"2016-05-16 12:49:34.000000000","message":"return [self._wrap_tenant(t) for t in  self._impl.list_tenants()]","commit_id":"489e549fa09fd0c972b18fd70c522903cf04b4e7"},{"author":{"_account_id":9545,"name":"Andriy Kurilin","email":"andr.kurilin@gmail.com","username":"akurilin"},"change_message_id":"4b50bf610419bb140536f8c5fc81d2761822bce1","unresolved":false,"context_lines":[{"line_number":218,"context_line":""},{"line_number":219,"context_line":"    def list_projects(self):"},{"line_number":220,"context_line":"        \"\"\"List all projects.\"\"\""},{"line_number":221,"context_line":"        return self._impl.list_tenants()"},{"line_number":222,"context_line":""},{"line_number":223,"context_line":"    def create_user(self, username, password, email\u003dNone, project_id\u003dNone,"},{"line_number":224,"context_line":"                    domain_name\u003d\"Default\"):"}],"source_content_type":"text/x-python","patch_set":14,"id":"3aaa91ec_b228549b","line":221,"in_reply_to":"dab17558_de529b66","updated":"2016-06-26 12:53:26.000000000","message":"Done","commit_id":"489e549fa09fd0c972b18fd70c522903cf04b4e7"},{"author":{"_account_id":10475,"name":"Alexander Maretskiy","email":"amaretskiy@mirantis.com","username":"maretskiy"},"change_message_id":"c55bcb8aacc99661508c8049ea219c7deed577ff","unresolved":false,"context_lines":[{"line_number":240,"context_line":""},{"line_number":241,"context_line":"    def list_users(self):"},{"line_number":242,"context_line":"        \"\"\"List all users.\"\"\""},{"line_number":243,"context_line":"        return self._impl.list_users()"},{"line_number":244,"context_line":""},{"line_number":245,"context_line":"    def delete_user(self, user_id):"},{"line_number":246,"context_line":"        \"\"\"Deletes user by its id.\"\"\""}],"source_content_type":"text/x-python","patch_set":14,"id":"dab17558_91cda133","line":243,"updated":"2016-05-16 12:49:34.000000000","message":"return [self._wrap_user(u) for u in self._impl.list_users()]","commit_id":"489e549fa09fd0c972b18fd70c522903cf04b4e7"},{"author":{"_account_id":9545,"name":"Andriy Kurilin","email":"andr.kurilin@gmail.com","username":"akurilin"},"change_message_id":"4b50bf610419bb140536f8c5fc81d2761822bce1","unresolved":false,"context_lines":[{"line_number":240,"context_line":""},{"line_number":241,"context_line":"    def list_users(self):"},{"line_number":242,"context_line":"        \"\"\"List all users.\"\"\""},{"line_number":243,"context_line":"        return self._impl.list_users()"},{"line_number":244,"context_line":""},{"line_number":245,"context_line":"    def delete_user(self, user_id):"},{"line_number":246,"context_line":"        \"\"\"Deletes user by its id.\"\"\""}],"source_content_type":"text/x-python","patch_set":14,"id":"3aaa91ec_f22e5ca3","line":243,"in_reply_to":"dab17558_91cda133","updated":"2016-06-26 12:53:26.000000000","message":"Done","commit_id":"489e549fa09fd0c972b18fd70c522903cf04b4e7"},{"author":{"_account_id":10475,"name":"Alexander Maretskiy","email":"amaretskiy@mirantis.com","username":"maretskiy"},"change_message_id":"c55bcb8aacc99661508c8049ea219c7deed577ff","unresolved":false,"context_lines":[{"line_number":256,"context_line":""},{"line_number":257,"context_line":"    def add_role(self, role_id, user_id, project_id):"},{"line_number":258,"context_line":"        \"\"\"Add role to user.\"\"\""},{"line_number":259,"context_line":"        return self._impl.add_role(role_id\u003drole_id, user_id\u003duser_id,"},{"line_number":260,"context_line":"                                   tenant_id\u003dproject_id)"},{"line_number":261,"context_line":""},{"line_number":262,"context_line":"    def delete_role(self, role_id):"}],"source_content_type":"text/x-python","patch_set":14,"id":"dab17558_ac004802","line":259,"updated":"2016-05-16 12:49:34.000000000","message":"return _wrap_role(\u003cresult\u003e)","commit_id":"489e549fa09fd0c972b18fd70c522903cf04b4e7"},{"author":{"_account_id":9545,"name":"Andriy Kurilin","email":"andr.kurilin@gmail.com","username":"akurilin"},"change_message_id":"4b50bf610419bb140536f8c5fc81d2761822bce1","unresolved":false,"context_lines":[{"line_number":256,"context_line":""},{"line_number":257,"context_line":"    def add_role(self, role_id, user_id, project_id):"},{"line_number":258,"context_line":"        \"\"\"Add role to user.\"\"\""},{"line_number":259,"context_line":"        return self._impl.add_role(role_id\u003drole_id, user_id\u003duser_id,"},{"line_number":260,"context_line":"                                   tenant_id\u003dproject_id)"},{"line_number":261,"context_line":""},{"line_number":262,"context_line":"    def delete_role(self, role_id):"}],"source_content_type":"text/x-python","patch_set":14,"id":"3aaa91ec_321bc482","line":259,"in_reply_to":"dab17558_ac004802","updated":"2016-06-26 12:53:26.000000000","message":"Done","commit_id":"489e549fa09fd0c972b18fd70c522903cf04b4e7"},{"author":{"_account_id":10475,"name":"Alexander Maretskiy","email":"amaretskiy@mirantis.com","username":"maretskiy"},"change_message_id":"c55bcb8aacc99661508c8049ea219c7deed577ff","unresolved":false,"context_lines":[{"line_number":402,"context_line":""},{"line_number":403,"context_line":"    def list_projects(self):"},{"line_number":404,"context_line":"        \"\"\"List all projects.\"\"\""},{"line_number":405,"context_line":"        return self._impl.list_projects()"},{"line_number":406,"context_line":""},{"line_number":407,"context_line":"    def create_user(self, username, password, email\u003dNone, project_id\u003dNone,"},{"line_number":408,"context_line":"                    domain_name\u003d\"Default\"):"}],"source_content_type":"text/x-python","patch_set":14,"id":"dab17558_6cc66069","line":405,"updated":"2016-05-16 12:49:34.000000000","message":"use _wrap_project","commit_id":"489e549fa09fd0c972b18fd70c522903cf04b4e7"},{"author":{"_account_id":9545,"name":"Andriy Kurilin","email":"andr.kurilin@gmail.com","username":"akurilin"},"change_message_id":"4b50bf610419bb140536f8c5fc81d2761822bce1","unresolved":false,"context_lines":[{"line_number":402,"context_line":""},{"line_number":403,"context_line":"    def list_projects(self):"},{"line_number":404,"context_line":"        \"\"\"List all projects.\"\"\""},{"line_number":405,"context_line":"        return self._impl.list_projects()"},{"line_number":406,"context_line":""},{"line_number":407,"context_line":"    def create_user(self, username, password, email\u003dNone, project_id\u003dNone,"},{"line_number":408,"context_line":"                    domain_name\u003d\"Default\"):"}],"source_content_type":"text/x-python","patch_set":14,"id":"3aaa91ec_5214904b","line":405,"in_reply_to":"dab17558_6cc66069","updated":"2016-06-26 12:53:26.000000000","message":"Done","commit_id":"489e549fa09fd0c972b18fd70c522903cf04b4e7"},{"author":{"_account_id":10475,"name":"Alexander Maretskiy","email":"amaretskiy@mirantis.com","username":"maretskiy"},"change_message_id":"c55bcb8aacc99661508c8049ea219c7deed577ff","unresolved":false,"context_lines":[{"line_number":423,"context_line":""},{"line_number":424,"context_line":"    def list_users(self):"},{"line_number":425,"context_line":"        \"\"\"List all users.\"\"\""},{"line_number":426,"context_line":"        return self._impl.list_users()"},{"line_number":427,"context_line":""},{"line_number":428,"context_line":"    def delete_user(self, user_id):"},{"line_number":429,"context_line":"        \"\"\"Deletes user by its id.\"\"\""}],"source_content_type":"text/x-python","patch_set":14,"id":"dab17558_ccadcc9c","line":426,"updated":"2016-05-16 12:49:34.000000000","message":"use _wrap_user","commit_id":"489e549fa09fd0c972b18fd70c522903cf04b4e7"},{"author":{"_account_id":9545,"name":"Andriy Kurilin","email":"andr.kurilin@gmail.com","username":"akurilin"},"change_message_id":"4b50bf610419bb140536f8c5fc81d2761822bce1","unresolved":false,"context_lines":[{"line_number":423,"context_line":""},{"line_number":424,"context_line":"    def list_users(self):"},{"line_number":425,"context_line":"        \"\"\"List all users.\"\"\""},{"line_number":426,"context_line":"        return self._impl.list_users()"},{"line_number":427,"context_line":""},{"line_number":428,"context_line":"    def delete_user(self, user_id):"},{"line_number":429,"context_line":"        \"\"\"Deletes user by its id.\"\"\""}],"source_content_type":"text/x-python","patch_set":14,"id":"3aaa91ec_b20fb435","line":426,"in_reply_to":"dab17558_ccadcc9c","updated":"2016-06-26 12:53:26.000000000","message":"Done","commit_id":"489e549fa09fd0c972b18fd70c522903cf04b4e7"},{"author":{"_account_id":10475,"name":"Alexander Maretskiy","email":"amaretskiy@mirantis.com","username":"maretskiy"},"change_message_id":"c55bcb8aacc99661508c8049ea219c7deed577ff","unresolved":false,"context_lines":[{"line_number":439,"context_line":""},{"line_number":440,"context_line":"    def add_role(self, role_id, user_id, project_id):"},{"line_number":441,"context_line":"        \"\"\"Add role to user.\"\"\""},{"line_number":442,"context_line":"        return self._impl.add_role(role_id\u003drole_id, user_id\u003duser_id,"},{"line_number":443,"context_line":"                                   project_id\u003dproject_id)"},{"line_number":444,"context_line":""},{"line_number":445,"context_line":"    def delete_role(self, role_id):"}],"source_content_type":"text/x-python","patch_set":14,"id":"dab17558_8c8e242c","line":442,"updated":"2016-05-16 12:49:34.000000000","message":"use _wrap_role","commit_id":"489e549fa09fd0c972b18fd70c522903cf04b4e7"},{"author":{"_account_id":9545,"name":"Andriy Kurilin","email":"andr.kurilin@gmail.com","username":"akurilin"},"change_message_id":"4b50bf610419bb140536f8c5fc81d2761822bce1","unresolved":false,"context_lines":[{"line_number":439,"context_line":""},{"line_number":440,"context_line":"    def add_role(self, role_id, user_id, project_id):"},{"line_number":441,"context_line":"        \"\"\"Add role to user.\"\"\""},{"line_number":442,"context_line":"        return self._impl.add_role(role_id\u003drole_id, user_id\u003duser_id,"},{"line_number":443,"context_line":"                                   project_id\u003dproject_id)"},{"line_number":444,"context_line":""},{"line_number":445,"context_line":"    def delete_role(self, role_id):"}],"source_content_type":"text/x-python","patch_set":14,"id":"3aaa91ec_920a7824","line":442,"in_reply_to":"dab17558_8c8e242c","updated":"2016-06-26 12:53:26.000000000","message":"Done","commit_id":"489e549fa09fd0c972b18fd70c522903cf04b4e7"},{"author":{"_account_id":10475,"name":"Alexander Maretskiy","email":"amaretskiy@mirantis.com","username":"maretskiy"},"change_message_id":"da3039d37e69af8b599838053a57857ed905417d","unresolved":false,"context_lines":[{"line_number":36,"context_line":""},{"line_number":37,"context_line":"class Identity(base.Service):"},{"line_number":38,"context_line":""},{"line_number":39,"context_line":"    def create_project(self, project_name, domain_name\u003d\"Default\"):"},{"line_number":40,"context_line":"        \"\"\"Creates new project/tenant and return project object."},{"line_number":41,"context_line":""},{"line_number":42,"context_line":"        :param project_name: Name of project to be created."}],"source_content_type":"text/x-python","patch_set":16,"id":"1aa78d24_056353dd","line":39,"updated":"2016-07-12 09:29:25.000000000","message":"This method is never used (overridden in all child classes)\n\nIf this is an abstract method (IMPLICIT!!!) then it should at least raise NotImplementedError instead of having some implementation\n\nSame for methods below","commit_id":"77fd88728b15acd62bfb98843c42cbd03ff2fa77"},{"author":{"_account_id":10475,"name":"Alexander Maretskiy","email":"amaretskiy@mirantis.com","username":"maretskiy"},"change_message_id":"da3039d37e69af8b599838053a57857ed905417d","unresolved":false,"context_lines":[{"line_number":116,"context_line":"        return Role(id\u003drole.id, name\u003drole.name)"},{"line_number":117,"context_line":""},{"line_number":118,"context_line":""},{"line_number":119,"context_line":"@base.service(\"keystone\", service_type\u003d\"identity\", version\u003d\"2\")"},{"line_number":120,"context_line":"class KeystoneV2Service(base.Service):"},{"line_number":121,"context_line":""},{"line_number":122,"context_line":"    is_applicable \u003d _is_applicable"}],"source_content_type":"text/x-python","patch_set":16,"id":"1aa78d24_857b9abd","line":119,"updated":"2016-07-12 09:29:25.000000000","message":"We use this decorator for services implementations but not for services itself (e.g. not for class Identity) - this is confusing","commit_id":"77fd88728b15acd62bfb98843c42cbd03ff2fa77"},{"author":{"_account_id":10475,"name":"Alexander Maretskiy","email":"amaretskiy@mirantis.com","username":"maretskiy"},"change_message_id":"da3039d37e69af8b599838053a57857ed905417d","unresolved":false,"context_lines":[{"line_number":117,"context_line":""},{"line_number":118,"context_line":""},{"line_number":119,"context_line":"@base.service(\"keystone\", service_type\u003d\"identity\", version\u003d\"2\")"},{"line_number":120,"context_line":"class KeystoneV2Service(base.Service):"},{"line_number":121,"context_line":""},{"line_number":122,"context_line":"    is_applicable \u003d _is_applicable"},{"line_number":123,"context_line":""}],"source_content_type":"text/x-python","patch_set":16,"id":"1aa78d24_85f6fa5a","line":120,"updated":"2016-07-12 09:29:25.000000000","message":"Why this is not based on Identity?","commit_id":"77fd88728b15acd62bfb98843c42cbd03ff2fa77"},{"author":{"_account_id":10475,"name":"Alexander Maretskiy","email":"amaretskiy@mirantis.com","username":"maretskiy"},"change_message_id":"da3039d37e69af8b599838053a57857ed905417d","unresolved":false,"context_lines":[{"line_number":395,"context_line":"        :param project_name: Name of project to be created."},{"line_number":396,"context_line":"        :param domain_name: Name or id of domain where to create project,"},{"line_number":397,"context_line":"        \"\"\""},{"line_number":398,"context_line":"        project \u003d self._impl.create_project(project_name)"},{"line_number":399,"context_line":"        return self._unify_project(project)"},{"line_number":400,"context_line":""},{"line_number":401,"context_line":"    def delete_project(self, project_id):"}],"source_content_type":"text/x-python","patch_set":16,"id":"1aa78d24_4526fbba","line":398,"updated":"2016-07-12 09:29:25.000000000","message":"..., domain_name\u003ddomain_name)","commit_id":"77fd88728b15acd62bfb98843c42cbd03ff2fa77"}],"rally/plugins/openstack/services/keystone.py":[{"author":{"_account_id":11748,"name":"Chris St. Pierre","email":"cstpierr@cisco.com","username":"stpierre"},"change_message_id":"191107a050da80238bb7af5dbe7a24b358b83e72","unresolved":false,"context_lines":[{"line_number":65,"context_line":""},{"line_number":66,"context_line":"    def delete_user(self, user_id):"},{"line_number":67,"context_line":"        \"\"\"Deletes user.\"\"\""},{"line_number":68,"context_line":"        return self._impl.delete_user(user_id)"},{"line_number":69,"context_line":""},{"line_number":70,"context_line":"    def list_users(self):"},{"line_number":71,"context_line":"        \"\"\"List all users.\"\"\""}],"source_content_type":"text/x-python","patch_set":4,"id":"1a122d0e_7de51dda","line":68,"updated":"2016-04-28 14:39:08.000000000","message":"We\u0027ve got this boilerplate line in here a lot. Could we add some magic to the Service superclass to make this just work? E.g.:\n\n    def __getattr__(self, name):\n        if hasattr(self, \"_impl\") and hasattr(self._impl, name):\n            return getattr(self._impl, name)\n\nThat way calling Identity.delete_user would resolve to Identity()._impl.delete_user, for instance.\n\nThis code is untested and getattr() is touchy so there\u0027s likely a bug that I haven\u0027t considered, but that would save us a *lot* of lines of boilerplate.","commit_id":"a53d0dff382b959b5abf92b59194bd1562fb5db7"},{"author":{"_account_id":9545,"name":"Andriy Kurilin","email":"andr.kurilin@gmail.com","username":"akurilin"},"change_message_id":"5f2e8f3805f66ac146a733ea9877a31bff8cd5e4","unresolved":false,"context_lines":[{"line_number":65,"context_line":""},{"line_number":66,"context_line":"    def delete_user(self, user_id):"},{"line_number":67,"context_line":"        \"\"\"Deletes user.\"\"\""},{"line_number":68,"context_line":"        return self._impl.delete_user(user_id)"},{"line_number":69,"context_line":""},{"line_number":70,"context_line":"    def list_users(self):"},{"line_number":71,"context_line":"        \"\"\"List all users.\"\"\""}],"source_content_type":"text/x-python","patch_set":4,"id":"1a122d0e_c0df2a67","line":68,"in_reply_to":"1a122d0e_7de51dda","updated":"2016-04-28 14:53:47.000000000","message":"As for me, services should replace both wrappers and utils(i.e rally.plugins.openstack.scenarios.keystone.utils). In this case, versioned classes(for example, KeystoneServiceV3) will have some specific methods which can be missed in another version(create_domain for example). Such methods should not be listed in base class, since it is designed to be compatible with every version.\n\nI think we can play with decorators which will mark method as a proxy method and construct a proper code in metaclass. It will add more magic and complicity, but, imo, we should implement it(maybe a bit later to be able quicker start working on services). \n\nAny suggestions?","commit_id":"a53d0dff382b959b5abf92b59194bd1562fb5db7"},{"author":{"_account_id":9545,"name":"Andriy Kurilin","email":"andr.kurilin@gmail.com","username":"akurilin"},"change_message_id":"3badd478e56e0bcd31e8ddbf420ebd5bc1c7aad9","unresolved":false,"context_lines":[{"line_number":65,"context_line":""},{"line_number":66,"context_line":"    def delete_user(self, user_id):"},{"line_number":67,"context_line":"        \"\"\"Deletes user.\"\"\""},{"line_number":68,"context_line":"        return self._impl.delete_user(user_id)"},{"line_number":69,"context_line":""},{"line_number":70,"context_line":"    def list_users(self):"},{"line_number":71,"context_line":"        \"\"\"List all users.\"\"\""}],"source_content_type":"text/x-python","patch_set":4,"id":"1a122d0e_f2e314a2","line":68,"in_reply_to":"1a122d0e_ac15c540","updated":"2016-04-28 17:24:32.000000000","message":"agree with #1","commit_id":"a53d0dff382b959b5abf92b59194bd1562fb5db7"},{"author":{"_account_id":11748,"name":"Chris St. Pierre","email":"cstpierr@cisco.com","username":"stpierre"},"change_message_id":"a1a09a692c64f24510ca1cd3a22dacc5bf0ed293","unresolved":false,"context_lines":[{"line_number":65,"context_line":""},{"line_number":66,"context_line":"    def delete_user(self, user_id):"},{"line_number":67,"context_line":"        \"\"\"Deletes user.\"\"\""},{"line_number":68,"context_line":"        return self._impl.delete_user(user_id)"},{"line_number":69,"context_line":""},{"line_number":70,"context_line":"    def list_users(self):"},{"line_number":71,"context_line":"        \"\"\"List all users.\"\"\""}],"source_content_type":"text/x-python","patch_set":4,"id":"1a122d0e_ac15c540","line":68,"in_reply_to":"1a122d0e_c0df2a67","updated":"2016-04-28 16:19:23.000000000","message":"That\u0027s a great point. First off, let\u0027s add create_domain or something like that to this review, so that we have an example of that use case in our initial PoC. I think that\u0027ll be important to see how that use case will be handled.\n\nNow, I think we can do this in a few ways:\n\n1. Auto-detect methods as I initially suggested, and provide a decorator for methods that shouldn\u0027t be detected, e.g.:\n\n    @version_specific_method\n    def create_domain(self, ...):\n\nThat decorator could just func.version_specific \u003d True, which we could then filter on in __getattr__(). This is a minimal amount of boilerplate, but I\u0027m worried that we\u0027ll forget that decorator in places and wind up with an inconsistent API, though.\n\n2. Provide a simple helper to create the methods that are always just delegated. E.g.:\n\n    def create_delegated_method(name):\n        def inner(self, *args, **kwargs):\n            return getattr(self._impl, name)(*args, **kwargs)\n\n        return inner\n\n    ...\n\n    class Identity(Service):\n        ...\n    \n        list_users \u003d create_delegated_method(\"list_users\")\n\nThat winds up with a bunch of boilerplate, still.\n\n3. The first time an Identity object is instantiated, inspect all of the implementations and automatically add all of these functions on the fly. That\u0027s a little too magical for my tastes, though.\n\nI think I lean towards #1, since:\n\na) Methods that only exist in one implementation are much rarer than methods that just delegate to each implementation, and we should optimize for the common case.\n\nb) The most obvious mistake would be to forget the decorator, the downside of which is that the Identity class has an extra method that shouldn\u0027t be there (and isn\u0027t used). This is a very minor risk.\n\nc) It eliminates boilerplate.","commit_id":"a53d0dff382b959b5abf92b59194bd1562fb5db7"}]}
