prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Add exception handling to docs
  Hi :) 
  
  
  I was reading the [docs](https://github.com/googleapis/google-api-python-client/blob/master/docs/start.md) looking for an example to handle exceptions from when request.execute() goes wrong e.g. a 403 due to 
  Exceeding qouta limits.
  
  
  I would like for the docs to be updated with a try: and except: like this
  ``` python
      try:
          response = request.execute()
      except HttpError as e:
          logger.error('Error response status code %d, reason %s:', e.resp.status, e.content)
          return {'error': 403, 'body' : 'YouTube API Data v3 qouta limit exceeded'}
  ```
  or something else in the `except` block
   
  If you're happy with this I'd like to contribute this as a first timer to open source?
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `googleapiclient/errors.py`
  Content:
  ```
  1 # Copyright 2014 Google Inc. All Rights Reserved.
  2 #
  3 # Licensed under the Apache License, Version 2.0 (the "License");
  4 # you may not use this file except in compliance with the License.
  5 # You may obtain a copy of the License at
  6 #
  7 #      http://www.apache.org/licenses/LICENSE-2.0
  8 #
  9 # Unless required by applicable law or agreed to in writing, software
  10 # distributed under the License is distributed on an "AS IS" BASIS,
  11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12 # See the License for the specific language governing permissions and
  13 # limitations under the License.
  14 
  15 """Errors for the library.
  16 
  17 All exceptions defined by the library
  18 should be defined in this file.
  19 """
  20 from __future__ import absolute_import
  21 
  22 __author__ = "jcgregorio@google.com (Joe Gregorio)"
  23 
  24 import json
  25 
  26 from googleapiclient import _helpers as util
  27 
  28 
  29 class Error(Exception):
  30     """Base error for this module."""
  31 
  32     pass
  33 
  34 
  35 class HttpError(Error):
  36     """HTTP data was invalid or unexpected."""
  37 
  38     @util.positional(3)
  39     def __init__(self, resp, content, uri=None):
  40         self.resp = resp
  41         if not isinstance(content, bytes):
  42             raise TypeError("HTTP content should be bytes")
  43         self.content = content
  44         self.uri = uri
  45         self.error_details = ""
  46 
  47     def _get_reason(self):
  48         """Calculate the reason for the error from the response content."""
  49         reason = self.resp.reason
  50         try:
  51             data = json.loads(self.content.decode("utf-8"))
  52             if isinstance(data, dict):
  53                 reason = data["error"]["message"]
  54                 if "details" in data["error"]:
  55                     self.error_details = data["error"]["details"]
  56                 elif "detail" in data["error"]:
  57                     self.error_details = data["error"]["detail"]
  58             elif isinstance(data, list) and len(data) > 0:
  59                 first_error = data[0]
  60                 reason = first_error["error"]["message"]
  61                 if "details" in first_error["error"]:
  62                     self.error_details = first_error["error"]["details"]
  63         except (ValueError, KeyError, TypeError):
  64             pass
  65         if reason is None:
  66             reason = ""
  67         return reason
  68 
  69     def __repr__(self):
  70         reason = self._get_reason()
  71         if self.error_details:
  72             return '<HttpError %s when requesting %s returned "%s". Details: "%s">' % (
  73                 self.resp.status,
  74                 self.uri,
  75                 reason.strip(),
  76                 self.error_details,
  77             )
  78         elif self.uri:
  79             return '<HttpError %s when requesting %s returned "%s">' % (
  80                 self.resp.status,
  81                 self.uri,
  82                 self._get_reason().strip(),
  83             )
  84         else:
  85             return '<HttpError %s "%s">' % (self.resp.status, self._get_reason())
  86 
  87     __str__ = __repr__
  88 
  89 
  90 class InvalidJsonError(Error):
  91     """The JSON returned could not be parsed."""
  92 
  93     pass
  94 
  95 
  96 class UnknownFileType(Error):
  97     """File type unknown or unexpected."""
  98 
  99     pass
  100 
  101 
  102 class UnknownLinkType(Error):
  103     """Link type unknown or unexpected."""
  104 
  105     pass
  106 
  107 
  108 class UnknownApiNameOrVersion(Error):
  109     """No API with that name and version exists."""
  110 
  111     pass
  112 
  113 
  114 class UnacceptableMimeTypeError(Error):
  115     """That is an unacceptable mimetype for this operation."""
  116 
  117     pass
  118 
  119 
  120 class MediaUploadSizeError(Error):
  121     """Media is larger than the method can accept."""
  122 
  123     pass
  124 
  125 
  126 class ResumableUploadError(HttpError):
  127     """Error occurred during resumable upload."""
  128 
  129     pass
  130 
  131 
  132 class InvalidChunkSizeError(Error):
  133     """The given chunksize is not valid."""
  134 
  135     pass
  136 
  137 
  138 class InvalidNotificationError(Error):
  139     """The channel Notification is invalid."""
  140 
  141     pass
  142 
  143 
  144 class BatchError(HttpError):
  145     """Error occurred during batch operations."""
  146 
  147     @util.positional(2)
  148     def __init__(self, reason, resp=None, content=None):
  149         self.resp = resp
  150         self.content = content
  151         self.reason = reason
  152 
  153     def __repr__(self):
  154         if getattr(self.resp, "status", None) is None:
  155             return '<BatchError "%s">' % (self.reason)
  156         else:
  157             return '<BatchError %s "%s">' % (self.resp.status, self.reason)
  158 
  159     __str__ = __repr__
  160 
  161 
  162 class UnexpectedMethodError(Error):
  163     """Exception raised by RequestMockBuilder on unexpected calls."""
  164 
  165     @util.positional(1)
  166     def __init__(self, methodId=None):
  167         """Constructor for an UnexpectedMethodError."""
  168         super(UnexpectedMethodError, self).__init__(
  169             "Received unexpected call %s" % methodId
  170         )
  171 
  172 
  173 class UnexpectedBodyError(Error):
  174     """Exception raised by RequestMockBuilder on unexpected bodies."""
  175 
  176     def __init__(self, expected, provided):
  177         """Constructor for an UnexpectedMethodError."""
  178         super(UnexpectedBodyError, self).__init__(
  179             "Expected: [%s] - Provided: [%s]" % (expected, provided)
  180         )
  181
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/googleapiclient/errors.py b/googleapiclient/errors.py
  --- a/googleapiclient/errors.py
  +++ b/googleapiclient/errors.py
  @@ -51,10 +51,9 @@
               data = json.loads(self.content.decode("utf-8"))
               if isinstance(data, dict):
                   reason = data["error"]["message"]
  -                if "details" in data["error"]:
  -                    self.error_details = data["error"]["details"]
  -                elif "detail" in data["error"]:
  -                    self.error_details = data["error"]["detail"]
  +                error_detail_keyword = next((kw for kw in ["detail", "details", "message"] if kw in data["error"]), "")
  +                if error_detail_keyword:
  +                    self.error_details = data["error"][error_detail_keyword]
               elif isinstance(data, list) and len(data) > 0:
                   first_error = data[0]
                   reason = first_error["error"]["message"]
  
verification_info: {"golden_diff": "diff --git a/googleapiclient/errors.py b/googleapiclient/errors.py\n--- a/googleapiclient/errors.py\n+++ b/googleapiclient/errors.py\n@@ -51,10 +51,9 @@\n             data = json.loads(self.content.decode(\"utf-8\"))\n             if isinstance(data, dict):\n                 reason = data[\"error\"][\"message\"]\n-                if \"details\" in data[\"error\"]:\n-                    self.error_details = data[\"error\"][\"details\"]\n-                elif \"detail\" in data[\"error\"]:\n-                    self.error_details = data[\"error\"][\"detail\"]\n+                error_detail_keyword = next((kw for kw in [\"detail\", \"details\", \"message\"] if kw in data[\"error\"]), \"\")\n+                if error_detail_keyword:\n+                    self.error_details = data[\"error\"][error_detail_keyword]\n             elif isinstance(data, list) and len(data) > 0:\n                 first_error = data[0]\n                 reason = first_error[\"error\"][\"message\"]\n", "issue": "Add exception handling to docs\nHi :) \r\n\r\n\r\nI was reading the [docs](https://github.com/googleapis/google-api-python-client/blob/master/docs/start.md) looking for an example to handle exceptions from when request.execute() goes wrong e.g. a 403 due to \r\nExceeding qouta limits.\r\n\r\n\r\nI would like for the docs to be updated with a try: and except: like this\r\n``` python\r\n    try:\r\n        response = request.execute()\r\n    except HttpError as e:\r\n        logger.error('Error response status code %d, reason %s:', e.resp.status, e.content)\r\n        return {'error': 403, 'body' : 'YouTube API Data v3 qouta limit exceeded'}\r\n```\r\nor something else in the `except` block\r\n \r\nIf you're happy with this I'd like to contribute this as a first timer to open source?\r\n\n", "before_files": [{"content": "# Copyright 2014 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Errors for the library.\n\nAll exceptions defined by the library\nshould be defined in this file.\n\"\"\"\nfrom __future__ import absolute_import\n\n__author__ = \"jcgregorio@google.com (Joe Gregorio)\"\n\nimport json\n\nfrom googleapiclient import _helpers as util\n\n\nclass Error(Exception):\n    \"\"\"Base error for this module.\"\"\"\n\n    pass\n\n\nclass HttpError(Error):\n    \"\"\"HTTP data was invalid or unexpected.\"\"\"\n\n    @util.positional(3)\n    def __init__(self, resp, content, uri=None):\n        self.resp = resp\n        if not isinstance(content, bytes):\n            raise TypeError(\"HTTP content should be bytes\")\n        self.content = content\n        self.uri = uri\n        self.error_details = \"\"\n\n    def _get_reason(self):\n        \"\"\"Calculate the reason for the error from the response content.\"\"\"\n        reason = self.resp.reason\n        try:\n            data = json.loads(self.content.decode(\"utf-8\"))\n            if isinstance(data, dict):\n                reason = data[\"error\"][\"message\"]\n                if \"details\" in data[\"error\"]:\n                    self.error_details = data[\"error\"][\"details\"]\n                elif \"detail\" in data[\"error\"]:\n                    self.error_details = data[\"error\"][\"detail\"]\n            elif isinstance(data, list) and len(data) > 0:\n                first_error = data[0]\n                reason = first_error[\"error\"][\"message\"]\n                if \"details\" in first_error[\"error\"]:\n                    self.error_details = first_error[\"error\"][\"details\"]\n        except (ValueError, KeyError, TypeError):\n            pass\n        if reason is None:\n            reason = \"\"\n        return reason\n\n    def __repr__(self):\n        reason = self._get_reason()\n        if self.error_details:\n            return '<HttpError %s when requesting %s returned \"%s\". Details: \"%s\">' % (\n                self.resp.status,\n                self.uri,\n                reason.strip(),\n                self.error_details,\n            )\n        elif self.uri:\n            return '<HttpError %s when requesting %s returned \"%s\">' % (\n                self.resp.status,\n                self.uri,\n                self._get_reason().strip(),\n            )\n        else:\n            return '<HttpError %s \"%s\">' % (self.resp.status, self._get_reason())\n\n    __str__ = __repr__\n\n\nclass InvalidJsonError(Error):\n    \"\"\"The JSON returned could not be parsed.\"\"\"\n\n    pass\n\n\nclass UnknownFileType(Error):\n    \"\"\"File type unknown or unexpected.\"\"\"\n\n    pass\n\n\nclass UnknownLinkType(Error):\n    \"\"\"Link type unknown or unexpected.\"\"\"\n\n    pass\n\n\nclass UnknownApiNameOrVersion(Error):\n    \"\"\"No API with that name and version exists.\"\"\"\n\n    pass\n\n\nclass UnacceptableMimeTypeError(Error):\n    \"\"\"That is an unacceptable mimetype for this operation.\"\"\"\n\n    pass\n\n\nclass MediaUploadSizeError(Error):\n    \"\"\"Media is larger than the method can accept.\"\"\"\n\n    pass\n\n\nclass ResumableUploadError(HttpError):\n    \"\"\"Error occurred during resumable upload.\"\"\"\n\n    pass\n\n\nclass InvalidChunkSizeError(Error):\n    \"\"\"The given chunksize is not valid.\"\"\"\n\n    pass\n\n\nclass InvalidNotificationError(Error):\n    \"\"\"The channel Notification is invalid.\"\"\"\n\n    pass\n\n\nclass BatchError(HttpError):\n    \"\"\"Error occurred during batch operations.\"\"\"\n\n    @util.positional(2)\n    def __init__(self, reason, resp=None, content=None):\n        self.resp = resp\n        self.content = content\n        self.reason = reason\n\n    def __repr__(self):\n        if getattr(self.resp, \"status\", None) is None:\n            return '<BatchError \"%s\">' % (self.reason)\n        else:\n            return '<BatchError %s \"%s\">' % (self.resp.status, self.reason)\n\n    __str__ = __repr__\n\n\nclass UnexpectedMethodError(Error):\n    \"\"\"Exception raised by RequestMockBuilder on unexpected calls.\"\"\"\n\n    @util.positional(1)\n    def __init__(self, methodId=None):\n        \"\"\"Constructor for an UnexpectedMethodError.\"\"\"\n        super(UnexpectedMethodError, self).__init__(\n            \"Received unexpected call %s\" % methodId\n        )\n\n\nclass UnexpectedBodyError(Error):\n    \"\"\"Exception raised by RequestMockBuilder on unexpected bodies.\"\"\"\n\n    def __init__(self, expected, provided):\n        \"\"\"Constructor for an UnexpectedMethodError.\"\"\"\n        super(UnexpectedBodyError, self).__init__(\n            \"Expected: [%s] - Provided: [%s]\" % (expected, provided)\n        )\n", "path": "googleapiclient/errors.py"}], "after_files": [{"content": "# Copyright 2014 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Errors for the library.\n\nAll exceptions defined by the library\nshould be defined in this file.\n\"\"\"\nfrom __future__ import absolute_import\n\n__author__ = \"jcgregorio@google.com (Joe Gregorio)\"\n\nimport json\n\nfrom googleapiclient import _helpers as util\n\n\nclass Error(Exception):\n    \"\"\"Base error for this module.\"\"\"\n\n    pass\n\n\nclass HttpError(Error):\n    \"\"\"HTTP data was invalid or unexpected.\"\"\"\n\n    @util.positional(3)\n    def __init__(self, resp, content, uri=None):\n        self.resp = resp\n        if not isinstance(content, bytes):\n            raise TypeError(\"HTTP content should be bytes\")\n        self.content = content\n        self.uri = uri\n        self.error_details = \"\"\n\n    def _get_reason(self):\n        \"\"\"Calculate the reason for the error from the response content.\"\"\"\n        reason = self.resp.reason\n        try:\n            data = json.loads(self.content.decode(\"utf-8\"))\n            if isinstance(data, dict):\n                reason = data[\"error\"][\"message\"]\n                error_detail_keyword = next((kw for kw in [\"detail\", \"details\", \"message\"] if kw in data[\"error\"]), \"\")\n                if error_detail_keyword:\n                    self.error_details = data[\"error\"][error_detail_keyword]\n            elif isinstance(data, list) and len(data) > 0:\n                first_error = data[0]\n                reason = first_error[\"error\"][\"message\"]\n                if \"details\" in first_error[\"error\"]:\n                    self.error_details = first_error[\"error\"][\"details\"]\n        except (ValueError, KeyError, TypeError):\n            pass\n        if reason is None:\n            reason = \"\"\n        return reason\n\n    def __repr__(self):\n        reason = self._get_reason()\n        if self.error_details:\n            return '<HttpError %s when requesting %s returned \"%s\". Details: \"%s\">' % (\n                self.resp.status,\n                self.uri,\n                reason.strip(),\n                self.error_details,\n            )\n        elif self.uri:\n            return '<HttpError %s when requesting %s returned \"%s\">' % (\n                self.resp.status,\n                self.uri,\n                self._get_reason().strip(),\n            )\n        else:\n            return '<HttpError %s \"%s\">' % (self.resp.status, self._get_reason())\n\n    __str__ = __repr__\n\n\nclass InvalidJsonError(Error):\n    \"\"\"The JSON returned could not be parsed.\"\"\"\n\n    pass\n\n\nclass UnknownFileType(Error):\n    \"\"\"File type unknown or unexpected.\"\"\"\n\n    pass\n\n\nclass UnknownLinkType(Error):\n    \"\"\"Link type unknown or unexpected.\"\"\"\n\n    pass\n\n\nclass UnknownApiNameOrVersion(Error):\n    \"\"\"No API with that name and version exists.\"\"\"\n\n    pass\n\n\nclass UnacceptableMimeTypeError(Error):\n    \"\"\"That is an unacceptable mimetype for this operation.\"\"\"\n\n    pass\n\n\nclass MediaUploadSizeError(Error):\n    \"\"\"Media is larger than the method can accept.\"\"\"\n\n    pass\n\n\nclass ResumableUploadError(HttpError):\n    \"\"\"Error occurred during resumable upload.\"\"\"\n\n    pass\n\n\nclass InvalidChunkSizeError(Error):\n    \"\"\"The given chunksize is not valid.\"\"\"\n\n    pass\n\n\nclass InvalidNotificationError(Error):\n    \"\"\"The channel Notification is invalid.\"\"\"\n\n    pass\n\n\nclass BatchError(HttpError):\n    \"\"\"Error occurred during batch operations.\"\"\"\n\n    @util.positional(2)\n    def __init__(self, reason, resp=None, content=None):\n        self.resp = resp\n        self.content = content\n        self.reason = reason\n\n    def __repr__(self):\n        if getattr(self.resp, \"status\", None) is None:\n            return '<BatchError \"%s\">' % (self.reason)\n        else:\n            return '<BatchError %s \"%s\">' % (self.resp.status, self.reason)\n\n    __str__ = __repr__\n\n\nclass UnexpectedMethodError(Error):\n    \"\"\"Exception raised by RequestMockBuilder on unexpected calls.\"\"\"\n\n    @util.positional(1)\n    def __init__(self, methodId=None):\n        \"\"\"Constructor for an UnexpectedMethodError.\"\"\"\n        super(UnexpectedMethodError, self).__init__(\n            \"Received unexpected call %s\" % methodId\n        )\n\n\nclass UnexpectedBodyError(Error):\n    \"\"\"Exception raised by RequestMockBuilder on unexpected bodies.\"\"\"\n\n    def __init__(self, expected, provided):\n        \"\"\"Constructor for an UnexpectedMethodError.\"\"\"\n        super(UnexpectedBodyError, self).__init__(\n            \"Expected: [%s] - Provided: [%s]\" % (expected, provided)\n        )\n", "path": "googleapiclient/errors.py"}]}