3434 S3PathResolver
3535from awscli .customizations .utils import uni_print
3636from awscli .customizations .s3 .syncstrategy .base import MissingFileSync , \
37- SizeAndLastModifiedSync , NeverSync , AlwaysSync
38- from awscli .customizations .s3 .syncstrategy .caseconflict import CaseConflictSync
37+ SizeAndLastModifiedSync , NeverSync
3938from awscli .customizations .s3 import transferconfig
4039from awscli .utils import resolve_v2_debug_mode
4140
483482 )
484483}
485484
486- CASE_CONFLICT = {
487- 'name' : 'case-conflict' ,
488- 'choices' : [
489- 'ignore' ,
490- 'skip' ,
491- 'warn' ,
492- 'error' ,
493- ],
494- 'default' : 'warn' ,
495- 'help_text' : (
496- "Configures behavior when attempting to download multiple objects "
497- "whose keys differ only by case, which can cause undefined behavior "
498- "on case-insensitive filesystems. "
499- "This parameter only applies for commands that perform multiple S3 "
500- "to local downloads. "
501- f"See <a href='{ CaseConflictSync .DOC_URI } '>Handling case "
502- "conflicts</a> for details. Valid values are: "
503- "<ul>"
504- "<li>``error`` - Raise an error and abort downloads.</li>"
505- "<li>``warn`` - The default value. Emit a warning and download "
506- "the object.</li>"
507- "<li>``skip`` - Skip downloading the object.</li>"
508- "<li>``ignore`` - Ignore the conflict and download the object.</li>"
509- "</ul>"
510- ),
511- }
512-
513485TRANSFER_ARGS = [DRYRUN , QUIET , INCLUDE , EXCLUDE , ACL ,
514486 FOLLOW_SYMLINKS , NO_FOLLOW_SYMLINKS , NO_GUESS_MIME_TYPE ,
515487 SSE , SSE_C , SSE_C_KEY , SSE_KMS_KEY_ID , SSE_C_COPY_SOURCE ,
@@ -835,8 +807,7 @@ class CpCommand(S3TransferCommand):
835807 "or <S3Uri> <S3Uri>"
836808 ARG_TABLE = [{'name' : 'paths' , 'nargs' : 2 , 'positional_arg' : True ,
837809 'synopsis' : USAGE }] + TRANSFER_ARGS + \
838- [METADATA , METADATA_DIRECTIVE , EXPECTED_SIZE , RECURSIVE ,
839- CASE_CONFLICT ]
810+ [METADATA , METADATA_DIRECTIVE , EXPECTED_SIZE , RECURSIVE ]
840811
841812
842813class MvCommand (S3TransferCommand ):
@@ -846,8 +817,7 @@ class MvCommand(S3TransferCommand):
846817 "or <S3Uri> <S3Uri>"
847818 ARG_TABLE = [{'name' : 'paths' , 'nargs' : 2 , 'positional_arg' : True ,
848819 'synopsis' : USAGE }] + TRANSFER_ARGS + \
849- [METADATA , METADATA_DIRECTIVE , RECURSIVE , VALIDATE_SAME_S3_PATHS ,
850- CASE_CONFLICT ]
820+ [METADATA , METADATA_DIRECTIVE , RECURSIVE , VALIDATE_SAME_S3_PATHS ]
851821
852822
853823class RmCommand (S3TransferCommand ):
@@ -869,7 +839,7 @@ class SyncCommand(S3TransferCommand):
869839 "<LocalPath> or <S3Uri> <S3Uri>"
870840 ARG_TABLE = [{'name' : 'paths' , 'nargs' : 2 , 'positional_arg' : True ,
871841 'synopsis' : USAGE }] + TRANSFER_ARGS + \
872- [METADATA , METADATA_DIRECTIVE , CASE_CONFLICT ]
842+ [METADATA , METADATA_DIRECTIVE ]
873843
874844
875845class MbCommand (S3Command ):
@@ -1034,16 +1004,7 @@ def choose_sync_strategies(self):
10341004 # Set the default strategies.
10351005 sync_strategies ['file_at_src_and_dest_sync_strategy' ] = \
10361006 SizeAndLastModifiedSync ()
1037- if self ._should_handle_case_conflicts ():
1038- sync_strategies ['file_not_at_dest_sync_strategy' ] = (
1039- CaseConflictSync (
1040- on_case_conflict = self .parameters ['case_conflict' ]
1041- )
1042- )
1043- else :
1044- sync_strategies ['file_not_at_dest_sync_strategy' ] = (
1045- MissingFileSync ()
1046- )
1007+ sync_strategies ['file_not_at_dest_sync_strategy' ] = MissingFileSync ()
10471008 sync_strategies ['file_not_at_src_sync_strategy' ] = NeverSync ()
10481009
10491010 # Determine what strategies to override if any.
@@ -1177,12 +1138,6 @@ def run(self):
11771138 'filters' : [create_filter (self .parameters )],
11781139 'file_info_builder' : [file_info_builder ],
11791140 's3_handler' : [s3_transfer_handler ]}
1180- if self ._should_handle_case_conflicts ():
1181- self ._handle_case_conflicts (
1182- command_dict ,
1183- rev_files ,
1184- rev_generator ,
1185- )
11861141 elif self .cmd == 'rm' :
11871142 command_dict = {'setup' : [files ],
11881143 'file_generator' : [file_generator ],
@@ -1195,12 +1150,6 @@ def run(self):
11951150 'filters' : [create_filter (self .parameters )],
11961151 'file_info_builder' : [file_info_builder ],
11971152 's3_handler' : [s3_transfer_handler ]}
1198- if self ._should_handle_case_conflicts ():
1199- self ._handle_case_conflicts (
1200- command_dict ,
1201- rev_files ,
1202- rev_generator ,
1203- )
12041153
12051154 files = command_dict ['setup' ]
12061155 while self .instructions :
@@ -1266,74 +1215,6 @@ def _map_sse_c_params(self, request_parameters, paths_type):
12661215 }
12671216 )
12681217
1269- def _should_handle_case_conflicts (self ):
1270- return (
1271- self .cmd in {'sync' , 'cp' , 'mv' }
1272- and self .parameters .get ('paths_type' ) == 's3local'
1273- and self .parameters ['case_conflict' ] != 'ignore'
1274- and self .parameters .get ('dir_op' )
1275- )
1276-
1277- def _handle_case_conflicts (self , command_dict , rev_files , rev_generator ):
1278- # Objects are not returned in lexicographical order when
1279- # operated on S3 Express directory buckets. This is required
1280- # for sync operations to behave correctly, which is what
1281- # recursive copies and moves fall back to so potential case
1282- # conflicts can be detected and handled.
1283- if not is_s3express_bucket (
1284- split_s3_bucket_key (self .parameters ['src' ])[0 ]
1285- ):
1286- self ._modify_instructions_for_case_conflicts (
1287- command_dict , rev_files , rev_generator
1288- )
1289- return
1290- # `skip` and `error` are not valid choices in this case because
1291- # it's not possible to detect case conflicts.
1292- if self .parameters ['case_conflict' ] not in {'ignore' , 'warn' }:
1293- raise ValueError (
1294- f"`{ self .parameters ['case_conflict' ]} ` is not a valid value "
1295- "for `--case-conflict` when operating on S3 Express "
1296- "directory buckets. Valid values: `warn`, `ignore`."
1297- )
1298- msg = (
1299- "warning: Recursive copies/moves from an S3 Express "
1300- "directory bucket to a case-insensitive local filesystem "
1301- "may result in undefined behavior if there are "
1302- "S3 object key names that differ only by case. To disable "
1303- "this warning, set the `--case-conflict` parameter to `ignore`. "
1304- f"For more information, see { CaseConflictSync .DOC_URI } ."
1305- )
1306- uni_print (msg , sys .stderr )
1307-
1308- def _modify_instructions_for_case_conflicts (
1309- self , command_dict , rev_files , rev_generator
1310- ):
1311- # Command will perform recursive S3 to local downloads.
1312- # Checking for potential case conflicts requires knowledge
1313- # of local files. Instead of writing a separate validation
1314- # mechanism for recursive downloads, we modify the instructions
1315- # to mimic a sync command.
1316- sync_strategies = {
1317- # Local filename exists with exact case match. Always sync
1318- # because it's a copy operation.
1319- 'file_at_src_and_dest_sync_strategy' : AlwaysSync (),
1320- # Local filename either doesn't exist or differs only by case.
1321- # Let `CaseConflictSync` determine which it is and handle it
1322- # according to configured `--case-conflict` parameter.
1323- 'file_not_at_dest_sync_strategy' : CaseConflictSync (
1324- on_case_conflict = self .parameters ['case_conflict' ]
1325- ),
1326- # Copy is one-way so never sync if not at source.
1327- 'file_not_at_src_sync_strategy' : NeverSync (),
1328- }
1329- command_dict ['setup' ].append (rev_files )
1330- command_dict ['file_generator' ].append (rev_generator )
1331- command_dict ['filters' ].append (create_filter (self .parameters ))
1332- command_dict ['comparator' ] = [Comparator (** sync_strategies )]
1333- self .instructions .insert (
1334- self .instructions .index ('file_info_builder' ), 'comparator'
1335- )
1336-
13371218
13381219class CommandParameters (object ):
13391220 """
0 commit comments