parameters: (src_url, exp_src_url, src_url_names_container,
                      have_multiple_srcs, exp_dst_url, have_existing_dest_subdir,
                      recursion_requested)
return_statement: return new_exp_dst_url
docstring: Constructs the destination URL for a given exp_src_url/exp_dst_url pair.
  
    Uses context-dependent naming rules that mimic Linux cp and mv behavior.
  
    Args:
      src_url: Source StorageUrl to be copied.
      exp_src_url: Single StorageUrl from wildcard expansion of src_url.
      src_url_names_container: True if src_url names a container (including the
          case of a wildcard-named bucket subdir (like gs://bucket/abc,
          where gs://bucket/abc/* matched some objects).
      have_multiple_srcs: True if this is a multi-source request. This can be
          true if src_url wildcard-expanded to multiple URLs or if there were
          multiple source URLs in the request.
      exp_dst_url: the expanded StorageUrl requested for the cp destination.
          Final written path is constructed from this plus a context-dependent
          variant of src_url.
      have_existing_dest_subdir: bool indicator whether dest is an existing
        subdirectory.
      recursion_requested: True if a recursive operation has been requested.
  
    Returns:
      StorageUrl to use for copy.
  
    Raises:
      CommandException if destination object name not specified for
      source and source is a stream.
docstring_summary: Constructs the destination URL for a given exp_src_url/exp_dst_url pair.
docstring_tokens: Constructs the destination URL for a given exp_src_url / exp_dst_url pair .
function: def ConstructDstUrl(src_url, exp_src_url, src_url_names_container,
                      have_multiple_srcs, exp_dst_url, have_existing_dest_subdir,
                      recursion_requested):
    """Constructs the destination URL for a given exp_src_url/exp_dst_url pair.
  
    Uses context-dependent naming rules that mimic Linux cp and mv behavior.
  
    Args:
      src_url: Source StorageUrl to be copied.
      exp_src_url: Single StorageUrl from wildcard expansion of src_url.
      src_url_names_container: True if src_url names a container (including the
          case of a wildcard-named bucket subdir (like gs://bucket/abc,
          where gs://bucket/abc/* matched some objects).
      have_multiple_srcs: True if this is a multi-source request. This can be
          true if src_url wildcard-expanded to multiple URLs or if there were
          multiple source URLs in the request.
      exp_dst_url: the expanded StorageUrl requested for the cp destination.
          Final written path is constructed from this plus a context-dependent
          variant of src_url.
      have_existing_dest_subdir: bool indicator whether dest is an existing
        subdirectory.
      recursion_requested: True if a recursive operation has been requested.
  
    Returns:
      StorageUrl to use for copy.
  
    Raises:
      CommandException if destination object name not specified for
      source and source is a stream.
    """
    if _ShouldTreatDstUrlAsSingleton(
        have_multiple_srcs, have_existing_dest_subdir, exp_dst_url,
        recursion_requested):
      # We're copying one file or object to one file or object.
      return exp_dst_url
  
    if exp_src_url.IsFileUrl() and exp_src_url.IsStream():
      if have_existing_dest_subdir:
        raise CommandException('Destination object name needed when '
                               'source is a stream')
      return exp_dst_url
  
    if not recursion_requested and not have_multiple_srcs:
      # We're copying one file or object to a subdirectory. Append final comp
      # of exp_src_url to exp_dst_url.
      src_final_comp = exp_src_url.object_name.rpartition(src_url.delim)[-1]
      return StorageUrlFromString('%s%s%s' % (
          exp_dst_url.url_string.rstrip(exp_dst_url.delim),
          exp_dst_url.delim, src_final_comp))
  
    # Else we're copying multiple sources to a directory, bucket, or a bucket
    # "sub-directory".
  
    # Ensure exp_dst_url ends in delim char if we're doing a multi-src copy or
    # a copy to a directory. (The check for copying to a directory needs
    # special-case handling so that the command:
    #   gsutil cp gs://bucket/obj dir
    # will turn into file://dir/ instead of file://dir -- the latter would cause
    # the file "dirobj" to be created.)
    # Note: need to check have_multiple_srcs or src_url.names_container()
    # because src_url could be a bucket containing a single object, named
    # as gs://bucket.
    if ((have_multiple_srcs or src_url_names_container or
         (exp_dst_url.IsFileUrl() and exp_dst_url.IsDirectory()))
        and not exp_dst_url.url_string.endswith(exp_dst_url.delim)):
      exp_dst_url = StorageUrlFromString('%s%s' % (exp_dst_url.url_string,
                                                   exp_dst_url.delim))
  
    # Making naming behavior match how things work with local Linux cp and mv
    # operations depends on many factors, including whether the destination is a
    # container, the plurality of the source(s), and whether the mv command is
    # being used:
    # 1. For the "mv" command that specifies a non-existent destination subdir,
    #    renaming should occur at the level of the src subdir, vs appending that
    #    subdir beneath the dst subdir like is done for copying. For example:
    #      gsutil rm -r gs://bucket
    #      gsutil cp -r dir1 gs://bucket
    #      gsutil cp -r dir2 gs://bucket/subdir1
    #      gsutil mv gs://bucket/subdir1 gs://bucket/subdir2
    #    would (if using cp naming behavior) end up with paths like:
    #      gs://bucket/subdir2/subdir1/dir2/.svn/all-wcprops
    #    whereas mv naming behavior should result in:
    #      gs://bucket/subdir2/dir2/.svn/all-wcprops
    # 2. Copying from directories, buckets, or bucket subdirs should result in
    #    objects/files mirroring the source directory hierarchy. For example:
    #      gsutil cp dir1/dir2 gs://bucket
    #    should create the object gs://bucket/dir2/file2, assuming dir1/dir2
    #    contains file2).
    #    To be consistent with Linux cp behavior, there's one more wrinkle when
    #    working with subdirs: The resulting object names depend on whether the
    #    destination subdirectory exists. For example, if gs://bucket/subdir
    #    exists, the command:
    #      gsutil cp -r dir1/dir2 gs://bucket/subdir
    #    should create objects named like gs://bucket/subdir/dir2/a/b/c. In
    #    contrast, if gs://bucket/subdir does not exist, this same command
    #    should create objects named like gs://bucket/subdir/a/b/c.
    # 3. Copying individual files or objects to dirs, buckets or bucket subdirs
    #    should result in objects/files named by the final source file name
    #    component. Example:
    #      gsutil cp dir1/*.txt gs://bucket
    #    should create the objects gs://bucket/f1.txt and gs://bucket/f2.txt,
    #    assuming dir1 contains f1.txt and f2.txt.
  
    recursive_move_to_new_subdir = False
    if (global_copy_helper_opts.perform_mv and recursion_requested
        and src_url_names_container and not have_existing_dest_subdir):
      # Case 1. Handle naming rules for bucket subdir mv. Here we want to
      # line up the src_url against its expansion, to find the base to build
      # the new name. For example, running the command:
      #   gsutil mv gs://bucket/abcd gs://bucket/xyz
      # when processing exp_src_url=gs://bucket/abcd/123
      # exp_src_url_tail should become /123
      # Note: mv.py code disallows wildcard specification of source URL.
      recursive_move_to_new_subdir = True
      exp_src_url_tail = (
          exp_src_url.url_string[len(src_url.url_string):])
      dst_key_name = '%s/%s' % (exp_dst_url.object_name.rstrip('/'),
                                exp_src_url_tail.strip('/'))
  
    elif src_url_names_container and (exp_dst_url.IsCloudUrl() or
                                      exp_dst_url.IsDirectory()):
      # Case 2.  Container copy to a destination other than a file.
      # Build dst_key_name from subpath of exp_src_url past
      # where src_url ends. For example, for src_url=gs://bucket/ and
      # exp_src_url=gs://bucket/src_subdir/obj, dst_key_name should be
      # src_subdir/obj.
      src_url_path_sans_final_dir = GetPathBeforeFinalDir(src_url)
      dst_key_name = exp_src_url.versionless_url_string[
          len(src_url_path_sans_final_dir):].lstrip(src_url.delim)
      # Handle case where dst_url is a non-existent subdir.
      if not have_existing_dest_subdir:
        dst_key_name = dst_key_name.partition(src_url.delim)[-1]
      # Handle special case where src_url was a directory named with '.' or
      # './', so that running a command like:
      #   gsutil cp -r . gs://dest
      # will produce obj names of the form gs://dest/abc instead of
      # gs://dest/./abc.
      if dst_key_name.startswith('.%s' % os.sep):
        dst_key_name = dst_key_name[2:]
  
    else:
      # Case 3.
      dst_key_name = exp_src_url.object_name.rpartition(src_url.delim)[-1]
  
    if (not recursive_move_to_new_subdir and (
        exp_dst_url.IsFileUrl() or _ShouldTreatDstUrlAsBucketSubDir(
            have_multiple_srcs, exp_dst_url, have_existing_dest_subdir,
            src_url_names_container, recursion_requested))):
      if exp_dst_url.object_name and exp_dst_url.object_name.endswith(
          exp_dst_url.delim):
        dst_key_name = '%s%s%s' % (
            exp_dst_url.object_name.rstrip(exp_dst_url.delim),
            exp_dst_url.delim, dst_key_name)
      else:
        delim = exp_dst_url.delim if exp_dst_url.object_name else ''
        dst_key_name = '%s%s%s' % (exp_dst_url.object_name or '',
                                   delim, dst_key_name)
  
    new_exp_dst_url = exp_dst_url.Clone()
    new_exp_dst_url.object_name = dst_key_name.replace(src_url.delim,
                                                       exp_dst_url.delim)
    return new_exp_dst_url
function_tokens: def ConstructDstUrl ( src_url , exp_src_url , src_url_names_container , have_multiple_srcs , exp_dst_url , have_existing_dest_subdir , recursion_requested ) : if _ShouldTreatDstUrlAsSingleton ( have_multiple_srcs , have_existing_dest_subdir , exp_dst_url , recursion_requested ) : # We're copying one file or object to one file or object. return exp_dst_url if exp_src_url . IsFileUrl ( ) and exp_src_url . IsStream ( ) : if have_existing_dest_subdir : raise CommandException ( 'Destination object name needed when ' 'source is a stream' ) return exp_dst_url if not recursion_requested and not have_multiple_srcs : # We're copying one file or object to a subdirectory. Append final comp # of exp_src_url to exp_dst_url. src_final_comp = exp_src_url . object_name . rpartition ( src_url . delim ) [ - 1 ] return StorageUrlFromString ( '%s%s%s' % ( exp_dst_url . url_string . rstrip ( exp_dst_url . delim ) , exp_dst_url . delim , src_final_comp ) ) # Else we're copying multiple sources to a directory, bucket, or a bucket # "sub-directory". # Ensure exp_dst_url ends in delim char if we're doing a multi-src copy or # a copy to a directory. (The check for copying to a directory needs # special-case handling so that the command: #   gsutil cp gs://bucket/obj dir # will turn into file://dir/ instead of file://dir -- the latter would cause # the file "dirobj" to be created.) # Note: need to check have_multiple_srcs or src_url.names_container() # because src_url could be a bucket containing a single object, named # as gs://bucket. if ( ( have_multiple_srcs or src_url_names_container or ( exp_dst_url . IsFileUrl ( ) and exp_dst_url . IsDirectory ( ) ) ) and not exp_dst_url . url_string . endswith ( exp_dst_url . delim ) ) : exp_dst_url = StorageUrlFromString ( '%s%s' % ( exp_dst_url . url_string , exp_dst_url . delim ) ) # Making naming behavior match how things work with local Linux cp and mv # operations depends on many factors, including whether the destination is a # container, the plurality of the source(s), and whether the mv command is # being used: # 1. For the "mv" command that specifies a non-existent destination subdir, #    renaming should occur at the level of the src subdir, vs appending that #    subdir beneath the dst subdir like is done for copying. For example: #      gsutil rm -r gs://bucket #      gsutil cp -r dir1 gs://bucket #      gsutil cp -r dir2 gs://bucket/subdir1 #      gsutil mv gs://bucket/subdir1 gs://bucket/subdir2 #    would (if using cp naming behavior) end up with paths like: #      gs://bucket/subdir2/subdir1/dir2/.svn/all-wcprops #    whereas mv naming behavior should result in: #      gs://bucket/subdir2/dir2/.svn/all-wcprops # 2. Copying from directories, buckets, or bucket subdirs should result in #    objects/files mirroring the source directory hierarchy. For example: #      gsutil cp dir1/dir2 gs://bucket #    should create the object gs://bucket/dir2/file2, assuming dir1/dir2 #    contains file2). #    To be consistent with Linux cp behavior, there's one more wrinkle when #    working with subdirs: The resulting object names depend on whether the #    destination subdirectory exists. For example, if gs://bucket/subdir #    exists, the command: #      gsutil cp -r dir1/dir2 gs://bucket/subdir #    should create objects named like gs://bucket/subdir/dir2/a/b/c. In #    contrast, if gs://bucket/subdir does not exist, this same command #    should create objects named like gs://bucket/subdir/a/b/c. # 3. Copying individual files or objects to dirs, buckets or bucket subdirs #    should result in objects/files named by the final source file name #    component. Example: #      gsutil cp dir1/*.txt gs://bucket #    should create the objects gs://bucket/f1.txt and gs://bucket/f2.txt, #    assuming dir1 contains f1.txt and f2.txt. recursive_move_to_new_subdir = False if ( global_copy_helper_opts . perform_mv and recursion_requested and src_url_names_container and not have_existing_dest_subdir ) : # Case 1. Handle naming rules for bucket subdir mv. Here we want to # line up the src_url against its expansion, to find the base to build # the new name. For example, running the command: #   gsutil mv gs://bucket/abcd gs://bucket/xyz # when processing exp_src_url=gs://bucket/abcd/123 # exp_src_url_tail should become /123 # Note: mv.py code disallows wildcard specification of source URL. recursive_move_to_new_subdir = True exp_src_url_tail = ( exp_src_url . url_string [ len ( src_url . url_string ) : ] ) dst_key_name = '%s/%s' % ( exp_dst_url . object_name . rstrip ( '/' ) , exp_src_url_tail . strip ( '/' ) ) elif src_url_names_container and ( exp_dst_url . IsCloudUrl ( ) or exp_dst_url . IsDirectory ( ) ) : # Case 2.  Container copy to a destination other than a file. # Build dst_key_name from subpath of exp_src_url past # where src_url ends. For example, for src_url=gs://bucket/ and # exp_src_url=gs://bucket/src_subdir/obj, dst_key_name should be # src_subdir/obj. src_url_path_sans_final_dir = GetPathBeforeFinalDir ( src_url ) dst_key_name = exp_src_url . versionless_url_string [ len ( src_url_path_sans_final_dir ) : ] . lstrip ( src_url . delim ) # Handle case where dst_url is a non-existent subdir. if not have_existing_dest_subdir : dst_key_name = dst_key_name . partition ( src_url . delim ) [ - 1 ] # Handle special case where src_url was a directory named with '.' or # './', so that running a command like: #   gsutil cp -r . gs://dest # will produce obj names of the form gs://dest/abc instead of # gs://dest/./abc. if dst_key_name . startswith ( '.%s' % os . sep ) : dst_key_name = dst_key_name [ 2 : ] else : # Case 3. dst_key_name = exp_src_url . object_name . rpartition ( src_url . delim ) [ - 1 ] if ( not recursive_move_to_new_subdir and ( exp_dst_url . IsFileUrl ( ) or _ShouldTreatDstUrlAsBucketSubDir ( have_multiple_srcs , exp_dst_url , have_existing_dest_subdir , src_url_names_container , recursion_requested ) ) ) : if exp_dst_url . object_name and exp_dst_url . object_name . endswith ( exp_dst_url . delim ) : dst_key_name = '%s%s%s' % ( exp_dst_url . object_name . rstrip ( exp_dst_url . delim ) , exp_dst_url . delim , dst_key_name ) else : delim = exp_dst_url . delim if exp_dst_url . object_name else '' dst_key_name = '%s%s%s' % ( exp_dst_url . object_name or '' , delim , dst_key_name ) new_exp_dst_url = exp_dst_url . Clone ( ) new_exp_dst_url . object_name = dst_key_name . replace ( src_url . delim , exp_dst_url . delim ) return new_exp_dst_url