prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  JSON Cache for AssumeRoleProvider not truncating files
  When we open a file for writing, if we're reusing the same file (same cache key) we don't truncate the file before writing.  If the second JSON response is smaller it will result in extra data at the end of the JSON document.
  
  This will trigger a json parsing error, which raises a KeyError, which causes the cred provider to retrieve a new set of temporary credentials because it thinks the file is not in the cache.
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `awscli/customizations/assumerole.py`
  Content:
  ```
  1 import os
  2 import json
  3 import logging
  4 
  5 from botocore.exceptions import ProfileNotFound
  6 
  7 LOG = logging.getLogger(__name__)
  8 
  9 
  10 def register_assume_role_provider(event_handlers):
  11     event_handlers.register('session-initialized',
  12                             inject_assume_role_provider_cache,
  13                             unique_id='inject_assume_role_cred_provider_cache')
  14 
  15 
  16 def inject_assume_role_provider_cache(session, **kwargs):
  17     try:
  18         cred_chain = session.get_component('credential_provider')
  19     except ProfileNotFound:
  20         # If a user has provided a profile that does not exist,
  21         # trying to retrieve components/config on the session
  22         # will raise ProfileNotFound.  Sometimes this is invalid:
  23         #
  24         # "ec2 describe-instances --profile unknown"
  25         #
  26         # and sometimes this is perfectly valid:
  27         #
  28         # "configure set region us-west-2 --profile brand-new-profile"
  29         #
  30         # Because we can't know (and don't want to know) whether
  31         # the customer is trying to do something valid, we just
  32         # immediately return.  If it's invalid something else
  33         # up the stack will raise ProfileNotFound, otherwise
  34         # the configure (and other) commands will work as expected.
  35         LOG.debug("ProfileNotFound caught when trying to inject "
  36                   "assume-role cred provider cache.  Not configuring "
  37                   "JSONFileCache for assume-role.")
  38         return
  39     provider = cred_chain.get_provider('assume-role')
  40     provider.cache = JSONFileCache()
  41 
  42 
  43 class JSONFileCache(object):
  44     """JSON file cache.
  45 
  46     This provides a dict like interface that stores JSON serializable
  47     objects.
  48 
  49     The objects are serialized to JSON and stored in a file.  These
  50     values can be retrieved at a later time.
  51 
  52     """
  53 
  54     CACHE_DIR = os.path.expanduser(os.path.join('~', '.aws', 'cli', 'cache'))
  55 
  56     def __init__(self, working_dir=CACHE_DIR):
  57         self._working_dir = working_dir
  58 
  59     def __contains__(self, cache_key):
  60         actual_key = self._convert_cache_key(cache_key)
  61         return os.path.isfile(actual_key)
  62 
  63     def __getitem__(self, cache_key):
  64         """Retrieve value from a cache key."""
  65         actual_key = self._convert_cache_key(cache_key)
  66         try:
  67             with open(actual_key) as f:
  68                 return json.load(f)
  69         except (OSError, ValueError, IOError):
  70             raise KeyError(cache_key)
  71 
  72     def __setitem__(self, cache_key, value):
  73         full_key = self._convert_cache_key(cache_key)
  74         try:
  75             file_content = json.dumps(value)
  76         except (TypeError, ValueError):
  77             raise ValueError("Value cannot be cached, must be "
  78                              "JSON serializable: %s" % value)
  79         if not os.path.isdir(self._working_dir):
  80             os.makedirs(self._working_dir)
  81         with os.fdopen(os.open(full_key,
  82                                os.O_WRONLY | os.O_CREAT, 0o600), 'w') as f:
  83             f.write(file_content)
  84 
  85     def _convert_cache_key(self, cache_key):
  86         full_path = os.path.join(self._working_dir, cache_key + '.json')
  87         return full_path
  88
  ```
  --- 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/awscli/customizations/assumerole.py b/awscli/customizations/assumerole.py
  --- a/awscli/customizations/assumerole.py
  +++ b/awscli/customizations/assumerole.py
  @@ -80,6 +80,7 @@
               os.makedirs(self._working_dir)
           with os.fdopen(os.open(full_key,
                                  os.O_WRONLY | os.O_CREAT, 0o600), 'w') as f:
  +            f.truncate()
               f.write(file_content)
   
       def _convert_cache_key(self, cache_key):
  
verification_info: {"golden_diff": "diff --git a/awscli/customizations/assumerole.py b/awscli/customizations/assumerole.py\n--- a/awscli/customizations/assumerole.py\n+++ b/awscli/customizations/assumerole.py\n@@ -80,6 +80,7 @@\n             os.makedirs(self._working_dir)\n         with os.fdopen(os.open(full_key,\n                                os.O_WRONLY | os.O_CREAT, 0o600), 'w') as f:\n+            f.truncate()\n             f.write(file_content)\n \n     def _convert_cache_key(self, cache_key):\n", "issue": "JSON Cache for AssumeRoleProvider not truncating files\nWhen we open a file for writing, if we're reusing the same file (same cache key) we don't truncate the file before writing.  If the second JSON response is smaller it will result in extra data at the end of the JSON document.\n\nThis will trigger a json parsing error, which raises a KeyError, which causes the cred provider to retrieve a new set of temporary credentials because it thinks the file is not in the cache.\n\n", "before_files": [{"content": "import os\nimport json\nimport logging\n\nfrom botocore.exceptions import ProfileNotFound\n\nLOG = logging.getLogger(__name__)\n\n\ndef register_assume_role_provider(event_handlers):\n    event_handlers.register('session-initialized',\n                            inject_assume_role_provider_cache,\n                            unique_id='inject_assume_role_cred_provider_cache')\n\n\ndef inject_assume_role_provider_cache(session, **kwargs):\n    try:\n        cred_chain = session.get_component('credential_provider')\n    except ProfileNotFound:\n        # If a user has provided a profile that does not exist,\n        # trying to retrieve components/config on the session\n        # will raise ProfileNotFound.  Sometimes this is invalid:\n        #\n        # \"ec2 describe-instances --profile unknown\"\n        #\n        # and sometimes this is perfectly valid:\n        #\n        # \"configure set region us-west-2 --profile brand-new-profile\"\n        #\n        # Because we can't know (and don't want to know) whether\n        # the customer is trying to do something valid, we just\n        # immediately return.  If it's invalid something else\n        # up the stack will raise ProfileNotFound, otherwise\n        # the configure (and other) commands will work as expected.\n        LOG.debug(\"ProfileNotFound caught when trying to inject \"\n                  \"assume-role cred provider cache.  Not configuring \"\n                  \"JSONFileCache for assume-role.\")\n        return\n    provider = cred_chain.get_provider('assume-role')\n    provider.cache = JSONFileCache()\n\n\nclass JSONFileCache(object):\n    \"\"\"JSON file cache.\n\n    This provides a dict like interface that stores JSON serializable\n    objects.\n\n    The objects are serialized to JSON and stored in a file.  These\n    values can be retrieved at a later time.\n\n    \"\"\"\n\n    CACHE_DIR = os.path.expanduser(os.path.join('~', '.aws', 'cli', 'cache'))\n\n    def __init__(self, working_dir=CACHE_DIR):\n        self._working_dir = working_dir\n\n    def __contains__(self, cache_key):\n        actual_key = self._convert_cache_key(cache_key)\n        return os.path.isfile(actual_key)\n\n    def __getitem__(self, cache_key):\n        \"\"\"Retrieve value from a cache key.\"\"\"\n        actual_key = self._convert_cache_key(cache_key)\n        try:\n            with open(actual_key) as f:\n                return json.load(f)\n        except (OSError, ValueError, IOError):\n            raise KeyError(cache_key)\n\n    def __setitem__(self, cache_key, value):\n        full_key = self._convert_cache_key(cache_key)\n        try:\n            file_content = json.dumps(value)\n        except (TypeError, ValueError):\n            raise ValueError(\"Value cannot be cached, must be \"\n                             \"JSON serializable: %s\" % value)\n        if not os.path.isdir(self._working_dir):\n            os.makedirs(self._working_dir)\n        with os.fdopen(os.open(full_key,\n                               os.O_WRONLY | os.O_CREAT, 0o600), 'w') as f:\n            f.write(file_content)\n\n    def _convert_cache_key(self, cache_key):\n        full_path = os.path.join(self._working_dir, cache_key + '.json')\n        return full_path\n", "path": "awscli/customizations/assumerole.py"}], "after_files": [{"content": "import os\nimport json\nimport logging\n\nfrom botocore.exceptions import ProfileNotFound\n\nLOG = logging.getLogger(__name__)\n\n\ndef register_assume_role_provider(event_handlers):\n    event_handlers.register('session-initialized',\n                            inject_assume_role_provider_cache,\n                            unique_id='inject_assume_role_cred_provider_cache')\n\n\ndef inject_assume_role_provider_cache(session, **kwargs):\n    try:\n        cred_chain = session.get_component('credential_provider')\n    except ProfileNotFound:\n        # If a user has provided a profile that does not exist,\n        # trying to retrieve components/config on the session\n        # will raise ProfileNotFound.  Sometimes this is invalid:\n        #\n        # \"ec2 describe-instances --profile unknown\"\n        #\n        # and sometimes this is perfectly valid:\n        #\n        # \"configure set region us-west-2 --profile brand-new-profile\"\n        #\n        # Because we can't know (and don't want to know) whether\n        # the customer is trying to do something valid, we just\n        # immediately return.  If it's invalid something else\n        # up the stack will raise ProfileNotFound, otherwise\n        # the configure (and other) commands will work as expected.\n        LOG.debug(\"ProfileNotFound caught when trying to inject \"\n                  \"assume-role cred provider cache.  Not configuring \"\n                  \"JSONFileCache for assume-role.\")\n        return\n    provider = cred_chain.get_provider('assume-role')\n    provider.cache = JSONFileCache()\n\n\nclass JSONFileCache(object):\n    \"\"\"JSON file cache.\n\n    This provides a dict like interface that stores JSON serializable\n    objects.\n\n    The objects are serialized to JSON and stored in a file.  These\n    values can be retrieved at a later time.\n\n    \"\"\"\n\n    CACHE_DIR = os.path.expanduser(os.path.join('~', '.aws', 'cli', 'cache'))\n\n    def __init__(self, working_dir=CACHE_DIR):\n        self._working_dir = working_dir\n\n    def __contains__(self, cache_key):\n        actual_key = self._convert_cache_key(cache_key)\n        return os.path.isfile(actual_key)\n\n    def __getitem__(self, cache_key):\n        \"\"\"Retrieve value from a cache key.\"\"\"\n        actual_key = self._convert_cache_key(cache_key)\n        try:\n            with open(actual_key) as f:\n                return json.load(f)\n        except (OSError, ValueError, IOError):\n            raise KeyError(cache_key)\n\n    def __setitem__(self, cache_key, value):\n        full_key = self._convert_cache_key(cache_key)\n        try:\n            file_content = json.dumps(value)\n        except (TypeError, ValueError):\n            raise ValueError(\"Value cannot be cached, must be \"\n                             \"JSON serializable: %s\" % value)\n        if not os.path.isdir(self._working_dir):\n            os.makedirs(self._working_dir)\n        with os.fdopen(os.open(full_key,\n                               os.O_WRONLY | os.O_CREAT, 0o600), 'w') as f:\n            f.truncate()\n            f.write(file_content)\n\n    def _convert_cache_key(self, cache_key):\n        full_path = os.path.join(self._working_dir, cache_key + '.json')\n        return full_path\n", "path": "awscli/customizations/assumerole.py"}]}