import json import os import tempfile import zipfile import FreeCAD as App FREECAD_COMMIT = "0108fd4b4850cc46e625b60e53cea7a7bbe69f8d" def object_state(obj): return { "state": [str(value) for value in obj.State], "mustExecute": bool(obj.MustExecute), "documentTouched": bool(obj.Document.isTouched()), } def change_case(case_id, attr=0, runtime_status=None): document = App.newDocument("PropertyStatus_" + case_id) try: obj = document.addObject("App::FeatureTest", "Feature") obj.addProperty("App::PropertyInteger", "Value", "Oracle", "Status probe", attr) if runtime_status: obj.setPropertyStatus("Value", runtime_status) document.recompute() document.purgeTouched() before = object_state(obj) obj.Value = 7 changed = object_state(obj) recomputed = int(document.recompute()) after_recompute = object_state(obj) return { "id": case_id, "attr": int(attr), "runtimeStatus": runtime_status, "reportedStatus": [str(value) for value in obj.getPropertyStatus("Value")], "reportedType": [str(value) for value in obj.getTypeOfProperty("Value")], "before": before, "changed": changed, "recomputeCount": recomputed, "afterRecompute": after_recompute, } finally: App.closeDocument(document.Name) def locked_dynamic_case(): document = App.newDocument("PropertyStatus_LockDynamic") try: obj = document.addObject("App::FeaturePython", "Feature") obj.addProperty("App::PropertyInteger", "LockedValue", "Oracle") obj.setPropertyStatus("LockedValue", "LockDynamic") failures = {} for operation, callback in { "remove": lambda: obj.removeProperty("LockedValue"), "rename": lambda: obj.renameProperty("LockedValue", "RenamedValue"), }.items(): try: callback() failures[operation] = None except Exception as error: failures[operation] = {"type": type(error).__name__, "message": str(error)} return { "reportedStatus": [str(value) for value in obj.getPropertyStatus("LockedValue")], "propertyStillPresent": "LockedValue" in obj.PropertiesList, "renamedPropertyPresent": "RenamedValue" in obj.PropertiesList, "failures": failures, } finally: App.closeDocument(document.Name) def archive_case(): with tempfile.TemporaryDirectory(prefix="freecad-property-status-") as temp_dir: path = os.path.join(temp_dir, "PropertyStatus.FCStd") document = App.newDocument("PropertyStatusArchive") try: obj = document.addObject("App::FeaturePython", "Feature") obj.addProperty("App::PropertyInteger", "Persisted", "Oracle") obj.Persisted = 11 obj.setPropertyStatus("Persisted", "Output") obj.addProperty("App::PropertyInteger", "RuntimeTransient", "Oracle") obj.RuntimeTransient = 12 obj.setPropertyStatus("RuntimeTransient", "Transient") obj.addProperty("App::PropertyInteger", "TypeTransient", "Oracle", "", int(App.PropertyType.Prop_Transient)) obj.TypeTransient = 13 obj.addProperty("App::PropertyInteger", "NoPersist", "Oracle", "", int(App.PropertyType.Prop_NoPersist)) obj.NoPersist = 14 document.recompute() document.saveAs(path) finally: App.closeDocument(document.Name) with zipfile.ZipFile(path, "r") as archive: document_xml = archive.read("Document.xml").decode("utf-8") reopened = App.openDocument(path) try: obj = reopened.getObject("Feature") return { "xml": { "persistedProperty": '