From 3a2e9bc67235e76c2bab71f5ab638c281b17ec74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 23 Feb 2021 11:56:38 +0100 Subject: [PATCH] Simplify @pyside_cache decorator This fixes a compatibility issue with Python 3.9+, and at the same time avoids a not-yet-quite-stable area of Blender's Python API. --- CHANGELOG.md | 4 +++ blender_cloud/blender.py | 4 +-- blender_cloud/flamenco/__init__.py | 2 +- blender_cloud/utils.py | 47 ++++++++++++------------------ 4 files changed, 25 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4569cb..222078d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Blender Cloud changelog +## Version 1.19 (in development) + +- Another Python 3.9+ compatibility fix. + ## Version 1.18 (2021-02-16) - Add compatibility with Python 3.9 (as used in Blender 2.93). diff --git a/blender_cloud/blender.py b/blender_cloud/blender.py index 871c5f3..c8d63ce 100644 --- a/blender_cloud/blender.py +++ b/blender_cloud/blender.py @@ -47,7 +47,7 @@ log = logging.getLogger(__name__) icons = None -@pyside_cache("version") +@pyside_cache def blender_syncable_versions(self, context): """Returns the list of items used by SyncStatusProperties.version EnumProperty.""" @@ -117,7 +117,7 @@ class SyncStatusProperties(PropertyGroup): self["available_blender_versions"] = new_versions -@pyside_cache("project") +@pyside_cache def bcloud_available_projects(self, context): """Returns the list of items used by BlenderCloudProjectGroup.project EnumProperty.""" diff --git a/blender_cloud/flamenco/__init__.py b/blender_cloud/flamenco/__init__.py index 7a47b01..088761a 100644 --- a/blender_cloud/flamenco/__init__.py +++ b/blender_cloud/flamenco/__init__.py @@ -91,7 +91,7 @@ def scene_sample_count(scene) -> int: return samples -@pyside_cache("manager") +@pyside_cache def available_managers(self, context): """Returns the list of items used by a manager-selector EnumProperty.""" diff --git a/blender_cloud/utils.py b/blender_cloud/utils.py index ce2ac06..d0f398f 100644 --- a/blender_cloud/utils.py +++ b/blender_cloud/utils.py @@ -19,6 +19,7 @@ import json import pathlib import typing +from typing import Any, Dict, Optional, Tuple def sizeof_fmt(num: int, suffix="B") -> str: @@ -35,7 +36,7 @@ def sizeof_fmt(num: int, suffix="B") -> str: return "%.1f Yi%s" % (num, suffix) -def find_in_path(path: pathlib.Path, filename: str) -> typing.Optional[pathlib.Path]: +def find_in_path(path: pathlib.Path, filename: str) -> Optional[pathlib.Path]: """Performs a breadth-first search for the filename. Returns the path that contains the file, or None if not found. @@ -66,41 +67,29 @@ def find_in_path(path: pathlib.Path, filename: str) -> typing.Optional[pathlib.P return None -def pyside_cache(propname): +# Mapping from (module name, function name) to the last value returned by that function. +_pyside_cache: Dict[Tuple[str, str], Any] = {} + + +def pyside_cache(wrapped): """Decorator, stores the result of the decorated callable in Python-managed memory. This is to work around the warning at https://www.blender.org/api/blender_python_api_master/bpy.props.html#bpy.props.EnumProperty """ - if callable(propname): - raise TypeError('Usage: pyside_cache("property_name")') + import functools - def decorator(wrapped): - """Stores the result of the callable in Python-managed memory. - - This is to work around the warning at - https://www.blender.org/api/blender_python_api_master/bpy.props.html#bpy.props.EnumProperty - """ - - import functools - - @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 + @functools.wraps(wrapped) + # We can't use (*args, **kwargs), because EnumProperty explicitly checks + # for the number of fixed positional arguments. + def decorator(self, context): + result = None + try: + result = wrapped(self, context) + return result + finally: + _pyside_cache[wrapped.__module__, wrapped.__name__] = result return decorator