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()

```

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):

{"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"}]}