Compare commits

..

4 Commits

11 changed files with 49 additions and 105 deletions

View File

@ -1,42 +1,5 @@
# Blender Cloud changelog # Blender Cloud changelog
## Version 1.25 (2022-02-25)
- Compatibility with Blender 3.1 (Python 3.10).
- Bump blender-asset-tracer to version 1.11, for UDIM support.
## Version 1.24 (2022-02-04)
- Bump blender-asset-tracer version 1.8 → 1.10, for fixing a bug where files were doubly-compressed.
## Version 1.23 (2021-11-09)
- Bump blender-asset-tracer version 1.7 → 1.8, for compatibility with sending read-only blend files to Flamenco.
## Version 1.22 (2021-11-05)
- Fix Windows incompatibility when using Shaman URLs as job storage path.
- Bump blender-asset-tracer version 1.6 → 1.7, for compatibility with files compressed by Blender 3.0.
## Version 1.21 (2021-07-27)
- Bump blender-asset-tracer version 1.5.1 → 1.6, for better compatibility with Geometry Nodes.
## Version 1.20 (2021-07-22)
- Bump blender-asset-tracer version 1.3.1 -> 1.5.1.
- Blender-asset-tracer "Strict Pointer Mode" disabled, to avoid issues with
not-entirely-synced library overrides.
## Version 1.19 (2021-02-23)
- Another Python 3.9+ compatibility fix.
## Version 1.18 (2021-02-16) ## Version 1.18 (2021-02-16)
- Add compatibility with Python 3.9 (as used in Blender 2.93). - Add compatibility with Python 3.9 (as used in Blender 2.93).

View File

@ -21,7 +21,7 @@
bl_info = { bl_info = {
"name": "Blender Cloud", "name": "Blender Cloud",
"author": "Sybren A. Stüvel, Francesco Siddi, Inês Almeida, Antony Riakiotakis", "author": "Sybren A. Stüvel, Francesco Siddi, Inês Almeida, Antony Riakiotakis",
"version": (1, 25), "version": (1, 18),
"blender": (2, 80, 0), "blender": (2, 80, 0),
"location": "Addon Preferences panel, and Ctrl+Shift+Alt+A anywhere for texture browser", "location": "Addon Preferences panel, and Ctrl+Shift+Alt+A anywhere for texture browser",
"description": "Texture library browser and Blender Sync. Requires the Blender ID addon " "description": "Texture library browser and Blender Sync. Requires the Blender ID addon "

View File

@ -56,11 +56,8 @@ def setup_asyncio_executor():
from . import pillar from . import pillar
# Python 3.8 deprecated the 'loop' parameter, 3.10 removed it.
kwargs = {"loop": loop} if sys.version_info < (3, 8) else {}
# No more than this many Pillar calls should be made simultaneously # No more than this many Pillar calls should be made simultaneously
pillar.pillar_semaphore = asyncio.Semaphore(3, **kwargs) pillar.pillar_semaphore = asyncio.Semaphore(3, loop=loop)
def kick_async_loop(*args) -> bool: def kick_async_loop(*args) -> bool:

View File

@ -668,8 +668,6 @@ class ATTRACT_OT_open_meta_blendfile(AttractOperatorMixin, Operator):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
if context.selected_sequences is None:
return False
return bool( return bool(
any(cls.filename_from_metadata(s) for s in context.selected_sequences) any(cls.filename_from_metadata(s) for s in context.selected_sequences)
) )

View File

@ -47,7 +47,7 @@ log = logging.getLogger(__name__)
icons = None icons = None
@pyside_cache @pyside_cache("version")
def blender_syncable_versions(self, context): def blender_syncable_versions(self, context):
"""Returns the list of items used by SyncStatusProperties.version EnumProperty.""" """Returns the list of items used by SyncStatusProperties.version EnumProperty."""
@ -117,7 +117,7 @@ class SyncStatusProperties(PropertyGroup):
self["available_blender_versions"] = new_versions self["available_blender_versions"] = new_versions
@pyside_cache @pyside_cache("project")
def bcloud_available_projects(self, context): def bcloud_available_projects(self, context):
"""Returns the list of items used by BlenderCloudProjectGroup.project EnumProperty.""" """Returns the list of items used by BlenderCloudProjectGroup.project EnumProperty."""

View File

@ -42,10 +42,11 @@ else:
from .. import blender from .. import blender
import bpy import bpy
from bpy.types import Operator, PropertyGroup from bpy.types import AddonPreferences, Operator, WindowManager, Scene, PropertyGroup
from bpy.props import ( from bpy.props import (
StringProperty, StringProperty,
EnumProperty, EnumProperty,
PointerProperty,
BoolProperty, BoolProperty,
IntProperty, IntProperty,
) )
@ -53,8 +54,6 @@ from bpy.props import (
from .. import async_loop, pillar, project_specific, utils from .. import async_loop, pillar, project_specific, utils
from ..utils import pyside_cache, redraw from ..utils import pyside_cache, redraw
import blender_asset_tracer.blendfile
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
# Global flag used to determine whether panels etc. can be drawn. # Global flag used to determine whether panels etc. can be drawn.
@ -92,7 +91,7 @@ def scene_sample_count(scene) -> int:
return samples return samples
@pyside_cache @pyside_cache("manager")
def available_managers(self, context): def available_managers(self, context):
"""Returns the list of items used by a manager-selector EnumProperty.""" """Returns the list of items used by a manager-selector EnumProperty."""
@ -270,7 +269,7 @@ def is_file_inside_job_storage(prefs, current_file: typing.Union[str, Path]) ->
if isinstance(current_file, str): if isinstance(current_file, str):
# Shaman URLs are always remote, so the current file cannot be in there. # Shaman URLs are always remote, so the current file cannot be in there.
if is_shaman_url(prefs.flamenco_job_file_path): if is_shaman_url(current_file):
return False return False
current_file = Path(current_file) current_file = Path(current_file)
@ -650,12 +649,6 @@ class FLAMENCO_OT_render(
self.log.debug("projdir: %s", projdir) self.log.debug("projdir: %s", projdir)
# Due to issues with library overrides and unsynced pointers, it's quite
# common for the Blender Animation Studio to get crashes of BAT. To avoid
# these, Strict Pointer Mode is disabled.
blender_asset_tracer.blendfile.set_strict_pointer_mode(False)
if is_shaman_url(prefs.flamenco_job_file_path): if is_shaman_url(prefs.flamenco_job_file_path):
endpoint, _ = bat_interface.parse_shaman_endpoint( endpoint, _ = bat_interface.parse_shaman_endpoint(
prefs.flamenco_job_file_path prefs.flamenco_job_file_path
@ -954,7 +947,7 @@ async def create_job(
*, *,
priority: int = 50, priority: int = 50,
job_description: str = None, job_description: str = None,
start_paused=False, start_paused=False
) -> dict: ) -> dict:
"""Creates a render job at Flamenco Server, returning the job object as dictionary.""" """Creates a render job at Flamenco Server, returning the job object as dictionary."""
@ -1007,7 +1000,7 @@ def _render_output_path(
render_image_format: str, render_image_format: str,
flamenco_render_frame_range: str, flamenco_render_frame_range: str,
*, *,
include_rel_path: bool = True, include_rel_path: bool = True
) -> typing.Optional[PurePath]: ) -> typing.Optional[PurePath]:
"""Cached version of render_output_path() """Cached version of render_output_path()

View File

@ -25,7 +25,6 @@ import logging
from contextlib import closing, contextmanager from contextlib import closing, contextmanager
import urllib.parse import urllib.parse
import pathlib import pathlib
import sys
import requests.adapters import requests.adapters
import requests.packages.urllib3.util.retry import requests.packages.urllib3.util.retry
@ -262,17 +261,14 @@ async def pillar_call(pillar_func, *args, caching=True, **kwargs):
) )
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
# Python 3.8 deprecated the 'loop' parameter, 3.10 removed it.
kwargs = {"loop": loop} if sys.version_info < (3, 8) else {}
# Use explicit calls to acquire() and release() so that we have more control over # Use explicit calls to acquire() and release() so that we have more control over
# how long we wait and how we handle timeouts. # how long we wait and how we handle timeouts.
try: try:
await asyncio.wait_for(pillar_semaphore.acquire(), timeout=10, **kwargs) await asyncio.wait_for(pillar_semaphore.acquire(), timeout=10, loop=loop)
except asyncio.TimeoutError: except asyncio.TimeoutError:
log.info("Waiting for semaphore to call %s", pillar_func.__name__) log.info("Waiting for semaphore to call %s", pillar_func.__name__)
try: try:
await asyncio.wait_for(pillar_semaphore.acquire(), timeout=50, **kwargs) await asyncio.wait_for(pillar_semaphore.acquire(), timeout=50, loop=loop)
except asyncio.TimeoutError: except asyncio.TimeoutError:
raise RuntimeError("Timeout waiting for Pillar Semaphore!") raise RuntimeError("Timeout waiting for Pillar Semaphore!")
@ -652,11 +648,9 @@ async def fetch_texture_thumbs(
for texture_node in texture_nodes for texture_node in texture_nodes
) )
# Python 3.8 deprecated the 'loop' parameter, 3.10 removed it.
kwargs = {"loop": asyncio.get_event_loop()} if sys.version_info < (3, 8) else {}
# raises any exception from failed handle_texture_node() calls. # raises any exception from failed handle_texture_node() calls.
await asyncio.gather(*coros, **kwargs) loop = asyncio.get_event_loop()
await asyncio.gather(*coros, loop=loop)
log.info("fetch_texture_thumbs: Done downloading texture thumbnails") log.info("fetch_texture_thumbs: Done downloading texture thumbnails")
@ -935,10 +929,8 @@ async def download_texture(
) )
downloaders.append(dlr) downloaders.append(dlr)
# Python 3.8 deprecated the 'loop' parameter, 3.10 removed it. loop = asyncio.get_event_loop()
kwargs = {"loop": asyncio.get_event_loop()} if sys.version_info < (3, 8) else {} return await asyncio.gather(*downloaders, return_exceptions=True, loop=loop)
return await asyncio.gather(*downloaders, return_exceptions=True, **kwargs)
async def upload_file( async def upload_file(

View File

@ -19,7 +19,6 @@
import json import json
import pathlib import pathlib
import typing import typing
from typing import Any, Dict, Optional, Tuple
def sizeof_fmt(num: int, suffix="B") -> str: def sizeof_fmt(num: int, suffix="B") -> str:
@ -36,7 +35,7 @@ def sizeof_fmt(num: int, suffix="B") -> str:
return "%.1f Yi%s" % (num, suffix) return "%.1f Yi%s" % (num, suffix)
def find_in_path(path: pathlib.Path, filename: str) -> Optional[pathlib.Path]: def find_in_path(path: pathlib.Path, filename: str) -> typing.Optional[pathlib.Path]:
"""Performs a breadth-first search for the filename. """Performs a breadth-first search for the filename.
Returns the path that contains the file, or None if not found. Returns the path that contains the file, or None if not found.
@ -67,29 +66,41 @@ def find_in_path(path: pathlib.Path, filename: str) -> Optional[pathlib.Path]:
return None return None
# Mapping from (module name, function name) to the last value returned by that function. def pyside_cache(propname):
_pyside_cache: Dict[Tuple[str, str], Any] = {}
def pyside_cache(wrapped):
"""Decorator, stores the result of the decorated callable in Python-managed memory. """Decorator, stores the result of the decorated callable in Python-managed memory.
This is to work around the warning at This is to work around the warning at
https://www.blender.org/api/blender_python_api_master/bpy.props.html#bpy.props.EnumProperty https://www.blender.org/api/blender_python_api_master/bpy.props.html#bpy.props.EnumProperty
""" """
import functools if callable(propname):
raise TypeError('Usage: pyside_cache("property_name")')
@functools.wraps(wrapped) def decorator(wrapped):
# We can't use (*args, **kwargs), because EnumProperty explicitly checks """Stores the result of the callable in Python-managed memory.
# for the number of fixed positional arguments.
def decorator(self, context): This is to work around the warning at
result = None https://www.blender.org/api/blender_python_api_master/bpy.props.html#bpy.props.EnumProperty
try: """
result = wrapped(self, context)
return result import functools
finally:
_pyside_cache[wrapped.__module__, wrapped.__name__] = result @functools.wraps(wrapped)
# We can't use (*args, **kwargs), because EnumProperty explicitly checks
# for the number of fixed positional arguments.
def wrapper(self, context):
result = None
try:
result = wrapped(self, context)
return result
finally:
try:
rna_type, rna_info = self.bl_rna.__annotations__[propname]
except AttributeError:
rna_type, rna_info = getattr(self.bl_rna, propname)
rna_info["_cached_result"] = result
return wrapper
return decorator return decorator

View File

@ -58,12 +58,8 @@ def wheel_filename(fname_prefix: str) -> str:
if not wheels: if not wheels:
raise RuntimeError("Unable to find wheel at %r" % path_pattern) raise RuntimeError("Unable to find wheel at %r" % path_pattern)
# If there are multiple wheels that match, load the last-modified one. # If there are multiple wheels that match, load the latest one.
# Alphabetical sorting isn't going to cut it since BAT 1.10 was released. wheels.sort()
def modtime(filename: str) -> int:
return os.stat(filename).st_mtime
wheels.sort(key=modtime)
return wheels[-1] return wheels[-1]
@ -72,8 +68,3 @@ def load_wheels():
load_wheel("lockfile", "lockfile") load_wheel("lockfile", "lockfile")
load_wheel("cachecontrol", "CacheControl") load_wheel("cachecontrol", "CacheControl")
load_wheel("pillarsdk", "pillarsdk") load_wheel("pillarsdk", "pillarsdk")
if __name__ == "__main__":
wheel = wheel_filename("blender_asset_tracer")
print(f"Wheel: {wheel}")

View File

@ -3,7 +3,7 @@
lockfile==0.12.2 lockfile==0.12.2
pillarsdk==1.8.0 pillarsdk==1.8.0
wheel==0.29.0 wheel==0.29.0
blender-asset-tracer==1.11 blender-asset-tracer==1.3.1
# Secondary requirements: # Secondary requirements:
asn1crypto==0.24.0 asn1crypto==0.24.0

View File

@ -248,9 +248,8 @@ setup(
"install_egg_info": AvoidEggInfo, "install_egg_info": AvoidEggInfo,
"wheels": BuildWheels, "wheels": BuildWheels,
}, },
name="blender_cloud",
description="The Blender Cloud addon allows browsing the Blender Cloud from Blender.", description="The Blender Cloud addon allows browsing the Blender Cloud from Blender.",
version="1.25", version="1.18",
author="Sybren A. Stüvel", author="Sybren A. Stüvel",
author_email="sybren@stuvel.eu", author_email="sybren@stuvel.eu",
packages=find_packages("."), packages=find_packages("."),