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