##// END OF EJS Templates
pytype: disable a few errors about Windows specific module attributes...
Matt Harbison -
r49843:d500df2e default
parent child Browse files
Show More
@@ -1,111 +1,115 b''
1 import os
1 import os
2
2
3 from . import (
3 from . import (
4 encoding,
4 encoding,
5 pycompat,
5 pycompat,
6 util,
6 util,
7 win32,
7 win32,
8 )
8 )
9
9
10 try:
10 try:
11 import _winreg as winreg # pytype: disable=import-error
11 import _winreg as winreg # pytype: disable=import-error
12
12
13 winreg.CloseKey
13 winreg.CloseKey
14 except ImportError:
14 except ImportError:
15 # py2 only
15 # py2 only
16 import winreg # pytype: disable=import-error
16 import winreg # pytype: disable=import-error
17
17
18 # MS-DOS 'more' is the only pager available by default on Windows.
18 # MS-DOS 'more' is the only pager available by default on Windows.
19 fallbackpager = b'more'
19 fallbackpager = b'more'
20
20
21
21
22 def systemrcpath():
22 def systemrcpath():
23 '''return default os-specific hgrc search path'''
23 '''return default os-specific hgrc search path'''
24 rcpath = []
24 rcpath = []
25 filename = win32.executablepath()
25 filename = win32.executablepath()
26 # Use mercurial.ini found in directory with hg.exe
26 # Use mercurial.ini found in directory with hg.exe
27 progrc = os.path.join(os.path.dirname(filename), b'mercurial.ini')
27 progrc = os.path.join(os.path.dirname(filename), b'mercurial.ini')
28 rcpath.append(progrc)
28 rcpath.append(progrc)
29
29
30 def _processdir(progrcd):
30 def _processdir(progrcd):
31 if os.path.isdir(progrcd):
31 if os.path.isdir(progrcd):
32 for f, kind in sorted(util.listdir(progrcd)):
32 for f, kind in sorted(util.listdir(progrcd)):
33 if f.endswith(b'.rc'):
33 if f.endswith(b'.rc'):
34 rcpath.append(os.path.join(progrcd, f))
34 rcpath.append(os.path.join(progrcd, f))
35
35
36 # Use hgrc.d found in directory with hg.exe
36 # Use hgrc.d found in directory with hg.exe
37 _processdir(os.path.join(os.path.dirname(filename), b'hgrc.d'))
37 _processdir(os.path.join(os.path.dirname(filename), b'hgrc.d'))
38
38
39 # treat a PROGRAMDATA directory as equivalent to /etc/mercurial
39 # treat a PROGRAMDATA directory as equivalent to /etc/mercurial
40 programdata = encoding.environ.get(b'PROGRAMDATA')
40 programdata = encoding.environ.get(b'PROGRAMDATA')
41 if programdata:
41 if programdata:
42 programdata = os.path.join(programdata, b'Mercurial')
42 programdata = os.path.join(programdata, b'Mercurial')
43 _processdir(os.path.join(programdata, b'hgrc.d'))
43 _processdir(os.path.join(programdata, b'hgrc.d'))
44
44
45 ini = os.path.join(programdata, b'mercurial.ini')
45 ini = os.path.join(programdata, b'mercurial.ini')
46 if os.path.isfile(ini):
46 if os.path.isfile(ini):
47 rcpath.append(ini)
47 rcpath.append(ini)
48
48
49 ini = os.path.join(programdata, b'hgrc')
49 ini = os.path.join(programdata, b'hgrc')
50 if os.path.isfile(ini):
50 if os.path.isfile(ini):
51 rcpath.append(ini)
51 rcpath.append(ini)
52
52
53 # next look for a system rcpath in the registry
53 # next look for a system rcpath in the registry
54 value = util.lookupreg(
54 value = util.lookupreg(
55 b'SOFTWARE\\Mercurial', None, winreg.HKEY_LOCAL_MACHINE
55 # pytype: disable=module-attr
56 b'SOFTWARE\\Mercurial',
57 None,
58 winreg.HKEY_LOCAL_MACHINE
59 # pytype: enable=module-attr
56 )
60 )
57 if value and isinstance(value, bytes):
61 if value and isinstance(value, bytes):
58 value = util.localpath(value)
62 value = util.localpath(value)
59 for p in value.split(pycompat.ospathsep):
63 for p in value.split(pycompat.ospathsep):
60 if p.lower().endswith(b'mercurial.ini'):
64 if p.lower().endswith(b'mercurial.ini'):
61 rcpath.append(p)
65 rcpath.append(p)
62 else:
66 else:
63 _processdir(p)
67 _processdir(p)
64 return rcpath
68 return rcpath
65
69
66
70
67 def userrcpath():
71 def userrcpath():
68 '''return os-specific hgrc search path to the user dir'''
72 '''return os-specific hgrc search path to the user dir'''
69 home = _legacy_expanduser(b'~')
73 home = _legacy_expanduser(b'~')
70 path = [os.path.join(home, b'mercurial.ini'), os.path.join(home, b'.hgrc')]
74 path = [os.path.join(home, b'mercurial.ini'), os.path.join(home, b'.hgrc')]
71 userprofile = encoding.environ.get(b'USERPROFILE')
75 userprofile = encoding.environ.get(b'USERPROFILE')
72 if userprofile and userprofile != home:
76 if userprofile and userprofile != home:
73 path.append(os.path.join(userprofile, b'mercurial.ini'))
77 path.append(os.path.join(userprofile, b'mercurial.ini'))
74 path.append(os.path.join(userprofile, b'.hgrc'))
78 path.append(os.path.join(userprofile, b'.hgrc'))
75 return path
79 return path
76
80
77
81
78 def _legacy_expanduser(path):
82 def _legacy_expanduser(path):
79 """Expand ~ and ~user constructs in the pre 3.8 style"""
83 """Expand ~ and ~user constructs in the pre 3.8 style"""
80
84
81 # Python 3.8+ changed the expansion of '~' from HOME to USERPROFILE. See
85 # Python 3.8+ changed the expansion of '~' from HOME to USERPROFILE. See
82 # https://bugs.python.org/issue36264. It also seems to capitalize the drive
86 # https://bugs.python.org/issue36264. It also seems to capitalize the drive
83 # letter, as though it was processed through os.path.realpath().
87 # letter, as though it was processed through os.path.realpath().
84 if not path.startswith(b'~'):
88 if not path.startswith(b'~'):
85 return path
89 return path
86
90
87 i, n = 1, len(path)
91 i, n = 1, len(path)
88 while i < n and path[i] not in b'\\/':
92 while i < n and path[i] not in b'\\/':
89 i += 1
93 i += 1
90
94
91 if b'HOME' in encoding.environ:
95 if b'HOME' in encoding.environ:
92 userhome = encoding.environ[b'HOME']
96 userhome = encoding.environ[b'HOME']
93 elif b'USERPROFILE' in encoding.environ:
97 elif b'USERPROFILE' in encoding.environ:
94 userhome = encoding.environ[b'USERPROFILE']
98 userhome = encoding.environ[b'USERPROFILE']
95 elif b'HOMEPATH' not in encoding.environ:
99 elif b'HOMEPATH' not in encoding.environ:
96 return path
100 return path
97 else:
101 else:
98 try:
102 try:
99 drive = encoding.environ[b'HOMEDRIVE']
103 drive = encoding.environ[b'HOMEDRIVE']
100 except KeyError:
104 except KeyError:
101 drive = b''
105 drive = b''
102 userhome = os.path.join(drive, encoding.environ[b'HOMEPATH'])
106 userhome = os.path.join(drive, encoding.environ[b'HOMEPATH'])
103
107
104 if i != 1: # ~user
108 if i != 1: # ~user
105 userhome = os.path.join(os.path.dirname(userhome), path[1:i])
109 userhome = os.path.join(os.path.dirname(userhome), path[1:i])
106
110
107 return userhome + path[i:]
111 return userhome + path[i:]
108
112
109
113
110 def termsize(ui):
114 def termsize(ui):
111 return win32.termsize()
115 return win32.termsize()
@@ -1,728 +1,735 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 Olivia Mackall <olivia@selenic.com> and others
3 # Copyright 2005-2009 Olivia Mackall <olivia@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
8
9 import errno
9 import errno
10 import getpass
10 import getpass
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 # pytype: disable=import-error
29 import _winreg as winreg # pytype: disable=import-error
30
30
31 winreg.CloseKey
31 winreg.CloseKey
32 except ImportError:
32 except ImportError:
33 # py2 only
33 # py2 only
34 import winreg # pytype: disable=import-error
34 import winreg # pytype: disable=import-error
35
35
36 osutil = policy.importmod('osutil')
36 osutil = policy.importmod('osutil')
37
37
38 getfsmountpoint = win32.getvolumename
38 getfsmountpoint = win32.getvolumename
39 getfstype = win32.getfstype
39 getfstype = win32.getfstype
40 getuser = win32.getuser
40 getuser = win32.getuser
41 hidewindow = win32.hidewindow
41 hidewindow = win32.hidewindow
42 makedir = win32.makedir
42 makedir = win32.makedir
43 nlinks = win32.nlinks
43 nlinks = win32.nlinks
44 oslink = win32.oslink
44 oslink = win32.oslink
45 samedevice = win32.samedevice
45 samedevice = win32.samedevice
46 samefile = win32.samefile
46 samefile = win32.samefile
47 setsignalhandler = win32.setsignalhandler
47 setsignalhandler = win32.setsignalhandler
48 spawndetached = win32.spawndetached
48 spawndetached = win32.spawndetached
49 split = os.path.split
49 split = os.path.split
50 testpid = win32.testpid
50 testpid = win32.testpid
51 unlink = win32.unlink
51 unlink = win32.unlink
52
52
53 umask = 0o022
53 umask = 0o022
54
54
55
55
56 class mixedfilemodewrapper:
56 class mixedfilemodewrapper:
57 """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.
58
58
59 fopen() and fdopen() on Windows have a specific-to-Windows requirement
59 fopen() and fdopen() on Windows have a specific-to-Windows requirement
60 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
61 function when switching between reads and writes. Without this extra call,
61 function when switching between reads and writes. Without this extra call,
62 Python will raise a not very intuitive "IOError: [Errno 0] Error."
62 Python will raise a not very intuitive "IOError: [Errno 0] Error."
63
63
64 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
65 mode and automatically adds checks or inserts appropriate file positioning
65 mode and automatically adds checks or inserts appropriate file positioning
66 calls when necessary.
66 calls when necessary.
67 """
67 """
68
68
69 OPNONE = 0
69 OPNONE = 0
70 OPREAD = 1
70 OPREAD = 1
71 OPWRITE = 2
71 OPWRITE = 2
72
72
73 def __init__(self, fp):
73 def __init__(self, fp):
74 object.__setattr__(self, '_fp', fp)
74 object.__setattr__(self, '_fp', fp)
75 object.__setattr__(self, '_lastop', 0)
75 object.__setattr__(self, '_lastop', 0)
76
76
77 def __enter__(self):
77 def __enter__(self):
78 self._fp.__enter__()
78 self._fp.__enter__()
79 return self
79 return self
80
80
81 def __exit__(self, exc_type, exc_val, exc_tb):
81 def __exit__(self, exc_type, exc_val, exc_tb):
82 self._fp.__exit__(exc_type, exc_val, exc_tb)
82 self._fp.__exit__(exc_type, exc_val, exc_tb)
83
83
84 def __getattr__(self, name):
84 def __getattr__(self, name):
85 return getattr(self._fp, name)
85 return getattr(self._fp, name)
86
86
87 def __setattr__(self, name, value):
87 def __setattr__(self, name, value):
88 return self._fp.__setattr__(name, value)
88 return self._fp.__setattr__(name, value)
89
89
90 def _noopseek(self):
90 def _noopseek(self):
91 self._fp.seek(0, os.SEEK_CUR)
91 self._fp.seek(0, os.SEEK_CUR)
92
92
93 def seek(self, *args, **kwargs):
93 def seek(self, *args, **kwargs):
94 object.__setattr__(self, '_lastop', self.OPNONE)
94 object.__setattr__(self, '_lastop', self.OPNONE)
95 return self._fp.seek(*args, **kwargs)
95 return self._fp.seek(*args, **kwargs)
96
96
97 def write(self, d):
97 def write(self, d):
98 if self._lastop == self.OPREAD:
98 if self._lastop == self.OPREAD:
99 self._noopseek()
99 self._noopseek()
100
100
101 object.__setattr__(self, '_lastop', self.OPWRITE)
101 object.__setattr__(self, '_lastop', self.OPWRITE)
102 return self._fp.write(d)
102 return self._fp.write(d)
103
103
104 def writelines(self, *args, **kwargs):
104 def writelines(self, *args, **kwargs):
105 if self._lastop == self.OPREAD:
105 if self._lastop == self.OPREAD:
106 self._noopeseek()
106 self._noopeseek()
107
107
108 object.__setattr__(self, '_lastop', self.OPWRITE)
108 object.__setattr__(self, '_lastop', self.OPWRITE)
109 return self._fp.writelines(*args, **kwargs)
109 return self._fp.writelines(*args, **kwargs)
110
110
111 def read(self, *args, **kwargs):
111 def read(self, *args, **kwargs):
112 if self._lastop == self.OPWRITE:
112 if self._lastop == self.OPWRITE:
113 self._noopseek()
113 self._noopseek()
114
114
115 object.__setattr__(self, '_lastop', self.OPREAD)
115 object.__setattr__(self, '_lastop', self.OPREAD)
116 return self._fp.read(*args, **kwargs)
116 return self._fp.read(*args, **kwargs)
117
117
118 def readline(self, *args, **kwargs):
118 def readline(self, *args, **kwargs):
119 if self._lastop == self.OPWRITE:
119 if self._lastop == self.OPWRITE:
120 self._noopseek()
120 self._noopseek()
121
121
122 object.__setattr__(self, '_lastop', self.OPREAD)
122 object.__setattr__(self, '_lastop', self.OPREAD)
123 return self._fp.readline(*args, **kwargs)
123 return self._fp.readline(*args, **kwargs)
124
124
125 def readlines(self, *args, **kwargs):
125 def readlines(self, *args, **kwargs):
126 if self._lastop == self.OPWRITE:
126 if self._lastop == self.OPWRITE:
127 self._noopseek()
127 self._noopseek()
128
128
129 object.__setattr__(self, '_lastop', self.OPREAD)
129 object.__setattr__(self, '_lastop', self.OPREAD)
130 return self._fp.readlines(*args, **kwargs)
130 return self._fp.readlines(*args, **kwargs)
131
131
132
132
133 class fdproxy:
133 class fdproxy:
134 """Wraps osutil.posixfile() to override the name attribute to reflect the
134 """Wraps osutil.posixfile() to override the name attribute to reflect the
135 underlying file name.
135 underlying file name.
136 """
136 """
137
137
138 def __init__(self, name, fp):
138 def __init__(self, name, fp):
139 self.name = name
139 self.name = name
140 self._fp = fp
140 self._fp = fp
141
141
142 def __enter__(self):
142 def __enter__(self):
143 self._fp.__enter__()
143 self._fp.__enter__()
144 # 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
145 # still available.
145 # still available.
146 return self
146 return self
147
147
148 def __exit__(self, exc_type, exc_value, traceback):
148 def __exit__(self, exc_type, exc_value, traceback):
149 self._fp.__exit__(exc_type, exc_value, traceback)
149 self._fp.__exit__(exc_type, exc_value, traceback)
150
150
151 def __iter__(self):
151 def __iter__(self):
152 return iter(self._fp)
152 return iter(self._fp)
153
153
154 def __getattr__(self, name):
154 def __getattr__(self, name):
155 return getattr(self._fp, name)
155 return getattr(self._fp, name)
156
156
157
157
158 def posixfile(name, mode=b'r', buffering=-1):
158 def posixfile(name, mode=b'r', buffering=-1):
159 '''Open a file with even more POSIX-like semantics'''
159 '''Open a file with even more POSIX-like semantics'''
160 try:
160 try:
161 fp = osutil.posixfile(name, mode, buffering) # may raise WindowsError
161 fp = osutil.posixfile(name, mode, buffering) # may raise WindowsError
162
162
163 # 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
164 # underlying file descriptor.
164 # underlying file descriptor.
165 fp = fdproxy(name, fp)
165 fp = fdproxy(name, fp)
166
166
167 # The position when opening in append mode is implementation defined, so
167 # The position when opening in append mode is implementation defined, so
168 # make it consistent with other platforms, which position at EOF.
168 # make it consistent with other platforms, which position at EOF.
169 if b'a' in mode:
169 if b'a' in mode:
170 fp.seek(0, os.SEEK_END)
170 fp.seek(0, os.SEEK_END)
171
171
172 if b'+' in mode:
172 if b'+' in mode:
173 return mixedfilemodewrapper(fp)
173 return mixedfilemodewrapper(fp)
174
174
175 return fp
175 return fp
176 except WindowsError as err: # pytype: disable=name-error
176 except WindowsError as err: # pytype: disable=name-error
177 # convert to a friendlier exception
177 # convert to a friendlier exception
178 raise IOError(
178 raise IOError(
179 err.errno, '%s: %s' % (encoding.strfromlocal(name), err.strerror)
179 err.errno, '%s: %s' % (encoding.strfromlocal(name), err.strerror)
180 )
180 )
181
181
182
182
183 # may be wrapped by win32mbcs extension
183 # may be wrapped by win32mbcs extension
184 listdir = osutil.listdir
184 listdir = osutil.listdir
185
185
186
186
187 # copied from .utils.procutil, remove after Python 2 support was dropped
187 # copied from .utils.procutil, remove after Python 2 support was dropped
188 def _isatty(fp):
188 def _isatty(fp):
189 try:
189 try:
190 return fp.isatty()
190 return fp.isatty()
191 except AttributeError:
191 except AttributeError:
192 return False
192 return False
193
193
194
194
195 def get_password():
195 def get_password():
196 """Prompt for password with echo off, using Windows getch().
196 """Prompt for password with echo off, using Windows getch().
197
197
198 This shouldn't be called directly- use ``ui.getpass()`` instead, which
198 This shouldn't be called directly- use ``ui.getpass()`` instead, which
199 checks if the session is interactive first.
199 checks if the session is interactive first.
200 """
200 """
201 pw = u""
201 pw = u""
202 while True:
202 while True:
203 c = msvcrt.getwch() # pytype: disable=module-attr
203 c = msvcrt.getwch() # pytype: disable=module-attr
204 if c == u'\r' or c == u'\n':
204 if c == u'\r' or c == u'\n':
205 break
205 break
206 if c == u'\003':
206 if c == u'\003':
207 raise KeyboardInterrupt
207 raise KeyboardInterrupt
208 if c == u'\b':
208 if c == u'\b':
209 pw = pw[:-1]
209 pw = pw[:-1]
210 else:
210 else:
211 pw = pw + c
211 pw = pw + c
212 msvcrt.putwch(u'\r') # pytype: disable=module-attr
212 msvcrt.putwch(u'\r') # pytype: disable=module-attr
213 msvcrt.putwch(u'\n') # pytype: disable=module-attr
213 msvcrt.putwch(u'\n') # pytype: disable=module-attr
214 return encoding.unitolocal(pw)
214 return encoding.unitolocal(pw)
215
215
216
216
217 class winstdout:
217 class winstdout:
218 """Some files on Windows misbehave.
218 """Some files on Windows misbehave.
219
219
220 When writing to a broken pipe, EINVAL instead of EPIPE may be raised.
220 When writing to a broken pipe, EINVAL instead of EPIPE may be raised.
221
221
222 When writing too many bytes to a console at the same, a "Not enough space"
222 When writing too many bytes to a console at the same, a "Not enough space"
223 error may happen. Python 3 already works around that.
223 error may happen. Python 3 already works around that.
224 """
224 """
225
225
226 def __init__(self, fp):
226 def __init__(self, fp):
227 self.fp = fp
227 self.fp = fp
228
228
229 def __getattr__(self, key):
229 def __getattr__(self, key):
230 return getattr(self.fp, key)
230 return getattr(self.fp, key)
231
231
232 def close(self):
232 def close(self):
233 try:
233 try:
234 self.fp.close()
234 self.fp.close()
235 except IOError:
235 except IOError:
236 pass
236 pass
237
237
238 def write(self, s):
238 def write(self, s):
239 try:
239 try:
240 return self.fp.write(s)
240 return self.fp.write(s)
241 except IOError as inst:
241 except IOError as inst:
242 if inst.errno != 0 and not win32.lasterrorwaspipeerror(inst):
242 if inst.errno != 0 and not win32.lasterrorwaspipeerror(inst):
243 raise
243 raise
244 self.close()
244 self.close()
245 raise IOError(errno.EPIPE, 'Broken pipe')
245 raise IOError(errno.EPIPE, 'Broken pipe')
246
246
247 def flush(self):
247 def flush(self):
248 try:
248 try:
249 return self.fp.flush()
249 return self.fp.flush()
250 except IOError as inst:
250 except IOError as inst:
251 if not win32.lasterrorwaspipeerror(inst):
251 if not win32.lasterrorwaspipeerror(inst):
252 raise
252 raise
253 raise IOError(errno.EPIPE, 'Broken pipe')
253 raise IOError(errno.EPIPE, 'Broken pipe')
254
254
255
255
256 def openhardlinks():
256 def openhardlinks():
257 return True
257 return True
258
258
259
259
260 def parsepatchoutput(output_line):
260 def parsepatchoutput(output_line):
261 """parses the output produced by patch and returns the filename"""
261 """parses the output produced by patch and returns the filename"""
262 pf = output_line[14:]
262 pf = output_line[14:]
263 if pf[0] == b'`':
263 if pf[0] == b'`':
264 pf = pf[1:-1] # Remove the quotes
264 pf = pf[1:-1] # Remove the quotes
265 return pf
265 return pf
266
266
267
267
268 def sshargs(sshcmd, host, user, port):
268 def sshargs(sshcmd, host, user, port):
269 '''Build argument list for ssh or Plink'''
269 '''Build argument list for ssh or Plink'''
270 pflag = b'plink' in sshcmd.lower() and b'-P' or b'-p'
270 pflag = b'plink' in sshcmd.lower() and b'-P' or b'-p'
271 args = user and (b"%s@%s" % (user, host)) or host
271 args = user and (b"%s@%s" % (user, host)) or host
272 if args.startswith(b'-') or args.startswith(b'/'):
272 if args.startswith(b'-') or args.startswith(b'/'):
273 raise error.Abort(
273 raise error.Abort(
274 _(b'illegal ssh hostname or username starting with - or /: %s')
274 _(b'illegal ssh hostname or username starting with - or /: %s')
275 % args
275 % args
276 )
276 )
277 args = shellquote(args)
277 args = shellquote(args)
278 if port:
278 if port:
279 args = b'%s %s %s' % (pflag, shellquote(port), args)
279 args = b'%s %s %s' % (pflag, shellquote(port), args)
280 return args
280 return args
281
281
282
282
283 def setflags(f, l, x):
283 def setflags(f, l, x):
284 pass
284 pass
285
285
286
286
287 def copymode(src, dst, mode=None, enforcewritable=False):
287 def copymode(src, dst, mode=None, enforcewritable=False):
288 pass
288 pass
289
289
290
290
291 def checkexec(path):
291 def checkexec(path):
292 return False
292 return False
293
293
294
294
295 def checklink(path):
295 def checklink(path):
296 return False
296 return False
297
297
298
298
299 def setbinary(fd):
299 def setbinary(fd):
300 # When run without console, pipes may expose invalid
300 # When run without console, pipes may expose invalid
301 # fileno(), usually set to -1.
301 # fileno(), usually set to -1.
302 fno = getattr(fd, 'fileno', None)
302 fno = getattr(fd, 'fileno', None)
303 if fno is not None and fno() >= 0:
303 if fno is not None and fno() >= 0:
304 msvcrt.setmode(fno(), os.O_BINARY) # pytype: disable=module-attr
304 msvcrt.setmode(fno(), os.O_BINARY) # pytype: disable=module-attr
305
305
306
306
307 def pconvert(path):
307 def pconvert(path):
308 return path.replace(pycompat.ossep, b'/')
308 return path.replace(pycompat.ossep, b'/')
309
309
310
310
311 def localpath(path):
311 def localpath(path):
312 return path.replace(b'/', b'\\')
312 return path.replace(b'/', b'\\')
313
313
314
314
315 def normpath(path):
315 def normpath(path):
316 return pconvert(os.path.normpath(path))
316 return pconvert(os.path.normpath(path))
317
317
318
318
319 def normcase(path):
319 def normcase(path):
320 return encoding.upper(path) # NTFS compares via upper()
320 return encoding.upper(path) # NTFS compares via upper()
321
321
322
322
323 DRIVE_RE_B = re.compile(b'^[a-z]:')
323 DRIVE_RE_B = re.compile(b'^[a-z]:')
324 DRIVE_RE_S = re.compile('^[a-z]:')
324 DRIVE_RE_S = re.compile('^[a-z]:')
325
325
326
326
327 def abspath(path):
327 def abspath(path):
328 abs_path = os.path.abspath(path) # re-exports
328 abs_path = os.path.abspath(path) # re-exports
329 # Python on Windows is inconsistent regarding the capitalization of drive
329 # Python on Windows is inconsistent regarding the capitalization of drive
330 # letter and this cause issue with various path comparison along the way.
330 # letter and this cause issue with various path comparison along the way.
331 # So we normalize the drive later to upper case here.
331 # So we normalize the drive later to upper case here.
332 #
332 #
333 # See https://bugs.python.org/issue40368 for and example of this hell.
333 # See https://bugs.python.org/issue40368 for and example of this hell.
334 if isinstance(abs_path, bytes):
334 if isinstance(abs_path, bytes):
335 if DRIVE_RE_B.match(abs_path):
335 if DRIVE_RE_B.match(abs_path):
336 abs_path = abs_path[0:1].upper() + abs_path[1:]
336 abs_path = abs_path[0:1].upper() + abs_path[1:]
337 elif DRIVE_RE_S.match(abs_path):
337 elif DRIVE_RE_S.match(abs_path):
338 abs_path = abs_path[0:1].upper() + abs_path[1:]
338 abs_path = abs_path[0:1].upper() + abs_path[1:]
339 return abs_path
339 return abs_path
340
340
341
341
342 # see posix.py for definitions
342 # see posix.py for definitions
343 normcasespec = encoding.normcasespecs.upper
343 normcasespec = encoding.normcasespecs.upper
344 normcasefallback = encoding.upperfallback
344 normcasefallback = encoding.upperfallback
345
345
346
346
347 def samestat(s1, s2):
347 def samestat(s1, s2):
348 return False
348 return False
349
349
350
350
351 def shelltocmdexe(path, env):
351 def shelltocmdexe(path, env):
352 r"""Convert shell variables in the form $var and ${var} inside ``path``
352 r"""Convert shell variables in the form $var and ${var} inside ``path``
353 to %var% form. Existing Windows style variables are left unchanged.
353 to %var% form. Existing Windows style variables are left unchanged.
354
354
355 The variables are limited to the given environment. Unknown variables are
355 The variables are limited to the given environment. Unknown variables are
356 left unchanged.
356 left unchanged.
357
357
358 >>> e = {b'var1': b'v1', b'var2': b'v2', b'var3': b'v3'}
358 >>> e = {b'var1': b'v1', b'var2': b'v2', b'var3': b'v3'}
359 >>> # Only valid values are expanded
359 >>> # Only valid values are expanded
360 >>> shelltocmdexe(b'cmd $var1 ${var2} %var3% $missing ${missing} %missing%',
360 >>> shelltocmdexe(b'cmd $var1 ${var2} %var3% $missing ${missing} %missing%',
361 ... e)
361 ... e)
362 'cmd %var1% %var2% %var3% $missing ${missing} %missing%'
362 'cmd %var1% %var2% %var3% $missing ${missing} %missing%'
363 >>> # Single quote prevents expansion, as does \$ escaping
363 >>> # Single quote prevents expansion, as does \$ escaping
364 >>> shelltocmdexe(b"cmd '$var1 ${var2} %var3%' \$var1 \${var2} \\", e)
364 >>> shelltocmdexe(b"cmd '$var1 ${var2} %var3%' \$var1 \${var2} \\", e)
365 'cmd "$var1 ${var2} %var3%" $var1 ${var2} \\'
365 'cmd "$var1 ${var2} %var3%" $var1 ${var2} \\'
366 >>> # $$ is not special. %% is not special either, but can be the end and
366 >>> # $$ is not special. %% is not special either, but can be the end and
367 >>> # start of consecutive variables
367 >>> # start of consecutive variables
368 >>> shelltocmdexe(b"cmd $$ %% %var1%%var2%", e)
368 >>> shelltocmdexe(b"cmd $$ %% %var1%%var2%", e)
369 'cmd $$ %% %var1%%var2%'
369 'cmd $$ %% %var1%%var2%'
370 >>> # No double substitution
370 >>> # No double substitution
371 >>> shelltocmdexe(b"$var1 %var1%", {b'var1': b'%var2%', b'var2': b'boom'})
371 >>> shelltocmdexe(b"$var1 %var1%", {b'var1': b'%var2%', b'var2': b'boom'})
372 '%var1% %var1%'
372 '%var1% %var1%'
373 >>> # Tilde expansion
373 >>> # Tilde expansion
374 >>> shelltocmdexe(b"~/dir ~\dir2 ~tmpfile \~/", {})
374 >>> shelltocmdexe(b"~/dir ~\dir2 ~tmpfile \~/", {})
375 '%USERPROFILE%/dir %USERPROFILE%\\dir2 ~tmpfile ~/'
375 '%USERPROFILE%/dir %USERPROFILE%\\dir2 ~tmpfile ~/'
376 """
376 """
377 if not any(c in path for c in b"$'~"):
377 if not any(c in path for c in b"$'~"):
378 return path
378 return path
379
379
380 varchars = pycompat.sysbytes(string.ascii_letters + string.digits) + b'_-'
380 varchars = pycompat.sysbytes(string.ascii_letters + string.digits) + b'_-'
381
381
382 res = b''
382 res = b''
383 index = 0
383 index = 0
384 pathlen = len(path)
384 pathlen = len(path)
385 while index < pathlen:
385 while index < pathlen:
386 c = path[index : index + 1]
386 c = path[index : index + 1]
387 if c == b'\'': # no expansion within single quotes
387 if c == b'\'': # no expansion within single quotes
388 path = path[index + 1 :]
388 path = path[index + 1 :]
389 pathlen = len(path)
389 pathlen = len(path)
390 try:
390 try:
391 index = path.index(b'\'')
391 index = path.index(b'\'')
392 res += b'"' + path[:index] + b'"'
392 res += b'"' + path[:index] + b'"'
393 except ValueError:
393 except ValueError:
394 res += c + path
394 res += c + path
395 index = pathlen - 1
395 index = pathlen - 1
396 elif c == b'%': # variable
396 elif c == b'%': # variable
397 path = path[index + 1 :]
397 path = path[index + 1 :]
398 pathlen = len(path)
398 pathlen = len(path)
399 try:
399 try:
400 index = path.index(b'%')
400 index = path.index(b'%')
401 except ValueError:
401 except ValueError:
402 res += b'%' + path
402 res += b'%' + path
403 index = pathlen - 1
403 index = pathlen - 1
404 else:
404 else:
405 var = path[:index]
405 var = path[:index]
406 res += b'%' + var + b'%'
406 res += b'%' + var + b'%'
407 elif c == b'$': # variable
407 elif c == b'$': # variable
408 if path[index + 1 : index + 2] == b'{':
408 if path[index + 1 : index + 2] == b'{':
409 path = path[index + 2 :]
409 path = path[index + 2 :]
410 pathlen = len(path)
410 pathlen = len(path)
411 try:
411 try:
412 index = path.index(b'}')
412 index = path.index(b'}')
413 var = path[:index]
413 var = path[:index]
414
414
415 # See below for why empty variables are handled specially
415 # See below for why empty variables are handled specially
416 if env.get(var, b'') != b'':
416 if env.get(var, b'') != b'':
417 res += b'%' + var + b'%'
417 res += b'%' + var + b'%'
418 else:
418 else:
419 res += b'${' + var + b'}'
419 res += b'${' + var + b'}'
420 except ValueError:
420 except ValueError:
421 res += b'${' + path
421 res += b'${' + path
422 index = pathlen - 1
422 index = pathlen - 1
423 else:
423 else:
424 var = b''
424 var = b''
425 index += 1
425 index += 1
426 c = path[index : index + 1]
426 c = path[index : index + 1]
427 while c != b'' and c in varchars:
427 while c != b'' and c in varchars:
428 var += c
428 var += c
429 index += 1
429 index += 1
430 c = path[index : index + 1]
430 c = path[index : index + 1]
431 # Some variables (like HG_OLDNODE) may be defined, but have an
431 # Some variables (like HG_OLDNODE) may be defined, but have an
432 # empty value. Those need to be skipped because when spawning
432 # empty value. Those need to be skipped because when spawning
433 # cmd.exe to run the hook, it doesn't replace %VAR% for an empty
433 # cmd.exe to run the hook, it doesn't replace %VAR% for an empty
434 # VAR, and that really confuses things like revset expressions.
434 # VAR, and that really confuses things like revset expressions.
435 # OTOH, if it's left in Unix format and the hook runs sh.exe, it
435 # OTOH, if it's left in Unix format and the hook runs sh.exe, it
436 # will substitute to an empty string, and everything is happy.
436 # will substitute to an empty string, and everything is happy.
437 if env.get(var, b'') != b'':
437 if env.get(var, b'') != b'':
438 res += b'%' + var + b'%'
438 res += b'%' + var + b'%'
439 else:
439 else:
440 res += b'$' + var
440 res += b'$' + var
441
441
442 if c != b'':
442 if c != b'':
443 index -= 1
443 index -= 1
444 elif (
444 elif (
445 c == b'~'
445 c == b'~'
446 and index + 1 < pathlen
446 and index + 1 < pathlen
447 and path[index + 1 : index + 2] in (b'\\', b'/')
447 and path[index + 1 : index + 2] in (b'\\', b'/')
448 ):
448 ):
449 res += b"%USERPROFILE%"
449 res += b"%USERPROFILE%"
450 elif (
450 elif (
451 c == b'\\'
451 c == b'\\'
452 and index + 1 < pathlen
452 and index + 1 < pathlen
453 and path[index + 1 : index + 2] in (b'$', b'~')
453 and path[index + 1 : index + 2] in (b'$', b'~')
454 ):
454 ):
455 # Skip '\', but only if it is escaping $ or ~
455 # Skip '\', but only if it is escaping $ or ~
456 res += path[index + 1 : index + 2]
456 res += path[index + 1 : index + 2]
457 index += 1
457 index += 1
458 else:
458 else:
459 res += c
459 res += c
460
460
461 index += 1
461 index += 1
462 return res
462 return res
463
463
464
464
465 # A sequence of backslashes is special iff it precedes a double quote:
465 # A sequence of backslashes is special iff it precedes a double quote:
466 # - if there's an even number of backslashes, the double quote is not
466 # - if there's an even number of backslashes, the double quote is not
467 # quoted (i.e. it ends the quoted region)
467 # quoted (i.e. it ends the quoted region)
468 # - if there's an odd number of backslashes, the double quote is quoted
468 # - if there's an odd number of backslashes, the double quote is quoted
469 # - in both cases, every pair of backslashes is unquoted into a single
469 # - in both cases, every pair of backslashes is unquoted into a single
470 # backslash
470 # backslash
471 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
471 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
472 # So, to quote a string, we must surround it in double quotes, double
472 # So, to quote a string, we must surround it in double quotes, double
473 # the number of backslashes that precede double quotes and add another
473 # the number of backslashes that precede double quotes and add another
474 # backslash before every double quote (being careful with the double
474 # backslash before every double quote (being careful with the double
475 # quote we've appended to the end)
475 # quote we've appended to the end)
476 _quotere = None
476 _quotere = None
477 _needsshellquote = None
477 _needsshellquote = None
478
478
479
479
480 def shellquote(s):
480 def shellquote(s):
481 r"""
481 r"""
482 >>> shellquote(br'C:\Users\xyz')
482 >>> shellquote(br'C:\Users\xyz')
483 '"C:\\Users\\xyz"'
483 '"C:\\Users\\xyz"'
484 >>> shellquote(br'C:\Users\xyz/mixed')
484 >>> shellquote(br'C:\Users\xyz/mixed')
485 '"C:\\Users\\xyz/mixed"'
485 '"C:\\Users\\xyz/mixed"'
486 >>> # Would be safe not to quote too, since it is all double backslashes
486 >>> # Would be safe not to quote too, since it is all double backslashes
487 >>> shellquote(br'C:\\Users\\xyz')
487 >>> shellquote(br'C:\\Users\\xyz')
488 '"C:\\\\Users\\\\xyz"'
488 '"C:\\\\Users\\\\xyz"'
489 >>> # But this must be quoted
489 >>> # But this must be quoted
490 >>> shellquote(br'C:\\Users\\xyz/abc')
490 >>> shellquote(br'C:\\Users\\xyz/abc')
491 '"C:\\\\Users\\\\xyz/abc"'
491 '"C:\\\\Users\\\\xyz/abc"'
492 """
492 """
493 global _quotere
493 global _quotere
494 if _quotere is None:
494 if _quotere is None:
495 _quotere = re.compile(br'(\\*)("|\\$)')
495 _quotere = re.compile(br'(\\*)("|\\$)')
496 global _needsshellquote
496 global _needsshellquote
497 if _needsshellquote is None:
497 if _needsshellquote is None:
498 # ":" is also treated as "safe character", because it is used as a part
498 # ":" is also treated as "safe character", because it is used as a part
499 # of path name on Windows. "\" is also part of a path name, but isn't
499 # of path name on Windows. "\" is also part of a path name, but isn't
500 # safe because shlex.split() (kind of) treats it as an escape char and
500 # safe because shlex.split() (kind of) treats it as an escape char and
501 # drops it. It will leave the next character, even if it is another
501 # drops it. It will leave the next character, even if it is another
502 # "\".
502 # "\".
503 _needsshellquote = re.compile(br'[^a-zA-Z0-9._:/-]').search
503 _needsshellquote = re.compile(br'[^a-zA-Z0-9._:/-]').search
504 if s and not _needsshellquote(s) and not _quotere.search(s):
504 if s and not _needsshellquote(s) and not _quotere.search(s):
505 # "s" shouldn't have to be quoted
505 # "s" shouldn't have to be quoted
506 return s
506 return s
507 return b'"%s"' % _quotere.sub(br'\1\1\\\2', s)
507 return b'"%s"' % _quotere.sub(br'\1\1\\\2', s)
508
508
509
509
510 def _unquote(s):
510 def _unquote(s):
511 if s.startswith(b'"') and s.endswith(b'"'):
511 if s.startswith(b'"') and s.endswith(b'"'):
512 return s[1:-1]
512 return s[1:-1]
513 return s
513 return s
514
514
515
515
516 def shellsplit(s):
516 def shellsplit(s):
517 """Parse a command string in cmd.exe way (best-effort)"""
517 """Parse a command string in cmd.exe way (best-effort)"""
518 return pycompat.maplist(_unquote, pycompat.shlexsplit(s, posix=False))
518 return pycompat.maplist(_unquote, pycompat.shlexsplit(s, posix=False))
519
519
520
520
521 # if you change this stub into a real check, please try to implement the
521 # if you change this stub into a real check, please try to implement the
522 # username and groupname functions above, too.
522 # username and groupname functions above, too.
523 def isowner(st):
523 def isowner(st):
524 return True
524 return True
525
525
526
526
527 def findexe(command):
527 def findexe(command):
528 """Find executable for command searching like cmd.exe does.
528 """Find executable for command searching like cmd.exe does.
529 If command is a basename then PATH is searched for command.
529 If command is a basename then PATH is searched for command.
530 PATH isn't searched if command is an absolute or relative path.
530 PATH isn't searched if command is an absolute or relative path.
531 An extension from PATHEXT is found and added if not present.
531 An extension from PATHEXT is found and added if not present.
532 If command isn't found None is returned."""
532 If command isn't found None is returned."""
533 pathext = encoding.environ.get(b'PATHEXT', b'.COM;.EXE;.BAT;.CMD')
533 pathext = encoding.environ.get(b'PATHEXT', b'.COM;.EXE;.BAT;.CMD')
534 pathexts = [ext for ext in pathext.lower().split(pycompat.ospathsep)]
534 pathexts = [ext for ext in pathext.lower().split(pycompat.ospathsep)]
535 if os.path.splitext(command)[1].lower() in pathexts:
535 if os.path.splitext(command)[1].lower() in pathexts:
536 pathexts = [b'']
536 pathexts = [b'']
537
537
538 def findexisting(pathcommand):
538 def findexisting(pathcommand):
539 """Will append extension (if needed) and return existing file"""
539 """Will append extension (if needed) and return existing file"""
540 for ext in pathexts:
540 for ext in pathexts:
541 executable = pathcommand + ext
541 executable = pathcommand + ext
542 if os.path.exists(executable):
542 if os.path.exists(executable):
543 return executable
543 return executable
544 return None
544 return None
545
545
546 if pycompat.ossep in command:
546 if pycompat.ossep in command:
547 return findexisting(command)
547 return findexisting(command)
548
548
549 for path in encoding.environ.get(b'PATH', b'').split(pycompat.ospathsep):
549 for path in encoding.environ.get(b'PATH', b'').split(pycompat.ospathsep):
550 executable = findexisting(os.path.join(path, command))
550 executable = findexisting(os.path.join(path, command))
551 if executable is not None:
551 if executable is not None:
552 return executable
552 return executable
553 return findexisting(os.path.expanduser(os.path.expandvars(command)))
553 return findexisting(os.path.expanduser(os.path.expandvars(command)))
554
554
555
555
556 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK}
556 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK}
557
557
558
558
559 def statfiles(files):
559 def statfiles(files):
560 """Stat each file in files. Yield each stat, or None if a file
560 """Stat each file in files. Yield each stat, or None if a file
561 does not exist or has a type we don't care about.
561 does not exist or has a type we don't care about.
562
562
563 Cluster and cache stat per directory to minimize number of OS stat calls."""
563 Cluster and cache stat per directory to minimize number of OS stat calls."""
564 dircache = {} # dirname -> filename -> status | None if file does not exist
564 dircache = {} # dirname -> filename -> status | None if file does not exist
565 getkind = stat.S_IFMT
565 getkind = stat.S_IFMT
566 for nf in files:
566 for nf in files:
567 nf = normcase(nf)
567 nf = normcase(nf)
568 dir, base = os.path.split(nf)
568 dir, base = os.path.split(nf)
569 if not dir:
569 if not dir:
570 dir = b'.'
570 dir = b'.'
571 cache = dircache.get(dir, None)
571 cache = dircache.get(dir, None)
572 if cache is None:
572 if cache is None:
573 try:
573 try:
574 dmap = {
574 dmap = {
575 normcase(n): s
575 normcase(n): s
576 for n, k, s in listdir(dir, True)
576 for n, k, s in listdir(dir, True)
577 if getkind(s.st_mode) in _wantedkinds
577 if getkind(s.st_mode) in _wantedkinds
578 }
578 }
579 except OSError as err:
579 except OSError as err:
580 # Python >= 2.5 returns ENOENT and adds winerror field
580 # Python >= 2.5 returns ENOENT and adds winerror field
581 # EINVAL is raised if dir is not a directory.
581 # EINVAL is raised if dir is not a directory.
582 if err.errno not in (errno.ENOENT, errno.EINVAL, errno.ENOTDIR):
582 if err.errno not in (errno.ENOENT, errno.EINVAL, errno.ENOTDIR):
583 raise
583 raise
584 dmap = {}
584 dmap = {}
585 cache = dircache.setdefault(dir, dmap)
585 cache = dircache.setdefault(dir, dmap)
586 yield cache.get(base, None)
586 yield cache.get(base, None)
587
587
588
588
589 def username(uid=None):
589 def username(uid=None):
590 """Return the name of the user with the given uid.
590 """Return the name of the user with the given uid.
591
591
592 If uid is None, return the name of the current user."""
592 If uid is None, return the name of the current user."""
593 if not uid:
593 if not uid:
594 return pycompat.fsencode(getpass.getuser())
594 return pycompat.fsencode(getpass.getuser())
595 return None
595 return None
596
596
597
597
598 def groupname(gid=None):
598 def groupname(gid=None):
599 """Return the name of the group with the given gid.
599 """Return the name of the group with the given gid.
600
600
601 If gid is None, return the name of the current group."""
601 If gid is None, return the name of the current group."""
602 return None
602 return None
603
603
604
604
605 def readlink(pathname):
605 def readlink(pathname):
606 path = pycompat.fsdecode(pathname)
606 path = pycompat.fsdecode(pathname)
607 try:
607 try:
608 link = os.readlink(path)
608 link = os.readlink(path)
609 except ValueError as e:
609 except ValueError as e:
610 # On py2, os.readlink() raises an AttributeError since it is
610 # On py2, os.readlink() raises an AttributeError since it is
611 # unsupported. On py3, reading a non-link raises a ValueError. Simply
611 # unsupported. On py3, reading a non-link raises a ValueError. Simply
612 # treat this as the error the locking code has been expecting up to now
612 # treat this as the error the locking code has been expecting up to now
613 # until an effort can be made to enable symlink support on Windows.
613 # until an effort can be made to enable symlink support on Windows.
614 raise AttributeError(e)
614 raise AttributeError(e)
615 return pycompat.fsencode(link)
615 return pycompat.fsencode(link)
616
616
617
617
618 def removedirs(name):
618 def removedirs(name):
619 """special version of os.removedirs that does not remove symlinked
619 """special version of os.removedirs that does not remove symlinked
620 directories or junction points if they actually contain files"""
620 directories or junction points if they actually contain files"""
621 if listdir(name):
621 if listdir(name):
622 return
622 return
623 os.rmdir(name)
623 os.rmdir(name)
624 head, tail = os.path.split(name)
624 head, tail = os.path.split(name)
625 if not tail:
625 if not tail:
626 head, tail = os.path.split(head)
626 head, tail = os.path.split(head)
627 while head and tail:
627 while head and tail:
628 try:
628 try:
629 if listdir(head):
629 if listdir(head):
630 return
630 return
631 os.rmdir(head)
631 os.rmdir(head)
632 except (ValueError, OSError):
632 except (ValueError, OSError):
633 break
633 break
634 head, tail = os.path.split(head)
634 head, tail = os.path.split(head)
635
635
636
636
637 def rename(src, dst):
637 def rename(src, dst):
638 '''atomically rename file src to dst, replacing dst if it exists'''
638 '''atomically rename file src to dst, replacing dst if it exists'''
639 try:
639 try:
640 os.rename(src, dst)
640 os.rename(src, dst)
641 except OSError as e:
641 except OSError as e:
642 if e.errno != errno.EEXIST:
642 if e.errno != errno.EEXIST:
643 raise
643 raise
644 unlink(dst)
644 unlink(dst)
645 os.rename(src, dst)
645 os.rename(src, dst)
646
646
647
647
648 def gethgcmd():
648 def gethgcmd():
649 return [encoding.strtolocal(arg) for arg in [sys.executable] + sys.argv[:1]]
649 return [encoding.strtolocal(arg) for arg in [sys.executable] + sys.argv[:1]]
650
650
651
651
652 def groupmembers(name):
652 def groupmembers(name):
653 # Don't support groups on Windows for now
653 # Don't support groups on Windows for now
654 raise KeyError
654 raise KeyError
655
655
656
656
657 def isexec(f):
657 def isexec(f):
658 return False
658 return False
659
659
660
660
661 class cachestat:
661 class cachestat:
662 def __init__(self, path):
662 def __init__(self, path):
663 pass
663 pass
664
664
665 def cacheable(self):
665 def cacheable(self):
666 return False
666 return False
667
667
668
668
669 def lookupreg(key, valname=None, scope=None):
669 def lookupreg(key, valname=None, scope=None):
670 """Look up a key/value name in the Windows registry.
670 """Look up a key/value name in the Windows registry.
671
671
672 valname: value name. If unspecified, the default value for the key
672 valname: value name. If unspecified, the default value for the key
673 is used.
673 is used.
674 scope: optionally specify scope for registry lookup, this can be
674 scope: optionally specify scope for registry lookup, this can be
675 a sequence of scopes to look up in order. Default (CURRENT_USER,
675 a sequence of scopes to look up in order. Default (CURRENT_USER,
676 LOCAL_MACHINE).
676 LOCAL_MACHINE).
677 """
677 """
678 if scope is None:
678 if scope is None:
679 # pytype: disable=module-attr
679 scope = (winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE)
680 scope = (winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE)
681 # pytype: enable=module-attr
680 elif not isinstance(scope, (list, tuple)):
682 elif not isinstance(scope, (list, tuple)):
681 scope = (scope,)
683 scope = (scope,)
682 for s in scope:
684 for s in scope:
683 try:
685 try:
686 # pytype: disable=module-attr
684 with winreg.OpenKey(s, encoding.strfromlocal(key)) as hkey:
687 with winreg.OpenKey(s, encoding.strfromlocal(key)) as hkey:
688 # pytype: enable=module-attr
685 name = valname and encoding.strfromlocal(valname) or valname
689 name = valname and encoding.strfromlocal(valname) or valname
690 # pytype: disable=module-attr
686 val = winreg.QueryValueEx(hkey, name)[0]
691 val = winreg.QueryValueEx(hkey, name)[0]
692 # pytype: enable=module-attr
693
687 # never let a Unicode string escape into the wild
694 # never let a Unicode string escape into the wild
688 return encoding.unitolocal(val)
695 return encoding.unitolocal(val)
689 except EnvironmentError:
696 except EnvironmentError:
690 pass
697 pass
691
698
692
699
693 expandglobs = True
700 expandglobs = True
694
701
695
702
696 def statislink(st):
703 def statislink(st):
697 '''check whether a stat result is a symlink'''
704 '''check whether a stat result is a symlink'''
698 return False
705 return False
699
706
700
707
701 def statisexec(st):
708 def statisexec(st):
702 '''check whether a stat result is an executable file'''
709 '''check whether a stat result is an executable file'''
703 return False
710 return False
704
711
705
712
706 def poll(fds):
713 def poll(fds):
707 # see posix.py for description
714 # see posix.py for description
708 raise NotImplementedError()
715 raise NotImplementedError()
709
716
710
717
711 def readpipe(pipe):
718 def readpipe(pipe):
712 """Read all available data from a pipe."""
719 """Read all available data from a pipe."""
713 chunks = []
720 chunks = []
714 while True:
721 while True:
715 size = win32.peekpipe(pipe)
722 size = win32.peekpipe(pipe)
716 if not size:
723 if not size:
717 break
724 break
718
725
719 s = pipe.read(size)
726 s = pipe.read(size)
720 if not s:
727 if not s:
721 break
728 break
722 chunks.append(s)
729 chunks.append(s)
723
730
724 return b''.join(chunks)
731 return b''.join(chunks)
725
732
726
733
727 def bindunixsocket(sock, path):
734 def bindunixsocket(sock, path):
728 raise NotImplementedError('unsupported platform')
735 raise NotImplementedError('unsupported platform')
General Comments 0
You need to be logged in to leave comments. Login now