Mercurial > public > mercurial-scm > hg
view mercurial/pure/osutil.py @ 49275:c6a3243567b6
chg: replace mercurial.util.recvfds() by simpler pure Python implementation
On Python 3, we have socket.socket.recvmsg(). This makes it possible to receive
FDs in pure Python code. The new code behaves like the previous
implementations, except that it?s more strict about the format of the ancillary
data. This works because we know in which format the FDs are passed.
Because the code is (and always has been) specific to chg (payload is 1 byte,
number of passed FDs is limited) and we now have only one implementation and
the code is very short, I decided to stop exposing a function in
mercurial.util.
Note on terminology: The SCM_RIGHTS mechanism is used to share open file
descriptions to another process over a socket. The sending side passes an array
of file descriptors and the receiving side receives an array of file
descriptors. The file descriptors are different in general on both sides but
refer to the same open file descriptions. The two terms are often conflated,
even in the official documentation. That?s why I used ?FD? above, which could
mean both ?file descriptor? and ?file description?.
author | Manuel Jacob <me@manueljacob.de> |
---|---|
date | Thu, 02 Jun 2022 23:57:56 +0200 |
parents | 642e31cb55f0 |
children | 18c8c18993f0 |
line wrap: on
line source
# osutil.py - pure Python version of osutil.c # # Copyright 2009 Olivia Mackall <olivia@selenic.com> and others # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. import ctypes import ctypes.util import os import stat as statmod from ..pycompat import getattr from .. import ( encoding, pycompat, ) def _mode_to_kind(mode): 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 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(pycompat.ossep): prefix += pycompat.ossep names = os.listdir(path) names.sort() for fn in names: st = os.lstat(prefix + fn) if fn == skip and statmod.S_ISDIR(st.st_mode): 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 if not pycompat.iswindows: posixfile = open else: import msvcrt _kernel32 = ctypes.windll.kernel32 # pytype: disable=module-attr _DWORD = ctypes.c_ulong _LPCSTR = _LPSTR = ctypes.c_char_p _HANDLE = ctypes.c_void_p _INVALID_HANDLE_VALUE = _HANDLE(-1).value # CreateFile _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 # open_osfhandle flags _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() # pytype: disable=module-attr raise IOError( err.errno, '%s: %s' % (encoding.strfromlocal(name), err.strerror) ) class posixfile: """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=b'r', bufsize=-1): if b'b' in mode: flags = _O_BINARY else: flags = _O_TEXT m0 = mode[0:1] if m0 == b'r' and b'+' not in mode: 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 == b'r': creation = _OPEN_EXISTING elif m0 == b'w': creation = _CREATE_ALWAYS elif m0 == b'a': creation = _OPEN_ALWAYS flags |= _O_APPEND else: raise ValueError("invalid mode: %s" % pycompat.sysstr(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) fd = msvcrt.open_osfhandle(fh, flags) # pytype: disable=module-attr if fd == -1: _kernel32.CloseHandle(fh) _raiseioerror(name) f = os.fdopen(fd, pycompat.sysstr(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) def __enter__(self): self._file.__enter__() return self def __exit__(self, exc_type, exc_value, exc_tb): return self._file.__exit__(exc_type, exc_value, exc_tb)