The .get() function raises a queue.Empty exception anyway, so there was no real need for the .empty() call. Furthermore, it turned out to really slow things down.
146 lines
5.4 KiB
Python
146 lines
5.4 KiB
Python
# ***** BEGIN GPL LICENSE BLOCK *****
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software Foundation,
|
|
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
#
|
|
# ***** END GPL LICENCE BLOCK *****
|
|
#
|
|
# (c) 2018, Blender Foundation - Sybren A. Stüvel
|
|
"""Callback class definition for BAT Pack progress reporting."""
|
|
import threading
|
|
|
|
import functools
|
|
import logging
|
|
import pathlib
|
|
import queue
|
|
import typing
|
|
|
|
import blender_asset_tracer.trace.progress
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
class Callback(blender_asset_tracer.trace.progress.Callback):
|
|
"""BAT Pack progress reporting."""
|
|
|
|
def pack_start(self) -> None:
|
|
"""Called when packing starts."""
|
|
|
|
def pack_done(self,
|
|
output_blendfile: pathlib.Path,
|
|
missing_files: typing.Set[pathlib.Path]) -> None:
|
|
"""Called when packing is done."""
|
|
|
|
def pack_aborted(self):
|
|
"""Called when packing was aborted."""
|
|
|
|
def trace_blendfile(self, filename: pathlib.Path) -> None:
|
|
"""Called for every blendfile opened when tracing dependencies."""
|
|
|
|
def trace_asset(self, filename: pathlib.Path) -> None:
|
|
"""Called for every asset found when tracing dependencies.
|
|
|
|
Note that this can also be a blend file.
|
|
"""
|
|
|
|
def rewrite_blendfile(self, orig_filename: pathlib.Path) -> None:
|
|
"""Called for every rewritten blendfile."""
|
|
|
|
def transfer_file(self, src: pathlib.Path, dst: pathlib.Path) -> None:
|
|
"""Called when a file transfer starts."""
|
|
|
|
def transfer_file_skipped(self, src: pathlib.Path, dst: pathlib.Path) -> None:
|
|
"""Called when a file is skipped because it already exists."""
|
|
|
|
def transfer_progress(self, total_bytes: int, transferred_bytes: int) -> None:
|
|
"""Called during file transfer, with per-pack info (not per file).
|
|
|
|
:param total_bytes: The total amount of bytes to be transferred for
|
|
the current packing operation. This can increase while transfer
|
|
is happening, when more files are discovered (because transfer
|
|
starts in a separate thread before all files are found).
|
|
:param transferred_bytes: The total amount of bytes transfered for
|
|
the current packing operation.
|
|
"""
|
|
|
|
def missing_file(self, filename: pathlib.Path) -> None:
|
|
"""Called for every asset that does not exist on the filesystem."""
|
|
|
|
|
|
class ThreadSafeCallback(Callback):
|
|
"""Thread-safe wrapper for Callback instances.
|
|
|
|
Progress calls are queued until flush() is called. The queued calls are
|
|
called in the same thread as the one calling flush().
|
|
"""
|
|
|
|
def __init__(self, wrapped: Callback) -> None:
|
|
self.log = log.getChild('ThreadSafeCallback')
|
|
self.wrapped = wrapped
|
|
|
|
# Thread-safe queue for passing progress reports on the main thread.
|
|
self._reporting_queue = queue.Queue() # type: queue.Queue[typing.Callable]
|
|
self._main_thread_id = threading.get_ident()
|
|
|
|
def _queue(self, func: typing.Callable, *args, **kwargs):
|
|
partial = functools.partial(func, *args, **kwargs)
|
|
|
|
if self._main_thread_id == threading.get_ident():
|
|
partial()
|
|
else:
|
|
self._reporting_queue.put(partial)
|
|
|
|
def pack_start(self) -> None:
|
|
self._queue(self.wrapped.pack_start)
|
|
|
|
def pack_done(self,
|
|
output_blendfile: pathlib.Path,
|
|
missing_files: typing.Set[pathlib.Path]) -> None:
|
|
self._queue(self.wrapped.pack_done, output_blendfile, missing_files)
|
|
|
|
def trace_blendfile(self, filename: pathlib.Path) -> None:
|
|
self._queue(self.wrapped.trace_blendfile, filename)
|
|
|
|
def trace_asset(self, filename: pathlib.Path) -> None:
|
|
self._queue(self.wrapped.trace_asset, filename)
|
|
|
|
def transfer_file(self, src: pathlib.Path, dst: pathlib.Path) -> None:
|
|
self._queue(self.wrapped.transfer_file, src, dst)
|
|
|
|
def transfer_file_skipped(self, src: pathlib.Path, dst: pathlib.Path) -> None:
|
|
self._queue(self.wrapped.transfer_file_skipped, src, dst)
|
|
|
|
def transfer_progress(self, total_bytes: int, transferred_bytes: int) -> None:
|
|
self._queue(self.wrapped.transfer_progress, total_bytes, transferred_bytes)
|
|
|
|
def missing_file(self, filename: pathlib.Path) -> None:
|
|
self._queue(self.wrapped.missing_file, filename)
|
|
|
|
def flush(self, timeout: float = None) -> None:
|
|
"""Call the queued calls, call this in the main thread."""
|
|
|
|
while True:
|
|
try:
|
|
call = self._reporting_queue.get(block=timeout is not None,
|
|
timeout=timeout)
|
|
except queue.Empty:
|
|
return
|
|
|
|
try:
|
|
call()
|
|
except Exception:
|
|
# Don't let the handling of one callback call
|
|
# block the entire flush process.
|
|
self.log.exception('Error calling %s', call)
|