diff --git a/blender_asset_tracer/cli/pack.py b/blender_asset_tracer/cli/pack.py index 1b7bc63..41e26c2 100644 --- a/blender_asset_tracer/cli/pack.py +++ b/blender_asset_tracer/cli/pack.py @@ -4,6 +4,7 @@ import pathlib import sys import typing +import blender_asset_tracer.pack.transfer from blender_asset_tracer import pack log = logging.getLogger(__name__) @@ -34,7 +35,7 @@ def cli_pack(args): try: packer.execute() - except pack.queued_copy.FileCopyError as ex: + except blender_asset_tracer.pack.transfer.FileTransferError as ex: log.error("%d files couldn't be copied, starting with %s", len(ex.files_remaining), ex.files_remaining[0]) raise SystemExit(1) diff --git a/blender_asset_tracer/pack/queued_copy.py b/blender_asset_tracer/pack/queued_copy.py index ba0def8..3863ac4 100644 --- a/blender_asset_tracer/pack/queued_copy.py +++ b/blender_asset_tracer/pack/queued_copy.py @@ -6,23 +6,17 @@ import queue import shutil import typing +from . import transfer + log = logging.getLogger(__name__) -class FileCopyError(IOError): - """Raised when one or more files could not be transferred.""" - - def __init__(self, message, files_remaining: typing.List[pathlib.Path]) -> None: - super().__init__(message) - self.files_remaining = files_remaining - - class Action(enum.Enum): COPY = 1 MOVE = 2 -class FileCopier(threading.Thread): +class FileCopier(threading.Thread, transfer.FileTransferer): """Copies or moves files in source directory order.""" def __init__(self, *args, **kwargs) -> None: @@ -62,8 +56,9 @@ class FileCopier(threading.Thread): src, dst = self.queue.get_nowait() files_remaining.append(src) assert files_remaining - raise FileCopyError("%d files couldn't be transferred" % len(files_remaining), - files_remaining) + raise transfer.FileTransferError( + "%d files couldn't be transferred" % len(files_remaining), + files_remaining) def run(self): files_transferred = 0 @@ -98,8 +93,8 @@ class FileCopier(threading.Thread): dst.parent.mkdir(parents=True, exist_ok=True) # TODO(Sybren): when we target Py 3.6+, remove the str() calls. - transfer = transfer_funcs[act] - transfer(str(src), str(dst)) + tfunc = transfer_funcs[act] + tfunc(str(src), str(dst)) files_transferred += 1 except Exception: diff --git a/blender_asset_tracer/pack/transfer.py b/blender_asset_tracer/pack/transfer.py new file mode 100644 index 0000000..11b7816 --- /dev/null +++ b/blender_asset_tracer/pack/transfer.py @@ -0,0 +1,44 @@ +import abc +import pathlib +import typing + + +class FileTransferError(IOError): + """Raised when one or more files could not be transferred.""" + + def __init__(self, message, files_remaining: typing.List[pathlib.Path]) -> None: + super().__init__(message) + self.files_remaining = files_remaining + + +class FileTransferer(metaclass=abc.ABCMeta): + """Interface for file transfer classes.""" + + @abc.abstractmethod + def start(self): + """Starts the file transfer thread/process. + + This could spin up a separate thread to perform the actual file + transfer. After start() is called, implementations should still accept + calls to the queue_xxx() methods. In other words, this is not to be + used as queue-and-then-start, but as start-and-then-queue. + """ + + @abc.abstractmethod + def queue_copy(self, src: pathlib.Path, dst: pathlib.Path) -> None: + """Queue a copy action from 'src' to 'dst'.""" + + @abc.abstractmethod + def queue_move(self, src: pathlib.Path, dst: pathlib.Path) -> None: + """Queue a move action from 'src' to 'dst'.""" + + @abc.abstractmethod + def done_and_join(self) -> None: + """Indicate all files have been queued, and wait until done. + + After this function has been called, the queue_xxx() methods should not + be called any more. + + :raises FileTransferError: if there was an error transferring one or + more files. + """