##// END OF EJS Templates
util: introduce ctxmanager, to avoid nested try/finally blocks...
util: introduce ctxmanager, to avoid nested try/finally blocks This is similar in spirit to contextlib.nested in Python <= 2.6, but uses an extra level of indirection to avoid its inability to clean up if an __enter__ method raises an exception. Why add this mechanism? It greatly simplifies scoped resource management, and lets us eliminate several hundred lines of try/finally blocks. In many of these cases the "finally" is separated from the "try" by hundreds of lines of code, which makes the connection between resource acquisition and disposal difficult to follow. (The preferred mechanism would be the "multi-with" syntax of 2.7+, but Mercurial can't move to 2.7 for a while.) Intended use: >>> with ctxmanager(lambda: file('foo'), lambda: file('bar')) as c: >>> f1, f2 = c() This will open both foo and bar when c() is invoked, and will close both upon exit from the block. If the attempt to open bar raises an exception, the block will not be entered - but foo will still be closed.

File last commit:

r27512:e2aa9c40 default
r27703:4e27c0a7 default
Show More
osutil.py
256 lines | 8.3 KiB | text/x-python | PythonLexer
Martin Geisler
pure/osutil: add copyright and license header
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
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Martin Geisler
pure/osutil: add copyright and license header
r8232
Gregory Szorc
osutil: use absolute_import
r27338 from __future__ import absolute_import
Yuya Nishihara
osutil: implement pure version of recvfds() for PyPy...
r27474 import ctypes
import ctypes.util
Martin Geisler
move mercurial.osutil to mercurial.pure.osutil
r7704 import os
Yuya Nishihara
osutil: implement pure version of recvfds() for PyPy...
r27474 import socket
Benoit Boissinot
style: use consistent variable names (*mod) with imports which would shadow
r10651 import stat as statmod
Yuya Nishihara
osutil: implement pure version of recvfds() for PyPy...
r27474 import sys
Martin Geisler
move mercurial.osutil to mercurial.pure.osutil
r7704
def _mode_to_kind(mode):
Benoit Boissinot
style: use consistent variable names (*mod) with imports which would shadow
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
move mercurial.osutil to mercurial.pure.osutil
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)
Benoit Boissinot
style: use consistent variable names (*mod) with imports which would shadow
r10651 if fn == skip and statmod.S_ISDIR(st.st_mode):
Martin Geisler
move mercurial.osutil to mercurial.pure.osutil
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
posixfile: remove posixfile_nt and fix import bug in windows.py...
r8421
Adrian Buehlmann
pure: provide more correct implementation of posixfile for Windows...
r14413 if os.name != 'nt':
posixfile = open
Yuya Nishihara
osutil: implement pure version of recvfds() for PyPy...
r27474
_SCM_RIGHTS = 0x01
_socklen_t = ctypes.c_uint
if sys.platform == 'linux2':
# socket.h says "the type should be socklen_t but the definition of
# the kernel is incompatible with this."
_cmsg_len_t = ctypes.c_size_t
_msg_controllen_t = ctypes.c_size_t
_msg_iovlen_t = ctypes.c_size_t
else:
_cmsg_len_t = _socklen_t
_msg_controllen_t = _socklen_t
_msg_iovlen_t = ctypes.c_int
class _iovec(ctypes.Structure):
_fields_ = [
('iov_base', ctypes.c_void_p),
('iov_len', ctypes.c_size_t),
]
class _msghdr(ctypes.Structure):
_fields_ = [
('msg_name', ctypes.c_void_p),
('msg_namelen', _socklen_t),
('msg_iov', ctypes.POINTER(_iovec)),
('msg_iovlen', _msg_iovlen_t),
('msg_control', ctypes.c_void_p),
('msg_controllen', _msg_controllen_t),
('msg_flags', ctypes.c_int),
]
class _cmsghdr(ctypes.Structure):
_fields_ = [
('cmsg_len', _cmsg_len_t),
('cmsg_level', ctypes.c_int),
('cmsg_type', ctypes.c_int),
('cmsg_data', ctypes.c_ubyte * 0),
]
_libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True)
_recvmsg = _libc.recvmsg
Bryan O'Sullivan
osutil: c_ssize_t is only available in Python >= 2.7
r27512 _recvmsg.restype = getattr(ctypes, 'c_ssize_t', ctypes.c_long)
Yuya Nishihara
osutil: implement pure version of recvfds() for PyPy...
r27474 _recvmsg.argtypes = (ctypes.c_int, ctypes.POINTER(_msghdr), ctypes.c_int)
def _CMSG_FIRSTHDR(msgh):
if msgh.msg_controllen < ctypes.sizeof(_cmsghdr):
return
cmsgptr = ctypes.cast(msgh.msg_control, ctypes.POINTER(_cmsghdr))
return cmsgptr.contents
# The pure version is less portable than the native version because the
# handling of socket ancillary data heavily depends on C preprocessor.
# Also, some length fields are wrongly typed in Linux kernel.
def recvfds(sockfd):
"""receive list of file descriptors via socket"""
dummy = (ctypes.c_ubyte * 1)()
iov = _iovec(ctypes.cast(dummy, ctypes.c_void_p), ctypes.sizeof(dummy))
cbuf = ctypes.create_string_buffer(256)
msgh = _msghdr(None, 0,
ctypes.pointer(iov), 1,
ctypes.cast(cbuf, ctypes.c_void_p), ctypes.sizeof(cbuf),
0)
r = _recvmsg(sockfd, ctypes.byref(msgh), 0)
if r < 0:
e = ctypes.get_errno()
raise OSError(e, os.strerror(e))
# assumes that the first cmsg has fds because it isn't easy to write
# portable CMSG_NXTHDR() with ctypes.
cmsg = _CMSG_FIRSTHDR(msgh)
if not cmsg:
return []
if (cmsg.cmsg_level != socket.SOL_SOCKET or
cmsg.cmsg_type != _SCM_RIGHTS):
return []
rfds = ctypes.cast(cmsg.cmsg_data, ctypes.POINTER(ctypes.c_int))
rfdscount = ((cmsg.cmsg_len - _cmsghdr.cmsg_data.offset) /
ctypes.sizeof(ctypes.c_int))
return [rfds[i] for i in xrange(rfdscount)]
Adrian Buehlmann
pure: provide more correct implementation of posixfile for Windows...
r14413 else:
Gregory Szorc
osutil: use absolute_import
r27338 import msvcrt
Adrian Buehlmann
pure: provide more correct implementation of posixfile for Windows...
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
Mads Kiilerich
check-code: catch trailing space in comments
r18959 # CreateFile
Adrian Buehlmann
pure: provide more correct implementation of posixfile for Windows...
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
declare local constants instead of using magic values and comments
r17429 # open_osfhandle flags
Adrian Buehlmann
pure: provide more correct implementation of posixfile for Windows...
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()
Gregory Szorc
osutil: remove Python 2.4 errno conversion workaround
r25645 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
Adrian Buehlmann
pure: provide more correct implementation of posixfile for Windows...
r14413
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]
Brodie Rao
cleanup: "not x in y" -> "x not in y"
r16686 if m0 == 'r' and '+' not in mode:
Adrian Buehlmann
pure: provide more correct implementation of posixfile for Windows...
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)
Adrian Buehlmann
pure/osutil: use Python's msvcrt module (issue3380)...
r16474 fd = msvcrt.open_osfhandle(fh, flags)
Adrian Buehlmann
pure: provide more correct implementation of posixfile for Windows...
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)