[Bf-blender-cvs] [ae3d5dd] asset-engine: First (totally experimental and useless) version of a future 'BlenderCloud' asset engine.

Bastien Montagne noreply at git.blender.org
Mon Aug 29 17:13:33 CEST 2016


Commit: ae3d5ddc7d1104b937761a7dbf8cce7246b9b94e
Author: Bastien Montagne
Date:   Mon Aug 29 17:10:39 2016 +0200
Branches: asset-engine
https://developer.blender.org/rBae3d5ddc7d1104b937761a7dbf8cce7246b9b94e

First (totally experimental and useless) version of a future 'BlenderCloud' asset engine.

For now it's nothing but an empty shell - but a working proof of concept of how to 'embed'
that ugly asyncio-based stuff into a background working thread for asset engines.

Hopefully more funny stuff to come soon(ish).

===================================================================

M	release/scripts/startup/bl_operators/__init__.py
A	release/scripts/startup/bl_operators/claude/__init__.py
A	release/scripts/startup/bl_operators/claude/appdirs.py
A	release/scripts/startup/bl_operators/claude/cache.py
A	release/scripts/startup/bl_operators/claude/pillar.py
A	release/scripts/startup/bl_operators/claude/texture_browser.py
A	release/scripts/startup/bl_operators/claude/wheels/CacheControl-0.11.6-py3-none-any.whl
A	release/scripts/startup/bl_operators/claude/wheels/__init__.py
A	release/scripts/startup/bl_operators/claude/wheels/lockfile-0.12.2-py2.py3-none-any.whl
A	release/scripts/startup/bl_operators/claude/wheels/pillarsdk-1.2.0-py2.py3-none-any.whl
A	release/scripts/startup/bl_operators/claude/wheels/pillarsdk-1.4.0-py2.py3-none-any.whl

===================================================================

diff --git a/release/scripts/startup/bl_operators/__init__.py b/release/scripts/startup/bl_operators/__init__.py
index 1fb7868..0b13200 100644
--- a/release/scripts/startup/bl_operators/__init__.py
+++ b/release/scripts/startup/bl_operators/__init__.py
@@ -53,6 +53,7 @@ _modules = [
 
 _modules.append("amber")
 _modules.append("amber_asyncio")
+_modules.append("claude")
 
 import bpy
 
diff --git a/release/scripts/startup/bl_operators/claude/__init__.py b/release/scripts/startup/bl_operators/claude/__init__.py
new file mode 100644
index 0000000..1a64ae2
--- /dev/null
+++ b/release/scripts/startup/bl_operators/claude/__init__.py
@@ -0,0 +1,437 @@
+# ##### 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>
+
+# Blender Cloud Asset Engine, using Pillar and based on the Cloud addon.
+# "Claude" (French first name pronounced in a similar way as “cloud”) stands for Cloud Light Asset Under Development Engine.
+# Note: This will be a simple addon later, but until it gets to master, it's simpler to have it as a startup module!
+
+import bpy
+from bpy.types import (
+        AssetEngine,
+        Panel,
+        PropertyGroup,
+        )
+from bpy.props import (
+        StringProperty,
+        BoolProperty,
+        IntProperty,
+        FloatProperty,
+        EnumProperty,
+        CollectionProperty,
+        )
+
+# Support reloading
+if 'pillar' in locals():
+    import importlib
+
+    wheels = importlib.reload(wheels)
+    wheels.load_wheels()
+
+    pillar = importlib.reload(pillar)
+    cache = importlib.reload(cache)
+else:
+    from . import wheels
+
+    wheels.load_wheels()
+
+    from . import pillar, cache
+
+import concurrent.futures as futures
+import asyncio
+import threading
+
+import binascii
+import hashlib
+import json
+import os
+import stat
+import struct
+import time
+import random
+
+import pillarsdk
+from . import pillar, cache
+
+REQUIRED_ROLES_FOR_TEXTURE_BROWSER = {'subscriber', 'demo'}
+
+
+##########
+# Helpers.
+
+
+#############
+# Claude Jobs.
+class ClaudeJob:
+    @staticmethod
+    async def check_loop(loop, evt_cancel):
+        """Async function merely endlessly checking 'cancel' flag is not set, and stopping the loop in case it is..."""
+        while True:
+            if evt_cancel.is_set():
+                print("CAAANNNNNNCCCEEEEEEEELLLLLEDDDDDDDD!")
+                loop.stop()
+                return
+            await asyncio.sleep(1e-6)
+
+    @classmethod
+    def run_loop(cls, loop, evt_cancel):
+        """Function called in worker thread, sets the loop for current thread, and runs it."""
+        asyncio.set_event_loop(loop)
+        asyncio.ensure_future(cls.check_loop(loop, evt_cancel))
+        loop.run_forever()
+        loop.close()
+
+    def cancel(self):
+        self.evt_cancel.set()
+        self.running_loop.cancel()
+        futures.wait((self.running_loop,))
+
+    def __init__(self, executor, job_id):
+        self.executor = executor
+
+        self.job_id = job_id
+        self.status = {'VALID'}
+        self.progress = 0.0
+
+        self.loop = asyncio.new_event_loop()
+        #~ self.loop.set_default_executor(executor)
+        self.evt_cancel = threading.Event()
+        self.running_loop = self.executor.submit(self.run_loop, self.loop, self.evt_cancel)
+
+    def __del__(self):
+        self.cancel()
+
+
+class ClaudeJobList(ClaudeJob):
+    @staticmethod
+    async def ls(evt_cancel, node):
+        if evt_cancel.is_set():
+            return None
+
+        print("we should be listing Cloud content from Node ", node, "...")
+        await asyncio.sleep(1)
+
+        return ["..", "a", "b"]
+
+    def start(self):
+        self.nbr = 0
+        self.tot = 0
+        self.ls_task = asyncio.run_coroutine_threadsafe(self.ls(self.evt_cancel, self.curr_node), self.loop)
+        self.status = {'VALID', 'RUNNING'}
+
+    def update(self, dirs):
+        if self.evt_cancel.is_set():
+            self.cancel()
+            return
+
+        self.status = {'VALID', 'RUNNING'}
+        if self.ls_task is not None:
+            if not self.ls_task.done():
+                dirs[:] = [".."]
+                return
+            print("ls finished, we should have our children nodes now!")
+            dirs[:] = self.ls_task.result()
+            print(dirs)
+            self.ls_task = None
+
+        self.progress = self.nbr / self.tot if self.tot else 0.0
+        if self.ls_task is None:
+            self.status = {'VALID'}
+
+    def cancel(self):
+        print("CANCELLING...")
+        super().cancel()
+        if self.ls_task is not None and not self.ls_task.done():
+            self.ls_task.cancel()
+        self.status = {'VALID'}
+
+    def __init__(self, executor, job_id, curr_node):
+        super().__init__(executor, job_id)
+        self.curr_node = curr_node
+
+        self.ls_task = None
+
+        self.start()
+
+
+"""
+class AmberJobPreviews(AmberJob):
+    @staticmethod
+    def preview(uuid):
+        time.sleep(0.1)  # 100% Artificial Lag (c)
+        w = random.randint(2, 8)
+        h = random.randint(2, 8)
+        return [w, h, [random.getrandbits(32) for i in range(w * h)]]
+
+    def start(self, uuids):
+        self.nbr = 0
+        self.preview_tasks = {uuid.uuid_asset[:]: self.executor.submit(self.preview, uuid.uuid_asset[:]) for uuid in uuids.uuids}
+        self.tot = len(self.preview_tasks)
+        self.status = {'VALID', 'RUNNING'}
+
+    def update(self, uuids):
+        self.status = {'VALID', 'RUNNING'}
+
+        uuids = {uuid.uuid_asset[:]: uuid for uuid in uuids.uuids}
+
+        new_uuids = set(uuids)
+        old_uuids = set(self.preview_tasks)
+        del_uuids = old_uuids - new_uuids
+        new_uuids -= old_uuids
+
+        for uuid in del_uuids:
+            self.preview_tasks[uuid].cancel()
+            del self.preview_tasks[uuid]
+
+        for uuid in new_uuids:
+            self.preview_tasks[uuid] = self.executor.submit(self.preview, uuid)
+
+        self.tot = len(self.preview_tasks)
+        self.nbr = 0
+
+        done_uuids = set()
+        for uuid, tsk in self.preview_tasks.items():
+            if tsk.done():
+                w, h, pixels = tsk.result()
+                uuids[uuid].preview_size = (w, h)
+                uuids[uuid].preview_pixels = pixels
+                self.nbr += 1
+                done_uuids.add(uuid)
+
+        for uuid in done_uuids:
+            del self.preview_tasks[uuid]
+
+        self.progress = self.nbr / self.tot
+        if not self.preview_tasks:
+            self.status = {'VALID'}
+
+    def __init__(self, executor, job_id, uuids):
+        super().__init__(executor, job_id)
+        self.preview_tasks = {}
+
+        self.start(uuids)
+
+    def __del__(self):
+        # Avoid useless work!
+        for tsk in self.preview_tasks.values():
+            tsk.cancel()
+"""
+
+
+###########################
+# Main Asset Engine class.
+class AssetEngineClaude(AssetEngine):
+    bl_label = "Claude"
+    bl_version = (0 << 16) + (0 << 8) + 1  # Usual maj.min.rev version scheme...
+
+    def __init__(self):
+        self.executor = futures.ThreadPoolExecutor(8)  # Using threads for now, if issues arise we'll switch to process.
+
+        self.reset()
+
+        self.job_uuid = 1
+
+    def __del__(self):
+        # XXX This errors, saying self has no executor attribute... Suspect some py/RNA funky game. :/
+        #     Even though it does not seem to be an issue, this is not nice and shall be fixed somehow.
+        # XXX This is still erroring... Looks like we should rather have a 'remove' callback or so. :|
+        #~ executor = getattr(self, "executor", None)
+        #~ if executor is not None:
+            #~ executor.shutdown(wait=False)
+        pass
+
+    ########## Various helpers ##########
+    def reset(self):
+        print("Claude Reset!")
+        self.jobs = {}
+        self.root = ""
+        self.dirs = []
+
+        self.sortedfiltered = []
+
+    def pretty_version(self, v=None):
+        if v is None:
+            v = self.bl_version
+        return "%d.%d.%d" % ((v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF)
+
+    ########## PY-API only ##########
+    # UI header
+    def draw_header(self, layout, context):
+        st = context.space_data
+        params = st.params
+
+        # can be None when save/reload with a file selector open
+        if params:
+            is_lib_browser = params.use_library_browsing
+
+            layout.prop(params, "display_type", expand=True, text="")
+            layout.prop(params, "display_size", text="")
+            layout.prop(params, "sort_method", expand=True, text="")
+
+            layout.prop(params, "show_hidden", text="", icon='FILE_HIDDEN')
+            layout.prop(params, "use_filter", text="", icon='FILTER')
+
+            row = layout.row(align=True)
+            row.active = params.use_filter
+
+            if params.filter_glob:
+                #if st.active_operator and hasattr(st.active_operator, "filter_glob"):
+                #    row.prop(params, "filter_glob", text="")
+                row.label(params.filter_glob)
+            else:
+                row.prop(params, "use_filter_blender", text="")
+                row.prop(params, "use_filter_backup", text="")
+                row.prop(params, "use_filter_image", text="")
+                row.prop(params, "use_filter_movie", text="")
+                row.prop(params, "use_filter_script", text="")
+            

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list