[PATCH 1 of 5] port win32.py to using the Python ctypes library
Steve Borho
steve at borho.org
Tue Feb 8 11:48:00 CST 2011
On Tue, Feb 8, 2011 at 10:52 AM, Adrian Buehlmann <adrian at cadifra.com> wrote:
> # HG changeset patch
> # User Adrian Buehlmann <adrian at cadifra.com>
> # Date 1297121623 -3600
> # Node ID 3ba4920422e9abcdc8b8634c5c12c68051286a8c
> # Parent 69e69b131458023d21ec40aa48fc5299e43ce69b
> port win32.py to using the Python ctypes library
>
> The pywin32 package is no longer needed.
>
> ctypes is now required for running Mercurial on Windows.
>
> ctypes is included in Python since version 2.5. For Python 2.4, ctypes is
> available as an extra installer package for Windows.
>
> Also moved spawndetached() from windows.py to win32.py and fixed it, using
> ctypes as well. spawndetached was defunct with Python 2.6.6 because Python
> removed their undocumented subprocess.CreateProcess. This fixes
> 'hg serve -d' on Windows.
>
> diff --git a/contrib/wix/dist.wxs b/contrib/wix/dist.wxs
> --- a/contrib/wix/dist.wxs
> +++ b/contrib/wix/dist.wxs
> @@ -16,23 +16,14 @@
> <File Name="mercurial.parsers.pyd" />
> <File Name="pyexpat.pyd" />
> <File Name="python26.dll" />
> - <File Name="pythoncom26.dll" />
> - <File Name="pywintypes26.dll" />
> <File Name="bz2.pyd" />
> <File Name="select.pyd" />
> <File Name="unicodedata.pyd" />
> - <File Name="win32api.pyd" />
> - <File Name="win32com.shell.shell.pyd" />
> - <File Name="win32console.pyd" />
> - <File Name="win32file.pyd" />
> - <File Name="win32gui.pyd" />
> - <File Name="win32pipe.pyd" />
> - <File Name="win32process.pyd" />
> + <File Name="_ctypes.pyd" />
> <File Name="_elementtree.pyd" />
> <File Name="_hashlib.pyd" />
> <File Name="_socket.pyd" />
> <File Name="_ssl.pyd" />
> - <File Name="_win32sysloader.pyd" />
> </Component>
> </DirectoryRef>
> </Fragment>
> diff --git a/contrib/wix/guids.wxi b/contrib/wix/guids.wxi
> --- a/contrib/wix/guids.wxi
> +++ b/contrib/wix/guids.wxi
> @@ -9,7 +9,7 @@
> <?define contrib.vim.guid = {BB04903A-652D-4C4F-9590-2BD07A2304F2} ?>
>
> <!-- dist.wxs -->
> - <?define dist.guid = {0F63D160-0740-4BAF-BF25-0C6930310F51} ?>
> + <?define dist.guid = {C3B634A4-1B05-4A40-94A9-38EE853CF693} ?>
>
> <!-- doc.wxs -->
> <?define doc.hg.1.html.guid = {AAAA3FDA-EDC5-4220-B59D-D342722358A2} ?>
> diff --git a/mercurial/posix.py b/mercurial/posix.py
> --- a/mercurial/posix.py
> +++ b/mercurial/posix.py
> @@ -13,6 +13,7 @@ posixfile = open
> nulldev = '/dev/null'
> normpath = os.path.normpath
> samestat = os.path.samestat
> +os_link = os.link
> unlink = os.unlink
> rename = os.rename
> expandglobs = False
> @@ -24,6 +25,10 @@ def openhardlinks():
> '''return true if it is safe to hold open file handles to hardlinks'''
> return True
>
> +def nlinks(name):
> + '''return number of hardlinks for the given file'''
> + return os.lstat(name).st_nlink
> +
> def rcfiles(path):
> rcs = [os.path.join(path, 'hgrc')]
> rcdir = os.path.join(path, 'hgrc.d')
> diff --git a/mercurial/util.py b/mercurial/util.py
> --- a/mercurial/util.py
> +++ b/mercurial/util.py
> @@ -554,16 +554,6 @@ class path_auditor(object):
> # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
> self.auditeddir.update(prefixes)
>
> -def nlinks(pathname):
> - """Return number of hardlinks for the given file."""
> - return os.lstat(pathname).st_nlink
> -
> -if hasattr(os, 'link'):
> - os_link = os.link
> -else:
> - def os_link(src, dst):
> - raise OSError(0, _("Hardlinks not supported"))
> -
> def lookup_reg(key, name=None, scope=None):
> return None
>
> diff --git a/mercurial/win32.py b/mercurial/win32.py
> --- a/mercurial/win32.py
> +++ b/mercurial/win32.py
> @@ -5,73 +5,174 @@
> # This software may be used and distributed according to the terms of the
> # GNU General Public License version 2 or any later version.
>
> -"""Utility functions that use win32 API.
> +import osutil, encoding
> +import ctypes, errno, os, struct, subprocess
>
> -Mark Hammond's win32all package allows better functionality on
> -Windows. This module overrides definitions in util.py. If not
> -available, import of this module will fail, and generic code will be
> -used.
> -"""
> +_kernel32 = ctypes.windll.kernel32
>
> -import win32api
> +_BOOL = ctypes.c_long
> +_WORD = ctypes.c_ushort
> +_DWORD = ctypes.c_ulong
> +_LPCSTR = _LPSTR = ctypes.c_char_p
> +_HANDLE = ctypes.c_void_p
> +_HWND = _HANDLE
>
> -import errno, os, sys, pywintypes, win32con, win32file, win32process
> -import winerror, win32gui, win32console
> -import osutil, encoding
> -from win32com.shell import shell, shellcon
> +_INVALID_HANDLE_VALUE = -1
> +
> +# GetLastError
> +_ERROR_SUCCESS = 0
> +_ERROR_INVALID_PARAMETER = 87
> +
> +# WPARAM is defined as UINT_PTR (unsigned type)
> +# LPARAM is defined as LONG_PTR (signed type)
> +if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p):
> + _WPARAM = ctypes.c_ulong
> + _LPARAM = ctypes.c_long
> +elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p):
> + _WPARAM = ctypes.c_ulonglong
> + _LPARAM = ctypes.c_longlong
> +
> +class _FILETIME(ctypes.Structure):
> + _fields_ = [('dwLowDateTime', _DWORD),
> + ('dwHighDateTime', _DWORD)]
> +
> +class _BY_HANDLE_FILE_INFORMATION(ctypes.Structure):
> + _fields_ = [('dwFileAttributes', _DWORD),
> + ('ftCreationTime', _FILETIME),
> + ('ftLastAccessTime', _FILETIME),
> + ('ftLastWriteTime', _FILETIME),
> + ('dwVolumeSerialNumber', _DWORD),
> + ('nFileSizeHigh', _DWORD),
> + ('nFileSizeLow', _DWORD),
> + ('nNumberOfLinks', _DWORD),
> + ('nFileIndexHigh', _DWORD),
> + ('nFileIndexLow', _DWORD)]
> +
> +# CreateFile
> +_FILE_SHARE_READ = 0x00000001
> +_FILE_SHARE_WRITE = 0x00000002
> +_FILE_SHARE_DELETE = 0x00000004
> +
> +_OPEN_EXISTING = 3
> +
> +# Process Security and Access Rights
> +_PROCESS_QUERY_INFORMATION = 0x0400
> +
> +# GetExitCodeProcess
> +_STILL_ACTIVE = 259
> +
> +# registry
> +_HKEY_CURRENT_USER = 0x80000001L
> +_HKEY_LOCAL_MACHINE = 0x80000002L
> +_KEY_READ = 0x20019
> +_RRF_RT_ANY = 0x0000ffff
> +_REG_SZ = 1
> +_REG_DWORD = 4
> +
> +class _STARTUPINFO(ctypes.Structure):
> + _fields_ = [('cb', _DWORD),
> + ('lpReserved', _LPSTR),
> + ('lpDesktop', _LPSTR),
> + ('lpTitle', _LPSTR),
> + ('dwX', _DWORD),
> + ('dwY', _DWORD),
> + ('dwXSize', _DWORD),
> + ('dwYSize', _DWORD),
> + ('dwXCountChars', _DWORD),
> + ('dwYCountChars', _DWORD),
> + ('dwFillAttribute', _DWORD),
> + ('dwFlags', _DWORD),
> + ('wShowWindow', _WORD),
> + ('cbReserved2', _WORD),
> + ('lpReserved2', ctypes.c_char_p),
> + ('hStdInput', _HANDLE),
> + ('hStdOutput', _HANDLE),
> + ('hStdError', _HANDLE)]
> +
> +class _PROCESS_INFORMATION(ctypes.Structure):
> + _fields_ = [('hProcess', _HANDLE),
> + ('hThread', _HANDLE),
> + ('dwProcessId', _DWORD),
> + ('dwThreadId', _DWORD)]
> +
> +_DETACHED_PROCESS = 0x00000008
> +_STARTF_USESHOWWINDOW = 0x00000001
> +_SW_HIDE = 0
> +
> +class _COORD(ctypes.Structure):
> + _fields_ = [('X', ctypes.c_short),
> + ('Y', ctypes.c_short)]
> +
> +class _SMALL_RECT(ctypes.Structure):
> + _fields_ = [('Left', ctypes.c_short),
> + ('Top', ctypes.c_short),
> + ('Right', ctypes.c_short),
> + ('Bottom', ctypes.c_short)]
> +
> +class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
> + _fields_ = [('dwSize', _COORD),
> + ('dwCursorPosition', _COORD),
> + ('wAttributes', _WORD),
> + ('srWindow', _SMALL_RECT),
> + ('dwMaximumWindowSize', _COORD)]
> +
> +_STD_ERROR_HANDLE = 0xfffffff4L # (DWORD)-12
> +
> +def _raiseoserror(name):
> + err = ctypes.WinError()
> + raise OSError(err.errno, '%s: %s' % (name, err.strerror))
> +
> +def _getfileinfo(name):
> + fh = _kernel32.CreateFileA(name,
> + 0, _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
> + None, _OPEN_EXISTING, 0, None)
> + if fh == _INVALID_HANDLE_VALUE:
> + _raiseoserror(name)
> + try:
> + fi = _BY_HANDLE_FILE_INFORMATION()
> + if not _kernel32.GetFileInformationByHandle(fh, ctypes.byref(fi)):
> + _raiseoserror(name)
> + return fi
> + finally:
> + if fh != _INVALID_HANDLE_VALUE:
> + _kernel32.CloseHandle(fh)
>
> def os_link(src, dst):
> - try:
> - win32file.CreateHardLink(dst, src)
> - except pywintypes.error:
> - raise OSError(errno.EINVAL, 'target implements hardlinks improperly')
> - except NotImplementedError: # Another fake error win Win98
> - raise OSError(errno.EINVAL, 'Hardlinking not supported')
> + if not _kernel32.CreateHardLinkA(dst, src, None):
> + _raiseoserror(src)
>
> -def _getfileinfo(pathname):
> - """Return number of hardlinks for the given file."""
> - try:
> - fh = win32file.CreateFile(pathname,
> - win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
> - None, win32file.OPEN_EXISTING, 0, None)
> - except pywintypes.error:
> - raise OSError(errno.ENOENT, 'The system cannot find the file specified')
> - try:
> - return win32file.GetFileInformationByHandle(fh)
> - finally:
> - fh.Close()
> -
> -def nlinks(pathname):
> - """Return number of hardlinks for the given file."""
> - return _getfileinfo(pathname)[7]
> +def nlinks(name):
> + '''return number of hardlinks for the given file'''
> + return _getfileinfo(name).nNumberOfLinks
>
> def samefile(fpath1, fpath2):
> - """Returns whether fpath1 and fpath2 refer to the same file. This is only
> - guaranteed to work for files, not directories."""
> + '''Returns whether fpath1 and fpath2 refer to the same file. This is only
> + guaranteed to work for files, not directories.'''
> res1 = _getfileinfo(fpath1)
> res2 = _getfileinfo(fpath2)
> - # Index 4 is the volume serial number, and 8 and 9 contain the file ID
> - return res1[4] == res2[4] and res1[8] == res2[8] and res1[9] == res2[9]
> + return (res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
> + and res1.nFileIndexHigh == res2.nFileIndexHigh
> + and res1.nFileIndexLow == res2.nFileIndexLow)
>
> def samedevice(fpath1, fpath2):
> - """Returns whether fpath1 and fpath2 are on the same device. This is only
> - guaranteed to work for files, not directories."""
> + '''Returns whether fpath1 and fpath2 are on the same device. This is only
> + guaranteed to work for files, not directories.'''
> res1 = _getfileinfo(fpath1)
> res2 = _getfileinfo(fpath2)
> - return res1[4] == res2[4]
> + return res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
>
> def testpid(pid):
> '''return True if pid is still running or unable to
> determine, False otherwise'''
> - try:
> - handle = win32api.OpenProcess(
> - win32con.PROCESS_QUERY_INFORMATION, False, pid)
> - if handle:
> - status = win32process.GetExitCodeProcess(handle)
> - return status == win32con.STILL_ACTIVE
> - except pywintypes.error, details:
> - return details[0] != winerror.ERROR_INVALID_PARAMETER
> - return True
> + h = _kernel32.OpenProcess(_PROCESS_QUERY_INFORMATION, False, pid)
> + if h:
> + try:
> + status = _DWORD()
> + if _kernel32.GetExitCodeProcess(h, ctypes.byref(status)):
> + return status.value == _STILL_ACTIVE
> + finally:
> + _kernel32.CloseHandle(h)
> + return _kernel32.GetLastError() != _ERROR_INVALID_PARAMETER
>
> def lookup_reg(key, valname=None, scope=None):
> ''' Look up a key/value name in the Windows registry.
> @@ -82,101 +183,168 @@ def lookup_reg(key, valname=None, scope=
> a sequence of scopes to look up in order. Default (CURRENT_USER,
> LOCAL_MACHINE).
> '''
> - try:
> - from _winreg import HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, \
> - QueryValueEx, OpenKey
> - except ImportError:
> - return None
> -
> + adv = ctypes.windll.advapi32
> + byref = ctypes.byref
> if scope is None:
> - scope = (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE)
> + scope = (_HKEY_CURRENT_USER, _HKEY_LOCAL_MACHINE)
> elif not isinstance(scope, (list, tuple)):
> scope = (scope,)
> for s in scope:
> + kh = _HANDLE()
> + res = adv.RegOpenKeyExA(s, key, 0, _KEY_READ, ctypes.byref(kh))
> + if res != _ERROR_SUCCESS:
> + continue
> try:
> - val = QueryValueEx(OpenKey(s, key), valname)[0]
> - # never let a Unicode string escape into the wild
> - return encoding.tolocal(val.encode('UTF-8'))
> - except EnvironmentError:
> - pass
> + size = _DWORD(600)
> + type = _DWORD()
> + buf = ctypes.create_string_buffer(size.value + 1)
> + res = adv.RegGetValueA(kh.value, None, valname, _RRF_RT_ANY,
> + byref(type), byref(buf), byref(size))
> + if res != _ERROR_SUCCESS:
> + continue
> + if type.value == _REG_SZ:
> + # never let a Unicode string escape into the wild
> + return encoding.tolocal(buf.value.encode('UTF-8'))
> + elif type.value == _REG_DWORD:
> + fmt = '<L'
> + s = ctypes.string_at(byref(buf), struct.calcsize(fmt))
> + return struct.unpack(fmt, s)[0]
> + finally:
> + if kh.value:
> + adv.RegCloseKey(kh.value)
>
> def system_rcpath_win32():
> '''return default os-specific hgrc search path'''
> - filename = win32api.GetModuleFileName(0)
> + rcpath = []
> + size = 600
> + buf = ctypes.create_string_buffer(size + 1)
> + len = _kernel32.GetModuleFileNameA(None, ctypes.byref(buf), size)
> + if not len:
> + return rcpath
> + filename = buf.value
> # Use mercurial.ini found in directory with hg.exe
> progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
> if os.path.isfile(progrc):
> - return [progrc]
> + rcpath.append(progrc)
> + return rcpath
> # Use hgrc.d found in directory with hg.exe
> progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d')
> if os.path.isdir(progrcd):
> - rcpath = []
> for f, kind in osutil.listdir(progrcd):
> if f.endswith('.rc'):
> rcpath.append(os.path.join(progrcd, f))
> return rcpath
> # else look for a system rcpath in the registry
> - try:
> - value = win32api.RegQueryValue(
> - win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Mercurial')
> - rcpath = []
> - for p in value.split(os.pathsep):
> - if p.lower().endswith('mercurial.ini'):
> - rcpath.append(p)
> - elif os.path.isdir(p):
> - for f, kind in osutil.listdir(p):
> - if f.endswith('.rc'):
> - rcpath.append(os.path.join(p, f))
> + value = lookup_reg('SOFTWARE\\Mercurial', None, _HKEY_LOCAL_MACHINE)
> + if not isinstance(value, str) or not value:
> return rcpath
> - except pywintypes.error:
> - return []
> + value = value.replace('/', os.sep)
> + for p in value.split(os.pathsep):
> + if p.lower().endswith('mercurial.ini'):
> + rcpath.append(p)
> + elif os.path.isdir(p):
> + for f, kind in osutil.listdir(p):
> + if f.endswith('.rc'):
> + rcpath.append(os.path.join(p, f))
> + return rcpath
>
> def user_rcpath_win32():
> '''return os-specific hgrc search path to the user dir'''
> userdir = os.path.expanduser('~')
> - if sys.getwindowsversion()[3] != 2 and userdir == '~':
> - # We are on win < nt: fetch the APPDATA directory location and use
> - # the parent directory as the user home dir.
> - appdir = shell.SHGetPathFromIDList(
> - shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_APPDATA))
> - userdir = os.path.dirname(appdir)
> return [os.path.join(userdir, 'mercurial.ini'),
> os.path.join(userdir, '.hgrc')]
>
> def getuser():
> '''return name of current user'''
> - return win32api.GetUserName()
> + adv = ctypes.windll.advapi32
> + size = _DWORD(300)
> + buf = ctypes.create_string_buffer(size.value + 1)
> + if not adv.GetUserNameA(ctypes.byref(buf), ctypes.byref(size)):
> + raise ctypes.WinError()
> + return buf.value
> +
> +_SIGNAL_HANDLER = ctypes.WINFUNCTYPE(_BOOL, _DWORD)
> +_signal_handler = []
>
> def set_signal_handler_win32():
> - """Register a termination handler for console events including
> + '''Register a termination handler for console events including
> CTRL+C. python signal handlers do not work well with socket
> operations.
> - """
> + '''
> def handler(event):
> - win32process.ExitProcess(1)
> - win32api.SetConsoleCtrlHandler(handler)
> + _kernel32.ExitProcess(1)
> +
> + if _signal_handler:
> + return # already registered
> + h = _SIGNAL_HANDLER(handler)
> + _signal_handler.append(h) # needed to prevent garbage collection
> + if not _kernel32.SetConsoleCtrlHandler(h, True):
> + raise ctypes.WinError()
> +
> +_WNDENUMPROC = ctypes.WINFUNCTYPE(_BOOL, _HWND, _LPARAM)
>
> def hidewindow():
> - def callback(*args, **kwargs):
> - hwnd, pid = args
> - wpid = win32process.GetWindowThreadProcessId(hwnd)[1]
> - if pid == wpid:
> - win32gui.ShowWindow(hwnd, win32con.SW_HIDE)
> + user32 = ctypes.windll.user32
>
> - pid = win32process.GetCurrentProcessId()
> - win32gui.EnumWindows(callback, pid)
> + def callback(hwnd, pid):
> + wpid = _DWORD()
> + user32.GetWindowThreadProcessId(hwnd, ctypes.byref(wpid))
> + if pid == wpid.value:
> + user32.ShowWindow(hwnd, _SW_HIDE)
> + return False # stop enumerating windows
> + return True
> +
> + pid = _kernel32.GetCurrentProcessId()
> + user32.EnumWindows(_WNDENUMPROC(callback), pid)
>
> def termwidth():
> - try:
> - # Query stderr to avoid problems with redirections
> - screenbuf = win32console.GetStdHandle(win32console.STD_ERROR_HANDLE)
> - if screenbuf is None:
> - return 79
> - try:
> - window = screenbuf.GetConsoleScreenBufferInfo()['Window']
> - width = window.Right - window.Left
> - return width
> - finally:
> - screenbuf.Detach()
> - except pywintypes.error:
> - return 79
> + # cmd.exe does not handle CR like a unix console, the CR is
> + # counted in the line length. On 80 columns consoles, if 80
> + # characters are written, the following CR won't apply on the
> + # current line but on the new one. Keep room for it.
> + width = 79
> + # Query stderr to avoid problems with redirections
> + screenbuf = _kernel32.GetStdHandle(
> + _STD_ERROR_HANDLE) # don't close the handle returned
> + if screenbuf is None or screenbuf == _INVALID_HANDLE_VALUE:
> + return width
> + csbi = _CONSOLE_SCREEN_BUFFER_INFO()
> + if not _kernel32.GetConsoleScreenBufferInfo(
> + screenbuf, ctypes.byref(csbi)):
> + return width
> + width = csbi.srWindow.Right - csbi.srWindow.Left
> + return width
> +
> +def spawndetached(args):
> + # No standard library function really spawns a fully detached
> + # process under win32 because they allocate pipes or other objects
> + # to handle standard streams communications. Passing these objects
> + # to the child process requires handle inheritance to be enabled
> + # which makes really detached processes impossible.
> + si = _STARTUPINFO()
> + si.cb = ctypes.sizeof(_STARTUPINFO)
> + si.dwFlags = _STARTF_USESHOWWINDOW
> + si.wShowWindow = _SW_HIDE
> +
> + pi = _PROCESS_INFORMATION()
> +
> + env = ''
> + for k in os.environ:
> + env += "%s=%s\0" % (k, os.environ[k])
> + if not env:
> + env = '\0'
> + env += '\0'
> +
> + args = subprocess.list2cmdline(args)
> + # Not running the command in shell mode makes python26 hang when
> + # writing to hgweb output socket.
> + comspec = os.environ.get("COMSPEC", "cmd.exe")
> + args = comspec + " /c " + args
> +
> + res = _kernel32.CreateProcessA(
> + None, args, None, None, False, _DETACHED_PROCESS,
> + env, os.getcwd(), ctypes.byref(si), ctypes.byref(pi))
> + if not res:
> + raise ctypes.WinError()
> +
> + return pi.dwProcessId
> diff --git a/mercurial/windows.py b/mercurial/windows.py
> --- a/mercurial/windows.py
> +++ b/mercurial/windows.py
> @@ -71,7 +71,7 @@ def _is_win_9x():
> return 'command' in os.environ.get('comspec', '')
>
> def openhardlinks():
> - return not _is_win_9x() and "win32api" in globals()
> + return not _is_win_9x()
>
> def system_rcpath():
> try:
> @@ -106,10 +106,6 @@ def sshargs(sshcmd, host, user, port):
> args = user and ("%s@%s" % (user, host)) or host
> return port and ("%s %s %s" % (args, pflag, port)) or args
>
> -def testpid(pid):
> - '''return False if pid dead, True if running or not known'''
> - return True
> -
> def set_flags(f, l, x):
> pass
>
> @@ -211,7 +207,7 @@ def find_exe(command):
> def set_signal_handler():
> try:
> set_signal_handler_win32()
> - except NameError:
> + except WindowsError:
> pass
>
> def statfiles(files):
> @@ -241,11 +237,6 @@ def statfiles(files):
> cache = dircache.setdefault(dir, dmap)
> yield cache.get(base, None)
>
> -def getuser():
> - '''return name of current user'''
> - raise error.Abort(_('user name not available - set USERNAME '
> - 'environment variable'))
> -
> def username(uid=None):
> """Return the name of the user with the given uid.
>
> @@ -335,37 +326,6 @@ def rename(src, dst):
> unlink(dst)
> os.rename(src, dst)
>
> -def spawndetached(args):
> - # No standard library function really spawns a fully detached
> - # process under win32 because they allocate pipes or other objects
> - # to handle standard streams communications. Passing these objects
> - # to the child process requires handle inheritance to be enabled
> - # which makes really detached processes impossible.
> - class STARTUPINFO:
> - dwFlags = subprocess.STARTF_USESHOWWINDOW
> - hStdInput = None
> - hStdOutput = None
> - hStdError = None
> - wShowWindow = subprocess.SW_HIDE
> -
> - args = subprocess.list2cmdline(args)
> - # Not running the command in shell mode makes python26 hang when
> - # writing to hgweb output socket.
> - comspec = os.environ.get("COMSPEC", "cmd.exe")
> - args = comspec + " /c " + args
> - hp, ht, pid, tid = subprocess.CreateProcess(
> - None, args,
> - # no special security
> - None, None,
> - # Do not inherit handles
> - 0,
> - # DETACHED_PROCESS
> - 0x00000008,
> - os.environ,
> - os.getcwd(),
> - STARTUPINFO())
> - return pid
> -
> def gethgcmd():
> return [sys.executable] + sys.argv[:1]
>
> @@ -380,10 +340,6 @@ def groupmembers(name):
> # Don't support groups on Windows for now
> raise KeyError()
>
> -try:
> - # override functions with win32 versions if possible
> - from win32 import *
> -except ImportError:
> - pass
> +from win32 import *
This probably deserves a separate patch, but there's little point in
keeping both a windows.py and win32.py now.
--
Steve Borho
More information about the Mercurial-devel
mailing list