osutil.py
207 lines
| 5.9 KiB
| text/x-python
|
PythonLexer
Martin Geisler
|
r8232 | # osutil.py - pure Python version of osutil.c | ||
# | ||||
Raphaël Gomès
|
r47575 | # Copyright 2009 Olivia Mackall <olivia@selenic.com> and others | ||
Martin Geisler
|
r8232 | # | ||
# This software may be used and distributed according to the terms of the | ||||
Matt Mackall
|
r10263 | # GNU General Public License version 2 or any later version. | ||
Martin Geisler
|
r8232 | |||
Matt Harbison
|
r52756 | from __future__ import annotations | ||
Gregory Szorc
|
r27338 | |||
Yuya Nishihara
|
r27474 | import ctypes | ||
import ctypes.util | ||||
Martin Geisler
|
r7704 | import os | ||
Benoit Boissinot
|
r10651 | import stat as statmod | ||
Martin Geisler
|
r7704 | |||
Yuya Nishihara
|
r32367 | from .. import ( | ||
Matt Harbison
|
r39680 | encoding, | ||
Pulkit Goyal
|
r30304 | pycompat, | ||
) | ||||
Augie Fackler
|
r43346 | |||
Martin Geisler
|
r7704 | def _mode_to_kind(mode): | ||
Benoit Boissinot
|
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 | ||||
Martin Geisler
|
r7704 | return mode | ||
Augie Fackler
|
r43346 | |||
Yuya Nishihara
|
r32512 | def listdir(path, stat=False, skip=None): | ||
Augie Fackler
|
r46554 | """listdir(path, stat=False) -> list_of_tuples | ||
Martin Geisler
|
r7704 | |||
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) | ||||
Augie Fackler
|
r46554 | """ | ||
Martin Geisler
|
r7704 | result = [] | ||
prefix = path | ||||
Pulkit Goyal
|
r30304 | if not prefix.endswith(pycompat.ossep): | ||
prefix += pycompat.ossep | ||||
Martin Geisler
|
r7704 | names = os.listdir(path) | ||
names.sort() | ||||
for fn in names: | ||||
st = os.lstat(prefix + fn) | ||||
Benoit Boissinot
|
r10651 | if fn == skip and statmod.S_ISDIR(st.st_mode): | ||
Martin Geisler
|
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 | ||||
Sune Foldager
|
r8421 | |||
Augie Fackler
|
r43346 | |||
Jun Wu
|
r34646 | if not pycompat.iswindows: | ||
Adrian Buehlmann
|
r14413 | posixfile = open | ||
Yuya Nishihara
|
r27474 | |||
Augie Fackler
|
r43346 | |||
Adrian Buehlmann
|
r14413 | else: | ||
Gregory Szorc
|
r27338 | import msvcrt | ||
Adrian Buehlmann
|
r14413 | |||
Matt Harbison
|
r47544 | _kernel32 = ctypes.windll.kernel32 # pytype: disable=module-attr | ||
Adrian Buehlmann
|
r14413 | |||
_DWORD = ctypes.c_ulong | ||||
_LPCSTR = _LPSTR = ctypes.c_char_p | ||||
_HANDLE = ctypes.c_void_p | ||||
_INVALID_HANDLE_VALUE = _HANDLE(-1).value | ||||
Mads Kiilerich
|
r18959 | # CreateFile | ||
Adrian Buehlmann
|
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 | ||||
Mads Kiilerich
|
r17429 | # open_osfhandle flags | ||
Adrian Buehlmann
|
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) | ||||
Augie Fackler
|
r43346 | _kernel32.CreateFileA.argtypes = [ | ||
_LPCSTR, | ||||
_DWORD, | ||||
_DWORD, | ||||
ctypes.c_void_p, | ||||
_DWORD, | ||||
_DWORD, | ||||
_HANDLE, | ||||
] | ||||
Adrian Buehlmann
|
r14413 | _kernel32.CreateFileA.restype = _HANDLE | ||
def _raiseioerror(name): | ||||
Matt Harbison
|
r47544 | err = ctypes.WinError() # pytype: disable=module-attr | ||
Augie Fackler
|
r43346 | raise IOError( | ||
Augie Fackler
|
r43906 | err.errno, '%s: %s' % (encoding.strfromlocal(name), err.strerror) | ||
Augie Fackler
|
r43346 | ) | ||
Adrian Buehlmann
|
r14413 | |||
Gregory Szorc
|
r49801 | class posixfile: | ||
Augie Fackler
|
r46554 | """a file object aiming for POSIX-like semantics | ||
Adrian Buehlmann
|
r14413 | |||
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, | ||||
Augie Fackler
|
r46554 | until all reading processes have closed the file.""" | ||
Adrian Buehlmann
|
r14413 | |||
Matt Harbison
|
r39680 | def __init__(self, name, mode=b'r', bufsize=-1): | ||
if b'b' in mode: | ||||
Adrian Buehlmann
|
r14413 | flags = _O_BINARY | ||
else: | ||||
flags = _O_TEXT | ||||
Matt Harbison
|
r39680 | m0 = mode[0:1] | ||
if m0 == b'r' and b'+' not in mode: | ||||
Adrian Buehlmann
|
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 | ||||
Matt Harbison
|
r39680 | if m0 == b'r': | ||
Adrian Buehlmann
|
r14413 | creation = _OPEN_EXISTING | ||
Matt Harbison
|
r39680 | elif m0 == b'w': | ||
Adrian Buehlmann
|
r14413 | creation = _CREATE_ALWAYS | ||
Matt Harbison
|
r39680 | elif m0 == b'a': | ||
Adrian Buehlmann
|
r14413 | creation = _OPEN_ALWAYS | ||
flags |= _O_APPEND | ||||
else: | ||||
Augie Fackler
|
r43809 | raise ValueError("invalid mode: %s" % pycompat.sysstr(mode)) | ||
Adrian Buehlmann
|
r14413 | |||
Augie Fackler
|
r43346 | fh = _kernel32.CreateFileA( | ||
name, | ||||
access, | ||||
_FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE, | ||||
None, | ||||
creation, | ||||
_FILE_ATTRIBUTE_NORMAL, | ||||
None, | ||||
) | ||||
Adrian Buehlmann
|
r14413 | if fh == _INVALID_HANDLE_VALUE: | ||
_raiseioerror(name) | ||||
Matt Harbison
|
r47544 | fd = msvcrt.open_osfhandle(fh, flags) # pytype: disable=module-attr | ||
Adrian Buehlmann
|
r14413 | if fd == -1: | ||
_kernel32.CloseHandle(fh) | ||||
_raiseioerror(name) | ||||
Pulkit Goyal
|
r30925 | f = os.fdopen(fd, pycompat.sysstr(mode), bufsize) | ||
Adrian Buehlmann
|
r14413 | # 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. | ||||
Augie Fackler
|
r43906 | object.__setattr__(self, 'name', name) | ||
object.__setattr__(self, '_file', f) | ||||
Adrian Buehlmann
|
r14413 | |||
def __iter__(self): | ||||
return self._file | ||||
def __getattr__(self, name): | ||||
return getattr(self._file, name) | ||||
def __setattr__(self, name, value): | ||||
Augie Fackler
|
r46554 | """mimics the read-only attributes of Python file objects | ||
Adrian Buehlmann
|
r14413 | by raising 'TypeError: readonly attribute' if someone tries: | ||
f = posixfile('foo.txt') | ||||
Augie Fackler
|
r46553 | f.name = 'bla' | ||
Augie Fackler
|
r46554 | """ | ||
Adrian Buehlmann
|
r14413 | return self._file.__setattr__(name, value) | ||
Gregory Szorc
|
r27704 | |||
def __enter__(self): | ||||
Matt Harbison
|
r40976 | self._file.__enter__() | ||
return self | ||||
Gregory Szorc
|
r27704 | |||
def __exit__(self, exc_type, exc_value, exc_tb): | ||||
return self._file.__exit__(exc_type, exc_value, exc_tb) | ||||