prompt: 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()
  
  ```
  
golden_diff: 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:
  
verification_info: {"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"}]}