##// END OF EJS Templates
windows: suppress pytype warnings for Windows imports and functions...
Matt Harbison -
r44207:fe73ec69 default
parent child Browse files
Show More
@@ -1,506 +1,506 b''
1 # pycompat.py - portability shim for python 3
1 # pycompat.py - portability shim for python 3
2 #
2 #
3 # This software may be used and distributed according to the terms of the
3 # This software may be used and distributed according to the terms of the
4 # GNU General Public License version 2 or any later version.
4 # GNU General Public License version 2 or any later version.
5
5
6 """Mercurial portability shim for python 3.
6 """Mercurial portability shim for python 3.
7
7
8 This contains aliases to hide python version-specific details from the core.
8 This contains aliases to hide python version-specific details from the core.
9 """
9 """
10
10
11 from __future__ import absolute_import
11 from __future__ import absolute_import
12
12
13 import getopt
13 import getopt
14 import inspect
14 import inspect
15 import json
15 import json
16 import os
16 import os
17 import shlex
17 import shlex
18 import sys
18 import sys
19 import tempfile
19 import tempfile
20
20
21 ispy3 = sys.version_info[0] >= 3
21 ispy3 = sys.version_info[0] >= 3
22 ispypy = '__pypy__' in sys.builtin_module_names
22 ispypy = '__pypy__' in sys.builtin_module_names
23
23
24 if not ispy3:
24 if not ispy3:
25 import cookielib
25 import cookielib
26 import cPickle as pickle
26 import cPickle as pickle
27 import httplib
27 import httplib
28 import Queue as queue
28 import Queue as queue
29 import SocketServer as socketserver
29 import SocketServer as socketserver
30 import xmlrpclib
30 import xmlrpclib
31
31
32 from .thirdparty.concurrent import futures
32 from .thirdparty.concurrent import futures
33
33
34 def future_set_exception_info(f, exc_info):
34 def future_set_exception_info(f, exc_info):
35 f.set_exception_info(*exc_info)
35 f.set_exception_info(*exc_info)
36
36
37
37
38 else:
38 else:
39 import concurrent.futures as futures
39 import concurrent.futures as futures
40 import http.cookiejar as cookielib
40 import http.cookiejar as cookielib
41 import http.client as httplib
41 import http.client as httplib
42 import pickle
42 import pickle
43 import queue as queue
43 import queue as queue
44 import socketserver
44 import socketserver
45 import xmlrpc.client as xmlrpclib
45 import xmlrpc.client as xmlrpclib
46
46
47 def future_set_exception_info(f, exc_info):
47 def future_set_exception_info(f, exc_info):
48 f.set_exception(exc_info[0])
48 f.set_exception(exc_info[0])
49
49
50
50
51 def identity(a):
51 def identity(a):
52 return a
52 return a
53
53
54
54
55 def _rapply(f, xs):
55 def _rapply(f, xs):
56 if xs is None:
56 if xs is None:
57 # assume None means non-value of optional data
57 # assume None means non-value of optional data
58 return xs
58 return xs
59 if isinstance(xs, (list, set, tuple)):
59 if isinstance(xs, (list, set, tuple)):
60 return type(xs)(_rapply(f, x) for x in xs)
60 return type(xs)(_rapply(f, x) for x in xs)
61 if isinstance(xs, dict):
61 if isinstance(xs, dict):
62 return type(xs)((_rapply(f, k), _rapply(f, v)) for k, v in xs.items())
62 return type(xs)((_rapply(f, k), _rapply(f, v)) for k, v in xs.items())
63 return f(xs)
63 return f(xs)
64
64
65
65
66 def rapply(f, xs):
66 def rapply(f, xs):
67 """Apply function recursively to every item preserving the data structure
67 """Apply function recursively to every item preserving the data structure
68
68
69 >>> def f(x):
69 >>> def f(x):
70 ... return 'f(%s)' % x
70 ... return 'f(%s)' % x
71 >>> rapply(f, None) is None
71 >>> rapply(f, None) is None
72 True
72 True
73 >>> rapply(f, 'a')
73 >>> rapply(f, 'a')
74 'f(a)'
74 'f(a)'
75 >>> rapply(f, {'a'}) == {'f(a)'}
75 >>> rapply(f, {'a'}) == {'f(a)'}
76 True
76 True
77 >>> rapply(f, ['a', 'b', None, {'c': 'd'}, []])
77 >>> rapply(f, ['a', 'b', None, {'c': 'd'}, []])
78 ['f(a)', 'f(b)', None, {'f(c)': 'f(d)'}, []]
78 ['f(a)', 'f(b)', None, {'f(c)': 'f(d)'}, []]
79
79
80 >>> xs = [object()]
80 >>> xs = [object()]
81 >>> rapply(identity, xs) is xs
81 >>> rapply(identity, xs) is xs
82 True
82 True
83 """
83 """
84 if f is identity:
84 if f is identity:
85 # fast path mainly for py2
85 # fast path mainly for py2
86 return xs
86 return xs
87 return _rapply(f, xs)
87 return _rapply(f, xs)
88
88
89
89
90 if ispy3:
90 if ispy3:
91 import builtins
91 import builtins
92 import codecs
92 import codecs
93 import functools
93 import functools
94 import io
94 import io
95 import struct
95 import struct
96
96
97 if os.name == r'nt' and sys.version_info >= (3, 6):
97 if os.name == r'nt' and sys.version_info >= (3, 6):
98 # MBCS (or ANSI) filesystem encoding must be used as before.
98 # MBCS (or ANSI) filesystem encoding must be used as before.
99 # Otherwise non-ASCII filenames in existing repositories would be
99 # Otherwise non-ASCII filenames in existing repositories would be
100 # corrupted.
100 # corrupted.
101 # This must be set once prior to any fsencode/fsdecode calls.
101 # This must be set once prior to any fsencode/fsdecode calls.
102 sys._enablelegacywindowsfsencoding()
102 sys._enablelegacywindowsfsencoding() # pytype: disable=module-attr
103
103
104 fsencode = os.fsencode
104 fsencode = os.fsencode
105 fsdecode = os.fsdecode
105 fsdecode = os.fsdecode
106 oscurdir = os.curdir.encode('ascii')
106 oscurdir = os.curdir.encode('ascii')
107 oslinesep = os.linesep.encode('ascii')
107 oslinesep = os.linesep.encode('ascii')
108 osname = os.name.encode('ascii')
108 osname = os.name.encode('ascii')
109 ospathsep = os.pathsep.encode('ascii')
109 ospathsep = os.pathsep.encode('ascii')
110 ospardir = os.pardir.encode('ascii')
110 ospardir = os.pardir.encode('ascii')
111 ossep = os.sep.encode('ascii')
111 ossep = os.sep.encode('ascii')
112 osaltsep = os.altsep
112 osaltsep = os.altsep
113 if osaltsep:
113 if osaltsep:
114 osaltsep = osaltsep.encode('ascii')
114 osaltsep = osaltsep.encode('ascii')
115
115
116 sysplatform = sys.platform.encode('ascii')
116 sysplatform = sys.platform.encode('ascii')
117 sysexecutable = sys.executable
117 sysexecutable = sys.executable
118 if sysexecutable:
118 if sysexecutable:
119 sysexecutable = os.fsencode(sysexecutable)
119 sysexecutable = os.fsencode(sysexecutable)
120 bytesio = io.BytesIO
120 bytesio = io.BytesIO
121 # TODO deprecate stringio name, as it is a lie on Python 3.
121 # TODO deprecate stringio name, as it is a lie on Python 3.
122 stringio = bytesio
122 stringio = bytesio
123
123
124 def maplist(*args):
124 def maplist(*args):
125 return list(map(*args))
125 return list(map(*args))
126
126
127 def rangelist(*args):
127 def rangelist(*args):
128 return list(range(*args))
128 return list(range(*args))
129
129
130 def ziplist(*args):
130 def ziplist(*args):
131 return list(zip(*args))
131 return list(zip(*args))
132
132
133 rawinput = input
133 rawinput = input
134 getargspec = inspect.getfullargspec
134 getargspec = inspect.getfullargspec
135
135
136 long = int
136 long = int
137
137
138 # TODO: .buffer might not exist if std streams were replaced; we'll need
138 # TODO: .buffer might not exist if std streams were replaced; we'll need
139 # a silly wrapper to make a bytes stream backed by a unicode one.
139 # a silly wrapper to make a bytes stream backed by a unicode one.
140 stdin = sys.stdin.buffer
140 stdin = sys.stdin.buffer
141 stdout = sys.stdout.buffer
141 stdout = sys.stdout.buffer
142 stderr = sys.stderr.buffer
142 stderr = sys.stderr.buffer
143
143
144 # Since Python 3 converts argv to wchar_t type by Py_DecodeLocale() on Unix,
144 # Since Python 3 converts argv to wchar_t type by Py_DecodeLocale() on Unix,
145 # we can use os.fsencode() to get back bytes argv.
145 # we can use os.fsencode() to get back bytes argv.
146 #
146 #
147 # https://hg.python.org/cpython/file/v3.5.1/Programs/python.c#l55
147 # https://hg.python.org/cpython/file/v3.5.1/Programs/python.c#l55
148 #
148 #
149 # On Windows, the native argv is unicode and is converted to MBCS bytes
149 # On Windows, the native argv is unicode and is converted to MBCS bytes
150 # since we do enable the legacy filesystem encoding.
150 # since we do enable the legacy filesystem encoding.
151 if getattr(sys, 'argv', None) is not None:
151 if getattr(sys, 'argv', None) is not None:
152 sysargv = list(map(os.fsencode, sys.argv))
152 sysargv = list(map(os.fsencode, sys.argv))
153
153
154 bytechr = struct.Struct('>B').pack
154 bytechr = struct.Struct('>B').pack
155 byterepr = b'%r'.__mod__
155 byterepr = b'%r'.__mod__
156
156
157 class bytestr(bytes):
157 class bytestr(bytes):
158 """A bytes which mostly acts as a Python 2 str
158 """A bytes which mostly acts as a Python 2 str
159
159
160 >>> bytestr(), bytestr(bytearray(b'foo')), bytestr(u'ascii'), bytestr(1)
160 >>> bytestr(), bytestr(bytearray(b'foo')), bytestr(u'ascii'), bytestr(1)
161 ('', 'foo', 'ascii', '1')
161 ('', 'foo', 'ascii', '1')
162 >>> s = bytestr(b'foo')
162 >>> s = bytestr(b'foo')
163 >>> assert s is bytestr(s)
163 >>> assert s is bytestr(s)
164
164
165 __bytes__() should be called if provided:
165 __bytes__() should be called if provided:
166
166
167 >>> class bytesable(object):
167 >>> class bytesable(object):
168 ... def __bytes__(self):
168 ... def __bytes__(self):
169 ... return b'bytes'
169 ... return b'bytes'
170 >>> bytestr(bytesable())
170 >>> bytestr(bytesable())
171 'bytes'
171 'bytes'
172
172
173 There's no implicit conversion from non-ascii str as its encoding is
173 There's no implicit conversion from non-ascii str as its encoding is
174 unknown:
174 unknown:
175
175
176 >>> bytestr(chr(0x80)) # doctest: +ELLIPSIS
176 >>> bytestr(chr(0x80)) # doctest: +ELLIPSIS
177 Traceback (most recent call last):
177 Traceback (most recent call last):
178 ...
178 ...
179 UnicodeEncodeError: ...
179 UnicodeEncodeError: ...
180
180
181 Comparison between bytestr and bytes should work:
181 Comparison between bytestr and bytes should work:
182
182
183 >>> assert bytestr(b'foo') == b'foo'
183 >>> assert bytestr(b'foo') == b'foo'
184 >>> assert b'foo' == bytestr(b'foo')
184 >>> assert b'foo' == bytestr(b'foo')
185 >>> assert b'f' in bytestr(b'foo')
185 >>> assert b'f' in bytestr(b'foo')
186 >>> assert bytestr(b'f') in b'foo'
186 >>> assert bytestr(b'f') in b'foo'
187
187
188 Sliced elements should be bytes, not integer:
188 Sliced elements should be bytes, not integer:
189
189
190 >>> s[1], s[:2]
190 >>> s[1], s[:2]
191 (b'o', b'fo')
191 (b'o', b'fo')
192 >>> list(s), list(reversed(s))
192 >>> list(s), list(reversed(s))
193 ([b'f', b'o', b'o'], [b'o', b'o', b'f'])
193 ([b'f', b'o', b'o'], [b'o', b'o', b'f'])
194
194
195 As bytestr type isn't propagated across operations, you need to cast
195 As bytestr type isn't propagated across operations, you need to cast
196 bytes to bytestr explicitly:
196 bytes to bytestr explicitly:
197
197
198 >>> s = bytestr(b'foo').upper()
198 >>> s = bytestr(b'foo').upper()
199 >>> t = bytestr(s)
199 >>> t = bytestr(s)
200 >>> s[0], t[0]
200 >>> s[0], t[0]
201 (70, b'F')
201 (70, b'F')
202
202
203 Be careful to not pass a bytestr object to a function which expects
203 Be careful to not pass a bytestr object to a function which expects
204 bytearray-like behavior.
204 bytearray-like behavior.
205
205
206 >>> t = bytes(t) # cast to bytes
206 >>> t = bytes(t) # cast to bytes
207 >>> assert type(t) is bytes
207 >>> assert type(t) is bytes
208 """
208 """
209
209
210 def __new__(cls, s=b''):
210 def __new__(cls, s=b''):
211 if isinstance(s, bytestr):
211 if isinstance(s, bytestr):
212 return s
212 return s
213 if not isinstance(
213 if not isinstance(
214 s, (bytes, bytearray)
214 s, (bytes, bytearray)
215 ) and not hasattr( # hasattr-py3-only
215 ) and not hasattr( # hasattr-py3-only
216 s, u'__bytes__'
216 s, u'__bytes__'
217 ):
217 ):
218 s = str(s).encode('ascii')
218 s = str(s).encode('ascii')
219 return bytes.__new__(cls, s)
219 return bytes.__new__(cls, s)
220
220
221 def __getitem__(self, key):
221 def __getitem__(self, key):
222 s = bytes.__getitem__(self, key)
222 s = bytes.__getitem__(self, key)
223 if not isinstance(s, bytes):
223 if not isinstance(s, bytes):
224 s = bytechr(s)
224 s = bytechr(s)
225 return s
225 return s
226
226
227 def __iter__(self):
227 def __iter__(self):
228 return iterbytestr(bytes.__iter__(self))
228 return iterbytestr(bytes.__iter__(self))
229
229
230 def __repr__(self):
230 def __repr__(self):
231 return bytes.__repr__(self)[1:] # drop b''
231 return bytes.__repr__(self)[1:] # drop b''
232
232
233 def iterbytestr(s):
233 def iterbytestr(s):
234 """Iterate bytes as if it were a str object of Python 2"""
234 """Iterate bytes as if it were a str object of Python 2"""
235 return map(bytechr, s)
235 return map(bytechr, s)
236
236
237 def maybebytestr(s):
237 def maybebytestr(s):
238 """Promote bytes to bytestr"""
238 """Promote bytes to bytestr"""
239 if isinstance(s, bytes):
239 if isinstance(s, bytes):
240 return bytestr(s)
240 return bytestr(s)
241 return s
241 return s
242
242
243 def sysbytes(s):
243 def sysbytes(s):
244 """Convert an internal str (e.g. keyword, __doc__) back to bytes
244 """Convert an internal str (e.g. keyword, __doc__) back to bytes
245
245
246 This never raises UnicodeEncodeError, but only ASCII characters
246 This never raises UnicodeEncodeError, but only ASCII characters
247 can be round-trip by sysstr(sysbytes(s)).
247 can be round-trip by sysstr(sysbytes(s)).
248 """
248 """
249 return s.encode('utf-8')
249 return s.encode('utf-8')
250
250
251 def sysstr(s):
251 def sysstr(s):
252 """Return a keyword str to be passed to Python functions such as
252 """Return a keyword str to be passed to Python functions such as
253 getattr() and str.encode()
253 getattr() and str.encode()
254
254
255 This never raises UnicodeDecodeError. Non-ascii characters are
255 This never raises UnicodeDecodeError. Non-ascii characters are
256 considered invalid and mapped to arbitrary but unique code points
256 considered invalid and mapped to arbitrary but unique code points
257 such that 'sysstr(a) != sysstr(b)' for all 'a != b'.
257 such that 'sysstr(a) != sysstr(b)' for all 'a != b'.
258 """
258 """
259 if isinstance(s, builtins.str):
259 if isinstance(s, builtins.str):
260 return s
260 return s
261 return s.decode('latin-1')
261 return s.decode('latin-1')
262
262
263 def strurl(url):
263 def strurl(url):
264 """Converts a bytes url back to str"""
264 """Converts a bytes url back to str"""
265 if isinstance(url, bytes):
265 if isinstance(url, bytes):
266 return url.decode('ascii')
266 return url.decode('ascii')
267 return url
267 return url
268
268
269 def bytesurl(url):
269 def bytesurl(url):
270 """Converts a str url to bytes by encoding in ascii"""
270 """Converts a str url to bytes by encoding in ascii"""
271 if isinstance(url, str):
271 if isinstance(url, str):
272 return url.encode('ascii')
272 return url.encode('ascii')
273 return url
273 return url
274
274
275 def raisewithtb(exc, tb):
275 def raisewithtb(exc, tb):
276 """Raise exception with the given traceback"""
276 """Raise exception with the given traceback"""
277 raise exc.with_traceback(tb)
277 raise exc.with_traceback(tb)
278
278
279 def getdoc(obj):
279 def getdoc(obj):
280 """Get docstring as bytes; may be None so gettext() won't confuse it
280 """Get docstring as bytes; may be None so gettext() won't confuse it
281 with _('')"""
281 with _('')"""
282 doc = getattr(obj, '__doc__', None)
282 doc = getattr(obj, '__doc__', None)
283 if doc is None:
283 if doc is None:
284 return doc
284 return doc
285 return sysbytes(doc)
285 return sysbytes(doc)
286
286
287 def _wrapattrfunc(f):
287 def _wrapattrfunc(f):
288 @functools.wraps(f)
288 @functools.wraps(f)
289 def w(object, name, *args):
289 def w(object, name, *args):
290 return f(object, sysstr(name), *args)
290 return f(object, sysstr(name), *args)
291
291
292 return w
292 return w
293
293
294 # these wrappers are automagically imported by hgloader
294 # these wrappers are automagically imported by hgloader
295 delattr = _wrapattrfunc(builtins.delattr)
295 delattr = _wrapattrfunc(builtins.delattr)
296 getattr = _wrapattrfunc(builtins.getattr)
296 getattr = _wrapattrfunc(builtins.getattr)
297 hasattr = _wrapattrfunc(builtins.hasattr)
297 hasattr = _wrapattrfunc(builtins.hasattr)
298 setattr = _wrapattrfunc(builtins.setattr)
298 setattr = _wrapattrfunc(builtins.setattr)
299 xrange = builtins.range
299 xrange = builtins.range
300 unicode = str
300 unicode = str
301
301
302 def open(name, mode=b'r', buffering=-1, encoding=None):
302 def open(name, mode=b'r', buffering=-1, encoding=None):
303 return builtins.open(name, sysstr(mode), buffering, encoding)
303 return builtins.open(name, sysstr(mode), buffering, encoding)
304
304
305 safehasattr = _wrapattrfunc(builtins.hasattr)
305 safehasattr = _wrapattrfunc(builtins.hasattr)
306
306
307 def _getoptbwrapper(orig, args, shortlist, namelist):
307 def _getoptbwrapper(orig, args, shortlist, namelist):
308 """
308 """
309 Takes bytes arguments, converts them to unicode, pass them to
309 Takes bytes arguments, converts them to unicode, pass them to
310 getopt.getopt(), convert the returned values back to bytes and then
310 getopt.getopt(), convert the returned values back to bytes and then
311 return them for Python 3 compatibility as getopt.getopt() don't accepts
311 return them for Python 3 compatibility as getopt.getopt() don't accepts
312 bytes on Python 3.
312 bytes on Python 3.
313 """
313 """
314 args = [a.decode('latin-1') for a in args]
314 args = [a.decode('latin-1') for a in args]
315 shortlist = shortlist.decode('latin-1')
315 shortlist = shortlist.decode('latin-1')
316 namelist = [a.decode('latin-1') for a in namelist]
316 namelist = [a.decode('latin-1') for a in namelist]
317 opts, args = orig(args, shortlist, namelist)
317 opts, args = orig(args, shortlist, namelist)
318 opts = [(a[0].encode('latin-1'), a[1].encode('latin-1')) for a in opts]
318 opts = [(a[0].encode('latin-1'), a[1].encode('latin-1')) for a in opts]
319 args = [a.encode('latin-1') for a in args]
319 args = [a.encode('latin-1') for a in args]
320 return opts, args
320 return opts, args
321
321
322 def strkwargs(dic):
322 def strkwargs(dic):
323 """
323 """
324 Converts the keys of a python dictonary to str i.e. unicodes so that
324 Converts the keys of a python dictonary to str i.e. unicodes so that
325 they can be passed as keyword arguments as dictonaries with bytes keys
325 they can be passed as keyword arguments as dictonaries with bytes keys
326 can't be passed as keyword arguments to functions on Python 3.
326 can't be passed as keyword arguments to functions on Python 3.
327 """
327 """
328 dic = dict((k.decode('latin-1'), v) for k, v in dic.items())
328 dic = dict((k.decode('latin-1'), v) for k, v in dic.items())
329 return dic
329 return dic
330
330
331 def byteskwargs(dic):
331 def byteskwargs(dic):
332 """
332 """
333 Converts keys of python dictonaries to bytes as they were converted to
333 Converts keys of python dictonaries to bytes as they were converted to
334 str to pass that dictonary as a keyword argument on Python 3.
334 str to pass that dictonary as a keyword argument on Python 3.
335 """
335 """
336 dic = dict((k.encode('latin-1'), v) for k, v in dic.items())
336 dic = dict((k.encode('latin-1'), v) for k, v in dic.items())
337 return dic
337 return dic
338
338
339 # TODO: handle shlex.shlex().
339 # TODO: handle shlex.shlex().
340 def shlexsplit(s, comments=False, posix=True):
340 def shlexsplit(s, comments=False, posix=True):
341 """
341 """
342 Takes bytes argument, convert it to str i.e. unicodes, pass that into
342 Takes bytes argument, convert it to str i.e. unicodes, pass that into
343 shlex.split(), convert the returned value to bytes and return that for
343 shlex.split(), convert the returned value to bytes and return that for
344 Python 3 compatibility as shelx.split() don't accept bytes on Python 3.
344 Python 3 compatibility as shelx.split() don't accept bytes on Python 3.
345 """
345 """
346 ret = shlex.split(s.decode('latin-1'), comments, posix)
346 ret = shlex.split(s.decode('latin-1'), comments, posix)
347 return [a.encode('latin-1') for a in ret]
347 return [a.encode('latin-1') for a in ret]
348
348
349 iteritems = lambda x: x.items()
349 iteritems = lambda x: x.items()
350 itervalues = lambda x: x.values()
350 itervalues = lambda x: x.values()
351
351
352 # Python 3.5's json.load and json.loads require str. We polyfill its
352 # Python 3.5's json.load and json.loads require str. We polyfill its
353 # code for detecting encoding from bytes.
353 # code for detecting encoding from bytes.
354 if sys.version_info[0:2] < (3, 6):
354 if sys.version_info[0:2] < (3, 6):
355
355
356 def _detect_encoding(b):
356 def _detect_encoding(b):
357 bstartswith = b.startswith
357 bstartswith = b.startswith
358 if bstartswith((codecs.BOM_UTF32_BE, codecs.BOM_UTF32_LE)):
358 if bstartswith((codecs.BOM_UTF32_BE, codecs.BOM_UTF32_LE)):
359 return 'utf-32'
359 return 'utf-32'
360 if bstartswith((codecs.BOM_UTF16_BE, codecs.BOM_UTF16_LE)):
360 if bstartswith((codecs.BOM_UTF16_BE, codecs.BOM_UTF16_LE)):
361 return 'utf-16'
361 return 'utf-16'
362 if bstartswith(codecs.BOM_UTF8):
362 if bstartswith(codecs.BOM_UTF8):
363 return 'utf-8-sig'
363 return 'utf-8-sig'
364
364
365 if len(b) >= 4:
365 if len(b) >= 4:
366 if not b[0]:
366 if not b[0]:
367 # 00 00 -- -- - utf-32-be
367 # 00 00 -- -- - utf-32-be
368 # 00 XX -- -- - utf-16-be
368 # 00 XX -- -- - utf-16-be
369 return 'utf-16-be' if b[1] else 'utf-32-be'
369 return 'utf-16-be' if b[1] else 'utf-32-be'
370 if not b[1]:
370 if not b[1]:
371 # XX 00 00 00 - utf-32-le
371 # XX 00 00 00 - utf-32-le
372 # XX 00 00 XX - utf-16-le
372 # XX 00 00 XX - utf-16-le
373 # XX 00 XX -- - utf-16-le
373 # XX 00 XX -- - utf-16-le
374 return 'utf-16-le' if b[2] or b[3] else 'utf-32-le'
374 return 'utf-16-le' if b[2] or b[3] else 'utf-32-le'
375 elif len(b) == 2:
375 elif len(b) == 2:
376 if not b[0]:
376 if not b[0]:
377 # 00 XX - utf-16-be
377 # 00 XX - utf-16-be
378 return 'utf-16-be'
378 return 'utf-16-be'
379 if not b[1]:
379 if not b[1]:
380 # XX 00 - utf-16-le
380 # XX 00 - utf-16-le
381 return 'utf-16-le'
381 return 'utf-16-le'
382 # default
382 # default
383 return 'utf-8'
383 return 'utf-8'
384
384
385 def json_loads(s, *args, **kwargs):
385 def json_loads(s, *args, **kwargs):
386 if isinstance(s, (bytes, bytearray)):
386 if isinstance(s, (bytes, bytearray)):
387 s = s.decode(_detect_encoding(s), 'surrogatepass')
387 s = s.decode(_detect_encoding(s), 'surrogatepass')
388
388
389 return json.loads(s, *args, **kwargs)
389 return json.loads(s, *args, **kwargs)
390
390
391 else:
391 else:
392 json_loads = json.loads
392 json_loads = json.loads
393
393
394 else:
394 else:
395 import cStringIO
395 import cStringIO
396
396
397 xrange = xrange
397 xrange = xrange
398 unicode = unicode
398 unicode = unicode
399 bytechr = chr
399 bytechr = chr
400 byterepr = repr
400 byterepr = repr
401 bytestr = str
401 bytestr = str
402 iterbytestr = iter
402 iterbytestr = iter
403 maybebytestr = identity
403 maybebytestr = identity
404 sysbytes = identity
404 sysbytes = identity
405 sysstr = identity
405 sysstr = identity
406 strurl = identity
406 strurl = identity
407 bytesurl = identity
407 bytesurl = identity
408 open = open
408 open = open
409 delattr = delattr
409 delattr = delattr
410 getattr = getattr
410 getattr = getattr
411 hasattr = hasattr
411 hasattr = hasattr
412 setattr = setattr
412 setattr = setattr
413
413
414 # this can't be parsed on Python 3
414 # this can't be parsed on Python 3
415 exec(b'def raisewithtb(exc, tb):\n raise exc, None, tb\n')
415 exec(b'def raisewithtb(exc, tb):\n raise exc, None, tb\n')
416
416
417 def fsencode(filename):
417 def fsencode(filename):
418 """
418 """
419 Partial backport from os.py in Python 3, which only accepts bytes.
419 Partial backport from os.py in Python 3, which only accepts bytes.
420 In Python 2, our paths should only ever be bytes, a unicode path
420 In Python 2, our paths should only ever be bytes, a unicode path
421 indicates a bug.
421 indicates a bug.
422 """
422 """
423 if isinstance(filename, str):
423 if isinstance(filename, str):
424 return filename
424 return filename
425 else:
425 else:
426 raise TypeError("expect str, not %s" % type(filename).__name__)
426 raise TypeError("expect str, not %s" % type(filename).__name__)
427
427
428 # In Python 2, fsdecode() has a very chance to receive bytes. So it's
428 # In Python 2, fsdecode() has a very chance to receive bytes. So it's
429 # better not to touch Python 2 part as it's already working fine.
429 # better not to touch Python 2 part as it's already working fine.
430 fsdecode = identity
430 fsdecode = identity
431
431
432 def getdoc(obj):
432 def getdoc(obj):
433 return getattr(obj, '__doc__', None)
433 return getattr(obj, '__doc__', None)
434
434
435 _notset = object()
435 _notset = object()
436
436
437 def safehasattr(thing, attr):
437 def safehasattr(thing, attr):
438 return getattr(thing, attr, _notset) is not _notset
438 return getattr(thing, attr, _notset) is not _notset
439
439
440 def _getoptbwrapper(orig, args, shortlist, namelist):
440 def _getoptbwrapper(orig, args, shortlist, namelist):
441 return orig(args, shortlist, namelist)
441 return orig(args, shortlist, namelist)
442
442
443 strkwargs = identity
443 strkwargs = identity
444 byteskwargs = identity
444 byteskwargs = identity
445
445
446 oscurdir = os.curdir
446 oscurdir = os.curdir
447 oslinesep = os.linesep
447 oslinesep = os.linesep
448 osname = os.name
448 osname = os.name
449 ospathsep = os.pathsep
449 ospathsep = os.pathsep
450 ospardir = os.pardir
450 ospardir = os.pardir
451 ossep = os.sep
451 ossep = os.sep
452 osaltsep = os.altsep
452 osaltsep = os.altsep
453 long = long
453 long = long
454 stdin = sys.stdin
454 stdin = sys.stdin
455 stdout = sys.stdout
455 stdout = sys.stdout
456 stderr = sys.stderr
456 stderr = sys.stderr
457 if getattr(sys, 'argv', None) is not None:
457 if getattr(sys, 'argv', None) is not None:
458 sysargv = sys.argv
458 sysargv = sys.argv
459 sysplatform = sys.platform
459 sysplatform = sys.platform
460 sysexecutable = sys.executable
460 sysexecutable = sys.executable
461 shlexsplit = shlex.split
461 shlexsplit = shlex.split
462 bytesio = cStringIO.StringIO
462 bytesio = cStringIO.StringIO
463 stringio = bytesio
463 stringio = bytesio
464 maplist = map
464 maplist = map
465 rangelist = range
465 rangelist = range
466 ziplist = zip
466 ziplist = zip
467 rawinput = raw_input
467 rawinput = raw_input
468 getargspec = inspect.getargspec
468 getargspec = inspect.getargspec
469 iteritems = lambda x: x.iteritems()
469 iteritems = lambda x: x.iteritems()
470 itervalues = lambda x: x.itervalues()
470 itervalues = lambda x: x.itervalues()
471 json_loads = json.loads
471 json_loads = json.loads
472
472
473 isjython = sysplatform.startswith(b'java')
473 isjython = sysplatform.startswith(b'java')
474
474
475 isdarwin = sysplatform.startswith(b'darwin')
475 isdarwin = sysplatform.startswith(b'darwin')
476 islinux = sysplatform.startswith(b'linux')
476 islinux = sysplatform.startswith(b'linux')
477 isposix = osname == b'posix'
477 isposix = osname == b'posix'
478 iswindows = osname == b'nt'
478 iswindows = osname == b'nt'
479
479
480
480
481 def getoptb(args, shortlist, namelist):
481 def getoptb(args, shortlist, namelist):
482 return _getoptbwrapper(getopt.getopt, args, shortlist, namelist)
482 return _getoptbwrapper(getopt.getopt, args, shortlist, namelist)
483
483
484
484
485 def gnugetoptb(args, shortlist, namelist):
485 def gnugetoptb(args, shortlist, namelist):
486 return _getoptbwrapper(getopt.gnu_getopt, args, shortlist, namelist)
486 return _getoptbwrapper(getopt.gnu_getopt, args, shortlist, namelist)
487
487
488
488
489 def mkdtemp(suffix=b'', prefix=b'tmp', dir=None):
489 def mkdtemp(suffix=b'', prefix=b'tmp', dir=None):
490 return tempfile.mkdtemp(suffix, prefix, dir)
490 return tempfile.mkdtemp(suffix, prefix, dir)
491
491
492
492
493 # text=True is not supported; use util.from/tonativeeol() instead
493 # text=True is not supported; use util.from/tonativeeol() instead
494 def mkstemp(suffix=b'', prefix=b'tmp', dir=None):
494 def mkstemp(suffix=b'', prefix=b'tmp', dir=None):
495 return tempfile.mkstemp(suffix, prefix, dir)
495 return tempfile.mkstemp(suffix, prefix, dir)
496
496
497
497
498 # mode must include 'b'ytes as encoding= is not supported
498 # mode must include 'b'ytes as encoding= is not supported
499 def namedtempfile(
499 def namedtempfile(
500 mode=b'w+b', bufsize=-1, suffix=b'', prefix=b'tmp', dir=None, delete=True
500 mode=b'w+b', bufsize=-1, suffix=b'', prefix=b'tmp', dir=None, delete=True
501 ):
501 ):
502 mode = sysstr(mode)
502 mode = sysstr(mode)
503 assert 'b' in mode
503 assert 'b' in mode
504 return tempfile.NamedTemporaryFile(
504 return tempfile.NamedTemporaryFile(
505 mode, bufsize, suffix=suffix, prefix=prefix, dir=dir, delete=delete
505 mode, bufsize, suffix=suffix, prefix=prefix, dir=dir, delete=delete
506 )
506 )
@@ -1,65 +1,66 b''
1 from __future__ import absolute_import
1 from __future__ import absolute_import
2
2
3 import os
3 import os
4
4
5 from . import (
5 from . import (
6 encoding,
6 encoding,
7 pycompat,
7 pycompat,
8 util,
8 util,
9 win32,
9 win32,
10 )
10 )
11
11
12 try:
12 try:
13 import _winreg as winreg
13 import _winreg as winreg # pytype: disable=import-error
14
14
15 winreg.CloseKey
15 winreg.CloseKey
16 except ImportError:
16 except ImportError:
17 import winreg
17 # py2 only
18 import winreg # pytype: disable=import-error
18
19
19 # MS-DOS 'more' is the only pager available by default on Windows.
20 # MS-DOS 'more' is the only pager available by default on Windows.
20 fallbackpager = b'more'
21 fallbackpager = b'more'
21
22
22
23
23 def systemrcpath():
24 def systemrcpath():
24 '''return default os-specific hgrc search path'''
25 '''return default os-specific hgrc search path'''
25 rcpath = []
26 rcpath = []
26 filename = win32.executablepath()
27 filename = win32.executablepath()
27 # Use mercurial.ini found in directory with hg.exe
28 # Use mercurial.ini found in directory with hg.exe
28 progrc = os.path.join(os.path.dirname(filename), b'mercurial.ini')
29 progrc = os.path.join(os.path.dirname(filename), b'mercurial.ini')
29 rcpath.append(progrc)
30 rcpath.append(progrc)
30 # Use hgrc.d found in directory with hg.exe
31 # Use hgrc.d found in directory with hg.exe
31 progrcd = os.path.join(os.path.dirname(filename), b'hgrc.d')
32 progrcd = os.path.join(os.path.dirname(filename), b'hgrc.d')
32 if os.path.isdir(progrcd):
33 if os.path.isdir(progrcd):
33 for f, kind in util.listdir(progrcd):
34 for f, kind in util.listdir(progrcd):
34 if f.endswith(b'.rc'):
35 if f.endswith(b'.rc'):
35 rcpath.append(os.path.join(progrcd, f))
36 rcpath.append(os.path.join(progrcd, f))
36 # else look for a system rcpath in the registry
37 # else look for a system rcpath in the registry
37 value = util.lookupreg(
38 value = util.lookupreg(
38 b'SOFTWARE\\Mercurial', None, winreg.HKEY_LOCAL_MACHINE
39 b'SOFTWARE\\Mercurial', None, winreg.HKEY_LOCAL_MACHINE
39 )
40 )
40 if not isinstance(value, str) or not value:
41 if not isinstance(value, str) or not value:
41 return rcpath
42 return rcpath
42 value = util.localpath(value)
43 value = util.localpath(value)
43 for p in value.split(pycompat.ospathsep):
44 for p in value.split(pycompat.ospathsep):
44 if p.lower().endswith(b'mercurial.ini'):
45 if p.lower().endswith(b'mercurial.ini'):
45 rcpath.append(p)
46 rcpath.append(p)
46 elif os.path.isdir(p):
47 elif os.path.isdir(p):
47 for f, kind in util.listdir(p):
48 for f, kind in util.listdir(p):
48 if f.endswith(b'.rc'):
49 if f.endswith(b'.rc'):
49 rcpath.append(os.path.join(p, f))
50 rcpath.append(os.path.join(p, f))
50 return rcpath
51 return rcpath
51
52
52
53
53 def userrcpath():
54 def userrcpath():
54 '''return os-specific hgrc search path to the user dir'''
55 '''return os-specific hgrc search path to the user dir'''
55 home = os.path.expanduser(b'~')
56 home = os.path.expanduser(b'~')
56 path = [os.path.join(home, b'mercurial.ini'), os.path.join(home, b'.hgrc')]
57 path = [os.path.join(home, b'mercurial.ini'), os.path.join(home, b'.hgrc')]
57 userprofile = encoding.environ.get(b'USERPROFILE')
58 userprofile = encoding.environ.get(b'USERPROFILE')
58 if userprofile and userprofile != home:
59 if userprofile and userprofile != home:
59 path.append(os.path.join(userprofile, b'mercurial.ini'))
60 path.append(os.path.join(userprofile, b'mercurial.ini'))
60 path.append(os.path.join(userprofile, b'.hgrc'))
61 path.append(os.path.join(userprofile, b'.hgrc'))
61 return path
62 return path
62
63
63
64
64 def termsize(ui):
65 def termsize(ui):
65 return win32.termsize()
66 return win32.termsize()
@@ -1,689 +1,690 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 string
15 import string
16 import sys
16 import sys
17
17
18 from .i18n import _
18 from .i18n import _
19 from .pycompat import getattr
19 from .pycompat import getattr
20 from . import (
20 from . import (
21 encoding,
21 encoding,
22 error,
22 error,
23 policy,
23 policy,
24 pycompat,
24 pycompat,
25 win32,
25 win32,
26 )
26 )
27
27
28 try:
28 try:
29 import _winreg as winreg
29 import _winreg as winreg # pytype: disable=import-error
30
30
31 winreg.CloseKey
31 winreg.CloseKey
32 except ImportError:
32 except ImportError:
33 import winreg
33 # py2 only
34 import winreg # pytype: disable=import-error
34
35
35 osutil = policy.importmod('osutil')
36 osutil = policy.importmod('osutil')
36
37
37 getfsmountpoint = win32.getvolumename
38 getfsmountpoint = win32.getvolumename
38 getfstype = win32.getfstype
39 getfstype = win32.getfstype
39 getuser = win32.getuser
40 getuser = win32.getuser
40 hidewindow = win32.hidewindow
41 hidewindow = win32.hidewindow
41 makedir = win32.makedir
42 makedir = win32.makedir
42 nlinks = win32.nlinks
43 nlinks = win32.nlinks
43 oslink = win32.oslink
44 oslink = win32.oslink
44 samedevice = win32.samedevice
45 samedevice = win32.samedevice
45 samefile = win32.samefile
46 samefile = win32.samefile
46 setsignalhandler = win32.setsignalhandler
47 setsignalhandler = win32.setsignalhandler
47 spawndetached = win32.spawndetached
48 spawndetached = win32.spawndetached
48 split = os.path.split
49 split = os.path.split
49 testpid = win32.testpid
50 testpid = win32.testpid
50 unlink = win32.unlink
51 unlink = win32.unlink
51
52
52 umask = 0o022
53 umask = 0o022
53
54
54
55
55 class mixedfilemodewrapper(object):
56 class mixedfilemodewrapper(object):
56 """Wraps a file handle when it is opened in read/write mode.
57 """Wraps a file handle when it is opened in read/write mode.
57
58
58 fopen() and fdopen() on Windows have a specific-to-Windows requirement
59 fopen() and fdopen() on Windows have a specific-to-Windows requirement
59 that files opened with mode r+, w+, or a+ make a call to a file positioning
60 that files opened with mode r+, w+, or a+ make a call to a file positioning
60 function when switching between reads and writes. Without this extra call,
61 function when switching between reads and writes. Without this extra call,
61 Python will raise a not very intuitive "IOError: [Errno 0] Error."
62 Python will raise a not very intuitive "IOError: [Errno 0] Error."
62
63
63 This class wraps posixfile instances when the file is opened in read/write
64 This class wraps posixfile instances when the file is opened in read/write
64 mode and automatically adds checks or inserts appropriate file positioning
65 mode and automatically adds checks or inserts appropriate file positioning
65 calls when necessary.
66 calls when necessary.
66 """
67 """
67
68
68 OPNONE = 0
69 OPNONE = 0
69 OPREAD = 1
70 OPREAD = 1
70 OPWRITE = 2
71 OPWRITE = 2
71
72
72 def __init__(self, fp):
73 def __init__(self, fp):
73 object.__setattr__(self, '_fp', fp)
74 object.__setattr__(self, '_fp', fp)
74 object.__setattr__(self, '_lastop', 0)
75 object.__setattr__(self, '_lastop', 0)
75
76
76 def __enter__(self):
77 def __enter__(self):
77 self._fp.__enter__()
78 self._fp.__enter__()
78 return self
79 return self
79
80
80 def __exit__(self, exc_type, exc_val, exc_tb):
81 def __exit__(self, exc_type, exc_val, exc_tb):
81 self._fp.__exit__(exc_type, exc_val, exc_tb)
82 self._fp.__exit__(exc_type, exc_val, exc_tb)
82
83
83 def __getattr__(self, name):
84 def __getattr__(self, name):
84 return getattr(self._fp, name)
85 return getattr(self._fp, name)
85
86
86 def __setattr__(self, name, value):
87 def __setattr__(self, name, value):
87 return self._fp.__setattr__(name, value)
88 return self._fp.__setattr__(name, value)
88
89
89 def _noopseek(self):
90 def _noopseek(self):
90 self._fp.seek(0, os.SEEK_CUR)
91 self._fp.seek(0, os.SEEK_CUR)
91
92
92 def seek(self, *args, **kwargs):
93 def seek(self, *args, **kwargs):
93 object.__setattr__(self, '_lastop', self.OPNONE)
94 object.__setattr__(self, '_lastop', self.OPNONE)
94 return self._fp.seek(*args, **kwargs)
95 return self._fp.seek(*args, **kwargs)
95
96
96 def write(self, d):
97 def write(self, d):
97 if self._lastop == self.OPREAD:
98 if self._lastop == self.OPREAD:
98 self._noopseek()
99 self._noopseek()
99
100
100 object.__setattr__(self, '_lastop', self.OPWRITE)
101 object.__setattr__(self, '_lastop', self.OPWRITE)
101 return self._fp.write(d)
102 return self._fp.write(d)
102
103
103 def writelines(self, *args, **kwargs):
104 def writelines(self, *args, **kwargs):
104 if self._lastop == self.OPREAD:
105 if self._lastop == self.OPREAD:
105 self._noopeseek()
106 self._noopeseek()
106
107
107 object.__setattr__(self, '_lastop', self.OPWRITE)
108 object.__setattr__(self, '_lastop', self.OPWRITE)
108 return self._fp.writelines(*args, **kwargs)
109 return self._fp.writelines(*args, **kwargs)
109
110
110 def read(self, *args, **kwargs):
111 def read(self, *args, **kwargs):
111 if self._lastop == self.OPWRITE:
112 if self._lastop == self.OPWRITE:
112 self._noopseek()
113 self._noopseek()
113
114
114 object.__setattr__(self, '_lastop', self.OPREAD)
115 object.__setattr__(self, '_lastop', self.OPREAD)
115 return self._fp.read(*args, **kwargs)
116 return self._fp.read(*args, **kwargs)
116
117
117 def readline(self, *args, **kwargs):
118 def readline(self, *args, **kwargs):
118 if self._lastop == self.OPWRITE:
119 if self._lastop == self.OPWRITE:
119 self._noopseek()
120 self._noopseek()
120
121
121 object.__setattr__(self, '_lastop', self.OPREAD)
122 object.__setattr__(self, '_lastop', self.OPREAD)
122 return self._fp.readline(*args, **kwargs)
123 return self._fp.readline(*args, **kwargs)
123
124
124 def readlines(self, *args, **kwargs):
125 def readlines(self, *args, **kwargs):
125 if self._lastop == self.OPWRITE:
126 if self._lastop == self.OPWRITE:
126 self._noopseek()
127 self._noopseek()
127
128
128 object.__setattr__(self, '_lastop', self.OPREAD)
129 object.__setattr__(self, '_lastop', self.OPREAD)
129 return self._fp.readlines(*args, **kwargs)
130 return self._fp.readlines(*args, **kwargs)
130
131
131
132
132 class fdproxy(object):
133 class fdproxy(object):
133 """Wraps osutil.posixfile() to override the name attribute to reflect the
134 """Wraps osutil.posixfile() to override the name attribute to reflect the
134 underlying file name.
135 underlying file name.
135 """
136 """
136
137
137 def __init__(self, name, fp):
138 def __init__(self, name, fp):
138 self.name = name
139 self.name = name
139 self._fp = fp
140 self._fp = fp
140
141
141 def __enter__(self):
142 def __enter__(self):
142 self._fp.__enter__()
143 self._fp.__enter__()
143 # Return this wrapper for the context manager so that the name is
144 # Return this wrapper for the context manager so that the name is
144 # still available.
145 # still available.
145 return self
146 return self
146
147
147 def __exit__(self, exc_type, exc_value, traceback):
148 def __exit__(self, exc_type, exc_value, traceback):
148 self._fp.__exit__(exc_type, exc_value, traceback)
149 self._fp.__exit__(exc_type, exc_value, traceback)
149
150
150 def __iter__(self):
151 def __iter__(self):
151 return iter(self._fp)
152 return iter(self._fp)
152
153
153 def __getattr__(self, name):
154 def __getattr__(self, name):
154 return getattr(self._fp, name)
155 return getattr(self._fp, name)
155
156
156
157
157 def posixfile(name, mode=b'r', buffering=-1):
158 def posixfile(name, mode=b'r', buffering=-1):
158 '''Open a file with even more POSIX-like semantics'''
159 '''Open a file with even more POSIX-like semantics'''
159 try:
160 try:
160 fp = osutil.posixfile(name, mode, buffering) # may raise WindowsError
161 fp = osutil.posixfile(name, mode, buffering) # may raise WindowsError
161
162
162 # PyFile_FromFd() ignores the name, and seems to report fp.name as the
163 # PyFile_FromFd() ignores the name, and seems to report fp.name as the
163 # underlying file descriptor.
164 # underlying file descriptor.
164 if pycompat.ispy3:
165 if pycompat.ispy3:
165 fp = fdproxy(name, fp)
166 fp = fdproxy(name, fp)
166
167
167 # The position when opening in append mode is implementation defined, so
168 # The position when opening in append mode is implementation defined, so
168 # make it consistent with other platforms, which position at EOF.
169 # make it consistent with other platforms, which position at EOF.
169 if b'a' in mode:
170 if b'a' in mode:
170 fp.seek(0, os.SEEK_END)
171 fp.seek(0, os.SEEK_END)
171
172
172 if b'+' in mode:
173 if b'+' in mode:
173 return mixedfilemodewrapper(fp)
174 return mixedfilemodewrapper(fp)
174
175
175 return fp
176 return fp
176 except WindowsError as err:
177 except WindowsError as err:
177 # convert to a friendlier exception
178 # convert to a friendlier exception
178 raise IOError(
179 raise IOError(
179 err.errno, '%s: %s' % (encoding.strfromlocal(name), err.strerror)
180 err.errno, '%s: %s' % (encoding.strfromlocal(name), err.strerror)
180 )
181 )
181
182
182
183
183 # may be wrapped by win32mbcs extension
184 # may be wrapped by win32mbcs extension
184 listdir = osutil.listdir
185 listdir = osutil.listdir
185
186
186
187
187 class winstdout(object):
188 class winstdout(object):
188 '''stdout on windows misbehaves if sent through a pipe'''
189 '''stdout on windows misbehaves if sent through a pipe'''
189
190
190 def __init__(self, fp):
191 def __init__(self, fp):
191 self.fp = fp
192 self.fp = fp
192
193
193 def __getattr__(self, key):
194 def __getattr__(self, key):
194 return getattr(self.fp, key)
195 return getattr(self.fp, key)
195
196
196 def close(self):
197 def close(self):
197 try:
198 try:
198 self.fp.close()
199 self.fp.close()
199 except IOError:
200 except IOError:
200 pass
201 pass
201
202
202 def write(self, s):
203 def write(self, s):
203 try:
204 try:
204 # This is workaround for "Not enough space" error on
205 # This is workaround for "Not enough space" error on
205 # writing large size of data to console.
206 # writing large size of data to console.
206 limit = 16000
207 limit = 16000
207 l = len(s)
208 l = len(s)
208 start = 0
209 start = 0
209 self.softspace = 0
210 self.softspace = 0
210 while start < l:
211 while start < l:
211 end = start + limit
212 end = start + limit
212 self.fp.write(s[start:end])
213 self.fp.write(s[start:end])
213 start = end
214 start = end
214 except IOError as inst:
215 except IOError as inst:
215 if inst.errno != 0 and not win32.lasterrorwaspipeerror(inst):
216 if inst.errno != 0 and not win32.lasterrorwaspipeerror(inst):
216 raise
217 raise
217 self.close()
218 self.close()
218 raise IOError(errno.EPIPE, 'Broken pipe')
219 raise IOError(errno.EPIPE, 'Broken pipe')
219
220
220 def flush(self):
221 def flush(self):
221 try:
222 try:
222 return self.fp.flush()
223 return self.fp.flush()
223 except IOError as inst:
224 except IOError as inst:
224 if not win32.lasterrorwaspipeerror(inst):
225 if not win32.lasterrorwaspipeerror(inst):
225 raise
226 raise
226 raise IOError(errno.EPIPE, 'Broken pipe')
227 raise IOError(errno.EPIPE, 'Broken pipe')
227
228
228
229
229 def _is_win_9x():
230 def _is_win_9x():
230 '''return true if run on windows 95, 98 or me.'''
231 '''return true if run on windows 95, 98 or me.'''
231 try:
232 try:
232 return sys.getwindowsversion()[3] == 1
233 return sys.getwindowsversion()[3] == 1
233 except AttributeError:
234 except AttributeError:
234 return b'command' in encoding.environ.get(b'comspec', b'')
235 return b'command' in encoding.environ.get(b'comspec', b'')
235
236
236
237
237 def openhardlinks():
238 def openhardlinks():
238 return not _is_win_9x()
239 return not _is_win_9x()
239
240
240
241
241 def parsepatchoutput(output_line):
242 def parsepatchoutput(output_line):
242 """parses the output produced by patch and returns the filename"""
243 """parses the output produced by patch and returns the filename"""
243 pf = output_line[14:]
244 pf = output_line[14:]
244 if pf[0] == b'`':
245 if pf[0] == b'`':
245 pf = pf[1:-1] # Remove the quotes
246 pf = pf[1:-1] # Remove the quotes
246 return pf
247 return pf
247
248
248
249
249 def sshargs(sshcmd, host, user, port):
250 def sshargs(sshcmd, host, user, port):
250 '''Build argument list for ssh or Plink'''
251 '''Build argument list for ssh or Plink'''
251 pflag = b'plink' in sshcmd.lower() and b'-P' or b'-p'
252 pflag = b'plink' in sshcmd.lower() and b'-P' or b'-p'
252 args = user and (b"%s@%s" % (user, host)) or host
253 args = user and (b"%s@%s" % (user, host)) or host
253 if args.startswith(b'-') or args.startswith(b'/'):
254 if args.startswith(b'-') or args.startswith(b'/'):
254 raise error.Abort(
255 raise error.Abort(
255 _(b'illegal ssh hostname or username starting with - or /: %s')
256 _(b'illegal ssh hostname or username starting with - or /: %s')
256 % args
257 % args
257 )
258 )
258 args = shellquote(args)
259 args = shellquote(args)
259 if port:
260 if port:
260 args = b'%s %s %s' % (pflag, shellquote(port), args)
261 args = b'%s %s %s' % (pflag, shellquote(port), args)
261 return args
262 return args
262
263
263
264
264 def setflags(f, l, x):
265 def setflags(f, l, x):
265 pass
266 pass
266
267
267
268
268 def copymode(src, dst, mode=None, enforcewritable=False):
269 def copymode(src, dst, mode=None, enforcewritable=False):
269 pass
270 pass
270
271
271
272
272 def checkexec(path):
273 def checkexec(path):
273 return False
274 return False
274
275
275
276
276 def checklink(path):
277 def checklink(path):
277 return False
278 return False
278
279
279
280
280 def setbinary(fd):
281 def setbinary(fd):
281 # When run without console, pipes may expose invalid
282 # When run without console, pipes may expose invalid
282 # fileno(), usually set to -1.
283 # fileno(), usually set to -1.
283 fno = getattr(fd, 'fileno', None)
284 fno = getattr(fd, 'fileno', None)
284 if fno is not None and fno() >= 0:
285 if fno is not None and fno() >= 0:
285 msvcrt.setmode(fno(), os.O_BINARY)
286 msvcrt.setmode(fno(), os.O_BINARY) # pytype: disable=module-attr
286
287
287
288
288 def pconvert(path):
289 def pconvert(path):
289 return path.replace(pycompat.ossep, b'/')
290 return path.replace(pycompat.ossep, b'/')
290
291
291
292
292 def localpath(path):
293 def localpath(path):
293 return path.replace(b'/', b'\\')
294 return path.replace(b'/', b'\\')
294
295
295
296
296 def normpath(path):
297 def normpath(path):
297 return pconvert(os.path.normpath(path))
298 return pconvert(os.path.normpath(path))
298
299
299
300
300 def normcase(path):
301 def normcase(path):
301 return encoding.upper(path) # NTFS compares via upper()
302 return encoding.upper(path) # NTFS compares via upper()
302
303
303
304
304 # see posix.py for definitions
305 # see posix.py for definitions
305 normcasespec = encoding.normcasespecs.upper
306 normcasespec = encoding.normcasespecs.upper
306 normcasefallback = encoding.upperfallback
307 normcasefallback = encoding.upperfallback
307
308
308
309
309 def samestat(s1, s2):
310 def samestat(s1, s2):
310 return False
311 return False
311
312
312
313
313 def shelltocmdexe(path, env):
314 def shelltocmdexe(path, env):
314 r"""Convert shell variables in the form $var and ${var} inside ``path``
315 r"""Convert shell variables in the form $var and ${var} inside ``path``
315 to %var% form. Existing Windows style variables are left unchanged.
316 to %var% form. Existing Windows style variables are left unchanged.
316
317
317 The variables are limited to the given environment. Unknown variables are
318 The variables are limited to the given environment. Unknown variables are
318 left unchanged.
319 left unchanged.
319
320
320 >>> e = {b'var1': b'v1', b'var2': b'v2', b'var3': b'v3'}
321 >>> e = {b'var1': b'v1', b'var2': b'v2', b'var3': b'v3'}
321 >>> # Only valid values are expanded
322 >>> # Only valid values are expanded
322 >>> shelltocmdexe(b'cmd $var1 ${var2} %var3% $missing ${missing} %missing%',
323 >>> shelltocmdexe(b'cmd $var1 ${var2} %var3% $missing ${missing} %missing%',
323 ... e)
324 ... e)
324 'cmd %var1% %var2% %var3% $missing ${missing} %missing%'
325 'cmd %var1% %var2% %var3% $missing ${missing} %missing%'
325 >>> # Single quote prevents expansion, as does \$ escaping
326 >>> # Single quote prevents expansion, as does \$ escaping
326 >>> shelltocmdexe(b"cmd '$var1 ${var2} %var3%' \$var1 \${var2} \\", e)
327 >>> shelltocmdexe(b"cmd '$var1 ${var2} %var3%' \$var1 \${var2} \\", e)
327 'cmd "$var1 ${var2} %var3%" $var1 ${var2} \\'
328 'cmd "$var1 ${var2} %var3%" $var1 ${var2} \\'
328 >>> # $$ is not special. %% is not special either, but can be the end and
329 >>> # $$ is not special. %% is not special either, but can be the end and
329 >>> # start of consecutive variables
330 >>> # start of consecutive variables
330 >>> shelltocmdexe(b"cmd $$ %% %var1%%var2%", e)
331 >>> shelltocmdexe(b"cmd $$ %% %var1%%var2%", e)
331 'cmd $$ %% %var1%%var2%'
332 'cmd $$ %% %var1%%var2%'
332 >>> # No double substitution
333 >>> # No double substitution
333 >>> shelltocmdexe(b"$var1 %var1%", {b'var1': b'%var2%', b'var2': b'boom'})
334 >>> shelltocmdexe(b"$var1 %var1%", {b'var1': b'%var2%', b'var2': b'boom'})
334 '%var1% %var1%'
335 '%var1% %var1%'
335 >>> # Tilde expansion
336 >>> # Tilde expansion
336 >>> shelltocmdexe(b"~/dir ~\dir2 ~tmpfile \~/", {})
337 >>> shelltocmdexe(b"~/dir ~\dir2 ~tmpfile \~/", {})
337 '%USERPROFILE%/dir %USERPROFILE%\\dir2 ~tmpfile ~/'
338 '%USERPROFILE%/dir %USERPROFILE%\\dir2 ~tmpfile ~/'
338 """
339 """
339 if not any(c in path for c in b"$'~"):
340 if not any(c in path for c in b"$'~"):
340 return path
341 return path
341
342
342 varchars = pycompat.sysbytes(string.ascii_letters + string.digits) + b'_-'
343 varchars = pycompat.sysbytes(string.ascii_letters + string.digits) + b'_-'
343
344
344 res = b''
345 res = b''
345 index = 0
346 index = 0
346 pathlen = len(path)
347 pathlen = len(path)
347 while index < pathlen:
348 while index < pathlen:
348 c = path[index : index + 1]
349 c = path[index : index + 1]
349 if c == b'\'': # no expansion within single quotes
350 if c == b'\'': # no expansion within single quotes
350 path = path[index + 1 :]
351 path = path[index + 1 :]
351 pathlen = len(path)
352 pathlen = len(path)
352 try:
353 try:
353 index = path.index(b'\'')
354 index = path.index(b'\'')
354 res += b'"' + path[:index] + b'"'
355 res += b'"' + path[:index] + b'"'
355 except ValueError:
356 except ValueError:
356 res += c + path
357 res += c + path
357 index = pathlen - 1
358 index = pathlen - 1
358 elif c == b'%': # variable
359 elif c == b'%': # variable
359 path = path[index + 1 :]
360 path = path[index + 1 :]
360 pathlen = len(path)
361 pathlen = len(path)
361 try:
362 try:
362 index = path.index(b'%')
363 index = path.index(b'%')
363 except ValueError:
364 except ValueError:
364 res += b'%' + path
365 res += b'%' + path
365 index = pathlen - 1
366 index = pathlen - 1
366 else:
367 else:
367 var = path[:index]
368 var = path[:index]
368 res += b'%' + var + b'%'
369 res += b'%' + var + b'%'
369 elif c == b'$': # variable
370 elif c == b'$': # variable
370 if path[index + 1 : index + 2] == b'{':
371 if path[index + 1 : index + 2] == b'{':
371 path = path[index + 2 :]
372 path = path[index + 2 :]
372 pathlen = len(path)
373 pathlen = len(path)
373 try:
374 try:
374 index = path.index(b'}')
375 index = path.index(b'}')
375 var = path[:index]
376 var = path[:index]
376
377
377 # See below for why empty variables are handled specially
378 # See below for why empty variables are handled specially
378 if env.get(var, b'') != b'':
379 if env.get(var, b'') != b'':
379 res += b'%' + var + b'%'
380 res += b'%' + var + b'%'
380 else:
381 else:
381 res += b'${' + var + b'}'
382 res += b'${' + var + b'}'
382 except ValueError:
383 except ValueError:
383 res += b'${' + path
384 res += b'${' + path
384 index = pathlen - 1
385 index = pathlen - 1
385 else:
386 else:
386 var = b''
387 var = b''
387 index += 1
388 index += 1
388 c = path[index : index + 1]
389 c = path[index : index + 1]
389 while c != b'' and c in varchars:
390 while c != b'' and c in varchars:
390 var += c
391 var += c
391 index += 1
392 index += 1
392 c = path[index : index + 1]
393 c = path[index : index + 1]
393 # Some variables (like HG_OLDNODE) may be defined, but have an
394 # Some variables (like HG_OLDNODE) may be defined, but have an
394 # empty value. Those need to be skipped because when spawning
395 # empty value. Those need to be skipped because when spawning
395 # cmd.exe to run the hook, it doesn't replace %VAR% for an empty
396 # cmd.exe to run the hook, it doesn't replace %VAR% for an empty
396 # VAR, and that really confuses things like revset expressions.
397 # VAR, and that really confuses things like revset expressions.
397 # OTOH, if it's left in Unix format and the hook runs sh.exe, it
398 # OTOH, if it's left in Unix format and the hook runs sh.exe, it
398 # will substitute to an empty string, and everything is happy.
399 # will substitute to an empty string, and everything is happy.
399 if env.get(var, b'') != b'':
400 if env.get(var, b'') != b'':
400 res += b'%' + var + b'%'
401 res += b'%' + var + b'%'
401 else:
402 else:
402 res += b'$' + var
403 res += b'$' + var
403
404
404 if c != b'':
405 if c != b'':
405 index -= 1
406 index -= 1
406 elif (
407 elif (
407 c == b'~'
408 c == b'~'
408 and index + 1 < pathlen
409 and index + 1 < pathlen
409 and path[index + 1 : index + 2] in (b'\\', b'/')
410 and path[index + 1 : index + 2] in (b'\\', b'/')
410 ):
411 ):
411 res += b"%USERPROFILE%"
412 res += b"%USERPROFILE%"
412 elif (
413 elif (
413 c == b'\\'
414 c == b'\\'
414 and index + 1 < pathlen
415 and index + 1 < pathlen
415 and path[index + 1 : index + 2] in (b'$', b'~')
416 and path[index + 1 : index + 2] in (b'$', b'~')
416 ):
417 ):
417 # Skip '\', but only if it is escaping $ or ~
418 # Skip '\', but only if it is escaping $ or ~
418 res += path[index + 1 : index + 2]
419 res += path[index + 1 : index + 2]
419 index += 1
420 index += 1
420 else:
421 else:
421 res += c
422 res += c
422
423
423 index += 1
424 index += 1
424 return res
425 return res
425
426
426
427
427 # A sequence of backslashes is special iff it precedes a double quote:
428 # A sequence of backslashes is special iff it precedes a double quote:
428 # - if there's an even number of backslashes, the double quote is not
429 # - if there's an even number of backslashes, the double quote is not
429 # quoted (i.e. it ends the quoted region)
430 # quoted (i.e. it ends the quoted region)
430 # - if there's an odd number of backslashes, the double quote is quoted
431 # - if there's an odd number of backslashes, the double quote is quoted
431 # - in both cases, every pair of backslashes is unquoted into a single
432 # - in both cases, every pair of backslashes is unquoted into a single
432 # backslash
433 # backslash
433 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
434 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
434 # So, to quote a string, we must surround it in double quotes, double
435 # So, to quote a string, we must surround it in double quotes, double
435 # the number of backslashes that precede double quotes and add another
436 # the number of backslashes that precede double quotes and add another
436 # backslash before every double quote (being careful with the double
437 # backslash before every double quote (being careful with the double
437 # quote we've appended to the end)
438 # quote we've appended to the end)
438 _quotere = None
439 _quotere = None
439 _needsshellquote = None
440 _needsshellquote = None
440
441
441
442
442 def shellquote(s):
443 def shellquote(s):
443 r"""
444 r"""
444 >>> shellquote(br'C:\Users\xyz')
445 >>> shellquote(br'C:\Users\xyz')
445 '"C:\\Users\\xyz"'
446 '"C:\\Users\\xyz"'
446 >>> shellquote(br'C:\Users\xyz/mixed')
447 >>> shellquote(br'C:\Users\xyz/mixed')
447 '"C:\\Users\\xyz/mixed"'
448 '"C:\\Users\\xyz/mixed"'
448 >>> # Would be safe not to quote too, since it is all double backslashes
449 >>> # Would be safe not to quote too, since it is all double backslashes
449 >>> shellquote(br'C:\\Users\\xyz')
450 >>> shellquote(br'C:\\Users\\xyz')
450 '"C:\\\\Users\\\\xyz"'
451 '"C:\\\\Users\\\\xyz"'
451 >>> # But this must be quoted
452 >>> # But this must be quoted
452 >>> shellquote(br'C:\\Users\\xyz/abc')
453 >>> shellquote(br'C:\\Users\\xyz/abc')
453 '"C:\\\\Users\\\\xyz/abc"'
454 '"C:\\\\Users\\\\xyz/abc"'
454 """
455 """
455 global _quotere
456 global _quotere
456 if _quotere is None:
457 if _quotere is None:
457 _quotere = re.compile(br'(\\*)("|\\$)')
458 _quotere = re.compile(br'(\\*)("|\\$)')
458 global _needsshellquote
459 global _needsshellquote
459 if _needsshellquote is None:
460 if _needsshellquote is None:
460 # ":" is also treated as "safe character", because it is used as a part
461 # ":" is also treated as "safe character", because it is used as a part
461 # of path name on Windows. "\" is also part of a path name, but isn't
462 # of path name on Windows. "\" is also part of a path name, but isn't
462 # safe because shlex.split() (kind of) treats it as an escape char and
463 # safe because shlex.split() (kind of) treats it as an escape char and
463 # drops it. It will leave the next character, even if it is another
464 # drops it. It will leave the next character, even if it is another
464 # "\".
465 # "\".
465 _needsshellquote = re.compile(br'[^a-zA-Z0-9._:/-]').search
466 _needsshellquote = re.compile(br'[^a-zA-Z0-9._:/-]').search
466 if s and not _needsshellquote(s) and not _quotere.search(s):
467 if s and not _needsshellquote(s) and not _quotere.search(s):
467 # "s" shouldn't have to be quoted
468 # "s" shouldn't have to be quoted
468 return s
469 return s
469 return b'"%s"' % _quotere.sub(br'\1\1\\\2', s)
470 return b'"%s"' % _quotere.sub(br'\1\1\\\2', s)
470
471
471
472
472 def _unquote(s):
473 def _unquote(s):
473 if s.startswith(b'"') and s.endswith(b'"'):
474 if s.startswith(b'"') and s.endswith(b'"'):
474 return s[1:-1]
475 return s[1:-1]
475 return s
476 return s
476
477
477
478
478 def shellsplit(s):
479 def shellsplit(s):
479 """Parse a command string in cmd.exe way (best-effort)"""
480 """Parse a command string in cmd.exe way (best-effort)"""
480 return pycompat.maplist(_unquote, pycompat.shlexsplit(s, posix=False))
481 return pycompat.maplist(_unquote, pycompat.shlexsplit(s, posix=False))
481
482
482
483
483 def quotecommand(cmd):
484 def quotecommand(cmd):
484 """Build a command string suitable for os.popen* calls."""
485 """Build a command string suitable for os.popen* calls."""
485 if sys.version_info < (2, 7, 1):
486 if sys.version_info < (2, 7, 1):
486 # Python versions since 2.7.1 do this extra quoting themselves
487 # Python versions since 2.7.1 do this extra quoting themselves
487 return b'"' + cmd + b'"'
488 return b'"' + cmd + b'"'
488 return cmd
489 return cmd
489
490
490
491
491 # if you change this stub into a real check, please try to implement the
492 # if you change this stub into a real check, please try to implement the
492 # username and groupname functions above, too.
493 # username and groupname functions above, too.
493 def isowner(st):
494 def isowner(st):
494 return True
495 return True
495
496
496
497
497 def findexe(command):
498 def findexe(command):
498 '''Find executable for command searching like cmd.exe does.
499 '''Find executable for command searching like cmd.exe does.
499 If command is a basename then PATH is searched for command.
500 If command is a basename then PATH is searched for command.
500 PATH isn't searched if command is an absolute or relative path.
501 PATH isn't searched if command is an absolute or relative path.
501 An extension from PATHEXT is found and added if not present.
502 An extension from PATHEXT is found and added if not present.
502 If command isn't found None is returned.'''
503 If command isn't found None is returned.'''
503 pathext = encoding.environ.get(b'PATHEXT', b'.COM;.EXE;.BAT;.CMD')
504 pathext = encoding.environ.get(b'PATHEXT', b'.COM;.EXE;.BAT;.CMD')
504 pathexts = [ext for ext in pathext.lower().split(pycompat.ospathsep)]
505 pathexts = [ext for ext in pathext.lower().split(pycompat.ospathsep)]
505 if os.path.splitext(command)[1].lower() in pathexts:
506 if os.path.splitext(command)[1].lower() in pathexts:
506 pathexts = [b'']
507 pathexts = [b'']
507
508
508 def findexisting(pathcommand):
509 def findexisting(pathcommand):
509 b'Will append extension (if needed) and return existing file'
510 b'Will append extension (if needed) and return existing file'
510 for ext in pathexts:
511 for ext in pathexts:
511 executable = pathcommand + ext
512 executable = pathcommand + ext
512 if os.path.exists(executable):
513 if os.path.exists(executable):
513 return executable
514 return executable
514 return None
515 return None
515
516
516 if pycompat.ossep in command:
517 if pycompat.ossep in command:
517 return findexisting(command)
518 return findexisting(command)
518
519
519 for path in encoding.environ.get(b'PATH', b'').split(pycompat.ospathsep):
520 for path in encoding.environ.get(b'PATH', b'').split(pycompat.ospathsep):
520 executable = findexisting(os.path.join(path, command))
521 executable = findexisting(os.path.join(path, command))
521 if executable is not None:
522 if executable is not None:
522 return executable
523 return executable
523 return findexisting(os.path.expanduser(os.path.expandvars(command)))
524 return findexisting(os.path.expanduser(os.path.expandvars(command)))
524
525
525
526
526 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK}
527 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK}
527
528
528
529
529 def statfiles(files):
530 def statfiles(files):
530 '''Stat each file in files. Yield each stat, or None if a file
531 '''Stat each file in files. Yield each stat, or None if a file
531 does not exist or has a type we don't care about.
532 does not exist or has a type we don't care about.
532
533
533 Cluster and cache stat per directory to minimize number of OS stat calls.'''
534 Cluster and cache stat per directory to minimize number of OS stat calls.'''
534 dircache = {} # dirname -> filename -> status | None if file does not exist
535 dircache = {} # dirname -> filename -> status | None if file does not exist
535 getkind = stat.S_IFMT
536 getkind = stat.S_IFMT
536 for nf in files:
537 for nf in files:
537 nf = normcase(nf)
538 nf = normcase(nf)
538 dir, base = os.path.split(nf)
539 dir, base = os.path.split(nf)
539 if not dir:
540 if not dir:
540 dir = b'.'
541 dir = b'.'
541 cache = dircache.get(dir, None)
542 cache = dircache.get(dir, None)
542 if cache is None:
543 if cache is None:
543 try:
544 try:
544 dmap = dict(
545 dmap = dict(
545 [
546 [
546 (normcase(n), s)
547 (normcase(n), s)
547 for n, k, s in listdir(dir, True)
548 for n, k, s in listdir(dir, True)
548 if getkind(s.st_mode) in _wantedkinds
549 if getkind(s.st_mode) in _wantedkinds
549 ]
550 ]
550 )
551 )
551 except OSError as err:
552 except OSError as err:
552 # Python >= 2.5 returns ENOENT and adds winerror field
553 # Python >= 2.5 returns ENOENT and adds winerror field
553 # EINVAL is raised if dir is not a directory.
554 # EINVAL is raised if dir is not a directory.
554 if err.errno not in (errno.ENOENT, errno.EINVAL, errno.ENOTDIR):
555 if err.errno not in (errno.ENOENT, errno.EINVAL, errno.ENOTDIR):
555 raise
556 raise
556 dmap = {}
557 dmap = {}
557 cache = dircache.setdefault(dir, dmap)
558 cache = dircache.setdefault(dir, dmap)
558 yield cache.get(base, None)
559 yield cache.get(base, None)
559
560
560
561
561 def username(uid=None):
562 def username(uid=None):
562 """Return the name of the user with the given uid.
563 """Return the name of the user with the given uid.
563
564
564 If uid is None, return the name of the current user."""
565 If uid is None, return the name of the current user."""
565 return None
566 return None
566
567
567
568
568 def groupname(gid=None):
569 def groupname(gid=None):
569 """Return the name of the group with the given gid.
570 """Return the name of the group with the given gid.
570
571
571 If gid is None, return the name of the current group."""
572 If gid is None, return the name of the current group."""
572 return None
573 return None
573
574
574
575
575 def readlink(pathname):
576 def readlink(pathname):
576 return pycompat.fsencode(os.readlink(pycompat.fsdecode(pathname)))
577 return pycompat.fsencode(os.readlink(pycompat.fsdecode(pathname)))
577
578
578
579
579 def removedirs(name):
580 def removedirs(name):
580 """special version of os.removedirs that does not remove symlinked
581 """special version of os.removedirs that does not remove symlinked
581 directories or junction points if they actually contain files"""
582 directories or junction points if they actually contain files"""
582 if listdir(name):
583 if listdir(name):
583 return
584 return
584 os.rmdir(name)
585 os.rmdir(name)
585 head, tail = os.path.split(name)
586 head, tail = os.path.split(name)
586 if not tail:
587 if not tail:
587 head, tail = os.path.split(head)
588 head, tail = os.path.split(head)
588 while head and tail:
589 while head and tail:
589 try:
590 try:
590 if listdir(head):
591 if listdir(head):
591 return
592 return
592 os.rmdir(head)
593 os.rmdir(head)
593 except (ValueError, OSError):
594 except (ValueError, OSError):
594 break
595 break
595 head, tail = os.path.split(head)
596 head, tail = os.path.split(head)
596
597
597
598
598 def rename(src, dst):
599 def rename(src, dst):
599 '''atomically rename file src to dst, replacing dst if it exists'''
600 '''atomically rename file src to dst, replacing dst if it exists'''
600 try:
601 try:
601 os.rename(src, dst)
602 os.rename(src, dst)
602 except OSError as e:
603 except OSError as e:
603 if e.errno != errno.EEXIST:
604 if e.errno != errno.EEXIST:
604 raise
605 raise
605 unlink(dst)
606 unlink(dst)
606 os.rename(src, dst)
607 os.rename(src, dst)
607
608
608
609
609 def gethgcmd():
610 def gethgcmd():
610 return [encoding.strtolocal(arg) for arg in [sys.executable] + sys.argv[:1]]
611 return [encoding.strtolocal(arg) for arg in [sys.executable] + sys.argv[:1]]
611
612
612
613
613 def groupmembers(name):
614 def groupmembers(name):
614 # Don't support groups on Windows for now
615 # Don't support groups on Windows for now
615 raise KeyError
616 raise KeyError
616
617
617
618
618 def isexec(f):
619 def isexec(f):
619 return False
620 return False
620
621
621
622
622 class cachestat(object):
623 class cachestat(object):
623 def __init__(self, path):
624 def __init__(self, path):
624 pass
625 pass
625
626
626 def cacheable(self):
627 def cacheable(self):
627 return False
628 return False
628
629
629
630
630 def lookupreg(key, valname=None, scope=None):
631 def lookupreg(key, valname=None, scope=None):
631 ''' Look up a key/value name in the Windows registry.
632 ''' Look up a key/value name in the Windows registry.
632
633
633 valname: value name. If unspecified, the default value for the key
634 valname: value name. If unspecified, the default value for the key
634 is used.
635 is used.
635 scope: optionally specify scope for registry lookup, this can be
636 scope: optionally specify scope for registry lookup, this can be
636 a sequence of scopes to look up in order. Default (CURRENT_USER,
637 a sequence of scopes to look up in order. Default (CURRENT_USER,
637 LOCAL_MACHINE).
638 LOCAL_MACHINE).
638 '''
639 '''
639 if scope is None:
640 if scope is None:
640 scope = (winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE)
641 scope = (winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE)
641 elif not isinstance(scope, (list, tuple)):
642 elif not isinstance(scope, (list, tuple)):
642 scope = (scope,)
643 scope = (scope,)
643 for s in scope:
644 for s in scope:
644 try:
645 try:
645 with winreg.OpenKey(s, encoding.strfromlocal(key)) as hkey:
646 with winreg.OpenKey(s, encoding.strfromlocal(key)) as hkey:
646 name = valname and encoding.strfromlocal(valname) or valname
647 name = valname and encoding.strfromlocal(valname) or valname
647 val = winreg.QueryValueEx(hkey, name)[0]
648 val = winreg.QueryValueEx(hkey, name)[0]
648 # never let a Unicode string escape into the wild
649 # never let a Unicode string escape into the wild
649 return encoding.unitolocal(val)
650 return encoding.unitolocal(val)
650 except EnvironmentError:
651 except EnvironmentError:
651 pass
652 pass
652
653
653
654
654 expandglobs = True
655 expandglobs = True
655
656
656
657
657 def statislink(st):
658 def statislink(st):
658 '''check whether a stat result is a symlink'''
659 '''check whether a stat result is a symlink'''
659 return False
660 return False
660
661
661
662
662 def statisexec(st):
663 def statisexec(st):
663 '''check whether a stat result is an executable file'''
664 '''check whether a stat result is an executable file'''
664 return False
665 return False
665
666
666
667
667 def poll(fds):
668 def poll(fds):
668 # see posix.py for description
669 # see posix.py for description
669 raise NotImplementedError()
670 raise NotImplementedError()
670
671
671
672
672 def readpipe(pipe):
673 def readpipe(pipe):
673 """Read all available data from a pipe."""
674 """Read all available data from a pipe."""
674 chunks = []
675 chunks = []
675 while True:
676 while True:
676 size = win32.peekpipe(pipe)
677 size = win32.peekpipe(pipe)
677 if not size:
678 if not size:
678 break
679 break
679
680
680 s = pipe.read(size)
681 s = pipe.read(size)
681 if not s:
682 if not s:
682 break
683 break
683 chunks.append(s)
684 chunks.append(s)
684
685
685 return b''.join(chunks)
686 return b''.join(chunks)
686
687
687
688
688 def bindunixsocket(sock, path):
689 def bindunixsocket(sock, path):
689 raise NotImplementedError('unsupported platform')
690 raise NotImplementedError('unsupported platform')
General Comments 0
You need to be logged in to leave comments. Login now