def copy(source: str, destination: str, *, follow_symlinks: bool = False) -> None: """Copy source and destination files. This function overwrites the destination if it already exists, and also tries to copy ownership information. :param str source: The source to be copied to destination. :param str destination: Where to put the copy. :param bool follow_symlinks: Whether or not symlinks should be followed. :raises SnapcraftCopyFileNotFoundError: If source doesn't exist. """ # If os.link raised an I/O error, it may have left a file behind. Skip on # OSError in case it doesn't exist or is a directory. with suppress(OSError): os.unlink(destination) try: shutil.copy2(source, destination, follow_symlinks=follow_symlinks) except FileNotFoundError: raise SnapcraftCopyFileNotFoundError(source) uid = os.stat(source, follow_symlinks=follow_symlinks).st_uid gid = os.stat(source, follow_symlinks=follow_symlinks).st_gid try: os.chown(destination, uid, gid, follow_symlinks=follow_symlinks) except PermissionError as e: logger.debug("Unable to chown {destination}: {error}".format( destination=destination, error=e))
def link(source: str, destination: str, *, follow_symlinks: bool = False) -> None: """Hard-link source and destination files. :param str source: The source to which destination will be linked. :param str destination: The destination to be linked to source. :param bool follow_symlinks: Whether or not symlinks should be followed. :raises SnapcraftCopyFileNotFoundError: If source doesn't exist. """ # Note that follow_symlinks doesn't seem to work for os.link, so we'll # implement this logic ourselves using realpath. source_path = source if follow_symlinks: source_path = os.path.realpath(source) if not os.path.exists(os.path.dirname(destination)): create_similar_directory(os.path.dirname(source_path), os.path.dirname(destination)) # Setting follow_symlinks=False in case this bug is ever fixed # upstream-- we want this function to continue supporting NOT following # symlinks. try: os.link(source_path, destination, follow_symlinks=False) except FileNotFoundError: raise SnapcraftCopyFileNotFoundError(source)
def _copy(source: str, destination: str, follow_symlinks: bool=False) -> None: # If os.link raised an I/O error, it may have left a file behind. Skip on # OSError in case it doesn't exist or is a directory. with suppress(OSError): os.unlink(destination) try: shutil.copy2( source, destination, follow_symlinks=follow_symlinks) except FileNotFoundError: raise SnapcraftCopyFileNotFoundError(source) uid = os.stat(source, follow_symlinks=follow_symlinks).st_uid gid = os.stat(source, follow_symlinks=follow_symlinks).st_gid try: os.chown(destination, uid, gid, follow_symlinks=follow_symlinks) except PermissionError as e: logger.debug('Unable to chown {destination}: {error}'.format( destination=destination, error=e))
def _link(source: str, destination: str, follow_symlinks: bool=False) -> None: # Note that follow_symlinks doesn't seem to work for os.link, so we'll # implement this logic ourselves using realpath. source_path = source if follow_symlinks: source_path = os.path.realpath(source) if not os.path.exists(os.path.dirname(destination)): create_similar_directory( os.path.dirname(source_path), os.path.dirname(destination)) # Setting follow_symlinks=False in case this bug is ever fixed # upstream-- we want this function to continue supporting NOT following # symlinks. try: os.link(source_path, destination, follow_symlinks=False) except FileNotFoundError: raise SnapcraftCopyFileNotFoundError(source)