We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
S3 Sync with "--delete" option is deleting files that have folders with the same name!
If you have a folder in your S3 bucket and a file name that contains the same starting characters as the folder name, the target files will be intermittently be deleted upon each run.

For example, create a folder in your S3 bucket:

s3://my-bucket/test/

Now create some txt files in the bucket with 'test' as the first 4 characters of the file name.. ie:

s3://my-bucket/test-123.txt
s3://my-bucket/test-321.txt
s3://my-bucket/test.txt

Run `aws s3 sync --delete s3://my-bucket /my/local/folder`.. Do this 3 or 4 times. You will see each file 'test-123.txt', 'test-321.txt', 'test.txt' will get intermittently be deleted and downloaded with each run. Each run produces different results.

Having files unexpectedly being deleting is a big concern as we use 's3 sync --delete' for daily backup's.

Please see AWS post for originating report https://forums.aws.amazon.com/message.jspa?messageID=497335#497335


--- 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/filegenerator.py`
Content:
```
1 # Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License"). You
4 # may not use this file except in compliance with the License. A copy of
5 # the License is located at
6 #
7 #     http://aws.amazon.com/apache2.0/
8 #
9 # or in the "license" file accompanying this file. This file is
10 # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11 # ANY KIND, either express or implied. See the License for the specific
12 # language governing permissions and limitations under the License.
13 import os
14 
15 from dateutil.parser import parse
16 from dateutil.tz import tzlocal
17 
18 from awscli.customizations.s3.fileinfo import FileInfo
19 from awscli.customizations.s3.utils import find_bucket_key, get_file_stat
20 
21 
22 class FileGenerator(object):
23     """
24     This is a class the creates a generator to yield files based on information
25     returned from the ``FileFormat`` class.  It is universal in the sense that
26     it will handle s3 files, local files, local directories, and s3 objects
27     under the same common prefix.  The generator yields corresponding
28     ``FileInfo`` objects to send to a ``Comparator`` or ``S3Handler``.
29     """
30     def __init__(self, service, endpoint, operation_name, parameters):
31         self._service = service
32         self._endpoint = endpoint
33         self.operation_name = operation_name
34 
35     def call(self, files):
36         """
37         This is the generalized function to yield the ``FileInfo`` objects.
38         ``dir_op`` and ``use_src_name`` flags affect which files are used and
39         ensure the proper destination paths and compare keys are formed.
40         """
41         src = files['src']
42         dest = files['dest']
43         src_type = src['type']
44         dest_type = dest['type']
45         function_table = {'s3': self.list_objects, 'local': self.list_files}
46         sep_table = {'s3': '/', 'local': os.sep}
47         source = src['path']
48         file_list = function_table[src_type](source, files['dir_op'])
49         for src_path, size, last_update in file_list:
50             if files['dir_op']:
51                 rel_path = src_path[len(src['path']):]
52             else:
53                 rel_path = src_path.split(sep_table[src_type])[-1]
54             compare_key = rel_path.replace(sep_table[src_type], '/')
55             if files['use_src_name']:
56                 dest_path = dest['path']
57                 dest_path += rel_path.replace(sep_table[src_type],
58                                               sep_table[dest_type])
59             else:
60                 dest_path = dest['path']
61             yield FileInfo(src=src_path, dest=dest_path,
62                            compare_key=compare_key, size=size,
63                            last_update=last_update, src_type=src_type,
64                            service=self._service, endpoint=self._endpoint,
65                            dest_type=dest_type,
66                            operation_name=self.operation_name)
67 
68     def list_files(self, path, dir_op):
69         """
70         This function yields the appropriate local file or local files
71         under a directory depending on if the operation is on a directory.
72         For directories a depth first search is implemented in order to
73         follow the same sorted pattern as a s3 list objects operation
74         outputs.  It yields the file's source path, size, and last
75         update
76         """
77         join, isdir, isfile = os.path.join, os.path.isdir, os.path.isfile
78         error, listdir = os.error, os.listdir
79         if not dir_op:
80             size, last_update = get_file_stat(path)
81             yield path, size, last_update
82         else:
83             names = sorted(listdir(path))
84             for name in names:
85                 file_path = join(path, name)
86                 if isdir(file_path):
87                     for x in self.list_files(file_path, dir_op):
88                         yield x
89                 else:
90                     size, last_update = get_file_stat(file_path)
91                     yield file_path, size, last_update
92 
93     def list_objects(self, s3_path, dir_op):
94         """
95         This function yields the appropriate object or objects under a
96         common prefix depending if the operation is on objects under a
97         common prefix.  It yields the file's source path, size, and last
98         update.
99         """
100         operation = self._service.get_operation('ListObjects')
101         bucket, prefix = find_bucket_key(s3_path)
102         iterator = operation.paginate(self._endpoint, bucket=bucket,
103                                       prefix=prefix)
104         for html_response, response_data in iterator:
105             contents = response_data['Contents']
106             for content in contents:
107                 src_path = bucket + '/' + content['Key']
108                 size = content['Size']
109                 last_update = parse(content['LastModified'])
110                 last_update = last_update.astimezone(tzlocal())
111                 if size == 0 and src_path.endswith('/'):
112                     if self.operation_name == 'delete':
113                         # This is to filter out manually created folders
114                         # in S3.  They have a size zero and would be
115                         # undesirably downloaded.  Local directories
116                         # are automatically created when they do not
117                         # exist locally.  But user should be able to
118                         # delete them.
119                         yield src_path, size, last_update
120                 elif not dir_op and s3_path != src_path:
121                     pass
122                 else:
123                     yield src_path, size, last_update
124
```
--- 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/filegenerator.py b/awscli/customizations/s3/filegenerator.py
--- a/awscli/customizations/s3/filegenerator.py
+++ b/awscli/customizations/s3/filegenerator.py
@@ -80,10 +80,28 @@
             size, last_update = get_file_stat(path)
             yield path, size, last_update
         else:
-            names = sorted(listdir(path))
+            # We need to list files in byte order based on the full
+            # expanded path of the key: 'test/1/2/3.txt'  However, listdir()
+            # will only give us contents a single directory at a time, so we'll
+            # get 'test'.  At the same time we don't want to load the entire
+            # list of files into memory.  This is handled by first going
+            # through the current directory contents and adding the directory
+            # separator to any directories.  We can then sort the contents,
+            # and ensure byte order.
+            names = listdir(path)
+            for i, name in enumerate(names):
+                file_path = join(path, name)
+                if isdir(file_path):
+                    names[i] = name + os.path.sep
+            names.sort()
             for name in names:
                 file_path = join(path, name)
                 if isdir(file_path):
+                    # Anything in a directory will have a prefix of this
+                    # current directory and will come before the
+                    # remaining contents in this directory.  This means we need
+                    # to recurse into this sub directory before yielding the
+                    # rest of this directory's contents.
                     for x in self.list_files(file_path, dir_op):
                         yield x
                 else:

{"golden_diff": "diff --git a/awscli/customizations/s3/filegenerator.py b/awscli/customizations/s3/filegenerator.py\n--- a/awscli/customizations/s3/filegenerator.py\n+++ b/awscli/customizations/s3/filegenerator.py\n@@ -80,10 +80,28 @@\n             size, last_update = get_file_stat(path)\n             yield path, size, last_update\n         else:\n-            names = sorted(listdir(path))\n+            # We need to list files in byte order based on the full\n+            # expanded path of the key: 'test/1/2/3.txt'  However, listdir()\n+            # will only give us contents a single directory at a time, so we'll\n+            # get 'test'.  At the same time we don't want to load the entire\n+            # list of files into memory.  This is handled by first going\n+            # through the current directory contents and adding the directory\n+            # separator to any directories.  We can then sort the contents,\n+            # and ensure byte order.\n+            names = listdir(path)\n+            for i, name in enumerate(names):\n+                file_path = join(path, name)\n+                if isdir(file_path):\n+                    names[i] = name + os.path.sep\n+            names.sort()\n             for name in names:\n                 file_path = join(path, name)\n                 if isdir(file_path):\n+                    # Anything in a directory will have a prefix of this\n+                    # current directory and will come before the\n+                    # remaining contents in this directory.  This means we need\n+                    # to recurse into this sub directory before yielding the\n+                    # rest of this directory's contents.\n                     for x in self.list_files(file_path, dir_op):\n                         yield x\n                 else:\n", "issue": "S3 Sync with \"--delete\" option is deleting files that have folders with the same name!\nIf you have a folder in your S3 bucket and a file name that contains the same starting characters as the folder name, the target files will be intermittently be deleted upon each run.\n\nFor example, create a folder in your S3 bucket:\n\ns3://my-bucket/test/\n\nNow create some txt files in the bucket with 'test' as the first 4 characters of the file name.. ie:\n\ns3://my-bucket/test-123.txt\ns3://my-bucket/test-321.txt\ns3://my-bucket/test.txt\n\nRun `aws s3 sync --delete s3://my-bucket /my/local/folder`.. Do this 3 or 4 times. You will see each file 'test-123.txt', 'test-321.txt', 'test.txt' will get intermittently be deleted and downloaded with each run. Each run produces different results.\n\nHaving files unexpectedly being deleting is a big concern as we use 's3 sync --delete' for daily backup's.\n\nPlease see AWS post for originating report https://forums.aws.amazon.com/message.jspa?messageID=497335#497335\n\n", "before_files": [{"content": "# Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"). You\n# may not use this file except in compliance with the License. A copy of\n# the License is located at\n#\n#     http://aws.amazon.com/apache2.0/\n#\n# or in the \"license\" file accompanying this file. This file is\n# distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF\n# ANY KIND, either express or implied. See the License for the specific\n# language governing permissions and limitations under the License.\nimport os\n\nfrom dateutil.parser import parse\nfrom dateutil.tz import tzlocal\n\nfrom awscli.customizations.s3.fileinfo import FileInfo\nfrom awscli.customizations.s3.utils import find_bucket_key, get_file_stat\n\n\nclass FileGenerator(object):\n    \"\"\"\n    This is a class the creates a generator to yield files based on information\n    returned from the ``FileFormat`` class.  It is universal in the sense that\n    it will handle s3 files, local files, local directories, and s3 objects\n    under the same common prefix.  The generator yields corresponding\n    ``FileInfo`` objects to send to a ``Comparator`` or ``S3Handler``.\n    \"\"\"\n    def __init__(self, service, endpoint, operation_name, parameters):\n        self._service = service\n        self._endpoint = endpoint\n        self.operation_name = operation_name\n\n    def call(self, files):\n        \"\"\"\n        This is the generalized function to yield the ``FileInfo`` objects.\n        ``dir_op`` and ``use_src_name`` flags affect which files are used and\n        ensure the proper destination paths and compare keys are formed.\n        \"\"\"\n        src = files['src']\n        dest = files['dest']\n        src_type = src['type']\n        dest_type = dest['type']\n        function_table = {'s3': self.list_objects, 'local': self.list_files}\n        sep_table = {'s3': '/', 'local': os.sep}\n        source = src['path']\n        file_list = function_table[src_type](source, files['dir_op'])\n        for src_path, size, last_update in file_list:\n            if files['dir_op']:\n                rel_path = src_path[len(src['path']):]\n            else:\n                rel_path = src_path.split(sep_table[src_type])[-1]\n            compare_key = rel_path.replace(sep_table[src_type], '/')\n            if files['use_src_name']:\n                dest_path = dest['path']\n                dest_path += rel_path.replace(sep_table[src_type],\n                                              sep_table[dest_type])\n            else:\n                dest_path = dest['path']\n            yield FileInfo(src=src_path, dest=dest_path,\n                           compare_key=compare_key, size=size,\n                           last_update=last_update, src_type=src_type,\n                           service=self._service, endpoint=self._endpoint,\n                           dest_type=dest_type,\n                           operation_name=self.operation_name)\n\n    def list_files(self, path, dir_op):\n        \"\"\"\n        This function yields the appropriate local file or local files\n        under a directory depending on if the operation is on a directory.\n        For directories a depth first search is implemented in order to\n        follow the same sorted pattern as a s3 list objects operation\n        outputs.  It yields the file's source path, size, and last\n        update\n        \"\"\"\n        join, isdir, isfile = os.path.join, os.path.isdir, os.path.isfile\n        error, listdir = os.error, os.listdir\n        if not dir_op:\n            size, last_update = get_file_stat(path)\n            yield path, size, last_update\n        else:\n            names = sorted(listdir(path))\n            for name in names:\n                file_path = join(path, name)\n                if isdir(file_path):\n                    for x in self.list_files(file_path, dir_op):\n                        yield x\n                else:\n                    size, last_update = get_file_stat(file_path)\n                    yield file_path, size, last_update\n\n    def list_objects(self, s3_path, dir_op):\n        \"\"\"\n        This function yields the appropriate object or objects under a\n        common prefix depending if the operation is on objects under a\n        common prefix.  It yields the file's source path, size, and last\n        update.\n        \"\"\"\n        operation = self._service.get_operation('ListObjects')\n        bucket, prefix = find_bucket_key(s3_path)\n        iterator = operation.paginate(self._endpoint, bucket=bucket,\n                                      prefix=prefix)\n        for html_response, response_data in iterator:\n            contents = response_data['Contents']\n            for content in contents:\n                src_path = bucket + '/' + content['Key']\n                size = content['Size']\n                last_update = parse(content['LastModified'])\n                last_update = last_update.astimezone(tzlocal())\n                if size == 0 and src_path.endswith('/'):\n                    if self.operation_name == 'delete':\n                        # This is to filter out manually created folders\n                        # in S3.  They have a size zero and would be\n                        # undesirably downloaded.  Local directories\n                        # are automatically created when they do not\n                        # exist locally.  But user should be able to\n                        # delete them.\n                        yield src_path, size, last_update\n                elif not dir_op and s3_path != src_path:\n                    pass\n                else:\n                    yield src_path, size, last_update\n", "path": "awscli/customizations/s3/filegenerator.py"}], "after_files": [{"content": "# Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"). You\n# may not use this file except in compliance with the License. A copy of\n# the License is located at\n#\n#     http://aws.amazon.com/apache2.0/\n#\n# or in the \"license\" file accompanying this file. This file is\n# distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF\n# ANY KIND, either express or implied. See the License for the specific\n# language governing permissions and limitations under the License.\nimport os\n\nfrom dateutil.parser import parse\nfrom dateutil.tz import tzlocal\n\nfrom awscli.customizations.s3.fileinfo import FileInfo\nfrom awscli.customizations.s3.utils import find_bucket_key, get_file_stat\n\n\nclass FileGenerator(object):\n    \"\"\"\n    This is a class the creates a generator to yield files based on information\n    returned from the ``FileFormat`` class.  It is universal in the sense that\n    it will handle s3 files, local files, local directories, and s3 objects\n    under the same common prefix.  The generator yields corresponding\n    ``FileInfo`` objects to send to a ``Comparator`` or ``S3Handler``.\n    \"\"\"\n    def __init__(self, service, endpoint, operation_name, parameters):\n        self._service = service\n        self._endpoint = endpoint\n        self.operation_name = operation_name\n\n    def call(self, files):\n        \"\"\"\n        This is the generalized function to yield the ``FileInfo`` objects.\n        ``dir_op`` and ``use_src_name`` flags affect which files are used and\n        ensure the proper destination paths and compare keys are formed.\n        \"\"\"\n        src = files['src']\n        dest = files['dest']\n        src_type = src['type']\n        dest_type = dest['type']\n        function_table = {'s3': self.list_objects, 'local': self.list_files}\n        sep_table = {'s3': '/', 'local': os.sep}\n        source = src['path']\n        file_list = function_table[src_type](source, files['dir_op'])\n        for src_path, size, last_update in file_list:\n            if files['dir_op']:\n                rel_path = src_path[len(src['path']):]\n            else:\n                rel_path = src_path.split(sep_table[src_type])[-1]\n            compare_key = rel_path.replace(sep_table[src_type], '/')\n            if files['use_src_name']:\n                dest_path = dest['path']\n                dest_path += rel_path.replace(sep_table[src_type],\n                                              sep_table[dest_type])\n            else:\n                dest_path = dest['path']\n            yield FileInfo(src=src_path, dest=dest_path,\n                           compare_key=compare_key, size=size,\n                           last_update=last_update, src_type=src_type,\n                           service=self._service, endpoint=self._endpoint,\n                           dest_type=dest_type,\n                           operation_name=self.operation_name)\n\n    def list_files(self, path, dir_op):\n        \"\"\"\n        This function yields the appropriate local file or local files\n        under a directory depending on if the operation is on a directory.\n        For directories a depth first search is implemented in order to\n        follow the same sorted pattern as a s3 list objects operation\n        outputs.  It yields the file's source path, size, and last\n        update\n        \"\"\"\n        join, isdir, isfile = os.path.join, os.path.isdir, os.path.isfile\n        error, listdir = os.error, os.listdir\n        if not dir_op:\n            size, last_update = get_file_stat(path)\n            yield path, size, last_update\n        else:\n            # We need to list files in byte order based on the full\n            # expanded path of the key: 'test/1/2/3.txt'  However, listdir()\n            # will only give us contents a single directory at a time, so we'll\n            # get 'test'.  At the same time we don't want to load the entire\n            # list of files into memory.  This is handled by first going\n            # through the current directory contents and adding the directory\n            # separator to any directories.  We can then sort the contents,\n            # and ensure byte order.\n            names = listdir(path)\n            for i, name in enumerate(names):\n                file_path = join(path, name)\n                if isdir(file_path):\n                    names[i] = name + os.path.sep\n            names.sort()\n            for name in names:\n                file_path = join(path, name)\n                if isdir(file_path):\n                    # Anything in a directory will have a prefix of this\n                    # current directory and will come before the\n                    # remaining contents in this directory.  This means we need\n                    # to recurse into this sub directory before yielding the\n                    # rest of this directory's contents.\n                    for x in self.list_files(file_path, dir_op):\n                        yield x\n                else:\n                    size, last_update = get_file_stat(file_path)\n                    yield file_path, size, last_update\n\n    def list_objects(self, s3_path, dir_op):\n        \"\"\"\n        This function yields the appropriate object or objects under a\n        common prefix depending if the operation is on objects under a\n        common prefix.  It yields the file's source path, size, and last\n        update.\n        \"\"\"\n        operation = self._service.get_operation('ListObjects')\n        bucket, prefix = find_bucket_key(s3_path)\n        iterator = operation.paginate(self._endpoint, bucket=bucket,\n                                      prefix=prefix)\n        for html_response, response_data in iterator:\n            contents = response_data['Contents']\n            for content in contents:\n                src_path = bucket + '/' + content['Key']\n                size = content['Size']\n                last_update = parse(content['LastModified'])\n                last_update = last_update.astimezone(tzlocal())\n                if size == 0 and src_path.endswith('/'):\n                    if self.operation_name == 'delete':\n                        # This is to filter out manually created folders\n                        # in S3.  They have a size zero and would be\n                        # undesirably downloaded.  Local directories\n                        # are automatically created when they do not\n                        # exist locally.  But user should be able to\n                        # delete them.\n                        yield src_path, size, last_update\n                elif not dir_op and s3_path != src_path:\n                    pass\n                else:\n                    yield src_path, size, last_update\n", "path": "awscli/customizations/s3/filegenerator.py"}]}