2025-08-06 23:13:58 +00:00
|
|
|
# SPDX-FileCopyrightText: 2025 Blender Authors
|
|
|
|
|
#
|
|
|
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
|
|
2025-08-07 09:21:31 +10:00
|
|
|
# ./blender.bin --background --factory-startup --python tests/python/bl_pyapi_bpy_app_tempdir.py -- --verbose
|
2025-08-06 23:13:58 +00:00
|
|
|
|
|
|
|
|
# NOTE(ideasman42):
|
|
|
|
|
#
|
|
|
|
|
# - Creating a directory without write permissions across all supported platforms
|
|
|
|
|
# is it not trivial, for the purpose of these tests simply create a file as a way of
|
|
|
|
|
# pointing to a temporary path which can't have sub-directories created under it.
|
|
|
|
|
#
|
|
|
|
|
# - Internally, when no temporary path is set the hard coded path `/tmp/` is used.
|
|
|
|
|
# For the purpose of the tests being complete - control over this path would be needed too.
|
|
|
|
|
#
|
|
|
|
|
# - When changing the temp directory & on exit
|
|
|
|
|
# Blender's *session* temp directory is recursively removed.
|
|
|
|
|
#
|
|
|
|
|
# These tests were added as part of a fix for a serious flaw (see #144042)
|
|
|
|
|
# which would recursively delete the users `C:\` since the tests aren't sand-boxed
|
|
|
|
|
# all of the following tests reference paths within a newly creating temporary directory
|
|
|
|
|
# instead of referencing root paths to avoid risks for developers who run tests.
|
|
|
|
|
#
|
|
|
|
|
# - A related test exists `BLI_tempfile_test.cc` however this doesn't deal with
|
|
|
|
|
# Blender's user preferences and fallbacks used in Blender.
|
|
|
|
|
#
|
|
|
|
|
|
|
|
|
|
__all__ = (
|
|
|
|
|
"main",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
import unittest
|
|
|
|
|
import tempfile
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
is_win32 = os.name == "nt"
|
|
|
|
|
TEMP_ENV = "TEMP" if is_win32 else "TMPDIR"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def system_temp_set(path: str) -> None:
|
|
|
|
|
os.environ[TEMP_ENV] = path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def prefs_temp_set(path: str) -> None:
|
|
|
|
|
import bpy # type: ignore
|
|
|
|
|
bpy.context.preferences.filepaths.temporary_directory = path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def prefs_temp_get() -> str:
|
|
|
|
|
import bpy
|
|
|
|
|
result = bpy.context.preferences.filepaths.temporary_directory
|
|
|
|
|
assert isinstance(result, str)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def blender_tempdir_session_get() -> str:
|
|
|
|
|
import bpy
|
|
|
|
|
result = bpy.app.tempdir
|
|
|
|
|
assert isinstance(result, str)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def empty_file(path: str) -> None:
|
|
|
|
|
with open(path, 'wb') as _fh:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def commonpath_safe(paths: list[str]) -> str:
|
|
|
|
|
if is_win32:
|
|
|
|
|
try:
|
|
|
|
|
return os.path.commonpath(paths)
|
|
|
|
|
except ValueError:
|
|
|
|
|
# Workaround error on Windows.
|
|
|
|
|
# ValueError: Paths don't have the same drive
|
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
return os.path.commonpath(paths)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestTempDir(unittest.TestCase):
|
|
|
|
|
|
|
|
|
|
def setUp(self) -> None:
|
|
|
|
|
print(prefs_temp_get())
|
|
|
|
|
assert prefs_temp_get() == ""
|
|
|
|
|
system_temp_set("")
|
|
|
|
|
prefs_temp_set("")
|
|
|
|
|
|
|
|
|
|
def tearDown(self) -> None:
|
|
|
|
|
prefs_temp_set("")
|
|
|
|
|
system_temp_set("")
|
|
|
|
|
|
|
|
|
|
def test_fallback(self) -> None:
|
|
|
|
|
# Set a file for preferences & system temp, ensure neither are used.
|
|
|
|
|
with tempfile.TemporaryDirectory() as tempdir:
|
|
|
|
|
|
|
|
|
|
temp_sys = os.path.join(tempdir, "a_sys")
|
|
|
|
|
temp_bpy = os.path.join(tempdir, "b_bpy")
|
|
|
|
|
|
|
|
|
|
empty_file(temp_sys)
|
|
|
|
|
empty_file(temp_bpy)
|
|
|
|
|
|
|
|
|
|
system_temp_set(temp_sys)
|
|
|
|
|
prefs_temp_set(temp_bpy)
|
|
|
|
|
|
|
|
|
|
temp_session = blender_tempdir_session_get()
|
|
|
|
|
|
|
|
|
|
# Ensure neither are used.
|
|
|
|
|
# This will try to use `/tmp/`.
|
|
|
|
|
commonpath_test = commonpath_safe([temp_sys, temp_session])
|
|
|
|
|
self.assertFalse(commonpath_test and os.path.samefile(temp_sys, commonpath_test))
|
|
|
|
|
commonpath_test = commonpath_safe([temp_bpy, temp_session])
|
|
|
|
|
self.assertFalse(commonpath_test and os.path.samefile(temp_bpy, commonpath_test))
|
|
|
|
|
|
|
|
|
|
def test_system(self) -> None:
|
|
|
|
|
# Set an file as the preferences temp directory, ensure the system path is used.
|
|
|
|
|
with tempfile.TemporaryDirectory() as tempdir:
|
|
|
|
|
|
|
|
|
|
temp_sys = os.path.join(tempdir, "a_sys")
|
|
|
|
|
temp_bpy = os.path.join(tempdir, "b_bpy")
|
|
|
|
|
|
|
|
|
|
os.mkdir(temp_sys)
|
|
|
|
|
empty_file(temp_bpy)
|
|
|
|
|
|
|
|
|
|
system_temp_set(temp_sys)
|
|
|
|
|
prefs_temp_set(temp_bpy)
|
|
|
|
|
|
|
|
|
|
temp_session = blender_tempdir_session_get()
|
|
|
|
|
|
|
|
|
|
self.assertTrue(os.path.samefile(temp_sys, os.path.commonpath([temp_sys, temp_session])))
|
|
|
|
|
|
|
|
|
|
def test_prefs(self) -> None:
|
|
|
|
|
# Set an file as the system temp directory, ensure the preferences path is used.
|
|
|
|
|
with tempfile.TemporaryDirectory() as tempdir:
|
|
|
|
|
|
|
|
|
|
temp_sys = os.path.join(tempdir, "a_sys")
|
|
|
|
|
temp_bpy = os.path.join(tempdir, "b_bpy")
|
|
|
|
|
|
|
|
|
|
empty_file(temp_sys)
|
|
|
|
|
os.mkdir(temp_bpy)
|
|
|
|
|
|
|
|
|
|
system_temp_set(temp_sys)
|
|
|
|
|
prefs_temp_set(temp_bpy)
|
|
|
|
|
|
|
|
|
|
temp_session = blender_tempdir_session_get()
|
|
|
|
|
|
|
|
|
|
self.assertTrue(os.path.samefile(temp_bpy, os.path.commonpath([temp_bpy, temp_session])))
|
|
|
|
|
|
|
|
|
|
def test_system_to_prefs(self) -> None:
|
|
|
|
|
with tempfile.TemporaryDirectory() as tempdir:
|
|
|
|
|
temp_sys = os.path.join(tempdir, "a_sys")
|
|
|
|
|
temp_bpy = os.path.join(tempdir, "b_bpy")
|
|
|
|
|
|
|
|
|
|
os.mkdir(temp_sys)
|
|
|
|
|
empty_file(temp_bpy)
|
|
|
|
|
|
|
|
|
|
system_temp_set(temp_sys)
|
|
|
|
|
prefs_temp_set(temp_bpy)
|
|
|
|
|
|
|
|
|
|
temp_session = blender_tempdir_session_get()
|
|
|
|
|
self.assertTrue(os.path.samefile(temp_sys, os.path.commonpath([temp_sys, temp_session])))
|
|
|
|
|
|
|
|
|
|
# Now set the preferences and ensure the previous directory gets purged and the new one set.
|
|
|
|
|
os.unlink(temp_bpy)
|
|
|
|
|
os.mkdir(temp_bpy)
|
|
|
|
|
|
|
|
|
|
# Ensure the preferences path is now used.
|
|
|
|
|
prefs_temp_set(temp_bpy)
|
|
|
|
|
|
|
|
|
|
self.assertFalse(os.path.exists(temp_session))
|
|
|
|
|
temp_session = blender_tempdir_session_get()
|
|
|
|
|
self.assertTrue(os.path.samefile(temp_bpy, os.path.commonpath([temp_bpy, temp_session])))
|
|
|
|
|
|
|
|
|
|
def test_prefs_to_system(self) -> None:
|
|
|
|
|
with tempfile.TemporaryDirectory() as tempdir:
|
|
|
|
|
temp_sys = os.path.join(tempdir, "a_sys")
|
|
|
|
|
temp_bpy = os.path.join(tempdir, "b_bpy")
|
|
|
|
|
|
|
|
|
|
empty_file(temp_sys)
|
|
|
|
|
os.mkdir(temp_bpy)
|
|
|
|
|
|
|
|
|
|
system_temp_set(temp_sys)
|
|
|
|
|
prefs_temp_set(temp_bpy)
|
|
|
|
|
|
|
|
|
|
temp_session = blender_tempdir_session_get()
|
|
|
|
|
self.assertTrue(os.path.samefile(temp_bpy, os.path.commonpath([temp_bpy, temp_session])))
|
|
|
|
|
|
|
|
|
|
# Now set the preferences and ensure the previous directory gets purged and the new one set.
|
|
|
|
|
os.unlink(temp_sys)
|
|
|
|
|
os.mkdir(temp_sys)
|
|
|
|
|
|
|
|
|
|
# Ensure the system path is now used.
|
|
|
|
|
prefs_temp_set(temp_sys)
|
|
|
|
|
|
|
|
|
|
self.assertFalse(os.path.exists(temp_session))
|
|
|
|
|
temp_session = blender_tempdir_session_get()
|
|
|
|
|
self.assertTrue(os.path.samefile(temp_sys, os.path.commonpath([temp_sys, temp_session])))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
import sys
|
|
|
|
|
unittest.main(argv=[__file__] + (sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
main()
|