From 13b93a9ea89b7d8994f2311adfe0fd0018793b94 Mon Sep 17 00:00:00 2001 From: Bruno Postle Date: Mon, 20 Jan 2025 08:14:51 +0000 Subject: [PATCH] Move cache_dir from site-packages to local folder (#5856) Previously cache_dir was located under site-packages, which is managed by blender and could be read-only. Now location defaults to eg. ~/.cache/bonsai or equivalent location on other platforms. See #5564 Note this adds a dependency on the platformdirs python module --- src/bonsai/Makefile | 2 ++ src/bonsai/bonsai/bim/__init__.py | 1 + src/bonsai/bonsai/bim/ifc.py | 5 +++-- src/bonsai/bonsai/bim/module/drawing/operator.py | 2 +- src/bonsai/bonsai/bim/operator.py | 16 ++++++++++++++++ src/bonsai/bonsai/bim/prop.py | 10 ++++++++++ src/bonsai/bonsai/bim/ui.py | 4 ++++ src/bonsai/bonsai/tool/debug.py | 2 +- src/bonsai/test/tool/test_debug.py | 2 +- 9 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/bonsai/Makefile b/src/bonsai/Makefile index 3702d78216..446b74c3a3 100644 --- a/src/bonsai/Makefile +++ b/src/bonsai/Makefile @@ -177,6 +177,8 @@ endif # Is not platform specific but has platform specific dependencies. cd build && . env/$(VENV_ACTIVATE) && $(PIP) download python-socketio[asyncio_client] $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --dest=./wheels cd build && . env/$(VENV_ACTIVATE) && $(PIP) download aiohttp $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --dest=./wheels + # Required to access platform specific paths + cd build && . env/$(VENV_ACTIVATE) && $(PIP) download platformdirs --dest=./wheels # Required by light module cd build && . env/$(VENV_ACTIVATE) && $(PIP) download pytz --dest=./wheels cd build && . env/$(VENV_ACTIVATE) && $(PIP) download tzfpy $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --dest=./wheels diff --git a/src/bonsai/bonsai/bim/__init__.py b/src/bonsai/bonsai/bim/__init__.py index b2f5c9b288..b931c61d04 100644 --- a/src/bonsai/bonsai/bim/__init__.py +++ b/src/bonsai/bonsai/bim/__init__.py @@ -116,6 +116,7 @@ classes = [ operator.RemoveIfcFile, operator.RevertClippingPlaneCut, operator.SelectDataDir, + operator.SelectCacheDir, operator.SelectIfcFile, operator.SelectSchemaDir, operator.SelectURIAttribute, diff --git a/src/bonsai/bonsai/bim/ifc.py b/src/bonsai/bonsai/bim/ifc.py index ebf4b2fe09..9576c60459 100644 --- a/src/bonsai/bonsai/bim/ifc.py +++ b/src/bonsai/bonsai/bim/ifc.py @@ -121,7 +121,8 @@ class IfcStore: if IfcStore.cache is None and IfcStore.path: ifc_key = IfcStore.path + IfcStore.file.wrapped_data.header.file_name.time_stamp ifc_hash = hashlib.md5(ifc_key.encode("utf-8")).hexdigest() - IfcStore.cache_path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "cache", f"{ifc_hash}.h5") + os.makedirs(bpy.context.scene.BIMProperties.cache_dir, exist_ok=True) + IfcStore.cache_path = os.path.join(bpy.context.scene.BIMProperties.cache_dir, f"{ifc_hash}.h5") cache_path = Path(IfcStore.cache_path) cache_settings = ifcopenshell.geom.settings() serializer_settings = ifcopenshell.geom.serializer_settings() @@ -161,7 +162,7 @@ class IfcStore: assert IfcStore.file ifc_key = IfcStore.path + IfcStore.file.wrapped_data.header.file_name.time_stamp ifc_hash = hashlib.md5(ifc_key.encode("utf-8")).hexdigest() - new_cache_path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "cache", f"{ifc_hash}.h5") + new_cache_path = os.path.join(bpy.context.scene.BIMProperties.cache_dir, f"{ifc_hash}.h5") IfcStore.cache = None try: shutil.move(IfcStore.cache_path, new_cache_path) diff --git a/src/bonsai/bonsai/bim/module/drawing/operator.py b/src/bonsai/bonsai/bim/module/drawing/operator.py index 4526555f7d..c71178f38a 100644 --- a/src/bonsai/bonsai/bim/module/drawing/operator.py +++ b/src/bonsai/bonsai/bim/module/drawing/operator.py @@ -730,7 +730,7 @@ class CreateDrawing(bpy.types.Operator): # Don't use draw.main() just whilst we're prototyping and experimenting # TODO: hash paths are never used ifc_hash = hashlib.md5(ifc_path.encode("utf-8")).hexdigest() - ifc_cache_path = os.path.join(context.scene.BIMProperties.data_dir, "cache", f"{ifc_hash}.h5") + ifc_cache_path = os.path.join(context.scene.BIMProperties.cache_dir, f"{ifc_hash}.h5") self.serialiser.setFile(ifc) drawing_elements = tool.Drawing.get_drawing_elements(self.camera_element, ifc_file=ifc) diff --git a/src/bonsai/bonsai/bim/operator.py b/src/bonsai/bonsai/bim/operator.py index bfcd24126e..db96a96e18 100644 --- a/src/bonsai/bonsai/bim/operator.py +++ b/src/bonsai/bonsai/bim/operator.py @@ -227,6 +227,22 @@ class SelectDataDir(bpy.types.Operator): return {"RUNNING_MODAL"} +class SelectCacheDir(bpy.types.Operator): + bl_idname = "bim.select_cache_dir" + bl_label = "Select Cache Directory" + bl_options = {"REGISTER", "UNDO"} + bl_description = "Select the directory that contains HDF5 cache files" + filepath: bpy.props.StringProperty(subtype="FILE_PATH") + + def execute(self, context): + context.scene.BIMProperties.cache_dir = os.path.dirname(self.filepath) + return {"FINISHED"} + + def invoke(self, context, event): + context.window_manager.fileselect_add(self) + return {"RUNNING_MODAL"} + + class SelectSchemaDir(bpy.types.Operator): bl_idname = "bim.select_schema_dir" bl_label = "Select Schema Directory" diff --git a/src/bonsai/bonsai/bim/prop.py b/src/bonsai/bonsai/bim/prop.py index 4f2bbfe66d..96df7cbb09 100644 --- a/src/bonsai/bonsai/bim/prop.py +++ b/src/bonsai/bonsai/bim/prop.py @@ -21,6 +21,7 @@ import os import bpy import json import importlib +import platformdirs import ifcopenshell import ifcopenshell.util.pset import ifcopenshell.util.unit @@ -130,6 +131,12 @@ def update_data_dir(self: "BIMProperties", context: bpy.types.Context) -> None: bonsai.bim.schema.ifc.data_dir = context.scene.BIMProperties.data_dir +def update_cache_dir(self: "BIMProperties", context: bpy.types.Context) -> None: + import bonsai.bim.schema + + bonsai.bim.schema.ifc.cache_dir = context.scene.BIMProperties.cache_dir + + def update_ifc_file(self: "BIMProperties", context: bpy.types.Context) -> None: if context.scene.BIMProperties.ifc_file: bonsai.bim.handler.loadIfcStore(context.scene) @@ -434,6 +441,9 @@ class BIMProperties(PropertyGroup): data_dir: StringProperty( default=os.path.join(cwd, "data") + os.path.sep, name="Data Directory", update=update_data_dir ) + cache_dir: StringProperty( + default=platformdirs.user_cache_dir("bonsai"), name="Cache Directory", update=update_cache_dir + ) has_blend_warning: BoolProperty(name="Has Blend Warning", default=False) pset_dir: StringProperty(default=os.path.join("psets") + os.path.sep, name="Default Psets Directory") ifc_file: StringProperty(name="IFC File", update=update_ifc_file) diff --git a/src/bonsai/bonsai/bim/ui.py b/src/bonsai/bonsai/bim/ui.py index 890e022684..0cec4b98a0 100644 --- a/src/bonsai/bonsai/bim/ui.py +++ b/src/bonsai/bonsai/bim/ui.py @@ -361,6 +361,10 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences): row.prop(context.scene.BIMProperties, "data_dir") row.operator("bim.select_data_dir", icon="FILE_FOLDER", text="") + row = self.layout.row(align=True) + row.prop(context.scene.BIMProperties, "cache_dir") + row.operator("bim.select_cache_dir", icon="FILE_FOLDER", text="") + def draw_drawing_settings(self, layout, context): layout.prop(context.scene.BIMProperties, "pset_dir") layout.prop(context.scene.DocProperties, "sheets_dir") diff --git a/src/bonsai/bonsai/tool/debug.py b/src/bonsai/bonsai/tool/debug.py index eb4dcd7df7..76644eda48 100644 --- a/src/bonsai/bonsai/tool/debug.py +++ b/src/bonsai/bonsai/tool/debug.py @@ -47,7 +47,7 @@ class Debug(bonsai.core.tool.Debug): @classmethod def purge_hdf5_cache(cls) -> None: - cache_dir = os.path.join(bpy.context.scene.BIMProperties.data_dir, "cache") + cache_dir = bpy.context.scene.BIMProperties.cache_dir filelist = [f for f in os.listdir(cache_dir) if f.endswith(".h5")] for f in filelist: try: diff --git a/src/bonsai/test/tool/test_debug.py b/src/bonsai/test/tool/test_debug.py index f59f01ef60..90b7eed030 100644 --- a/src/bonsai/test/tool/test_debug.py +++ b/src/bonsai/test/tool/test_debug.py @@ -55,7 +55,7 @@ class TestLoadExpress(NewFile): class TestPurgeHdf5Cache(NewFile): def test_run(self): - cache_dir = Path(bpy.context.scene.BIMProperties.data_dir) / "cache" + cache_dir = Path(bpy.context.scene.BIMProperties.cache_dir) test_file = cache_dir / "test.h5" test_file.parent.mkdir(parents=True, exist_ok=True) test_file.touch()