Пример #1
0
	def poll(command: str) -> Deletion:

		if command.lower() == Deletion.HARD.name.lower():
			if show_confirmation: print(Style.BRIGHT + "Permanently deleted {}".format(path))
			if dry:
				logger.debug("Operating in dry mode. Would otherwise have deleted {}".format(path))
			else:
				delete_fn(path)
				logger.debug("Permanently deleted {}".format(path))
			return Deletion.HARD

		elif command.lower() == Deletion.TRASH.name.lower():
			if dry:
				logger.debug("Operating in dry mode. Would otherwise have trashed {} to {}".format(path, trash_dir))
			else:
				shutil.move(path, trash_dir)
				logger.debug("Trashed {} to {}".format(path, trash_dir))
			if show_confirmation: print(Style.BRIGHT + "Trashed {} to {}".format(path, trash_dir))
			return Deletion.TRASH

		elif command.lower() == Deletion.NO.name.lower() or len(command) == 0 and allow_ignore:
			logger.debug("Will not delete {}".format(path))
			return Deletion.NO

		else:
			print(Fore.RED + "Enter {}".format(' or '.join(choices)))
			return None
Пример #2
0
def wrap_cmd_call(cmd: List[str],
                  stdout=subprocess.PIPE,
                  stderr=subprocess.PIPE,
                  shell_cmd: str = None,
                  cwd: Optional[str] = None,
                  timeout_secs: Optional[float] = None) -> (str, str):
    """Calls an external command, waits, and throws a ExternalCommandFailed for nonzero exit codes.
	Returns (stdout, stderr).
	The user can optionally provide a shell to run the command with, e.g. "powershell.exe" 
	"""
    cmd = [str(p) for p in cmd]
    if shell_cmd:
        cmd = [shell_cmd] + cmd
    logger.debug("Calling '{}'".format(' '.join(cmd)))
    p = subprocess.Popen(cmd, stdout=stdout, stderr=stderr, cwd=cwd)
    out, err, exit_code = None, None, None
    try:
        (out, err) = p.communicate(timeout=timeout_secs)
        out = out.decode('utf-8')
        err = err.decode('utf-8')
        exit_code = p.wait(timeout=timeout_secs)
    except Exception as e:
        _log(out, err, logger.warning)
        raise e
    finally:
        p.kill()
    if exit_code != 0:
        _log(out, err, logger.warning)
        raise ExternalCommandFailed(
            "Got nonzero exit code {} from '{}'".format(
                exit_code, ' '.join(cmd)), cmd, exit_code, out, err)
    _log(out, err, logger.debug)
    return out, err
Пример #3
0
    def run(self, args: List[str]) -> None:

        full_args = self.parser.parse_args(args[1:2])
        subcommand = full_args.subcommand.replace('-', '_')

        if not hasattr(self.target,
                       subcommand) and not subcommand.startswith('_'):
            print(Fore.RED + 'Unrecognized subcommand {}'.format(subcommand))
            self.parser.print_help()
            return

        # clever; from Chase Seibert: https://chase-seibert.github.io/blog/2014/03/21/python-multilevel-argparse.html
        # use dispatch pattern to invoke method with same name
        try:
            if self.temp_dir is not None:
                if pexists(self.temp_dir) and pdir(self.temp_dir):
                    shutil.rmtree(self.temp_dir)
                elif pexists(self.temp_dir):
                    raise InvalidDirectoryException(self.temp_dir)
                remake_dirs(self.temp_dir)
                logger.debug("Created temp dir at {}".format(self.temp_dir))
            getattr(self.target, subcommand)()
        except NaturalExpectedException as e:
            pass  # ignore totally
        except KeyboardInterrupt as e:
            try:
                logger.fatal("Received cancellation signal", exc_info=True)
                self.cancel_handler(e)
            except BaseException:
                pass
            raise e
        except SystemExit as e:
            try:
                logger.fatal("Received system exit signal", exc_info=True)
                self.cancel_handler(e)
            except BaseException:
                pass
            raise e
        except BaseException as e:
            try:
                logger.fatal("{} failed!".format(self.parser.prog),
                             exc_info=True)
                self.error_handler(e)
            except BaseException:
                pass
            raise e
        finally:
            if self.temp_dir is not None:
                if pexists(self.temp_dir):
                    logger.debug("Deleted temp dir at {}".format(
                        self.temp_dir))
                    shutil.rmtree(self.temp_dir)
                    try:
                        os.remove(self.temp_dir)
                    except IOError:
                        pass
Пример #4
0
def smart_log_callback(source, line, prefix: str = '') -> None:
    line = line.decode('utf-8')
    if line.startswith('FATAL:'):
        logger.fatal(prefix + line)
    elif line.startswith('ERROR:'):
        logger.error(prefix + line)
    elif line.startswith('WARNING:'):
        logger.warning(prefix + line)
    elif line.startswith('INFO:'):
        logger.info(prefix + line)
    elif line.startswith('DEBUG:'):
        logger.debug(prefix + line)
    else:
        logger.debug(prefix + line)
Пример #5
0
def slow_delete(path: str, wait: int = 5, delete_fn: Callable[[str], None] = deletion_fn):
	logger.debug("Deleting directory tree {} ...".format(path))
	print(Fore.BLUE + "Waiting for {}s before deleting {}: ".format(wait, path), end='')
	for i in range(0, wait):
		time.sleep(1)
		print(Fore.BLUE + str(wait-i) + ' ', end='')
	time.sleep(1)
	print(Fore.BLUE + '...', end='')
	chmod_err = delete_fn(path)
	print(Fore.BLUE + ' deleted.')
	#if chmod_err is not None:
	#	try:
	#		raise chmod_err
	#	except:
	#		logger.warning("Couldn't chmod {}".format(path), exc_info=True)
	logger.debug("Deleted directory tree {}".format(path))
Пример #6
0
def stream_cmd_call(cmd: List[str],
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    shell_cmd: str = None,
                    cwd: Optional[str] = None,
                    timeout_secs: Optional[float] = None,
                    log_callback: Callable[[PipeType, bytes], None] = None,
                    bufsize: int = 1) -> None:
    """Calls an external command, waits, and throws a ExternalCommandFailed for nonzero exit codes.
	Returns (stdout, stderr).
	The user can optionally provide a shell to run the command with, e.g. "powershell.exe" 
	"""
    if log_callback is None:
        log_callback = smart_log_callback
    cmd = [str(p) for p in cmd]
    if shell_cmd:
        cmd = [shell_cmd] + cmd
    logger.debug("Streaming '{}'".format(' '.join(cmd)))

    p = subprocess.Popen(cmd,
                         stdout=PIPE,
                         stderr=PIPE,
                         cwd=cwd,
                         bufsize=bufsize)
    try:
        q = Queue()
        Thread(target=_reader, args=[PipeType.STDOUT, p.stdout, q]).start()
        Thread(target=_reader, args=[PipeType.STDERR, p.stderr, q]).start()
        for _ in range(2):
            for source, line in iter(q.get, None):
                log_callback(source, line)
        exit_code = p.wait(timeout=timeout_secs)
    finally:
        p.kill()
    if exit_code != 0:
        raise ExternalCommandFailed(
            "Got nonzero exit code {} from '{}'".format(
                exit_code, ' '.join(cmd)), cmd, exit_code, '<<unknown>>',
            '<<unknown>>')
Пример #7
0
def deletion_fn(path) -> Optional[Exception]:
	"""
	Deletes files or directories, which should work even in Windows.
	:return Returns None, or an Exception for minor warnings
	"""
	# we need this because of Windows
	chmod_err = None
	try:
		os.chmod(path, stat.S_IRWXU)
	except Exception as e:
		chmod_err = e
	# another reason for returning exception:
	# We don't want to interrupt the current line being printed like in slow_delete
	if os.path.isdir(path):
		shutil.rmtree(path, ignore_errors=True) # ignore_errors because of Windows
		try:
			os.remove(path)  # again, because of Windows
		except IOError: pass  # almost definitely because it doesn't exist
	else:
		os.remove(path)
	logger.debug("Permanently deleted {}".format(path))
	return chmod_err