def _open_file_browser_for_folder(path): """ This method will take a path to a folder and open it in an OS's file browser. :param path: A folder path :raises: RuntimeError: If the Platform is not supported or if the file browser couldn't be launched. :raises: ValueError: If the path is not a valid directory. """ log.debug("Launching file system browser for folder %s" % path) # Check that we don't have a file path. if not os.path.isdir(path): raise ValueError("The path \"%s\" is not a valid directory." % path) # get the setting system = sys.platform # build the commands for opening the folder on the various OS's if system.startswith("linux"): cmd_args = ["xdg-open", path] elif system == "darwin": cmd_args = ["open", path] elif system == "win32": cmd_args = ["cmd.exe", "/C", "start", path] else: raise RuntimeError("Platform '%s' is not supported." % system) log.debug("Executing command '%s'" % cmd_args) exit_code = subprocess.call(cmd_args) if exit_code != 0: raise RuntimeError("Failed to launch a file browser for folder '%s'. " "Error code %s" % (path, exit_code))
def safe_delete_folder(path): """ Deletes a folder and all of its contents recursively, even if it has read-only items. .. note:: Problems deleting any items will be reported as warnings in the log output but otherwise ignored and skipped; meaning the function will continue deleting as much as it can. :param path: File system path to location to the folder to be deleted """ def _on_rm_error(func, path, exc_info): """ Error function called whenever shutil.rmtree fails to remove a file system item. Exceptions raised by this function will not be caught. :param func: The function which raised the exception; it will be: os.path.islink(), os.listdir(), os.remove() or os.rmdir(). :param path: The path name passed to function. :param exc_info: The exception information return by sys.exc_info(). """ if func == os.unlink or func == os.remove or func == os.rmdir: try: attr = get_permissions(path) if not (attr & stat.S_IWRITE): os.chmod(path, stat.S_IWRITE | attr) try: func(path) except Exception as e: log.warning("Couldn't delete %s: %s. Skipping" % (path, e)) else: log.warning("Couldn't delete %s: Skipping" % path) except Exception as e: log.warning("Could not delete %s: %s. Skipping" % (path, e)) else: log.warning("Couldn't delete %s. Skipping." % path) if os.path.exists(path): try: # On Windows, Python's shutil can't delete read-only files, # so if we were trying to delete one, remove the flag. # Inspired by http://stackoverflow.com/a/4829285/1074536 shutil.rmtree(path, onerror=_on_rm_error) except Exception as e: log.warning("Could not delete %s: %s" % (path, e)) else: log.warning("Could not delete: %s. Folder does not exist" % path)
def lock_file(self, path, chowned_set=None, utime=False): log.debug("Locking %s" % path) wm_path = path_utils.conform_path(path, os_name="linux") log.debug("stat [%c %#o] %s:%s (%d:%d): %s" % self._curstat(path)) lock_file(wm_path, preserveTime=not utime) # The shared set from the current session should be passed in, # to keep track of which files # have been chown'd to in watchman case of a rollback. # This is a keyword argument to stay compatible # with spk. if chowned_set is not None: chowned_set.add(path)
def _on_rm_error(func, path, exc_info): """ Error function called whenever shutil.rmtree fails to remove a file system item. Exceptions raised by this function will not be caught. :param func: The function which raised the exception; it will be: os.path.islink(), os.listdir(), os.remove() or os.rmdir(). :param path: The path name passed to function. :param exc_info: The exception information return by sys.exc_info(). """ if func == os.unlink or func == os.remove or func == os.rmdir: try: attr = get_permissions(path) if not (attr & stat.S_IWRITE): os.chmod(path, stat.S_IWRITE | attr) try: func(path) except Exception as e: log.warning("Couldn't delete %s: %s. Skipping" % (path, e)) else: log.warning("Couldn't delete %s: Skipping" % path) except Exception as e: log.warning("Could not delete %s: %s. Skipping" % (path, e)) else: log.warning("Couldn't delete %s. Skipping." % path)
def lock_dir_recursive(self, path, chowned_set=None, utime=False): log.debug("Locking recursively %s" % path) # wm_path = path_utils.conform_path(path, os_name="linux") log.debug("stat [%c %#o] %s:%s (%d:%d): %s" % self._curstat(path)) file_utils.lock_dir_recursive(path, preserveTime=not utime) for root, dirs, files in os.walk(path): for d in dirs: this_dir = os.path.join(root, d) if chowned_set is not None: chowned_set.add(this_dir) for f in files: this_file = os.path.join(root, f) if chowned_set is not None: chowned_set.add(this_file)
def auto_created_yml(path): """ A context manager for opening/closing an auto-generated yaml file. - clears umask - any existing files will be removed - the given path will be open for writing in text mode - a standard header will be added Usage example:: with filesystem.auto_created_yml(yaml_path) as fh: fh.write("foobar: blah") # fh is automatically closed upon exiting the context :param path: path to yml file to open for writing :return: file handle. """ log.debug("Creating auto-generated config file %s" % path) # clean out any existing file and replace it with a new one. safe_delete_file(path) with open(path, "w+") as fh: fh.write("# This file was auto generated by the Dsk.\n") fh.write("# Please do not modify by hand as it may be ") fh.write("# overwritten at any point.\n") fh.write("# Created %s\n" % (datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))) fh.write("# \n") # on entering the context yield fh # on exiting the context fh.write("\n") fh.write("# End of file.\n")
def _open_file_browser_for_file(path): """ This method will take a path to a file and open it in an OS's file browser and attempt to highlight it. :param path: A file path :raises: RuntimeError: If the Platform is not supported or if the file browser couldn't be launched. :raises: ValueError: If the path is not a valid file path. """ log.debug("Launching file system browser for file %s" % path) if not os.path.isfile(path): raise ValueError("The path \"%s\" is not a valid file path." % path) # get the setting system = sys.platform if system.startswith("linux"): # note: there isn't a straight forward way to do # this on linux, so just open the directory instead. cmd_args = ["xdg-open", os.path.dirname(path)] elif system == "darwin": cmd_args = ["open", "-R", path] elif system == "win32": # /select makes windows select the file within the explorer window # The problem with this approach is that it always returns back an # error code of 1 even if it does behave correctly. cmd_args = ["explorer", "/select,", path] else: raise Exception("Platform '%s' is not supported." % system) log.debug("Executing command '%s'" % cmd_args) exit_code = subprocess.call(cmd_args) # cannot trust exit code from windows, see above if system != "win32" and exit_code != 0: raise RuntimeError("Failed to launch a file browser for file '%s'. " "Error code %s" % (path, exit_code))
def get_unused_path(base_path): """ Return an unused file path from the given base path by appending if needed a number at the end of the basename of the path, right before the first ".", if any. For example, ``/tmp/foo_1.bar.blah`` would be returned for ``/tmp/foo.bar.blah`` if it already exists. If the given path does not exist, the original path is returned. .. note:: The returned path is not _reserved_, so it is possible that other processes could create the returned path before it is used by the caller. :param str base_path: Target path. :returns: A string. """ if not os.path.exists(base_path): # Bail out quickly if everything is fine with the path return base_path # Split the base path and find an unused path folder, basename = os.path.split(base_path) # two entries. base_parts = basename.split(".", 1) + [""] numbering = 0 while True: numbering += 1 name = "%s_%d%s" % (base_parts[0], numbering, ".%s" % base_parts[1] if base_parts[1] else "") path = os.path.join(folder, name) log.debug("Checking if %s exists..." % path) if not os.path.exists(path): break return path
def safe_delete_file(path): """ Deletes the given file if it exists. Ignores any errors raised in the process and logs them as warnings. If the user does not have sufficient permissions to remove the file, nothing will happen, it will simply be skipped over. :param path: Full path to file to remove """ try: if os.path.exists(path): # on windows, make sure file is not read-only if sys.platform == "win32": # make sure we have write permission attr = os.stat(path)[0] if not attr & stat.S_IWRITE: os.chmod(path, stat.S_IWRITE) os.remove(path) except Exception as e: log.warning("File '%s' could not be deleted, skipping: %s" % (path, e))
def is_write_permitted(a_top_root): api = EnviApi() api.reset(a_top_root) log.info("Loading %s as user %s" % (a_top_root, getpass.getuser())) api.load_data() if not api.is_valid(): log.error("This is not a valid config %s" % a_top_root) return False varea = api.get_userdev_config_path(getpass.getuser()) log.info("is_write_permitted: Checking if %s in %s\n" % (a_top_root, " ".join(varea))) if a_top_root in varea: return True return False
def move_folder(cls, src, dst, folder_permissions=0o775, rm_dir=False, do_log=True): """Moves a directory. First copies all content into target. Then deletes all content from sources. Skips files that won't delete. :param src: Source path to copy from :param dst: Destination to copy to :param folder_permissions: permissions to use for new folders """ if os.path.exists(src): if do_log is True: log.debug("Moving directory: %s -> %s" % (src, dst)) # first copy the content in the core folder src_files = cls.copy_folder_nocheck(src, dst, folder_permissions) # now clear out the install location if do_log is True: log.debug("Clearing out source location...") for f in src_files: try: # on windows, ensure all files are writable if sys.platform == "win32": attr = os.stat(f)[0] if (not attr & stat.S_IWRITE): # file is readonly! - turn off this attribute os.chmod(f, stat.S_IWRITE) os.remove(f) except Exception as e: log.error("Could not delete file %s: %s" % (f, e)) if rm_dir: try: all_files = os.listdir(src) if len(all_files) == 0: os.rmdir(src) if do_log is True: log.debug("dir %s: removed" % (src)) else: if do_log is True: log.info("no Empty %s" % all_files) except Exception as e: log.error("Could not delete dir %s: %s" % (src, e))
def copy_folder(cls, src, dst, folder_permissions=0o775, skip_list=None): """ Alternative implementation to ``shutil.copytree`` Copies recursively and creates folders if they don't already exist. Always skips system files such as ``"__MACOSX"``, ``".DS_Store"``, etc. Files will the extension ``.sh``, ``.bat`` or ``.exe`` will be given executable permissions. Returns a list of files that were copied. :param src: Source path to copy from :param dst: Destination to copy to :param folder_permissions: permissions to use for new folders :param skip_list: List of file names to skip. If this parameter is omitted or set to None, common files such as ``.git``, ``.gitignore`` etc will be ignored. :returns: List of files copied """ # files or directories to always skip SKIP_LIST_ALWAYS = ["__MACOSX", ".DS_Store"] # compute full skip list # note: we don't do # actual_skip_list = skip_list or SKIP_LIST_DEFAULT # because we want users to be able to pass in # skip_list=[] in order to clear the default skip list. if skip_list is None: actual_skip_list = SKIP_LIST_DEFAULT else: actual_skip_list = skip_list # add the items we always want to skip actual_skip_list.extend(SKIP_LIST_ALWAYS) files = [] if not os.path.exists(dst): os.mkdir(dst, folder_permissions) names = os.listdir(src) for name in names: # get rid of system files if name in actual_skip_list: continue srcname = os.path.join(src, name) dstname = os.path.join(dst, name) try: if os.path.isdir(srcname): if os.path.exists(dstname): try: cls.move_folder(dstname, tempfile.mkdtemp(), rm_dir=True, do_log=False) except Exception as e: log.error("Could not delete directory %s: %s" % (dstname, e)) pass # maybe we should raise files.extend( cls.copy_folder(srcname, dstname, folder_permissions)) else: shutil.copy(srcname, dstname) files.append(srcname) except (IOError, os.error) as e: raise IOError("Can't copy %s to %s: %s" % (srcname, dstname, e)) return files