[Bf-extensions-cvs] [c3f2438] master: New blend-file addon
Campbell Barton
noreply at git.blender.org
Fri Jan 29 08:52:24 CET 2016
Commit: c3f24386bb4ec7c91949a1f024bb1d6464cfd5a2
Author: Campbell Barton
Date: Thu Jan 28 13:29:04 2016 +1100
Branches: master
https://developer.blender.org/rBAc3f24386bb4ec7c91949a1f024bb1d6464cfd5a2
New blend-file addon
Currently only expose packing functionality for packing
a file and all its deps into a ZIP (shared code with the cloud).
Can run directly or from the command line (without blender) via `blendfile_pack`.
Also adds subprocess_helper module which we may want to re-use elsewhere,
allowing to run external processes that don't lock blender and can be cancelled by pressing Esc.
===================================================================
A io_blend_utils/__init__.py
A io_blend_utils/bl_utils/pipe_non_blocking.py
A io_blend_utils/bl_utils/subprocess_helper.py
A io_blend_utils/blend/blendfile.py
A io_blend_utils/blend/blendfile_path_walker.py
A io_blend_utils/blendfile_pack.py
A io_blend_utils/utils/system.py
===================================================================
diff --git a/io_blend_utils/__init__.py b/io_blend_utils/__init__.py
new file mode 100644
index 0000000..adbd2d0
--- /dev/null
+++ b/io_blend_utils/__init__.py
@@ -0,0 +1,110 @@
+# ***** 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 *****
+
+bl_info = {
+ "name": "Blend File Utils",
+ "author": "Campbell Barton",
+ "version": (0, 1),
+ "blender": (2, 76, 0),
+ "location": "File > External Data > Blend Utils",
+ "description": "Utility for packing blend files",
+ "warning": "",
+ "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Import-Export/BlendFile_Utils",
+ "support": 'OFFICIAL',
+ "category": "Import-Export",
+ }
+
+
+import bpy
+from bpy.types import Operator
+from bpy_extras.io_utils import ExportHelper
+
+from .bl_utils.subprocess_helper import SubprocessHelper
+
+
+class ExportBlendPack(Operator, ExportHelper, SubprocessHelper):
+ """Packs a blend file and all its dependencies into an archive for easy redistribution"""
+ bl_idname = "export_blend.pack"
+ bl_label = "Pack Blend to Archive"
+
+ # ExportHelper
+ filename_ext = ".zip"
+
+ # SubprocessHelper
+ report_interval = 0.25
+
+ temp_dir = None
+
+ @classmethod
+ def poll(cls, context):
+ return bpy.data.is_saved
+
+ def process_pre(self):
+ import os
+ import tempfile
+
+ self.temp_dir = tempfile.TemporaryDirectory()
+
+ filepath_blend = bpy.data.filepath
+
+ self.command = (
+ bpy.app.binary_path_python,
+ os.path.join(os.path.dirname(__file__), "blendfile_pack.py"),
+ # file to pack
+ "--input", filepath_blend,
+ # file to write
+ "--output", bpy.path.ensure_ext(self.filepath, ".zip"),
+ "--temp", self.temp_dir.name,
+ )
+
+ def process_post(self, returncode):
+ if self.temp_dir is not None:
+ try:
+ self.temp_dir.cleanup()
+ except:
+ import traceback
+ traceback.print_exc()
+
+
+def menu_func(self, context):
+ layout = self.layout
+ layout.separator()
+ layout.operator(ExportBlendPack.bl_idname)
+
+
+classes = (
+ ExportBlendPack,
+ )
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bpy.types.INFO_MT_file_external_data.append(menu_func)
+
+
+def unregister():
+ for cls in classes:
+ bpy.utils.unregister_class(cls)
+
+ bpy.types.INFO_MT_file_external_data.remove(menu_func)
+
+
+if __name__ == "__main__":
+ register()
diff --git a/io_blend_utils/bl_utils/pipe_non_blocking.py b/io_blend_utils/bl_utils/pipe_non_blocking.py
new file mode 100644
index 0000000..ead0a73
--- /dev/null
+++ b/io_blend_utils/bl_utils/pipe_non_blocking.py
@@ -0,0 +1,100 @@
+# ##### 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+"""
+Example use:
+
+ p = subprocess.Popen(
+ command,
+ stdout=subprocess.PIPE,
+ )
+
+ pipe_non_blocking_set(p.stdout.fileno())
+
+ try:
+ data = os.read(p.stdout.fileno(), 1)
+ except PortableBlockingIOError as ex:
+ if not pipe_non_blocking_is_error_blocking(ex):
+ raise ex
+"""
+
+
+__all__ = (
+ "pipe_non_blocking_set",
+ "pipe_non_blocking_is_error_blocking",
+ "PortableBlockingIOError",
+ )
+
+import os
+
+
+if os.name == "nt":
+ # MS-Windows Version
+ def pipe_non_blocking_set(fd):
+ # Constant could define globally but avoid polluting the name-space
+ # thanks to: http://stackoverflow.com/questions/34504970
+ import msvcrt
+
+ from ctypes import windll, byref, wintypes, WinError, POINTER
+ from ctypes.wintypes import HANDLE, DWORD, BOOL
+
+ LPDWORD = POINTER(DWORD)
+
+ PIPE_NOWAIT = wintypes.DWORD(0x00000001)
+
+ def pipe_no_wait(pipefd):
+ SetNamedPipeHandleState = windll.kernel32.SetNamedPipeHandleState
+ SetNamedPipeHandleState.argtypes = [HANDLE, LPDWORD, LPDWORD, LPDWORD]
+ SetNamedPipeHandleState.restype = BOOL
+
+ h = msvcrt.get_osfhandle(pipefd)
+
+ res = windll.kernel32.SetNamedPipeHandleState(h, byref(PIPE_NOWAIT), None, None)
+ if res == 0:
+ print(WinError())
+ return False
+ return True
+
+ return pipe_no_wait(fd)
+
+ def pipe_non_blocking_is_error_blocking(ex):
+ if not isinstance(ex, PortableBlockingIOError):
+ return False
+ from ctypes import GetLastError
+ ERROR_NO_DATA = 232
+
+ return (GetLastError() == ERROR_NO_DATA)
+
+ PortableBlockingIOError = OSError
+else:
+ # Posix Version
+ def pipe_non_blocking_set(fd):
+ import fcntl
+ fl = fcntl.fcntl(fd, fcntl.F_GETFL)
+ fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
+ return True
+
+ # only for compatibility with 'nt' version.
+ def pipe_non_blocking_is_error_blocking(ex):
+ if not isinstance(ex, PortableBlockingIOError):
+ return False
+ return True
+
+ PortableBlockingIOError = BlockingIOError
diff --git a/io_blend_utils/bl_utils/subprocess_helper.py b/io_blend_utils/bl_utils/subprocess_helper.py
new file mode 100644
index 0000000..2d289d3
--- /dev/null
+++ b/io_blend_utils/bl_utils/subprocess_helper.py
@@ -0,0 +1,172 @@
+# ##### 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+"""
+Defines an operator mix-in to use for non-blocking command line access.
+"""
+
+class SubprocessHelper:
+ """
+ Mix-in class for operators to run commands in a non-blocking way.
+
+ This uses a modal operator to manage an external process.
+
+ Subclass must define:
+ ``command``:
+ List of arguments to pass to subprocess.Popen
+ report_interval: Time in seconds between updating reports.
+
+ ``process_pre()``:
+ Callback that runs before the process executes.
+
+ ``process_post(returncode)``:
+ Callback that runs when the process has ende.
+ returncode is -1 if the process was terminated.
+ """
+
+ @staticmethod
+ def _non_blocking_readlines(f, chunk=64):
+ """
+ Iterate over lines, yielding b'' when nothings left
+ or when new data is not yet available.
+ """
+ import os
+
+ from .pipe_non_blocking import (
+ pipe_non_blocking_set,
+ pipe_non_blocking_is_error_blocking,
+ PortableBlockingIOError,
+ )
+
+ fd = f.fileno()
+ pipe_non_blocking_set(fd)
+
+ blocks = []
+
+ while True:
+ try:
+ data = os.read(fd, chunk)
+ if not data:
+ # case were reading finishes with no trailing newline
+ yield b''.join(blocks)
+ blocks.clear()
+ except PortableBlockingIOError as ex:
+ if not pipe_non_blocking_is_error_blocking(ex):
+ raise ex
+
+ yield b''
+ continue
+
+ while True:
+ n = data.find(b'\n')
+ if n == -1:
+ break
+
+ yield b''.join(blocks) + data[:n + 1]
+ data = data[n + 1:]
+ blocks.clear()
+ blocks.append(data)
+
+ def _report_output(self):
+ stdout_line_iter, stderr_line_iter = self._buffer_iter
+ for line_iter, report_type in (
+ (stdout_line_iter, {'INFO'}),
+ (stderr_line_iter, {'WARNING'})
+ ):
+ while True:
+ line = next(line_iter).rstrip() # rstrip all, to include \r on windows
+ if not line:
+ break
+ self.report(report_type, line.decode(encoding='utf-8', errors='surrogateescape'))
+
+ def _wm_enter
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-extensions-cvs
mailing list