osutil.py
170 lines
| 5.2 KiB
| text/x-python
|
PythonLexer
|
r8232 | # osutil.py - pure Python version of osutil.c | ||
# | ||||
# Copyright 2009 Matt Mackall <mpm@selenic.com> and others | ||||
# | ||||
# This software may be used and distributed according to the terms of the | ||||
|
r10263 | # GNU General Public License version 2 or any later version. | ||
|
r8232 | |||
|
r7704 | import os | ||
|
r10651 | import stat as statmod | ||
|
r7704 | |||
def _mode_to_kind(mode): | ||||
|
r10651 | if statmod.S_ISREG(mode): | ||
return statmod.S_IFREG | ||||
if statmod.S_ISDIR(mode): | ||||
return statmod.S_IFDIR | ||||
if statmod.S_ISLNK(mode): | ||||
return statmod.S_IFLNK | ||||
if statmod.S_ISBLK(mode): | ||||
return statmod.S_IFBLK | ||||
if statmod.S_ISCHR(mode): | ||||
return statmod.S_IFCHR | ||||
if statmod.S_ISFIFO(mode): | ||||
return statmod.S_IFIFO | ||||
if statmod.S_ISSOCK(mode): | ||||
return statmod.S_IFSOCK | ||||
|
r7704 | return mode | ||
def listdir(path, stat=False, skip=None): | ||||
'''listdir(path, stat=False) -> list_of_tuples | ||||
Return a sorted list containing information about the entries | ||||
in the directory. | ||||
If stat is True, each element is a 3-tuple: | ||||
(name, type, stat object) | ||||
Otherwise, each element is a 2-tuple: | ||||
(name, type) | ||||
''' | ||||
result = [] | ||||
prefix = path | ||||
if not prefix.endswith(os.sep): | ||||
prefix += os.sep | ||||
names = os.listdir(path) | ||||
names.sort() | ||||
for fn in names: | ||||
st = os.lstat(prefix + fn) | ||||
|
r10651 | if fn == skip and statmod.S_ISDIR(st.st_mode): | ||
|
r7704 | return [] | ||
if stat: | ||||
result.append((fn, _mode_to_kind(st.st_mode), st)) | ||||
else: | ||||
result.append((fn, _mode_to_kind(st.st_mode))) | ||||
return result | ||||
|
r8421 | |||
|
r14413 | if os.name != 'nt': | ||
posixfile = open | ||||
else: | ||||
|
r16474 | import ctypes, msvcrt | ||
|
r14413 | |||
_kernel32 = ctypes.windll.kernel32 | ||||
_DWORD = ctypes.c_ulong | ||||
_LPCSTR = _LPSTR = ctypes.c_char_p | ||||
_HANDLE = ctypes.c_void_p | ||||
_INVALID_HANDLE_VALUE = _HANDLE(-1).value | ||||
|
r18959 | # CreateFile | ||
|
r14413 | _FILE_SHARE_READ = 0x00000001 | ||
_FILE_SHARE_WRITE = 0x00000002 | ||||
_FILE_SHARE_DELETE = 0x00000004 | ||||
_CREATE_ALWAYS = 2 | ||||
_OPEN_EXISTING = 3 | ||||
_OPEN_ALWAYS = 4 | ||||
_GENERIC_READ = 0x80000000 | ||||
_GENERIC_WRITE = 0x40000000 | ||||
_FILE_ATTRIBUTE_NORMAL = 0x80 | ||||
|
r17429 | # open_osfhandle flags | ||
|
r14413 | _O_RDONLY = 0x0000 | ||
_O_RDWR = 0x0002 | ||||
_O_APPEND = 0x0008 | ||||
_O_TEXT = 0x4000 | ||||
_O_BINARY = 0x8000 | ||||
# types of parameters of C functions used (required by pypy) | ||||
_kernel32.CreateFileA.argtypes = [_LPCSTR, _DWORD, _DWORD, ctypes.c_void_p, | ||||
_DWORD, _DWORD, _HANDLE] | ||||
_kernel32.CreateFileA.restype = _HANDLE | ||||
def _raiseioerror(name): | ||||
err = ctypes.WinError() | ||||
raise IOError(err.errno, '%s: %s' % (name, err.strerror)) | ||||
class posixfile(object): | ||||
'''a file object aiming for POSIX-like semantics | ||||
CPython's open() returns a file that was opened *without* setting the | ||||
_FILE_SHARE_DELETE flag, which causes rename and unlink to abort. | ||||
This even happens if any hardlinked copy of the file is in open state. | ||||
We set _FILE_SHARE_DELETE here, so files opened with posixfile can be | ||||
renamed and deleted while they are held open. | ||||
Note that if a file opened with posixfile is unlinked, the file | ||||
remains but cannot be opened again or be recreated under the same name, | ||||
until all reading processes have closed the file.''' | ||||
def __init__(self, name, mode='r', bufsize=-1): | ||||
if 'b' in mode: | ||||
flags = _O_BINARY | ||||
else: | ||||
flags = _O_TEXT | ||||
m0 = mode[0] | ||||
|
r16686 | if m0 == 'r' and '+' not in mode: | ||
|
r14413 | flags |= _O_RDONLY | ||
access = _GENERIC_READ | ||||
else: | ||||
# work around http://support.microsoft.com/kb/899149 and | ||||
# set _O_RDWR for 'w' and 'a', even if mode has no '+' | ||||
flags |= _O_RDWR | ||||
access = _GENERIC_READ | _GENERIC_WRITE | ||||
if m0 == 'r': | ||||
creation = _OPEN_EXISTING | ||||
elif m0 == 'w': | ||||
creation = _CREATE_ALWAYS | ||||
elif m0 == 'a': | ||||
creation = _OPEN_ALWAYS | ||||
flags |= _O_APPEND | ||||
else: | ||||
raise ValueError("invalid mode: %s" % mode) | ||||
fh = _kernel32.CreateFileA(name, access, | ||||
_FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE, | ||||
None, creation, _FILE_ATTRIBUTE_NORMAL, None) | ||||
if fh == _INVALID_HANDLE_VALUE: | ||||
_raiseioerror(name) | ||||
|
r16474 | fd = msvcrt.open_osfhandle(fh, flags) | ||
|
r14413 | if fd == -1: | ||
_kernel32.CloseHandle(fh) | ||||
_raiseioerror(name) | ||||
f = os.fdopen(fd, mode, bufsize) | ||||
# unfortunately, f.name is '<fdopen>' at this point -- so we store | ||||
# the name on this wrapper. We cannot just assign to f.name, | ||||
# because that attribute is read-only. | ||||
object.__setattr__(self, 'name', name) | ||||
object.__setattr__(self, '_file', f) | ||||
def __iter__(self): | ||||
return self._file | ||||
def __getattr__(self, name): | ||||
return getattr(self._file, name) | ||||
def __setattr__(self, name, value): | ||||
'''mimics the read-only attributes of Python file objects | ||||
by raising 'TypeError: readonly attribute' if someone tries: | ||||
f = posixfile('foo.txt') | ||||
f.name = 'bla' ''' | ||||
return self._file.__setattr__(name, value) | ||||