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