[Python-ideas] chdir context manager (original) (raw)

Laurens Van Houtven _ at lvh.cc
Sat Jan 19 11:19:41 CET 2013


+1

On Sat, Jan 19, 2013 at 11:10 AM, Daniel Shahaf <d.s at daniel.shahaf.name>wrote:

The following is a common pattern (used by, for example, shutil.makearchive):

savecwd = os.getcwd() try: foo() finally: os.chdir(savecwd) I suggest this deserves a context manager: with savedcwd(): 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.savedcwd, based on os.fchdir. The patch also adds os.chdir to os.supportsdirfd 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_ _<pathfd>. The descriptor must refer to an opened directory, not an open file. + See also :func:shutil.savedcwd 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:: savedcwd() + + 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("havefunctions"): set = set() add("HAVEFACCESSAT", "access") + add("HAVEFCHDIR", "chdir") add("HAVEFCHMODAT", "chmod") add("HAVEFCHOWNAT", "chown") add("HAVEFSTATAT", "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 "unregisterunpackformat", "unpackarchive", "ignorepatterns", "chown", "which"] # diskusage is added later, if available on the platform + # savedcwd is added later, if available on the platform class Error(OSError): pass @@ -1111,3 +1112,20 @@ def which(cmd, mode=os.FOK | os.XOK, p if accesscheck(name, mode): return name return None + +# Define the chdir context manager. +if os.chdir in os.supportsdirfd: + class savedcwd: + def init(self): + pass + def enter(self): + self.dh = os.open(os.curdir, + os.ORDONLY | getattr(os, 'ODIRECTORY', 0)) + return self.dh + def exit(self, exctype, excvalue, traceback): + try: + os.chdir(self.dh) + finally: + os.close(self.dh) + return False + all.append('savedcwd') diff -r 74b0461346f0 Lib/test/testshutil.py --- a/Lib/test/testshutil.py Fri Jan 18 17:53:18 2013 -0800 +++ b/Lib/test/testshutil.py Sat Jan 19 09:39:27 2013 +0000 @@ -1276,6 +1276,20 @@ class TestShutil(unittest.TestCase): rv = shutil.copytree(srcdir, dstdir) self.assertEqual(['foo'], os.listdir(rv)) + def testsavedcwd(self): + if hasattr(os, 'fchdir'): + tempdir = self.mkdtemp() + origdir = os.getcwd() + with shutil.savedcwd() as dirfd: + os.chdir(tempdir) + newdir = os.getcwd() + self.assertIsInstance(dirfd, int) + finaldir = os.getcwd() + self.assertEqual(origdir, finaldir) + self.assertEqual(tempdir, newdir) + else: + self.assertFalse(hasattr(shutil, 'savedcwd')) + class TestWhich(unittest.TestCase):


Python-ideas mailing list Python-ideas at python.org http://mail.python.org/mailman/listinfo/python-ideas

-- cheers lvh -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.python.org/pipermail/python-ideas/attachments/20130119/fc28ac41/attachment.html>



More information about the Python-ideas mailing list