##// END OF EJS Templates
win32: assign winstdout to sys.__stdout__ as well (issue2888)...
Idan Kamara -
r14892:d2d59271 stable
parent child Browse files
Show More
@@ -1,286 +1,286 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 i18n import _
8 from i18n import _
9 import osutil
9 import osutil
10 import errno, msvcrt, os, re, sys
10 import errno, msvcrt, os, re, sys
11
11
12 nulldev = 'NUL:'
12 nulldev = 'NUL:'
13 umask = 002
13 umask = 002
14
14
15 # wrap osutil.posixfile to provide friendlier exceptions
15 # wrap osutil.posixfile to provide friendlier exceptions
16 def posixfile(name, mode='r', buffering=-1):
16 def posixfile(name, mode='r', buffering=-1):
17 try:
17 try:
18 return osutil.posixfile(name, mode, buffering)
18 return osutil.posixfile(name, mode, buffering)
19 except WindowsError, err:
19 except WindowsError, err:
20 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
20 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
21 posixfile.__doc__ = osutil.posixfile.__doc__
21 posixfile.__doc__ = osutil.posixfile.__doc__
22
22
23 class winstdout(object):
23 class winstdout(object):
24 '''stdout on windows misbehaves if sent through a pipe'''
24 '''stdout on windows misbehaves if sent through a pipe'''
25
25
26 def __init__(self, fp):
26 def __init__(self, fp):
27 self.fp = fp
27 self.fp = fp
28
28
29 def __getattr__(self, key):
29 def __getattr__(self, key):
30 return getattr(self.fp, key)
30 return getattr(self.fp, key)
31
31
32 def close(self):
32 def close(self):
33 try:
33 try:
34 self.fp.close()
34 self.fp.close()
35 except IOError:
35 except IOError:
36 pass
36 pass
37
37
38 def write(self, s):
38 def write(self, s):
39 try:
39 try:
40 # This is workaround for "Not enough space" error on
40 # This is workaround for "Not enough space" error on
41 # writing large size of data to console.
41 # writing large size of data to console.
42 limit = 16000
42 limit = 16000
43 l = len(s)
43 l = len(s)
44 start = 0
44 start = 0
45 self.softspace = 0
45 self.softspace = 0
46 while start < l:
46 while start < l:
47 end = start + limit
47 end = start + limit
48 self.fp.write(s[start:end])
48 self.fp.write(s[start:end])
49 start = end
49 start = end
50 except IOError, inst:
50 except IOError, inst:
51 if inst.errno != 0:
51 if inst.errno != 0:
52 raise
52 raise
53 self.close()
53 self.close()
54 raise IOError(errno.EPIPE, 'Broken pipe')
54 raise IOError(errno.EPIPE, 'Broken pipe')
55
55
56 def flush(self):
56 def flush(self):
57 try:
57 try:
58 return self.fp.flush()
58 return self.fp.flush()
59 except IOError, inst:
59 except IOError, inst:
60 if inst.errno != errno.EINVAL:
60 if inst.errno != errno.EINVAL:
61 raise
61 raise
62 self.close()
62 self.close()
63 raise IOError(errno.EPIPE, 'Broken pipe')
63 raise IOError(errno.EPIPE, 'Broken pipe')
64
64
65 sys.stdout = winstdout(sys.stdout)
65 sys.__stdout__ = sys.stdout = winstdout(sys.stdout)
66
66
67 def _is_win_9x():
67 def _is_win_9x():
68 '''return true if run on windows 95, 98 or me.'''
68 '''return true if run on windows 95, 98 or me.'''
69 try:
69 try:
70 return sys.getwindowsversion()[3] == 1
70 return sys.getwindowsversion()[3] == 1
71 except AttributeError:
71 except AttributeError:
72 return 'command' in os.environ.get('comspec', '')
72 return 'command' in os.environ.get('comspec', '')
73
73
74 def openhardlinks():
74 def openhardlinks():
75 return not _is_win_9x()
75 return not _is_win_9x()
76
76
77 def parsepatchoutput(output_line):
77 def parsepatchoutput(output_line):
78 """parses the output produced by patch and returns the filename"""
78 """parses the output produced by patch and returns the filename"""
79 pf = output_line[14:]
79 pf = output_line[14:]
80 if pf[0] == '`':
80 if pf[0] == '`':
81 pf = pf[1:-1] # Remove the quotes
81 pf = pf[1:-1] # Remove the quotes
82 return pf
82 return pf
83
83
84 def sshargs(sshcmd, host, user, port):
84 def sshargs(sshcmd, host, user, port):
85 '''Build argument list for ssh or Plink'''
85 '''Build argument list for ssh or Plink'''
86 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
86 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
87 args = user and ("%s@%s" % (user, host)) or host
87 args = user and ("%s@%s" % (user, host)) or host
88 return port and ("%s %s %s" % (args, pflag, port)) or args
88 return port and ("%s %s %s" % (args, pflag, port)) or args
89
89
90 def setflags(f, l, x):
90 def setflags(f, l, x):
91 pass
91 pass
92
92
93 def checkexec(path):
93 def checkexec(path):
94 return False
94 return False
95
95
96 def checklink(path):
96 def checklink(path):
97 return False
97 return False
98
98
99 def setbinary(fd):
99 def setbinary(fd):
100 # When run without console, pipes may expose invalid
100 # When run without console, pipes may expose invalid
101 # fileno(), usually set to -1.
101 # fileno(), usually set to -1.
102 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
102 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
103 msvcrt.setmode(fd.fileno(), os.O_BINARY)
103 msvcrt.setmode(fd.fileno(), os.O_BINARY)
104
104
105 def pconvert(path):
105 def pconvert(path):
106 return '/'.join(path.split(os.sep))
106 return '/'.join(path.split(os.sep))
107
107
108 def localpath(path):
108 def localpath(path):
109 return path.replace('/', '\\')
109 return path.replace('/', '\\')
110
110
111 def normpath(path):
111 def normpath(path):
112 return pconvert(os.path.normpath(path))
112 return pconvert(os.path.normpath(path))
113
113
114 def realpath(path):
114 def realpath(path):
115 '''
115 '''
116 Returns the true, canonical file system path equivalent to the given
116 Returns the true, canonical file system path equivalent to the given
117 path.
117 path.
118 '''
118 '''
119 # TODO: There may be a more clever way to do this that also handles other,
119 # TODO: There may be a more clever way to do this that also handles other,
120 # less common file systems.
120 # less common file systems.
121 return os.path.normpath(os.path.normcase(os.path.realpath(path)))
121 return os.path.normpath(os.path.normcase(os.path.realpath(path)))
122
122
123 def samestat(s1, s2):
123 def samestat(s1, s2):
124 return False
124 return False
125
125
126 # A sequence of backslashes is special iff it precedes a double quote:
126 # A sequence of backslashes is special iff it precedes a double quote:
127 # - if there's an even number of backslashes, the double quote is not
127 # - if there's an even number of backslashes, the double quote is not
128 # quoted (i.e. it ends the quoted region)
128 # quoted (i.e. it ends the quoted region)
129 # - if there's an odd number of backslashes, the double quote is quoted
129 # - if there's an odd number of backslashes, the double quote is quoted
130 # - in both cases, every pair of backslashes is unquoted into a single
130 # - in both cases, every pair of backslashes is unquoted into a single
131 # backslash
131 # backslash
132 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
132 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
133 # So, to quote a string, we must surround it in double quotes, double
133 # So, to quote a string, we must surround it in double quotes, double
134 # the number of backslashes that preceed double quotes and add another
134 # the number of backslashes that preceed double quotes and add another
135 # backslash before every double quote (being careful with the double
135 # backslash before every double quote (being careful with the double
136 # quote we've appended to the end)
136 # quote we've appended to the end)
137 _quotere = None
137 _quotere = None
138 def shellquote(s):
138 def shellquote(s):
139 global _quotere
139 global _quotere
140 if _quotere is None:
140 if _quotere is None:
141 _quotere = re.compile(r'(\\*)("|\\$)')
141 _quotere = re.compile(r'(\\*)("|\\$)')
142 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
142 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
143
143
144 def quotecommand(cmd):
144 def quotecommand(cmd):
145 """Build a command string suitable for os.popen* calls."""
145 """Build a command string suitable for os.popen* calls."""
146 if sys.version_info < (2, 7, 1):
146 if sys.version_info < (2, 7, 1):
147 # Python versions since 2.7.1 do this extra quoting themselves
147 # Python versions since 2.7.1 do this extra quoting themselves
148 return '"' + cmd + '"'
148 return '"' + cmd + '"'
149 return cmd
149 return cmd
150
150
151 def popen(command, mode='r'):
151 def popen(command, mode='r'):
152 # Work around "popen spawned process may not write to stdout
152 # Work around "popen spawned process may not write to stdout
153 # under windows"
153 # under windows"
154 # http://bugs.python.org/issue1366
154 # http://bugs.python.org/issue1366
155 command += " 2> %s" % nulldev
155 command += " 2> %s" % nulldev
156 return os.popen(quotecommand(command), mode)
156 return os.popen(quotecommand(command), mode)
157
157
158 def explainexit(code):
158 def explainexit(code):
159 return _("exited with status %d") % code, code
159 return _("exited with status %d") % code, code
160
160
161 # if you change this stub into a real check, please try to implement the
161 # if you change this stub into a real check, please try to implement the
162 # username and groupname functions above, too.
162 # username and groupname functions above, too.
163 def isowner(st):
163 def isowner(st):
164 return True
164 return True
165
165
166 def findexe(command):
166 def findexe(command):
167 '''Find executable for command searching like cmd.exe does.
167 '''Find executable for command searching like cmd.exe does.
168 If command is a basename then PATH is searched for command.
168 If command is a basename then PATH is searched for command.
169 PATH isn't searched if command is an absolute or relative path.
169 PATH isn't searched if command is an absolute or relative path.
170 An extension from PATHEXT is found and added if not present.
170 An extension from PATHEXT is found and added if not present.
171 If command isn't found None is returned.'''
171 If command isn't found None is returned.'''
172 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
172 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
173 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
173 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
174 if os.path.splitext(command)[1].lower() in pathexts:
174 if os.path.splitext(command)[1].lower() in pathexts:
175 pathexts = ['']
175 pathexts = ['']
176
176
177 def findexisting(pathcommand):
177 def findexisting(pathcommand):
178 'Will append extension (if needed) and return existing file'
178 'Will append extension (if needed) and return existing file'
179 for ext in pathexts:
179 for ext in pathexts:
180 executable = pathcommand + ext
180 executable = pathcommand + ext
181 if os.path.exists(executable):
181 if os.path.exists(executable):
182 return executable
182 return executable
183 return None
183 return None
184
184
185 if os.sep in command:
185 if os.sep in command:
186 return findexisting(command)
186 return findexisting(command)
187
187
188 for path in os.environ.get('PATH', '').split(os.pathsep):
188 for path in os.environ.get('PATH', '').split(os.pathsep):
189 executable = findexisting(os.path.join(path, command))
189 executable = findexisting(os.path.join(path, command))
190 if executable is not None:
190 if executable is not None:
191 return executable
191 return executable
192 return findexisting(os.path.expanduser(os.path.expandvars(command)))
192 return findexisting(os.path.expanduser(os.path.expandvars(command)))
193
193
194 def statfiles(files):
194 def statfiles(files):
195 '''Stat each file in files and yield stat or None if file does not exist.
195 '''Stat each file in files and yield stat or None if file does not exist.
196 Cluster and cache stat per directory to minimize number of OS stat calls.'''
196 Cluster and cache stat per directory to minimize number of OS stat calls.'''
197 ncase = os.path.normcase
197 ncase = os.path.normcase
198 dircache = {} # dirname -> filename -> status | None if file does not exist
198 dircache = {} # dirname -> filename -> status | None if file does not exist
199 for nf in files:
199 for nf in files:
200 nf = ncase(nf)
200 nf = ncase(nf)
201 dir, base = os.path.split(nf)
201 dir, base = os.path.split(nf)
202 if not dir:
202 if not dir:
203 dir = '.'
203 dir = '.'
204 cache = dircache.get(dir, None)
204 cache = dircache.get(dir, None)
205 if cache is None:
205 if cache is None:
206 try:
206 try:
207 dmap = dict([(ncase(n), s)
207 dmap = dict([(ncase(n), s)
208 for n, k, s in osutil.listdir(dir, True)])
208 for n, k, s in osutil.listdir(dir, True)])
209 except OSError, err:
209 except OSError, err:
210 # handle directory not found in Python version prior to 2.5
210 # handle directory not found in Python version prior to 2.5
211 # Python <= 2.4 returns native Windows code 3 in errno
211 # Python <= 2.4 returns native Windows code 3 in errno
212 # Python >= 2.5 returns ENOENT and adds winerror field
212 # Python >= 2.5 returns ENOENT and adds winerror field
213 # EINVAL is raised if dir is not a directory.
213 # EINVAL is raised if dir is not a directory.
214 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
214 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
215 errno.ENOTDIR):
215 errno.ENOTDIR):
216 raise
216 raise
217 dmap = {}
217 dmap = {}
218 cache = dircache.setdefault(dir, dmap)
218 cache = dircache.setdefault(dir, dmap)
219 yield cache.get(base, None)
219 yield cache.get(base, None)
220
220
221 def username(uid=None):
221 def username(uid=None):
222 """Return the name of the user with the given uid.
222 """Return the name of the user with the given uid.
223
223
224 If uid is None, return the name of the current user."""
224 If uid is None, return the name of the current user."""
225 return None
225 return None
226
226
227 def groupname(gid=None):
227 def groupname(gid=None):
228 """Return the name of the group with the given gid.
228 """Return the name of the group with the given gid.
229
229
230 If gid is None, return the name of the current group."""
230 If gid is None, return the name of the current group."""
231 return None
231 return None
232
232
233 def _removedirs(name):
233 def _removedirs(name):
234 """special version of os.removedirs that does not remove symlinked
234 """special version of os.removedirs that does not remove symlinked
235 directories or junction points if they actually contain files"""
235 directories or junction points if they actually contain files"""
236 if osutil.listdir(name):
236 if osutil.listdir(name):
237 return
237 return
238 os.rmdir(name)
238 os.rmdir(name)
239 head, tail = os.path.split(name)
239 head, tail = os.path.split(name)
240 if not tail:
240 if not tail:
241 head, tail = os.path.split(head)
241 head, tail = os.path.split(head)
242 while head and tail:
242 while head and tail:
243 try:
243 try:
244 if osutil.listdir(head):
244 if osutil.listdir(head):
245 return
245 return
246 os.rmdir(head)
246 os.rmdir(head)
247 except (ValueError, OSError):
247 except (ValueError, OSError):
248 break
248 break
249 head, tail = os.path.split(head)
249 head, tail = os.path.split(head)
250
250
251 def unlinkpath(f):
251 def unlinkpath(f):
252 """unlink and remove the directory if it is empty"""
252 """unlink and remove the directory if it is empty"""
253 unlink(f)
253 unlink(f)
254 # try removing directories that might now be empty
254 # try removing directories that might now be empty
255 try:
255 try:
256 _removedirs(os.path.dirname(f))
256 _removedirs(os.path.dirname(f))
257 except OSError:
257 except OSError:
258 pass
258 pass
259
259
260 def rename(src, dst):
260 def rename(src, dst):
261 '''atomically rename file src to dst, replacing dst if it exists'''
261 '''atomically rename file src to dst, replacing dst if it exists'''
262 try:
262 try:
263 os.rename(src, dst)
263 os.rename(src, dst)
264 except OSError, e:
264 except OSError, e:
265 if e.errno != errno.EEXIST:
265 if e.errno != errno.EEXIST:
266 raise
266 raise
267 unlink(dst)
267 unlink(dst)
268 os.rename(src, dst)
268 os.rename(src, dst)
269
269
270 def gethgcmd():
270 def gethgcmd():
271 return [sys.executable] + sys.argv[:1]
271 return [sys.executable] + sys.argv[:1]
272
272
273 def termwidth():
273 def termwidth():
274 # cmd.exe does not handle CR like a unix console, the CR is
274 # cmd.exe does not handle CR like a unix console, the CR is
275 # counted in the line length. On 80 columns consoles, if 80
275 # counted in the line length. On 80 columns consoles, if 80
276 # characters are written, the following CR won't apply on the
276 # characters are written, the following CR won't apply on the
277 # current line but on the new one. Keep room for it.
277 # current line but on the new one. Keep room for it.
278 return 79
278 return 79
279
279
280 def groupmembers(name):
280 def groupmembers(name):
281 # Don't support groups on Windows for now
281 # Don't support groups on Windows for now
282 raise KeyError()
282 raise KeyError()
283
283
284 from win32 import *
284 from win32 import *
285
285
286 expandglobs = True
286 expandglobs = True
General Comments 0
You need to be logged in to leave comments. Login now