##// END OF EJS Templates
windows: do not replace sys.stdout by winstdout...
Yuya Nishihara -
r30474:b02e210a default
parent child Browse files
Show More
@@ -1,480 +1,478 b''
1 # windows.py - Windows utility function implementations for Mercurial
1 # windows.py - Windows utility function implementations for Mercurial
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import msvcrt
11 import msvcrt
12 import os
12 import os
13 import re
13 import re
14 import stat
14 import stat
15 import sys
15 import sys
16
16
17 from .i18n import _
17 from .i18n import _
18 from . import (
18 from . import (
19 encoding,
19 encoding,
20 osutil,
20 osutil,
21 win32,
21 win32,
22 )
22 )
23
23
24 try:
24 try:
25 import _winreg as winreg
25 import _winreg as winreg
26 winreg.CloseKey
26 winreg.CloseKey
27 except ImportError:
27 except ImportError:
28 import winreg
28 import winreg
29
29
30 executablepath = win32.executablepath
30 executablepath = win32.executablepath
31 getuser = win32.getuser
31 getuser = win32.getuser
32 hidewindow = win32.hidewindow
32 hidewindow = win32.hidewindow
33 makedir = win32.makedir
33 makedir = win32.makedir
34 nlinks = win32.nlinks
34 nlinks = win32.nlinks
35 oslink = win32.oslink
35 oslink = win32.oslink
36 samedevice = win32.samedevice
36 samedevice = win32.samedevice
37 samefile = win32.samefile
37 samefile = win32.samefile
38 setsignalhandler = win32.setsignalhandler
38 setsignalhandler = win32.setsignalhandler
39 spawndetached = win32.spawndetached
39 spawndetached = win32.spawndetached
40 split = os.path.split
40 split = os.path.split
41 testpid = win32.testpid
41 testpid = win32.testpid
42 unlink = win32.unlink
42 unlink = win32.unlink
43
43
44 umask = 0o022
44 umask = 0o022
45
45
46 class mixedfilemodewrapper(object):
46 class mixedfilemodewrapper(object):
47 """Wraps a file handle when it is opened in read/write mode.
47 """Wraps a file handle when it is opened in read/write mode.
48
48
49 fopen() and fdopen() on Windows have a specific-to-Windows requirement
49 fopen() and fdopen() on Windows have a specific-to-Windows requirement
50 that files opened with mode r+, w+, or a+ make a call to a file positioning
50 that files opened with mode r+, w+, or a+ make a call to a file positioning
51 function when switching between reads and writes. Without this extra call,
51 function when switching between reads and writes. Without this extra call,
52 Python will raise a not very intuitive "IOError: [Errno 0] Error."
52 Python will raise a not very intuitive "IOError: [Errno 0] Error."
53
53
54 This class wraps posixfile instances when the file is opened in read/write
54 This class wraps posixfile instances when the file is opened in read/write
55 mode and automatically adds checks or inserts appropriate file positioning
55 mode and automatically adds checks or inserts appropriate file positioning
56 calls when necessary.
56 calls when necessary.
57 """
57 """
58 OPNONE = 0
58 OPNONE = 0
59 OPREAD = 1
59 OPREAD = 1
60 OPWRITE = 2
60 OPWRITE = 2
61
61
62 def __init__(self, fp):
62 def __init__(self, fp):
63 object.__setattr__(self, '_fp', fp)
63 object.__setattr__(self, '_fp', fp)
64 object.__setattr__(self, '_lastop', 0)
64 object.__setattr__(self, '_lastop', 0)
65
65
66 def __getattr__(self, name):
66 def __getattr__(self, name):
67 return getattr(self._fp, name)
67 return getattr(self._fp, name)
68
68
69 def __setattr__(self, name, value):
69 def __setattr__(self, name, value):
70 return self._fp.__setattr__(name, value)
70 return self._fp.__setattr__(name, value)
71
71
72 def _noopseek(self):
72 def _noopseek(self):
73 self._fp.seek(0, os.SEEK_CUR)
73 self._fp.seek(0, os.SEEK_CUR)
74
74
75 def seek(self, *args, **kwargs):
75 def seek(self, *args, **kwargs):
76 object.__setattr__(self, '_lastop', self.OPNONE)
76 object.__setattr__(self, '_lastop', self.OPNONE)
77 return self._fp.seek(*args, **kwargs)
77 return self._fp.seek(*args, **kwargs)
78
78
79 def write(self, d):
79 def write(self, d):
80 if self._lastop == self.OPREAD:
80 if self._lastop == self.OPREAD:
81 self._noopseek()
81 self._noopseek()
82
82
83 object.__setattr__(self, '_lastop', self.OPWRITE)
83 object.__setattr__(self, '_lastop', self.OPWRITE)
84 return self._fp.write(d)
84 return self._fp.write(d)
85
85
86 def writelines(self, *args, **kwargs):
86 def writelines(self, *args, **kwargs):
87 if self._lastop == self.OPREAD:
87 if self._lastop == self.OPREAD:
88 self._noopeseek()
88 self._noopeseek()
89
89
90 object.__setattr__(self, '_lastop', self.OPWRITE)
90 object.__setattr__(self, '_lastop', self.OPWRITE)
91 return self._fp.writelines(*args, **kwargs)
91 return self._fp.writelines(*args, **kwargs)
92
92
93 def read(self, *args, **kwargs):
93 def read(self, *args, **kwargs):
94 if self._lastop == self.OPWRITE:
94 if self._lastop == self.OPWRITE:
95 self._noopseek()
95 self._noopseek()
96
96
97 object.__setattr__(self, '_lastop', self.OPREAD)
97 object.__setattr__(self, '_lastop', self.OPREAD)
98 return self._fp.read(*args, **kwargs)
98 return self._fp.read(*args, **kwargs)
99
99
100 def readline(self, *args, **kwargs):
100 def readline(self, *args, **kwargs):
101 if self._lastop == self.OPWRITE:
101 if self._lastop == self.OPWRITE:
102 self._noopseek()
102 self._noopseek()
103
103
104 object.__setattr__(self, '_lastop', self.OPREAD)
104 object.__setattr__(self, '_lastop', self.OPREAD)
105 return self._fp.readline(*args, **kwargs)
105 return self._fp.readline(*args, **kwargs)
106
106
107 def readlines(self, *args, **kwargs):
107 def readlines(self, *args, **kwargs):
108 if self._lastop == self.OPWRITE:
108 if self._lastop == self.OPWRITE:
109 self._noopseek()
109 self._noopseek()
110
110
111 object.__setattr__(self, '_lastop', self.OPREAD)
111 object.__setattr__(self, '_lastop', self.OPREAD)
112 return self._fp.readlines(*args, **kwargs)
112 return self._fp.readlines(*args, **kwargs)
113
113
114 def posixfile(name, mode='r', buffering=-1):
114 def posixfile(name, mode='r', buffering=-1):
115 '''Open a file with even more POSIX-like semantics'''
115 '''Open a file with even more POSIX-like semantics'''
116 try:
116 try:
117 fp = osutil.posixfile(name, mode, buffering) # may raise WindowsError
117 fp = osutil.posixfile(name, mode, buffering) # may raise WindowsError
118
118
119 # The position when opening in append mode is implementation defined, so
119 # The position when opening in append mode is implementation defined, so
120 # make it consistent with other platforms, which position at EOF.
120 # make it consistent with other platforms, which position at EOF.
121 if 'a' in mode:
121 if 'a' in mode:
122 fp.seek(0, os.SEEK_END)
122 fp.seek(0, os.SEEK_END)
123
123
124 if '+' in mode:
124 if '+' in mode:
125 return mixedfilemodewrapper(fp)
125 return mixedfilemodewrapper(fp)
126
126
127 return fp
127 return fp
128 except WindowsError as err:
128 except WindowsError as err:
129 # convert to a friendlier exception
129 # convert to a friendlier exception
130 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
130 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
131
131
132 class winstdout(object):
132 class winstdout(object):
133 '''stdout on windows misbehaves if sent through a pipe'''
133 '''stdout on windows misbehaves if sent through a pipe'''
134
134
135 def __init__(self, fp):
135 def __init__(self, fp):
136 self.fp = fp
136 self.fp = fp
137
137
138 def __getattr__(self, key):
138 def __getattr__(self, key):
139 return getattr(self.fp, key)
139 return getattr(self.fp, key)
140
140
141 def close(self):
141 def close(self):
142 try:
142 try:
143 self.fp.close()
143 self.fp.close()
144 except IOError:
144 except IOError:
145 pass
145 pass
146
146
147 def write(self, s):
147 def write(self, s):
148 try:
148 try:
149 # This is workaround for "Not enough space" error on
149 # This is workaround for "Not enough space" error on
150 # writing large size of data to console.
150 # writing large size of data to console.
151 limit = 16000
151 limit = 16000
152 l = len(s)
152 l = len(s)
153 start = 0
153 start = 0
154 self.softspace = 0
154 self.softspace = 0
155 while start < l:
155 while start < l:
156 end = start + limit
156 end = start + limit
157 self.fp.write(s[start:end])
157 self.fp.write(s[start:end])
158 start = end
158 start = end
159 except IOError as inst:
159 except IOError as inst:
160 if inst.errno != 0:
160 if inst.errno != 0:
161 raise
161 raise
162 self.close()
162 self.close()
163 raise IOError(errno.EPIPE, 'Broken pipe')
163 raise IOError(errno.EPIPE, 'Broken pipe')
164
164
165 def flush(self):
165 def flush(self):
166 try:
166 try:
167 return self.fp.flush()
167 return self.fp.flush()
168 except IOError as inst:
168 except IOError as inst:
169 if inst.errno != errno.EINVAL:
169 if inst.errno != errno.EINVAL:
170 raise
170 raise
171 self.close()
171 self.close()
172 raise IOError(errno.EPIPE, 'Broken pipe')
172 raise IOError(errno.EPIPE, 'Broken pipe')
173
173
174 sys.stdout = winstdout(sys.stdout)
175
176 def _is_win_9x():
174 def _is_win_9x():
177 '''return true if run on windows 95, 98 or me.'''
175 '''return true if run on windows 95, 98 or me.'''
178 try:
176 try:
179 return sys.getwindowsversion()[3] == 1
177 return sys.getwindowsversion()[3] == 1
180 except AttributeError:
178 except AttributeError:
181 return 'command' in os.environ.get('comspec', '')
179 return 'command' in os.environ.get('comspec', '')
182
180
183 def openhardlinks():
181 def openhardlinks():
184 return not _is_win_9x()
182 return not _is_win_9x()
185
183
186 def parsepatchoutput(output_line):
184 def parsepatchoutput(output_line):
187 """parses the output produced by patch and returns the filename"""
185 """parses the output produced by patch and returns the filename"""
188 pf = output_line[14:]
186 pf = output_line[14:]
189 if pf[0] == '`':
187 if pf[0] == '`':
190 pf = pf[1:-1] # Remove the quotes
188 pf = pf[1:-1] # Remove the quotes
191 return pf
189 return pf
192
190
193 def sshargs(sshcmd, host, user, port):
191 def sshargs(sshcmd, host, user, port):
194 '''Build argument list for ssh or Plink'''
192 '''Build argument list for ssh or Plink'''
195 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
193 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
196 args = user and ("%s@%s" % (user, host)) or host
194 args = user and ("%s@%s" % (user, host)) or host
197 return port and ("%s %s %s" % (args, pflag, port)) or args
195 return port and ("%s %s %s" % (args, pflag, port)) or args
198
196
199 def setflags(f, l, x):
197 def setflags(f, l, x):
200 pass
198 pass
201
199
202 def copymode(src, dst, mode=None):
200 def copymode(src, dst, mode=None):
203 pass
201 pass
204
202
205 def checkexec(path):
203 def checkexec(path):
206 return False
204 return False
207
205
208 def checklink(path):
206 def checklink(path):
209 return False
207 return False
210
208
211 def setbinary(fd):
209 def setbinary(fd):
212 # When run without console, pipes may expose invalid
210 # When run without console, pipes may expose invalid
213 # fileno(), usually set to -1.
211 # fileno(), usually set to -1.
214 fno = getattr(fd, 'fileno', None)
212 fno = getattr(fd, 'fileno', None)
215 if fno is not None and fno() >= 0:
213 if fno is not None and fno() >= 0:
216 msvcrt.setmode(fno(), os.O_BINARY)
214 msvcrt.setmode(fno(), os.O_BINARY)
217
215
218 def pconvert(path):
216 def pconvert(path):
219 return path.replace(os.sep, '/')
217 return path.replace(os.sep, '/')
220
218
221 def localpath(path):
219 def localpath(path):
222 return path.replace('/', '\\')
220 return path.replace('/', '\\')
223
221
224 def normpath(path):
222 def normpath(path):
225 return pconvert(os.path.normpath(path))
223 return pconvert(os.path.normpath(path))
226
224
227 def normcase(path):
225 def normcase(path):
228 return encoding.upper(path) # NTFS compares via upper()
226 return encoding.upper(path) # NTFS compares via upper()
229
227
230 # see posix.py for definitions
228 # see posix.py for definitions
231 normcasespec = encoding.normcasespecs.upper
229 normcasespec = encoding.normcasespecs.upper
232 normcasefallback = encoding.upperfallback
230 normcasefallback = encoding.upperfallback
233
231
234 def samestat(s1, s2):
232 def samestat(s1, s2):
235 return False
233 return False
236
234
237 # A sequence of backslashes is special iff it precedes a double quote:
235 # A sequence of backslashes is special iff it precedes a double quote:
238 # - if there's an even number of backslashes, the double quote is not
236 # - if there's an even number of backslashes, the double quote is not
239 # quoted (i.e. it ends the quoted region)
237 # quoted (i.e. it ends the quoted region)
240 # - if there's an odd number of backslashes, the double quote is quoted
238 # - if there's an odd number of backslashes, the double quote is quoted
241 # - in both cases, every pair of backslashes is unquoted into a single
239 # - in both cases, every pair of backslashes is unquoted into a single
242 # backslash
240 # backslash
243 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
241 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
244 # So, to quote a string, we must surround it in double quotes, double
242 # So, to quote a string, we must surround it in double quotes, double
245 # the number of backslashes that precede double quotes and add another
243 # the number of backslashes that precede double quotes and add another
246 # backslash before every double quote (being careful with the double
244 # backslash before every double quote (being careful with the double
247 # quote we've appended to the end)
245 # quote we've appended to the end)
248 _quotere = None
246 _quotere = None
249 _needsshellquote = None
247 _needsshellquote = None
250 def shellquote(s):
248 def shellquote(s):
251 r"""
249 r"""
252 >>> shellquote(r'C:\Users\xyz')
250 >>> shellquote(r'C:\Users\xyz')
253 '"C:\\Users\\xyz"'
251 '"C:\\Users\\xyz"'
254 >>> shellquote(r'C:\Users\xyz/mixed')
252 >>> shellquote(r'C:\Users\xyz/mixed')
255 '"C:\\Users\\xyz/mixed"'
253 '"C:\\Users\\xyz/mixed"'
256 >>> # Would be safe not to quote too, since it is all double backslashes
254 >>> # Would be safe not to quote too, since it is all double backslashes
257 >>> shellquote(r'C:\\Users\\xyz')
255 >>> shellquote(r'C:\\Users\\xyz')
258 '"C:\\\\Users\\\\xyz"'
256 '"C:\\\\Users\\\\xyz"'
259 >>> # But this must be quoted
257 >>> # But this must be quoted
260 >>> shellquote(r'C:\\Users\\xyz/abc')
258 >>> shellquote(r'C:\\Users\\xyz/abc')
261 '"C:\\\\Users\\\\xyz/abc"'
259 '"C:\\\\Users\\\\xyz/abc"'
262 """
260 """
263 global _quotere
261 global _quotere
264 if _quotere is None:
262 if _quotere is None:
265 _quotere = re.compile(r'(\\*)("|\\$)')
263 _quotere = re.compile(r'(\\*)("|\\$)')
266 global _needsshellquote
264 global _needsshellquote
267 if _needsshellquote is None:
265 if _needsshellquote is None:
268 # ":" is also treated as "safe character", because it is used as a part
266 # ":" is also treated as "safe character", because it is used as a part
269 # of path name on Windows. "\" is also part of a path name, but isn't
267 # of path name on Windows. "\" is also part of a path name, but isn't
270 # safe because shlex.split() (kind of) treats it as an escape char and
268 # safe because shlex.split() (kind of) treats it as an escape char and
271 # drops it. It will leave the next character, even if it is another
269 # drops it. It will leave the next character, even if it is another
272 # "\".
270 # "\".
273 _needsshellquote = re.compile(r'[^a-zA-Z0-9._:/-]').search
271 _needsshellquote = re.compile(r'[^a-zA-Z0-9._:/-]').search
274 if s and not _needsshellquote(s) and not _quotere.search(s):
272 if s and not _needsshellquote(s) and not _quotere.search(s):
275 # "s" shouldn't have to be quoted
273 # "s" shouldn't have to be quoted
276 return s
274 return s
277 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
275 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
278
276
279 def quotecommand(cmd):
277 def quotecommand(cmd):
280 """Build a command string suitable for os.popen* calls."""
278 """Build a command string suitable for os.popen* calls."""
281 if sys.version_info < (2, 7, 1):
279 if sys.version_info < (2, 7, 1):
282 # Python versions since 2.7.1 do this extra quoting themselves
280 # Python versions since 2.7.1 do this extra quoting themselves
283 return '"' + cmd + '"'
281 return '"' + cmd + '"'
284 return cmd
282 return cmd
285
283
286 def popen(command, mode='r'):
284 def popen(command, mode='r'):
287 # Work around "popen spawned process may not write to stdout
285 # Work around "popen spawned process may not write to stdout
288 # under windows"
286 # under windows"
289 # http://bugs.python.org/issue1366
287 # http://bugs.python.org/issue1366
290 command += " 2> %s" % os.devnull
288 command += " 2> %s" % os.devnull
291 return os.popen(quotecommand(command), mode)
289 return os.popen(quotecommand(command), mode)
292
290
293 def explainexit(code):
291 def explainexit(code):
294 return _("exited with status %d") % code, code
292 return _("exited with status %d") % code, code
295
293
296 # if you change this stub into a real check, please try to implement the
294 # if you change this stub into a real check, please try to implement the
297 # username and groupname functions above, too.
295 # username and groupname functions above, too.
298 def isowner(st):
296 def isowner(st):
299 return True
297 return True
300
298
301 def findexe(command):
299 def findexe(command):
302 '''Find executable for command searching like cmd.exe does.
300 '''Find executable for command searching like cmd.exe does.
303 If command is a basename then PATH is searched for command.
301 If command is a basename then PATH is searched for command.
304 PATH isn't searched if command is an absolute or relative path.
302 PATH isn't searched if command is an absolute or relative path.
305 An extension from PATHEXT is found and added if not present.
303 An extension from PATHEXT is found and added if not present.
306 If command isn't found None is returned.'''
304 If command isn't found None is returned.'''
307 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
305 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
308 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
306 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
309 if os.path.splitext(command)[1].lower() in pathexts:
307 if os.path.splitext(command)[1].lower() in pathexts:
310 pathexts = ['']
308 pathexts = ['']
311
309
312 def findexisting(pathcommand):
310 def findexisting(pathcommand):
313 'Will append extension (if needed) and return existing file'
311 'Will append extension (if needed) and return existing file'
314 for ext in pathexts:
312 for ext in pathexts:
315 executable = pathcommand + ext
313 executable = pathcommand + ext
316 if os.path.exists(executable):
314 if os.path.exists(executable):
317 return executable
315 return executable
318 return None
316 return None
319
317
320 if os.sep in command:
318 if os.sep in command:
321 return findexisting(command)
319 return findexisting(command)
322
320
323 for path in os.environ.get('PATH', '').split(os.pathsep):
321 for path in os.environ.get('PATH', '').split(os.pathsep):
324 executable = findexisting(os.path.join(path, command))
322 executable = findexisting(os.path.join(path, command))
325 if executable is not None:
323 if executable is not None:
326 return executable
324 return executable
327 return findexisting(os.path.expanduser(os.path.expandvars(command)))
325 return findexisting(os.path.expanduser(os.path.expandvars(command)))
328
326
329 _wantedkinds = set([stat.S_IFREG, stat.S_IFLNK])
327 _wantedkinds = set([stat.S_IFREG, stat.S_IFLNK])
330
328
331 def statfiles(files):
329 def statfiles(files):
332 '''Stat each file in files. Yield each stat, or None if a file
330 '''Stat each file in files. Yield each stat, or None if a file
333 does not exist or has a type we don't care about.
331 does not exist or has a type we don't care about.
334
332
335 Cluster and cache stat per directory to minimize number of OS stat calls.'''
333 Cluster and cache stat per directory to minimize number of OS stat calls.'''
336 dircache = {} # dirname -> filename -> status | None if file does not exist
334 dircache = {} # dirname -> filename -> status | None if file does not exist
337 getkind = stat.S_IFMT
335 getkind = stat.S_IFMT
338 for nf in files:
336 for nf in files:
339 nf = normcase(nf)
337 nf = normcase(nf)
340 dir, base = os.path.split(nf)
338 dir, base = os.path.split(nf)
341 if not dir:
339 if not dir:
342 dir = '.'
340 dir = '.'
343 cache = dircache.get(dir, None)
341 cache = dircache.get(dir, None)
344 if cache is None:
342 if cache is None:
345 try:
343 try:
346 dmap = dict([(normcase(n), s)
344 dmap = dict([(normcase(n), s)
347 for n, k, s in osutil.listdir(dir, True)
345 for n, k, s in osutil.listdir(dir, True)
348 if getkind(s.st_mode) in _wantedkinds])
346 if getkind(s.st_mode) in _wantedkinds])
349 except OSError as err:
347 except OSError as err:
350 # Python >= 2.5 returns ENOENT and adds winerror field
348 # Python >= 2.5 returns ENOENT and adds winerror field
351 # EINVAL is raised if dir is not a directory.
349 # EINVAL is raised if dir is not a directory.
352 if err.errno not in (errno.ENOENT, errno.EINVAL,
350 if err.errno not in (errno.ENOENT, errno.EINVAL,
353 errno.ENOTDIR):
351 errno.ENOTDIR):
354 raise
352 raise
355 dmap = {}
353 dmap = {}
356 cache = dircache.setdefault(dir, dmap)
354 cache = dircache.setdefault(dir, dmap)
357 yield cache.get(base, None)
355 yield cache.get(base, None)
358
356
359 def username(uid=None):
357 def username(uid=None):
360 """Return the name of the user with the given uid.
358 """Return the name of the user with the given uid.
361
359
362 If uid is None, return the name of the current user."""
360 If uid is None, return the name of the current user."""
363 return None
361 return None
364
362
365 def groupname(gid=None):
363 def groupname(gid=None):
366 """Return the name of the group with the given gid.
364 """Return the name of the group with the given gid.
367
365
368 If gid is None, return the name of the current group."""
366 If gid is None, return the name of the current group."""
369 return None
367 return None
370
368
371 def removedirs(name):
369 def removedirs(name):
372 """special version of os.removedirs that does not remove symlinked
370 """special version of os.removedirs that does not remove symlinked
373 directories or junction points if they actually contain files"""
371 directories or junction points if they actually contain files"""
374 if osutil.listdir(name):
372 if osutil.listdir(name):
375 return
373 return
376 os.rmdir(name)
374 os.rmdir(name)
377 head, tail = os.path.split(name)
375 head, tail = os.path.split(name)
378 if not tail:
376 if not tail:
379 head, tail = os.path.split(head)
377 head, tail = os.path.split(head)
380 while head and tail:
378 while head and tail:
381 try:
379 try:
382 if osutil.listdir(head):
380 if osutil.listdir(head):
383 return
381 return
384 os.rmdir(head)
382 os.rmdir(head)
385 except (ValueError, OSError):
383 except (ValueError, OSError):
386 break
384 break
387 head, tail = os.path.split(head)
385 head, tail = os.path.split(head)
388
386
389 def unlinkpath(f, ignoremissing=False):
387 def unlinkpath(f, ignoremissing=False):
390 """unlink and remove the directory if it is empty"""
388 """unlink and remove the directory if it is empty"""
391 try:
389 try:
392 unlink(f)
390 unlink(f)
393 except OSError as e:
391 except OSError as e:
394 if not (ignoremissing and e.errno == errno.ENOENT):
392 if not (ignoremissing and e.errno == errno.ENOENT):
395 raise
393 raise
396 # try removing directories that might now be empty
394 # try removing directories that might now be empty
397 try:
395 try:
398 removedirs(os.path.dirname(f))
396 removedirs(os.path.dirname(f))
399 except OSError:
397 except OSError:
400 pass
398 pass
401
399
402 def rename(src, dst):
400 def rename(src, dst):
403 '''atomically rename file src to dst, replacing dst if it exists'''
401 '''atomically rename file src to dst, replacing dst if it exists'''
404 try:
402 try:
405 os.rename(src, dst)
403 os.rename(src, dst)
406 except OSError as e:
404 except OSError as e:
407 if e.errno != errno.EEXIST:
405 if e.errno != errno.EEXIST:
408 raise
406 raise
409 unlink(dst)
407 unlink(dst)
410 os.rename(src, dst)
408 os.rename(src, dst)
411
409
412 def gethgcmd():
410 def gethgcmd():
413 return [sys.executable] + sys.argv[:1]
411 return [sys.executable] + sys.argv[:1]
414
412
415 def groupmembers(name):
413 def groupmembers(name):
416 # Don't support groups on Windows for now
414 # Don't support groups on Windows for now
417 raise KeyError
415 raise KeyError
418
416
419 def isexec(f):
417 def isexec(f):
420 return False
418 return False
421
419
422 class cachestat(object):
420 class cachestat(object):
423 def __init__(self, path):
421 def __init__(self, path):
424 pass
422 pass
425
423
426 def cacheable(self):
424 def cacheable(self):
427 return False
425 return False
428
426
429 def lookupreg(key, valname=None, scope=None):
427 def lookupreg(key, valname=None, scope=None):
430 ''' Look up a key/value name in the Windows registry.
428 ''' Look up a key/value name in the Windows registry.
431
429
432 valname: value name. If unspecified, the default value for the key
430 valname: value name. If unspecified, the default value for the key
433 is used.
431 is used.
434 scope: optionally specify scope for registry lookup, this can be
432 scope: optionally specify scope for registry lookup, this can be
435 a sequence of scopes to look up in order. Default (CURRENT_USER,
433 a sequence of scopes to look up in order. Default (CURRENT_USER,
436 LOCAL_MACHINE).
434 LOCAL_MACHINE).
437 '''
435 '''
438 if scope is None:
436 if scope is None:
439 scope = (winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE)
437 scope = (winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE)
440 elif not isinstance(scope, (list, tuple)):
438 elif not isinstance(scope, (list, tuple)):
441 scope = (scope,)
439 scope = (scope,)
442 for s in scope:
440 for s in scope:
443 try:
441 try:
444 val = winreg.QueryValueEx(winreg.OpenKey(s, key), valname)[0]
442 val = winreg.QueryValueEx(winreg.OpenKey(s, key), valname)[0]
445 # never let a Unicode string escape into the wild
443 # never let a Unicode string escape into the wild
446 return encoding.tolocal(val.encode('UTF-8'))
444 return encoding.tolocal(val.encode('UTF-8'))
447 except EnvironmentError:
445 except EnvironmentError:
448 pass
446 pass
449
447
450 expandglobs = True
448 expandglobs = True
451
449
452 def statislink(st):
450 def statislink(st):
453 '''check whether a stat result is a symlink'''
451 '''check whether a stat result is a symlink'''
454 return False
452 return False
455
453
456 def statisexec(st):
454 def statisexec(st):
457 '''check whether a stat result is an executable file'''
455 '''check whether a stat result is an executable file'''
458 return False
456 return False
459
457
460 def poll(fds):
458 def poll(fds):
461 # see posix.py for description
459 # see posix.py for description
462 raise NotImplementedError()
460 raise NotImplementedError()
463
461
464 def readpipe(pipe):
462 def readpipe(pipe):
465 """Read all available data from a pipe."""
463 """Read all available data from a pipe."""
466 chunks = []
464 chunks = []
467 while True:
465 while True:
468 size = win32.peekpipe(pipe)
466 size = win32.peekpipe(pipe)
469 if not size:
467 if not size:
470 break
468 break
471
469
472 s = pipe.read(size)
470 s = pipe.read(size)
473 if not s:
471 if not s:
474 break
472 break
475 chunks.append(s)
473 chunks.append(s)
476
474
477 return ''.join(chunks)
475 return ''.join(chunks)
478
476
479 def bindunixsocket(sock, path):
477 def bindunixsocket(sock, path):
480 raise NotImplementedError('unsupported platform')
478 raise NotImplementedError('unsupported platform')
General Comments 0
You need to be logged in to leave comments. Login now