[pypy-svn] pypy default: On Windows, os.chdir() is supposed to raise WindowsErrors.

amauryfa commits-noreply at bitbucket.org
Thu Jan 20 19:36:49 CET 2011


Author: Amaury Forgeot d'Arc <amauryfa at gmail.com>
Branch: 
Changeset: r41069:08e757460def
Date: 2011-01-20 16:34 +0100
http://bitbucket.org/pypy/pypy/changeset/08e757460def/

Log:	On Windows, os.chdir() is supposed to raise WindowsErrors. Use a
	win32 implementation instead.

diff --git a/pypy/rpython/module/ll_os.py b/pypy/rpython/module/ll_os.py
--- a/pypy/rpython/module/ll_os.py
+++ b/pypy/rpython/module/ll_os.py
@@ -1277,12 +1277,17 @@
         os_chdir = self.llexternal(traits.posix_function_name('chdir'),
                                    [traits.CCHARP], rffi.INT)
 
-        def chdir_llimpl(path):
+        def os_chdir_llimpl(path):
             res = rffi.cast(lltype.Signed, os_chdir(path))
             if res < 0:
                 raise OSError(rposix.get_errno(), "os_chdir failed")
 
-        return extdef([traits.str], s_None, llimpl=chdir_llimpl,
+        # On Windows, use an implementation that will produce Win32 errors
+        if sys.platform == 'win32':
+            from pypy.rpython.module.ll_win32file import make_chdir_impl
+            os_chdir_llimpl = make_chdir_impl(traits)
+
+        return extdef([traits.str], s_None, llimpl=os_chdir_llimpl,
                       export_name=traits.ll_os_name('chdir'))
 
     @registering_str_unicode(os.mkdir)

diff --git a/pypy/rpython/module/ll_win32file.py b/pypy/rpython/module/ll_win32file.py
--- a/pypy/rpython/module/ll_win32file.py
+++ b/pypy/rpython/module/ll_win32file.py
@@ -129,6 +129,21 @@
              traits.CCHARP, LPSTRP],
             rwin32.DWORD)
 
+        GetCurrentDirectory = external(
+            'GetCurrentDirectory' + suffix,
+            [rwin32.DWORD, traits.CCHARP],
+            rwin32.DWORD)
+
+        SetCurrentDirectory = external(
+            'SetCurrentDirectory' + suffix,
+            [traits.CCHARP],
+            rwin32.BOOL)
+
+        SetEnvironmentVariable = external(
+            'SetEnvironmentVariable' + suffix,
+            [traits.CCHARP, traits.CCHARP],
+            rwin32.BOOL)
+
     return Win32Traits
 
 #_______________________________________________________________
@@ -189,6 +204,54 @@
     return listdir_llimpl
 
 #_______________________________________________________________
+# chdir
+
+def make_chdir_impl(traits):
+    from pypy.rlib import rwin32
+    win32traits = make_win32_traits(traits)
+
+    if traits.str is unicode:
+        def isUNC(path):
+            return path[0] == u'\\' or path[0] == u'/'
+        def magic_envvar(path):
+            return u'=' + path[0] + u':'
+    else:
+        def isUNC(path):
+            return path[0] == '\\' or path[0] == '/'
+        def magic_envvar(path):
+            return '=' + path[0] + ':'
+
+    @func_renamer('chdir_llimpl_%s' % traits.str.__name__)
+    def chdir_llimpl(path):
+        """This is a reimplementation of the C library's chdir function,
+        but one that produces Win32 errors instead of DOS error codes.
+        chdir is essentially a wrapper around SetCurrentDirectory; however,
+        it also needs to set "magic" environment variables indicating
+        the per-drive current directory, which are of the form =<drive>:
+        """
+        if not win32traits.SetCurrentDirectory(path):
+            raise rwin32.lastWindowsError()
+
+        with rffi.scoped_alloc_buffer(rwin32.MAX_PATH) as path:
+            res = win32traits.GetCurrentDirectory(rwin32.MAX_PATH + 1, path.raw)
+            if not res:
+                raise rwin32.lastWindowsError()
+            if res <= rwin32.MAX_PATH + 1:
+                new_path = path.str(rffi.cast(lltype.Signed, res))
+            else:
+                with rffi.scoped_alloc_buffer(rwin32.MAX_PATH) as path:
+                    res = win32traits.GetCurrentDirectory(res, path.raw)
+                    if not res:
+                        raise rwin32.lastWindowsError()
+                    new_path = path.str(rffi.cast(lltype.Signed, res))
+        if isUNC(new_path):
+            return
+        if not win32traits.SetEnvironmentVariable(magic_envvar(new_path), new_path):
+            raise rwin32.lastWindowsError()
+
+    return chdir_llimpl
+
+#_______________________________________________________________
 # getfullpathname
 
 def make_getfullpathname_impl(traits):

diff --git a/pypy/rpython/module/test/test_ll_os.py b/pypy/rpython/module/test/test_ll_os.py
--- a/pypy/rpython/module/test/test_ll_os.py
+++ b/pypy/rpython/module/test/test_ll_os.py
@@ -63,6 +63,25 @@
     data = getllimpl(os.getcwd)()
     assert data == os.getcwd()
 
+def test_chdir():
+    def check_special_envvar():
+        if sys.platform != 'win32':
+            return
+        pwd = os.getcwd()
+        import ctypes
+        buf = ctypes.create_string_buffer(1000)
+        ctypes.windll.kernel32.GetEnvironmentVariableA('=%c:' % pwd[0], buf, 1000)
+        assert str(buf.value) == pwd
+
+    pwd = os.getcwd()
+    try:
+        check_special_envvar()
+        getllimpl(os.chdir)('..')
+        assert os.getcwd() == os.path.dirname(pwd)
+        check_special_envvar()
+    finally:
+        os.chdir(pwd)
+
 def test_strerror():
     data = getllimpl(os.strerror)(2)
     assert data == os.strerror(2)


More information about the Pypy-commit mailing list