Skip to content

Commit 13dfeb2

Browse files
committed
ENH: Add slicer.util.chdir context manager
This commit implements a non thread-safe context manager to change the current working directory. Available in Python 3.11 as ``contextlib.chdir`` and adapted from python/cpython#28271.
1 parent eeba06a commit 13dfeb2

File tree

3 files changed

+76
-0
lines changed

3 files changed

+76
-0
lines changed

Applications/SlicerApp/Testing/Python/CMakeLists.txt

+6
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,12 @@ slicer_add_python_unittest(
272272
TESTNAME_PREFIX nomainwindow_
273273
)
274274

275+
slicer_add_python_unittest(
276+
SCRIPT ${Slicer_SOURCE_DIR}/Base/Python/slicer/tests/test_slicer_util_chdir.py
277+
SLICER_ARGS --no-main-window --disable-modules
278+
TESTNAME_PREFIX nomainwindow_
279+
)
280+
275281
slicer_add_python_unittest(
276282
SCRIPT ${Slicer_SOURCE_DIR}/Base/Python/slicer/tests/test_slicer_util_save.py
277283
SLICER_ARGS --no-main-window --disable-cli-modules --disable-scripted-loadable-modules DATA{${INPUT}/MR-head.nrrd}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import os
2+
import unittest
3+
import slicer
4+
5+
6+
class SlicerUtilChdirTests(unittest.TestCase):
7+
"""Available in Python 3.11 as ``TestChdir`` found in ``Lib/test/test_contextlib.py``
8+
and adapted from https://github.com/python/cpython/pull/28271
9+
"""
10+
def test_simple(self):
11+
old_cwd = os.getcwd()
12+
target = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../slicer"))
13+
self.assertNotEqual(old_cwd, target)
14+
15+
with slicer.util.chdir(target):
16+
self.assertEqual(os.getcwd(), target)
17+
self.assertEqual(os.getcwd(), old_cwd)
18+
19+
def test_reentrant(self):
20+
old_cwd = os.getcwd()
21+
target1 = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../slicer"))
22+
target2 = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../tests"))
23+
self.assertNotIn(old_cwd, (target1, target2))
24+
chdir1, chdir2 = slicer.util.chdir(target1), slicer.util.chdir(target2)
25+
26+
with chdir1:
27+
self.assertEqual(os.getcwd(), target1)
28+
with chdir2:
29+
self.assertEqual(os.getcwd(), target2)
30+
with chdir1:
31+
self.assertEqual(os.getcwd(), target1)
32+
self.assertEqual(os.getcwd(), target2)
33+
self.assertEqual(os.getcwd(), target1)
34+
self.assertEqual(os.getcwd(), old_cwd)
35+
36+
def test_exception(self):
37+
old_cwd = os.getcwd()
38+
target = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../slicer"))
39+
self.assertNotEqual(old_cwd, target)
40+
41+
try:
42+
with slicer.util.chdir(target):
43+
self.assertEqual(os.getcwd(), target)
44+
raise RuntimeError("boom")
45+
except RuntimeError as re:
46+
self.assertEqual(str(re), "boom")
47+
self.assertEqual(os.getcwd(), old_cwd)

Base/Python/slicer/util.py

+23
Original file line numberDiff line numberDiff line change
@@ -3095,6 +3095,29 @@ def getFilesInDirectory(directory, absolutePath=True):
30953095
return allFiles
30963096

30973097

3098+
class chdir:
3099+
"""Non thread-safe context manager to change the current working directory.
3100+
3101+
.. note::
3102+
3103+
Available in Python 3.11 as ``contextlib.chdir`` and adapted from https://github.com/python/cpython/pull/28271
3104+
3105+
Available in CTK as ``ctkScopedCurrentDir`` C++ class
3106+
"""
3107+
def __init__(self, path):
3108+
self.path = path
3109+
self._old_cwd = []
3110+
3111+
def __enter__(self):
3112+
import os
3113+
self._old_cwd.append(os.getcwd())
3114+
os.chdir(self.path)
3115+
3116+
def __exit__(self, *excinfo):
3117+
import os
3118+
os.chdir(self._old_cwd.pop())
3119+
3120+
30983121
def plot(narray, xColumnIndex = -1, columnNames = None, title = None, show = True, nodes = None):
30993122
"""Create a plot from a numpy array that contains two or more columns.
31003123

0 commit comments

Comments
 (0)