Author: Philip Jenvey <pjen...@underboss.org>
Branch: py3k
Changeset: r73587:1900e5ea6ec4
Date: 2014-09-17 12:53 -0700
http://bitbucket.org/pypy/pypy/changeset/1900e5ea6ec4/

Log:    issue1798: add nt._getfinalpathname. add _getfileinformation as well

diff --git a/pypy/module/posix/__init__.py b/pypy/module/posix/__init__.py
--- a/pypy/module/posix/__init__.py
+++ b/pypy/module/posix/__init__.py
@@ -164,6 +164,11 @@
     # not visible via os, inconsistency in nt:
     if hasattr(posix, '_getfullpathname'):
         interpleveldefs['_getfullpathname'] = 'interp_posix._getfullpathname'
+    if os.name == 'nt':
+        interpleveldefs.update({
+                '_getfileinformation': 'interp_posix._getfileinformation',
+                '_getfinalpathname': 'interp_posix._getfinalpathname',
+        })
     if hasattr(os, 'chroot'):
         interpleveldefs['chroot'] = 'interp_posix.chroot'
 
diff --git a/pypy/module/posix/interp_posix.py 
b/pypy/module/posix/interp_posix.py
--- a/pypy/module/posix/interp_posix.py
+++ b/pypy/module/posix/interp_posix.py
@@ -5,8 +5,7 @@
 from rpython.rlib.objectmodel import specialize
 from rpython.rlib.rarithmetic import r_longlong
 from rpython.rlib.unroll import unrolling_iterable
-from rpython.rtyper.module import ll_os_stat
-from rpython.rtyper.module.ll_os import RegisterOs
+from rpython.rtyper.module import ll_os, ll_os_stat
 
 from pypy.interpreter.gateway import unwrap_spec, WrappedDefault
 from pypy.interpreter.error import OperationError, wrap_oserror, wrap_oserror2
@@ -1205,7 +1204,7 @@
         raise wrap_oserror(space, e)
 
 def declare_new_w_star(name):
-    if name in RegisterOs.w_star_returning_int:
+    if name in ll_os.RegisterOs.w_star_returning_int:
         @unwrap_spec(status=c_int)
         def WSTAR(space, status):
             return space.wrap(getattr(os, name)(status))
@@ -1217,7 +1216,7 @@
     WSTAR.func_name = name
     return WSTAR
 
-for name in RegisterOs.w_star:
+for name in ll_os.RegisterOs.w_star:
     if hasattr(os, name):
         func = declare_new_w_star(name)
         globals()[name] = func
@@ -1383,3 +1382,25 @@
         if codeset:
             return space.wrap(codeset)
     return space.w_None
+
+if _WIN32:
+    @unwrap_spec(fd=c_int)
+    def _getfileinformation(space, fd):
+        try:
+            info = ll_os._getfileinformation(fd)
+        except OSError as e:
+            raise wrap_oserror(space, e)
+        return space.newtuple([space.wrap(info[0]),
+                               space.wrap(info[1]),
+                               space.wrap(info[2])])
+
+    def _getfinalpathname(space, w_path):
+        path = space.unicode_w(w_path)
+        try:
+            result = ll_os._getfinalpathname(path)
+        except ll_os.LLNotImplemented as e:
+            raise OperationError(space.w_NotImplementedError,
+                                 space.wrap(e.msg))
+        except OSError as e:
+            raise wrap_oserror2(space, e, w_path)
+        return space.wrap(result)
diff --git a/pypy/module/posix/test/test_posix2.py 
b/pypy/module/posix/test/test_posix2.py
--- a/pypy/module/posix/test/test_posix2.py
+++ b/pypy/module/posix/test/test_posix2.py
@@ -1040,6 +1040,21 @@
         # just ensure it returns something reasonable
         assert encoding is None or type(encoding) is str
 
+    if os.name == 'nt':
+        def test__getfileinformation(self):
+            import os
+            path = os.path.join(self.pdir, 'file1')
+            with open(path) as fp:
+                info = self.posix._getfileinformation(fp.fileno())
+            assert len(info) == 3
+            assert all(isinstance(obj, int) for obj in info)
+
+        def test__getfinalpathname(self):
+            import os
+            path = os.path.join(self.pdir, 'file1')
+            result = self.posix._getfinalpathname(path)
+            assert os.path.exists(result)
+
 
 class AppTestEnvironment(object):
     def setup_class(cls):
diff --git a/rpython/rlib/rdynload.py b/rpython/rlib/rdynload.py
--- a/rpython/rlib/rdynload.py
+++ b/rpython/rlib/rdynload.py
@@ -158,3 +158,4 @@
         return res
 
     LoadLibrary = rwin32.LoadLibrary
+    GetModuleHandle = rwin32.GetModuleHandle
diff --git a/rpython/rlib/rwin32.py b/rpython/rlib/rwin32.py
--- a/rpython/rlib/rwin32.py
+++ b/rpython/rlib/rwin32.py
@@ -130,6 +130,7 @@
     # is hidden by operations in ll2ctypes.  Call it now.
     GetLastError()
 
+    GetModuleHandle = winexternal('GetModuleHandleA', [rffi.CCHARP], HMODULE)
     LoadLibrary = winexternal('LoadLibraryA', [rffi.CCHARP], HMODULE)
     GetProcAddress = winexternal('GetProcAddress',
                                  [HMODULE, rffi.CCHARP],
diff --git a/rpython/rtyper/lltypesystem/ll2ctypes.py 
b/rpython/rtyper/lltypesystem/ll2ctypes.py
--- a/rpython/rtyper/lltypesystem/ll2ctypes.py
+++ b/rpython/rtyper/lltypesystem/ll2ctypes.py
@@ -361,7 +361,9 @@
             functype = ctypes.CFUNCTYPE
             if sys.platform == 'win32':
                 from rpython.rlib.clibffi import FFI_STDCALL, FFI_DEFAULT_ABI
-                if getattr(T.TO, 'ABI', FFI_DEFAULT_ABI) == FFI_STDCALL:
+                # XXX:
+                #if getattr(T.TO, 'ABI', FFI_DEFAULT_ABI) == FFI_STDCALL:
+                if getattr(T.TO, 'ABI', FFI_DEFAULT_ABI) == 'FFI_STDCALL':
                     # for win32 system call
                     functype = ctypes.WINFUNCTYPE
             argtypes = [get_ctypes_type(ARG) for ARG in T.TO.ARGS
diff --git a/rpython/rtyper/module/ll_os.py b/rpython/rtyper/module/ll_os.py
--- a/rpython/rtyper/module/ll_os.py
+++ b/rpython/rtyper/module/ll_os.py
@@ -105,6 +105,12 @@
 
 _CYGWIN = sys.platform == 'cygwin'
 
+# plain NotImplementedError is invalid RPython
+class LLNotImplemented(NotImplementedError):
+
+    def __init__(self, msg):
+        self.msg = msg
+
 class CConfig:
     """
     Definitions for platform integration.
@@ -1179,7 +1185,7 @@
                              condition=sys.platform=='win32')
     def register_posix__getfullpathname(self, traits):
         # this nt function is not exposed via os, but needed
-        # to get a correct implementation of os.abspath
+        # to get a correct implementation of os.path.abspath
         from rpython.rtyper.module.ll_win32file import 
make_getfullpathname_impl
         getfullpathname_llimpl = make_getfullpathname_impl(traits)
 
@@ -1963,10 +1969,12 @@
         return OsEnvironController()
 
 # ____________________________________________________________
-# Support for the WindowsError exception
+# Support for the WindowsError exception and misc functions
 
 if sys.platform == 'win32':
     from rpython.rlib import rwin32
+    from rpython.rtyper.module.ll_win32file import (
+        make__getfileinformation_impl, make__getfinalpathname_impl)
 
     class RegisterFormatError(BaseLazyRegistering):
         def __init__(self):
@@ -1977,3 +1985,6 @@
             return extdef([lltype.Signed], str,
                           "rwin32_FormatError",
                           llimpl=rwin32.llimpl_FormatError)
+
+    _getfileinformation = make__getfileinformation_impl(UnicodeTraits())
+    _getfinalpathname = make__getfinalpathname_impl(UnicodeTraits())
diff --git a/rpython/rtyper/module/ll_win32file.py 
b/rpython/rtyper/module/ll_win32file.py
--- a/rpython/rtyper/module/ll_win32file.py
+++ b/rpython/rtyper/module/ll_win32file.py
@@ -55,6 +55,15 @@
         FILE_TYPE_CHAR = platform.ConstantInteger('FILE_TYPE_CHAR')
         FILE_TYPE_PIPE = platform.ConstantInteger('FILE_TYPE_PIPE')
 
+        FILE_WRITE_ATTRIBUTES = platform.ConstantInteger(
+            'FILE_WRITE_ATTRIBUTES')
+        OPEN_EXISTING = platform.ConstantInteger(
+            'OPEN_EXISTING')
+        FILE_FLAG_BACKUP_SEMANTICS = platform.ConstantInteger(
+            'FILE_FLAG_BACKUP_SEMANTICS')
+        VOLUME_NAME_DOS = platform.ConstantInteger('VOLUME_NAME_DOS')
+        VOLUME_NAME_NT = platform.ConstantInteger('VOLUME_NAME_NT')
+
         WIN32_FILE_ATTRIBUTE_DATA = platform.Struct(
             'WIN32_FILE_ATTRIBUTE_DATA',
             [('dwFileAttributes', rwin32.DWORD),
@@ -67,14 +76,15 @@
         BY_HANDLE_FILE_INFORMATION = platform.Struct(
             'BY_HANDLE_FILE_INFORMATION',
             [('dwFileAttributes', rwin32.DWORD),
+             ('ftCreationTime', rwin32.FILETIME),
+             ('ftLastAccessTime', rwin32.FILETIME),
+             ('ftLastWriteTime', rwin32.FILETIME),
+             ('dwVolumeSerialNumber', rwin32.DWORD),
              ('nFileSizeHigh', rwin32.DWORD),
              ('nFileSizeLow', rwin32.DWORD),
              ('nNumberOfLinks', rwin32.DWORD),
              ('nFileIndexHigh', rwin32.DWORD),
-             ('nFileIndexLow', rwin32.DWORD),
-             ('ftCreationTime', rwin32.FILETIME),
-             ('ftLastAccessTime', rwin32.FILETIME),
-             ('ftLastWriteTime', rwin32.FILETIME)])
+             ('nFileIndexLow', rwin32.DWORD)])
 
     config = platform.configure(CConfig)
 
@@ -92,6 +102,8 @@
                        INVALID_FILE_ATTRIBUTES
                        _S_IFDIR _S_IFREG _S_IFCHR _S_IFIFO
                        FILE_TYPE_UNKNOWN FILE_TYPE_CHAR FILE_TYPE_PIPE
+                       FILE_WRITE_ATTRIBUTES OPEN_EXISTING 
FILE_FLAG_BACKUP_SEMANTICS
+                       VOLUME_NAME_DOS VOLUME_NAME_NT
                        ERROR_FILE_NOT_FOUND ERROR_NO_MORE_FILES
                        ERROR_SHARING_VIOLATION
                     '''.split():
@@ -163,6 +175,13 @@
             [traits.CCHARP, traits.CCHARP],
             rwin32.BOOL)
 
+        CreateFile = external(
+            'CreateFile' + apisuffix,
+            [traits.CCHARP, rwin32.DWORD, rwin32.DWORD,
+             rwin32.LPSECURITY_ATTRIBUTES, rwin32.DWORD, rwin32.DWORD,
+             rwin32.HANDLE],
+            rwin32.HANDLE)
+
         DeleteFile = external(
             'DeleteFile' + suffix,
             [traits.CCHARP],
@@ -173,6 +192,27 @@
             [traits.CCHARP, traits.CCHARP],
             rwin32.BOOL)
 
+        # dynamically loaded
+        GetFinalPathNameByHandle = None
+
+        @staticmethod
+        def check_GetFinalPathNameByHandle():
+            if Win32Traits.GetFinalPathNameByHandle:
+                return True
+
+            from rpython.rlib.rdynload import GetModuleHandle, dlsym
+            hKernel32 = GetModuleHandle("KERNEL32")
+            try:
+                func = dlsym(hKernel32, 'GetFinalPathNameByHandle' + suffix)
+            except KeyError:
+                return False
+
+            TYPE = lltype.Ptr(lltype.FuncType(
+                    [rwin32.HANDLE, traits.CCHARP, rwin32.DWORD, rwin32.DWORD],
+                    rwin32.DWORD, abi='FFI_STDCALL'))
+            Win32Traits.GetFinalPathNameByHandle = rffi.cast(TYPE, func)
+            return True
+
     return Win32Traits
 
 #_______________________________________________________________
@@ -336,27 +376,6 @@
     win32traits = make_win32_traits(traits)
     from rpython.rtyper.module.ll_os_stat import time_t_to_FILE_TIME
 
-    class CConfig:
-        _compilation_info_ = ExternalCompilationInfo(
-            includes = ['windows.h'],
-            )
-
-        FILE_WRITE_ATTRIBUTES = platform.ConstantInteger(
-            'FILE_WRITE_ATTRIBUTES')
-        OPEN_EXISTING = platform.ConstantInteger(
-            'OPEN_EXISTING')
-        FILE_FLAG_BACKUP_SEMANTICS = platform.ConstantInteger(
-            'FILE_FLAG_BACKUP_SEMANTICS')
-    globals().update(platform.configure(CConfig))
-
-    CreateFile = rffi.llexternal(
-        'CreateFile' + win32traits.apisuffix,
-        [traits.CCHARP, rwin32.DWORD, rwin32.DWORD,
-         rwin32.LPSECURITY_ATTRIBUTES, rwin32.DWORD, rwin32.DWORD,
-         rwin32.HANDLE],
-        rwin32.HANDLE,
-        calling_conv='win')
-
     GetSystemTime = rffi.llexternal(
         'GetSystemTime',
         [lltype.Ptr(rwin32.SYSTEMTIME)],
@@ -381,10 +400,10 @@
 
     @specialize.argtype(1)
     def os_utime_llimpl(path, tp):
-        hFile = CreateFile(path,
-                           FILE_WRITE_ATTRIBUTES, 0,
-                           None, OPEN_EXISTING,
-                           FILE_FLAG_BACKUP_SEMANTICS,
+        hFile = win32traits.CreateFile(path,
+                           win32traits.FILE_WRITE_ATTRIBUTES, 0,
+                           None, win32traits.OPEN_EXISTING,
+                           win32traits.FILE_FLAG_BACKUP_SEMANTICS,
                            rwin32.NULL_HANDLE)
         if hFile == rwin32.INVALID_HANDLE_VALUE:
             raise rwin32.lastWindowsError()
@@ -413,3 +432,68 @@
             lltype.free(mtime, flavor='raw')
 
     return os_utime_llimpl
+
+#_______________________________________________________________
+# _getfileinformation (py3)
+
+def make__getfileinformation_impl(traits):
+    from rpython.rlib import rwin32
+    win32traits = make_win32_traits(traits)
+
+    def _getfileinformation_llimpl(fd):
+        hFile = rwin32.get_osfhandle(fd)
+        with lltype.scoped_alloc(
+            win32traits.BY_HANDLE_FILE_INFORMATION) as info:
+            if win32traits.GetFileInformationByHandle(hFile, info) == 0:
+                raise lastWindowsError("_getfileinformation")
+            return (rffi.cast(lltype.Signed, info.c_dwVolumeSerialNumber),
+                    rffi.cast(lltype.Signed, info.c_nFileIndexHigh),
+                    rffi.cast(lltype.Signed, info.c_nFileIndexLow))
+
+    return _getfileinformation_llimpl
+
+#_______________________________________________________________
+# _getfinalpathname (py3)
+
+def make__getfinalpathname_impl(traits):
+    from rpython.rlib import rwin32
+    from rpython.rtyper.module.ll_os import LLNotImplemented
+    assert traits.str is unicode, 'Currently only handles unicode paths'
+    win32traits = make_win32_traits(traits)
+
+    def _getfinalpathname_llimpl(path):
+        if not win32traits.check_GetFinalPathNameByHandle():
+            raise LLNotImplemented("GetFinalPathNameByHandle not available on "
+                                   "this platform")
+
+        hFile = win32traits.CreateFile(path, 0, 0, None,
+                                       win32traits.OPEN_EXISTING,
+                                       win32traits.FILE_FLAG_BACKUP_SEMANTICS,
+                                       rwin32.NULL_HANDLE)
+        if hFile == rwin32.INVALID_HANDLE_VALUE:
+            raise rwin32.lastWindowsError("CreateFile")
+
+        VOLUME_NAME_DOS = rffi.cast(rwin32.DWORD, win32traits.VOLUME_NAME_DOS)
+        try:
+            size = win32traits.GetFinalPathNameByHandle(
+                hFile,
+                lltype.nullptr(traits.CCHARP.TO),
+                rffi.cast(rwin32.DWORD, 0),
+                VOLUME_NAME_DOS)
+            if size == 0:
+                raise rwin32.lastWindowsError("GetFinalPathNameByHandle")
+
+            with lltype.scoped_alloc(traits.CCHARP.TO, size + 1) as 
target_path:
+                result = win32traits.GetFinalPathNameByHandle(
+                    hFile,
+                    target_path,
+                    size,
+                    VOLUME_NAME_DOS)
+                if result == 0:
+                    raise rwin32.lastWindowsError("GetFinalPathNameByHandle")
+                return traits.charpsize2str(target_path,
+                                            rffi.cast(lltype.Signed, result))
+        finally:
+            rwin32.CloseHandle(hFile)
+
+    return _getfinalpathname_llimpl
diff --git a/rpython/rtyper/module/support.py b/rpython/rtyper/module/support.py
--- a/rpython/rtyper/module/support.py
+++ b/rpython/rtyper/module/support.py
@@ -49,6 +49,7 @@
     CHAR = rffi.CHAR
     CCHARP = rffi.CCHARP
     charp2str = staticmethod(rffi.charp2str)
+    charpsize2str = staticmethod(rffi.charpsize2str)
     scoped_str2charp = staticmethod(rffi.scoped_str2charp)
     str2charp = staticmethod(rffi.str2charp)
     free_charp = staticmethod(rffi.free_charp)
@@ -68,6 +69,7 @@
     CHAR = rffi.WCHAR_T
     CCHARP = rffi.CWCHARP
     charp2str = staticmethod(rffi.wcharp2unicode)
+    charpsize2str = staticmethod(rffi.wcharpsize2unicode)
     str2charp = staticmethod(rffi.unicode2wcharp)
     scoped_str2charp = staticmethod(rffi.scoped_unicode2wcharp)
     free_charp = staticmethod(rffi.free_wcharp)
diff --git a/rpython/rtyper/module/test/test_ll_win32file.py 
b/rpython/rtyper/module/test/test_ll_win32file.py
new file mode 100644
--- /dev/null
+++ b/rpython/rtyper/module/test/test_ll_win32file.py
@@ -0,0 +1,26 @@
+import os
+
+import py
+
+from rpython.rtyper.module import ll_os
+
+if not ll_os._WIN32:
+    py.test.skip("requires Windows")
+
+
+def test__getfinalpathname():
+    path = __file__.decode('mbcs')
+    try:
+        result = ll_os._getfinalpathname(path)
+    except ll_os.LLNotImplemented:
+        py.test.skip("_getfinalpathname not supported on this platform")
+    assert os.path.exists(result)
+
+
+def test__getfileinformation():
+    with open(__file__) as fp:
+        stat = os.fstat(fp.fileno())
+        info = ll_os._getfileinformation(fp.fileno())
+    serial, high, low = info
+    assert type(serial) in (int, long)
+    assert (high << 32) + low == stat.st_ino
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to