(original) (raw)
The following is a common pattern (used by, for example,
shutil.make\_archive):
� � save\_cwd = os.getcwd()
� � try:
� � � � foo()
� � finally:
� � � � os.chdir(save\_cwd)
I suggest this deserves a context manager:
� � with saved\_cwd():
� � � � foo()
Initial feedback on IRC suggests shutil as where this functionality
should live (other suggestions were made, such as pathlib). �Hence,
attached patch implements this as shutil.saved\_cwd, based on os.fchdir.
The patch also adds os.chdir to os.supports\_dir\_fd and documents the
context manager abilities of builtins.open() in its reference.
Thoughts?
Thanks,
Daniel
diff -r 74b0461346f0 Doc/library/functions.rst
\--- a/Doc/library/functions.rst Fri Jan 18 17:53:18 2013 -0800
+++ b/Doc/library/functions.rst Sat Jan 19 09:39:27 2013 +0000
@@ -828,6 +828,9 @@ are always available. �They are listed h
� � Open \*file\* and return a corresponding :term:\`file object\`. �If the file
� � cannot be opened, an :exc:\`OSError\` is raised.
\+ � This function can be used as a :term:\`context manager\` that closes the
\+ � file when it exits.
+
� � \*file\* is either a string or bytes object giving the pathname (absolute or
� � relative to the current working directory) of the file to be opened or
� � an integer file descriptor of the file to be wrapped. �(If a file descriptor
diff -r 74b0461346f0 Doc/library/os.rst
\--- a/Doc/library/os.rst � � � �Fri Jan 18 17:53:18 2013 -0800
+++ b/Doc/library/os.rst � � � �Sat Jan 19 09:39:27 2013 +0000
@@ -1315,6 +1315,9 @@ features:
� � This function can support :ref:\`specifying a file descriptor \`. �The
� � descriptor must refer to an opened directory, not an open file.
\+ � See also :func:\`shutil.saved\_cwd\` for a context manager that restores the
\+ � current working directory.
+
� � Availability: Unix, Windows.
� � .. versionadded:: 3.3
diff -r 74b0461346f0 Doc/library/shutil.rst
\--- a/Doc/library/shutil.rst � �Fri Jan 18 17:53:18 2013 -0800
+++ b/Doc/library/shutil.rst � �Sat Jan 19 09:39:27 2013 +0000
@@ -36,6 +36,19 @@ copying and removal. For operations on i
�Directory and files operations
�------------------------------
+.. function:: saved\_cwd()
+
\+ � Return a :term:\`context manager\` that restores the current working directory
\+ � when it exits. �See :func:\`os.chdir\` for changing the current working
\+ � directory.
+
\+ � The context manager returns an open file descriptor for the saved directory.
+
\+ � Only available when :func:\`os.chdir\` supports file descriptor arguments.
+
\+ � .. versionadded:: 3.4
+
+
�.. function:: copyfileobj(fsrc, fdst\[, length\])
� � Copy the contents of the file-like object \*fsrc\* to the file-like object \*fdst\*.
diff -r 74b0461346f0 Lib/os.py
\--- a/Lib/os.py Fri Jan 18 17:53:18 2013 -0800
+++ b/Lib/os.py Sat Jan 19 09:39:27 2013 +0000
@@ -120,6 +120,7 @@ if \_exists("\_have\_functions"):
� � �\_set = set()
� � �\_add("HAVE\_FACCESSAT", �"access")
\+ � �\_add("HAVE\_FCHDIR", � � "chdir")
� � �\_add("HAVE\_FCHMODAT", � "chmod")
� � �\_add("HAVE\_FCHOWNAT", � "chown")
� � �\_add("HAVE\_FSTATAT", � �"stat")
diff -r 74b0461346f0 Lib/shutil.py
\--- a/Lib/shutil.py � � Fri Jan 18 17:53:18 2013 -0800
+++ b/Lib/shutil.py � � Sat Jan 19 09:39:27 2013 +0000
@@ -38,6 +38,7 @@ \_\_all\_\_ = \["copyfileobj", "copyfile", "c
� � � � � � "unregister\_unpack\_format", "unpack\_archive",
� � � � � � "ignore\_patterns", "chown", "which"\]
� � � � � � # disk\_usage is added later, if available on the platform
\+ � � � � � # saved\_cwd is added later, if available on the platform
�class Error(OSError):
� � �pass
@@ -1111,3 +1112,20 @@ def which(cmd, mode=os.F\_OK | os.X\_OK, p
� � � � � � � � �if \_access\_check(name, mode):
� � � � � � � � � � �return name
� � �return None
+
+# Define the chdir context manager.
+if os.chdir in os.supports\_dir\_fd:
\+ � �class saved\_cwd:
\+ � � � �def \_\_init\_\_(self):
\+ � � � � � �pass
\+ � � � �def \_\_enter\_\_(self):
\+ � � � � � �self.dh = os.open(os.curdir,
\+ � � � � � � � � � � � � � � �os.O\_RDONLY | getattr(os, 'O\_DIRECTORY', 0))
\+ � � � � � �return self.dh
\+ � � � �def \_\_exit\_\_(self, exc\_type, exc\_value, traceback):
\+ � � � � � �try:
\+ � � � � � � � �os.chdir(self.dh)
\+ � � � � � �finally:
\+ � � � � � � � �os.close(self.dh)
\+ � � � � � �return False
\+ � �\_\_all\_\_.append('saved\_cwd')
diff -r 74b0461346f0 Lib/test/test\_shutil.py
\--- a/Lib/test/test\_shutil.py � Fri Jan 18 17:53:18 2013 -0800
+++ b/Lib/test/test\_shutil.py � Sat Jan 19 09:39:27 2013 +0000
@@ -1276,6 +1276,20 @@ class TestShutil(unittest.TestCase):
� � � � �rv = shutil.copytree(src\_dir, dst\_dir)
� � � � �self.assertEqual(\['foo'\], os.listdir(rv))
\+ � �def test\_saved\_cwd(self):
\+ � � � �if hasattr(os, 'fchdir'):
\+ � � � � � �temp\_dir = self.mkdtemp()
\+ � � � � � �orig\_dir = os.getcwd()
\+ � � � � � �with shutil.saved\_cwd() as dir\_fd:
\+ � � � � � � � �os.chdir(temp\_dir)
\+ � � � � � � � �new\_dir = os.getcwd()
\+ � � � � � � � �self.assertIsInstance(dir\_fd, int)
\+ � � � � � �final\_dir = os.getcwd()
\+ � � � � � �self.assertEqual(orig\_dir, final\_dir)
\+ � � � � � �self.assertEqual(temp\_dir, new\_dir)
\+ � � � �else:
\+ � � � � � �self.assertFalse(hasattr(shutil, 'saved\_cwd'))
+
�class TestWhich(unittest.TestCase):
\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
Python-ideas mailing list
Python-ideas@python.org
http://mail.python.org/mailman/listinfo/python-ideas
--