We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
S3 transfer does not properly handle running out of disk space
If we run out of disk space (including inodes) we keep trying to sync.  We also don't throw any kind of exception when this happens.  I believe the root cause if from this code snippet:

```
def save_file(filename, response_data, last_update):
    body = response_data['Body']
    etag = response_data['ETag'][1:-1]
    d = os.path.dirname(filename)
    try:
        if not os.path.exists(d):
            os.makedirs(d)
    except Exception:      # <---- bad
        pass

```

We should be more selective in this case and only allow `errno.EEXIST` errors.


--- 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/s3/fileinfo.py`
Content:
```
1 import os
2 import sys
3 import time
4 from functools import partial
5 import hashlib
6 
7 from dateutil.parser import parse
8 from dateutil.tz import tzlocal
9 
10 from botocore.compat import quote
11 from awscli.customizations.s3.utils import find_bucket_key, \
12         check_etag, check_error, operate, uni_print, \
13         guess_content_type, MD5Error
14 
15 
16 def read_file(filename):
17     """
18     This reads the file into a form that can be sent to S3
19     """
20     with open(filename, 'rb') as in_file:
21         return in_file.read()
22 
23 
24 def save_file(filename, response_data, last_update):
25     """
26     This writes to the file upon downloading.  It reads the data in the
27     response.  Makes a new directory if needed and then writes the
28     data to the file.  It also modifies the last modified time to that
29     of the S3 object.
30     """
31     body = response_data['Body']
32     etag = response_data['ETag'][1:-1]
33     d = os.path.dirname(filename)
34     try:
35         if not os.path.exists(d):
36             os.makedirs(d)
37     except Exception:
38         pass
39     md5 = hashlib.md5()
40     file_chunks = iter(partial(body.read, 1024 * 1024), b'')
41     with open(filename, 'wb') as out_file:
42         if not _is_multipart_etag(etag):
43             for chunk in file_chunks:
44                 md5.update(chunk)
45                 out_file.write(chunk)
46         else:
47             for chunk in file_chunks:
48                 out_file.write(chunk)
49     if not _is_multipart_etag(etag):
50         if etag != md5.hexdigest():
51             os.remove(filename)
52             raise MD5Error(filename)
53     last_update_tuple = last_update.timetuple()
54     mod_timestamp = time.mktime(last_update_tuple)
55     os.utime(filename, (int(mod_timestamp), int(mod_timestamp)))
56 
57 
58 def _is_multipart_etag(etag):
59     return '-' in etag
60 
61 
62 class TaskInfo(object):
63     """
64     This class contains important details related to performing a task.  This
65     object is usually only used for creating buckets, removing buckets, and
66     listing objects/buckets.  This object contains the attributes and
67     functions needed to perform the task.  Note that just instantiating one
68     of these objects will not be enough to run a listing or bucket command.
69     unless ``session`` and ``region`` are specified upon instantiation.
70 
71     :param src: the source path
72     :type src: string
73     :param src_type: if the source file is s3 or local.
74     :type src_type: string
75     :param operation: the operation being performed.
76     :type operation: string
77     :param session: ``botocore.session`` object
78     :param region: The region for the endpoint
79 
80     Note that a local file will always have its absolute path, and a s3 file
81     will have its path in the form of bucket/key
82     """
83     def __init__(self, src, src_type, operation_name, service, endpoint):
84         self.src = src
85         self.src_type = src_type
86         self.operation_name = operation_name
87         self.service = service
88         self.endpoint = endpoint
89 
90     def make_bucket(self):
91         """
92         This opereation makes a bucket.
93         """
94         bucket, key = find_bucket_key(self.src)
95         bucket_config = {'LocationConstraint': self.endpoint.region_name}
96         params = {'endpoint': self.endpoint, 'bucket': bucket}
97         if self.endpoint.region_name != 'us-east-1':
98             params['create_bucket_configuration'] = bucket_config
99         response_data, http = operate(self.service, 'CreateBucket', params)
100 
101     def remove_bucket(self):
102         """
103         This operation removes a bucket.
104         """
105         bucket, key = find_bucket_key(self.src)
106         params = {'endpoint': self.endpoint, 'bucket': bucket}
107         response_data, http = operate(self.service, 'DeleteBucket', params)
108 
109 
110 class FileInfo(TaskInfo):
111     """
112     This is a child object of the ``TaskInfo`` object.  It can perform more
113     operations such as ``upload``, ``download``, ``copy``, ``delete``,
114     ``move``.  Similiarly to
115     ``TaskInfo`` objects attributes like ``session`` need to be set in order
116     to perform operations.
117 
118     :param dest: the destination path
119     :type dest: string
120     :param compare_key: the name of the file relative to the specified
121         directory/prefix.  This variable is used when performing synching
122         or if the destination file is adopting the source file's name.
123     :type compare_key: string
124     :param size: The size of the file in bytes.
125     :type size: integer
126     :param last_update: the local time of last modification.
127     :type last_update: datetime object
128     :param dest_type: if the destination is s3 or local.
129     :param dest_type: string
130     :param parameters: a dictionary of important values this is assigned in
131         the ``BasicTask`` object.
132     """
133     def __init__(self, src, dest=None, compare_key=None, size=None,
134                  last_update=None, src_type=None, dest_type=None,
135                  operation_name=None, service=None, endpoint=None,
136                  parameters=None):
137         super(FileInfo, self).__init__(src, src_type=src_type,
138                                        operation_name=operation_name,
139                                        service=service,
140                                        endpoint=endpoint)
141         self.dest = dest
142         self.dest_type = dest_type
143         self.compare_key = compare_key
144         self.size = size
145         self.last_update = last_update
146         # Usually inject ``parameters`` from ``BasicTask`` class.
147         if parameters is not None:
148             self.parameters = parameters
149         else:
150             self.parameters = {'acl': None,
151                                'sse': None}
152 
153     def _permission_to_param(self, permission):
154         if permission == 'read':
155             return 'grant_read'
156         if permission == 'full':
157             return 'grant_full_control'
158         if permission == 'readacl':
159             return 'grant_read_acp'
160         if permission == 'writeacl':
161             return 'grant_write_acp'
162         raise ValueError('permission must be one of: '
163                          'read|readacl|writeacl|full')
164 
165     def _handle_object_params(self, params):
166         if self.parameters['acl']:
167             params['acl'] = self.parameters['acl'][0]
168         if self.parameters['grants']:
169             for grant in self.parameters['grants']:
170                 try:
171                     permission, grantee = grant.split('=', 1)
172                 except ValueError:
173                     raise ValueError('grants should be of the form '
174                                      'permission=principal')
175                 params[self._permission_to_param(permission)] = grantee
176         if self.parameters['sse']:
177             params['server_side_encryption'] = 'AES256'
178         if self.parameters['storage_class']:
179             params['storage_class'] = self.parameters['storage_class'][0]
180         if self.parameters['website_redirect']:
181             params['website_redirect_location'] = \
182                     self.parameters['website_redirect'][0]
183         if self.parameters['guess_mime_type']:
184             self._inject_content_type(params, self.src)
185         if self.parameters['content_type']:
186             params['content_type'] = self.parameters['content_type'][0]
187         if self.parameters['cache_control']:
188             params['cache_control'] = self.parameters['cache_control'][0]
189         if self.parameters['content_disposition']:
190             params['content_disposition'] = \
191                     self.parameters['content_disposition'][0]
192         if self.parameters['content_encoding']:
193             params['content_encoding'] = self.parameters['content_encoding'][0]
194         if self.parameters['content_language']:
195             params['content_language'] = self.parameters['content_language'][0]
196         if self.parameters['expires']:
197             params['expires'] = self.parameters['expires'][0]
198 
199     def upload(self):
200         """
201         Redirects the file to the multipart upload function if the file is
202         large.  If it is small enough, it puts the file as an object in s3.
203         """
204         with open(self.src, 'rb') as body:
205             bucket, key = find_bucket_key(self.dest)
206             params = {
207                 'endpoint': self.endpoint,
208                 'bucket': bucket,
209                 'key': key,
210                 'body': body,
211             }
212             self._handle_object_params(params)
213             response_data, http = operate(self.service, 'PutObject', params)
214             etag = response_data['ETag'][1:-1]
215             body.seek(0)
216             check_etag(etag, body)
217 
218     def _inject_content_type(self, params, filename):
219         # Add a content type param if we can guess the type.
220         guessed_type = guess_content_type(filename)
221         if guessed_type is not None:
222             params['content_type'] = guessed_type
223 
224     def download(self):
225         """
226         Redirects the file to the multipart download function if the file is
227         large.  If it is small enough, it gets the file as an object from s3.
228         """
229         bucket, key = find_bucket_key(self.src)
230         params = {'endpoint': self.endpoint, 'bucket': bucket, 'key': key}
231         response_data, http = operate(self.service, 'GetObject', params)
232         save_file(self.dest, response_data, self.last_update)
233 
234     def copy(self):
235         """
236         Copies a object in s3 to another location in s3.
237         """
238         copy_source = self.src
239         bucket, key = find_bucket_key(self.dest)
240         params = {'endpoint': self.endpoint, 'bucket': bucket,
241                   'copy_source': copy_source, 'key': key}
242         self._handle_object_params(params)
243         response_data, http = operate(self.service, 'CopyObject', params)
244 
245     def delete(self):
246         """
247         Deletes the file from s3 or local.  The src file and type is used
248         from the file info object.
249         """
250         if (self.src_type == 's3'):
251             bucket, key = find_bucket_key(self.src)
252             params = {'endpoint': self.endpoint, 'bucket': bucket, 'key': key}
253             response_data, http = operate(self.service, 'DeleteObject',
254                                           params)
255         else:
256             os.remove(self.src)
257 
258     def move(self):
259         """
260         Implements a move command for s3.
261         """
262         src = self.src_type
263         dest = self.dest_type
264         if src == 'local' and dest == 's3':
265             self.upload()
266         elif src == 's3' and dest == 's3':
267             self.copy()
268         elif src == 's3' and dest == 'local':
269             self.download()
270         else:
271             raise Exception("Invalid path arguments for mv")
272         self.delete()
273 
274     def create_multipart_upload(self):
275         bucket, key = find_bucket_key(self.dest)
276         params = {'endpoint': self.endpoint, 'bucket': bucket, 'key': key}
277         self._handle_object_params(params)
278         response_data, http = operate(self.service, 'CreateMultipartUpload',
279                                       params)
280         upload_id = response_data['UploadId']
281         return upload_id
282
```
--- 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/s3/fileinfo.py b/awscli/customizations/s3/fileinfo.py
--- a/awscli/customizations/s3/fileinfo.py
+++ b/awscli/customizations/s3/fileinfo.py
@@ -2,6 +2,7 @@
 import sys
 import time
 from functools import partial
+import errno
 import hashlib
 
 from dateutil.parser import parse
@@ -13,6 +14,10 @@
         guess_content_type, MD5Error
 
 
+class CreateDirectoryError(Exception):
+    pass
+
+
 def read_file(filename):
     """
     This reads the file into a form that can be sent to S3
@@ -34,8 +39,10 @@
     try:
         if not os.path.exists(d):
             os.makedirs(d)
-    except Exception:
-        pass
+    except OSError as e:
+        if not e.errno == errno.EEXIST:
+            raise CreateDirectoryError(
+                "Could not create directory %s: %s" % (d, e))
     md5 = hashlib.md5()
     file_chunks = iter(partial(body.read, 1024 * 1024), b'')
     with open(filename, 'wb') as out_file:

{"golden_diff": "diff --git a/awscli/customizations/s3/fileinfo.py b/awscli/customizations/s3/fileinfo.py\n--- a/awscli/customizations/s3/fileinfo.py\n+++ b/awscli/customizations/s3/fileinfo.py\n@@ -2,6 +2,7 @@\n import sys\n import time\n from functools import partial\n+import errno\n import hashlib\n \n from dateutil.parser import parse\n@@ -13,6 +14,10 @@\n         guess_content_type, MD5Error\n \n \n+class CreateDirectoryError(Exception):\n+    pass\n+\n+\n def read_file(filename):\n     \"\"\"\n     This reads the file into a form that can be sent to S3\n@@ -34,8 +39,10 @@\n     try:\n         if not os.path.exists(d):\n             os.makedirs(d)\n-    except Exception:\n-        pass\n+    except OSError as e:\n+        if not e.errno == errno.EEXIST:\n+            raise CreateDirectoryError(\n+                \"Could not create directory %s: %s\" % (d, e))\n     md5 = hashlib.md5()\n     file_chunks = iter(partial(body.read, 1024 * 1024), b'')\n     with open(filename, 'wb') as out_file:\n", "issue": "S3 transfer does not properly handle running out of disk space\nIf we run out of disk space (including inodes) we keep trying to sync.  We also don't throw any kind of exception when this happens.  I believe the root cause if from this code snippet:\n\n```\ndef save_file(filename, response_data, last_update):\n    body = response_data['Body']\n    etag = response_data['ETag'][1:-1]\n    d = os.path.dirname(filename)\n    try:\n        if not os.path.exists(d):\n            os.makedirs(d)\n    except Exception:      # <---- bad\n        pass\n\n```\n\nWe should be more selective in this case and only allow `errno.EEXIST` errors.\n\n", "before_files": [{"content": "import os\nimport sys\nimport time\nfrom functools import partial\nimport hashlib\n\nfrom dateutil.parser import parse\nfrom dateutil.tz import tzlocal\n\nfrom botocore.compat import quote\nfrom awscli.customizations.s3.utils import find_bucket_key, \\\n        check_etag, check_error, operate, uni_print, \\\n        guess_content_type, MD5Error\n\n\ndef read_file(filename):\n    \"\"\"\n    This reads the file into a form that can be sent to S3\n    \"\"\"\n    with open(filename, 'rb') as in_file:\n        return in_file.read()\n\n\ndef save_file(filename, response_data, last_update):\n    \"\"\"\n    This writes to the file upon downloading.  It reads the data in the\n    response.  Makes a new directory if needed and then writes the\n    data to the file.  It also modifies the last modified time to that\n    of the S3 object.\n    \"\"\"\n    body = response_data['Body']\n    etag = response_data['ETag'][1:-1]\n    d = os.path.dirname(filename)\n    try:\n        if not os.path.exists(d):\n            os.makedirs(d)\n    except Exception:\n        pass\n    md5 = hashlib.md5()\n    file_chunks = iter(partial(body.read, 1024 * 1024), b'')\n    with open(filename, 'wb') as out_file:\n        if not _is_multipart_etag(etag):\n            for chunk in file_chunks:\n                md5.update(chunk)\n                out_file.write(chunk)\n        else:\n            for chunk in file_chunks:\n                out_file.write(chunk)\n    if not _is_multipart_etag(etag):\n        if etag != md5.hexdigest():\n            os.remove(filename)\n            raise MD5Error(filename)\n    last_update_tuple = last_update.timetuple()\n    mod_timestamp = time.mktime(last_update_tuple)\n    os.utime(filename, (int(mod_timestamp), int(mod_timestamp)))\n\n\ndef _is_multipart_etag(etag):\n    return '-' in etag\n\n\nclass TaskInfo(object):\n    \"\"\"\n    This class contains important details related to performing a task.  This\n    object is usually only used for creating buckets, removing buckets, and\n    listing objects/buckets.  This object contains the attributes and\n    functions needed to perform the task.  Note that just instantiating one\n    of these objects will not be enough to run a listing or bucket command.\n    unless ``session`` and ``region`` are specified upon instantiation.\n\n    :param src: the source path\n    :type src: string\n    :param src_type: if the source file is s3 or local.\n    :type src_type: string\n    :param operation: the operation being performed.\n    :type operation: string\n    :param session: ``botocore.session`` object\n    :param region: The region for the endpoint\n\n    Note that a local file will always have its absolute path, and a s3 file\n    will have its path in the form of bucket/key\n    \"\"\"\n    def __init__(self, src, src_type, operation_name, service, endpoint):\n        self.src = src\n        self.src_type = src_type\n        self.operation_name = operation_name\n        self.service = service\n        self.endpoint = endpoint\n\n    def make_bucket(self):\n        \"\"\"\n        This opereation makes a bucket.\n        \"\"\"\n        bucket, key = find_bucket_key(self.src)\n        bucket_config = {'LocationConstraint': self.endpoint.region_name}\n        params = {'endpoint': self.endpoint, 'bucket': bucket}\n        if self.endpoint.region_name != 'us-east-1':\n            params['create_bucket_configuration'] = bucket_config\n        response_data, http = operate(self.service, 'CreateBucket', params)\n\n    def remove_bucket(self):\n        \"\"\"\n        This operation removes a bucket.\n        \"\"\"\n        bucket, key = find_bucket_key(self.src)\n        params = {'endpoint': self.endpoint, 'bucket': bucket}\n        response_data, http = operate(self.service, 'DeleteBucket', params)\n\n\nclass FileInfo(TaskInfo):\n    \"\"\"\n    This is a child object of the ``TaskInfo`` object.  It can perform more\n    operations such as ``upload``, ``download``, ``copy``, ``delete``,\n    ``move``.  Similiarly to\n    ``TaskInfo`` objects attributes like ``session`` need to be set in order\n    to perform operations.\n\n    :param dest: the destination path\n    :type dest: string\n    :param compare_key: the name of the file relative to the specified\n        directory/prefix.  This variable is used when performing synching\n        or if the destination file is adopting the source file's name.\n    :type compare_key: string\n    :param size: The size of the file in bytes.\n    :type size: integer\n    :param last_update: the local time of last modification.\n    :type last_update: datetime object\n    :param dest_type: if the destination is s3 or local.\n    :param dest_type: string\n    :param parameters: a dictionary of important values this is assigned in\n        the ``BasicTask`` object.\n    \"\"\"\n    def __init__(self, src, dest=None, compare_key=None, size=None,\n                 last_update=None, src_type=None, dest_type=None,\n                 operation_name=None, service=None, endpoint=None,\n                 parameters=None):\n        super(FileInfo, self).__init__(src, src_type=src_type,\n                                       operation_name=operation_name,\n                                       service=service,\n                                       endpoint=endpoint)\n        self.dest = dest\n        self.dest_type = dest_type\n        self.compare_key = compare_key\n        self.size = size\n        self.last_update = last_update\n        # Usually inject ``parameters`` from ``BasicTask`` class.\n        if parameters is not None:\n            self.parameters = parameters\n        else:\n            self.parameters = {'acl': None,\n                               'sse': None}\n\n    def _permission_to_param(self, permission):\n        if permission == 'read':\n            return 'grant_read'\n        if permission == 'full':\n            return 'grant_full_control'\n        if permission == 'readacl':\n            return 'grant_read_acp'\n        if permission == 'writeacl':\n            return 'grant_write_acp'\n        raise ValueError('permission must be one of: '\n                         'read|readacl|writeacl|full')\n\n    def _handle_object_params(self, params):\n        if self.parameters['acl']:\n            params['acl'] = self.parameters['acl'][0]\n        if self.parameters['grants']:\n            for grant in self.parameters['grants']:\n                try:\n                    permission, grantee = grant.split('=', 1)\n                except ValueError:\n                    raise ValueError('grants should be of the form '\n                                     'permission=principal')\n                params[self._permission_to_param(permission)] = grantee\n        if self.parameters['sse']:\n            params['server_side_encryption'] = 'AES256'\n        if self.parameters['storage_class']:\n            params['storage_class'] = self.parameters['storage_class'][0]\n        if self.parameters['website_redirect']:\n            params['website_redirect_location'] = \\\n                    self.parameters['website_redirect'][0]\n        if self.parameters['guess_mime_type']:\n            self._inject_content_type(params, self.src)\n        if self.parameters['content_type']:\n            params['content_type'] = self.parameters['content_type'][0]\n        if self.parameters['cache_control']:\n            params['cache_control'] = self.parameters['cache_control'][0]\n        if self.parameters['content_disposition']:\n            params['content_disposition'] = \\\n                    self.parameters['content_disposition'][0]\n        if self.parameters['content_encoding']:\n            params['content_encoding'] = self.parameters['content_encoding'][0]\n        if self.parameters['content_language']:\n            params['content_language'] = self.parameters['content_language'][0]\n        if self.parameters['expires']:\n            params['expires'] = self.parameters['expires'][0]\n\n    def upload(self):\n        \"\"\"\n        Redirects the file to the multipart upload function if the file is\n        large.  If it is small enough, it puts the file as an object in s3.\n        \"\"\"\n        with open(self.src, 'rb') as body:\n            bucket, key = find_bucket_key(self.dest)\n            params = {\n                'endpoint': self.endpoint,\n                'bucket': bucket,\n                'key': key,\n                'body': body,\n            }\n            self._handle_object_params(params)\n            response_data, http = operate(self.service, 'PutObject', params)\n            etag = response_data['ETag'][1:-1]\n            body.seek(0)\n            check_etag(etag, body)\n\n    def _inject_content_type(self, params, filename):\n        # Add a content type param if we can guess the type.\n        guessed_type = guess_content_type(filename)\n        if guessed_type is not None:\n            params['content_type'] = guessed_type\n\n    def download(self):\n        \"\"\"\n        Redirects the file to the multipart download function if the file is\n        large.  If it is small enough, it gets the file as an object from s3.\n        \"\"\"\n        bucket, key = find_bucket_key(self.src)\n        params = {'endpoint': self.endpoint, 'bucket': bucket, 'key': key}\n        response_data, http = operate(self.service, 'GetObject', params)\n        save_file(self.dest, response_data, self.last_update)\n\n    def copy(self):\n        \"\"\"\n        Copies a object in s3 to another location in s3.\n        \"\"\"\n        copy_source = self.src\n        bucket, key = find_bucket_key(self.dest)\n        params = {'endpoint': self.endpoint, 'bucket': bucket,\n                  'copy_source': copy_source, 'key': key}\n        self._handle_object_params(params)\n        response_data, http = operate(self.service, 'CopyObject', params)\n\n    def delete(self):\n        \"\"\"\n        Deletes the file from s3 or local.  The src file and type is used\n        from the file info object.\n        \"\"\"\n        if (self.src_type == 's3'):\n            bucket, key = find_bucket_key(self.src)\n            params = {'endpoint': self.endpoint, 'bucket': bucket, 'key': key}\n            response_data, http = operate(self.service, 'DeleteObject',\n                                          params)\n        else:\n            os.remove(self.src)\n\n    def move(self):\n        \"\"\"\n        Implements a move command for s3.\n        \"\"\"\n        src = self.src_type\n        dest = self.dest_type\n        if src == 'local' and dest == 's3':\n            self.upload()\n        elif src == 's3' and dest == 's3':\n            self.copy()\n        elif src == 's3' and dest == 'local':\n            self.download()\n        else:\n            raise Exception(\"Invalid path arguments for mv\")\n        self.delete()\n\n    def create_multipart_upload(self):\n        bucket, key = find_bucket_key(self.dest)\n        params = {'endpoint': self.endpoint, 'bucket': bucket, 'key': key}\n        self._handle_object_params(params)\n        response_data, http = operate(self.service, 'CreateMultipartUpload',\n                                      params)\n        upload_id = response_data['UploadId']\n        return upload_id\n", "path": "awscli/customizations/s3/fileinfo.py"}], "after_files": [{"content": "import os\nimport sys\nimport time\nfrom functools import partial\nimport errno\nimport hashlib\n\nfrom dateutil.parser import parse\nfrom dateutil.tz import tzlocal\n\nfrom botocore.compat import quote\nfrom awscli.customizations.s3.utils import find_bucket_key, \\\n        check_etag, check_error, operate, uni_print, \\\n        guess_content_type, MD5Error\n\n\nclass CreateDirectoryError(Exception):\n    pass\n\n\ndef read_file(filename):\n    \"\"\"\n    This reads the file into a form that can be sent to S3\n    \"\"\"\n    with open(filename, 'rb') as in_file:\n        return in_file.read()\n\n\ndef save_file(filename, response_data, last_update):\n    \"\"\"\n    This writes to the file upon downloading.  It reads the data in the\n    response.  Makes a new directory if needed and then writes the\n    data to the file.  It also modifies the last modified time to that\n    of the S3 object.\n    \"\"\"\n    body = response_data['Body']\n    etag = response_data['ETag'][1:-1]\n    d = os.path.dirname(filename)\n    try:\n        if not os.path.exists(d):\n            os.makedirs(d)\n    except OSError as e:\n        if not e.errno == errno.EEXIST:\n            raise CreateDirectoryError(\n                \"Could not create directory %s: %s\" % (d, e))\n    md5 = hashlib.md5()\n    file_chunks = iter(partial(body.read, 1024 * 1024), b'')\n    with open(filename, 'wb') as out_file:\n        if not _is_multipart_etag(etag):\n            for chunk in file_chunks:\n                md5.update(chunk)\n                out_file.write(chunk)\n        else:\n            for chunk in file_chunks:\n                out_file.write(chunk)\n    if not _is_multipart_etag(etag):\n        if etag != md5.hexdigest():\n            os.remove(filename)\n            raise MD5Error(filename)\n    last_update_tuple = last_update.timetuple()\n    mod_timestamp = time.mktime(last_update_tuple)\n    os.utime(filename, (int(mod_timestamp), int(mod_timestamp)))\n\n\ndef _is_multipart_etag(etag):\n    return '-' in etag\n\n\nclass TaskInfo(object):\n    \"\"\"\n    This class contains important details related to performing a task.  This\n    object is usually only used for creating buckets, removing buckets, and\n    listing objects/buckets.  This object contains the attributes and\n    functions needed to perform the task.  Note that just instantiating one\n    of these objects will not be enough to run a listing or bucket command.\n    unless ``session`` and ``region`` are specified upon instantiation.\n\n    :param src: the source path\n    :type src: string\n    :param src_type: if the source file is s3 or local.\n    :type src_type: string\n    :param operation: the operation being performed.\n    :type operation: string\n    :param session: ``botocore.session`` object\n    :param region: The region for the endpoint\n\n    Note that a local file will always have its absolute path, and a s3 file\n    will have its path in the form of bucket/key\n    \"\"\"\n    def __init__(self, src, src_type, operation_name, service, endpoint):\n        self.src = src\n        self.src_type = src_type\n        self.operation_name = operation_name\n        self.service = service\n        self.endpoint = endpoint\n\n    def make_bucket(self):\n        \"\"\"\n        This opereation makes a bucket.\n        \"\"\"\n        bucket, key = find_bucket_key(self.src)\n        bucket_config = {'LocationConstraint': self.endpoint.region_name}\n        params = {'endpoint': self.endpoint, 'bucket': bucket}\n        if self.endpoint.region_name != 'us-east-1':\n            params['create_bucket_configuration'] = bucket_config\n        response_data, http = operate(self.service, 'CreateBucket', params)\n\n    def remove_bucket(self):\n        \"\"\"\n        This operation removes a bucket.\n        \"\"\"\n        bucket, key = find_bucket_key(self.src)\n        params = {'endpoint': self.endpoint, 'bucket': bucket}\n        response_data, http = operate(self.service, 'DeleteBucket', params)\n\n\nclass FileInfo(TaskInfo):\n    \"\"\"\n    This is a child object of the ``TaskInfo`` object.  It can perform more\n    operations such as ``upload``, ``download``, ``copy``, ``delete``,\n    ``move``.  Similiarly to\n    ``TaskInfo`` objects attributes like ``session`` need to be set in order\n    to perform operations.\n\n    :param dest: the destination path\n    :type dest: string\n    :param compare_key: the name of the file relative to the specified\n        directory/prefix.  This variable is used when performing synching\n        or if the destination file is adopting the source file's name.\n    :type compare_key: string\n    :param size: The size of the file in bytes.\n    :type size: integer\n    :param last_update: the local time of last modification.\n    :type last_update: datetime object\n    :param dest_type: if the destination is s3 or local.\n    :param dest_type: string\n    :param parameters: a dictionary of important values this is assigned in\n        the ``BasicTask`` object.\n    \"\"\"\n    def __init__(self, src, dest=None, compare_key=None, size=None,\n                 last_update=None, src_type=None, dest_type=None,\n                 operation_name=None, service=None, endpoint=None,\n                 parameters=None):\n        super(FileInfo, self).__init__(src, src_type=src_type,\n                                       operation_name=operation_name,\n                                       service=service,\n                                       endpoint=endpoint)\n        self.dest = dest\n        self.dest_type = dest_type\n        self.compare_key = compare_key\n        self.size = size\n        self.last_update = last_update\n        # Usually inject ``parameters`` from ``BasicTask`` class.\n        if parameters is not None:\n            self.parameters = parameters\n        else:\n            self.parameters = {'acl': None,\n                               'sse': None}\n\n    def _permission_to_param(self, permission):\n        if permission == 'read':\n            return 'grant_read'\n        if permission == 'full':\n            return 'grant_full_control'\n        if permission == 'readacl':\n            return 'grant_read_acp'\n        if permission == 'writeacl':\n            return 'grant_write_acp'\n        raise ValueError('permission must be one of: '\n                         'read|readacl|writeacl|full')\n\n    def _handle_object_params(self, params):\n        if self.parameters['acl']:\n            params['acl'] = self.parameters['acl'][0]\n        if self.parameters['grants']:\n            for grant in self.parameters['grants']:\n                try:\n                    permission, grantee = grant.split('=', 1)\n                except ValueError:\n                    raise ValueError('grants should be of the form '\n                                     'permission=principal')\n                params[self._permission_to_param(permission)] = grantee\n        if self.parameters['sse']:\n            params['server_side_encryption'] = 'AES256'\n        if self.parameters['storage_class']:\n            params['storage_class'] = self.parameters['storage_class'][0]\n        if self.parameters['website_redirect']:\n            params['website_redirect_location'] = \\\n                    self.parameters['website_redirect'][0]\n        if self.parameters['guess_mime_type']:\n            self._inject_content_type(params, self.src)\n        if self.parameters['content_type']:\n            params['content_type'] = self.parameters['content_type'][0]\n        if self.parameters['cache_control']:\n            params['cache_control'] = self.parameters['cache_control'][0]\n        if self.parameters['content_disposition']:\n            params['content_disposition'] = \\\n                    self.parameters['content_disposition'][0]\n        if self.parameters['content_encoding']:\n            params['content_encoding'] = self.parameters['content_encoding'][0]\n        if self.parameters['content_language']:\n            params['content_language'] = self.parameters['content_language'][0]\n        if self.parameters['expires']:\n            params['expires'] = self.parameters['expires'][0]\n\n    def upload(self):\n        \"\"\"\n        Redirects the file to the multipart upload function if the file is\n        large.  If it is small enough, it puts the file as an object in s3.\n        \"\"\"\n        with open(self.src, 'rb') as body:\n            bucket, key = find_bucket_key(self.dest)\n            params = {\n                'endpoint': self.endpoint,\n                'bucket': bucket,\n                'key': key,\n                'body': body,\n            }\n            self._handle_object_params(params)\n            response_data, http = operate(self.service, 'PutObject', params)\n            etag = response_data['ETag'][1:-1]\n            body.seek(0)\n            check_etag(etag, body)\n\n    def _inject_content_type(self, params, filename):\n        # Add a content type param if we can guess the type.\n        guessed_type = guess_content_type(filename)\n        if guessed_type is not None:\n            params['content_type'] = guessed_type\n\n    def download(self):\n        \"\"\"\n        Redirects the file to the multipart download function if the file is\n        large.  If it is small enough, it gets the file as an object from s3.\n        \"\"\"\n        bucket, key = find_bucket_key(self.src)\n        params = {'endpoint': self.endpoint, 'bucket': bucket, 'key': key}\n        response_data, http = operate(self.service, 'GetObject', params)\n        save_file(self.dest, response_data, self.last_update)\n\n    def copy(self):\n        \"\"\"\n        Copies a object in s3 to another location in s3.\n        \"\"\"\n        copy_source = self.src\n        bucket, key = find_bucket_key(self.dest)\n        params = {'endpoint': self.endpoint, 'bucket': bucket,\n                  'copy_source': copy_source, 'key': key}\n        self._handle_object_params(params)\n        response_data, http = operate(self.service, 'CopyObject', params)\n\n    def delete(self):\n        \"\"\"\n        Deletes the file from s3 or local.  The src file and type is used\n        from the file info object.\n        \"\"\"\n        if (self.src_type == 's3'):\n            bucket, key = find_bucket_key(self.src)\n            params = {'endpoint': self.endpoint, 'bucket': bucket, 'key': key}\n            response_data, http = operate(self.service, 'DeleteObject',\n                                          params)\n        else:\n            os.remove(self.src)\n\n    def move(self):\n        \"\"\"\n        Implements a move command for s3.\n        \"\"\"\n        src = self.src_type\n        dest = self.dest_type\n        if src == 'local' and dest == 's3':\n            self.upload()\n        elif src == 's3' and dest == 's3':\n            self.copy()\n        elif src == 's3' and dest == 'local':\n            self.download()\n        else:\n            raise Exception(\"Invalid path arguments for mv\")\n        self.delete()\n\n    def create_multipart_upload(self):\n        bucket, key = find_bucket_key(self.dest)\n        params = {'endpoint': self.endpoint, 'bucket': bucket, 'key': key}\n        self._handle_object_params(params)\n        response_data, http = operate(self.service, 'CreateMultipartUpload',\n                                      params)\n        upload_id = response_data['UploadId']\n        return upload_id\n", "path": "awscli/customizations/s3/fileinfo.py"}]}