summaryrefslogtreecommitdiffstats
path: root/gamechestcli/remove.py
diff options
context:
space:
mode:
authorvg <vgm+dev@devys.org>2022-09-16 14:51:12 +0200
committervg <vgm+dev@devys.org>2022-09-16 14:51:12 +0200
commit5143dc3af380f65536ef624472e27ca8c86b65df (patch)
tree8245e5dca2882a0c5a7622301350cb930f12c5d1 /gamechestcli/remove.py
parent77c76b4e0407250fc0ba8ee0309a528a4a8f1f09 (diff)
downloadgamechest-5143dc3af380f65536ef624472e27ca8c86b65df.tar.gz
gamechest-5143dc3af380f65536ef624472e27ca8c86b65df.tar.bz2
gamechest-5143dc3af380f65536ef624472e27ca8c86b65df.zip
git-sync on seele
Diffstat (limited to 'gamechestcli/remove.py')
-rw-r--r--gamechestcli/remove.py170
1 files changed, 0 insertions, 170 deletions
diff --git a/gamechestcli/remove.py b/gamechestcli/remove.py
deleted file mode 100644
index 00247f7..0000000
--- a/gamechestcli/remove.py
+++ /dev/null
@@ -1,170 +0,0 @@
-#!/usr/bin/python3
-
-import io
-import os
-import re
-import subprocess
-import threading
-
-import humanfriendly
-
-from structures import Progress
-
-
-class Remove:
-
- _progress_re = re.compile(r'^\s*(\S+)\s+\[([^]]+)/s\]\s+ETA\s+(\S+)\s*$')
-
- def __init__(self, path):
- self.last_progress = Progress()
- self.path = path
- self.rm_proc = None
- self.pv_proc = None
- self.pipe = os.pipe()
- self.read_fd = io.open(self.pipe[0], 'r', encoding='utf8')
- self._proc_started = False
- self._filescount = 0
- self._counting_quit_event = threading.Event()
- self._counting_thread = threading.Thread(
- target=self._counting_worker,
- args=(self._counting_quit_event, path),
- )
- self._counting_thread.start()
-
- def _start_proc(self):
- common_parameters = dict(
- encoding='utf8',
- env={**os.environ,
- **{'LC_ALL':'C.UTF-8',
- 'LANG':'C.UTF-8',
- 'LANGUAGE':'C.UTF-8',
- }},
- )
- self.rm_proc = subprocess.Popen(
- [
- 'rm',
- '--verbose',
- '--recursive',
- '--one-file-system',
- '--preserve-root=all',
- '--interactive=never',
- '--',
- self.path,
- ],
- stdout=subprocess.PIPE,
- **common_parameters,
- )
- #self.rm_proc = subprocess.Popen( # only for testing purposes
- # [
- # 'testtools/fake-rm',
- # self.path,
- # ],
- # stdout=subprocess.PIPE,
- # **common_parameters,
- #)
- self.pv_proc = subprocess.Popen(
- [
- 'pv',
- '--force',
- '--size', str(self._filescount),
- '--format', '%b %a %e',
- '--line-mode',
- ],
- stdin=self.rm_proc.stdout,
- stdout=subprocess.DEVNULL,
- stderr=self.pipe[1],
- **common_parameters,
- )
- # close child's pipe fd on parent's side or selectors will hang if
- # process closes.
- os.close(self.pipe[1])
- self._proc_started = True
-
- def _counting_worker(self, event, path):
- # as counting files for a deep directory can take some time, make it
- # interruptible with a thread and event for cancelling.
- # using functools.reduce(x+1+len(y[2]), os.walk) would have been much
- # cleaner, but the code gets harder to read with the addition of the
- # event checking. Fallback to accumalator and iterations.
- total = 0
- for _, _, files in os.walk(path):
- total += 1 + len(files)
- if event.is_set():
- break
- # set filescount with calculated total (even then interrupted, as it
- # would be a better estimation than nothing).
- self._filescount = total
- # start next processes (if event was not set)
- if not event.is_set():
- self._start_proc()
-
- def select_fd(self):
- 'useful to use selectors with the process most meaningful fd'
- return self.read_fd
-
- def progress_read(self):
- if not self._proc_started:
- return self.last_progress
- line = self.read_fd.readline()
- if match := self._progress_re.search(line):
- written_bytes = humanfriendly.parse_size(match.group(1))
- self.last_progress = Progress(
- written_bytes,
- int(100 * written_bytes / self._filescount),
- humanfriendly.parse_size(match.group(2)),
- match.group(3),
- )
- return self.last_progress
-
- def terminate(self):
- if self._proc_started:
- for proc in (self.rm_proc, self.pv_proc):
- proc.terminate()
- else:
- self._counting_quit_event.set()
-
- def poll(self):
- 'returns None if not terminated, otherwise return returncode'
- if not self._proc_started:
- return None
- return self.pv_proc.poll()
-
- def wait(self, timeout=None):
- if self._proc_started:
- self.rm_proc.wait(timeout)
- return self.pv_proc.wait(timeout)
- else:
- self._counting_thread.join(timeout)
- return -1
-
- def close(self):
- self.read_fd.close()
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, value, traceback):
- self.terminate()
- self.wait()
- self.close()
-
-
-if __name__ == '__main__':
- import sys
- import contextlib
- import time
- import selectors
-
- with contextlib.ExitStack() as stack:
- stack.enter_context(contextlib.suppress(KeyboardInterrupt))
- remove = stack.enter_context(Remove(sys.argv[1]))
- selector = stack.enter_context(selectors.DefaultSelector())
- selector.register(remove.select_fd(), selectors.EVENT_READ)
- while remove.poll() is None:
- selector.select()
- progress = remove.progress_read()
- print(f'{progress.bytes}b {progress.percent}% '
- f'{progress.speed}b/s {progress.eta}')
- rc = remove.poll()
- print(f'ended with code: {rc}')
- print('test main ended')